package com.xcong.excoin.modules.documentary.service.impl;
|
|
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.StrUtil;
|
import com.alibaba.fastjson.JSONObject;
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.xcong.excoin.common.contants.AppContants;
|
import com.xcong.excoin.common.enumerates.CoinTypeEnum;
|
import com.xcong.excoin.common.enumerates.MemberWalletCoinEnum;
|
import com.xcong.excoin.common.enumerates.RabbitPriceTypeEnum;
|
import com.xcong.excoin.common.response.Result;
|
import com.xcong.excoin.common.system.service.CommonService;
|
import com.xcong.excoin.modules.coin.entity.MemberAccountMoneyChange;
|
import com.xcong.excoin.modules.contract.dao.ContractHoldOrderDao;
|
import com.xcong.excoin.modules.contract.dao.ContractOrderDao;
|
import com.xcong.excoin.modules.contract.entity.ContractEntrustOrderEntity;
|
import com.xcong.excoin.modules.contract.entity.ContractHoldOrderEntity;
|
import com.xcong.excoin.modules.contract.entity.ContractOrderEntity;
|
import com.xcong.excoin.modules.contract.mapper.ContractHoldOrderEntityMapper;
|
import com.xcong.excoin.modules.contract.service.RabbitOrderService;
|
import com.xcong.excoin.modules.documentary.common.NoticeConstant;
|
import com.xcong.excoin.modules.documentary.dao.*;
|
import com.xcong.excoin.modules.documentary.entity.FollowFollowerOrderRelationEntity;
|
import com.xcong.excoin.modules.documentary.entity.FollowFollowerProfitEntity;
|
import com.xcong.excoin.modules.documentary.entity.FollowFollowerSettingEntity;
|
import com.xcong.excoin.modules.documentary.entity.FollowTraderInfoEntity;
|
import com.xcong.excoin.modules.documentary.service.FollowOrderOperationService;
|
import com.xcong.excoin.modules.member.dao.MemberDao;
|
import com.xcong.excoin.modules.member.dao.MemberLevelRateDao;
|
import com.xcong.excoin.modules.member.dao.MemberSettingDao;
|
import com.xcong.excoin.modules.member.dao.MemberWalletContractDao;
|
import com.xcong.excoin.modules.member.entity.*;
|
import com.xcong.excoin.modules.platform.entity.PlatformTradeSettingEntity;
|
import com.xcong.excoin.rabbit.pricequeue.OrderModel;
|
import com.xcong.excoin.rabbit.producer.FollowProducer;
|
import com.xcong.excoin.rabbit.producer.OrderProducer;
|
import com.xcong.excoin.utils.*;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Isolation;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import javax.annotation.Resource;
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.util.ArrayList;
|
import java.util.Date;
|
import java.util.List;
|
|
/**
|
* @author helius
|
*/
|
@Slf4j
|
@Service
|
public class FollowOrderOperationServiceImpl implements FollowOrderOperationService {
|
|
@Resource
|
private ContractHoldOrderDao contractHoldOrderDao;
|
@Resource
|
private FollowFollowerSettingDao followFollowerSettingDao;
|
@Resource
|
private CacheSettingUtils cacheSettingUtils;
|
@Resource
|
private FollowFollowerOrderRelationDao followFollowerOrderRelationDao;
|
@Resource
|
private MemberWalletContractDao memberWalletContractDao;
|
@Resource
|
private MemberDao memberDao;
|
@Resource
|
private CommonService commonService;
|
@Resource
|
private ContractOrderDao contractOrderDao;
|
@Resource
|
private OrderProducer producer;
|
@Resource
|
private FollowTraderInfoDao followTraderInfoDao;
|
@Resource
|
private RabbitOrderService rabbitOrderService;
|
@Resource
|
private MemberLevelRateDao memberLevelRateDao;
|
@Resource
|
private MemberSettingDao memberSettingDao;
|
|
@Autowired
|
private FollowProducer followProducer;
|
@Autowired
|
private FollowTraderProfitDetailDao followTraderProfitDetailDao;
|
@Resource
|
private RedisUtils redisUtils;
|
|
@Override
|
@Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
|
public void addFollowerOrder(Long id) {
|
log.info("进入跟单处理逻辑 : {}", id);
|
// 查询交易员订单
|
ContractHoldOrderEntity holdOrderEntity = contractHoldOrderDao.selectById(id);
|
|
if (holdOrderEntity == null) {
|
try {
|
log.info("等待事务提交:{}", id);
|
Thread.sleep(200);
|
followProducer.sendAddFollowOrder(id);
|
return;
|
} catch (InterruptedException e) {
|
e.printStackTrace();
|
}
|
}
|
|
List<FollowFollowerSettingEntity> followerSettings = followFollowerSettingDao.selectAllFollowerSettingByTradeMemberId(holdOrderEntity.getMemberId());
|
log.info("进入跟单处理逻辑---跟单人数"+followerSettings.size());
|
// 开仓价
|
BigDecimal openPrice = holdOrderEntity.getOpeningPrice();
|
PlatformTradeSettingEntity tradeSettingEntity = cacheSettingUtils.getTradeSetting();
|
|
// 交易员信息
|
FollowTraderInfoEntity followTraderInfoEntity = followTraderInfoDao.selectTraderInfoByMemberId(holdOrderEntity.getMemberId());
|
|
// 点差
|
BigDecimal lotNumber = cacheSettingUtils.getSymbolSku(holdOrderEntity.getSymbol());
|
Long tradeMemberId = holdOrderEntity.getMemberId();
|
if (CollUtil.isNotEmpty(followerSettings)) {
|
List<Object> hasExist = new ArrayList<>();
|
for (FollowFollowerSettingEntity followerSetting : followerSettings) {
|
// 加redis锁,同一个用户不能同时触发两个跟单任务,否则会出现金额问题
|
while (true) {
|
boolean flag = redisUtils.setNotExist(AppContants.MEMBER_HAS_FOLLOW + "_" + followerSetting.getMemberId(), "1", 5);
|
// List<Object> followerMemberId = redisUtils.lGet(AppContants.MEMBER_HAS_FOLLOW, 0, -1);
|
// log.info("#跟单用户任务已存在:{}, 当前:{}#", followerMemberId, followerSetting.getMemberId());
|
// log.info("#------->{}#", followerMemberId.contains(followerSetting.getMemberId().intValue()));
|
// if (CollUtil.isEmpty(followerMemberId) || !followerMemberId.contains(followerSetting.getMemberId().intValue())) {
|
// log.info("跳出");
|
// hasExist.add(followerSetting.getMemberId());
|
// redisUtils.lSet(AppContants.MEMBER_HAS_FOLLOW, hasExist);
|
// break;
|
// }
|
|
if (flag) {
|
log.info("跳出");
|
break;
|
}
|
try {
|
Thread.sleep(500);
|
} catch (InterruptedException e) {
|
e.printStackTrace();
|
}
|
}
|
|
//更新更新消息提醒的状态
|
MemberSettingEntity memberSettingEntity = memberSettingDao.selectMemberSettingByMemberId(followerSetting.getMemberId());
|
if(ObjectUtil.isNotEmpty(memberSettingEntity)){
|
Long memberId = memberSettingEntity.getMemberId();
|
log.info("更新更新消息提醒的状态");
|
memberSettingDao.updateMessageReminderByMemberId(memberId);
|
}
|
|
if (!followerSetting.getSymbols().contains(holdOrderEntity.getSymbol().replace("/USDT", ""))) {
|
log.info("不在跟单币种内,不跟单:{},{}", followerSetting.getSymbols(), holdOrderEntity.getSymbol());
|
continue;
|
}
|
|
// 当前持仓张数
|
Integer holdingCnt = followFollowerOrderRelationDao.selectFollowerHoldingSymbolCnt(tradeMemberId, followerSetting.getMemberId());
|
|
// 跟单张数 根据跟随者设置,若为固定张数,则为其固定张数,若为固定比例,则与交易员张数相乘。但必须小于最大持仓张数
|
int symbolCnt = 0;
|
if (followerSetting.getFollowType().equals(FollowFollowerSettingEntity.FOLLOW_TYPE_PIECE)) {
|
symbolCnt = followerSetting.getFollowCnt();
|
} else {
|
symbolCnt = followerSetting.getFollowCnt() * holdOrderEntity.getSymbolCnt();
|
}
|
|
if (followerSetting.getMaxFollowCnt() != null && followerSetting.getMaxFollowCnt() !=0) {
|
// 若张数+当前持仓张数大于最大持仓张数,则取最大持仓减去当前持仓,若差值小于等于0,则不下单
|
if (symbolCnt + holdingCnt > followerSetting.getMaxFollowCnt()) {
|
symbolCnt = followerSetting.getMaxFollowCnt() - holdingCnt;
|
}
|
|
if (symbolCnt <= 0) {
|
LogRecordUtils.insertFollowerNotice(followerSetting.getMemberId(), NoticeConstant.OPEN_ORDER_FOLLOW_FAIL_TITLE, StrUtil.format(NoticeConstant.OPEN_ORDER_FOLLOW_FAIL_CONTENT, followTraderInfoEntity.getNickname()));
|
continue;
|
}
|
}
|
|
MemberWalletContractEntity walletContract = memberWalletContractDao.findWalletContractByMemberIdAndSymbol(followerSetting.getMemberId(), CoinTypeEnum.USDT.name());
|
if (walletContract == null) {
|
log.info("钱包地址不存在:{}", followerSetting.getMemberId());
|
continue;
|
}
|
MemberLevelRateEntity levelRateEntity = memberLevelRateDao.selectLeverRateByMemberIdAndSymbol(followerSetting.getMemberId(), holdOrderEntity.getSymbol());
|
|
// 开仓手续费 建仓价*规格*手数*手续费率
|
BigDecimal openFeePrice = openPrice.multiply(lotNumber)
|
.multiply(new BigDecimal(symbolCnt))
|
.multiply(tradeSettingEntity.getFeeRatio().divide(new BigDecimal(100)))
|
.setScale(8, BigDecimal.ROUND_DOWN);
|
|
// 保证金
|
BigDecimal bondAmount = openPrice.multiply(lotNumber).multiply(new BigDecimal(symbolCnt))
|
.multiply(BigDecimal.ONE.divide(new BigDecimal(levelRateEntity.getLevelRateUp())))
|
.setScale(8, BigDecimal.ROUND_DOWN);
|
|
// 预付款
|
BigDecimal prePaymentAmount = bondAmount.add(openFeePrice).add(openFeePrice);
|
log.info("可用的余额:{}, {}", prePaymentAmount, walletContract.getAvailableBalance());
|
if (prePaymentAmount.compareTo(walletContract.getAvailableBalance()) > -1) {
|
log.info("可用金额不足");
|
LogRecordUtils.insertFollowerNotice(followerSetting.getMemberId(), NoticeConstant.MONEY_NOT_ENOUGH_TITLE, StrUtil.format(NoticeConstant.MONEY_NOT_ENOUGH_CONTENT, followTraderInfoEntity.getNickname()));
|
continue;
|
}
|
|
boolean flag = false;
|
while(true) {
|
MemberWalletContractEntity updateEntity = new MemberWalletContractEntity();
|
updateEntity.setAvailableBalance(prePaymentAmount.negate());
|
updateEntity.setTotalBalance(openFeePrice.negate());
|
updateEntity.setId(walletContract.getId());
|
updateEntity.setVersion(walletContract.getVersion());
|
log.info("==={}, {}===", walletContract.getAvailableBalance(), walletContract.getVersion());
|
int i = memberWalletContractDao.updateWalletContractWithVersion(updateEntity);
|
if (i > 0) {
|
break;
|
}
|
|
walletContract = memberWalletContractDao.findWalletContractByMemberIdAndSymbol(followerSetting.getMemberId(), CoinTypeEnum.USDT.name());
|
if (prePaymentAmount.compareTo(walletContract.getAvailableBalance()) > -1) {
|
log.info("可用金额不足");
|
LogRecordUtils.insertFollowerNotice(followerSetting.getMemberId(), NoticeConstant.MONEY_NOT_ENOUGH_TITLE, StrUtil.format(NoticeConstant.MONEY_NOT_ENOUGH_CONTENT, followTraderInfoEntity.getNickname()));
|
flag = true;
|
break;
|
}
|
|
log.info("---{}, {}--", walletContract.getAvailableBalance(), walletContract.getVersion());
|
try {
|
Thread.sleep(5000);
|
} catch (InterruptedException e) {
|
e.printStackTrace();
|
}
|
}
|
|
if (flag) {
|
continue;
|
}
|
|
MemberEntity memberEntity = memberDao.selectById(followerSetting.getMemberId());
|
// 强平价
|
BigDecimal forceClosingPrice = CalculateUtil.getForceSetPrice(bondAmount, openPrice, symbolCnt, lotNumber, holdOrderEntity.getOpeningType(), memberEntity);
|
ContractHoldOrderEntity followHoldOrder = new ContractHoldOrderEntity();
|
followHoldOrder.setMemberId(memberEntity.getId());
|
followHoldOrder.setOrderNo(commonService.generateOrderNo(memberEntity.getId()));
|
followHoldOrder.setPositionType(ContractEntrustOrderEntity.POSITION_TYPE_ADD);
|
followHoldOrder.setTradeType(ContractHoldOrderEntity.TRADE_TYPE_MARK);
|
followHoldOrder.setSymbol(holdOrderEntity.getSymbol());
|
followHoldOrder.setSymbolCnt(symbolCnt);
|
followHoldOrder.setSymbolCntSale(symbolCnt);
|
followHoldOrder.setSymbolSku(lotNumber);
|
followHoldOrder.setLeverRatio(levelRateEntity.getLevelRateUp());
|
followHoldOrder.setForceClosingPrice(forceClosingPrice);
|
followHoldOrder.setOpeningFeeAmount(openFeePrice);
|
followHoldOrder.setOpeningPrice(openPrice);
|
followHoldOrder.setOpeningType(holdOrderEntity.getOpeningType());
|
followHoldOrder.setMarkPrice(openPrice);
|
followHoldOrder.setIsCanClosing(ContractHoldOrderEntity.ORDER_CAN_CLOSING_Y);
|
followHoldOrder.setPrePaymentAmount(prePaymentAmount);
|
followHoldOrder.setBondAmount(bondAmount.add(openFeePrice));
|
followHoldOrder.setOperateNo(1);
|
// 设置合约类型
|
followHoldOrder.setContractType(ContractOrderEntity.CONTRACTTYPE_DOCUMENTARY);
|
|
ContractOrderEntity contractOrderEntity = ContractHoldOrderEntityMapper.INSTANCE.holdOrderToOrder(followHoldOrder);
|
contractOrderEntity.setOpeningTime(new Date());
|
contractHoldOrderDao.insert(followHoldOrder);
|
int i = contractOrderDao.insert(contractOrderEntity);
|
if (i > 0) {
|
// memberWalletContractDao.increaseWalletContractBalanceById(prePaymentAmount.negate(), openFeePrice.negate(), null, walletContract.getId());
|
|
FollowFollowerOrderRelationEntity relationEntity = new FollowFollowerOrderRelationEntity();
|
relationEntity.setIsShow(FollowFollowerOrderRelationEntity.IS_SHOW_Y);
|
relationEntity.setMemberId(followHoldOrder.getMemberId());
|
relationEntity.setOrderId(followHoldOrder.getId());
|
relationEntity.setOrderType(FollowFollowerOrderRelationEntity.ORDER_TYPE_HOLD);
|
relationEntity.setTradeId(followTraderInfoEntity.getId());
|
relationEntity.setTradeMemberId(followTraderInfoEntity.getMemberId());
|
relationEntity.setTradeOrderNo(holdOrderEntity.getOrderNo());
|
followFollowerOrderRelationDao.insert(relationEntity);
|
|
// 发送爆仓消息
|
sendOrderBombMsg(followHoldOrder.getId(), followHoldOrder.getOpeningType(), forceClosingPrice, followHoldOrder.getSymbol(), followHoldOrder.getOperateNo(), followHoldOrder.getMemberId());
|
|
// 计算佣金
|
ThreadPoolUtils.calReturnMoney(memberEntity.getId(), contractOrderEntity.getOpeningFeeAmount(), contractOrderEntity, AgentReturnEntity.ORDER_TYPE_OPEN);
|
|
// 插入财务流水
|
if (holdOrderEntity.getOpeningType() == ContractHoldOrderEntity.OPENING_TYPE_MORE) {
|
LogRecordUtils.insertMemberAccountFlow(memberEntity.getId(), prePaymentAmount, walletContract.getAvailableBalance().subtract(prePaymentAmount), holdOrderEntity.getSymbol(), "买涨持仓", "买涨:" + holdOrderEntity.getSymbol());
|
LogRecordUtils.insertFollowerNotice(memberEntity.getId(), NoticeConstant.OPEN_ORDER_TITLE, StrUtil.format(NoticeConstant.OPEN_ORDER_CONTENT, holdOrderEntity.getSymbol() + "开多", openPrice.setScale(2, BigDecimal.ROUND_HALF_UP).toString(), followTraderInfoEntity.getNickname()));
|
} else {
|
LogRecordUtils.insertMemberAccountFlow(memberEntity.getId(), prePaymentAmount, walletContract.getAvailableBalance().subtract(prePaymentAmount), holdOrderEntity.getSymbol(), "买跌持仓", "买跌:" + holdOrderEntity.getSymbol());
|
LogRecordUtils.insertFollowerNotice(memberEntity.getId(), NoticeConstant.OPEN_ORDER_TITLE, StrUtil.format(NoticeConstant.OPEN_ORDER_CONTENT, holdOrderEntity.getSymbol() + "开空", openPrice.setScale(2, BigDecimal.ROUND_HALF_UP).toString(), followTraderInfoEntity.getNickname()));
|
}
|
}
|
redisUtils.del(AppContants.MEMBER_HAS_FOLLOW + "_" + followerSetting.getMemberId());
|
}
|
// redisUtils.del(AppContants.MEMBER_HAS_FOLLOW);
|
}
|
}
|
|
public void sendOrderBombMsg(Long id, int type, BigDecimal forceClosingPrice, String symbol, int operateNo, Long memberId) {
|
OrderModel model = null;
|
// 开多
|
if (ContractHoldOrderEntity.OPENING_TYPE_MORE == type) {
|
model = new OrderModel(id, RabbitPriceTypeEnum.CLOSE_MORE_BOMB.getValue(), forceClosingPrice.setScale(8, RoundingMode.HALF_UP).toPlainString(), symbol, operateNo, memberId);
|
// 开空
|
} else {
|
model = new OrderModel(id, RabbitPriceTypeEnum.CLOSE_LESS_BOMB.getValue(), forceClosingPrice.setScale(8, RoundingMode.HALF_UP).toPlainString(), symbol, operateNo, memberId);
|
}
|
producer.sendPriceOperate(JSONObject.toJSONString(model));
|
}
|
|
|
@Override
|
public void closingFollowOrders(String orderNo) {
|
List<FollowFollowerOrderRelationEntity> orderRelations = followFollowerOrderRelationDao.selectFollowHoldOrderByTradeOrderNo(orderNo);
|
if (CollUtil.isNotEmpty(orderRelations)) {
|
for (FollowFollowerOrderRelationEntity orderRelation : orderRelations) {
|
List<Long> ids= new ArrayList<>();
|
ids.add(orderRelation.getOrderId());
|
rabbitOrderService.cancelHoldOrder(ids);
|
}
|
//生成返利记录和资金变化记录
|
List<FollowFollowerOrderRelationEntity> orderRelationDone = followFollowerOrderRelationDao.selectFollowOrderByTradeOrderNo(orderNo);
|
if(CollUtil.isNotEmpty(orderRelationDone)) {
|
List<Long> orderIds = new ArrayList<>();
|
for (FollowFollowerOrderRelationEntity orderRelationd : orderRelationDone) {
|
orderIds.add(orderRelationd.getOrderId());
|
}
|
|
if(CollUtil.isNotEmpty(orderIds)){
|
//获取对应的平仓记录单号
|
List<String> orderNosList = new ArrayList<>();
|
for(Long orderId : orderIds) {
|
String orderNos = contractOrderDao.selectOrderNoByOrderIds(orderId);
|
orderNosList.add(orderNos);
|
}
|
//获取总返佣
|
BigDecimal totalAmount = followTraderProfitDetailDao.selectFollowHoldOrderByFollowOrderNo(orderNosList);
|
totalAmount = (totalAmount == null?BigDecimal.ZERO:totalAmount.setScale(2, BigDecimal.ROUND_DOWN));
|
if(totalAmount.compareTo(BigDecimal.ZERO) > 0){
|
//增加返佣提醒
|
String symbol = contractOrderDao.selectById(orderIds.get(0)).getSymbol();
|
String orderNoTrader = orderRelationDone.get(0).getTradeOrderNo();
|
Long traderMemberId = orderRelationDone.get(0).getTradeMemberId();
|
LogRecordUtils.insertFollowerNotice(traderMemberId,
|
NoticeConstant.RETURN_MONEY_TITLE,
|
StrUtil.format(NoticeConstant.RETURN_MONEY_CONTENT,
|
orderNoTrader,
|
symbol,
|
totalAmount));
|
//带单返利的记录要在资产页面的其他记录
|
LogRecordUtils.insertMemberAccountMoneyChange(
|
traderMemberId,
|
StrUtil.format(NoticeConstant.RETURN_MONEY_CONTENT_MAMC,
|
symbol,
|
orderNoTrader.substring(8)),
|
totalAmount,
|
MemberWalletCoinEnum.WALLETCOINCODE.getValue(),
|
MemberAccountMoneyChange.STATUS_SUCCESS_INTEGER,
|
MemberAccountMoneyChange.TYPE_WALLET_AGENT);
|
}
|
}
|
}
|
}
|
}
|
}
|