From befbf57e4112d07003bff18102f556a1e5a154de Mon Sep 17 00:00:00 2001
From: zj <1772600164@qq.com>
Date: Wed, 22 Apr 2026 10:53:37 +0800
Subject: [PATCH] 1

---
 trading-order-service/src/main/java/com/yami/trading/service/trader/impl/TraderFollowUserOrderServiceImpl.java |  428 +++++++++++++++++++++++++++++++++--------------------
 1 files changed, 267 insertions(+), 161 deletions(-)

diff --git a/trading-order-service/src/main/java/com/yami/trading/service/trader/impl/TraderFollowUserOrderServiceImpl.java b/trading-order-service/src/main/java/com/yami/trading/service/trader/impl/TraderFollowUserOrderServiceImpl.java
index f9f7a99..f5e66cc 100644
--- a/trading-order-service/src/main/java/com/yami/trading/service/trader/impl/TraderFollowUserOrderServiceImpl.java
+++ b/trading-order-service/src/main/java/com/yami/trading/service/trader/impl/TraderFollowUserOrderServiceImpl.java
@@ -5,23 +5,24 @@
 import com.yami.trading.bean.contract.domain.ContractApplyOrder;
 import com.yami.trading.bean.contract.domain.ContractOrder;
 import com.yami.trading.bean.model.*;
+import com.yami.trading.bean.syspara.domain.Syspara;
 import com.yami.trading.bean.trader.domain.*;
 import com.yami.trading.common.constants.Constants;
 import com.yami.trading.common.constants.RedisKeys;
 import com.yami.trading.common.util.*;
 import com.yami.trading.dao.trader.TraderFollowUserOrderMapper;
-import com.yami.trading.service.FollowMoneyLogService;
-import com.yami.trading.service.FollowWalletService;
 import com.yami.trading.service.MoneyLogService;
 import com.yami.trading.service.WalletService;
 import com.yami.trading.service.contract.ContractApplyOrderService;
 import com.yami.trading.service.contract.ContractOrderService;
 import com.yami.trading.service.syspara.SysparaService;
+import com.yami.trading.bean.trader.FollowCommissionType;
 import com.yami.trading.service.trader.*;
 import com.yami.trading.service.user.UserRecomService;
 import com.yami.trading.service.user.UserService;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
@@ -29,9 +30,17 @@
 import java.math.RoundingMode;
 import java.text.DecimalFormat;
 import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
 @Service
 public class TraderFollowUserOrderServiceImpl implements TraderFollowUserOrderService {
+	private static final ExecutorService FOLLOW_EXECUTOR = Executors.newFixedThreadPool(8);
+
+	private final ConcurrentMap<String, Boolean> followTaskKeys = new ConcurrentHashMap<>();
+
 	@Resource
 	private TraderService traderService;
 	@Resource
@@ -42,13 +51,7 @@
 	private WalletService walletService;
 
 	@Resource
-	private FollowWalletService followWalletService;
-
-	@Resource
 	private MoneyLogService moneyLogService;
-
-	@Resource
-	private FollowMoneyLogService followMoneyLogService;
 
 	@Resource
 	private TraderOrderService traderOrderService;
@@ -62,7 +65,14 @@
 
 	@Resource
 	private UserService userService;
-	
+
+	@Resource
+	private FollowCommissionService followCommissionService;
+
+	@Lazy
+	@Resource
+	private ContractOrderService contractOrderService;
+
 	private static Log logger = LogFactory.getLog(TraderFollowUserOrderServiceImpl.class);
 
 	public List<Map<String, Object>> getPaged(Page page, String partyId, String state) {
@@ -86,6 +96,12 @@
 //		parameters.put("partyId", partyId);
 //
 //		queryString.append(" order by trader_user_order.CREATE_TIME desc ");
+		Long total = traderFollowUserOrderMapper.countListDatas(partyId, state);
+		if (total != null) {
+			page.setTotal(total.longValue());
+		} else {
+			page.setTotal(0L);
+		}
 		List<Map<String, Object>> datas = traderFollowUserOrderMapper.listDatas((page.getCurrent() - 1) * page.getSize(), page.getSize(), partyId, state);
 
 		List<Map<String, Object>> data = this.bulidData(datas);
@@ -146,8 +162,10 @@
 			map.put("force_close_price", entity.get("force_close_price"));
 
 			String trader_party_id = (String) entity.get("trader_party_id");
-			User user = userService.findByUserId(trader_party_id);
-			map.put("trader_username", user.getUserName());
+			User user = trader_party_id == null ? null : userService.findByUserId(trader_party_id);
+			map.put("trader_username", user != null && user.getUserName() != null ? user.getUserName() : "");
+			Object fn = entity.get("follow_trader_name");
+			map.put("follow_trader_name", fn != null ? fn.toString() : "");
 
 			result_traders.add(map);
 		}
@@ -190,20 +208,63 @@
 	}
 
 	@Override
