| | |
| | | |
| | | import cn.hutool.core.util.StrUtil; |
| | | 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.model.User; |
| | | import com.yami.trading.common.domain.BaseEntity; |
| | | import com.yami.trading.bean.trader.FollowCommissionType; |
| | | import com.yami.trading.bean.trader.domain.Trader; |
| | | import com.yami.trading.bean.trader.domain.TraderFollowSetting; |
| | | import com.yami.trading.bean.trader.domain.TraderFollowUser; |
| | | import com.yami.trading.bean.trader.domain.TraderFollowUserOrder; |
| | | import com.yami.trading.bean.trader.domain.TraderInviteLink; |
| | | import com.yami.trading.common.constants.Constants; |
| | | import com.yami.trading.common.exception.BusinessException; |
| | |
| | | import com.yami.trading.common.web.ResultObject; |
| | | import com.yami.trading.security.common.util.SecurityUtils; |
| | | import com.yami.trading.service.contract.ContractOrderService; |
| | | import com.yami.trading.service.item.ItemService; |
| | | import com.yami.trading.service.trader.*; |
| | | import com.yami.trading.service.user.UserService; |
| | | import org.apache.commons.logging.Log; |
| | |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.time.YearMonth; |
| | | import java.time.ZoneId; |
| | | import java.time.temporal.ChronoUnit; |
| | | import java.text.DecimalFormat; |
| | | import java.text.ParseException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.time.Instant; |
| | | import java.util.*; |
| | | import java.util.regex.Pattern; |
| | | |
| | | |
| | | |
| | |
| | | private TraderFollowUserService traderFollowUserService; |
| | | |
| | | @Autowired |
| | | private TraderFollowUserOrderService traderFollowUserOrderService; |
| | | |
| | | @Autowired |
| | | private TraderOrderService traderOrderService; |
| | | |
| | | @Autowired |
| | |
| | | |
| | | @Autowired |
| | | private UserService userService; |
| | | |
| | | @Autowired |
| | | private ItemService itemService; |
| | | |
| | | @Autowired |
| | | private TraderFollowSettingService traderFollowSettingService; |
| | |
| | | for (int i = 0; i < data.size(); i++) { |
| | | Map<String, Object> map = data.get(i); |
| | | String partyId = SecurityUtils.getCurrentUserId(); |
| | | Object commissionTypeForRemaining = map.get("follow_commission_type"); |
| | | if (partyId != null) { |
| | | TraderFollowUser user = this.traderFollowUserService.findByPartyIdAndTrader_partyId(partyId, map.get("partyId").toString()); |
| | | if (user != null) { |
| | | /** |
| | | * 1跟随 2未跟随 |
| | | */ |
| | | map.put("follow_state", "1"); |
| | | map.put("follow_state", user.getState()); |
| | | map.put("follow_volume", user.getVolume()); |
| | | map.put("follow_volume_max", user.getVolumeMax()); |
| | | map.put("follow_monthly_remaining_days", |
| | | computeFollowMonthlyRemainingDays(commissionTypeForRemaining, user)); |
| | | map.remove("partyId"); |
| | | } else { |
| | | map.put("follow_state", "2"); |
| | | map.put("follow_monthly_remaining_days", null); |
| | | map.remove("partyId"); |
| | | } |
| | | |
| | | } else { |
| | | map.put("follow_state", "2"); |
| | | map.put("follow_monthly_remaining_days", null); |
| | | map.remove("partyId"); |
| | | } |
| | | } |
| | |
| | | @RequestMapping(action + "istrader.action") |
| | | public Object istrader(HttpServletRequest request) { |
| | | ResultObject resultObject = new ResultObject(); |
| | | String id = request.getParameter("id"); |
| | | try { |
| | | String id = request.getParameter("id"); |
| | | |
| | | Trader data = null; |
| | | if(StringUtils.isNotEmpty(id)) { |
| | | data = traderService.findById(id); |
| | | } else{ |
| | | String partyId = SecurityUtils.getCurrentUserId(); |
| | | data = traderService.findByPartyId(partyId); |
| | | } |
| | | if(null == data) { |
| | | Trader data = null; |
| | | if (StringUtils.isNotEmpty(id)) { |
| | | data = traderService.findById(id); |
| | | } else { |
| | | String partyId = SecurityUtils.getCurrentUserId(); |
| | | if (StringUtils.isEmptyString(partyId)) { |
| | | resultObject.setCode("1"); |
| | | resultObject.setMsg("用户未登录"); |
| | | Map<String, Object> empty = new HashMap<>(); |
| | | empty.put("exists", false); |
| | | empty.put("checked", null); |
| | | empty.put("can_reapply", false); |
| | | empty.put("reject_reason", ""); |
| | | empty.put("name", ""); |
| | | empty.put("symbols", ""); |
| | | empty.put("follow_volumn_min", 0d); |
| | | empty.put("follow_commission_type", FollowCommissionType.LEGACY); |
| | | empty.put("follow_commission_monthly_amount", "0"); |
| | | empty.put("follow_commission_daily_pct", 0d); |
| | | empty.put("profit_share_ratio", 0d); |
| | | empty.put("id", ""); |
| | | empty.put("img", ""); |
| | | empty.put("img_path", ""); |
| | | resultObject.setData(empty); |
| | | return resultObject; |
| | | } |
| | | data = traderService.findByPartyId(partyId); |
| | | } |
| | | Map<String, Object> ret = new HashMap<>(); |
| | | ret.put("exists", data != null); |
| | | ret.put("checked", data == null ? null : data.getChecked()); |
| | | ret.put("can_reapply", data != null && data.getChecked() == -1); |
| | | ret.put("reject_reason", data != null && data.getChecked() == -1 ? data.getRemarks() : ""); |
| | | ret.put("name", data == null ? "" : data.getName()); |
| | | ret.put("symbols", data == null ? "" : data.getSymbols()); |
| | | ret.put("follow_volumn_min", data == null || data.getFollowVolumnMin() == null ? 0d : data.getFollowVolumnMin().doubleValue()); |
| | | if (data != null) { |
| | | ret.put("follow_commission_type", FollowCommissionType.normalizeOrLegacy(data.getFollowCommissionType())); |
| | | ret.put("follow_commission_monthly_amount", |
| | | data.getFollowCommissionMonthlyAmount() == null ? "0" |
| | | : data.getFollowCommissionMonthlyAmount().stripTrailingZeros().toPlainString()); |
| | | ret.put("follow_commission_daily_pct", Arith.mul(data.getFollowCommissionDailyPct(), 100)); |
| | | ret.put("profit_share_ratio", Arith.mul(data.getProfitShareRatio(), 100)); |
| | | } else { |
| | | ret.put("follow_commission_type", FollowCommissionType.LEGACY); |
| | | ret.put("follow_commission_monthly_amount", "0"); |
| | | ret.put("follow_commission_daily_pct", 0d); |
| | | ret.put("profit_share_ratio", 0d); |
| | | } |
| | | ret.put("id", data == null ? "" : data.getUuid()); |
| | | if (data != null && !StringUtils.isEmptyString(data.getImg())) { |
| | | ret.put("img", Constants.WEB_URL + "/public/showimg!showImg.action?imagePath=" + data.getImg()); |
| | | ret.put("img_path", data.getImg()); |
| | | } else { |
| | | ret.put("img", ""); |
| | | ret.put("img_path", ""); |
| | | } |
| | | resultObject.setData(ret); |
| | | resultObject.setCode("0"); |
| | | return resultObject; |
| | | } catch (Exception e) { |
| | | logger.error("istrader error", e); |
| | | resultObject.setCode("1"); |
| | | resultObject.setData(false); |
| | | resultObject.setMsg(e.getMessage() != null ? e.getMessage() : "程序错误"); |
| | | Map<String, Object> err = new HashMap<>(); |
| | | err.put("exists", false); |
| | | err.put("checked", null); |
| | | err.put("can_reapply", false); |
| | | err.put("reject_reason", ""); |
| | | err.put("name", ""); |
| | | err.put("symbols", ""); |
| | | err.put("follow_volumn_min", 0d); |
| | | err.put("follow_commission_type", FollowCommissionType.LEGACY); |
| | | err.put("follow_commission_monthly_amount", "0"); |
| | | err.put("follow_commission_daily_pct", 0d); |
| | | err.put("profit_share_ratio", 0d); |
| | | err.put("id", ""); |
| | | err.put("img", ""); |
| | | err.put("img_path", ""); |
| | | resultObject.setData(err); |
| | | return resultObject; |
| | | } |
| | | |
| | | resultObject.setData(true); |
| | | resultObject.setCode("0"); |
| | | return resultObject; |
| | | } |
| | | |
| | | @RequestMapping(action + "get.action") |
| | | /** |
| | | * 交易员详情(本人或指定 id)。同时注册 {@code /api/trader/get},避免网关对 {@code !} 路径返回 404。 |
| | | */ |
| | | @RequestMapping(value = { action + "get.action", "/api/trader/get" }) |
| | | public Object get(HttpServletRequest request) { |
| | | ResultObject resultObject = new ResultObject(); |
| | | String id = request.getParameter("id"); |
| | |
| | | } |
| | | Page<Trader> page = new Page<>(1, 1000000); |
| | | Trader data = null; |
| | | if(StringUtils.isNotEmpty(id)) { |
| | | if (StringUtils.isNotEmpty(id)) { |
| | | data = traderService.findById(id); |
| | | } else{ |
| | | if (null == data) { |
| | | resultObject.setCode("1"); |
| | | resultObject.setMsg("交易员不存在"); |
| | | return resultObject; |
| | | } |
| | | } else { |
| | | String partyId = SecurityUtils.getCurrentUserId(); |
| | | if (StringUtils.isEmptyString(partyId)) { |
| | | resultObject.setCode("1"); |
| | | resultObject.setMsg("用户未登录"); |
| | | return resultObject; |
| | | } |
| | | data = traderService.findByPartyId(partyId); |
| | | } |
| | | if(null == data) { |
| | | resultObject.setCode("1"); |
| | | resultObject.setMsg("交易员不存在"); |
| | | return resultObject; |
| | | if (null == data) { |
| | | resultObject.setCode("1"); |
| | | /** 未传 id 时按当前登录用户查 T_TRADER;无记录表示从未申请或数据未写入,与「传错 uuid」区分 */ |
| | | resultObject.setMsg("当前账号暂无交易员记录,请先提交申请或确认登录账号是否正确"); |
| | | return resultObject; |
| | | } |
| | | } |
| | | |
| | | Map<String, Object> retData = bulidData(data, type, symbol, page); |
| | |
| | | return remain; |
| | | } |
| | | |
| | | /** 带单品种分隔:分号、逗号(含全角) */ |
| | | private static final Pattern TRADER_SYMBOL_SPLIT = Pattern.compile("[;;,,]+"); |
| | | |
| | | /** |
| | | * 校验带单品种是否在系统合约品种(Item)中存在;支持多品种(与 T_TRADER.SYMBOLS 约定一致,用分号拼接规范 symbol)。 |
| | | * 匹配顺序:symbol(含缓存/remarks 映射)→ 小写 symbol → 展示名 name(兼容旧数据)。 |
| | | */ |
| | | private String validateAndNormalizeTraderSymbols(String raw) throws BusinessException { |
| | | if (StringUtils.isEmptyString(raw)) { |
| | | throw new BusinessException("请输入带单品种"); |
| | | } |
| | | String trimmed = raw.trim(); |
| | | String[] tokens = TRADER_SYMBOL_SPLIT.split(trimmed); |
| | | LinkedHashSet<String> seen = new LinkedHashSet<>(); |
| | | List<String> normalized = new ArrayList<>(); |
| | | for (String token : tokens) { |
| | | if (token == null) { |
| | | continue; |
| | | } |
| | | String part = token.trim(); |
| | | if (part.isEmpty()) { |
| | | continue; |
| | | } |
| | | Item item = itemService.findBySymbol(part); |
| | | if (item == null) { |
| | | item = itemService.findBySymbol(part.toLowerCase(Locale.ROOT)); |
| | | } |
| | | if (item == null) { |
| | | item = itemService.lambdaQuery() |
| | | .eq(Item::getName, part) |
| | | .eq(BaseEntity::getDelFlag, 0) |
| | | .last("LIMIT 1") |
| | | .one(); |
| | | } |
| | | if (item == null || StringUtils.isEmptyString(item.getSymbol())) { |
| | | throw new BusinessException("带单品种无效或不支持:" + part); |
| | | } |
| | | String canon = item.getSymbol(); |
| | | if (seen.add(canon)) { |
| | | normalized.add(canon); |
| | | } |
| | | } |
| | | if (normalized.isEmpty()) { |
| | | throw new BusinessException("请输入带单品种"); |
| | | } |
| | | return String.join(";", normalized); |
| | | } |
| | | |
| | | @RequestMapping(action + "apply.action") |
| | | public Object apply(HttpServletRequest request) { |
| | | ResultObject resultObject = new ResultObject(); |
| | | String partyId = SecurityUtils.getCurrentUserId(); |
| | | |
| | | String symbols = request.getParameter("symbols"); |
| | | if (symbols != null) { |
| | | symbols = symbols.trim(); |
| | | } |
| | | String name = request.getParameter("name"); |
| | | |
| | | String follow_volumn_min_param = request.getParameter("follow_volumn_min"); |
| | | int follow_volumn_min = StringUtils.isEmptyString(follow_volumn_min_param)?0:Integer.parseInt(follow_volumn_min_param); |
| | | |
| | | String state = "1"; |
| | | String img = request.getParameter("img"); |
| | | String remarks = request.getParameter("remarks"); |
| | | |
| | | try { |
| | | BigDecimal follow_volumn_min = BigDecimal.ZERO; |
| | | if (!StringUtils.isEmptyString(follow_volumn_min_param)) { |
| | | try { |
| | | follow_volumn_min = new BigDecimal(follow_volumn_min_param.trim()); |
| | | } catch (NumberFormatException e) { |
| | | throw new BusinessException("最小跟单币数量格式不正确"); |
| | | } |
| | | } |
| | | if (follow_volumn_min.signum() < 0) { |
| | | throw new BusinessException("最小跟单币数量不能小于0"); |
| | | } |
| | | if (StringUtils.isEmptyString(remarks)) { |
| | | throw new BusinessException("交易员简介不能为空"); |
| | | } |
| | | |
| | | User party = userService.findByUserId(partyId); |
| | | |
| | | if (party == null) { |
| | |
| | | if (Constants.SECURITY_ROLE_TEST.equals(party.getRoleName())) { |
| | | throw new BusinessException("试用用户无法添加!"); |
| | | } |
| | | symbols = validateAndNormalizeTraderSymbols(symbols); |
| | | Trader exist = traderService.findByPartyId(partyId); |
| | | if (exist != null) { |
| | | throw new BusinessException("交易员已存在!"); |
| | | |
| | | if (exist != null && exist.getChecked() != -1) { |
| | | throw new BusinessException("Trader application already exists!"); |
| | | } |
| | | Trader trader = new Trader(); |
| | | trader.setUuid(ApplicationUtil.getCurrentTimeUUID()); |
| | | trader.setPartyId(partyId); |
| | | Trader trader = exist == null ? new Trader() : exist; |
| | | if (exist == null) { |
| | | trader.setUuid(ApplicationUtil.getCurrentTimeUUID()); |
| | | trader.setPartyId(partyId); |
| | | trader.setCreateTime(new Date()); |
| | | } |
| | | trader.setName(StringUtils.isEmptyString(name)?party.getUserName():name); |
| | | trader.setSymbols(symbols); |
| | | |
| | | trader.setState(state); |
| | | trader.setCreateTime(new Date()); |
| | | trader.setImg(img); |
| | | trader.setFollowVolumnMin(follow_volumn_min); |
| | | trader.setRemarks(remarks); |
| | | trader.setFollowVolumnMin(follow_volumn_min.stripTrailingZeros()); |
| | | trader.setChecked(0); |
| | | |
| | | traderService.save(trader); |
| | | String fcType = request.getParameter("follow_commission_type"); |
| | | String fcMonthly = request.getParameter("follow_commission_monthly_amount"); |
| | | String fcDaily = request.getParameter("follow_commission_daily_pct"); |
| | | String normalizedType = FollowCommissionType.normalizeOrLegacy(fcType); |
| | | trader.setFollowCommissionType(normalizedType); |
| | | if (FollowCommissionType.isMonthlyFixed(normalizedType)) { |
| | | BigDecimal m = BigDecimal.ZERO; |
| | | if (!StringUtils.isEmptyString(fcMonthly)) { |
| | | try { |
| | | m = new BigDecimal(fcMonthly.trim()); |
| | | } catch (NumberFormatException e) { |
| | | throw new BusinessException("月跟单费金额格式不正确"); |
| | | } |
| | | } |
| | | if (m.compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw new BusinessException("按月跟单模式须填写大于 0 的月费用"); |
| | | } |
| | | trader.setFollowCommissionMonthlyAmount(m); |
| | | trader.setFollowCommissionDailyPct(0D); |
| | | } else if (FollowCommissionType.isDailyProfitPct(normalizedType)) { |
| | | double pct = 0D; |
| | | if (!StringUtils.isEmptyString(fcDaily)) { |
| | | try { |
| | | pct = Double.parseDouble(fcDaily.trim()); |
| | | } catch (NumberFormatException e) { |
| | | throw new BusinessException("按日利润提成比例格式不正确"); |
| | | } |
| | | } |
| | | if (pct <= 0 || pct > 100) { |
| | | throw new BusinessException("按日利润提成比例须在 0~100 之间"); |
| | | } |
| | | trader.setFollowCommissionDailyPct(Arith.div(pct, 100)); |
| | | trader.setFollowCommissionMonthlyAmount(BigDecimal.ZERO); |
| | | } else { |
| | | trader.setFollowCommissionMonthlyAmount(BigDecimal.ZERO); |
| | | trader.setFollowCommissionDailyPct(0D); |
| | | } |
| | | |
| | | String profitShareParam = request.getParameter("profit_share_ratio"); |
| | | if (FollowCommissionType.isLegacy(normalizedType)) { |
| | | if (StringUtils.isEmptyString(profitShareParam)) { |
| | | throw new BusinessException("单笔盈利分成须填写利润分成比例(0~100,单位:%)"); |
| | | } |
| | | double psPct; |
| | | try { |
| | | psPct = Double.parseDouble(profitShareParam.trim()); |
| | | } catch (NumberFormatException e) { |
| | | throw new BusinessException("利润分成比例格式不正确"); |
| | | } |
| | | if (psPct <= 0 || psPct > 100) { |
| | | throw new BusinessException("利润分成比例须在 0~100 之间(单位:%)"); |
| | | } |
| | | trader.setProfitShareRatio(Arith.div(psPct, 100)); |
| | | } else { |
| | | trader.setProfitShareRatio(0D); |
| | | } |
| | | |
| | | if (exist == null) { |
| | | traderService.save(trader); |
| | | } else { |
| | | traderService.update(trader); |
| | | } |
| | | |
| | | |
| | | |
| | |
| | | } catch (BusinessException e) { |
| | | resultObject.setCode("1"); |
| | | resultObject.setMsg(e.getMessage()); |
| | | } catch (NumberFormatException e) { |
| | | resultObject.setCode("1"); |
| | | resultObject.setMsg("最小跟单币数量格式不正确"); |
| | | } catch (Throwable t) { |
| | | logger.error("UserAction.register error ", t); |
| | | resultObject.setCode("1"); |
| | |
| | | @RequestMapping("showfollowsetting.action") |
| | | public Object show_follow_setting(HttpServletRequest request){ |
| | | ResultObject resultObject = new ResultObject(); |
| | | String partyId = SecurityUtils.getCurrentUserId(); |
| | | |
| | | TraderFollowSetting traderFollowSetting = traderFollowSettingService.findByPartyId(partyId); |
| | | |
| | | resultObject.setCode("0"); |
| | | resultObject.setMsg("设置成功"); |
| | | resultObject.setData(traderFollowSetting); |
| | | resultObject.setCode("1"); |
| | | resultObject.setMsg("跟单利息设置功能已下线"); |
| | | resultObject.setData(null); |
| | | return resultObject; |
| | | } |
| | | |
| | |
| | | @RequestMapping("followsetting.action") |
| | | public Object follow_setting(HttpServletRequest request){ |
| | | ResultObject resultObject = new ResultObject(); |
| | | String partyId = SecurityUtils.getCurrentUserId(); |
| | | |
| | | User user = userService.findByUserId(partyId); |
| | | |
| | | String days_setting= request.getParameter("days_setting"); |
| | | |
| | | if(StringUtils.isEmptyString(days_setting)) { |
| | | resultObject.setCode("1"); |
| | | resultObject.setMsg("借款天数不能为空"); |
| | | return resultObject; |
| | | } |
| | | |
| | | String rate = request.getParameter("rate"); |
| | | |
| | | if(StringUtils.isEmptyString(rate)) { |
| | | resultObject.setCode("1"); |
| | | resultObject.setMsg("带单佣金比例不能为空"); |
| | | return resultObject; |
| | | } |
| | | |
| | | if(!StringUtils.isDouble(rate)) { |
| | | resultObject.setCode("1"); |
| | | resultObject.setMsg("带单佣金比例格式不正确"); |
| | | return resultObject; |
| | | } |
| | | |
| | | if(null != traderFollowSettingService.findByPartyId(partyId)) { |
| | | resultObject.setCode("1"); |
| | | resultObject.setMsg("带单设置已存在"); |
| | | return resultObject; |
| | | } |
| | | |
| | | TraderFollowSetting traderFollowSetting = new TraderFollowSetting(); |
| | | traderFollowSetting.setPartyId(partyId); |
| | | traderFollowSetting.setUsername(user.getUserName()); |
| | | traderFollowSetting.setDaysSetting(days_setting); |
| | | traderFollowSetting.setRate(Double.parseDouble(rate)); |
| | | |
| | | resultObject.setCode("0"); |
| | | resultObject.setMsg("设置成功"); |
| | | resultObject.setCode("1"); |
| | | resultObject.setMsg("跟单利息设置功能已下线"); |
| | | return resultObject; |
| | | } |
| | | |
| | |
| | | @RequestMapping("updatefollowsetting.action") |
| | | public Object update_follow_setting(HttpServletRequest request){ |
| | | ResultObject resultObject = new ResultObject(); |
| | | String partyId = SecurityUtils.getCurrentUserId(); |
| | | |
| | | String id = request.getParameter("id"); |
| | | |
| | | String days_setting= request.getParameter("days_setting"); |
| | | |
| | | if(StringUtils.isEmptyString(id)) { |
| | | resultObject.setCode("1"); |
| | | resultObject.setMsg("更改记录ID不能为空"); |
| | | return resultObject; |
| | | } |
| | | |
| | | if(StringUtils.isEmptyString(days_setting)) { |
| | | resultObject.setCode("1"); |
| | | resultObject.setMsg("借款天数不能为空"); |
| | | return resultObject; |
| | | } |
| | | |
| | | String rate = request.getParameter("rate"); |
| | | |
| | | if(StringUtils.isEmptyString(rate)) { |
| | | resultObject.setCode("1"); |
| | | resultObject.setMsg("带单佣金比例不能为空"); |
| | | return resultObject; |
| | | } |
| | | |
| | | if(!StringUtils.isDouble(rate)) { |
| | | resultObject.setCode("1"); |
| | | resultObject.setMsg("带单佣金比例格式不正确"); |
| | | return resultObject; |
| | | } |
| | | |
| | | TraderFollowSetting traderFollowSetting = traderFollowSettingService.findById(id); |
| | | |
| | | if(null == traderFollowSetting) { |
| | | resultObject.setCode("1"); |
| | | resultObject.setMsg("记录不存在"); |
| | | return resultObject; |
| | | } |
| | | |
| | | |
| | | traderFollowSetting.setDaysSetting(days_setting); |
| | | traderFollowSetting.setRate(Double.parseDouble(rate)); |
| | | |
| | | traderFollowSettingService.update(traderFollowSetting); |
| | | |
| | | resultObject.setCode("0"); |
| | | resultObject.setMsg("设置成功"); |
| | | resultObject.setCode("1"); |
| | | resultObject.setMsg("跟单利息设置功能已下线"); |
| | | return resultObject; |
| | | } |
| | | |
| | |
| | | return "当前跟随人数加偏差值不能小于0"; |
| | | if (profit_share_ratio < 0.0D) |
| | | return "利润分成比例不能小于0"; |
| | | if (follower_max <= 0) |
| | | return "此次跟单最多跟随人数不能小于等于0"; |
| | | if (follower_max < Arith.add(follower_now, deviation_follower_now)) |
| | | return "此次跟单最多跟随人数不能小于当前跟随人数加偏差值"; |
| | | if (follow_volumn_min < 0) |
| | | return "最小跟单张数不能小于0"; |
| | | return null; |
| | |
| | | /** |
| | | * 查询类型 orders 当前委托 ,hisorders 历史委托 ,user 跟随者 |
| | | */ |
| | | trader_order = this.contractOrderService.getPaged(Long.valueOf(page.getCurrent()).intValue(), Long.valueOf(page.getSize()).intValue(), entity.getPartyId(), symbol, type, null); |
| | | trader_order = this.contractOrderService.buildDataFromOrders( |
| | | this.contractOrderService.findSubmittedTraderOwn(entity.getPartyId(), symbol)); |
| | | } else if("hisorders".equals(type)) { |
| | | trader_order = this.traderOrderService.getPaged(page, entity.getPartyId()); |
| | | } |
| | |
| | | |
| | | map.put("name", entity.getName()); |
| | | map.put("remarks", entity.getRemarks()); |
| | | /** 带单品种(与 T_TRADER.SYMBOLS 一致;交易员广场列表用 symbol_name,此处双字段避免前端遗漏) */ |
| | | String symbolsVal = StringUtils.isEmptyString(entity.getSymbols()) ? "" : entity.getSymbols(); |
| | | map.put("symbols", symbolsVal); |
| | | map.put("symbol_name", symbolsVal); |
| | | /** 带单开关:0 停止 1 开启 2 禁止(与后台交易员管理一致) */ |
| | | map.put("state", StringUtils.isEmptyString(entity.getState()) ? "" : entity.getState()); |
| | | /** |
| | | * 累计金额order_amount |
| | | */ |
| | | map.put("order_amount", df2.format(Arith.add(entity.getOrderAmount(), entity.getDeviationOrderAmount()))); |
| | | |
| | | // map.put("symbol_name", "BTC/USDT;ETH/USDT"); |
| | | map.put("profit", df2.format(Arith.add(entity.getProfit(), entity.getDeviationProfit()))); |
| | | double historyProfitTotal = contractOrderService.historyProfitForTraderTotalYield(entity); |
| | | map.put("profit", df2.format(historyProfitTotal)); |
| | | |
| | | map.put("order_profit", (int) Arith.add(entity.getOrderProfit(), entity.getDeviationOrderProfit())); |
| | | |
| | |
| | | map.put("profit_ratio", df2.format(Arith.add(Arith.mul(entity.getDeviationProfitRatio(), 100), |
| | | Arith.mul(entity.getProfitRatio(), 100)))); |
| | | |
| | | // 与广场 list 一致:历史=合约表已平仓全品种盈亏+偏差;分母优先已平仓保证金合计(与当前持仓 deposit 一致) |
| | | double historyAmountTotal = contractOrderService.historyAmountBasisForTraderTotalYield(entity); |
| | | double openProfitAgg = 0D; |
| | | double openDepositAgg = 0D; |
| | | List<ContractOrder> openTraderOwn = contractOrderService.findSubmittedTraderOwn(entity.getPartyId(), ""); |
| | | if (openTraderOwn != null) { |
| | | for (ContractOrder one : openTraderOwn) { |
| | | if (one == null) { |
| | | continue; |
| | | } |
| | | contractOrderService.wrapProfit(one); |
| | | openProfitAgg = Arith.add(openProfitAgg, one.getProfit() == null ? 0D : one.getProfit().doubleValue()); |
| | | openDepositAgg = Arith.add(openDepositAgg, one.getDeposit() == null ? 0D : one.getDeposit().doubleValue()); |
| | | } |
| | | } |
| | | double totalProfitAgg = Arith.add(historyProfitTotal, openProfitAgg); |
| | | double totalAmountAgg = Arith.add(historyAmountTotal, openDepositAgg); |
| | | double totalProfitRatioAgg = 0D; |
| | | if (totalAmountAgg > 0D) { |
| | | totalProfitRatioAgg = Arith.mul(Arith.div(totalProfitAgg, totalAmountAgg), 100); |
| | | } |
| | | map.put("total_profit", df2.format(totalProfitAgg)); |
| | | map.put("total_profit_ratio", df2.format(totalProfitRatioAgg)); |
| | | |
| | | map.put("profit_share_ratio", Arith.mul(entity.getProfitShareRatio(), 100)); |
| | | map.put("follow_commission_type", FollowCommissionType.normalizeOrLegacy(entity.getFollowCommissionType())); |
| | | map.put("follow_commission_monthly_amount", |
| | | entity.getFollowCommissionMonthlyAmount() == null ? "0" : entity.getFollowCommissionMonthlyAmount().stripTrailingZeros().toPlainString()); |
| | | map.put("follow_commission_daily_pct", Arith.mul(entity.getFollowCommissionDailyPct(), 100)); |
| | | map.put("follower_max", entity.getFollowerMax()); |
| | | if (entity.getCreateTime() != null) { |
| | | map.put("create_time", DateUtils.format(entity.getCreateTime(), "yyyy-MM-dd HH:mm:ss")); |
| | | } else { |
| | | map.put("create_time", ""); |
| | | } |
| | | Date date_now = new Date();// 取时单 |
| | | int days = daysBetween(entity.getCreateTime(), date_now); |
| | | if (days < 0) { |
| | |
| | | TraderFollowUser user = this.traderFollowUserService |
| | | .findByPartyIdAndTrader_partyId(partyId, entity.getPartyId()); |
| | | if (user != null) { |
| | | |
| | | map.put("follow_relation_exists", true); |
| | | map.put("follow_volume", user.getVolume()); |
| | | map.put("follow_volume_max", user.getVolumeMax()); |
| | | /** |
| | | * 跟单固定张数/固定比例---选择 1,固定张数,固定比例 |
| | | */ |
| | | map.put("follow_type", user.getFollowType()); |
| | | map.put("follow_state", "1"); |
| | | } else { |
| | | map.put("follow_state", user.getState()); |
| | | map.put("follow_symbol", StringUtils.isEmptyString(user.getSymbol()) ? "" : user.getSymbol()); |
| | | map.put("follow_lever_rate", user.getLeverRate()); |
| | | String followFailReason = StringUtils.isEmptyString(user.getFailReason()) ? "" : user.getFailReason(); |
| | | map.put("follow_fail_reason", followFailReason); |
| | | map.put("follow_fail_reason_key", followFailReasonKeyI18n(followFailReason)); |
| | | map.put("follow_last_fail_time", user.getLastFailTime()); |
| | | map.put("follow_user_cumulative_profit", df2.format(user.getProfit())); |
| | | double cumAmt = user.getAmountSum(); |
| | | double cumYieldPct = 0D; |
| | | if (cumAmt > 0D) { |
| | | cumYieldPct = Arith.mul(Arith.div(user.getProfit(), cumAmt), 100); |
| | | } |
| | | map.put("follow_user_cumulative_yield_pct", df2.format(cumYieldPct)); |
| | | |
| | | // 是否已有成功开仓(仅当前 submitted 持仓算成功) + 当前跟单持仓浮盈亏合计 |
| | | boolean followOpenSuccess = false; |
| | | String followOpenDirection = ""; |
| | | String followOpenTime = ""; |
| | | String followOpenOrderNo = ""; |
| | | double followMyOpenProfit = 0D; |
| | | double followMyOpenDeposit = 0D; |
| | | List<TraderFollowUserOrder> userFollowOrders = traderFollowUserOrderService |
| | | .findByPartyIdAndTraderPartyIdAndState(partyId, entity.getPartyId(), ContractOrder.STATE_SUBMITTED); |
| | | if (userFollowOrders != null && !userFollowOrders.isEmpty()) { |
| | | TraderFollowUserOrder latest = userFollowOrders.get(0); |
| | | for (TraderFollowUserOrder one : userFollowOrders) { |
| | | if (one != null && !StringUtils.isEmptyString(one.getUserOrderNo())) { |
| | | ContractOrder co = contractOrderService.findByOrderNo(one.getUserOrderNo()); |
| | | if (co != null && ContractOrder.STATE_SUBMITTED.equals(co.getState())) { |
| | | contractOrderService.wrapProfit(co); |
| | | followMyOpenProfit = Arith.add(followMyOpenProfit, |
| | | co.getProfit() == null ? 0D : co.getProfit().doubleValue()); |
| | | followMyOpenDeposit = Arith.add(followMyOpenDeposit, |
| | | co.getDeposit() == null ? 0D : co.getDeposit().doubleValue()); |
| | | } |
| | | } |
| | | if (one != null && one.getCreateTime() != null && latest != null |
| | | && latest.getCreateTime() != null && one.getCreateTime().after(latest.getCreateTime())) { |
| | | latest = one; |
| | | } |
| | | } |
| | | if (latest != null && !StringUtils.isEmptyString(latest.getUserOrderNo())) { |
| | | ContractOrder latestUserOrder = contractOrderService.findByOrderNo(latest.getUserOrderNo()); |
| | | if (latestUserOrder != null && ContractOrder.STATE_SUBMITTED.equals(latestUserOrder.getState())) { |
| | | followOpenSuccess = true; |
| | | followOpenDirection = StringUtils.isEmptyString(latestUserOrder.getDirection()) ? "" |
| | | : latestUserOrder.getDirection(); |
| | | if (latestUserOrder.getCreateTime() != null) { |
| | | followOpenTime = DateUtils.format(latestUserOrder.getCreateTime(), "yyyy-MM-dd HH:mm:ss"); |
| | | } |
| | | followOpenOrderNo = latestUserOrder.getOrderNo(); |
| | | } |
| | | } |
| | | } |
| | | map.put("follow_open_success", followOpenSuccess); |
| | | map.put("follow_open_direction", followOpenDirection); |
| | | map.put("follow_open_time", followOpenTime); |
| | | map.put("follow_open_order_no", followOpenOrderNo); |
| | | map.put("follow_my_open_profit", df2.format(followMyOpenProfit)); |
| | | map.put("follow_my_open_deposit", df2.format(followMyOpenDeposit)); |
| | | double followMyOpenYieldPct = 0D; |
| | | if (followMyOpenDeposit > 0D) { |
| | | followMyOpenYieldPct = Arith.mul(Arith.div(followMyOpenProfit, followMyOpenDeposit), 100); |
| | | } |
| | | map.put("follow_my_open_yield_pct", df2.format(followMyOpenYieldPct)); |
| | | map.put("follow_monthly_remaining_days", |
| | | computeFollowMonthlyRemainingDays(entity.getFollowCommissionType(), user)); |
| | | } else { |
| | | map.put("follow_relation_exists", false); |
| | | map.put("follow_state", "2"); |
| | | map.put("follow_fail_reason", ""); |
| | | map.put("follow_fail_reason_key", ""); |
| | | map.put("follow_last_fail_time", null); |
| | | map.put("follow_open_success", false); |
| | | map.put("follow_open_direction", ""); |
| | | map.put("follow_open_time", ""); |
| | | map.put("follow_open_order_no", ""); |
| | | map.put("follow_user_cumulative_profit", df2.format(0D)); |
| | | map.put("follow_user_cumulative_yield_pct", df2.format(0D)); |
| | | map.put("follow_my_open_profit", df2.format(0D)); |
| | | map.put("follow_my_open_deposit", df2.format(0D)); |
| | | map.put("follow_my_open_yield_pct", df2.format(0D)); |
| | | map.put("follow_monthly_remaining_days", null); |
| | | } |
| | | |
| | | } else { |
| | | map.put("follow_state", "2"); |
| | | map.put("follow_relation_exists", false); |
| | | map.put("follow_fail_reason", ""); |
| | | map.put("follow_fail_reason_key", ""); |
| | | map.put("follow_user_cumulative_profit", df2.format(0D)); |
| | | map.put("follow_user_cumulative_yield_pct", df2.format(0D)); |
| | | map.put("follow_my_open_profit", df2.format(0D)); |
| | | map.put("follow_my_open_deposit", df2.format(0D)); |
| | | map.put("follow_my_open_yield_pct", df2.format(0D)); |
| | | map.put("follow_monthly_remaining_days", null); |
| | | map.remove("partyId"); |
| | | } |
| | | map.put("follow_volumn_min", entity.getFollowVolumnMin()); |
| | | map.put("follow_volumn_min", entity.getFollowVolumnMin() == null ? 0d : entity.getFollowVolumnMin().doubleValue()); |
| | | /** 审核状态:0 待审、1 通过、-1 驳回(供前端在 istrader 异常时从 get 兜底) */ |
| | | map.put("checked", entity.getChecked()); |
| | | return map; |
| | | |
| | | } |
| | | |
| | | /** 与 ApiTraderUserController 一致:供 H5/PC i18n 映射跟单失败原因 */ |
| | | private static String followFailReasonKeyI18n(String reason) { |
| | | if (reason == null) { |
| | | return ""; |
| | | } |
| | | String r = reason.trim(); |
| | | if (r.isEmpty()) { |
| | | return ""; |
| | | } |
| | | 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 Integer computeFollowMonthlyRemainingDays(Object traderCommissionTypeObj, TraderFollowUser user) { |
| | | if (user == null) { |
| | | return null; |
| | | } |
| | | String typeNorm = traderCommissionTypeObj == null ? FollowCommissionType.LEGACY |
| | | : FollowCommissionType.normalizeOrLegacy(traderCommissionTypeObj.toString()); |
| | | if (!FollowCommissionType.isMonthlyFixed(typeNorm)) { |
| | | return null; |
| | | } |
| | | String st = user.getState(); |
| | | if (!(TraderFollowUser.STATE_FOLLOWING.equals(st) || TraderFollowUser.STATE_STOPPING.equals(st) |
| | | || TraderFollowUser.STATE_FAILED.equals(st))) { |
| | | return null; |
| | | } |
| | | String paid = user.getMonthlyFeePaidPeriod(); |
| | | if (StringUtils.isEmptyString(paid)) { |
| | | return null; |
| | | } |
| | | try { |
| | | ZoneId z = ZoneId.systemDefault(); |
| | | YearMonth paidYm = YearMonth.parse(paid); |
| | | YearMonth nowYm = YearMonth.now(z); |
| | | if (!paidYm.equals(nowYm)) { |
| | | return null; |
| | | } |
| | | LocalDate today = LocalDate.now(z); |
| | | LocalDate end = nowYm.atEndOfMonth(); |
| | | long days = ChronoUnit.DAYS.between(today, end) + 1L; |
| | | return (int) Math.max(0L, days); |
| | | } catch (Exception e) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | public static int daysBetween(Date smdate, Date bdate) throws ParseException { |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); |
| | | smdate = sdf.parse(sdf.format(smdate)); |