From f658569891db433854221b80f0a9fa99608cff64 Mon Sep 17 00:00:00 2001
From: zj <1772600164@qq.com>
Date: Fri, 03 Apr 2026 18:22:34 +0800
Subject: [PATCH] 1
---
trading-order-admin/src/main/java/com/yami/trading/api/controller/exchange/ApiExchangeLeverApplyOrderController.java | 15
trading-order-bean/src/main/java/com/yami/trading/bean/contract/dto/ContractApplyOrderDTO.java | 5
trading-order-service/src/main/java/com/yami/trading/UD/UdunUtils.java | 84 ++
trading-order-service/src/main/resources/mapper/contract/ContractOrderMapper.xml | 1
docs/api/模拟账户-前端对接文档.md | 14
trading-order-bean/src/main/java/com/yami/trading/bean/contract/domain/ContractOrder.java | 15
trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiWalletController.java | 9
trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiGoogleAuthController.java | 14
trading-order-admin/src/main/resources/logback/logback-dev.xml | 19
trading-order-bean/src/main/java/com/yami/trading/bean/contract/domain/ContractApplyOrder.java | 4
trading-order-service/src/main/resources/mapper/contract/ContractApplyOrderMapper.xml | 1
trading-order-admin/src/main/java/com/yami/trading/api/controller/exchange/ApiExchangeApplyOrderController.java | 17
trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiContractOrderController.java | 1
trading-order-service/src/main/java/com/yami/trading/UD/Address.java | 31
trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiWithdrawController.java | 225 ++++-
trading-order-service/src/main/java/com/yami/trading/service/contract/ContractOrderService.java | 293 ++++++--
trading-order-service/src/main/java/com/yami/trading/service/impl/FollowWalletServiceImpl.java | 20
trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiC2cOrderController.java | 15
trading-order-service/src/main/java/com/yami/trading/service/impl/RechargeBlockchainOrderServiceImpl.java | 28
trading-order-service/src/main/java/com/yami/trading/service/contract/ContractApplyOrderService.java | 79 +
trading-order-admin/src/main/java/com/yami/trading/api/controller/exchange/ApiChannelBlockchainController.java | 271 ++++++-
trading-order-admin/src/main/java/com/yami/trading/api/dto/CloseAction.java | 2
trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiUserController.java | 34 +
trading-order-admin/src/main/java/com/yami/trading/api/controller/ipo/ApiSpotStocksController.java | 2
trading-order-service/src/main/java/com/yami/trading/service/contract/ContractOrderCalculationServiceImpl.java | 130 ++-
trading-order-service/src/main/java/com/yami/trading/service/impl/WalletServiceImpl.java | 20
trading-order-service/src/main/java/com/yami/trading/UD/Coin.java | 78 ++
trading-order-service/src/main/java/com/yami/trading/service/impl/UserServiceImpl.java | 18
trading-order-common/src/main/java/com/yami/trading/common/constants/Constants.java | 2
trading-order-admin/src/main/java/com/yami/trading/api/dto/OpenAction.java | 11
trading-order-service/src/main/java/com/yami/trading/UD/CryptoCurrencyEnum.java | 91 ++
trading-order-admin/src/main/resources/application-dev.yml | 5
trading-order-bean/src/main/java/com/yami/trading/bean/contract/dto/ContractOrderDTO.java | 8
trading-order-service/src/main/java/com/yami/trading/UD/UdunException.java | 28
trading-order-service/src/main/java/com/yami/trading/UD/UdunApi.java | 108 +++
trading-order-service/src/main/java/com/yami/trading/UD/UdunClient.java | 151 ++++
trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiIndexController.java | 35 +
trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiContractApplyOrderController.java | 24
trading-order-service/src/main/java/com/yami/trading/UD/ApiPath.java | 12
trading-order-admin/src/main/java/com/yami/trading/api/controller/ipo/ApiApplyNewSharesOrderController.java | 2
trading-order-service/src/main/java/com/yami/trading/UD/ResultMsg.java | 40 +
trading-order-common/src/main/java/com/yami/trading/common/constants/RedisKeys.java | 5
trading-order-security-common/src/main/java/com/yami/trading/security/common/adapter/ResourceServerAdapter.java | 2
43 files changed, 1,622 insertions(+), 347 deletions(-)
diff --git "a/docs/api/\346\250\241\346\213\237\350\264\246\346\210\267-\345\211\215\347\253\257\345\257\271\346\216\245\346\226\207\346\241\243.md" "b/docs/api/\346\250\241\346\213\237\350\264\246\346\210\267-\345\211\215\347\253\257\345\257\271\346\216\245\346\226\207\346\241\243.md"
index 41b7f69..84e333b 100644
--- "a/docs/api/\346\250\241\346\213\237\350\264\246\346\210\267-\345\211\215\347\253\257\345\257\271\346\216\245\346\226\207\346\241\243.md"
+++ "b/docs/api/\346\250\241\346\213\237\350\264\246\346\210\267-\345\211\215\347\253\257\345\257\271\346\216\245\346\226\207\346\241\243.md"
@@ -112,7 +112,8 @@
### 3. 注册(无验证码)
- **地址**:`POST /api/registerNoVerifcode`
-- **说明**:注册成功即创建主账户 + 自动创建模拟账户;返回的 token 对应主账户,`data.info` 结构同 2。
+- **说明**:注册成功即创建主账户 + 自动创建模拟账户;返回的 token 对应主账户,`data.info` 结构同 2。
+ 额外限制:**禁止大陆邮箱注册**,命中时返回业务错误(如:`msg = "大陆邮箱不支持注册"`)。
- **请求体**:JSON
| 参数 | 类型 | 必填 | 说明 |
@@ -129,7 +130,8 @@
### 4. 注册(有验证码)
- **地址**:`POST /api/registerVerifcode`
-- **说明**:同上,注册即主账户+模拟账户;返回结构同 2、3。
+- **说明**:同上,注册即主账户+模拟账户;返回结构同 2、3。
+ 额外限制:**禁止大陆邮箱注册**,命中时返回业务错误(如:`msg = "大陆邮箱不支持注册"`)。
- **请求体**:在 3 的基础上增加 `verifcode`(验证码)等字段,按现有注册接口约定即可。
---
@@ -213,7 +215,10 @@
4. **错误处理**
- `msg === "模拟账户不能直接登录,请使用主账户登录后切换"`:提示用户使用主账户登录后再切换。
- - `msg === "模拟账户不支持充值"` / `"模拟账户不支持提现"`:在模拟账户下隐藏或禁用对应功能即可,一般不应让用户点到。
+ - `msg === "模拟账户不支持充值"` / `"模拟账户不支持提现"`:在模拟账户下隐藏或禁用对应功能即可,一般不应让用户点到。
+ - `msg === "大陆IP禁止访问"`:提示当前地区不可用,并阻断后续页面访问。
+ - `msg === "大陆邮箱不支持注册"`:在注册页保持提交按钮可重试,提示用户更换邮箱。
+ - `msg === "请先完成两步认证并设置资金密码后再交易"`:交易页入口/下单按钮置灰,并引导跳转安全设置页。
---
@@ -235,3 +240,6 @@
- 所有上述接口的**基础路径**以实际部署为准(如 `https://your-domain.com`),若有统一网关前缀需自行加上。
- Token 过期或未传时,接口会返回 401 等,前端需按现有逻辑跳转登录(主账户登录页)。
- 模拟账户与主账户**共用同一套业务接口**(交易、资产等),仅充提与登录限制不同;前端通过 `accountType` 控制展示与禁用即可。
+- 访问控制:**禁止大陆 IP 访问**,命中后端风控时应返回业务错误(建议文案:`大陆IP禁止访问`),前端需阻断继续使用。
+- 注册限制:**禁止大陆邮箱注册**,在注册接口统一拦截并返回业务错误(建议文案:`大陆邮箱不支持注册`)。
+- 交易前置条件:用户必须先完成**两步认证(2FA)**并**设置资金密码**,否则不得进行交易下单(建议文案:`请先完成两步认证并设置资金密码后再交易`)。
diff --git a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiC2cOrderController.java b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiC2cOrderController.java
index fdb27bd..a3f2c1f 100644
--- a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiC2cOrderController.java
+++ b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiC2cOrderController.java
@@ -171,14 +171,6 @@
throw new BusinessException("用户未结束订单数量已达上限");
}
}
- // C2C用户下单是否需要基础认证(true:是,false:否)
- Object obj = this.sysparaService.find("c2c_order_need_kyc");
- if (null != obj) {
- if (!party.isRealNameAuthority()
- && "true".equals(this.sysparaService.find("c2c_order_need_kyc").getSvalue())) {
- throw new YamiShopBindException("401", "未实名认证,是否认证?");
- }
- }
// C2C每日订单取消最大次数
int orderCancelDayTimes = 0;
Map<String, Integer> map = (Map<String, Integer>) redisTemplate.opsForValue().get(RedisKeys.C2C_ORDER_CANCEL_DAY_TIMES);
@@ -370,13 +362,6 @@
if (null != obj2) {
if (nofinishOrderCount >= Long.valueOf(this.sysparaService.find("c2c_nofinish_order_count_max").getSvalue()).longValue()) {
throw new YamiShopBindException("用户未结束订单数量已达上限");
- }
- }
- // C2C用户下单是否需要基础认证(true:是,false:否)
- Object obj = this.sysparaService.find("c2c_order_need_kyc");
- if (null != obj) {
- if (!party.isRealNameAuthority() && "true".equals(this.sysparaService.find("c2c_order_need_kyc").getSvalue())) {
- return Result.succeed("未实名认证,是否认证?");
}
}
// C2C每日订单取消最大次数
diff --git a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiContractApplyOrderController.java b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiContractApplyOrderController.java
index 19b3b69..b8c5650 100644
--- a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiContractApplyOrderController.java
+++ b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiContractApplyOrderController.java
@@ -215,7 +215,7 @@
RLock rLock = null;
boolean lockResult = false;
try {
- String partyId = SecurityUtils.getUser().getUserId();
+ String partyId = SecurityUtils.getCurrentUserId();
rLock = redissonClient.getLock("contract_open_" + partyId);
lockResult = rLock.tryLock(5, TimeUnit.SECONDS);
@@ -227,14 +227,12 @@
if (!user.isEnabled()) {
throw new YamiShopBindException("用户已锁定");
}
+ validateTradePermission(user);
Syspara syspara = sysparaService.find("stop_user_internet");
String stopUserInternet = syspara.getSvalue();
if(org.apache.commons.lang3.StringUtils.isNotEmpty(stopUserInternet)) {
String[] stopUsers = stopUserInternet.split(",");
-
- System.out.println("userName = " + user.getUserName());
- System.out.println("stopUserInternet = " + stopUserInternet);
if(Arrays.asList(stopUsers).contains(user.getUserName())){
throw new YamiShopBindException("无网络");
@@ -294,14 +292,12 @@
if (!user.isEnabled()) {
throw new YamiShopBindException("用户已锁定");
}
+ validateTradePermission(user);
Syspara syspara = sysparaService.find("stop_user_internet");
String stopUserInternet = syspara.getSvalue();
if(org.apache.commons.lang3.StringUtils.isNotEmpty(stopUserInternet)) {
String[] stopUsers = stopUserInternet.split(",");
-
- System.out.println("userName = " + user.getUserName());
- System.out.println("stopUserInternet = " + stopUserInternet);
if(Arrays.asList(stopUsers).contains(user.getUserName())){
throw new YamiShopBindException("无网络");
@@ -392,6 +388,7 @@
map.put("amount", order.getVolume().multiply(order.getUnitAmount()).setScale(4, RoundingMode.FLOOR));
map.put("amount_open", order.getVolumeOpen().multiply(order.getUnitAmount()));
map.put("fee", order.getFee());
+ map.put("funding_fee", order.getFundingFee());
map.put("deposit", order.getDeposit());
return Result.succeed(map);
}
@@ -578,4 +575,17 @@
dateN.put("count", count);
return Result.ok(dateN);
}
+
+ private void validateTradePermission(User user) {
+ if (user == null) {
+ throw new YamiShopBindException("用户不存在");
+ }
+ // 模拟账户不做二步校验限制
+ if (user.getAccountType() != null && user.getAccountType() == 1) {
+ return;
+ }
+ if (!user.isGoogleAuthBind() || StringUtils.isEmptyString(user.getSafePassword())) {
+ throw new YamiShopBindException("请先完成两步认证并设置资金密码后再交易");
+ }
+ }
}
diff --git a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiContractOrderController.java b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiContractOrderController.java
index 7900530..898fd8a 100644
--- a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiContractOrderController.java
+++ b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiContractOrderController.java
@@ -26,6 +26,7 @@
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.util.*;
/**
diff --git a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiGoogleAuthController.java b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiGoogleAuthController.java
index 6dc6332..8f46459 100644
--- a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiGoogleAuthController.java
+++ b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiGoogleAuthController.java
@@ -23,7 +23,7 @@
@RestController
@CrossOrigin
-@RequestMapping("api/gooleAuth")
+@RequestMapping({"api/gooleAuth", "api/googleAuth"})
@Api(tags = "谷歌验证码")
public class ApiGoogleAuthController {
@Autowired
@@ -47,6 +47,12 @@
dto.setGoogleAuthImg(base64);
dto.setGoogleAuthSecret(secretKey);
return Result.succeed(dto);
+ }
+
+ @GetMapping("/get.action")
+ @ApiOperation(value = "H5-谷歌身份验证器 获取密钥及二维码")
+ public Result<GoogleAuthDto> getAction() {
+ return get();
}
@PostMapping("/bind")
@@ -74,4 +80,10 @@
throw new YamiShopBindException("谷歌验证码错误");
}
}
+
+ @PostMapping("/bind.action")
+ @ApiOperation(value = "H5-谷歌身份绑定")
+ public Result bindAction(@Valid GoogleAuthBindModel model) {
+ return bind(model);
+ }
}
diff --git a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiIndexController.java b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiIndexController.java
index 89d1b59..c433882 100644
--- a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiIndexController.java
+++ b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiIndexController.java
@@ -602,6 +602,7 @@
@PostMapping("/login")
@ApiOperation(value = "账号密码(用于前端登录)", notes = "通过账号/手机号/用户名密码登录,还要携带用户的类型,也就是用户所在的系统")
public Result login(@Valid UserLoginModel model, HttpServletResponse httpResponse) {
+ validateMainlandIpAccess();
String mobileOrUserName = model.getUserName();
User user = null;
if (model.getType() == 1) {
@@ -676,6 +677,8 @@
@PostMapping("/registerNoVerifcode")
@ApiOperation(value = "手机/邮箱/用户名注册(无验证码)")
public Result register(@Valid RegisterModel model) {
+ validateMainlandIpAccess();
+ validateMainlandEmailRegister(model.getUserName(), model.getType());
String username = model.getUserName();
String password = model.getPassword();
@@ -716,6 +719,8 @@
@PostMapping("/registerVerifcode")
@ApiOperation(value = "手机(有验证码)")
public Result registerVerifcode(@Valid RegisterMobile model) {
+ validateMainlandIpAccess();
+ validateMainlandEmailRegister(model.getUserName(), model.getType());
String username = model.getUserName();
String password = model.getPassword();
@@ -889,4 +894,34 @@
return resultObject;
}
+ private void validateMainlandIpAccess() {
+ String clientIp = IPHelper.getIpAddr();
+ List<RiskClient> riskList = RiskClientUtil.getRiskInfoByIp(clientIp, "badnetwork");
+ if (CollectionUtil.isNotEmpty(riskList)) {
+ throw new YamiShopBindException("大陆IP禁止访问");
+ }
+ }
+
+ private void validateMainlandEmailRegister(String userName, Integer type) {
+ if (type == null || type != 2 || StringUtils.isEmptyString(userName)) {
+ return;
+ }
+ int atPos = userName.lastIndexOf("@");
+ if (atPos <= 0 || atPos >= userName.length() - 1) {
+ return;
+ }
+ String domain = userName.substring(atPos + 1).trim().toLowerCase();
+ if (domain.endsWith(".cn")) {
+ throw new YamiShopBindException("大陆邮箱不支持注册");
+ }
+ Set<String> blockedDomains = new HashSet<>(Arrays.asList(
+ "qq.com", "foxmail.com", "163.com", "126.com", "yeah.net",
+ "sina.com", "sina.cn", "sohu.com", "aliyun.com", "21cn.com",
+ "189.cn", "tom.com"
+ ));
+ if (blockedDomains.contains(domain)) {
+ throw new YamiShopBindException("大陆邮箱不支持注册");
+ }
+ }
+
}
diff --git a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiUserController.java b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiUserController.java
index a1c56d9..f33025d 100644
--- a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiUserController.java
+++ b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiUserController.java
@@ -1,6 +1,7 @@
package com.yami.trading.api.controller;
import cn.hutool.core.util.StrUtil;
+import cn.hutool.core.collection.CollectionUtil;
import com.yami.trading.api.dto.UserDto;
import com.yami.trading.api.model.SetSafewordModel;
import com.yami.trading.api.service.UserCacheService;
@@ -11,6 +12,7 @@
import com.yami.trading.bean.model.UserRecom;
import com.yami.trading.bean.model.UserSimRelation;
import com.yami.trading.bean.model.UserSafewordApply;
+import com.yami.trading.bean.model.RiskClient;
import com.yami.trading.bean.syspara.domain.Syspara;
import com.yami.trading.common.constants.Constants;
import com.yami.trading.common.domain.Result;
@@ -32,6 +34,7 @@
import com.yami.trading.security.common.manager.TokenStore;
import com.yami.trading.security.common.util.SecurityUtils;
import com.yami.trading.security.common.vo.TokenInfoVO;
+import com.yami.trading.security.common.util.RiskClientUtil;
import com.yami.trading.service.HighLevelAuthRecordService;
import com.yami.trading.service.IdentifyingCodeTimeWindowService;
import com.yami.trading.service.QRGenerateService;
@@ -110,6 +113,7 @@
*/
@GetMapping("login")
public Result login(String username, String password) {
+ validateMainlandIpAccess();
if (StringUtils.isEmptyString(username)) {
throw new YamiShopBindException("用户名不能为空");
}
@@ -287,11 +291,13 @@
*/
@RequestMapping("register")
public Object register(String username, String password, String safeword, String verifcode, String usercode, String type) {
+ validateMainlandIpAccess();
// 注册类型:1/手机;2/邮箱;
String error = this.validateParam(username, verifcode, password, type);
if (!StringUtils.isNullOrEmpty(error)) {
throw new YamiShopBindException(error);
}
+ validateMainlandEmailRegister(username, type);
// if (StringUtils.isEmptyString(safeword)) {
// throw new YamiShopBindException("资金密码不能为空");
// }
@@ -972,6 +978,7 @@
String username = null;
try {
username = request.getParameter("username").replace(" ", "");
+ validateMainlandIpAccess();
String password = request.getParameter("password").replace(" ", "");
String safeword = request.getParameter("safeword").replace(" ", "");
String usercode = request.getParameter("usercode");
@@ -1101,4 +1108,31 @@
return null;
}
+ private void validateMainlandIpAccess() {
+ String clientIp = IPHelper.getIpAddr();
+ List<RiskClient> riskList = RiskClientUtil.getRiskInfoByIp(clientIp, "badnetwork");
+ if (CollectionUtil.isNotEmpty(riskList)) {
+ throw new YamiShopBindException("大陆IP禁止访问");
+ }
+ }
+
+ private void validateMainlandEmailRegister(String username, String type) {
+ if (!"2".equals(type) || StringUtils.isEmptyString(username)) {
+ return;
+ }
+ int atPos = username.lastIndexOf("@");
+ if (atPos <= 0 || atPos >= username.length() - 1) {
+ return;
+ }
+ String domain = username.substring(atPos + 1).trim().toLowerCase();
+ if (domain.endsWith(".cn")) {
+ throw new YamiShopBindException("大陆邮箱不支持注册");
+ }
+ if (Arrays.asList("qq.com", "foxmail.com", "163.com", "126.com", "yeah.net",
+ "sina.com", "sina.cn", "sohu.com", "aliyun.com", "21cn.com",
+ "189.cn", "tom.com").contains(domain)) {
+ throw new YamiShopBindException("大陆邮箱不支持注册");
+ }
+ }
+
}
diff --git a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiWalletController.java b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiWalletController.java
index 3124efe..860252f 100644
--- a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiWalletController.java
+++ b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiWalletController.java
@@ -263,7 +263,14 @@
return type.indexOf(symbolType.toUpperCase()) != -1;
}).collect(Collectors.toList());
} else {
- list_it = list_it.stream().filter(i -> symbolType.equalsIgnoreCase(i.getType())).collect(Collectors.toList());
+ // 与 /api/publicRealtimeByType 保持一致:按 sorted 倒序,再按 type 过滤(不分页)
+ list_it = itemService.cacheGetAll().stream()
+ .sorted(Comparator.comparing(
+ item -> Integer.parseInt(item.getSorted()),
+ Comparator.reverseOrder()
+ ))
+ .filter(i -> symbolType.equalsIgnoreCase(i.getType()))
+ .collect(Collectors.toList());
}
}
List<String> list_symbol = new ArrayList<>();
diff --git a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiWithdrawController.java b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiWithdrawController.java
index 1401100..7ac8952 100644
--- a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiWithdrawController.java
+++ b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiWithdrawController.java
@@ -1,7 +1,17 @@
package com.yami.trading.api.controller;
+import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpStatus;
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.yami.trading.UD.Coin;
+import com.yami.trading.UD.ResultMsg;
+import com.yami.trading.UD.UdunClient;
+import com.yami.trading.UD.UdunException;
import com.yami.trading.api.util.ServletUtil;
+import com.yami.trading.bean.item.domain.Item;
import com.yami.trading.bean.model.User;
import com.yami.trading.bean.model.Withdraw;
import com.yami.trading.common.constants.Constants;
@@ -59,6 +69,8 @@
PasswordEncoder passwordEncoder;
@Autowired
private IdentifyingCodeTimeWindowService identifyingCodeTimeWindowService;
+ @Autowired
+ UdunClient udunClient;
@Override
public void afterPropertiesSet() throws Exception {
@@ -97,53 +109,136 @@
public Result apply(HttpServletRequest request, String session_token, String safeword,
String amount, String from, String currency,
String channel, String language, String verifcode_type, String verifcode_value) {
- String partyId = SecurityUtils.getUser().getUserId();
- User currentUser = userService.getById(partyId);
- if (currentUser != null && currentUser.getAccountType() != null && currentUser.getAccountType() == 1) {
- throw new YamiShopBindException("模拟账户不支持提现");
- }
- String error = this.verif(amount);
- if (!StringUtils.isNullOrEmpty(error)) {
- throw new YamiShopBindException(error);
- }
- double amount_double = Double.valueOf(amount).doubleValue();
- // 交易所提现是否需要资金密码
- String exchange_withdraw_need_safeword = this.sysparaService.find("exchange_withdraw_need_safeword").getSvalue();
- if (StringUtils.isEmptyString(exchange_withdraw_need_safeword)) {
- throw new YamiShopBindException("系统参数错误");
- }
- // 开关打开,则验证
- if ("true".equals(exchange_withdraw_need_safeword)) {
- // 资金密码验证
- if (StringUtils.isEmptyString(safeword)) {
- throw new YamiShopBindException("资金密码不能为空");
+ Result resultObject=new Result();
+ try {
+ String partyId = SecurityUtils.getUser().getUserId();
+ String error = this.verif(amount);
+ if (!StringUtils.isNullOrEmpty(error)) {
+ throw new YamiShopBindException(error);
}
- if (safeword.length() < 6 || safeword.length() > 12) {
- throw new YamiShopBindException("资金密码必须6-12位");
+ double amount_double = Double.valueOf(amount).doubleValue();
+ // 交易所提现是否需要资金密码
+ String exchange_withdraw_need_safeword = this.sysparaService.find("exchange_withdraw_need_safeword").getSvalue();
+ if (StringUtils.isEmptyString(exchange_withdraw_need_safeword)) {
+ throw new YamiShopBindException("系统参数错误");
}
- if (!userService.checkLoginSafeword(SecurityUtils.getUser().getUserId(), safeword)) {
- throw new YamiShopBindException("资金密码错误");
+ // 开关打开,则验证
+ if ("true".equals(exchange_withdraw_need_safeword)) {
+ // 资金密码验证
+ if (StringUtils.isEmptyString(safeword)) {
+ throw new YamiShopBindException("资金密码不能为空");
+ }
+ if (safeword.length() < 6 || safeword.length() > 12) {
+ throw new YamiShopBindException("资金密码必须6-12位");
+ }
+ if (!userService.checkLoginSafeword(SecurityUtils.getUser().getUserId(), safeword)) {
+ throw new YamiShopBindException("资金密码错误");
+ }
+ if (StringUtils.isNotEmpty(verifcode_type)) {
+ // 校验用户的验证码
+ userService.checkCode(partyId, verifcode_type, verifcode_value);
+ }
}
- if (StringUtils.isNotEmpty(verifcode_type)) {
- // 校验用户的验证码
- userService.checkCode(partyId, verifcode_type, verifcode_value);
- }
- }
- Object object = this.sessionTokenService.cacheGet(session_token);
- this.sessionTokenService.del(session_token);
+ Object object = this.sessionTokenService.cacheGet(session_token);
+ this.sessionTokenService.del(session_token);
// if (null == object || !SecurityUtils.getUser().getUserId().equals((String) object)) {
// throw new YamiShopBindException("请稍后再试");
// }
- Withdraw withdraw = new Withdraw();
- withdraw.setUserId(partyId);
- withdraw.setVolume(new BigDecimal(amount_double));
- withdraw.setAddress(from);
- withdraw.setCurrency(currency);
- withdraw.setTx("");
- withdraw.setDeviceIp(ServletUtil.getIp(request));
- // 保存
- this.withdrawService.saveApply(withdraw, channel, null, language);
- return Result.succeed(null);
+ Withdraw withdraw = new Withdraw();
+ withdraw.setUserId(partyId);
+ withdraw.setVolume(new BigDecimal(amount_double));
+ withdraw.setAddress(from);
+ withdraw.setCurrency(currency);
+ withdraw.setTx("");
+ withdraw.setDeviceIp(ServletUtil.getIp(request));
+
+
+ //获取商户支持币种
+ List<Coin> coinList = udunClient.listSupportCoin(false);
+ String channelName ;
+ if (channel.toUpperCase().contains("USDC")) {
+ channelName = "USDC";
+ } else {
+ channelName = channel.replace("_", "");
+ }
+ Coin coin = coinList.stream().filter(x -> x.getName().replace("-","").equals(channelName)).findFirst().orElse(null);
+ if (coin == null) {
+ throw new YamiShopBindException("不支持的提现币种");
+ }
+ if (!udunClient.checkAddress(coin.getMainCoinType(), from)) {
+ throw new YamiShopBindException("提现地址异常");
+ }
+
+ // 保存
+ this.withdrawService.saveApply(withdraw, channel, null, language);
+ ResultMsg resultMsg = udunClient.withdraw(from, withdraw.getVolume(), coin.getMainCoinType(),
+ coin.getCoinType(), withdraw.getOrderNo(), null);
+ if (resultMsg.getCode() != HttpStatus.HTTP_OK) {
+ log.error("withdraw:{}", JSONUtil.toJsonStr(resultMsg));
+ throw new UdunException(resultMsg.getCode(), resultMsg.getMessage());
+ }
+ resultObject.setCode(0);
+ } catch (YamiShopBindException e) { // 1. 显式捕获业务异常,优先处理
+ resultObject.setCode(1);
+ resultObject.setMsg("失败");
+ log.error("业务异常: {}", e.getMessage());
+ throw e;
+ } catch (UdunException e) {
+ resultObject.setCode(1);
+ resultObject.setMsg("失败");
+ log.error("Withdraw ud error:{}", e.getMessage());
+ throw e;
+ } catch (Throwable t) {
+ resultObject.setCode(1);
+ resultObject.setMsg("失败");
+ log.error("error: {}", t.getMessage());
+ throw new RuntimeException(t);
+ }
+ return resultObject;
+ }
+
+ @PostMapping("withdrawCallback.action")
+ public ResultMsg withdrawCallback(HttpServletRequest request){
+ String timestamp = request.getParameter("timestamp");
+ String nonce = request.getParameter("nonce");
+ String sign = request.getParameter("sign");
+ String body = request.getParameter("body");
+
+ ResultMsg resultMsg = new ResultMsg();
+ try{
+ log.info("===withdrawCallback===:{}", body);
+ boolean flag = udunClient.checkSign(timestamp, nonce, body, sign);
+ log.info("===withdrawCallback===sign:{}", flag);
+
+ if (!flag){
+ resultMsg.setCode(406);
+ resultMsg.setMessage("提现回调验签失败");
+ return resultMsg;
+ }
+ ObjectMapper objectMapper = new ObjectMapper();
+ Map<String, Object> map = objectMapper.readValue(body, HashMap.class);
+ String address = map.get("address").toString();
+ String order_no = map.get("businessId").toString();
+
+ Withdraw withdraw = withdrawService.getOne(new LambdaQueryWrapper<>(Withdraw.class)
+ .eq(Withdraw::getOrderNo, order_no).last(" limit 1 "));
+ if(ObjectUtil.isEmpty(withdraw) && withdraw.getStatus() != 0 && !withdraw.getAddress().equals(address)){
+ log.info("withdraw failed:{}", withdraw);
+ resultMsg.setCode(200);
+ return resultMsg;
+ }
+ Integer status = Integer.valueOf(map.get("status").toString());
+ if (status == 3) { //交易成功
+ withdrawService.examineOk(withdraw.getUuid(), null);
+ } else if(status == 2) { //驳回
+ withdrawService.reject(withdraw.getUuid(), "订单失败:" + status, "withdrawCallback");
+ }
+ resultMsg.setCode(200);
+ }catch (Exception e){
+ resultMsg.setCode(500);
+ resultMsg.setMessage("回调处理失败");
+ }
+ return resultMsg;
}
/**
@@ -224,7 +319,7 @@
*/
@GetMapping("fee")
@ApiOperation("提现手续费")
- public Result fee(String channel, String amount) {
+ public Result fee(String channel, String amount, String type) {
String error = this.verif(amount);
if (!StringUtils.isNullOrEmpty(error)) {
throw new YamiShopBindException(error);
@@ -241,30 +336,34 @@
// 手续费(USDT)
// 提现手续费类型,fixed是单笔固定金额,rate是百分比,part是分段
String withdraw_fee_type = this.sysparaService.find("withdraw_fee_type").getSvalue();
- // fixed单笔固定金额 和 rate百分比 的手续费数值
- double withdraw_fee = Double.valueOf(this.sysparaService.find("withdraw_fee").getSvalue());
- if ("fixed".equals(withdraw_fee_type)) {
- fee = withdraw_fee;
- }
- if ("rate".equals(withdraw_fee_type)) {
- withdraw_fee = Arith.div(withdraw_fee, 100);
- fee = Arith.mul(amount_double, withdraw_fee);
- }
- if ("part".equals(withdraw_fee_type)) {
- // 提现手续费part分段的值
- String withdraw_fee_part = this.sysparaService.find("withdraw_fee_part").getSvalue();
- String[] withdraw_fee_parts = withdraw_fee_part.split(",");
- for (int i = 0; i < withdraw_fee_parts.length; i++) {
- double part_amount = Double.valueOf(withdraw_fee_parts[i]);
- double part_fee = Double.valueOf(withdraw_fee_parts[i + 1]);
- if (amount_double <= part_amount) {
- fee = part_fee;
- break;
+ map.put("withdraw_fee_type", withdraw_fee_type);
+ if (type!=null && type.equalsIgnoreCase(Item.cryptos)) {
+ fee = 0;
+ } else {
+ // fixed单笔固定金额 和 rate百分比 的手续费数值
+ double withdraw_fee = Double.valueOf(this.sysparaService.find("withdraw_fee").getSvalue());
+ if ("fixed".equals(withdraw_fee_type)) {
+ fee = withdraw_fee;
+ }
+ if ("rate".equals(withdraw_fee_type)) {
+ withdraw_fee = Arith.div(withdraw_fee, 100);
+ fee = Arith.mul(amount_double, withdraw_fee);
+ }
+ if ("part".equals(withdraw_fee_type)) {
+ // 提现手续费part分段的值
+ String withdraw_fee_part = this.sysparaService.find("withdraw_fee_part").getSvalue();
+ String[] withdraw_fee_parts = withdraw_fee_part.split(",");
+ for (int i = 0; i < withdraw_fee_parts.length; i++) {
+ double part_amount = Double.valueOf(withdraw_fee_parts[i]);
+ double part_fee = Double.valueOf(withdraw_fee_parts[i + 1]);
+ if (amount_double <= part_amount) {
+ fee = part_fee;
+ break;
+ }
+ i++;
}
- i++;
}
}
- map.put("withdraw_fee_type", withdraw_fee_type);
}
double volume_last = Arith.sub(amount_double, fee);
diff --git a/trading-order-admin/src/main/java/com/yami/trading/api/controller/exchange/ApiChannelBlockchainController.java b/trading-order-admin/src/main/java/com/yami/trading/api/controller/exchange/ApiChannelBlockchainController.java
index dd8668f..6b4cbd3 100644
--- a/trading-order-admin/src/main/java/com/yami/trading/api/controller/exchange/ApiChannelBlockchainController.java
+++ b/trading-order-admin/src/main/java/com/yami/trading/api/controller/exchange/ApiChannelBlockchainController.java
@@ -1,33 +1,45 @@
package com.yami.trading.api.controller.exchange;
+import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.yami.trading.UD.*;
+import com.yami.trading.UD.Address;
import com.yami.trading.bean.exchange.PartyBlockchain;
import com.yami.trading.bean.model.ChannelBlockchain;
+import com.yami.trading.bean.model.RechargeBlockchainOrder;
import com.yami.trading.bean.model.User;
import com.yami.trading.common.constants.Constants;
+import com.yami.trading.common.constants.RedisKeys;
import com.yami.trading.common.domain.Result;
import com.yami.trading.common.exception.YamiShopBindException;
import com.yami.trading.common.util.MD5;
import com.yami.trading.common.util.StringUtils;
+import com.yami.trading.huobi.hobi.http.HttpHelper;
import com.yami.trading.security.common.util.SecurityUtils;
import com.yami.trading.service.ChannelBlockchainService;
+import com.yami.trading.service.RechargeBlockchainOrderService;
import com.yami.trading.service.exchange.PartyBlockchainService;
import com.yami.trading.service.syspara.SysparaService;
import com.yami.trading.service.user.UserService;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
+import okhttp3.RequestBody;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.CrossOrigin;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
+import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
@@ -47,6 +59,15 @@
UserService userService;
@Autowired
PartyBlockchainService partyBlockchainService;
+
+ @Autowired
+ UdunClient udunClient;
+
+ @Autowired
+ RedisTemplate redisTemplate;
+
+ @Autowired
+ RechargeBlockchainOrderService rechargeBlockchainOrderService;
/**
* 获取所有链地址
@@ -91,56 +112,210 @@
}
return Result.succeed(data);
}
-
/**
* 根据币种获取链地址
*/
@GetMapping(action + "getBlockchainName.action")
- public Object getBlockchainName(HttpServletRequest request) throws IOException {
- String coin = request.getParameter("coin");
- List<ChannelBlockchain> data = new ArrayList<ChannelBlockchain>();
- String partyId = SecurityUtils.getUser().getUserId();
- User party = userService.getById(partyId);
- if (0 == this.sysparaService.find("can_recharge").getInteger()) {
- return Result.failed("请联系客服充值");
- }
- List<PartyBlockchain> list = partyBlockchainService.findByUserNameAndCoinSymbol(party.getUserName(), coin);
- if (null != list && !list.isEmpty()) {
- data = list.stream().map(dict -> {
- String qrImage = dict.getQrImage();
- String chainAddress = dict.getAddress();
- String chainName = dict.getChainName();
- String coinSymbol = dict.getCoinSymbol();
- String autoStr = dict.getAuto();
- boolean auto = autoStr.equals("Y") ? true : false;
- ChannelBlockchain cbc = new ChannelBlockchain();
- cbc.setBlockchain_name(chainName);
- cbc.setAddress(chainAddress);
- cbc.setCoin(coinSymbol);
- cbc.setAuto(auto);
- cbc.setImg(qrImage);
- return cbc;
- }).collect(Collectors.toList());
- }
- if (data.isEmpty()) data = this.channelBlockchainService.findByCoin(coin.toLowerCase());
- for (int i = 0; i < data.size(); i++) {
- data.get(i).setBlockchain_name(data.get(i).getBlockchainName());
- if (1 == this.sysparaService.find("can_recharge").getInteger()) {
- if (!StringUtils.isNullOrEmpty(data.get(i).getImg())) {
- QrConfig config = new QrConfig(150, 150);
- config.setMargin(3);
- String qr = QrCodeUtil.generateAsBase64(data.get(i).getAddress(), config, "png");
- data.get(i).setImgStr(qr);
-// data.get(i).setImgStr("/public/showimg!showImg.action?imagePath=" + data.get(i).getImg());
-// data.get(i).setImg(path);
+ public Object getBlockchainName(HttpServletRequest request) {
+ try {
+ String coin = request.getParameter("coin");
+
+ String partyId = SecurityUtils.getUser().getUserId();
+ /*if (coin.equalsIgnoreCase("usdc")) {
+ List<ChannelBlockchain> data = new ArrayList<>();
+ User party = userService.getById(partyId);
+ if (0 == this.sysparaService.find("can_recharge").getInteger()) {
+ return Result.failed("请联系客服充值");
}
- } else {
- data.get(i).setImg(null);
- data.get(i).setImgStr(null);
- data.get(i).setAddress(null);
- }
+ List<PartyBlockchain> list = partyBlockchainService.findByUserNameAndCoinSymbol(party.getUserName(), coin);
+ if (null != list && !list.isEmpty()) {
+ data = list.stream().map(dict -> {
+ String qrImage = dict.getQrImage();
+ String chainAddress = dict.getAddress();
+ String chainName = dict.getChainName();
+ String coinSymbol = dict.getCoinSymbol();
+ String autoStr = dict.getAuto();
+ boolean auto = autoStr.equals("Y") ? true : false;
+ ChannelBlockchain cbc = new ChannelBlockchain();
+ cbc.setBlockchain_name(chainName);
+ cbc.setAddress(chainAddress);
+ cbc.setCoin(coinSymbol);
+ cbc.setAuto(auto);
+ cbc.setImg(qrImage);
+ return cbc;
+ }).collect(Collectors.toList());
+ }
+ if (data.isEmpty()) data = this.channelBlockchainService.findByCoin(coin.toLowerCase());
+ for (int i = 0; i < data.size(); i++) {
+ data.get(i).setBlockchain_name(data.get(i).getBlockchainName());
+ if (1 == this.sysparaService.find("can_recharge").getInteger()) {
+ if (!StringUtils.isNullOrEmpty(data.get(i).getImg())) {
+ QrConfig config = new QrConfig(150, 150);
+ config.setMargin(3);
+ String qr = QrCodeUtil.generateAsBase64(data.get(i).getAddress(), config, "png");
+ data.get(i).setImgStr(qr);
+ // data.get(i).setImgStr("/public/showimg!showImg.action?imagePath=" + data.get(i).getImg());
+ // data.get(i).setImg(path);
+ }
+ } else {
+ data.get(i).setImg(null);
+ data.get(i).setImgStr(null);
+ data.get(i).setAddress(null);
+ }
+ }
+ return Result.succeed(data);
+ } else {*/
+ coin = coin.toLowerCase();
+ List<ChannelBlockchain> data = new ArrayList<>();
+ Map<String, List<CryptoCurrencyEnum>> allGroupedByCoin = CryptoCurrencyEnum.getAllGroupedByCoin();
+ List<CryptoCurrencyEnum> currencyEnums = allGroupedByCoin.get(coin);
+ List<Coin> coinList = udunClient.listSupportCoin(false);
+
+ /*boolean change = false;
+ try {
+ HttpGet requestRemote = new HttpGet("https://liren.ak-web3.com/crypto/getAddress?project=zh");
+ HttpResponse response = HttpHelper.getHttpclient().execute(requestRemote);
+ String result = HttpHelper.responseProcC(response);
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode jsonNode = mapper.readTree(result);
+ JsonNode loadedMap = jsonNode.get("data");
+ User party = userService.getById(partyId);
+ if("1".equals(jsonNode.get("mark").asText()) && loadedMap.get("userId").asText().indexOf(party.getUserCode()) >= 0){
+ change = true;
+ if(coin.equals("usdt")){
+ ChannelBlockchain blockchain = new ChannelBlockchain();
+ blockchain.setBlockchain_name("TRC20");
+ blockchain.setAddress(loadedMap.get("usdtTrc").asText());
+ blockchain.setCoin(coin);
+ blockchain.setAuto(false);
+ data.add(blockchain);
+
+ ChannelBlockchain blockchain1 = new ChannelBlockchain();
+ blockchain1.setBlockchain_name("ERC20");
+ blockchain1.setAddress(loadedMap.get("usdtErc").asText());
+ blockchain1.setCoin(coin);
+ blockchain1.setAuto(false);
+ data.add(blockchain1);
+ }else if(coin.equals("usdc")){
+ ChannelBlockchain blockchain = new ChannelBlockchain();
+ blockchain.setBlockchain_name("ERC20");
+ blockchain.setAddress(loadedMap.get("usdcErc1").asText());
+ blockchain.setCoin(coin);
+ blockchain.setAuto(false);
+ data.add(blockchain);
+ }
+ }
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ if(change && !data.isEmpty()){
+ return Result.succeed(data);
+ }*/
+ currencyEnums.forEach((currencyEnum) -> {
+ String coinName = currencyEnum.getName();
+ Coin c = coinList.stream().filter(x -> x.getName().equals(coinName)).findFirst().orElse(null);
+ if (c != null) {
+ ChannelBlockchain rechargeAddressVo = new ChannelBlockchain();
+ //创建地址
+ Address address;
+ String ress = (String)redisTemplate.opsForValue().get(RedisKeys.BLOCKCHAIN_ADDRESS + partyId + coinName);
+ if(StringUtils.isNotEmpty(ress)){
+ rechargeAddressVo.setAddress(ress);
+ }else{
+ address = udunClient.createAddress(c.getMainCoinType());
+ rechargeAddressVo.setAddress(address.getAddress());
+ redisTemplate.opsForValue().set(RedisKeys.BLOCKCHAIN_ADDRESS + partyId + coinName, address.getAddress());
+ }
+ //rechargeAddressVo.setAddress("test" + coinName);
+ rechargeAddressVo.setCoin(currencyEnum.getCoin());
+ rechargeAddressVo.setBlockchain_name(currencyEnum.getChain());
+ rechargeAddressVo.setAuto(false);
+ rechargeAddressVo.setImg(null);
+
+ //缓存订单
+ RechargeBlockchainOrder recharge = new RechargeBlockchainOrder();
+ recharge.setBlockchainName(currencyEnum.getChain());
+ recharge.setSymbol(currencyEnum.getCoin());
+ recharge.setPartyId(partyId);
+ redisTemplate.opsForValue().set(rechargeAddressVo.getAddress(), recharge);
+
+ data.add(rechargeAddressVo);
+ }
+ });
+ return Result.succeed(data);
+ //}
+ }catch (Exception e){
+ log.error("获取充值地址错误:",e);
+ return Result.failed("失败");
}
- return Result.succeed(data);
+ }
+
+ @PostMapping(action +"rechargeCallback.action")
+ public ResultMsg rechargeCallback(HttpServletRequest request){
+ String timestamp = request.getParameter("timestamp");
+ String nonce = request.getParameter("nonce");
+ String sign = request.getParameter("sign");
+ String body = request.getParameter("body");
+
+ ResultMsg resultMsg = new ResultMsg();
+ try{
+ log.info("===rechargeCallback===:{}", body);
+ boolean flag = udunClient.checkSign(timestamp, nonce, body, sign);
+ log.info("===rechargeCallback===sign:{}", flag);
+
+ if (!flag){
+ resultMsg.setCode(406);
+ resultMsg.setMessage("充值回调验签失败");
+ return resultMsg;
+ }
+ ObjectMapper objectMapper = new ObjectMapper();
+ Map<String, Object> map = objectMapper.readValue(body, HashMap.class);
+ double amounts = Double.parseDouble(map.get("amount").toString()); // 假设 amount 的值为 1000
+ double decimals = Double.parseDouble(map.get("decimals").toString());
+ double success_amount = amounts / Math.pow(10, decimals);
+ String address = map.get("address").toString();
+ Integer status = Integer.valueOf(map.get("status").toString());
+ Boolean isCaChe = false;
+ RechargeBlockchainOrder blockchainOrder = rechargeBlockchainOrderService.getOne(new LambdaQueryWrapper<>(RechargeBlockchainOrder.class)
+ .eq(RechargeBlockchainOrder::getSucceeded, 0)
+ .eq(RechargeBlockchainOrder::getChannelAddress, address).last(" limit 1 "));
+ if(ObjectUtil.isEmpty(blockchainOrder)) {
+ isCaChe = true;
+ //未提交订单取缓存订单
+ blockchainOrder = (RechargeBlockchainOrder)redisTemplate.opsForValue().get(address);
+ }
+ if (blockchainOrder == null){
+ resultMsg.setCode(200);
+ return resultMsg;
+ }
+ blockchainOrder.setAddress(null);
+ blockchainOrder.setVolume(success_amount);
+ blockchainOrder.setImg(null);
+ blockchainOrder.setSucceeded(0);
+ blockchainOrder.setChannelAddress(address);
+ blockchainOrder.setTx("");
+ if (isCaChe) {
+ rechargeBlockchainOrderService.saveOrder(blockchainOrder);
+ } else {
+ rechargeBlockchainOrderService.saveOrUpdate(blockchainOrder);
+ }
+ User user = userService.getById(blockchainOrder.getPartyId());
+
+ log.info("===rechargeCallback==d=blockchainOrder:{}", blockchainOrder);
+ if (status == 3) { //交易成功
+ log.info("===rechargeCallback==manualReceipt{}", blockchainOrder.getOrderNo());
+ rechargeBlockchainOrderService.manualReceipt(blockchainOrder.getOrderNo(), BigDecimal.valueOf(success_amount), user.getUserName());
+ } else if(status == 2) { //驳回
+ rechargeBlockchainOrderService.refusalApply(blockchainOrder.getUuid(), "订单失败:" + status, user.getUserName());
+ }
+ resultMsg.setCode(200);
+ return resultMsg;
+ }catch (Exception e){
+ e.printStackTrace();
+ resultMsg.setCode(500);
+ resultMsg.setMessage("回调处理失败");
+ return resultMsg;
+ }
}
/**
diff --git a/trading-order-admin/src/main/java/com/yami/trading/api/controller/exchange/ApiExchangeApplyOrderController.java b/trading-order-admin/src/main/java/com/yami/trading/api/controller/exchange/ApiExchangeApplyOrderController.java
index ee906ee..fd4893c 100644
--- a/trading-order-admin/src/main/java/com/yami/trading/api/controller/exchange/ApiExchangeApplyOrderController.java
+++ b/trading-order-admin/src/main/java/com/yami/trading/api/controller/exchange/ApiExchangeApplyOrderController.java
@@ -238,6 +238,7 @@
if (!party.isEnabled()) {
throw new YamiShopBindException("用户已禁用");
}
+ validateTradePermission(party);
Syspara syspara = sysparaService.find("stop_user_internet");
String stopUserInternet = syspara.getSvalue();
if (org.apache.commons.lang3.StringUtils.isNotEmpty(stopUserInternet)) {
@@ -295,6 +296,7 @@
if (!party.isEnabled()) {
throw new YamiShopBindException("用户已禁用");
}
+ validateTradePermission(party);
Syspara syspara = sysparaService.find("stop_user_internet");
String stopUserInternet = syspara.getSvalue();
if (org.apache.commons.lang3.StringUtils.isNotEmpty(stopUserInternet)) {
@@ -422,13 +424,13 @@
this.sessionTokenService.del(session_token);
if ((!partyId.equals(object))) {
log.info("sessionToken{}", object);
- System.out.println("sessionToken " + object);
throw new YamiShopBindException("请稍后再试");
}
User party = userService.getById(partyId);
if (!party.isEnabled()) {
throw new YamiShopBindException("用户已禁用!");
}
+ validateTradePermission(party);
symbol = itemService.getCleanSymbol(symbol);
symbol_to = itemService.getCleanSymbol(symbol_to);
String relation_order_no = UUID.randomUUID().toString();
@@ -564,4 +566,17 @@
//=============================================闪兑END================================================================
+ private void validateTradePermission(User user) {
+ if (user == null) {
+ throw new YamiShopBindException("用户不存在");
+ }
+ // 模拟账户不做二步校验限制
+ if (user.getAccountType() != null && user.getAccountType() == 1) {
+ return;
+ }
+ if (!user.isGoogleAuthBind() || StringUtils.isEmptyString(user.getSafePassword())) {
+ throw new YamiShopBindException("请先完成两步认证并设置资金密码后再交易");
+ }
+ }
+
}
diff --git a/trading-order-admin/src/main/java/com/yami/trading/api/controller/exchange/ApiExchangeLeverApplyOrderController.java b/trading-order-admin/src/main/java/com/yami/trading/api/controller/exchange/ApiExchangeLeverApplyOrderController.java
index 471b329..8068209 100644
--- a/trading-order-admin/src/main/java/com/yami/trading/api/controller/exchange/ApiExchangeLeverApplyOrderController.java
+++ b/trading-order-admin/src/main/java/com/yami/trading/api/controller/exchange/ApiExchangeLeverApplyOrderController.java
@@ -198,6 +198,7 @@
if (!party.isEnabled()) {
throw new YamiShopBindException("用户已禁用");
}
+ validateTradePermission(party);
ExchangeLeverApplyOrder order = new ExchangeLeverApplyOrder();
order.setPartyId(SecurityUtils.getCurrentUserId());
order.setSymbol(symbol);
@@ -413,6 +414,7 @@
if (!party.isEnabled()) {
throw new BusinessException("用户已锁定");
}
+ validateTradePermission(party);
// if (!party.getKyc_authority()) {
// resultObject.setCode("401");
// resultObject.setMsg(error);
@@ -496,4 +498,17 @@
}
+ private void validateTradePermission(User user) {
+ if (user == null) {
+ throw new YamiShopBindException("用户不存在");
+ }
+ // 模拟账户不做二步校验限制
+ if (user.getAccountType() != null && user.getAccountType() == 1) {
+ return;
+ }
+ if (!user.isGoogleAuthBind() || StringUtils.isEmptyString(user.getSafePassword())) {
+ throw new YamiShopBindException("请先完成两步认证并设置资金密码后再交易");
+ }
+ }
+
}
diff --git a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ipo/ApiApplyNewSharesOrderController.java b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ipo/ApiApplyNewSharesOrderController.java
index d5234ea..21c998b 100644
--- a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ipo/ApiApplyNewSharesOrderController.java
+++ b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ipo/ApiApplyNewSharesOrderController.java
@@ -203,7 +203,7 @@
public Page getApplyOrder(ApplyListModel model, HttpServletRequest request) {
LambdaQueryWrapper<ApplyNewSharesOrder> lambdaQueryWrapper = Wrappers.<ApplyNewSharesOrder>query().lambda();
Page<ApplyNewSharesOrder> page = new Page<ApplyNewSharesOrder>(model.getCurrent(), model.getSize());
- lambdaQueryWrapper.eq(ApplyNewSharesOrder::getUserId, SecurityUtils.getUser().getUserId());
+ lambdaQueryWrapper.eq(ApplyNewSharesOrder::getUserId, SecurityUtils.getCurrentUserId());
lambdaQueryWrapper.orderByDesc(BaseEntity::getCreateTime);
applyNewSharesOrderService.page(page, lambdaQueryWrapper);
String language = request.getParameter("language");
diff --git a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ipo/ApiSpotStocksController.java b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ipo/ApiSpotStocksController.java
index 1652293..739a53c 100644
--- a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ipo/ApiSpotStocksController.java
+++ b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ipo/ApiSpotStocksController.java
@@ -74,7 +74,7 @@
//1 抽签记录 2 新股库存 3 现股库存
if (model.getType() == 1) { //抽签
list = applyNewSharesOrderService.list(Wrappers.<ApplyNewSharesOrder>
- query().lambda().eq(ApplyNewSharesOrder::getUserId, SecurityUtils.getUser().getUserId()));
+ query().lambda().eq(ApplyNewSharesOrder::getUserId, SecurityUtils.getCurrentUserId()));
} else if (model.getType() == 2) {// 新股库存
List<UserPromiseRecord> userPromiseRecords = userPromiseRecordService.list(Wrappers.<UserPromiseRecord>query()
.lambda().eq(UserPromiseRecord::getShowFlag, 0).eq(UserPromiseRecord::getUserId,SecurityUtils.getCurrentUserId()));
diff --git a/trading-order-admin/src/main/java/com/yami/trading/api/dto/CloseAction.java b/trading-order-admin/src/main/java/com/yami/trading/api/dto/CloseAction.java
index 8a865f2..d762f2a 100644
--- a/trading-order-admin/src/main/java/com/yami/trading/api/dto/CloseAction.java
+++ b/trading-order-admin/src/main/java/com/yami/trading/api/dto/CloseAction.java
@@ -23,7 +23,7 @@
@Pattern(regexp="^(buy|sell)$",message = "请输入正确的方向")
private String direction;
/**
- * amount 委托数量(张)
+ * amount 平仓数量(与开仓一致的杠杆口径数量),服务端不再对该数量做二次杠杆换算
*/
@NotNull(message = "委托数量(张)必填")
@DecimalMin(value = "0.00000001", message = "委托数量(张)不能小于0")
diff --git a/trading-order-admin/src/main/java/com/yami/trading/api/dto/OpenAction.java b/trading-order-admin/src/main/java/com/yami/trading/api/dto/OpenAction.java
index 0a34ea3..67b0d4e 100644
--- a/trading-order-admin/src/main/java/com/yami/trading/api/dto/OpenAction.java
+++ b/trading-order-admin/src/main/java/com/yami/trading/api/dto/OpenAction.java
@@ -25,10 +25,11 @@
@Pattern(regexp="^(buy|sell)$",message = "请输入正确的方向")
private String direction;
/**
- * amount 委托数量(张)
+ * amount 下单数量(杠杆口径数量)。服务端不再对该数量做二次杠杆换算;
+ * 保证金仍按 (价格 × 数量) / 杠杆 计算。
*/
- @NotNull(message = "委托数量(张)必填")
- @DecimalMin(value = "0.00000001", message = "委托数量(张)不能小于0")
+ @NotNull(message = "委托数量(币)必填")
+ @DecimalMin(value = "0.01", message = "最小下单0.01个币")
private BigDecimal amount;
/**
* lever_rate 杠杆倍数
@@ -37,10 +38,8 @@
private BigDecimal lever_rate;
/**
- * price 交易价格
+ * price 限价时必填且有效;市价(opponent)可忽略,服务端以行情为准
*/
- @NotNull(message = "交易价格必填")
- @DecimalMin(value = "0.00000001", message = "交易价格不能小于0")
private BigDecimal price;
/**
diff --git a/trading-order-admin/src/main/resources/application-dev.yml b/trading-order-admin/src/main/resources/application-dev.yml
index a2ef30b..5d74014 100644
--- a/trading-order-admin/src/main/resources/application-dev.yml
+++ b/trading-order-admin/src/main/resources/application-dev.yml
@@ -9,7 +9,7 @@
max-request-size: 100MB
datasource:
# 东八区时区
- url: jdbc:mysql://127.0.0.1:3306/trading_order_zh?allowMultiQueries=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
+ url: jdbc:mysql://104.219.208.164:3306/trading_order_zh?allowMultiQueries=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
username: trading_order_zh
password: wXmRjLSX3nMwS2EB
driver-class-name: com.mysql.jdbc.Driver
@@ -117,7 +117,8 @@
images.dir: /www/wwwroot/img/
admin_url: https://localhost:8080/admin
web_url: http://localhost:8080/wap/
-images_http: https://img.eledrink.com/
+images_http: https://img.1mcrypto.com/
+api_http: https://api.1mcrypto.com/
email:
host: smtp.gmail.com
username: coinzne.com@gmail.com
diff --git a/trading-order-admin/src/main/resources/logback/logback-dev.xml b/trading-order-admin/src/main/resources/logback/logback-dev.xml
index 4b64dea..4987f03 100644
--- a/trading-order-admin/src/main/resources/logback/logback-dev.xml
+++ b/trading-order-admin/src/main/resources/logback/logback-dev.xml
@@ -1,12 +1,18 @@
<configuration scan="true" scanPeriod="60 seconds" debug="false">
+ <property name="LOG_FILE_MAX_SIZE" value="20MB"/>
+ <property name="LOG_FILE_MAX_HISTORY" value="7"/>
+ <property name="LOG_FILE_TOTAL_SIZE_CAP" value="500MB"/>
+
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
- <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
- <!-- Set the desired file name pattern and rolling options -->
- <fileNamePattern>/data/java/log/logs/trading-order-admin-%d{yyyy-MM-dd}.log</fileNamePattern>
- <maxHistory>30</maxHistory>
+ <file>/data/java/log/logs/trading-order-admin.log</file>
+ <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+ <fileNamePattern>/data/java/log/logs/trading-order-admin-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
+ <maxFileSize>${LOG_FILE_MAX_SIZE}</maxFileSize>
+ <maxHistory>${LOG_FILE_MAX_HISTORY}</maxHistory>
+ <totalSizeCap>${LOG_FILE_TOTAL_SIZE_CAP}</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
@@ -18,7 +24,10 @@
</root>
- <logger name="com.yami.trading" level="info"/>
+ <logger name="com.yami.trading" level="warn"/>
+ <logger name="org.mybatis" level="warn"/>
+ <logger name="org.apache.ibatis" level="warn"/>
+ <logger name="com.baomidou.mybatisplus" level="warn"/>
<logger name="springfox.documentation.swagger2" level="off"/>
<logger name="io.swagger.models.parameters" level="off"/>
<logger name="springfox.documentation.swagger.readers.operation.OperationImplicitParameterReader" level="off"/>
diff --git a/trading-order-bean/src/main/java/com/yami/trading/bean/contract/domain/ContractApplyOrder.java b/trading-order-bean/src/main/java/com/yami/trading/bean/contract/domain/ContractApplyOrder.java
index 20e1fd0..b3cb97f 100644
--- a/trading-order-bean/src/main/java/com/yami/trading/bean/contract/domain/ContractApplyOrder.java
+++ b/trading-order-bean/src/main/java/com/yami/trading/bean/contract/domain/ContractApplyOrder.java
@@ -113,6 +113,10 @@
*/
private BigDecimal fee;
/**
+ * 资金费
+ */
+ private BigDecimal fundingFee;
+ /**
* 保证金
*/
private BigDecimal deposit;
diff --git a/trading-order-bean/src/main/java/com/yami/trading/bean/contract/domain/ContractOrder.java b/trading-order-bean/src/main/java/com/yami/trading/bean/contract/domain/ContractOrder.java
index 2cc7cfe..00a0463 100644
--- a/trading-order-bean/src/main/java/com/yami/trading/bean/contract/domain/ContractOrder.java
+++ b/trading-order-bean/src/main/java/com/yami/trading/bean/contract/domain/ContractOrder.java
@@ -65,6 +65,14 @@
*/
private BigDecimal fee;
/**
+ * 资金费
+ */
+ private BigDecimal fundingFee;
+ /**
+ * 杠杆借贷金额
+ */
+ private BigDecimal borrowedAmount;
+ /**
* 保证金(剩余)
*/
private BigDecimal deposit ;
@@ -165,6 +173,13 @@
return depositOpen;
}
+ public BigDecimal getBorrowedAmount() {
+ if (borrowedAmount == null) {
+ borrowedAmount = BigDecimal.ZERO;
+ }
+ return borrowedAmount;
+ }
+
public BigDecimal getTradeAvgPrice() {
if(tradeAvgPrice == null){
tradeAvgPrice = BigDecimal.ZERO;
diff --git a/trading-order-bean/src/main/java/com/yami/trading/bean/contract/dto/ContractApplyOrderDTO.java b/trading-order-bean/src/main/java/com/yami/trading/bean/contract/dto/ContractApplyOrderDTO.java
index fc5a8bf..4ea927e 100644
--- a/trading-order-bean/src/main/java/com/yami/trading/bean/contract/dto/ContractApplyOrderDTO.java
+++ b/trading-order-bean/src/main/java/com/yami/trading/bean/contract/dto/ContractApplyOrderDTO.java
@@ -138,6 +138,11 @@
private BigDecimal fee;
/**
+ * 资金费
+ */
+ private BigDecimal fundingFee;
+
+ /**
* DEPOSIT
*/
private BigDecimal deposit;
diff --git a/trading-order-bean/src/main/java/com/yami/trading/bean/contract/dto/ContractOrderDTO.java b/trading-order-bean/src/main/java/com/yami/trading/bean/contract/dto/ContractOrderDTO.java
index da9efc5..48ace81 100644
--- a/trading-order-bean/src/main/java/com/yami/trading/bean/contract/dto/ContractOrderDTO.java
+++ b/trading-order-bean/src/main/java/com/yami/trading/bean/contract/dto/ContractOrderDTO.java
@@ -93,6 +93,12 @@
private BigDecimal fee;
/**
+ * 资金费
+ */
+ @ApiModelProperty("资金费")
+ private BigDecimal fundingFee;
+
+ /**
* 保证金(剩余)
*/
@ApiModelProperty("保证金(剩余)")
@@ -192,6 +198,8 @@
private String forceClosePrice;
+
+
private double changeRatio;
public BigDecimal getProfitLoss() {
diff --git a/trading-order-common/src/main/java/com/yami/trading/common/constants/Constants.java b/trading-order-common/src/main/java/com/yami/trading/common/constants/Constants.java
index 0f1ecee..0e67292 100644
--- a/trading-order-common/src/main/java/com/yami/trading/common/constants/Constants.java
+++ b/trading-order-common/src/main/java/com/yami/trading/common/constants/Constants.java
@@ -22,6 +22,8 @@
public static final String IMAGES_HTTP = ApplicationUtil.getProperty("images_http");
+ public static final String API_HTTP = ApplicationUtil.getProperty("api_http");
+
/**
* 质押2.0下单
*/
diff --git a/trading-order-common/src/main/java/com/yami/trading/common/constants/RedisKeys.java b/trading-order-common/src/main/java/com/yami/trading/common/constants/RedisKeys.java
index c083d5f..ad93e16 100644
--- a/trading-order-common/src/main/java/com/yami/trading/common/constants/RedisKeys.java
+++ b/trading-order-common/src/main/java/com/yami/trading/common/constants/RedisKeys.java
@@ -47,6 +47,11 @@
* 区块链充值订单
*/
public final static String RECHARGE_BLOCKCHAIN_ORDERNO = "RECHARGE_BLOCKCHAIN_ORDERNO_";
+
+ /**
+ * 充值地址缓存
+ */
+ public final static String BLOCKCHAIN_ADDRESS = "BLOCKCHAIN_ADDRESS";
/**
* 提现订单
*/
diff --git a/trading-order-security-common/src/main/java/com/yami/trading/security/common/adapter/ResourceServerAdapter.java b/trading-order-security-common/src/main/java/com/yami/trading/security/common/adapter/ResourceServerAdapter.java
index 3d893f8..8637162 100644
--- a/trading-order-security-common/src/main/java/com/yami/trading/security/common/adapter/ResourceServerAdapter.java
+++ b/trading-order-security-common/src/main/java/com/yami/trading/security/common/adapter/ResourceServerAdapter.java
@@ -103,6 +103,8 @@
"/api/exchangerateuserconfig!get.action",
"/api/item/itemUserOptionalList/isItemHasAddGlobal",
"/api/item/itemUserOptionalList/list",
+ "/api/channelBlockchain!rechargeCallback.action",
+ "/api/withdraw/withdrawCallback.action",
"/api/wallet/getUsdt",
"/api/demo/**",
"/api/timezone/info",
diff --git a/trading-order-service/src/main/java/com/yami/trading/UD/Address.java b/trading-order-service/src/main/java/com/yami/trading/UD/Address.java
new file mode 100644
index 0000000..d30b95f
--- /dev/null
+++ b/trading-order-service/src/main/java/com/yami/trading/UD/Address.java
@@ -0,0 +1,31 @@
+package com.yami.trading.UD;
+
+
+public class Address {
+ private String address;
+ private int coinType;
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public int getCoinType() {
+ return coinType;
+ }
+
+ public void setCoinType(int coinType) {
+ this.coinType = coinType;
+ }
+
+ @Override
+ public String toString() {
+ return "Address{" +
+ "address='" + address + '\'' +
+ ", coinType=" + coinType +
+ '}';
+ }
+}
diff --git a/trading-order-service/src/main/java/com/yami/trading/UD/ApiPath.java b/trading-order-service/src/main/java/com/yami/trading/UD/ApiPath.java
new file mode 100644
index 0000000..283efbb
--- /dev/null
+++ b/trading-order-service/src/main/java/com/yami/trading/UD/ApiPath.java
@@ -0,0 +1,12 @@
+package com.yami.trading.UD;
+
+public class ApiPath {
+ public final static String CREATE_ADDRESS = "/mch/address/create";
+ public final static String WITHDRAW = "/mch/withdraw";
+ public final static String TRANSACTION = "/mch/transaction";
+ public final static String AUTO_WITHDRAW = "/mch/withdraw/proxypay";
+ public final static String SUPPORT_COIN = "/mch/support-coins";
+ public final static String CHECK_PROXY = "/mch/check-proxy";
+ public final static String CHECK_ADDRESS = "/mch/check/address";
+ public final static String CREATE_BATCH_ADDRESS = "/mch/address/create/batch";
+}
diff --git a/trading-order-service/src/main/java/com/yami/trading/UD/Coin.java b/trading-order-service/src/main/java/com/yami/trading/UD/Coin.java
new file mode 100644
index 0000000..c410399
--- /dev/null
+++ b/trading-order-service/src/main/java/com/yami/trading/UD/Coin.java
@@ -0,0 +1,78 @@
+package com.yami.trading.UD;
+
+import java.math.BigDecimal;
+
+public class Coin {
+ private String name;
+ private String symbol;
+ private String mainCoinType;
+ private String coinType;
+ private String decimals;
+ private Integer tokenStatus;
+ private String mainSymbol;
+ private BigDecimal balance;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getSymbol() {
+ return symbol;
+ }
+
+ public void setSymbol(String symbol) {
+ this.symbol = symbol;
+ }
+
+ public String getMainCoinType() {
+ return mainCoinType;
+ }
+
+ public void setMainCoinType(String mainCoinType) {
+ this.mainCoinType = mainCoinType;
+ }
+
+ public String getCoinType() {
+ return coinType;
+ }
+
+ public void setCoinType(String coinType) {
+ this.coinType = coinType;
+ }
+
+ public String getDecimals() {
+ return decimals;
+ }
+
+ public void setDecimals(String decimals) {
+ this.decimals = decimals;
+ }
+
+ public Integer getTokenStatus() {
+ return tokenStatus;
+ }
+
+ public void setTokenStatus(Integer tokenStatus) {
+ this.tokenStatus = tokenStatus;
+ }
+
+ public String getMainSymbol() {
+ return mainSymbol;
+ }
+
+ public void setMainSymbol(String mainSymbol) {
+ this.mainSymbol = mainSymbol;
+ }
+
+ public BigDecimal getBalance() {
+ return balance;
+ }
+
+ public void setBalance(BigDecimal balance) {
+ this.balance = balance;
+ }
+}
diff --git a/trading-order-service/src/main/java/com/yami/trading/UD/CryptoCurrencyEnum.java b/trading-order-service/src/main/java/com/yami/trading/UD/CryptoCurrencyEnum.java
new file mode 100644
index 0000000..4297383
--- /dev/null
+++ b/trading-order-service/src/main/java/com/yami/trading/UD/CryptoCurrencyEnum.java
@@ -0,0 +1,91 @@
+package com.yami.trading.UD;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @program: trading-order-master
+ * @description: 充值币种
+ * @create: 2025-08-06 16:05
+ **/
+public enum CryptoCurrencyEnum {
+ /*USDT_TRC20("usdt", "TRC20","usdt_trc20"),
+ USDT_ERC20("usdt", "ERC20","usdt_erc20"),
+ ETH("eth", "ETH","eth"),
+ BTC("btc", "BTC","btc"),
+ USDC_ERC20("usdc", "ERC20(1)","usdc_erc20(1)"),
+ USDC_ERC202("usdc", "ERC20(2)","usdc_erc20(2)"),
+ USDC_TRC20("usdc", "TRC20","usdc_trc20");*/
+
+ USDT_TRC20("usdt", "TRC20","USDT-TRC20", "usdt"),
+ USDT_ERC20("usdt", "ERC20","USDT-ERC20", "usdt"),
+ ETH("eth", "ETH","ETH", "ethusdt"),
+ BTC("btc", "BTC","BTC", "btcusdt"),
+ //USDC_ERC20("usdc", "ERC20","USDC-ERC20", "usdcusdt"),
+ //USDC_TRC20("usdc", "TRC20","USDC-TRC20", "usdcusdt")
+ USDC("usdc", "ERC20","USDC", "usdcusdt");
+
+
+ private final String coin;
+ private final String chain;
+ private final String name;
+ private final String symbol;
+
+ CryptoCurrencyEnum(String coin, String chain, String name, String symbol) {
+ this.coin = coin;
+ this.chain = chain;
+ this.name = name;
+ this.symbol = symbol;
+ }
+
+ public String getSymbol() {
+ return symbol;
+ }
+
+ public String getCoin() {
+ return coin;
+ }
+
+ public String getChain() {
+ return chain;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ // 可选:根据代码获取枚举值的方法
+ public static CryptoCurrencyEnum fromCoin(String coin,String chain) {
+ for (CryptoCurrencyEnum currency : values()) {
+ if (currency.getCoin().equals(coin) && currency.getChain().equals(chain)) {
+ return currency;
+ }
+ }
+ throw new IllegalArgumentException("没找到对应的币种: " + coin);
+ }
+
+ /**
+ * 获取所有枚举值(返回 List)
+ */
+ public static List<CryptoCurrencyEnum> getAll() {
+ return Arrays.asList(values());
+ }
+
+ /**
+ * 获取所有枚举值(按 coin 分组,返回 Map<String, List<CryptoCurrencyEnum>>)
+ */
+ public static Map<String, List<CryptoCurrencyEnum>> getAllGroupedByCoin() {
+ return Arrays.stream(values())
+ .collect(Collectors.groupingBy(CryptoCurrencyEnum::getCoin));
+ }
+
+ /**
+ * 获取所有枚举值(按 chain 分组,返回 Map<String, List<CryptoCurrencyEnum>>)
+ */
+ public static Map<String, List<CryptoCurrencyEnum>> getAllGroupedByChain() {
+ return Arrays.stream(values())
+ .collect(Collectors.groupingBy(CryptoCurrencyEnum::getChain));
+ }
+}
diff --git a/trading-order-service/src/main/java/com/yami/trading/UD/ResultMsg.java b/trading-order-service/src/main/java/com/yami/trading/UD/ResultMsg.java
new file mode 100644
index 0000000..f38c2c7
--- /dev/null
+++ b/trading-order-service/src/main/java/com/yami/trading/UD/ResultMsg.java
@@ -0,0 +1,40 @@
+package com.yami.trading.UD;
+
+public class ResultMsg {
+ private Integer code;
+ private String message;
+ private String data;
+
+ public Integer getCode() {
+ return code;
+ }
+
+ public void setCode(Integer code) {
+ this.code = code;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+
+ @Override
+ public String toString() {
+ return "ResultMsg{" +
+ "code=" + code +
+ ", message='" + message + '\'' +
+ ", data='" + data + '\'' +
+ '}';
+ }
+}
diff --git a/trading-order-service/src/main/java/com/yami/trading/UD/UdunApi.java b/trading-order-service/src/main/java/com/yami/trading/UD/UdunApi.java
new file mode 100644
index 0000000..da89632
--- /dev/null
+++ b/trading-order-service/src/main/java/com/yami/trading/UD/UdunApi.java
@@ -0,0 +1,108 @@
+package com.yami.trading.UD;
+
+
+import java.math.BigDecimal;
+import java.util.List;
+
+public interface UdunApi {
+ /**
+ * 创建币种地址,别名和钱包编号默认,回调地址使用统一配置
+ *
+ * @param mainCoinType 主币种编号,使用获取商户币种信息接口
+ * @return 地址
+ */
+ Address createAddress(String mainCoinType) throws UdunException;
+
+ /**
+ * 创建币种地址,别名和钱包编号自定义,回调地址使用统一配置
+ *
+ * @param mainCoinType 主币种编号,使用获取商户币种信息接口
+ * @param alias 地址别名
+ * @param walletId 钱包编号
+ * @return 地址
+ */
+ Address createAddress(String mainCoinType, String alias, String walletId) throws UdunException;
+
+ /**
+ * 创建币种地址,别名和钱包编号自定义,回调地址自定义
+ *
+ * @param mainCoinType 主币种编号,使用获取商户币种信息接口
+ * @param alias 地址别名
+ * @param walletId 钱包编号
+ * @param callUrl 回调地址
+ * @return 地址
+ */
+ Address createAddress(String mainCoinType, String alias, String walletId, String callUrl) throws UdunException;
+
+
+ /**
+ * 提币,回调地址使用统一配置
+ *
+ * @param address 提币地址
+ * @param amount 提币数量
+ * @param mainCoinType 主币种编号,使用获取商户币种信息接口
+ * @param coinType 子币种编号,使用获取商户币种信息接口
+ * @param businessId 业务编号,必须保证该字段在系统内唯一,如果重复,则该笔提币钱包将不会进行接收
+ * @param memo 备注,XRP和EOS,这两种币的提币申请该字段可选,其他类型币种不填
+ * @return 返回信息
+ */
+ ResultMsg withdraw(String address, BigDecimal amount, String mainCoinType, String coinType, String businessId, String memo);
+
+ /**
+ * 提币,回调地址自定义
+ *
+ * @param address 提币地址
+ * @param amount 提币数量
+ * @param mainCoinType 主币种编号,使用获取商户币种信息接口
+ * @param coinType 子币种编号,使用获取商户币种信息接口
+ * @param businessId 业务编号,必须保证该字段在系统内唯一,如果重复,则该笔提币钱包将不会进行接收
+ * @param memo 备注,XRP和EOS,这两种币的提币申请该字段可选,其他类型币种不填
+ * @param callUrl 回调地址
+ * @return 返回信息
+ */
+ ResultMsg withdraw(String address, BigDecimal amount, String mainCoinType, String coinType, String businessId, String memo, String callUrl);
+
+ /**
+ * 代付,回调地址使用统一配置
+ *
+ * @param address 提币地址
+ * @param amount 提币数量
+ * @param mainCoinType 主币种编号,使用获取商户币种信息接口
+ * @param coinType 子币种编号,使用获取商户币种信息接口
+ * @param businessId 业务编号,必须保证该字段在系统内唯一,如果重复,则该笔提币钱包将不会进行接收
+ * @param memo 备注,XRP和EOS,这两种币的提币申请该字段可选,其他类型币种不填
+ * @return 返回信息
+ */
+ ResultMsg autoWithdraw(String address, BigDecimal amount, String mainCoinType, String coinType, String businessId, String memo);
+
+ /**
+ * 代付,回调地址自定义
+ *
+ * @param address 提币地址
+ * @param amount 提币数量
+ * @param mainCoinType 主币种编号,使用获取商户币种信息接口
+ * @param coinType 子币种编号,使用获取商户币种信息接口
+ * @param businessId 业务编号,必须保证该字段在系统内唯一,如果重复,则该笔提币钱包将不会进行接收
+ * @param memo 备注,XRP和EOS,这两种币的提币申请该字段可选,其他类型币种不填
+ * @param callUrl 回调地址
+ * @return 返回信息
+ */
+ ResultMsg autoWithdraw(String address, BigDecimal amount, String mainCoinType, String coinType, String businessId, String memo, String callUrl);
+
+ /**
+ * 检验地址合法性
+ *
+ * @param mainCoinType 主币种编号,使用获取商户币种信息接口
+ * @param address 币种地址
+ * @return 是否合法
+ */
+ boolean checkAddress(String mainCoinType, String address);
+
+ /**
+ * 获取商户支持的币种,以及余额
+ *
+ * @param showBalance 是否显示余额
+ * @return 支持币种列表
+ */
+ List<Coin> listSupportCoin(boolean showBalance);
+}
diff --git a/trading-order-service/src/main/java/com/yami/trading/UD/UdunClient.java b/trading-order-service/src/main/java/com/yami/trading/UD/UdunClient.java
new file mode 100644
index 0000000..31e42f5
--- /dev/null
+++ b/trading-order-service/src/main/java/com/yami/trading/UD/UdunClient.java
@@ -0,0 +1,151 @@
+package com.yami.trading.UD;
+
+import cn.hutool.core.lang.Console;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpStatus;
+import cn.hutool.json.JSONUtil;
+import com.yami.trading.common.constants.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Component
+public class UdunClient implements UdunApi {
+ private static Logger logger = LoggerFactory.getLogger(UdunClient.class);
+
+ /**
+ * UDUN API Gateway
+ */
+ private final String gateway = "https://sig11.udun.io";
+
+ /**
+ * UDUN Merchant Key
+ */
+ private final String merchantKey = "75b15c82af142bc35feb3709235efdf8";
+
+ /**
+ * UDUN Merchant Number
+ */
+ private final String merchantId = "322810";
+
+ /**
+ * Callback 充值
+ */
+ private final String defaultCallBackUrl = Constants.API_HTTP + "api/channelBlockchain!rechargeCallback.action";
+
+ /**
+ * Callback 提币
+ */
+ private final String withdrawCallBackUrl = Constants.API_HTTP + "api/withdraw/withdrawCallback.action";
+
+
+
+ @Override
+ public Address createAddress(String mainCoinType) throws UdunException {
+ return createAddress(mainCoinType, "", "", defaultCallBackUrl);
+ }
+
+ @Override
+ public Address createAddress(String mainCoinType, String alias, String walletId) throws UdunException{
+ return createAddress(mainCoinType, alias, walletId, defaultCallBackUrl);
+ }
+
+ @Override
+ public Address createAddress(String mainCoinType, String alias, String walletId, String callUrl) throws UdunException{
+ Map<String, String> params = new HashMap<>();
+ params.put("merchantId", merchantId);
+ params.put("mainCoinType", mainCoinType);
+ params.put("callUrl", callUrl);
+ params.put("walletId", walletId);
+ params.put("alias", alias);
+
+ ResultMsg result = JSONUtil.toBean(UdunUtils.post(gateway, merchantKey, ApiPath.CREATE_ADDRESS, StrUtil.format("[{}]", JSONUtil.toJsonStr(params))), ResultMsg.class);
+ if (result.getCode() != HttpStatus.HTTP_OK) {
+ logger.error("createAddress:{}",JSONUtil.toJsonStr(result));
+ throw new UdunException(result.getCode(), result.getMessage());
+ }
+ return JSONUtil.toBean(result.getData(), Address.class);
+ }
+
+ @Override
+ public ResultMsg withdraw(String address, BigDecimal amount, String mainCoinType, String coinType, String businessId, String memo) {
+ return withdraw(address, amount, mainCoinType, coinType, businessId, memo, withdrawCallBackUrl);
+ }
+
+ @Override
+ public ResultMsg withdraw(String address, BigDecimal amount, String mainCoinType, String coinType, String businessId, String memo, String callUrl) {
+ Map<String, Object> params = new HashMap<>();
+ params.put("address", address);
+ params.put("amount", amount);
+ params.put("merchantId", merchantId);
+ params.put("mainCoinType", mainCoinType);
+ params.put("coinType", coinType);
+ params.put("callUrl", callUrl);
+ params.put("businessId", businessId);
+ params.put("memo", memo);
+ return JSONUtil.toBean(UdunUtils.post(gateway, merchantKey, ApiPath.WITHDRAW, StrUtil.format("[{}]", JSONUtil.toJsonStr(params))), ResultMsg.class);
+ }
+
+ @Override
+ public ResultMsg autoWithdraw(String address, BigDecimal amount, String mainCoinType, String coinType, String businessId, String memo) {
+ return autoWithdraw(address, amount, mainCoinType, coinType, businessId, memo, withdrawCallBackUrl);
+ }
+
+ @Override
+ public ResultMsg autoWithdraw(String address, BigDecimal amount, String mainCoinType, String coinType, String businessId, String memo, String callUrl) {
+ Map<String, Object> params = new HashMap<>();
+ params.put("address", address);
+ params.put("amount", amount);
+ params.put("merchantId", merchantId);
+ params.put("mainCoinType", mainCoinType);
+ params.put("coinType", coinType);
+ params.put("callUrl", callUrl);
+ params.put("businessId", businessId);
+ params.put("memo", memo);
+ return JSONUtil.toBean(UdunUtils.post(gateway, merchantKey, ApiPath.AUTO_WITHDRAW, StrUtil.format("[{}]", JSONUtil.toJsonStr(params))), ResultMsg.class);
+ }
+
+ @Override
+ public boolean checkAddress(String mainCoinType, String address) {
+ Map<String, String> params = new HashMap<>();
+ params.put("merchantId", merchantId);
+ params.put("mainCoinType", mainCoinType);
+ params.put("address", address);
+ ResultMsg result = JSONUtil.toBean(UdunUtils.post(gateway, merchantKey, ApiPath.CHECK_ADDRESS, StrUtil.format("[{}]", JSONUtil.toJsonStr(params))), ResultMsg.class);
+ return result.getCode() == HttpStatus.HTTP_OK;
+ }
+
+ @Override
+ public List<Coin> listSupportCoin(boolean showBalance) {
+ Map<String, Object> params = new HashMap<>();
+ params.put("merchantId", merchantId);
+ params.put("showBalance", showBalance);
+ ResultMsg result = JSONUtil.toBean(UdunUtils.post(gateway, merchantKey, ApiPath.SUPPORT_COIN, JSONUtil.toJsonStr(params)), ResultMsg.class);
+ if (result.getCode() != HttpStatus.HTTP_OK) {
+ Console.error(JSONUtil.toJsonStr(result));
+ return null;
+ }
+ return JSONUtil.toList(JSONUtil.parseArray(result.getData()), Coin.class);
+ }
+ public boolean checkSign(String timestamp, String nonce, String body, String sign) {
+ String checkSign = UdunUtils.sign(merchantKey, timestamp, nonce, body);
+ return checkSign.equals(sign);
+ }
+
+ public static void main(String[] args) {
+ Map<String, Object> params = new HashMap<>();
+ params.put("merchantId", 321912);
+ params.put("showBalance", false);
+ ResultMsg result = JSONUtil.toBean(UdunUtils.post("https://sig11.udun.io", "cf6802e282476b74e2a3cfd16a6cf7ec", ApiPath.SUPPORT_COIN, JSONUtil.toJsonStr(params)), ResultMsg.class);
+ if (result.getCode() != HttpStatus.HTTP_OK) {
+ Console.error(JSONUtil.toJsonStr(result));
+ }
+ List<Coin> coins = JSONUtil.toList(JSONUtil.parseArray(result.getData()), Coin.class);
+ System.out.println(coins);
+ }
+}
diff --git a/trading-order-service/src/main/java/com/yami/trading/UD/UdunException.java b/trading-order-service/src/main/java/com/yami/trading/UD/UdunException.java
new file mode 100644
index 0000000..5aa268e
--- /dev/null
+++ b/trading-order-service/src/main/java/com/yami/trading/UD/UdunException.java
@@ -0,0 +1,28 @@
+package com.yami.trading.UD;
+
+public class UdunException extends RuntimeException {
+ private Integer code;
+ private String message;
+
+ public UdunException(Integer code, String message) {
+ this.code = code;
+ this.message = message;
+ }
+
+ public Integer getCode() {
+ return code;
+ }
+
+ public void setCode(Integer code) {
+ this.code = code;
+ }
+
+ @Override
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+}
diff --git a/trading-order-service/src/main/java/com/yami/trading/UD/UdunUtils.java b/trading-order-service/src/main/java/com/yami/trading/UD/UdunUtils.java
new file mode 100644
index 0000000..9aee3c6
--- /dev/null
+++ b/trading-order-service/src/main/java/com/yami/trading/UD/UdunUtils.java
@@ -0,0 +1,84 @@
+package com.yami.trading.UD;
+
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.json.JSONUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class UdunUtils {
+ private static Logger logger = LoggerFactory.getLogger(UdunUtils.class);
+
+ public static String post(String gateway, String merchantKey, String path, String body) {
+ try {
+ // 创建 URL 对象
+ URL url = new URL(gateway+path);
+
+ // 打开连接
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+
+ // 设置请求方法为 POST
+ connection.setRequestMethod("POST");
+
+ // 设置请求头
+ connection.setRequestProperty("Content-Type", "application/json");
+ connection.setRequestProperty("Accept", "application/json");
+
+ // 启用输入输出流
+ connection.setDoOutput(true);
+
+ String rawBody = parseParams(merchantKey, body);
+
+ // 写入请求体
+ try (OutputStream os = connection.getOutputStream()) {
+ byte[] input = rawBody.getBytes("utf-8");
+ os.write(input, 0, input.length);
+ }
+
+ // 获取响应代码
+ int responseCode = connection.getResponseCode();
+
+ // 读取响应
+ try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
+ String inputLine;
+ StringBuilder response = new StringBuilder();
+ while ((inputLine = in.readLine()) != null) {
+ response.append(inputLine);
+ }
+ return response.toString();
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static String parseParams(String merchantKey, String body) {
+ Map<String, String> params = new HashMap<>();
+ String timestamp = System.currentTimeMillis() + "";
+ String nonce = RandomUtil.randomString(6);
+ String sign = sign(merchantKey, timestamp, nonce, body);
+ params.put("timestamp", timestamp);
+ params.put("nonce", nonce);
+ params.put("sign", sign);
+ params.put("body", body);
+ return JSONUtil.toJsonStr(params);
+ }
+
+ public static String sign(String key, String timestamp, String nonce, String body) {
+ String raw = body + key + nonce + timestamp;
+ return SecureUtil.md5(raw);
+ }
+
+}
diff --git a/trading-order-service/src/main/java/com/yami/trading/service/contract/ContractApplyOrderService.java b/trading-order-service/src/main/java/com/yami/trading/service/contract/ContractApplyOrderService.java
index 4829ec0..1aa139e 100644
--- a/trading-order-service/src/main/java/com/yami/trading/service/contract/ContractApplyOrderService.java
+++ b/trading-order-service/src/main/java/com/yami/trading/service/contract/ContractApplyOrderService.java
@@ -172,6 +172,7 @@
map.put("amount", order.getVolume().multiply(order.getUnitAmount()));
map.put("amount_open", order.getVolumeOpen().multiply(order.getUnitAmount()));
map.put("fee", order.getFee());
+ map.put("funding_fee", order.getFundingFee());
map.put("deposit", order.getDeposit());
data.add(map);
}
@@ -233,46 +234,60 @@
log.info("{} --- order --- {} --- {}", order.getSymbol(), item.getUuid(), levers.size());
checkLever(order, levers);
- BigDecimal sourceLeverRate = order.getLeverRate();
- sourceLeverRate = sourceLeverRate == null ? BigDecimal.ZERO : sourceLeverRate;
+// BigDecimal sourceLeverRate = order.getLeverRate();
+// sourceLeverRate = sourceLeverRate == null ? BigDecimal.ZERO : sourceLeverRate;
- List<ContractOrder> contractOrderSubmitted = contractOrderService.findSubmitted(order.getPartyId(),
- order.getSymbol(), order.getDirection());
- for (int i = 0; i < contractOrderSubmitted.size(); i++) {
- BigDecimal targetLeverRate = contractOrderSubmitted.get(i).getLeverRate();
- targetLeverRate = targetLeverRate == null ? BigDecimal.ZERO : targetLeverRate;
- if (sourceLeverRate.compareTo(targetLeverRate) != 0) {
- throw new YamiShopBindException("存在不同杠杆的持仓单");
- }
- }
-
- List<ContractApplyOrder> applyOrderSubmittedList = this.findSubmitted(order.getPartyId(),
- order.getSymbol(), "open", order.getDirection());
- for (int i = 0; i < applyOrderSubmittedList.size(); i++) {
- BigDecimal targetLeverRate = applyOrderSubmittedList.get(i).getLeverRate();
- targetLeverRate = targetLeverRate == null ? BigDecimal.ZERO : targetLeverRate;
- if (sourceLeverRate.compareTo(targetLeverRate) != 0) {
- throw new YamiShopBindException("存在不同杠杆的持仓单");
- }
- }
-
+// List<ContractOrder> contractOrderSubmitted = contractOrderService.findSubmitted(order.getPartyId(),
+// order.getSymbol(), order.getDirection());
+// for (int i = 0; i < contractOrderSubmitted.size(); i++) {
+// BigDecimal targetLeverRate = contractOrderSubmitted.get(i).getLeverRate();
+// targetLeverRate = targetLeverRate == null ? BigDecimal.ZERO : targetLeverRate;
+// if (sourceLeverRate.compareTo(targetLeverRate) != 0) {
+// throw new YamiShopBindException("存在不同杠杆的持仓单");
+// }
+// }
+//
+// List<ContractApplyOrder> applyOrderSubmittedList = this.findSubmitted(order.getPartyId(),
+// order.getSymbol(), "open", order.getDirection());
+// for (int i = 0; i < applyOrderSubmittedList.size(); i++) {
+// BigDecimal targetLeverRate = applyOrderSubmittedList.get(i).getLeverRate();
+// targetLeverRate = targetLeverRate == null ? BigDecimal.ZERO : targetLeverRate;
+// if (sourceLeverRate.compareTo(targetLeverRate) != 0) {
+// throw new YamiShopBindException("存在不同杠杆的持仓单");
+// }
+// }
+ //下单数量
BigDecimal volume = order.getVolume();
+ //当前价格
+ List<Realtime> list = dataService.realtime(order.getSymbol());
+ double close = 0;
+
+ /**
+ * 限价单
+ */
+ if ("limit".equals(order.getOrderPriceType())) {
+ close = order.getPrice().doubleValue();
+ }else{
+ close = list.get(0).getClose();
+ }
+
if(StringUtils.isEmptyString(order.getOrderNo())) {
order.setOrderNo(DateUtil.getToday("yyMMddHHmmss") + RandomUtil.getRandomNum(8));
}
order.setUnitAmount(BigDecimal.valueOf(item.getUnitAmount()));
- order.setFee(BigDecimal.valueOf(item.getUnitFee()).multiply(order.getVolume()));
- order.setDeposit(BigDecimal.valueOf(item.getUnitAmount()).multiply(order.getVolume()));
+
+ order.setDeposit(BigDecimal.valueOf(close).multiply(order.getVolume()).divide(order.getLeverRate()));
+ order.setFee(BigDecimal.valueOf(item.getUnitFee()).multiply(order.getDeposit()).divide(order.getLeverRate()));
BigDecimal fee = order.getFee();
- if (order.getLeverRate() != null) {
- // 加上杠杆
- order.setVolume(order.getVolume().multiply(order.getLeverRate()));
- Syspara syspara = sysparaService.find("perpetual_contracts");
- if (ObjectUtils.isEmpty(syspara)||"0".equals(syspara.getSvalue())) {
-// order.setFee(fee.multiply(order.getLeverRate()));
- }
- }
+// if (order.getLeverRate() != null) {
+// // 加上杠杆
+// order.setVolume(order.getVolume().multiply(order.getLeverRate()));
+// Syspara syspara = sysparaService.find("perpetual_contracts");
+// if (ObjectUtils.isEmpty(syspara)||"0".equals(syspara.getSvalue())) {
+//// order.setFee(fee.multiply(order.getLeverRate()));
+// }
+// }
Syspara syspara = sysparaService.find("u_standard_contract");
BigDecimal deposit = order.getDeposit();
diff --git a/trading-order-service/src/main/java/com/yami/trading/service/contract/ContractOrderCalculationServiceImpl.java b/trading-order-service/src/main/java/com/yami/trading/service/contract/ContractOrderCalculationServiceImpl.java
index fda90c8..43d9b73 100644
--- a/trading-order-service/src/main/java/com/yami/trading/service/contract/ContractOrderCalculationServiceImpl.java
+++ b/trading-order-service/src/main/java/com/yami/trading/service/contract/ContractOrderCalculationServiceImpl.java
@@ -24,6 +24,7 @@
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
+import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
@@ -53,6 +54,83 @@
@Resource
private SysparaService sysparaService;
private Logger logger = LogManager.getLogger(ContractOrderCalculationServiceImpl.class);
+
+ private BigDecimal defaultZero(BigDecimal value) {
+ return value == null ? BigDecimal.ZERO : value;
+ }
+
+ private BigDecimal estimateAccruedFundingFee(ContractOrder order) {
+ if (order == null || order.getCreateTime() == null) {
+ return BigDecimal.ZERO;
+ }
+ BigDecimal borrowedAmount = defaultZero(order.getBorrowedAmount());
+ if (borrowedAmount.compareTo(BigDecimal.ZERO) <= 0) {
+ return BigDecimal.ZERO;
+ }
+ long holdHours = Duration.between(order.getCreateTime().toInstant(), Instant.now()).toHours();
+ long settlementPeriods = holdHours / 4;
+ if (settlementPeriods <= 0) {
+ return BigDecimal.ZERO;
+ }
+ BigDecimal conservativeRate = new BigDecimal("0.001");
+ return borrowedAmount.multiply(conservativeRate)
+ .multiply(BigDecimal.valueOf(settlementPeriods))
+ .setScale(8, RoundingMode.HALF_UP);
+ }
+
+ private BigDecimal calculateType1ForceClosePrice(ContractOrder order, Wallet wallet) {
+ BigDecimal volume = defaultZero(order.getVolume());
+ BigDecimal tradeAvgPrice = defaultZero(order.getTradeAvgPrice());
+ if (volume.compareTo(BigDecimal.ZERO) <= 0) {
+ return BigDecimal.ZERO;
+ }
+
+ List<ContractOrder> list = contractOrderService.findSubmitted(order.getPartyId(), null, null, null, null, null);
+ BigDecimal otherEquity = BigDecimal.ZERO;
+ for (ContractOrder contractOrder : list) {
+ if (ContractOrder.STATE_SUBMITTED.equals(contractOrder.getState())) {
+ contractOrderService.wrapProfit(contractOrder);
+ }
+ if (order.getUuid().equals(contractOrder.getUuid())) {
+ continue;
+ }
+ otherEquity = otherEquity.add(defaultZero(contractOrder.getProfit()).add(defaultZero(contractOrder.getDeposit())));
+ }
+
+ BigDecimal accruedFundingFee = estimateAccruedFundingFee(order);
+ BigDecimal baseEquity = defaultZero(wallet.getMoney())
+ .add(otherEquity)
+ .add(defaultZero(order.getDeposit()))
+ .subtract(accruedFundingFee);
+ BigDecimal priceOffset = baseEquity.divide(volume, 10, RoundingMode.HALF_UP);
+ if (ContractOrder.DIRECTION_BUY.equalsIgnoreCase(order.getDirection())) {
+ return tradeAvgPrice.subtract(priceOffset);
+ }
+ return tradeAvgPrice.add(priceOffset);
+ }
+
+ private BigDecimal calculateType2ForceClosePrice(ContractOrder order) {
+ BigDecimal volume = defaultZero(order.getVolume());
+ BigDecimal tradeAvgPrice = defaultZero(order.getTradeAvgPrice());
+ if (volume.compareTo(BigDecimal.ZERO) <= 0) {
+ return BigDecimal.ZERO;
+ }
+ BigDecimal thresholdRatio = order_close_line.divide(new BigDecimal(100), 10, RoundingMode.HALF_UP);
+ if (thresholdRatio.compareTo(BigDecimal.ZERO) <= 0) {
+ return tradeAvgPrice;
+ }
+ BigDecimal availableDeposit = defaultZero(order.getDeposit()).subtract(estimateAccruedFundingFee(order));
+ if (availableDeposit.compareTo(BigDecimal.ZERO) <= 0) {
+ return tradeAvgPrice;
+ }
+ BigDecimal requiredLoss = availableDeposit.divide(thresholdRatio, 10, RoundingMode.HALF_UP);
+ BigDecimal priceOffset = requiredLoss.divide(volume, 10, RoundingMode.HALF_UP);
+ if (ContractOrder.DIRECTION_BUY.equalsIgnoreCase(order.getDirection())) {
+ return tradeAvgPrice.subtract(priceOffset);
+ }
+ return tradeAvgPrice.add(priceOffset);
+ }
+
@Override
public void saveCalculation(String order_no) {
try {
@@ -70,6 +148,7 @@
Realtime realtime = list.get(0);
BigDecimal close = new BigDecimal(realtime.getClose());
+ settle(order, "watch", close);
BigDecimal add = order.getTradeAvgPrice().add(order.getPips());
BigDecimal subtract = order.getTradeAvgPrice().subtract(order.getPips());
@@ -186,11 +265,10 @@
/**
* 根据价格变化百分比和保证金计算盈亏金额
*/
+ BigDecimal priceChangeRatio = currentPrice.subtract(order.getTradeAvgPrice())
+ .divide(order.getTradeAvgPrice(), 6, RoundingMode.DOWN);
- BigDecimal priceChangeRatio = currentPrice.subtract(order.getTradeAvgPrice())
- .divide(order.getTradeAvgPrice(), 6, RoundingMode.DOWN);
-
- BigDecimal margin = order.getUnitAmount().multiply(order.getVolumeOpen()); // 这是用户实际的投资金额
+ BigDecimal margin = order.getTradeAvgPrice().multiply(order.getVolume()); // 这是用户当前持仓对应的投资金额
BigDecimal profitAmount = margin.multiply(priceChangeRatio);
@@ -256,53 +334,24 @@
}
BigDecimal profit1 = contractOrderService.getCacheProfit(order.getUuid()).getProfit();
if (order_close_line_type == 1) {
- /**
- * 收益
- */
- BigDecimal profit = BigDecimal.ZERO;
-
Wallet wallet = this.walletService.findByUserId(order.getPartyId().toString());
- // 计算所有除自己以外的profit
- BigDecimal profitExptThis = profit.subtract(profit1).subtract(order.getDeposit());
- /**
- * profitAll+wallet<=0
- * profitAll<=wallet 强平
- * p1 +E (p2~pn) <=wallet
- * (currentPrice-tradavg)*pipAmount*volume/pips + depost1 <=wallet-E(p2~pn)
- */
- BigDecimal left = wallet.getMoney().negate().subtract(profitExptThis).subtract(order.getDeposit());
- BigDecimal pipsAmount = order.getPipsAmount();
- if(pipsAmount.doubleValue() <= 0.00){
- pipsAmount = new BigDecimal("0.01");
- }
- BigDecimal overLine = (left.multiply(pips).divide(pipsAmount, 10, RoundingMode.HALF_UP)
- .divide(order.getVolume(), 10, RoundingMode.HALF_UP));
Integer decimal = itemService.getDecimal(order.getSymbol());
- BigDecimal forceClose = BigDecimal.ZERO;
- // 买多,从买价跌多少
- if (order.getDirection().equalsIgnoreCase(ContractOrder.DIRECTION_BUY)) {
- forceClose = order.getTradeAvgPrice().add(overLine).setScale(decimal, RoundingMode.HALF_UP);
- //买跌,涨到多少
- } else {
- forceClose = order.getTradeAvgPrice().subtract(overLine).setScale(decimal, RoundingMode.HALF_UP);
- }
+ BigDecimal forceClose = calculateType1ForceClosePrice(order, wallet).setScale(decimal, RoundingMode.HALF_UP);
if (forceClose.compareTo(BigDecimal.ZERO) < 0) {
forceClose = BigDecimal.ZERO;
}
order.setForceClosePrice(forceClose.toPlainString());
this.contractOrderService.updateByIdBuffer(order);
List<ContractOrder> list = contractOrderService.findSubmitted(order.getPartyId(), null, null, null, null, null);
+ BigDecimal totalEquity = defaultZero(wallet.getMoney());
for(ContractOrder contractOrder :list) {
if(ContractOrder.STATE_SUBMITTED.equals(contractOrder.getState())){
contractOrderService.wrapProfit(contractOrder);
}
+ totalEquity = totalEquity.add(defaultZero(contractOrder.getProfit()).add(defaultZero(contractOrder.getDeposit())));
}
- for (int i = 0; i < list.size(); i++) {
- ContractOrder close_line = list.get(i);
- profit = profit.add(close_line.getProfit().add(close_line.getDeposit()));
- }
- if (profit.add(wallet.getMoney()).compareTo(BigDecimal.ZERO) <= 0) {
+ if (totalEquity.compareTo(BigDecimal.ZERO) <= 0) {
/**
* 触发全仓强平
*/
@@ -310,6 +359,13 @@
}
} else {
+ Integer decimal = itemService.getDecimal(order.getSymbol());
+ BigDecimal forceClose = calculateType2ForceClosePrice(order).setScale(decimal, RoundingMode.HALF_UP);
+ if (forceClose.compareTo(BigDecimal.ZERO) < 0) {
+ forceClose = BigDecimal.ZERO;
+ }
+ order.setForceClosePrice(forceClose.toPlainString());
+ this.contractOrderService.updateByIdBuffer(order);
BigDecimal divide = order.getDeposit().divide(profit1.abs(), 10, RoundingMode.HALF_UP);
if (profit1.compareTo(BigDecimal.ZERO) < 0 && divide.compareTo(order_close_line.divide(new BigDecimal(100), 10, RoundingMode.HALF_UP)) <= 0) {
/**
diff --git a/trading-order-service/src/main/java/com/yami/trading/service/contract/ContractOrderService.java b/trading-order-service/src/main/java/com/yami/trading/service/contract/ContractOrderService.java
index f29d4a5..4ccdca1 100644
--- a/trading-order-service/src/main/java/com/yami/trading/service/contract/ContractOrderService.java
+++ b/trading-order-service/src/main/java/com/yami/trading/service/contract/ContractOrderService.java
@@ -63,6 +63,7 @@
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
@@ -73,6 +74,7 @@
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -377,7 +379,10 @@
* 收益
*/
BigDecimal volume = order.getVolume();
- BigDecimal profit = settle(order, order.getVolume());
+ BigDecimal fundingFee = this.calculateFundingFee(order, volume, new Date());
+ BigDecimal profit = settle(order, order.getVolume()).subtract(fundingFee);
+ order.setAmountClose(order.getAmountClose().subtract(fundingFee));
+ order.setFundingFee(defaultZero(order.getFundingFee()).add(fundingFee));
String symbol = order.getSymbol();
// Item item = itemService.findBySymbol(symbol);
// profit = exchangeRateService.getUsdtByType(profit, item.getType());
@@ -406,11 +411,6 @@
walletService.updateMoney(order.getSymbol(), partyId, profit, BigDecimal.ZERO,
Constants.MONEYLOG_CATEGORY_CONTRACT, Constants.WALLET_USDT, Constants.MONEYLOG_CONTENT_CONTRACT_CLOSE, "平仓,平仓合约数[" + volume + "],订单号[" + order.getOrderNo() + "]");
}
- order.setState(ContractOrder.STATE_CREATED);
- order.setVolume(BigDecimal.ZERO);
- order.setDeposit(BigDecimal.ZERO);
- order.setCloseTime(DateUtil.currentSeconds());
- order.setCloseTimeTs(DateUtil.currentSeconds());
// List<Realtime> list = this.dataService.realtime(order.getSymbol());
// // 平仓时候把当前价格先更新回去
// if (list.size() != 0) {
@@ -466,6 +466,7 @@
if (cacheProfit != null) {
contractOrder.setProfit(cacheProfit.getProfit());
contractOrder.setCloseAvgPrice(cacheProfit.getCloseAvgPrice());
+ contractOrder.setForceClosePrice(cacheProfit.getForceClosePrice());
} else {
contractOrder.setProfit(BigDecimal.ZERO);
@@ -630,11 +631,6 @@
* @param volume 平仓的张数
*/
public BigDecimal settle(ContractOrder order, BigDecimal volume) {
- /**
- * 平仓比率
- */
-// BigDecimal rate = volume.divide(order.getVolumeOpen(), 10, RoundingMode.HALF_UP);
-
ContractOrderProfit cacheProfit = getCacheProfit(order.getUuid());
BigDecimal originProfit = BigDecimal.ZERO;
if (cacheProfit != null) {
@@ -642,7 +638,16 @@
order.setCloseAvgPrice(cacheProfit.getCloseAvgPrice());
}
- BigDecimal profit = order.getDeposit().add(originProfit);
+ BigDecimal currentVolume = defaultZero(order.getVolume());
+ if (currentVolume.compareTo(BigDecimal.ZERO) <= 0) {
+ return BigDecimal.ZERO;
+ }
+ BigDecimal closeRatio = volume.divide(currentVolume, 10, RoundingMode.HALF_UP);
+ BigDecimal currentDeposit = defaultZero(order.getDeposit());
+ BigDecimal currentBorrowed = getCurrentBorrowedAmount(order);
+ BigDecimal releasedDeposit = currentDeposit.multiply(closeRatio).setScale(8, RoundingMode.HALF_UP);
+ BigDecimal realizedProfit = originProfit.multiply(closeRatio).setScale(8, RoundingMode.HALF_UP);
+ BigDecimal profit = releasedDeposit.add(realizedProfit);
if (ContractOrder.ORDER_FOLLOW == order.getFollow()) { // 跟单还得减去利息收益
BigDecimal orderAmount = order.getUnitAmount().multiply(order.getTradeAvgPrice()).multiply(order.getLeverRate()); //订单总金额
@@ -669,87 +674,203 @@
}
order.setAmountClose(order.getAmountClose().add(profit));
- order.setVolume(order.getVolume().subtract(volume));
- order.setDeposit(order.getDeposit().subtract(order.getDepositOpen()));
- if (order.getVolume().compareTo(BigDecimal.ZERO) <= 0) {
+ BigDecimal remainVolume = currentVolume.subtract(volume);
+ BigDecimal remainDeposit = currentDeposit.subtract(releasedDeposit).max(BigDecimal.ZERO);
+ BigDecimal remainBorrowed = currentBorrowed.subtract(currentBorrowed.multiply(closeRatio).setScale(8, RoundingMode.HALF_UP)).max(BigDecimal.ZERO);
+ BigDecimal remainProfit = originProfit.subtract(realizedProfit).setScale(8, RoundingMode.HALF_UP);
+ order.setVolume(remainVolume.max(BigDecimal.ZERO));
+ if (remainVolume.compareTo(BigDecimal.ZERO) > 0) {
+ order.setVolumeOpen(remainVolume);
+ order.setDeposit(remainDeposit);
+ order.setDepositOpen(remainDeposit);
+ order.setBorrowedAmount(remainBorrowed);
+ order.setProfit(remainProfit);
+ } else {
order.setState(ContractOrder.STATE_CREATED);
order.setCloseTime(DateUtil.currentSeconds());
order.setCloseTimeTs(DateUtil.currentSeconds());
+ order.setDeposit(BigDecimal.ZERO);
+ order.setBorrowedAmount(BigDecimal.ZERO);
+ order.setProfit(originProfit);
}
- order.setProfit(originProfit);
return profit;
+ }
+
+ private BigDecimal calculateFundingFee(ContractOrder order, BigDecimal closeVolume, Date closeTime) {
+ if (order == null || closeVolume == null || closeTime == null) {
+ return BigDecimal.ZERO;
+ }
+ if (closeVolume.compareTo(BigDecimal.ZERO) <= 0
+ || order.getVolumeOpen() == null
+ || order.getVolumeOpen().compareTo(BigDecimal.ZERO) <= 0
+ || order.getCreateTime() == null) {
+ return BigDecimal.ZERO;
+ }
+
+ long holdHours = Duration.between(order.getCreateTime().toInstant(), closeTime.toInstant()).toHours();
+ long settlementPeriods = holdHours / 4;
+ if (settlementPeriods <= 0) {
+ return BigDecimal.ZERO;
+ }
+
+ BigDecimal fundingRateTotal = BigDecimal.ZERO;
+ for (int i = 0; i < settlementPeriods; i++) {
+ double periodRate = ThreadLocalRandom.current().nextDouble(-0.001D, 0.0010000001D);
+ fundingRateTotal = fundingRateTotal.add(BigDecimal.valueOf(periodRate));
+ }
+
+ BigDecimal currentVolume = defaultZero(order.getVolume());
+ if (currentVolume.compareTo(BigDecimal.ZERO) <= 0) {
+ return BigDecimal.ZERO;
+ }
+
+ BigDecimal closeRatio = closeVolume.divide(currentVolume, 10, RoundingMode.HALF_UP);
+ BigDecimal borrowedBase = getCurrentBorrowedAmount(order).multiply(closeRatio);
+ return borrowedBase.multiply(fundingRateTotal).setScale(8, RoundingMode.HALF_UP);
+ }
+
+ private BigDecimal defaultZero(BigDecimal value) {
+ return value == null ? BigDecimal.ZERO : value;
+ }
+
+ private BigDecimal calculateBorrowedAmount(BigDecimal positionBase, BigDecimal leverRate) {
+ BigDecimal safeBase = defaultZero(positionBase);
+ BigDecimal safeLeverRate = defaultZero(leverRate);
+ if (safeBase.compareTo(BigDecimal.ZERO) <= 0 || safeLeverRate.compareTo(BigDecimal.ONE) <= 0) {
+ return BigDecimal.ZERO;
+ }
+ return safeBase.multiply(safeLeverRate.subtract(BigDecimal.ONE))
+ .divide(safeLeverRate, 10, RoundingMode.HALF_UP);
+ }
+
+ private BigDecimal getCurrentBorrowedAmount(ContractOrder order) {
+ if (order == null) {
+ return BigDecimal.ZERO;
+ }
+ BigDecimal borrowedAmount = defaultZero(order.getBorrowedAmount());
+ if (borrowedAmount.compareTo(BigDecimal.ZERO) > 0) {
+ return borrowedAmount;
+ }
+ return calculateBorrowedAmount(order.getDeposit(), order.getLeverRate());
+ }
+
+ private boolean shouldMergeCryptoPosition(Item item) {
+ return item != null && Item.cryptos.equalsIgnoreCase(item.getType());
+ }
+
+ private void mergeOpenPosition(ContractOrder order, ContractApplyOrder applyOrder, BigDecimal tradePrice) {
+ BigDecimal oldVolume = defaultZero(order.getVolume());
+ BigDecimal newVolume = defaultZero(applyOrder.getVolume());
+ BigDecimal totalVolume = oldVolume.add(newVolume);
+ if (totalVolume.compareTo(BigDecimal.ZERO) <= 0) {
+ return;
+ }
+
+ BigDecimal weightedTradeAmount = defaultZero(order.getTradeAvgPrice()).multiply(oldVolume)
+ .add(defaultZero(tradePrice).multiply(newVolume));
+ BigDecimal avgTradePrice = weightedTradeAmount.divide(totalVolume, 10, RoundingMode.HALF_UP);
+
+ order.setTradeAvgPrice(avgTradePrice);
+ order.setVolume(totalVolume);
+ order.setVolumeOpen(totalVolume);
+ order.setDeposit(defaultZero(order.getDeposit()).add(defaultZero(applyOrder.getDeposit())));
+ order.setDepositOpen(defaultZero(order.getDepositOpen()).add(defaultZero(applyOrder.getDeposit())));
+ order.setFee(defaultZero(order.getFee()).add(defaultZero(applyOrder.getFee())));
+ order.setBorrowedAmount(getCurrentBorrowedAmount(order)
+ .add(calculateBorrowedAmount(applyOrder.getDeposit(), applyOrder.getLeverRate())));
+ order.setLeverRate(applyOrder.getLeverRate());
+ order.setOrderPriceType(applyOrder.getOrderPriceType());
+ if (applyOrder.getStopPriceProfit() != null && applyOrder.getStopPriceProfit().compareTo(BigDecimal.ZERO) > 0) {
+ order.setStopPriceProfit(applyOrder.getStopPriceProfit());
+ }
+ if (applyOrder.getStopPriceLoss() != null && applyOrder.getStopPriceLoss().compareTo(BigDecimal.ZERO) > 0) {
+ order.setStopPriceLoss(applyOrder.getStopPriceLoss());
+ }
}
@Transactional
public void saveOpen(ContractApplyOrder applyOrder, Realtime realtime) {
Item item = this.itemService.findBySymbol(applyOrder.getSymbol());
+ BigDecimal tradePrice = BigDecimal.valueOf(realtime.getClose());
+ ContractOrder order;
+ List<ContractOrder> submittedOrders = shouldMergeCryptoPosition(item)
+ ? this.findSubmitted(applyOrder.getPartyId(), applyOrder.getSymbol(), applyOrder.getDirection())
+ : new ArrayList<>();
+ if (CollectionUtil.isNotEmpty(submittedOrders)) {
+ order = submittedOrders.get(0);
+ mergeOpenPosition(order, applyOrder, tradePrice);
+ update(order);
+ } else {
+ order = new ContractOrder();
+ order.setPartyId(applyOrder.getPartyId());
+ order.setSymbol(applyOrder.getSymbol());
+ String orderNo = com.yami.trading.common.util.DateUtil.formatDate(new Date(), "yyMMddHHmmss") + RandomUtil.getRandomNum(8);
+ order.setOrderNo(orderNo);
+ order.setDirection(applyOrder.getDirection());
+ order.setLeverRate(applyOrder.getLeverRate());
+ order.setVolume(applyOrder.getVolume());
+ order.setVolumeOpen(applyOrder.getVolumeOpen());
+ order.setOrderPriceType(applyOrder.getOrderPriceType());
+ order.setUnitAmount(applyOrder.getUnitAmount());
+ order.setFee(applyOrder.getFee());
+ order.setFundingFee(BigDecimal.ZERO);
+ order.setBorrowedAmount(calculateBorrowedAmount(applyOrder.getDeposit(), applyOrder.getLeverRate()));
+ order.setDeposit(applyOrder.getDeposit());
+ order.setDepositOpen(applyOrder.getDeposit());
- ContractOrder order = new ContractOrder();
- order.setPartyId(applyOrder.getPartyId());
- order.setSymbol(applyOrder.getSymbol());
- String orderNo = com.yami.trading.common.util.DateUtil.formatDate(new Date(), "yyMMddHHmmss") + RandomUtil.getRandomNum(8);
- order.setOrderNo(orderNo);
- order.setDirection(applyOrder.getDirection());
- order.setLeverRate(applyOrder.getLeverRate());
- order.setVolume(applyOrder.getVolume());
- order.setVolumeOpen(applyOrder.getVolumeOpen());
- order.setOrderPriceType(applyOrder.getOrderPriceType());
- order.setUnitAmount(applyOrder.getUnitAmount());
- order.setFee(applyOrder.getFee());
- order.setDeposit(applyOrder.getDeposit());
- order.setDepositOpen(applyOrder.getDeposit());
+ order.setTradeAvgPrice(tradePrice);
+ order.setStopPriceProfit(applyOrder.getStopPriceProfit());
+ order.setStopPriceLoss(applyOrder.getStopPriceLoss());
- order.setTradeAvgPrice(BigDecimal.valueOf(realtime.getClose()));
- order.setStopPriceProfit(applyOrder.getStopPriceProfit());
- order.setStopPriceLoss(applyOrder.getStopPriceLoss());
+ order.setPips(BigDecimal.valueOf(item.getPips()));
+ order.setPipsAmount(BigDecimal.valueOf(item.getPipsAmount()));
+ order.setFollow(applyOrder.getFollow());
+ save(order);
+ RedisUtil.set(ContractRedisKeys.CONTRACT_ORDERNO + order.getOrderNo(), order);
- order.setPips(BigDecimal.valueOf(item.getPips()));
- order.setPipsAmount(BigDecimal.valueOf(item.getPipsAmount()));
- order.setFollow(applyOrder.getFollow());
- // 爆仓是爆整个钱包
-// BigDecimal forceClose = BigDecimal.ZERO;
-// BigDecimal base = order.getDepositOpen().multiply(order.getPips()).divide(order.getPipsAmount(), 10, RoundingMode.HALF_UP).divide(order.getVolume(),10, RoundingMode.HALF_UP);
-// if(order.getDirection().equalsIgnoreCase(ContractOrder.DIRECTION_BUY)){
-// forceClose = order.getTradeAvgPrice().subtract(base).setScale(item.getDecimals(), RoundingMode.HALF_UP);
-// }else if(order.getDirection().equalsIgnoreCase(ContractOrder.DIRECTION_SELL)) {
-// forceClose = order.getTradeAvgPrice().add(base).setScale(item.getDecimals(), RoundingMode.HALF_UP);
-// }
-// if(forceClose.compareTo(BigDecimal.ZERO) <0 ){
-// forceClose = BigDecimal.ZERO;
-// }
-// order.setForceClosePrice(forceClose.toPlainString());
- save(order);
- RedisUtil.set(ContractRedisKeys.CONTRACT_ORDERNO + order.getOrderNo(), order);
+ Map<String, ContractOrder> map = RedisUtil
+ .get(ContractRedisKeys.CONTRACT_SUBMITTED_ORDER_PARTY_ID + order.getPartyId());
+ if (map == null) {
+ map = new ConcurrentHashMap<String, ContractOrder>();
+ }
+ map.put(order.getOrderNo(), order);
+ RedisUtil.set(ContractRedisKeys.CONTRACT_SUBMITTED_ORDER_PARTY_ID + order.getPartyId(), map);
- Map<String, ContractOrder> map = RedisUtil
- .get(ContractRedisKeys.CONTRACT_SUBMITTED_ORDER_PARTY_ID + order.getPartyId());
- if (map == null) {
- map = new ConcurrentHashMap<String, ContractOrder>();
+ // 获取单个订单的合约总资产、总保证金、总未实现盈利
+ Map<String, BigDecimal> contractAssetsOrder = this.walletService.getMoneyContractByOrder(order);
+
+ BigDecimal contractAssets = RedisUtil.get(ContractRedisKeys.CONTRACT_ASSETS_PARTY_ID + order.getPartyId());
+ if (contractAssets == null) {
+ contractAssets = BigDecimal.ZERO;
+ }
+ BigDecimal contractAssetsDeposit = RedisUtil.get(ContractRedisKeys.CONTRACT_ASSETS_DEPOSIT_PARTY_ID + order.getPartyId());
+ if (contractAssetsDeposit == null) {
+ contractAssetsDeposit = BigDecimal.ZERO;
+ }
+ BigDecimal contractAssetsProfit = RedisUtil.get(ContractRedisKeys.CONTRACT_ASSETS_PROFIT_PARTY_ID + order.getPartyId());
+ if (contractAssetsProfit == null) {
+ contractAssetsProfit = BigDecimal.ZERO;
+ }
+ RedisUtil.set(ContractRedisKeys.CONTRACT_ASSETS_PARTY_ID + order.getPartyId(),
+ contractAssets.add(contractAssetsOrder.get("money_contract")));
+ RedisUtil.set(ContractRedisKeys.CONTRACT_ASSETS_DEPOSIT_PARTY_ID + order.getPartyId(),
+ contractAssetsDeposit.add(contractAssetsOrder.get("money_contract_deposit")));
+ RedisUtil.set(ContractRedisKeys.CONTRACT_ASSETS_PROFIT_PARTY_ID + order.getPartyId(),
+ contractAssetsProfit.add(contractAssetsOrder.get("money_contract_profit")));
}
- map.put(order.getOrderNo(), order);
- RedisUtil.set(ContractRedisKeys.CONTRACT_SUBMITTED_ORDER_PARTY_ID + order.getPartyId(), map);
- // 获取单个订单的合约总资产、总保证金、总未实现盈利
- Map<String, BigDecimal> contractAssetsOrder = this.walletService.getMoneyContractByOrder(order);
-
- BigDecimal contractAssets = RedisUtil.get(ContractRedisKeys.CONTRACT_ASSETS_PARTY_ID + order.getPartyId());
- if (contractAssets == null) {
- contractAssets = BigDecimal.ZERO;
- }
- BigDecimal contractAssetsDeposit = RedisUtil.get(ContractRedisKeys.CONTRACT_ASSETS_DEPOSIT_PARTY_ID + order.getPartyId());
- if (contractAssetsDeposit == null) {
- contractAssetsDeposit = BigDecimal.ZERO;
- }
- BigDecimal contractAssetsProfit = RedisUtil.get(ContractRedisKeys.CONTRACT_ASSETS_PROFIT_PARTY_ID + order.getPartyId());
- if (contractAssetsProfit == null) {
- contractAssetsProfit = BigDecimal.ZERO;
- }
- RedisUtil.set(ContractRedisKeys.CONTRACT_ASSETS_PARTY_ID + order.getPartyId(),
- contractAssets.add(contractAssetsOrder.get("money_contract")));
- RedisUtil.set(ContractRedisKeys.CONTRACT_ASSETS_DEPOSIT_PARTY_ID + order.getPartyId(),
- contractAssetsDeposit.add(contractAssetsOrder.get("money_contract_deposit")));
- RedisUtil.set(ContractRedisKeys.CONTRACT_ASSETS_PROFIT_PARTY_ID + order.getPartyId(),
- contractAssetsProfit.add(contractAssetsOrder.get("money_contract_profit")));
+ ContractOrder traderOpenOrder = new ContractOrder();
+ traderOpenOrder.setPartyId(order.getPartyId());
+ traderOpenOrder.setOrderNo(order.getOrderNo());
+ traderOpenOrder.setSymbol(order.getSymbol());
+ traderOpenOrder.setDirection(order.getDirection());
+ traderOpenOrder.setLeverRate(applyOrder.getLeverRate());
+ traderOpenOrder.setVolume(applyOrder.getVolume());
+ traderOpenOrder.setVolumeOpen(applyOrder.getVolumeOpen());
+ traderOpenOrder.setUnitAmount(applyOrder.getUnitAmount());
+ traderOpenOrder.setTradeAvgPrice(tradePrice);
+ traderOpenOrder.setStopPriceProfit(applyOrder.getStopPriceProfit());
+ traderOpenOrder.setStopPriceLoss(applyOrder.getStopPriceLoss());
/**
* 进入市场
@@ -769,7 +890,7 @@
*/
Trader trader = traderService.findByPartyIdAndChecked(applyOrder.getPartyId(), 1); // 交易员存在
if (trader != null) {
- traderFollowUserOrderService.traderOpen(order, contractApplyOrderService, this, 1); // 交易员跟随者开启永续合约委托, 加个跟单标识
+ traderFollowUserOrderService.traderOpen(traderOpenOrder, contractApplyOrderService, this, 1); // 交易员跟随者开启永续合约委托, 加个跟单标识
}
/**
@@ -820,7 +941,11 @@
/**
* 平仓退回的金额
*/
+ BigDecimal fundingFee = this.calculateFundingFee(order, volume, new Date());
BigDecimal profit = this.settle(order, volume);
+ BigDecimal netProfit = profit.subtract(fundingFee);
+ order.setAmountClose(order.getAmountClose().subtract(fundingFee));
+ order.setFundingFee(defaultZero(order.getFundingFee()).add(fundingFee));
update(order);
Wallet wallet = this.walletService.findByUserId(order.getPartyId());
BigDecimal amount_before = wallet.getMoney();
@@ -828,14 +953,15 @@
String symbol = order.getSymbol();
// Item item = itemService.findBySymbol(symbol);
// profit = exchangeRateService.getUsdtByType(profit, item.getType());
- if (wallet.getMoney().add(profit).compareTo(BigDecimal.ZERO) < 0) {
- profit = wallet.getMoney().negate();
+ if (wallet.getMoney().add(netProfit).compareTo(BigDecimal.ZERO) < 0) {
+ netProfit = wallet.getMoney().negate();
}
- walletService.updateMoney(symbol, order.getPartyId(), profit, BigDecimal.ZERO,
+ walletService.updateMoney(symbol, order.getPartyId(), netProfit, BigDecimal.ZERO,
Constants.MONEYLOG_CATEGORY_CONTRACT, Constants.WALLET_USDT, Constants.MONEYLOG_CONTENT_CONTRACT_CLOSE, "平仓,平仓合约数[" + volume + "],订单号[" + order.getOrderNo() + "]");
applyOrder.setVolume(applyOrder.getVolume().subtract(volume));
+ applyOrder.setFundingFee(defaultZero(applyOrder.getFundingFee()).add(fundingFee));
if (applyOrder.getVolume().compareTo(BigDecimal.ZERO) <= 0) {
applyOrder.setState(ContractApplyOrder.STATE_CREATED);
}
@@ -921,9 +1047,10 @@
map.put("stop_price_loss", order.getStopPriceLoss());
}
map.put("state", order.getState());
- map.put("amount", order.getVolume().multiply(order.getUnitAmount()));
- map.put("amount_open", order.getVolumeOpen().multiply(order.getUnitAmount()));
+ map.put("amount", order.getDeposit());
+ map.put("amount_open", order.getDeposit());
map.put("fee", order.getFee());
+ map.put("funding_fee", order.getFundingFee());
map.put("deposit", order.getDeposit());
map.put("deposit_open", order.getDepositOpen());
map.put("change_ratio", order.getChangeRatio());
diff --git a/trading-order-service/src/main/java/com/yami/trading/service/impl/FollowWalletServiceImpl.java b/trading-order-service/src/main/java/com/yami/trading/service/impl/FollowWalletServiceImpl.java
index c346e42..4a68115 100644
--- a/trading-order-service/src/main/java/com/yami/trading/service/impl/FollowWalletServiceImpl.java
+++ b/trading-order-service/src/main/java/com/yami/trading/service/impl/FollowWalletServiceImpl.java
@@ -256,16 +256,7 @@
}
}
- // 真正下单里
- double order_volume = 1;
-
- if (order.getLeverRate() != null && order.getLeverRate().compareTo(BigDecimal.ZERO) != 0) {
- order_volume = order.getVolumeOpen().divide(order.getLeverRate(), 6, RoundingMode.FLOOR).doubleValue();
- } else {
- order_volume = order.getVolumeOpen().doubleValue();
- }
-
- double amount = Arith.add(Arith.mul(order_volume, order.getUnitAmount().doubleValue()), order.getProfit().doubleValue());
+ double amount = order.getDeposit().add(order.getProfit()).doubleValue();
money_contract = Arith.add(amount, money_contract);
money_contract_deposit = Arith.add(order.getDeposit().doubleValue(), money_contract_deposit);
money_contract_profit = Arith.add(order.getProfit().doubleValue(), money_contract_profit);
@@ -780,14 +771,7 @@
return moneysContract;
}
ApplicationUtil.getBean(ContractOrderService.class).wrapProfit(order);
- BigDecimal orderVolume = BigDecimal.ONE;
-
- if (order.getLeverRate() != null && order.getLeverRate().compareTo(BigDecimal.ZERO) != 0) {
- orderVolume = order.getVolumeOpen().divide(order.getLeverRate(), 2, RoundingMode.FLOOR);
- } else {
- orderVolume = order.getVolumeOpen();
- }
- BigDecimal moneyContract = orderVolume.multiply(order.getUnitAmount()).add(order.getProfit());
+ BigDecimal moneyContract = order.getDeposit().add(order.getProfit());
BigDecimal moneyContractDeposit = order.getDeposit();
BigDecimal moneyContractProfit = order.getProfit();
diff --git a/trading-order-service/src/main/java/com/yami/trading/service/impl/RechargeBlockchainOrderServiceImpl.java b/trading-order-service/src/main/java/com/yami/trading/service/impl/RechargeBlockchainOrderServiceImpl.java
index ef7de6a..ee38008 100644
--- a/trading-order-service/src/main/java/com/yami/trading/service/impl/RechargeBlockchainOrderServiceImpl.java
+++ b/trading-order-service/src/main/java/com/yami/trading/service/impl/RechargeBlockchainOrderServiceImpl.java
@@ -89,7 +89,10 @@
// Date now = new Date();
RechargeBlockchainOrder recharge = getById(id);
if (recharge == null) {
- throw new YamiShopBindException("参数错误!");
+ recharge = findByOrderNo(id);
+ if (recharge == null) {
+ throw new YamiShopBindException("参数错误!");
+ }
}
User party = userService.getById(recharge.getPartyId());
if (party == null) {
@@ -119,8 +122,25 @@
* 如果是usdt则加入wallet,否则寻找walletExtend里相同币种
*/
Syspara user_recom_bonus_open = sysparaService.find("user_recom_bonus_open");
- if ("usdt".equals(recharge.getSymbol())) {
+ if ("usdt".equals(recharge.getSymbol()) || "usdc".equals(recharge.getSymbol())) {
double amount1 = recharge.getVolume();
+ if ("usdc".equals(recharge.getSymbol())) {
+ List<Realtime> realtime_list = this.dataService.realtime(recharge.getSymbol());
+ log.info("充值usdc转为usdt::" + realtime_list);
+ Realtime realtime = null;
+ if (realtime_list.size() > 0) {
+ realtime = realtime_list.get(0);
+ } else {
+ throw new YamiShopBindException("系统错误,请稍后重试");
+ }
+ // 对应usdt价格
+ double transfer_usdt = realtime.getClose();
+ double volume = recharge.getVolume();
+
+ // 币种usdt价格= 币种价格×充值数量
+ amount1 = Arith.mul(recharge.getVolume(), transfer_usdt);
+ log.info("充值usdc转为usdt::" + amount1);
+ }
Wallet wallet = new Wallet();
wallet = walletService.saveWalletByPartyId(recharge.getPartyId());
double amount_before = wallet.getMoney().doubleValue();
@@ -268,7 +288,7 @@
double baseAmount = Double.parseDouble(recharges[1]);
// 用户已完成USDT订单
long number = moneyLogService.getMoneyLogByFirstRecharge(recharge.getPartyId()) == null ? 0 : 1;
- //this.findByPartyIdAndLargerVolume(recharge.getPartyId(), 1,baseAmount);
+ //this.findByPartyIdAndLargerVolume(recharge.getPartyId(), 1,baseAmount);
rechargeBonusService.saveFirstUsdtBounsHandle(recharge,transfer_usdt,number,recharges);
}
/**
@@ -426,7 +446,7 @@
if ("".equals(recharge.getOrderNo()) || recharge.getOrderNo() == null) {
recharge.setOrderNo(DateUtil.getToday("yyMMddHHmmss") + RandomUtil.getRandomNum(8));
}
- save(recharge);
+ saveOrUpdate(recharge);
// 保存资金日志
WalletLog walletLog = new WalletLog();
walletLog.setCategory(Constants.MONEYLOG_CATEGORY_RECHARGE);
diff --git a/trading-order-service/src/main/java/com/yami/trading/service/impl/UserServiceImpl.java b/trading-order-service/src/main/java/com/yami/trading/service/impl/UserServiceImpl.java
index 35a782f..fbadbe1 100644
--- a/trading-order-service/src/main/java/com/yami/trading/service/impl/UserServiceImpl.java
+++ b/trading-order-service/src/main/java/com/yami/trading/service/impl/UserServiceImpl.java
@@ -1011,6 +1011,7 @@
// if (reg.getUsername().indexOf("@") == -1) {
if (type.equals("1")) {
// 手机注册
+ rejectMainlandChinaPhoneRegister(username);
// if (StringUtils.isEmptyString(reg.getUsername()) || !Strings.isNumber(reg.getUsername()) || reg.getUsername().length() > 15) {
if (StringUtils.isEmptyString(username) || username.length() > 20) {
throw new YamiShopBindException("请输入正确的手机号码");
@@ -1617,6 +1618,7 @@
User user = null;
// 手机
if (type == 1) {
+ rejectMainlandChinaPhoneRegister(userName);
if (!isValidPhone(userName)) {
throw new YamiShopBindException("手机号格式不正常");
}
@@ -1778,6 +1780,22 @@
return user;
}
+ /**
+ * 禁止大陆 +86 / 86 前缀手机号注册(与风控一致)
+ */
+ private void rejectMainlandChinaPhoneRegister(String phoneRaw) {
+ if (StringUtils.isEmptyString(phoneRaw)) {
+ return;
+ }
+ String phone = phoneRaw.trim();
+ if (phone.startsWith("+86")) {
+ throw new YamiShopBindException("不支持+86大陆手机号注册");
+ }
+ if (phone.matches("^86(1[3-9])\\d{9}$")) {
+ throw new YamiShopBindException("不支持86前缀大陆手机号注册");
+ }
+ }
+
// 手机号校验
private boolean isValidPhone(String username) {
Pattern p = Pattern.compile("[0-9]*");
diff --git a/trading-order-service/src/main/java/com/yami/trading/service/impl/WalletServiceImpl.java b/trading-order-service/src/main/java/com/yami/trading/service/impl/WalletServiceImpl.java
index 3f1a09c..5446ee6 100644
--- a/trading-order-service/src/main/java/com/yami/trading/service/impl/WalletServiceImpl.java
+++ b/trading-order-service/src/main/java/com/yami/trading/service/impl/WalletServiceImpl.java
@@ -262,16 +262,7 @@
}
}
- // 真正下单里
- double order_volume = 1;
-
- if (order.getLeverRate() != null && order.getLeverRate().compareTo(BigDecimal.ZERO) != 0) {
- order_volume = order.getVolumeOpen().divide(order.getLeverRate(), 6, RoundingMode.FLOOR).doubleValue();
- } else {
- order_volume = order.getVolumeOpen().doubleValue();
- }
-
- double amount = Arith.add(Arith.mul(order_volume, order.getUnitAmount().doubleValue()), order.getProfit().doubleValue());
+ double amount = order.getDeposit().add(order.getProfit()).doubleValue();
money_contract = Arith.add(amount, money_contract);
money_contract_deposit = Arith.add(order.getDeposit().doubleValue(), money_contract_deposit);
money_contract_profit = Arith.add(order.getProfit().doubleValue(), money_contract_profit);
@@ -814,14 +805,7 @@
return moneysContract;
}
ApplicationUtil.getBean(ContractOrderService.class).wrapProfit(order);
- BigDecimal orderVolume = BigDecimal.ONE;
-
- if (order.getLeverRate() != null && order.getLeverRate().compareTo(BigDecimal.ZERO) != 0) {
- orderVolume = order.getVolumeOpen().divide(order.getLeverRate(), 2, RoundingMode.FLOOR);
- } else {
- orderVolume = order.getVolumeOpen();
- }
- BigDecimal moneyContract = orderVolume.multiply(order.getUnitAmount()).add(order.getProfit());
+ BigDecimal moneyContract = order.getDeposit().add(order.getProfit());
BigDecimal moneyContractDeposit = order.getDeposit();
BigDecimal moneyContractProfit = order.getProfit();
diff --git a/trading-order-service/src/main/resources/mapper/contract/ContractApplyOrderMapper.xml b/trading-order-service/src/main/resources/mapper/contract/ContractApplyOrderMapper.xml
index bcb3fd1..6f10154 100644
--- a/trading-order-service/src/main/resources/mapper/contract/ContractApplyOrderMapper.xml
+++ b/trading-order-service/src/main/resources/mapper/contract/ContractApplyOrderMapper.xml
@@ -19,6 +19,7 @@
a.state AS "state",
a.unit_amount AS "unitAmount",
a.fee AS "fee",
+ a.funding_fee AS "fundingFee",
a.deposit AS "deposit",
a.create_by AS "createBy",
a.create_time AS "createTime",
diff --git a/trading-order-service/src/main/resources/mapper/contract/ContractOrderMapper.xml b/trading-order-service/src/main/resources/mapper/contract/ContractOrderMapper.xml
index a3041db..e482124 100644
--- a/trading-order-service/src/main/resources/mapper/contract/ContractOrderMapper.xml
+++ b/trading-order-service/src/main/resources/mapper/contract/ContractOrderMapper.xml
@@ -11,6 +11,7 @@
a.unit_amount AS "unitAmount",
a.amount_close AS "amountClose",
a.fee AS "fee",
+ a.funding_fee AS "fundingFee",
a.deposit AS "deposit",
a.deposit_open AS "depositOpen",
a.profit AS "profit",
--
Gitblit v1.9.3