+	public void syncFollowUserOrderLinkAfterContractClose(ContractOrder contractOrder) {
+		if (contractOrder == null || !ContractOrder.STATE_CREATED.equals(contractOrder.getState())) {
+			return;
+		}
+		String partyId = contractOrder.getPartyId();
+		String orderNo = contractOrder.getOrderNo();
+		if (StringUtils.isEmptyString(partyId) || StringUtils.isEmptyString(orderNo)) {
+			return;
+		}
+		TraderFollowUserOrder link = findByPartyIdAndOrderNo(partyId, orderNo);
+		if (link == null) {
+			return;
+		}
+		if (!TraderFollowUserOrder.STATE_SUBMITTED.equals(link.getState())
+				&& !TraderFollowUserOrder.STATE_PROCESSING_CLOSE.equals(link.getState())) {
+			return;
+		}
+		link.setState(ContractOrder.STATE_CREATED);
+		update(link);
+	}
+
+	@Override
+	public void reconcileStaleSubmittedMappings(String partyId, String traderPartyId) {
+		if (StringUtils.isEmptyString(partyId) || StringUtils.isEmptyString(traderPartyId)) {
+			return;
+		}
+		List<TraderFollowUserOrder> list = traderFollowUserOrderMapper.selectList(
+				Wrappers.<TraderFollowUserOrder>lambdaQuery()
+						.eq(TraderFollowUserOrder::getPartyId, partyId)
+						.eq(TraderFollowUserOrder::getTraderPartyId, traderPartyId)
+						.eq(TraderFollowUserOrder::getState, TraderFollowUserOrder.STATE_SUBMITTED));
+		if (list == null || list.isEmpty()) {
+			return;
+		}
+		for (TraderFollowUserOrder link : list) {
+			if (StringUtils.isEmptyString(link.getUserOrderNo())) {
+				continue;
+			}
+			ContractOrder co = contractOrderService.findByOrderNo(link.getUserOrderNo());
+			if (co != null && ContractOrder.STATE_CREATED.equals(co.getState())) {
+				link.setState(ContractOrder.STATE_CREATED);
+				update(link);
+			}
+		}
+	}
+
+	@Override
 	public void traderOpen(ContractOrder contractOrder, ContractApplyOrderService contractApplyOrderService, ContractOrderService contractOrderService, int follow) {
 		if (isOrNotTrader(contractOrder.getPartyId())) {
-			CreateDelayThread lockDelayThread = new CreateDelayThread(contractOrder, contractApplyOrderService, contractOrderService, follow);
-			Thread t = new Thread(lockDelayThread);
-			t.start();
+			FOLLOW_EXECUTOR.submit(new CreateDelayThread(contractOrder, contractApplyOrderService, contractOrderService, follow));
 		}
 	}
 	
 	@Override
 	public void traderClose(ContractOrder contractOrder, ContractOrderService contractOrderService) {
 		if (isOrNotTrader(contractOrder.getPartyId())) {
-			CloseDelayThread lockDelayThread = new CloseDelayThread(contractOrder, contractOrderService);
-			Thread t = new Thread(lockDelayThread);
-			t.start();
+			FOLLOW_EXECUTOR.submit(new CloseDelayThread(contractOrder, contractOrderService));
 
 		}
 //			else {
@@ -228,7 +289,7 @@
 
 		public void run() {
 			try {
-				List<TraderFollowUser> users = traderFollowUserService.findByTrader_partyId(contractOrder.getPartyId()); //查找当前交易员的跟随者
+				List<TraderFollowUser> users = traderFollowUserService.findActiveByTraderPartyId(contractOrder.getPartyId()); // 查找当前交易员的有效跟随者
 				if (users != null) {
 					for (TraderFollowUser user : users) {
 						if (!"".equals(user.getPartyId())) {
@@ -236,8 +297,16 @@
 							 * 判断当前用户最多还可以买几张
 							 */
 							try {
+								String taskKey = buildTaskKey(contractOrder.getOrderNo(), user.getPartyId(), ContractApplyOrder.OFFSET_OPEN);
+								if (followTaskKeys.putIfAbsent(taskKey, Boolean.TRUE) != null) {
+									continue;
+								}
+								if (hasOpenFollowMapping(user.getPartyId(), contractOrder.getOrderNo())) {
+									followTaskKeys.remove(taskKey);
+									continue;
+								}
 								List<TraderFollowUserOrder> userOrders = findByPartyIdAndTraderPartyIdAndState(user.getPartyId(), contractOrder.getPartyId(), ContractOrder.STATE_SUBMITTED);
-								double volume_last = user.getVolumeMax();  // 跟单时设置的最大持仓张数
+								double volume_last = user.getVolumeMax();  // 跟单时设置的最大持仓币数量
 								if (userOrders != null) {
 									for (TraderFollowUserOrder userOrder : userOrders) {
 										volume_last = Arith.sub(volume_last, userOrder.getVolume());
@@ -247,6 +316,10 @@
 									continue;
 								}
 
+								if (user.getSymbol() != null && !user.getSymbol().trim().isEmpty()
+										&& !user.getSymbol().trim().equalsIgnoreCase(contractOrder.getSymbol())) {
+									continue;
+								}
 								ContractApplyOrder order = new ContractApplyOrder();
 								order.setOrderNo(DateUtil.getToday("yyMMddHHmmss") + RandomUtil.getRandomNum(8));
 								order.setPartyId(user.getPartyId());
@@ -255,28 +328,12 @@
 								order.setOffset(ContractApplyOrder.OFFSET_OPEN);
 								order.setFollow(follow); // 标记为跟单订单
 
-								/**
-								 * 跟单固定张数/固定比例---选择 1,固定张数,2,固定比例
-								 */
-								if ("1".equals(user.getFollowType())) {
-									if (volume_last < user.getVolume()) { // 剩余可下单张数小于用户设置的固定开仓单数
-										order.setVolume(new BigDecimal(volume_last));
-										order.setVolumeOpen(new BigDecimal(volume_last));
-									} else {
-										order.setVolume(BigDecimal.valueOf(user.getVolume()));
-										order.setVolumeOpen(BigDecimal.valueOf(user.getVolume()));
-									}
-								}
-								if ("2".equals(user.getFollowType())) {
-									if (volume_last < Arith.mul(contractOrder.getVolumeOpen().doubleValue(), user.getVolume())) {
-										order.setVolume(new BigDecimal(volume_last));
-										order.setVolumeOpen(new BigDecimal(volume_last));
-									} else {
-										order.setVolume(BigDecimal.valueOf(Arith.mul(contractOrder.getVolumeOpen().doubleValue(), user.getVolume())));
-										order.setVolumeOpen(BigDecimal.valueOf(Arith.mul(contractOrder.getVolumeOpen().doubleValue(), user.getVolume())));
-									}
-								}
-								order.setLeverRate(contractOrder.getLeverRate()); // 杠杆
+								double targetVolume = Math.min(volume_last, user.getVolume());
+								order.setVolume(BigDecimal.valueOf(targetVolume));
+								order.setVolumeOpen(BigDecimal.valueOf(targetVolume));
+								// 跟随者 LEVER_RATE:仅当 >0 时采用;未设置/null(库空→0)/≤0 一律默认 1 倍,不回退交易员持仓杠杆
+								double configuredLever = user.getLeverRate() > 0D ? user.getLeverRate() : 1D;
+								order.setLeverRate(BigDecimal.valueOf(configuredLever));
 								order.setPrice(contractOrder.getTradeAvgPrice()); // 永续合约交易委托价格,设置为交易员成交无效
 								order.setStopPriceProfit(contractOrder.getStopPriceProfit());
 								order.setStopPriceLoss(contractOrder.getStopPriceLoss());
@@ -331,6 +388,14 @@
 								traderFollowUserService.update(user);
 							} catch (Exception e) {
 								logger.error("TraderFollowUserOrderServiceImpl_error:", e);
+								String msg = e.getMessage();
+								if (msg == null || msg.isEmpty()) {
+									msg = e.getClass().getSimpleName();
+								}
+								traderFollowUserService.markFollowOpenFailed(user.getPartyId(), contractOrder.getPartyId(), msg);
+							} finally {
+								String taskKey = buildTaskKey(contractOrder.getOrderNo(), user.getPartyId(), ContractApplyOrder.OFFSET_OPEN);
+								followTaskKeys.remove(taskKey);
 							}
 						}
 						ThreadUtils.sleep(10);
@@ -379,12 +444,19 @@
 					if (orders != null) {
 						for (TraderFollowUserOrder order : orders) {
 							try {
+								String taskKey = buildTaskKey(contractOrder.getOrderNo(), order.getPartyId(), ContractApplyOrder.OFFSET_CLOSE);
+								if (followTaskKeys.putIfAbsent(taskKey, Boolean.TRUE) != null) {
+									continue;
+								}
 								if (ContractOrder.STATE_SUBMITTED.equals(order.getState())) {
+									order.setState(TraderFollowUserOrder.STATE_PROCESSING_CLOSE);
+									traderFollowUserOrderMapper.updateById(order);
 									ContractOrder user_contract_order = contractOrderService
 											.saveClose(order.getPartyId(), order.getUserOrderNo());
-									order.setState(ContractOrder.STATE_CREATED);
-									traderFollowUserOrderMapper.updateById(order);
-//									ApplicationUtil.executeUpdate(order);
+									if (user_contract_order == null) {
+										order.setState(TraderFollowUserOrder.STATE_SUBMITTED);
+										traderFollowUserOrderMapper.updateById(order);
+									}
 
 									if (user_contract_order != null) {
 										closeUserContractOrder(user_contract_order);
@@ -394,6 +466,8 @@
 							} catch (Exception e) {
 								logger.error("error:", e);
 							} finally {
+								String taskKey = buildTaskKey(contractOrder.getOrderNo(), order.getPartyId(), ContractApplyOrder.OFFSET_CLOSE);
+								followTaskKeys.remove(taskKey);
 							}
 							ThreadUtils.sleep(10);
 						}
@@ -426,7 +500,7 @@
 					trader_order.setCloseTime(new Date(contractOrder.getCloseTime()));
 					trader_order.setCreateTime(contractOrder.getCreateTime());
 					trader_order.setDirection(contractOrder.getDirection());
-					trader_order.setLeverRate(contractOrder.getLeverRate().doubleValue());
+					trader_order.setLeverRate(contractOrder.getLeverRate() == null ? 1D : contractOrder.getLeverRate().doubleValue());
 					trader_order.setState(contractOrder.getState());
 					trader_order.setVolumeOpen(contractOrder.getVolumeOpen().doubleValue());
 
@@ -461,70 +535,84 @@
 		 */
 
 		double follow_order_profit = 0;
-		if (traderFollowUserOrder != null && contractOrder.getProfit().doubleValue() > 0) {
-			Trader trader = traderService.findByPartyId(traderFollowUserOrder.getTraderPartyId());
-			follow_order_profit = Arith.mul(contractOrder.getProfit().doubleValue(), trader.getProfitShareRatio());
+		if (traderFollowUserOrder != null) {
+			traderFollowUserOrder.setState(contractOrder.getState());
+			update(traderFollowUserOrder);
 
-			FollowWallet wallet = followWalletService.saveWalletByPartyId(contractOrder.getPartyId());
-			double wallet_before = wallet.getMoney().doubleValue();
-			followWalletService.update(contractOrder.getPartyId(), Arith.sub(0, follow_order_profit));
+			TraderUser traderUser = traderUserService.saveTraderUserByPartyId(contractOrder.getPartyId());
+			traderUser.setProfit(Arith.add(traderUser.getProfit(), contractOrder.getProfit().doubleValue()));
+			traderUserService.update(traderUser);
 
-			FollowMoneyLog moneylog = new FollowMoneyLog();
-			moneylog.setCategory(Constants.MONEYLOG_CATEGORY_CONTRACT);
-			moneylog.setAmount_before(new BigDecimal(wallet_before));
-			moneylog.setAmount(BigDecimal.valueOf(Arith.sub(0, follow_order_profit)));
-			moneylog.setAmount_after(BigDecimal.valueOf(Arith.sub(wallet.getMoney().doubleValue(), follow_order_profit)));
-			moneylog.setLog("交易员订单号[" + traderFollowUserOrder.getTraderOrderNo() + "],跟单用户订单号["
-					+ contractOrder.getOrderNo() + "],跟单手续费[" + Arith.sub(0, follow_order_profit) + "]");
-			moneylog.setUserId(contractOrder.getPartyId());
-			moneylog.setWalletType(Constants.WALLET);
-			moneylog.setContent_type(Constants.MONEYLOG_CONTENT_FOLLOW_UP_FEE);
-
-			followMoneyLogService.save(moneylog);
-
-			Wallet wallet_trader = walletService.saveWalletByPartyId(trader.getPartyId());
-			double wallet_trader_before = wallet_trader.getMoney().doubleValue();
-			walletService.update(wallet_trader.getUserId(), follow_order_profit);
-
-			MoneyLog moneylog_trader = new MoneyLog();
-			moneylog_trader.setCategory(Constants.MONEYLOG_CATEGORY_CONTRACT);
-			moneylog_trader.setAmount_before(new BigDecimal(wallet_trader_before));
-			moneylog_trader.setAmount(new BigDecimal(follow_order_profit));
-			moneylog_trader.setAmount_after(BigDecimal.valueOf(Arith.add(wallet_trader.getMoney().doubleValue(), follow_order_profit)));
-			moneylog_trader.setLog("交易员订单号[" + traderFollowUserOrder.getTraderOrderNo() + "],跟单用户订单号["
-					+ contractOrder.getOrderNo() + "],带单手续费收益[" + follow_order_profit + "]");
-			moneylog_trader.setUserId(wallet_trader.getUserId());
-			moneylog_trader.setWalletType(Constants.WALLET);
-			moneylog_trader.setContent_type(Constants.MONEYLOG_CONTENT_FOLLOW_UP_FEE);
-
-			moneyLogService.save(moneylog_trader);
-
-			/**
-			 * 检查是否是跟单订单,如果是需要将TraderFollowUserOrder里的订单状态修改
-			 */
-
-			if (traderFollowUserOrder != null) {
-				traderFollowUserOrder.setState(contractOrder.getState());
-				update(traderFollowUserOrder);
-
-				/**
-				 * 将收益加入用户跟随累计
-				 */
-				TraderUser traderUser = traderUserService.saveTraderUserByPartyId(contractOrder.getPartyId());
-				traderUser.setProfit(Arith.add(traderUser.getProfit(), contractOrder.getProfit().doubleValue()));
-				traderUserService.update(traderUser);
-
-				TraderFollowUser traderFollowUser = traderFollowUserService.findByPartyIdAndTrader_partyId(
-						traderFollowUserOrder.getPartyId(),
-						traderFollowUserOrder.getTraderPartyId());
-				/**
-				 * 给用户跟随表添加累计金额
-				 */
+			TraderFollowUser traderFollowUser = traderFollowUserService.findByPartyIdAndTrader_partyId(
+					traderFollowUserOrder.getPartyId(),
+					traderFollowUserOrder.getTraderPartyId());
+			if (traderFollowUser != null) {
 				traderFollowUser.setProfit(Arith.add(traderFollowUser.getProfit(), contractOrder.getProfit().doubleValue()));
 				traderFollowUserService.update(traderFollowUser);
-
 			}
-			saveProfitBounsHandle(contractOrder);
+		}
+
+		if (traderFollowUserOrder != null) {
+			Trader trader = traderService.findByPartyId(traderFollowUserOrder.getTraderPartyId());
+			String commissionType = FollowCommissionType.normalizeOrLegacy(trader.getFollowCommissionType());
+			long closeSec = contractOrder.getCloseTime() != null && contractOrder.getCloseTime() > 0
+					? contractOrder.getCloseTime()
+					: System.currentTimeMillis() / 1000L;
+			if (FollowCommissionType.isDailyProfitPct(commissionType)) {
+				followCommissionService.accumulateDailyRealizedPnl(contractOrder.getPartyId().toString(),
+						traderFollowUserOrder.getTraderPartyId(), contractOrder.getProfit(), closeSec);
+			} else if (FollowCommissionType.isLegacy(commissionType) && contractOrder.getProfit().doubleValue() > 0) {
+				follow_order_profit = Arith.mul(contractOrder.getProfit().doubleValue(), trader.getProfitShareRatio());
+
+				Wallet wallet = walletService.saveWalletByPartyId(contractOrder.getPartyId());
+				double wallet_before = wallet.getMoney().doubleValue();
+				walletService.update(contractOrder.getPartyId(), Arith.sub(0, follow_order_profit));
+
+				String sym = contractOrder.getSymbol() == null ? "" : contractOrder.getSymbol().trim();
+				if (sym.isEmpty()) {
+					sym = "-";
+				}
+				String traderName = StringUtils.isEmptyString(trader.getName()) ? trader.getPartyId() : trader.getName().trim();
+				double sharePct = Arith.mul(trader.getProfitShareRatio(), 100D);
+				String feeStr = BigDecimal.valueOf(follow_order_profit).stripTrailingZeros().toPlainString();
+				String followerLog = String.format(
+						"[跟单佣金-盈利分润(经典模式)]交易对:%s|跟单用户平仓盈利分成|分润比例:%.4f%%|交易员委托单:%s|跟单持仓单:%s|主钱包扣款:USDT %s|带单员:%s",
+						sym, sharePct, traderFollowUserOrder.getTraderOrderNo(), contractOrder.getOrderNo(), feeStr, traderName);
+				String traderLog = String.format(
+						"[跟单佣金-盈利分润(经典模式)]交易对:%s|带单员分润入账|来源跟单用户平仓盈利|分润比例:%.4f%%|跟单持仓单:%s|对应交易员委托:%s|主钱包入账:USDT %s",
+						sym, sharePct, contractOrder.getOrderNo(), traderFollowUserOrder.getTraderOrderNo(), feeStr);
+
+				MoneyLog moneylog = new MoneyLog();
+				moneylog.setCategory(Constants.MONEYLOG_CATEGORY_CONTRACT);
+				moneylog.setAmountBefore(new BigDecimal(wallet_before));
+				moneylog.setAmount(BigDecimal.valueOf(Arith.sub(0, follow_order_profit)));
+				moneylog.setAmountAfter(BigDecimal.valueOf(Arith.sub(wallet.getMoney().doubleValue(), follow_order_profit)));
+				moneylog.setLog(followerLog + "|账变:跟随者主钱包扣款");
+				moneylog.setUserId(contractOrder.getPartyId());
+				moneylog.setWalletType(Constants.WALLET);
+				moneylog.setSymbol(Constants.WALLET_USDT);
+				moneylog.setContentType(Constants.MONEYLOG_CONTENT_FOLLOW_UP_FEE);
+
+				moneyLogService.save(moneylog);
+
+				Wallet wallet_trader = walletService.saveWalletByPartyId(trader.getPartyId());
+				double wallet_trader_before = wallet_trader.getMoney().doubleValue();
+				walletService.update(wallet_trader.getUserId(), follow_order_profit);
+
+				MoneyLog moneylog_trader = new MoneyLog();
+				moneylog_trader.setCategory(Constants.MONEYLOG_CATEGORY_CONTRACT);
+				moneylog_trader.setAmountBefore(new BigDecimal(wallet_trader_before));
+				moneylog_trader.setAmount(new BigDecimal(follow_order_profit));
+				moneylog_trader.setAmountAfter(BigDecimal.valueOf(Arith.add(wallet_trader.getMoney().doubleValue(), follow_order_profit)));
+				moneylog_trader.setLog(traderLog + "|账变:带单员主钱包入账");
+				moneylog_trader.setUserId(wallet_trader.getUserId());
+				moneylog_trader.setWalletType(Constants.WALLET);
+				moneylog_trader.setSymbol(Constants.WALLET_USDT);
+				moneylog_trader.setContentType(Constants.MONEYLOG_CONTENT_FOLLOW_UP_FEE);
+
+				moneyLogService.save(moneylog_trader);
+				saveProfitBounsHandle(contractOrder);
+			}
 		}
 	}
 
@@ -540,6 +628,7 @@
 		return null;
 	}
 
+	@Override
 	public List<TraderFollowUserOrder> findByPartyIdAndTraderPartyIdAndState(String partyId, String trader_partyId,
 			String state) {
 //		StringBuffer queryString = new StringBuffer(
@@ -568,73 +657,89 @@
 		return null;
 	}
 
+	private boolean hasOpenFollowMapping(String partyId, String traderOrderNo) {
+		List<TraderFollowUserOrder> list = traderFollowUserOrderMapper.selectList(
+				Wrappers.<TraderFollowUserOrder>lambdaQuery()
+						.eq(TraderFollowUserOrder::getPartyId, partyId)
+						.eq(TraderFollowUserOrder::getTraderOrderNo, traderOrderNo)
+						.in(TraderFollowUserOrder::getState,
+								TraderFollowUserOrder.STATE_SUBMITTED,
+								TraderFollowUserOrder.STATE_PROCESSING_CLOSE));
+		return list != null && !list.isEmpty();
+	}
+
+	private String buildTaskKey(String traderOrderNo, String followerPartyId, String actionType) {
+		return traderOrderNo + ":" + followerPartyId + ":" + actionType;
+	}
+
 	/**
 	 * 跟单产生手续费,奖励给推荐人
 	 * 
 	 * @param entity
 	 */
 	public void saveFeeBounsHandle(ContractApplyOrder entity) {
-		List<UserRecom> recom_parents = userRecomService.getParents(entity.getPartyId());
-		if (recom_parents == null) {
-			return;
-		}
-		if (recom_parents.isEmpty()) {
-			return;
-		}
-		/**
-		 * 上级为空则直接结束
-		 */
-
-		if ("".equals(recom_parents.get(0).getRecomUserId()) || recom_parents.get(0).getRecomUserId() == null) {
-			return;
-		}
-
-		/**
-		 * 获取数据库奖金分成比例
-		 */
-//		String trade_follow_bonus_parameters = sysparaService.find("trade_follow_bonus_parameters").getValue();
-		String trade_follow_bonus_parameters = sysparaService.find("trade_follow_bonus_parameters").getSvalue();
-		String[] trade_follow_bonus_array = trade_follow_bonus_parameters.split(",");
-
-		/**
-		 * 判断有几个父级代理,最多不超过3个有奖励
-		 */
-		for (int i = 0; i < recom_parents.size(); i++) {
-			if (i >= 3) {
+		try {
+			List<UserRecom> recom_parents = userRecomService.getParents(entity.getPartyId());
+			if (recom_parents == null) {
 				return;
 			}
-			/**
-			 * 邀请人是正式用户和演示用户才加奖金
-			 */
-			User party = new User();
-			party = userService.cacheUserBy(recom_parents.get(i).getRecomUserId());
-			if (!"MEMBER".equals(party.getRoleName()) && !"GUEST".equals(party.getRoleName())) {
-				continue;
+			if (recom_parents.isEmpty()) {
+				return;
 			}
-			double pip_amount = Double.parseDouble(trade_follow_bonus_array[i]);
-			double get_money = Arith.mul(entity.getFee().doubleValue(), pip_amount);
+			if ("".equals(recom_parents.get(0).getRecomUserId()) || recom_parents.get(0).getRecomUserId() == null) {
+				return;
+			}
 
-			Wallet wallet = walletService.saveWalletByPartyId(recom_parents.get(i).getRecomUserId());
-			double amount_before = wallet.getMoney().doubleValue();
-//				wallet.setMoney(Arith.add(wallet.getMoney(), get_money));
-			walletService.update(wallet.getUserId(), get_money);
+			Syspara bonusPara = sysparaService.find("trade_follow_bonus_parameters");
+			if (bonusPara == null || StringUtils.isEmptyString(bonusPara.getSvalue())) {
+				logger.warn("saveFeeBounsHandle: syspara trade_follow_bonus_parameters missing or empty, skip");
+				return;
+			}
+			String trade_follow_bonus_parameters = bonusPara.getSvalue().trim();
+			String[] trade_follow_bonus_array = trade_follow_bonus_parameters.split(",");
+			if (trade_follow_bonus_array.length == 0) {
+				return;
+			}
 
-			/**
-			 * 保存资金日志
-			 */
-			MoneyLog moneyLog = new MoneyLog();
-			moneyLog.setCategory(Constants.MONEYLOG_CATEGORY_REWARD);
-			moneyLog.setAmount_before(new BigDecimal(amount_before));
-			moneyLog.setAmount(new BigDecimal(get_money));
-			moneyLog.setAmount_after(BigDecimal.valueOf(Arith.add(wallet.getMoney().doubleValue(), get_money)));
-			moneyLog.setLog("第" + (i + 1) + "代用户跟单产生了交易,手续费奖励[" + get_money + "]");
-			moneyLog.setUserId(recom_parents.get(i).getRecomUserId());
-			moneyLog.setWalletType(Constants.WALLET);
-			moneyLog.setContent_type(Constants.MONEYLOG_CONTENT_REWARD);
-			moneyLogService.save(moneyLog);
+			for (int i = 0; i < recom_parents.size(); i++) {
+				if (i >= 3) {
+					return;
+				}
+				if (i >= trade_follow_bonus_array.length) {
+					logger.warn("saveFeeBounsHandle: bonus ratio array shorter than parent index " + i + ", skip rest");
+					return;
+				}
+				User party = userService.cacheUserBy(recom_parents.get(i).getRecomUserId());
+				if (party == null || (!"MEMBER".equals(party.getRoleName()) && !"GUEST".equals(party.getRoleName()))) {
+					continue;
+				}
+				String ratioStr = trade_follow_bonus_array[i] == null ? "" : trade_follow_bonus_array[i].trim();
+				if (ratioStr.isEmpty()) {
+					continue;
+				}
+				double pip_amount = Double.parseDouble(ratioStr);
+				double get_money = Arith.mul(entity.getFee().doubleValue(), pip_amount);
 
+				Wallet wallet = walletService.saveWalletByPartyId(recom_parents.get(i).getRecomUserId());
+				double amount_before = wallet.getMoney().doubleValue();
+				walletService.update(wallet.getUserId(), get_money);
+
+				MoneyLog moneyLog = new MoneyLog();
+				moneyLog.setCategory(Constants.MONEYLOG_CATEGORY_REWARD);
+				moneyLog.setAmountBefore(new BigDecimal(amount_before));
+				moneyLog.setAmount(new BigDecimal(get_money));
+				moneyLog.setAmountAfter(BigDecimal.valueOf(Arith.add(wallet.getMoney().doubleValue(), get_money)));
+				moneyLog.setLog("第" + (i + 1) + "代用户跟单产生了交易,手续费奖励[" + get_money + "]");
+				moneyLog.setUserId(recom_parents.get(i).getRecomUserId());
+				moneyLog.setWalletType(Constants.WALLET);
+				moneyLog.setSymbol(Constants.WALLET_USDT);
+				moneyLog.setContentType(Constants.MONEYLOG_CONTENT_REWARD);
+				moneyLogService.save(moneyLog);
+			}
+		} catch (Exception e) {
+			logger.error("saveFeeBounsHandle failed (ignored so follow open is not rolled into markFollowOpenFailed), orderNo="
+					+ (entity != null ? entity.getOrderNo() : "null"), e);
 		}
-
 	}
 
 	/**
@@ -693,13 +798,14 @@
 			 */
 			MoneyLog moneyLog = new MoneyLog();
 			moneyLog.setCategory(Constants.MONEYLOG_CATEGORY_REWARD);
-			moneyLog.setAmount_before(new BigDecimal(amount_before));
+			moneyLog.setAmountBefore(new BigDecimal(amount_before));
 			moneyLog.setAmount(new BigDecimal(get_money));
-			moneyLog.setAmount_after(BigDecimal.valueOf(Arith.add(wallet.getMoney().doubleValue(), get_money)));
+			moneyLog.setAmountAfter(BigDecimal.valueOf(Arith.add(wallet.getMoney().doubleValue(), get_money)));
 			moneyLog.setLog("第" + (i + 1) + "代用户跟单产生了交易,分红奖励[" + get_money + "]");
 			moneyLog.setUserId(recom_parents.get(i).getRecomUserId());
 			moneyLog.setWalletType(Constants.WALLET);
-			moneyLog.setContent_type(Constants.MONEYLOG_CONTENT_REWARD);
+			moneyLog.setSymbol(Constants.WALLET_USDT);
+			moneyLog.setContentType(Constants.MONEYLOG_CONTENT_REWARD);
 			moneyLogService.save(moneyLog);
 
 		}

--
Gitblit v1.9.3