34 files modified
9 files added
| | |
| | | ### 3. 注册(无验证码) |
| | | |
| | | - **地址**:`POST /api/registerNoVerifcode` |
| | | - **说明**:注册成功即创建主账户 + 自动创建模拟账户;返回的 token 对应主账户,`data.info` 结构同 2。 |
| | | - **说明**:注册成功即创建主账户 + 自动创建模拟账户;返回的 token 对应主账户,`data.info` 结构同 2。 |
| | | 额外限制:**禁止大陆邮箱注册**,命中时返回业务错误(如:`msg = "大陆邮箱不支持注册"`)。 |
| | | - **请求体**:JSON |
| | | |
| | | | 参数 | 类型 | 必填 | 说明 | |
| | |
| | | ### 4. 注册(有验证码) |
| | | |
| | | - **地址**:`POST /api/registerVerifcode` |
| | | - **说明**:同上,注册即主账户+模拟账户;返回结构同 2、3。 |
| | | - **说明**:同上,注册即主账户+模拟账户;返回结构同 2、3。 |
| | | 额外限制:**禁止大陆邮箱注册**,命中时返回业务错误(如:`msg = "大陆邮箱不支持注册"`)。 |
| | | - **请求体**:在 3 的基础上增加 `verifcode`(验证码)等字段,按现有注册接口约定即可。 |
| | | |
| | | --- |
| | |
| | | |
| | | 4. **错误处理** |
| | | - `msg === "模拟账户不能直接登录,请使用主账户登录后切换"`:提示用户使用主账户登录后再切换。 |
| | | - `msg === "模拟账户不支持充值"` / `"模拟账户不支持提现"`:在模拟账户下隐藏或禁用对应功能即可,一般不应让用户点到。 |
| | | - `msg === "模拟账户不支持充值"` / `"模拟账户不支持提现"`:在模拟账户下隐藏或禁用对应功能即可,一般不应让用户点到。 |
| | | - `msg === "大陆IP禁止访问"`:提示当前地区不可用,并阻断后续页面访问。 |
| | | - `msg === "大陆邮箱不支持注册"`:在注册页保持提交按钮可重试,提示用户更换邮箱。 |
| | | - `msg === "请先完成两步认证并设置资金密码后再交易"`:交易页入口/下单按钮置灰,并引导跳转安全设置页。 |
| | | |
| | | --- |
| | | |
| | |
| | | - 所有上述接口的**基础路径**以实际部署为准(如 `https://your-domain.com`),若有统一网关前缀需自行加上。 |
| | | - Token 过期或未传时,接口会返回 401 等,前端需按现有逻辑跳转登录(主账户登录页)。 |
| | | - 模拟账户与主账户**共用同一套业务接口**(交易、资产等),仅充提与登录限制不同;前端通过 `accountType` 控制展示与禁用即可。 |
| | | - 访问控制:**禁止大陆 IP 访问**,命中后端风控时应返回业务错误(建议文案:`大陆IP禁止访问`),前端需阻断继续使用。 |
| | | - 注册限制:**禁止大陆邮箱注册**,在注册接口统一拦截并返回业务错误(建议文案:`大陆邮箱不支持注册`)。 |
| | | - 交易前置条件:用户必须先完成**两步认证(2FA)**并**设置资金密码**,否则不得进行交易下单(建议文案:`请先完成两步认证并设置资金密码后再交易`)。 |
| | |
| | | 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); |
| | |
| | | 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每日订单取消最大次数 |
| | |
| | | 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); |
| | |
| | | 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("无网络"); |
| | |
| | | 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("无网络"); |
| | |
| | | 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); |
| | | } |
| | |
| | | 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("请先完成两步认证并设置资金密码后再交易"); |
| | | } |
| | | } |
| | | } |
| | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.io.IOException; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.util.*; |
| | | |
| | | /** |
| | |
| | | |
| | | @RestController |
| | | @CrossOrigin |
| | | @RequestMapping("api/gooleAuth") |
| | | @RequestMapping({"api/gooleAuth", "api/googleAuth"}) |
| | | @Api(tags = "谷歌验证码") |
| | | public class ApiGoogleAuthController { |
| | | @Autowired |
| | |
| | | dto.setGoogleAuthImg(base64); |
| | | dto.setGoogleAuthSecret(secretKey); |
| | | return Result.succeed(dto); |
| | | } |
| | | |
| | | @GetMapping("/get.action") |
| | | @ApiOperation(value = "H5-谷歌身份验证器 获取密钥及二维码") |
| | | public Result<GoogleAuthDto> getAction() { |
| | | return get(); |
| | | } |
| | | |
| | | @PostMapping("/bind") |
| | |
| | | throw new YamiShopBindException("谷歌验证码错误"); |
| | | } |
| | | } |
| | | |
| | | @PostMapping("/bind.action") |
| | | @ApiOperation(value = "H5-谷歌身份绑定") |
| | | public Result bindAction(@Valid GoogleAuthBindModel model) { |
| | | return bind(model); |
| | | } |
| | | } |
| | |
| | | @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) { |
| | |
| | | @PostMapping("/registerNoVerifcode") |
| | | @ApiOperation(value = "手机/邮箱/用户名注册(无验证码)") |
| | | public Result register(@Valid RegisterModel model) { |
| | | validateMainlandIpAccess(); |
| | | validateMainlandEmailRegister(model.getUserName(), model.getType()); |
| | | |
| | | String username = model.getUserName(); |
| | | String password = model.getPassword(); |
| | |
| | | @PostMapping("/registerVerifcode") |
| | | @ApiOperation(value = "手机(有验证码)") |
| | | public Result registerVerifcode(@Valid RegisterMobile model) { |
| | | validateMainlandIpAccess(); |
| | | validateMainlandEmailRegister(model.getUserName(), model.getType()); |
| | | |
| | | String username = model.getUserName(); |
| | | String password = model.getPassword(); |
| | |
| | | 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("大陆邮箱不支持注册"); |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | */ |
| | | @GetMapping("login") |
| | | public Result login(String username, String password) { |
| | | validateMainlandIpAccess(); |
| | | if (StringUtils.isEmptyString(username)) { |
| | | throw new YamiShopBindException("用户名不能为空"); |
| | | } |
| | |
| | | */ |
| | | @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("资金密码不能为空"); |
| | | // } |
| | |
| | | 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"); |
| | |
| | | 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("大陆邮箱不支持注册"); |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | 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<>(); |
| | |
| | | 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; |
| | |
| | | PasswordEncoder passwordEncoder; |
| | | @Autowired |
| | | private IdentifyingCodeTimeWindowService identifyingCodeTimeWindowService; |
| | | @Autowired |
| | | UdunClient udunClient; |
| | | |
| | | @Override |
| | | public void afterPropertiesSet() throws Exception { |
| | |
| | | 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; |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | @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); |
| | |
| | | // 手续费(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); |
| | |
| | | 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; |
| | | |
| | |
| | | UserService userService; |
| | | @Autowired |
| | | PartyBlockchainService partyBlockchainService; |
| | | |
| | | @Autowired |
| | | UdunClient udunClient; |
| | | |
| | | @Autowired |
| | | RedisTemplate redisTemplate; |
| | | |
| | | @Autowired |
| | | RechargeBlockchainOrderService rechargeBlockchainOrderService; |
| | | |
| | | /** |
| | | * 获取所有链地址 |
| | |
| | | } |
| | | 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; |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | 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)) { |
| | |
| | | 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)) { |
| | |
| | | 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(); |
| | |
| | | |
| | | //=============================================闪兑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("请先完成两步认证并设置资金密码后再交易"); |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | if (!party.isEnabled()) { |
| | | throw new YamiShopBindException("用户已禁用"); |
| | | } |
| | | validateTradePermission(party); |
| | | ExchangeLeverApplyOrder order = new ExchangeLeverApplyOrder(); |
| | | order.setPartyId(SecurityUtils.getCurrentUserId()); |
| | | order.setSymbol(symbol); |
| | |
| | | if (!party.isEnabled()) { |
| | | throw new BusinessException("用户已锁定"); |
| | | } |
| | | validateTradePermission(party); |
| | | // if (!party.getKyc_authority()) { |
| | | // resultObject.setCode("401"); |
| | | // resultObject.setMsg(error); |
| | |
| | | |
| | | } |
| | | |
| | | 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("请先完成两步认证并设置资金密码后再交易"); |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | 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"); |
| | |
| | | //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())); |
| | |
| | | @Pattern(regexp="^(buy|sell)$",message = "请输入正确的方向") |
| | | private String direction; |
| | | /** |
| | | * amount 委托数量(张) |
| | | * amount 平仓数量(与开仓一致的杠杆口径数量),服务端不再对该数量做二次杠杆换算 |
| | | */ |
| | | @NotNull(message = "委托数量(张)必填") |
| | | @DecimalMin(value = "0.00000001", message = "委托数量(张)不能小于0") |
| | |
| | | @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 杠杆倍数 |
| | |
| | | private BigDecimal lever_rate; |
| | | |
| | | /** |
| | | * price 交易价格 |
| | | * price 限价时必填且有效;市价(opponent)可忽略,服务端以行情为准 |
| | | */ |
| | | @NotNull(message = "交易价格必填") |
| | | @DecimalMin(value = "0.00000001", message = "交易价格不能小于0") |
| | | private BigDecimal price; |
| | | |
| | | /** |
| | |
| | | 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 |
| | |
| | | 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 |
| | |
| | | <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> |
| | |
| | | |
| | | </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"/> |
| | |
| | | */ |
| | | private BigDecimal fee; |
| | | /** |
| | | * 资金费 |
| | | */ |
| | | private BigDecimal fundingFee; |
| | | /** |
| | | * 保证金 |
| | | */ |
| | | private BigDecimal deposit; |
| | |
| | | */ |
| | | private BigDecimal fee; |
| | | /** |
| | | * 资金费 |
| | | */ |
| | | private BigDecimal fundingFee; |
| | | /** |
| | | * 杠杆借贷金额 |
| | | */ |
| | | private BigDecimal borrowedAmount; |
| | | /** |
| | | * 保证金(剩余) |
| | | */ |
| | | private BigDecimal deposit ; |
| | |
| | | return depositOpen; |
| | | } |
| | | |
| | | public BigDecimal getBorrowedAmount() { |
| | | if (borrowedAmount == null) { |
| | | borrowedAmount = BigDecimal.ZERO; |
| | | } |
| | | return borrowedAmount; |
| | | } |
| | | |
| | | public BigDecimal getTradeAvgPrice() { |
| | | if(tradeAvgPrice == null){ |
| | | tradeAvgPrice = BigDecimal.ZERO; |
| | |
| | | private BigDecimal fee; |
| | | |
| | | /** |
| | | * 资金费 |
| | | */ |
| | | private BigDecimal fundingFee; |
| | | |
| | | /** |
| | | * DEPOSIT |
| | | */ |
| | | private BigDecimal deposit; |
| | |
| | | private BigDecimal fee; |
| | | |
| | | /** |
| | | * 资金费 |
| | | */ |
| | | @ApiModelProperty("资金费") |
| | | private BigDecimal fundingFee; |
| | | |
| | | /** |
| | | * 保证金(剩余) |
| | | */ |
| | | @ApiModelProperty("保证金(剩余)") |
| | |
| | | private String forceClosePrice; |
| | | |
| | | |
| | | |
| | | |
| | | private double changeRatio; |
| | | |
| | | public BigDecimal getProfitLoss() { |
| | |
| | | |
| | | public static final String IMAGES_HTTP = ApplicationUtil.getProperty("images_http"); |
| | | |
| | | public static final String API_HTTP = ApplicationUtil.getProperty("api_http"); |
| | | |
| | | /** |
| | | * 质押2.0下单 |
| | | */ |
| | |
| | | * 区块链充值订单 |
| | | */ |
| | | public final static String RECHARGE_BLOCKCHAIN_ORDERNO = "RECHARGE_BLOCKCHAIN_ORDERNO_"; |
| | | |
| | | /** |
| | | * 充值地址缓存 |
| | | */ |
| | | public final static String BLOCKCHAIN_ADDRESS = "BLOCKCHAIN_ADDRESS"; |
| | | /** |
| | | * 提现订单 |
| | | */ |
| | |
| | | "/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", |
| New file |
| | |
| | | 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 + |
| | | '}'; |
| | | } |
| | | } |
| New file |
| | |
| | | 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"; |
| | | } |
| New file |
| | |
| | | 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; |
| | | } |
| | | } |
| New file |
| | |
| | | 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)); |
| | | } |
| | | } |
| New file |
| | |
| | | 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 + '\'' + |
| | | '}'; |
| | | } |
| | | } |
| New file |
| | |
| | | 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); |
| | | } |
| New file |
| | |
| | | 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); |
| | | } |
| | | } |
| New file |
| | |
| | | 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; |
| | | } |
| | | } |
| New file |
| | |
| | | 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); |
| | | } |
| | | |
| | | } |
| | |
| | | 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); |
| | | } |
| | |
| | | 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(); |
| | |
| | | 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; |
| | |
| | | @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 { |
| | |
| | | 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()); |
| | |
| | | /** |
| | | * 根据价格变化百分比和保证金计算盈亏金额 |
| | | */ |
| | | 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); |
| | | |
| | |
| | | } |
| | | 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) { |
| | | /** |
| | | * 触发全仓强平 |
| | | */ |
| | |
| | | |
| | | } |
| | | } 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) { |
| | | /** |
| | |
| | | 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; |
| | |
| | | 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; |
| | | |
| | |
| | | * 收益 |
| | | */ |
| | | 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()); |
| | |
| | | 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) { |
| | |
| | | if (cacheProfit != null) { |
| | | contractOrder.setProfit(cacheProfit.getProfit()); |
| | | contractOrder.setCloseAvgPrice(cacheProfit.getCloseAvgPrice()); |
| | | contractOrder.setForceClosePrice(cacheProfit.getForceClosePrice()); |
| | | } else { |
| | | contractOrder.setProfit(BigDecimal.ZERO); |
| | | |
| | |
| | | * @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) { |
| | |
| | | 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()); //订单总金额 |
| | |
| | | } |
| | | |
| | | 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()); |
| | | |
| | | /** |
| | | * 进入市场 |
| | |
| | | */ |
| | | Trader trader = traderService.findByPartyIdAndChecked(applyOrder.getPartyId(), 1); // 交易员存在 |
| | | if (trader != null) { |
| | | traderFollowUserOrderService.traderOpen(order, contractApplyOrderService, this, 1); // 交易员跟随者开启永续合约委托, 加个跟单标识 |
| | | traderFollowUserOrderService.traderOpen(traderOpenOrder, contractApplyOrderService, this, 1); // 交易员跟随者开启永续合约委托, 加个跟单标识 |
| | | } |
| | | |
| | | /** |
| | |
| | | /** |
| | | * 平仓退回的金额 |
| | | */ |
| | | 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(); |
| | |
| | | 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); |
| | | } |
| | |
| | | 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()); |
| | |
| | | } |
| | | |
| | | } |
| | | // 真正下单里 |
| | | 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); |
| | |
| | | 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(); |
| | | |
| | |
| | | // 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) { |
| | |
| | | * 如果是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(); |
| | |
| | | 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); |
| | | } |
| | | /** |
| | |
| | | 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); |
| | |
| | | // 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("请输入正确的手机号码"); |
| | |
| | | User user = null; |
| | | // 手机 |
| | | if (type == 1) { |
| | | rejectMainlandChinaPhoneRegister(userName); |
| | | if (!isValidPhone(userName)) { |
| | | throw new YamiShopBindException("手机号格式不正常"); |
| | | } |
| | |
| | | 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]*"); |
| | |
| | | } |
| | | |
| | | } |
| | | // 真正下单里 |
| | | 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); |
| | |
| | | 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(); |
| | | |
| | |
| | | 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", |
| | |
| | | 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", |