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-admin/src/main/java/com/yami/trading/api/controller/trader/ApiTraderUserController.java |  139 +++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 124 insertions(+), 15 deletions(-)

diff --git a/trading-order-admin/src/main/java/com/yami/trading/api/controller/trader/ApiTraderUserController.java b/trading-order-admin/src/main/java/com/yami/trading/api/controller/trader/ApiTraderUserController.java
index 3622def..9269697 100644
--- a/trading-order-admin/src/main/java/com/yami/trading/api/controller/trader/ApiTraderUserController.java
+++ b/trading-order-admin/src/main/java/com/yami/trading/api/controller/trader/ApiTraderUserController.java
@@ -1,10 +1,12 @@
 package com.yami.trading.api.controller.trader;
 
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.yami.trading.bean.contract.domain.ContractOrder;
 import com.yami.trading.bean.item.domain.Item;
 import com.yami.trading.bean.trader.domain.Trader;
 import com.yami.trading.bean.trader.domain.TraderFollowUser;
+import com.yami.trading.bean.trader.domain.TraderFollowUserOrder;
 import com.yami.trading.bean.trader.domain.TraderUser;
 import com.yami.trading.common.constants.Constants;
 import com.yami.trading.common.exception.BusinessException;
@@ -20,7 +22,6 @@
 import com.yami.trading.service.trader.TraderUserService;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.apache.poi.ss.formula.functions.T;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.CrossOrigin;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -121,6 +122,7 @@
 		ResultObject resultObject = new ResultObject();
 		String type = request.getParameter("type");
 		String page_no = request.getParameter("page_no");
