package com.yami.trading.api.controller; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.yami.trading.api.dto.CloseAction; import com.yami.trading.api.dto.OpenAction; import com.yami.trading.bean.contract.domain.ContractApplyOrder; import com.yami.trading.bean.contract.domain.ContractOrder; import com.yami.trading.bean.contract.mapstruct.ContractApplyOrderWrapper; import com.yami.trading.bean.data.domain.Realtime; import com.yami.trading.bean.item.domain.Item; import com.yami.trading.bean.item.dto.ItemLeverageDTO; import com.yami.trading.bean.model.User; import com.yami.trading.bean.model.Wallet; import com.yami.trading.bean.syspara.domain.Syspara; import com.yami.trading.common.domain.Result; import com.yami.trading.common.exception.YamiShopBindException; import com.yami.trading.common.lang.LangUtils; import com.yami.trading.common.util.MarketOpenChecker; import com.yami.trading.common.util.StringUtils; import com.yami.trading.common.util.ThreadUtils; import com.yami.trading.security.common.util.SecurityUtils; import com.yami.trading.service.SessionTokenService; import com.yami.trading.service.StrongLevelCalculationService; import com.yami.trading.service.WalletService; import com.yami.trading.service.contract.ContractApplyOrderService; import com.yami.trading.service.contract.ContractLockService; import com.yami.trading.service.contract.ContractOrderService; import com.yami.trading.service.data.DataService; import com.yami.trading.service.item.ItemLeverageService; import com.yami.trading.service.item.ItemService; import com.yami.trading.service.syspara.SysparaService; import com.yami.trading.service.user.UserService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.parameters.P; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import javax.validation.constraints.NotBlank; import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.*; import java.util.concurrent.TimeUnit; /** * 订单表Controller * * @author lucas * @version 2023-03-29 */ @Api(tags = "订单表") @RestController @Slf4j public class ApiContractApplyOrderController { private final String action = "/api/contractApplyOrder!"; @Autowired private ContractApplyOrderService contractApplyOrderService; @Autowired private DataService dataService; @Autowired private ContractApplyOrderWrapper contractApplyOrderWrapper; @Autowired private ItemService itemService; @Autowired private ItemLeverageService itemLeverageService; @Autowired private WalletService walletService; @Autowired private SysparaService sysparaService; @Autowired private RedissonClient redissonClient; @Autowired private UserService userService; @Autowired private ContractOrderService contractOrderService; @Autowired private ContractLockService contractLockService; @Autowired private SessionTokenService sessionTokenService; /** * 开仓页面参数 *

* symbol 币种 */ @RequestMapping(action + "openview.action") public Result> openview(@RequestParam(required = true) String symbol) throws IOException { Map data = new HashMap(); Item item = itemService.findBySymbol(symbol); data.put("amount", item.getUnitAmount()); data.put("fee", item.getUnitFee()); List lLeverageDTO = itemLeverageService.findByItemId(item.getUuid()); data.put("lever", lLeverageDTO); String partyId = SecurityUtils.getCurrentUserId(); if (!StrUtil.isBlank(partyId)) { Wallet wallet = walletService.findByUserId(partyId); // 账户剩余资 金 BigDecimal useAmount = item.getUnitAmount().add(item.getUnitFee()); BigDecimal volume = wallet.getMoney().divide(useAmount,0, RoundingMode.DOWN); data.put("volume", volume.toPlainString()); } else { data.put("volume", 0.00D); } data.put("open", MarketOpenChecker.isMarketOpenByItemCloseType(item.getOpenCloseType())); BigDecimal contractOpenLimitMin = new BigDecimal(sysparaService.find("contract_open_limit_min").getSvalue()); data.put("contract_open_limit_min", contractOpenLimitMin); BigDecimal contractOpenLimitMax = new BigDecimal(sysparaService.find("contract_open_limit_max").getSvalue()); data.put("contract_open_limit_max", contractOpenLimitMax); return Result.succeed(data); } /** * 平仓页面参数 *

* symbol 币种 */ @RequestMapping(action + "closeview.action") public Result> closeview(HttpServletRequest request, @RequestParam(required = true) String symbol) throws IOException { String direction = request.getParameter("direction"); // if(StringUtils.isEmptyString(direction)){ // direction = "sell"; // } String partyId = SecurityUtils.getCurrentUserId(); Map data = new HashMap(); if (StrUtil.isNotBlank(partyId)) { List list = this.contractOrderService.findSubmitted(partyId, symbol, direction); BigDecimal ordervolume = BigDecimal.ZERO; for (ContractOrder contractOrder : list) { ordervolume = ordervolume.add(contractOrder.getVolume()); } // List contractApplyOrderSubmitted = this.contractApplyOrderService.findSubmitted(partyId, symbol, ContractApplyOrder.OFFSET_CLOSE, direction); // for (ContractApplyOrder contractApplyOrder : contractApplyOrderSubmitted) { // ordervolume = ordervolume.subtract(contractApplyOrder.getVolume()); // } data.put("amount", ordervolume); if (null == list || 0 == list.size()) { data.put("lever_rate", 1); } else { if (null == list.get(0) || null == list.get(0).getLeverRate()) { data.put("lever_rate", 1); } else { data.put("lever_rate", list.get(0).getLeverRate()); } } String session_token = this.sessionTokenService.savePut(partyId); data.put("session_token", session_token); } else { data.put("amount", BigDecimal.ZERO); data.put("lever_rate", 1); } BigDecimal contractCloseLimitMin = new BigDecimal(sysparaService.find("contract_close_limit_min").getSvalue()); data.put("contract_close_limit_min", contractCloseLimitMin); BigDecimal contractCloseLimitMax = new BigDecimal(sysparaService.find("contract_close_limit_max").getSvalue()); data.put("contract_close_limit_max", contractCloseLimitMax); return Result.succeed(data); } /** * 开仓 */ @RequestMapping(action + "open.action") public Result open(@Valid OpenAction openAction) throws IOException, InterruptedException { String partyId = SecurityUtils.getUser().getUserId(); RLock rLock = redissonClient.getLock("contract_open_" + partyId); boolean lockAcquired = false; double faceValue = 0.01;//面值 double minAmount = 0.01;//最低张数 try { // 尝试获取锁,最多等待5秒 lockAcquired = rLock.tryLock(5, TimeUnit.SECONDS); if (!lockAcquired) { log.warn("无法获取锁: contract_open_{}", partyId); throw new YamiShopBindException("请稍后再试"); } //判断下单金额是否符合最低金额 最低下单张数:0.01 //合约张数 张数=保证金*杠杆倍数/(面值*最新成交价) double v = openAction.getAmount().doubleValue() * openAction.getLever_rate().doubleValue() / (faceValue * openAction.getPrice().doubleValue()); BigDecimal amount = BigDecimal.valueOf(v).setScale(4, RoundingMode.DOWN); if (amount.compareTo(new BigDecimal(faceValue)) < 0) { double minimumAmount = minAmount * faceValue * openAction.getPrice().doubleValue() / openAction.getLever_rate().doubleValue(); double roundedAmount = Math.ceil(minimumAmount * 10000) / 10000; throw new YamiShopBindException("最低下单金额:"+roundedAmount); } // 校验当前用户订单状态 checkContractOrderStatus(openAction, partyId); // 校验用户是否被锁定 checkUserStatus(partyId); // 校验用户网络限制 checkUserNetworkRestriction(partyId); // 创建新的合约申请订单 createContractApplyOrder(openAction, partyId); } catch (YamiShopBindException e) { log.error("错误信息: {}", e.getMessage(), e); throw e; // 重新抛出自定义异常 } catch (Exception e) { log.error("系统异常: {}", e.getMessage(), e); throw new YamiShopBindException("操作失败,请稍后再试"); } finally { // 确保释放锁 if (lockAcquired && rLock.isHeldByCurrentThread()) { rLock.unlock(); } } return Result.succeed(null, "ok"); } // 校验合约订单状态 private void checkContractOrderStatus(OpenAction openAction, String partyId) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(ContractOrder::getPartyId, partyId); wrapper.eq(ContractOrder::getState, "submitted"); wrapper.eq(ContractOrder::getLocationType, openAction.getLocationType() == 0 ? 1 : 0); List list = contractOrderService.list(wrapper); if (CollectionUtil.isNotEmpty(list)) { String errorMessage = openAction.getLocationType() == 0 ? "下单失败,当前持有全仓仓位,不支持逐仓下单!" : "下单失败,当前持有逐仓仓位,不支持全仓下单!"; throw new YamiShopBindException(errorMessage); } } // 校验用户状态 private void checkUserStatus(String partyId) { User user = userService.getById(partyId); if (!user.isEnabled()) { throw new YamiShopBindException("用户已锁定"); } } // 校验用户网络限制 private void checkUserNetworkRestriction(String partyId) { Syspara syspara = sysparaService.find("stop_user_internet"); String stopUserInternet = syspara.getSvalue(); if (StringUtils.isNotEmpty(stopUserInternet)) { String[] stopUsers = stopUserInternet.split(","); User user = userService.getById(partyId); if (Arrays.asList(stopUsers).contains(user.getUserName())) { throw new YamiShopBindException("无网络"); } } } // 创建合约申请订单 private void createContractApplyOrder(OpenAction openAction, String partyId) { ContractApplyOrder order = new ContractApplyOrder(); order.setPartyId(partyId); order.setSymbol(openAction.getSymbol()); order.setDirection(openAction.getDirection()); order.setOffset(ContractApplyOrder.OFFSET_OPEN); // order.setVolume(openAction.getAmount()); // order.setVolumeOpen(openAction.getAmount()); order.setLeverRate(openAction.getLever_rate()); order.setPrice(openAction.getPrice()); order.setStopPriceProfit(openAction.getStop_price_profit()); order.setStopPriceLoss(openAction.getStop_price_loss()); order.setOrderPriceType(openAction.getPrice_type()); order.setState(ContractApplyOrder.STATE_SUBMITTED); order.setMoney(openAction.getAmount()); contractApplyOrderService.saveCreate(order); } /** * 平仓 */ @RequestMapping(action + "close.action") public Result close(@Valid CloseAction closeAction) throws IOException, InterruptedException { String partyId = SecurityUtils.getUser().getUserId(); RLock rLock = redissonClient.getLock("contract_close_" + partyId); boolean lockResult = rLock.tryLock(5, TimeUnit.SECONDS); if (!lockResult) { throw new YamiShopBindException("请稍后再试"); } try { User user = userService.getById(partyId); if (!user.isEnabled()) { throw new YamiShopBindException("用户已锁定"); } 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("无网络"); } } ContractApplyOrder order = new ContractApplyOrder(); order.setPartyId(partyId); order.setSymbol(closeAction.getSymbol()); order.setDirection(closeAction.getDirection()); order.setOffset(ContractApplyOrder.OFFSET_OPEN); order.setVolume(closeAction.getAmount()); order.setVolumeOpen(closeAction.getAmount()); order.setPrice(closeAction.getPrice()); order.setOrderPriceType(closeAction.getPrice_type()); order.setState(ContractApplyOrder.STATE_SUBMITTED); this.contractApplyOrderService.saveCreate(order); } catch (Exception e) { log.error("平仓失败" , e); return Result.failed(e.getMessage()); } finally { rLock.unlock(); } return Result.succeed(null,"ok"); } /** * 订单表列表数据 * * @return */ @ApiOperation(value = "查询订单表列表数据") @RequestMapping(action + "list.action") public Result>> list(@RequestParam(value = "page_no", required = false,defaultValue = "1") Integer pageNo, @RequestParam(required = false) String type, @RequestParam(required = false) String symbol, @RequestParam(required = false)String startTime, @RequestParam(required = false)String endTime,@RequestParam(required = false)String symbolType ) throws Exception { Page page = new Page<>(); if ("orders".equals(type)) { page.setSize(1000); }else{ page.setSize(10); } Page> result = contractApplyOrderService.findList(page, SecurityUtils.getUser().getUserId(), symbol, type, startTime, endTime, symbolType); Result>> succeed = Result.succeed(result.getRecords()); succeed.setTotal(result.getTotal()); return succeed; } /** * 查询订单详情 *

* order_no 订单号 */ @RequestMapping(action + "get.action") public Result> get(@RequestParam @NotBlank String order_no) throws IOException { ContractApplyOrder order = this.contractApplyOrderService.findByOrderNo(order_no); if (order == null) { throw new YamiShopBindException("委托单不存在"); } Map map = new HashMap(); map.put("order_no", order.getOrderNo()); Item bySymbol = itemService.findBySymbol(order.getSymbol()); if(LangUtils.isEnItem()){ bySymbol.transName(); } map.put("name", bySymbol.getName()); map.put("symbol", order.getSymbol()); map.put("create_time_ts", order.getCreateTimeTs()); map.put("volume", order.getVolume()); map.put("volume_open", order.getVolumeOpen()); map.put("direction", order.getDirection()); map.put("offset", order.getOffset()); map.put("lever_rate", order.getLeverRate()); map.put("price", order.getPrice()); map.put("stop_price_profit", order.getStopPriceProfit()); if( order.getStopPriceLoss() !=null) { map.put("stop_price_loss", order.getStopPriceLoss().setScale(4, RoundingMode.HALF_UP)); }else{ map.put("stop_price_loss", null); } map.put("price_type", order.getOrderPriceType()); map.put("state", order.getState()); map.put("amount", order.getVolume().multiply(order.getUnitAmount()).setScale(4, RoundingMode.HALF_UP)); map.put("amount_open", order.getVolumeOpen().multiply(order.getUnitAmount())); map.put("fee", order.getFee()); map.put("deposit", order.getDeposit()); return Result.succeed(map); } /** * 撤单 *

* order_no 订单号 */ @RequestMapping(action + "cancel.action") public Result cancel(@RequestParam @NotBlank String order_no) throws IOException { try { CancelDelayThread lockDelayThread = new CancelDelayThread(SecurityUtils.getUser().getUserId(), order_no, this.contractApplyOrderService, false); Thread t = new Thread(lockDelayThread); t.start(); } catch (Exception e) { log.error("执行撤单异常", e); throw new YamiShopBindException("执行撤单异常"); } return Result.succeed(null,"success"); } /** * 一键撤单 */ @RequestMapping(action + "cancelAll.action") public Result cancelAll() throws IOException { try { CancelDelayThread lockDelayThread = new CancelDelayThread(SecurityUtils.getUser().getUserId(), "", this.contractApplyOrderService, true); Thread t = new Thread(lockDelayThread); t.start(); } catch (Exception e) { log.error("执行撤单异常", e); throw new YamiShopBindException("执行撤单异常"); } return Result.succeed(null,"success"); } /** * 新线程处理,直接拿到订单锁处理完成后退出 */ public class CancelDelayThread implements Runnable { private String partyId; private String order_no; private ContractApplyOrderService contractApplyOrderService; private boolean all = false; public void run() { try { while (true) { if (true == all) { // 一键撤单 // if (ContractLock.add("all")) { if (contractLockService.getContractLock("all")) { this.contractApplyOrderService.saveCancelAllByPartyId(partyId); // 处理完退出 break; } ThreadUtils.sleep(100); } else { // if (ContractLock.add(order_no)) { if (contractLockService.getContractLock(order_no)) { this.contractApplyOrderService.saveCancel(partyId, order_no); // 处理完退出 break; } ThreadUtils.sleep(100); } } } catch (Throwable t) { log.error("error:", t); } finally { ThreadUtils.sleep(100); if (true == all) { // ContractLock.remove("all"); contractLockService.removeContractLock("all"); } else { // ContractLock.remove(order_no); contractLockService.removeContractLock(order_no); } } } public CancelDelayThread(String partyId, String order_no, ContractApplyOrderService contractApplyOrderService, boolean all) { this.partyId = partyId; this.order_no = order_no; this.contractApplyOrderService = contractApplyOrderService; this.all = all; } } /** * 订单列表 *

* page_no 页码 * symbol 币种 * type 查询类型:orders 当前持仓单;hisorders 历史持仓单; */ @RequestMapping(action + "assets.action") public Result> assets(HttpServletRequest request) throws IOException { String page_no = request.getParameter("page_no"); if(StringUtils.isEmptyString(page_no)){ page_no="1"; } String symbol = request.getParameter("symbol"); String type = request.getParameter("type"); String symbolType = request.getParameter("symbolType"); if(StringUtils.isEmptyString(symbolType)){ symbolType = Item.forex; } Page page = new Page<>(); if ("orders".equals(type)) { page.setSize(1000); }else{ page.setSize(100); } Page result = contractApplyOrderService.findList(page, SecurityUtils.getUser().getUserId(), type, symbolType); List datas = result.getRecords(); if (!StringUtils.isInteger(page_no)) { throw new YamiShopBindException("页码不是整数"); } if (Integer.valueOf(page_no).intValue() <= 0) { throw new YamiShopBindException("页码不能小于等于0"); } Long count = 0L; String symbolsStr = ""; Set symbols = new HashSet(); for (int i = 0; i < datas.size(); i++) { String sym = datas.get(i).getSymbol(); if (!symbols.contains(sym)) { symbols.add(sym); if (i != 0) { symbolsStr = symbolsStr + "," + sym; } else { symbolsStr = sym; } } } List realtime_all = dataService.realtime(symbolsStr); if (realtime_all.size() <= 0) { realtime_all = new ArrayList(); } Map realtimeMap = new HashMap(); for (int i = 0; i < realtime_all.size(); i++) { realtimeMap.put(realtime_all.get(i).getSymbol(), realtime_all.get(i)); } double profitAll = 0; double closeAll = 0; double inventory_charge_all = 0; double fee_all = 0; double deposit_all = 0; double profitAndLoss = 0; double expectedProfitAndLoss = 0; for (int i = 0; i < datas.size(); i++) { // 标记价格 ContractApplyOrder data = datas.get(i); Realtime realtime = realtimeMap.get(data.getSymbol()); BigDecimal mark_price = realtime.getClose(); if(data.getDirection().equalsIgnoreCase("buy")){ expectedProfitAndLoss += data.getVolume().multiply(data.getLeverRate()).multiply(data.getUnitAmount()).multiply(mark_price.subtract(data.getPrice()).divide(data.getPrice(), 4, RoundingMode.HALF_UP)).doubleValue(); }else{ expectedProfitAndLoss -= data.getVolume().multiply(data.getLeverRate()).multiply(data.getUnitAmount()).multiply(mark_price.subtract(data.getPrice()).divide(data.getPrice(), 4, RoundingMode.HALF_UP)).doubleValue(); } //手续费 double fee = Double.parseDouble(data.getFee().toString()); fee_all += fee; //保证金 double deposit = Double.parseDouble(data.getDeposit().toString()); deposit_all += deposit; //deposit } Map dateN = new HashMap<>(); dateN.put("profit_all", Double.valueOf(String.format("%.5f", 0.0))); //利润 dateN.put("cash_deposit", Double.valueOf(String.format("%.5f", closeAll))); //入金 dateN.put("inventory_charge_all", Double.valueOf(String.format("%.5f", inventory_charge_all))); //库存费 dateN.put("fee_all", fee_all); //手续费 dateN.put("deposit_all", Double.valueOf(String.format("%.5f", deposit_all))); dateN.put("profitAndLoss", Double.valueOf(String.format("%.4f", 0.0D))); dateN.put("expectedProfitAndLoss", Double.valueOf(String.format("%.2f", expectedProfitAndLoss))); dateN.put("count", count); return Result.ok(dateN); } }