+		String page_size = request.getParameter("page_size");
 		try {
 			if (StringUtils.isNullOrEmpty(page_no)) {
 				page_no = "1";
@@ -131,7 +133,11 @@
 			if (Integer.valueOf(page_no).intValue() <= 0) {
 				throw new YamiShopBindException("页码不能小于等于0");
 			}
-			Page<T> page = new Page<>(1, 1000000);
+			int pageSize = 10;
+			if (!StringUtils.isNullOrEmpty(page_size) && StringUtils.isInteger(page_size)) {
+				pageSize = Math.max(1, Math.min(50, Integer.parseInt(page_size)));
+			}
+			Page<?> page = new Page<>(Integer.parseInt(page_no), pageSize);
 
 			String partyId = SecurityUtils.getCurrentUserId();
 
@@ -152,41 +158,92 @@
 
 	}
 
+	/** 交易员 SYMBOLS 可能为多品种,取第一个用于展示/查 Item */
+	private static String firstTraderSymbol(String symbolsRaw) {
+		if (symbolsRaw == null || symbolsRaw.trim().isEmpty()) {
+			return "";
+		}
+		for (String part : symbolsRaw.split("[;;,,\\s]+")) {
+			if (part != null && !part.trim().isEmpty()) {
+				return part.trim();
+			}
+		}
+		return "";
+	}
+
+	/**
+	 * 供前端 i18n 映射的常见跟单失败原因(存库仍为原文)。
+	 */
+	private static String followFailReasonKey(String reason) {
+		if (reason == null) {
+			return null;
+		}
+		String r = reason.trim();
+		if (r.isEmpty()) {
+			return null;
+		}
+		if (r.contains("余额不足")) {
+			return "INSUFFICIENT_BALANCE";
+		}
+		if (r.contains("只能选择交易员带单币种")) {
+			return "SYMBOL_NOT_IN_TRADER_LIST";
+		}
+		if (r.contains("跟单参数输入错误")) {
+			return "INVALID_FOLLOW_PARAMS";
+		}
+		if (r.contains("Insufficient balance") || r.contains("insufficient balance")) {
+			return "INSUFFICIENT_BALANCE";
+		}
+		return null;
+	}
+
 	private Map<String, Object> bulidData(TraderUser entity, String type, Page page) throws ParseException {
 
 		List<Map<String, Object>> trader_order = new ArrayList<Map<String, Object>>();
-		List<TraderFollowUser> follow_users = new ArrayList<TraderFollowUser>();
 
 		List<Map<String, Object>> follow_traders = new ArrayList<Map<String, Object>>();
-		follow_users = traderFollowUserService.findByPartyId(entity.getPartyId());
-		double folllow_trader_num = 0;
-		if (follow_users != null) {
-			folllow_trader_num = follow_users.size();
-		}
+		long folllow_trader_num = traderFollowUserService.countByPartyId(entity.getPartyId());
+		IPage<TraderFollowUser> traderUserPage = null;
 
 		/**
-		 * 跟随的交易员
+		 * 跟随的交易员(按更新时间倒序分页,最新在前)
 		 */
 		if ("trader".equals(type)) {
-			if (follow_users != null) {
-				for (TraderFollowUser user : follow_users) {
+			Page<TraderFollowUser> pg = new Page<>(page.getCurrent(), page.getSize());
+			traderUserPage = traderFollowUserService.pageByPartyId(pg, entity.getPartyId());
+			if (traderUserPage.getRecords() != null) {
+				for (TraderFollowUser user : traderUserPage.getRecords()) {
 					Trader trader = traderService.findByPartyId(user.getTraderPartyId());
-					Item item = itemService.findBySymbol(trader.getSymbols());
+					if (trader == null) {
+						continue;
+					}
+					String primarySymbol = firstTraderSymbol(trader.getSymbols());
+					Item item = StringUtils.isEmptyString(primarySymbol) ? null : itemService.findBySymbol(primarySymbol);
 					Map<String, Object> follow_trader = new HashMap<String, Object>();
 					follow_trader.put("profit", user.getProfit());
-					follow_trader.put("profitRation", BigDecimal.valueOf(user.getProfit()).divide(BigDecimal.valueOf(user.getAmountSum()), RoundingMode.HALF_UP));
+					BigDecimal profitRatio = BigDecimal.ZERO;
+					if (user.getAmountSum() > 0D) {
+						profitRatio = BigDecimal.valueOf(user.getProfit()).divide(BigDecimal.valueOf(user.getAmountSum()), 8,
+								RoundingMode.HALF_UP);
+					}
+					follow_trader.put("profitRation", profitRatio);
 					follow_trader.put("amountSum", user.getAmountSum());
 					follow_trader.put("username", trader.getName());
 					String path = Constants.WEB_URL + "/public/showimg!showImg.action?imagePath=" + trader.getImg();
 					follow_trader.put("img", path);
 					follow_trader.put("id", trader.getUuid());
-					follow_trader.put("followState", "1");
+					follow_trader.put("followState", user.getState());
+					follow_trader.put("followFailReason", user.getFailReason());
+					String failKey = followFailReasonKey(user.getFailReason());
+					follow_trader.put("follow_fail_reason_key", failKey != null ? failKey : "");
+					follow_trader.put("followLastFailTime", user.getLastFailTime());
 					follow_trader.put("followType", user.getFollowType());
 					follow_trader.put("volume", user.getVolume());
 					follow_trader.put("volumeMax", user.getVolumeMax());
+					follow_trader.put("lever_rate", user.getLeverRate());
 					follow_trader.put("followNow", trader.getFollowerNow());
 					follow_trader.put("followMax", trader.getFollowerMax());
-					follow_trader.put("symbols", item.getName());
+					follow_trader.put("symbols", item != null ? item.getName() : (StringUtils.isEmptyString(trader.getSymbols()) ? "" : trader.getSymbols()));
 					follow_traders.add(follow_trader);
 				}
 			}
@@ -202,6 +259,10 @@
 		map.put("orders", trader_order);
 		map.put("traders", follow_traders);
 		map.put("folllow_trader_num", folllow_trader_num);
+		map.put("traders_total", traderUserPage != null ? traderUserPage.getTotal() : folllow_trader_num);
+		if ("orders".equals(type) || "hisorders".equals(type)) {
+			map.put("orders_total", page.getTotal());
+		}
 
 		map.put("id", entity.getUuid());
 
@@ -219,6 +280,54 @@
 
 	}
 
+	/**
+	 * 当前跟单持仓列表。
+	 * 同时注册无 {@code !} 的路径,避免部分网关/WAF 对 {@code traderUser!positions.action} 返回 404。
+	 */
+	@RequestMapping(value = { action + "positions.action", "/api/traderUser/positions" })
+	public Object positions() {
+		ResultObject resultObject = new ResultObject();
+		String partyId = SecurityUtils.getCurrentUserId();
+		try {
+			List<ContractOrder> positions = contractOrderService.selectContractOrderByUserIdAndFollowAndState(
+					partyId, ContractOrder.ORDER_FOLLOW, ContractOrder.STATE_SUBMITTED);
+			List<Map<String, Object>> data = new ArrayList<>();
+			Map<String, String> followTraderNameCache = new HashMap<>();
+			Map<String, String> followTraderUuidCache = new HashMap<>();
+			if (positions != null) {
+				for (ContractOrder position : positions) {
+					Map<String, Object> row = contractOrderService.bulidOne(position);
+					String traderName = "";
+					String traderUuid = "";
+					if (position != null && !StringUtils.isEmptyString(position.getOrderNo())) {
+						TraderFollowUserOrder link = traderFollowUserOrderService.findByPartyIdAndOrderNo(partyId,
+								position.getOrderNo());
+						if (link != null && !StringUtils.isEmptyString(link.getTraderPartyId())) {
+							String tp = link.getTraderPartyId();
+							if (!followTraderNameCache.containsKey(tp)) {
+								Trader tr = traderService.findByPartyId(tp);
+								followTraderNameCache.put(tp, tr != null && !StringUtils.isEmptyString(tr.getName()) ? tr.getName() : "");
+								followTraderUuidCache.put(tp, tr != null && !StringUtils.isEmptyString(tr.getUuid()) ? tr.getUuid() : "");
+							}
+							traderName = followTraderNameCache.getOrDefault(tp, "");
+							traderUuid = followTraderUuidCache.getOrDefault(tp, "");
+						}
+					}
+					row.put("follow_trader_name", traderName);
+					row.put("follow_trader_id", traderUuid);
+					data.add(row);
+				}
+			}
+			resultObject.setCode("0");
+			resultObject.setData(data);
+		} catch (Exception e) {
+			resultObject.setCode("1");
+			resultObject.setMsg("程序错误");
+			logger.error("error:", e);
+		}
+		return resultObject;
+	}
+
 	public static int daysBetween(Date smdate, Date bdate) throws ParseException {
 		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
 		smdate = sdf.parse(sdf.format(smdate));

--
Gitblit v1.9.3