package com.bizzan.bitrade.controller;

import com.alibaba.druid.support.logging.Log;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bizzan.bitrade.constant.BooleanEnum;
import com.bizzan.bitrade.constant.PageModel;
import com.bizzan.bitrade.engine.ContractCoinMatch;
import com.bizzan.bitrade.engine.ContractCoinMatchFactory;
import com.bizzan.bitrade.engine.TradingAccountMonitor;
import com.bizzan.bitrade.entity.*;
import com.bizzan.bitrade.entity.form.OrderQueryForm;
import com.bizzan.bitrade.entity.transform.AuthMember;
import com.bizzan.bitrade.service.*;
import com.bizzan.bitrade.util.*;
import com.bizzan.bitrade.vo.ContractCoinVo;
import com.bizzan.bitrade.vo.TradingAccount;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.util.Assert;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import static com.bizzan.bitrade.constant.SysConstant.SESSION_MEMBER;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 委托订单处理类
 */
@Slf4j
@Api(value = "OrderController", tags = "委托订单处理接口")
@RestController
@RequestMapping("/order")
public class OrderController {
    @Autowired
    private MemberWalletService walletService;

    @Autowired
    private CoinService coinService;

    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    @Value("${exchange.max-cancel-times:-1}")
    private int maxCancelTimes;

    @Autowired
    private LocaleMessageSourceService msService;

    @Autowired
    private MemberService memberService;

    @Autowired
    private ContractCoinService contractCoinService;

    @Autowired
    private ContractOrderEntrustService contractOrderEntrustService;

    @Autowired
    private MemberTransactionService memberTransactionService;

    @Autowired
    private MemberContractWalletService memberContractWalletService;

    @Autowired
    private MemberContractPositionService memberContractPositionService;
    @Autowired
    private UserPurseService userPurseService;

    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private RedisLock redisLock;

    @Autowired
    private ContractCoinMatchFactory contractCoinMatchFactory; // 合约引擎工厂

    @Autowired
    private TradingAccountMonitor tradingAccountMonitor;

    @Autowired
    private ExperienceGiftRecordService experienceGiftRecordService;

    @Autowired
    private ExperienceGiftProductCoinService experienceGiftProductCoinService;


    private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private final int defaultVolumeScale = 1;

    /**
     * 合约下单（开仓）- 金本位
     * 二种操作类型：买入开多，卖出开多
     *
     * @param authMember
     * @return
     */
    @ApiOperation(value = "开仓下单", notes = "合约开仓下单")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "contractCoinId", value = "合约交易对ID", required = true, paramType = "form", dataType = "int"),
            @ApiImplicitParam(name = "direction", value = "方向 0：开多  1：开空", required = true, paramType = "form", dataType = "int"),
            @ApiImplicitParam(name = "type", value = "类型 0：市价 1：限价 2：计划委托", required = true, paramType = "form", dataType = "int"),
            @ApiImplicitParam(name = "triggerPrice", value = "触发价格", required = false, paramType = "form", dataType = "float"),
            @ApiImplicitParam(name = "entrustPrice", value = "委托价格", required = true, paramType = "form", dataType = "float"),
            @ApiImplicitParam(name = "lossPrice", value = "止损价格", required = false, paramType = "form", dataType = "float"),
            @ApiImplicitParam(name = "profitPrice", value = "止盈价格", required = false, paramType = "form", dataType = "float"),
            //@ApiImplicitParam(name = "leverage",value = "杠杆",required = true,paramType = "form",dataType = "int"),
            @ApiImplicitParam(name = "volume", value = "委托数量", required = true, paramType = "form", dataType = "float"),
            @ApiImplicitParam(name = "isPercentage", value = "是否百分比委托 1:百分比", required = false, paramType = "form", dataType = "int")
    })
    @PostMapping("open/v3")
    public MessageResult openOrder(@SessionAttribute(SESSION_MEMBER) AuthMember authMember,
                                   @RequestParam(value = "contractCoinId") Long contractCoinId,// 合约交易对
                                   @RequestParam(value = "direction") ContractOrderDirection direction,// 1：买（平空）  2：卖（平多）
                                   @RequestParam(value = "type") ContractOrderType type,// 0：市价 1：限价 2：计划委托
                                   @RequestParam(value = "triggerPrice", required = false) BigDecimal triggerPrice,// 触发价格
                                   @RequestParam(value = "entrustPrice") BigDecimal entrustPrice,// 委托价格(计划委托时如为0：市价成交)
                                   @RequestParam(value = "lossPrice", required = false) BigDecimal lossPrice,
                                   @RequestParam(value = "profitPrice", required = false) BigDecimal profitPrice,
                                   //@RequestParam(value = "leverage") BigDecimal leverage,// 委托价格
                                   @RequestParam(value = "volume") BigDecimal volume,// 委托数量（张）
                                   @RequestParam(value = "isPercentage", required = false) Integer isPercentage, //是否百分比
                                   @RequestParam(value = "isReversedOpen", required = false) Integer isReversedOpen,
                                   @RequestParam(value = "positionType", required = false) ContractPositionType positionType // 前端传递的仓位模式，优先使用
    ) {

        // 输入合法性检查
        if (contractCoinId == null || direction == null || type == null || volume == null) {
            return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
        }
        if (direction != ContractOrderDirection.BUY && direction != ContractOrderDirection.SELL) {
            return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
        }
        if (type != ContractOrderType.MARKET_PRICE && type != ContractOrderType.LIMIT_PRICE && type != ContractOrderType.SPOT_LIMIT) {
            return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
        }

        // 检查交易对是否存在
        ContractCoin contractCoin = contractCoinService.findOne(contractCoinId);
        if (null == contractCoin) {
            return MessageResult.error(500, msService.getMessage("NONSUPPORT_COIN"));
        }

        // 限价单及计划委托单需要输入委托价格
        if (type == ContractOrderType.LIMIT_PRICE || type == ContractOrderType.SPOT_LIMIT) {
            if (null == entrustPrice) {
                return MessageResult.error(500, msService.getMessage("INPUT_COMMISSION_PRICE"));
            }
        }
        // 检查用户合法性
        Member member = memberService.findOne(authMember.getId());
        if (null == member) return MessageResult.error(500, msService.getMessage("ACCOUNT_NOT_EXIST"));
        // 用户是否被禁止交易
        if (member.getTransactionStatus().equals(BooleanEnum.IS_FALSE)) {
            return MessageResult.error(500, msService.getMessage("CANNOT_TRADE"));
        }

        // 检查交易对是否可用
        if (contractCoin.getEnable() != 1 || contractCoin.getExchangeable() != 1) {
            return MessageResult.error(500, msService.getMessage("COIN_FORBIDDEN"));
        }
        // 是否允许开多仓（买涨）
        if (contractCoin.getEnableOpenBuy() == BooleanEnum.IS_FALSE && direction == ContractOrderDirection.BUY) {
            return MessageResult.error(500, msService.getMessage("SUSPEND_LONG1"));
        }
        // 是否允许开空仓（买跌）
        if (contractCoin.getEnableOpenSell() == BooleanEnum.IS_FALSE && direction == ContractOrderDirection.SELL) {
            return MessageResult.error(500, msService.getMessage("SUSPEND_SHORT1"));
        }
        // 是否允许市价开仓做多
        if (contractCoin.getEnableMarketBuy() == BooleanEnum.IS_FALSE && direction == ContractOrderDirection.BUY) {
            return MessageResult.error(500, msService.getMessage("SUSPEND_LONG2"));
        }
        // 是否允许市价开仓做空
        if (contractCoin.getEnableMarketSell() == BooleanEnum.IS_FALSE && direction == ContractOrderDirection.SELL) {
            return MessageResult.error(500, msService.getMessage("SUSPEND_SHORT2"));
        }

        // 合约倍数是否在规定范围
        /*if (contractCoin.getLeverageType() == 1) { // 分离倍数
            String[] leverageArr = contractCoin.getLeverage().split(",");
            boolean isExist = false;
            for (String str : leverageArr) {
                if (BigDecimal.valueOf(Integer.parseInt(str)).compareTo(leverage) == 0) {
                    isExist = true;
                }
            }
            if (!isExist) {
                return MessageResult.error(500, msService.getMessage("CONTRACT_MULTIPLE_NOT_EXIST"));
            }
        } else { // 范围倍数
            String[] leverageArr = contractCoin.getLeverage().split(",");
            //if (leverageArr.length != 2) return MessageResult.error(500, msService.getMessage("CONTRACT_MULTIPLE_ERROR"));

            BigDecimal low = BigDecimal.valueOf(Integer.parseInt(leverageArr[0]));
            BigDecimal high = BigDecimal.valueOf(Integer.parseInt(leverageArr[leverageArr.length-1]));
            if (leverage.compareTo(low) < 0 || leverage.compareTo(high) > 0) {
                return MessageResult.error(500, msService.getMessage("CONTRACT_MULTIPLE_NOT_ALLOWED"));
            }
        }*/

        /*BigDecimal totalPosition = memberContractPositionService.sumCurrentPositionByMemberId(member.getId());
        BigDecimal totalPosition = memberContractWallet.getUsdtBuyPosition().add(memberContractWallet.getUsdtFrozenBuyPosition()).add(memberContractWallet.getUsdtSellPosition().add(memberContractWallet.getUsdtFrozenSellPosition()));
        BigDecimal orderVolume = contractOrderEntrustService.sumVolumeByMemberIdAndContractCoinId(member.getId(), contractCoin.getId());
        if(contractCoin.getMaxTotalShare()!=null && volume.add(totalPosition).add(orderVolume).compareTo(contractCoin.getMaxTotalShare())>0)
        	return MessageResult.error(500, "不能超过最大持仓量"+contractCoin.getMaxTotalShare().setScale(2, BigDecimal.ROUND_DOWN));*/

        // 检查合约引擎是否存在
        if (!contractCoinMatchFactory.containsContractCoinMatch(contractCoin.getSymbol())) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_ENGINE_ERROR"));
        }

        //Map<String, BigDecimal> priceMap = tradingAccountMonitor.getPriceMapAtPresent();
        BigDecimal currentPrice = contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol()).getNowPrice();
        //memberContractPositionService.adjustMarginByCurrentPrice(member.getId(), priceMap);


        if (type == ContractOrderType.LIMIT_PRICE || type == ContractOrderType.SPOT_LIMIT) {
            long limitCount = contractOrderEntrustService.countRunningOpenByMemberId(member.getId());
            if (limitCount > 10) {
                return MessageResult.error(500, msService.getMessage("LIMIT_ORDER_MUCH"));
            }
        }

        //检查是否太快开对冲单
        /*ContractOrderEntrust lastestContractOrderEntrust = contractOrderEntrustService.getLatestInOppositeDirection(member.getId(), contractCoinId, direction);
        BigDecimal latestPosition = userPurse.getCurrentPositionByContractCoinIdAndDirection(contractCoinId, direction);
        if(lastestContractOrderEntrust!=null) {
	        if(lastestContractOrderEntrust.getType() == ContractOrderType.MARKET_PRICE) {
	        	if(latestPosition.compareTo(BigDecimal.ZERO)>0 && lastestContractOrderEntrust!=null && lastestContractOrderEntrust.getUsdtOpenTime()!=null && DateUtil.getTimeMillis()-lastestContractOrderEntrust.getUsdtOpenTime()<180000) {
	            	return MessageResult.error(500, "请不要在3分钟内开对冲单");
	            }
	        } else {
	        	if(lastestContractOrderEntrust.getStatus() == ContractOrderEntrustStatus.ENTRUST_SUCCESS) {
	        		if(latestPosition.compareTo(BigDecimal.ZERO)>0 && lastestContractOrderEntrust!=null && lastestContractOrderEntrust.getCreateTime()!=null && DateUtil.getTimeMillis()-lastestContractOrderEntrust.getCreateTime()<180000) {
	                	return MessageResult.error(500, "请不要在3分钟内开对冲单");
	                }
	        	} else {
			        if(lastestContractOrderEntrust!=null 
			        		&& lastestContractOrderEntrust.getCreateTime()!=null && DateUtil.getTimeMillis()-lastestContractOrderEntrust.getCreateTime()<180000) {
			        	return MessageResult.error(500, "请不要在3分钟内开对冲单");
			        }
	        	}
	        }
        }*/

        // 计划委托中的触发价格需要大于0
        if (type == ContractOrderType.SPOT_LIMIT) {
            if (triggerPrice.compareTo(BigDecimal.ZERO) <= 0) {
                return MessageResult.error(500, msService.getMessage("TRIGGER_PRICE1"));
            }
            if (entrustPrice.compareTo(BigDecimal.ZERO) < 0) {
                return MessageResult.error(500, msService.getMessage("TRIGGER_PRICE2"));
            }
        }

        BigDecimal openPrice = BigDecimal.ZERO;

        // 计算市价成交价格（大约）
        openPrice = currentPrice;
        if (direction == ContractOrderDirection.BUY) { // 买入，滑点计算，做多，更高价格成交
            if (contractCoin.getSpreadType() == 1) { // 滑点类型：百分比
                openPrice = currentPrice.add(currentPrice.multiply(contractCoin.getSpread())); // 已当前价成交（或滑点价成交）
            } else { // 滑点类型：固定额
                openPrice = currentPrice.add(contractCoin.getSpread());
            }
        } else { // 卖出，滑点计算，做空，更低价格成交
            if (contractCoin.getSpreadType() == 1) { // 滑点类型：百分比
                openPrice = currentPrice.subtract(currentPrice.multiply(contractCoin.getSpread())); // 已当前价成交（或滑点价成交）
            } else { // 滑点类型：固定额
                openPrice = currentPrice.subtract(contractCoin.getSpread());
            }
        }
        if (openPrice.compareTo(BigDecimal.ZERO) <= 0) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_ENGINE_ERROR"));
        }

        if (volume.compareTo(BigDecimal.ZERO) <= 0) {
            return MessageResult.error(500, msService.getMessage("INCORRECT_OPENING_NUMBER"));
        }

        ContractOrderEntrust orderEntrust = new ContractOrderEntrust();

        if (isPercentage != null && isPercentage == 1) {
            if (volume.compareTo(BigDecimal.ONE) < 0 || volume.compareTo(BigDecimal.valueOf(100)) > 0) {
                return MessageResult.error(500, msService.getMessage("ORDER_PERCENT_LIMIT"));
            }
        } else {
            if (volume.compareTo(contractCoin.getMinShare()) < 0)
                return MessageResult.error(500, msService.getMessage("ORDER_OPEN_LESS_THAN_MIN") + contractCoin.getMinShare().stripTrailingZeros().toPlainString());
        }
        orderEntrust.setIsPercentage(isPercentage);
        log.info("isPercentage={}, volume={}", isPercentage, volume);

        //检查止盈止损合法性
        boolean hasCut = false;
        if (type == ContractOrderType.LIMIT_PRICE || type == ContractOrderType.SPOT_LIMIT) {
            boolean hasLossPrice = lossPrice != null && lossPrice.compareTo(BigDecimal.ZERO) > 0;
            boolean hasProfitPrice = profitPrice != null && profitPrice.compareTo(BigDecimal.ZERO) > 0;

            if (hasLossPrice || hasProfitPrice) {
                if (type == ContractOrderType.LIMIT_PRICE) {
                    if (direction == ContractOrderDirection.BUY) {
                        if (entrustPrice.compareTo(openPrice) >= 0)
                            return MessageResult.error(500, msService.getMessage("LIMIT_PRICE_MUCH"));
                    } else {
                        if (entrustPrice.compareTo(openPrice) <= 0)
                            return MessageResult.error(500, msService.getMessage("LIMIT_PRICE_LESS"));
                    }
                } else if (type == ContractOrderType.SPOT_LIMIT) {
                    log.info("entrustPrice={}, triggerPrice={}", entrustPrice, triggerPrice);
                    if (entrustPrice.compareTo(BigDecimal.ZERO) <= 0)
                        return MessageResult.error(500, msService.getMessage("ORDER_CUT_ENTRUST_FIRST"));
                    if (direction == ContractOrderDirection.BUY) {
                        if (entrustPrice.compareTo(triggerPrice) > 0)
                            return MessageResult.error(500, msService.getMessage("ORDER_CUT_ENTRUST_GREATER"));
                    } else {
                        if (entrustPrice.compareTo(triggerPrice) < 0)
                            return MessageResult.error(500, msService.getMessage("ORDER_CUT_ENTRUST_LESS"));
                    }
                }
                hasCut = true;
            }

            if (hasLossPrice) {
                if (direction == ContractOrderDirection.BUY) {
                    //if(lossPrice.compareTo(entrustPrice)>=0 || lossPrice.compareTo(openPrice)>=0) {
                    if (lossPrice.compareTo(entrustPrice) >= 0) {
                        return MessageResult.error(500, msService.getMessage("ORDER_CUT_LOSS_LESS"));
                    }
                } else {
                    //if(lossPrice.compareTo(entrustPrice)<=0 || lossPrice.compareTo(openPrice)<=0) {
                    if (lossPrice.compareTo(entrustPrice) <= 0) {
                        return MessageResult.error(500, msService.getMessage("ORDER_CUT_LOSS_GREATER"));
                    }
                }
            }
            if (hasProfitPrice) {
                if (direction == ContractOrderDirection.BUY) {
                    //if(profitPrice.compareTo(entrustPrice)<=0 || profitPrice.compareTo(openPrice)<=0) {
                    if (profitPrice.compareTo(entrustPrice) <= 0) {
                        return MessageResult.error(500, msService.getMessage("ORDER_CUT_PROFIT_GREATER"));
                    }
                } else {
                    //if(profitPrice.compareTo(entrustPrice)>=0 || profitPrice.compareTo(openPrice)>=0) {
                    if (profitPrice.compareTo(entrustPrice) >= 0) {
                        return MessageResult.error(500, msService.getMessage("ORDER_CUT_PROFIT_LESS"));
                    }
                }
            }

        }

        UserPurse tradingAccount = userPurseService.findOne(member.getId());

        MemberContractWallet memberContractWallet = memberContractWalletService.findByMemberIdAndContractCoin(authMember.getId(), contractCoin);

        // 新建合约委托单
        orderEntrust.setContractId(contractCoin.getId()); // 合约ID
        orderEntrust.setMemberId(member.getId()); // 用户ID
        orderEntrust.setSymbol(contractCoin.getSymbol()); // 交易对符号
        orderEntrust.setBaseSymbol(contractCoin.getSymbol().split("/")[1]); // 基币/结算币
        orderEntrust.setCoinSymbol(contractCoin.getSymbol().split("/")[0]); // 币种符号
        orderEntrust.setDirection(direction); // 开仓方向：做多/做空
        orderEntrust.setContractOrderEntrustId(GeneratorUtil.getOrderId("CE"));
        orderEntrust.setVolume(volume); // 开仓张数
        orderEntrust.setTradedVolume(BigDecimal.ZERO); // 已交易数量
        orderEntrust.setTradedPrice(BigDecimal.ZERO); // 成交价格
        orderEntrust.setPrincipalUnit("USDT"); // 保证金单位
        orderEntrust.setPrincipalAmount(BigDecimal.ZERO); // 保证金数量
        orderEntrust.setCreateTime(DateUtil.getTimeMillis()); // 开仓时间
        orderEntrust.setType(type);
        orderEntrust.setTriggerPrice(triggerPrice); // 触发价
        orderEntrust.setEntrustPrice(entrustPrice); // 委托价格
        orderEntrust.setLossPrice(lossPrice);
        orderEntrust.setProfitPrice(profitPrice);
        orderEntrust.setEntrustType(ContractOrderEntrustType.OPEN); // 开仓
        orderEntrust.setTriggeringTime(0L); // 触发时间，暂时无效
        orderEntrust.setShareNumber(contractCoin.getShareNumber());
        orderEntrust.setProfitAndLoss(BigDecimal.ZERO); // 盈亏（仅平仓计算）
        orderEntrust.setPatterns(ContractOrderPattern.CROSSED); // 仓位模式
        orderEntrust.setOpenFee(BigDecimal.ZERO); // 开仓手续费
        orderEntrust.setStatus(ContractOrderEntrustStatus.ENTRUST_ING); // 委托状态：委托中
        orderEntrust.setCurrentPrice(currentPrice);
        orderEntrust.setIsBlast(0); // 不是爆仓单
        orderEntrust.setOpenType(type.ordinal());
        orderEntrust.setLeverage(memberContractWallet.getUsdtBuyLeverage().intValue());
        // 仓位模式优先级：带单员强制全仓 > 前端传递值 > DB中用户设置
        if (memberService.isTrader(member.getId())) {
            orderEntrust.setPositionType(ContractPositionType.INTEGRAL);
        } else if (positionType != null) {
            orderEntrust.setPositionType(positionType);
        } else {
            orderEntrust.setPositionType(tradingAccount.getPositionType());
        }
        orderEntrust.setMoneyType(tradingAccount.getMoneyType());
        if (type == ContractOrderType.SPOT_LIMIT) { // 是否是计划委托单
            orderEntrust.setIsFromSpot(1);
        } else {
            orderEntrust.setIsFromSpot(0);
        }
        if (isReversedOpen != null && isReversedOpen == 1) {
            orderEntrust.setIsReversedOpen(1);
        } else {
            orderEntrust.setIsReversedOpen(0);
        }

        if (orderEntrust.getType() != ContractOrderType.SPOT_LIMIT) {
            int success = tradingAccountMonitor.genVolumeForOrder(orderEntrust, false);
            if (success == 1) {
                return MessageResult.error(500, msService.getMessage("ORDER_OPEN_INSUFFICIENT_VOLUME"));
            } else if (success == 2) {
                return MessageResult.error(500, msService.getMessage("ORDER_OPEN_LESS_THAN_MIN") + contractCoin.getMinShare().stripTrailingZeros().toPlainString());
            }

        } else {
            orderEntrust.setDecimalScale(1);
        }

        // 保存委托单
        ContractOrderEntrust retObj = contractOrderEntrustService.save(orderEntrust);


        if (retObj != null) {
            // 发送消息至Exchange系统
            //kafkaTemplate.send("swap-order-open", JSON.toJSONString(retObj));
            contractCoinMatchFactory.getContractCoinMatch(retObj.getSymbol()).trade(retObj);

            //kafkaTemplate.send("follow-swap-order-open", JSON.toJSONString(retObj));

            //通知钱包变更
            //JSONObject jsonObj = new JSONObject();
            //jsonObj.put("symbol", contractCoin.getSymbol());
            //jsonObj.put("walletId", memberContractWallet.getId());
            //kafkaTemplate.send("member-wallet-change", JSON.toJSONString(jsonObj));

            log.info(">>>>>>>>>>订单提交完成>>>>>>>>>>");
            // 返回结果
            MessageResult result = MessageResult.success(msService.getMessage("SWAP_SUCCESS"));
            result.setData(retObj);
            return result;
        } else {
            // 返回结果
            MessageResult result = MessageResult.error(msService.getMessage("SWAP_FAILED"));
            result.setData(null);
            return result;
        }
    }

    @ApiOperation(value = "止盈止损单", notes = "设置止盈止损单")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "positionId", value = "仓位ID", required = true, paramType = "form", dataType = "int"),
            @ApiImplicitParam(name = "profitPrice", value = "止盈价格", required = true, paramType = "form", dataType = "float"),
            @ApiImplicitParam(name = "profitEntrustPrice", value = "止盈委托价格", required = false, paramType = "form", dataType = "float"),
            @ApiImplicitParam(name = "lossPrice", value = "止损价格", required = true, paramType = "form", dataType = "float"),
            @ApiImplicitParam(name = "lossEntrustPrice", value = "止损委托价格", required = false, paramType = "form", dataType = "float"),
            @ApiImplicitParam(name = "volume", value = "委托数量", required = true, paramType = "form", dataType = "float")
    })
    @PostMapping("cut")
    public MessageResult cutOrder(@SessionAttribute(SESSION_MEMBER) AuthMember authMember,
                                  @RequestParam(value = "positionId") Long positionId,// 合约交易对
                                  @RequestParam(value = "profitPrice", required = false) BigDecimal profitPrice,// 触发价格
                                  @RequestParam(value = "profitEntrustPrice", required = false) BigDecimal profitEntrustPrice,// 触发价格
                                  @RequestParam(value = "lossPrice", required = false) BigDecimal lossPrice,// 触发价格
                                  @RequestParam(value = "lossEntrustPrice", required = false) BigDecimal lossEntrustPrice,// 触发价格
                                  @RequestParam(value = "volume") BigDecimal volume,// 委托数量（张）
                                  Long entrustId
    ) {
        // 输入合法性检查
        if (positionId == null || volume == null) {
            return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
        }
        // 检查合约是否存在
        MemberContractPosition memberContractPosition = memberContractPositionService.findOne(positionId);
        if (memberContractPosition.getMemberId().longValue() != authMember.getId()) {
            return MessageResult.error(500, msService.getMessage("ILLEGAL_OPERATION"));
        }
        ContractCoin contractCoin = memberContractPosition.getCoin();
        if (null == contractCoin) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_NOT_EXIST"));
        }
        if (memberContractPosition.getIsLock() != null && memberContractPosition.getIsLock() == 1) {
            return MessageResult.error(501, msService.getMessage("CANNOT_CLOSE"));
        }
        if (memberContractPosition.getExperienceRecordId() != null) {
            return MessageResult.error(500, msService.getMessage("EG_USE_DEDICATED_INTERFACE"));
        }
        if (memberContractPosition.getFollowPositionId() != null) {
            return MessageResult.error(500, "跟单仓位不允许设置止盈止损");
        }
        if (memberContractPosition.getStatus() == ContractPositionStatus.DONE) {
            return MessageResult.error(500, msService.getMessage("CLOSED_POSITION"));
        }

        ContractOrderDirection direction;
        if (memberContractPosition.getDirection() == ContractOrderDirection.BUY)
            direction = ContractOrderDirection.SELL;
        else
            direction = ContractOrderDirection.BUY;

        // 检查用户合法性
        Member member = memberService.findOne(authMember.getId());
        if (null == member) return MessageResult.error(500, msService.getMessage("ACCOUNT_NOT_EXIST"));
        // 用户是否被禁止交易
        if (member.getTransactionStatus().equals(BooleanEnum.IS_FALSE)) {
            return MessageResult.error(500, msService.getMessage("CANNOT_TRADE"));
        }
        if (member.getIsLock() == 1) {
            return MessageResult.error(501, msService.getMessage("CANNOT_CLOSE"));
        }

        log.info("cut volume before scale={}", volume);
        volume = volume.setScale(memberContractPosition.getDecimalScale(), BigDecimal.ROUND_DOWN);
        log.info("cut volume after scale={}", volume);
        if (volume.compareTo(BigDecimal.ZERO) <= 0)
            return MessageResult.error(500, msService.getMessage("INCORRECT_NUMBER_CLOSING_POSITIONS"));//小于1张

        //if(volume.compareTo(BigDecimal.ONE) < 0)
        //    return MessageResult.error(500, msService.getMessage("INCORRECT_NUMBER_CLOSING_POSITIONS"));//小于1张
        //if(BigDecimal.valueOf(volume.intValue()).compareTo(volume) != 0)
        //    return MessageResult.error(500, msService.getMessage("INCORRECT_NUMBER_CLOSING_POSITIONS")); // 含有小数


        if (memberContractPosition.getAvailablePosition().compareTo(volume) < 0) {
            return MessageResult.error(500, msService.getMessage("WRONG_AMOUNT_COMMISSION"));
        }

        BigDecimal currentPrice = contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol()).getNowPrice();

        if (currentPrice.compareTo(BigDecimal.ZERO) <= 0) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_ENGINE_ERROR"));
        }

        // 计划委托中的触发价格需要大于0
        if (profitPrice.compareTo(BigDecimal.ZERO) <= 0 && lossPrice.compareTo(BigDecimal.ZERO) <= 0) {
            return MessageResult.error(500, msService.getMessage("TRIGGER_PRICE1"));
        }
        //log.info("direction={}, profitPrice={}, lossPrice={}, currentPrice={}", direction, profitPrice, lossPrice, currentPrice);
        if (direction == ContractOrderDirection.BUY) {
            if (profitPrice.compareTo(BigDecimal.ZERO) > 0 && profitPrice.compareTo(currentPrice) >= 0)
                return MessageResult.error(500, msService.getMessage("ORDER_CUT_PROFIT_LESS_DEAL"));
            if (lossPrice.compareTo(BigDecimal.ZERO) > 0 && lossPrice.compareTo(currentPrice) <= 0)
                return MessageResult.error(500, msService.getMessage("ORDER_CUT_LOSS_GREATER_DEAL"));
        } else {
            if (profitPrice.compareTo(BigDecimal.ZERO) > 0 && profitPrice.compareTo(currentPrice) <= 0)
                return MessageResult.error(500, msService.getMessage("ORDER_CUT_PROFIT_GREATER_DEAL"));
            if (lossPrice.compareTo(BigDecimal.ZERO) > 0 && lossPrice.compareTo(currentPrice) >= 0)
                return MessageResult.error(500, msService.getMessage("ORDER_CUT_LOSS_LESS_DEAL"));
        }

        // 新建合约委托单
        ContractOrderEntrust orderEntrust = new ContractOrderEntrust();
        orderEntrust.setContractId(contractCoin.getId()); // 合约ID
        orderEntrust.setMemberId(member.getId()); // 用户ID
        orderEntrust.setSymbol(contractCoin.getSymbol()); // 交易对符号
        orderEntrust.setBaseSymbol(contractCoin.getSymbol().split("/")[1]); // 基币/结算币
        orderEntrust.setCoinSymbol(contractCoin.getSymbol().split("/")[0]); // 币种符号
        orderEntrust.setDirection(direction); // 平仓方向：平空/平多
        orderEntrust.setContractOrderEntrustId(GeneratorUtil.getOrderId("CE"));
        orderEntrust.setVolume(volume); // 平仓张数
        orderEntrust.setTradedVolume(BigDecimal.ZERO); // 已交易数量
        orderEntrust.setTradedPrice(BigDecimal.ZERO); // 成交价格
        orderEntrust.setPrincipalUnit("USDT"); // 保证金单位
        orderEntrust.setPrincipalAmount(BigDecimal.ZERO); // 保证金数量
        orderEntrust.setCreateTime(DateUtil.getTimeMillis()); // 开仓时间
        orderEntrust.setType(ContractOrderType.SPOT_LIMIT);
        orderEntrust.setProfitPrice(profitPrice); // 触发价
        orderEntrust.setProfitEntrustPrice(profitEntrustPrice); // 委托价格
        orderEntrust.setLossPrice(lossPrice); // 触发价
        orderEntrust.setLossEntrustPrice(lossEntrustPrice); // 委托价格
        orderEntrust.setEntrustType(ContractOrderEntrustType.CLOSE); // 开仓
        orderEntrust.setTriggeringTime(0L); // 触发时间，暂时无效
        orderEntrust.setShareNumber(contractCoin.getShareNumber());
        orderEntrust.setProfitAndLoss(BigDecimal.ZERO); // 盈亏（仅平仓计算）
        orderEntrust.setPatterns(ContractOrderPattern.CROSSED); // 仓位模式
        orderEntrust.setCloseFee(BigDecimal.ZERO);
        orderEntrust.setPositionType(memberContractPosition.getType());
        orderEntrust.setCurrentPrice(currentPrice);
        orderEntrust.setIsBlast(0); // 不是爆仓单
        orderEntrust.setIsFromSpot(1);
        orderEntrust.setIsCut(1);
        orderEntrust.setPositionId(positionId);
        orderEntrust.setLeverage(memberContractPosition.getLeverage().intValue());
        orderEntrust.setUsdtOpenPrice(memberContractPosition.getOpenPrice());
        orderEntrust.setCloseType(CloseType.CUT);
        orderEntrust.setMoneyType(memberContractPosition.getMoneyType());
        orderEntrust.setDecimalScale(memberContractPosition.getDecimalScale());
        orderEntrust.setOpenType(memberContractPosition.getOpenType());
        // 体验金仓位标记，进入撮合引擎后不加入止盈止损队列
        orderEntrust.setExperienceRecordId(memberContractPosition.getExperienceRecordId());

        ContractOrderEntrust retObj = null;
        //int successCount = memberContractPositionService.freezePosition(positionId, volume);
        int successCount = 1;
        if (successCount == 1) {
            // 保存委托单
            orderEntrust.setStatus(ContractOrderEntrustStatus.ENTRUST_ING); // 委托状态：委托中
            retObj = contractOrderEntrustService.save(orderEntrust);
        } else {
            orderEntrust.setStatus(ContractOrderEntrustStatus.ENTRUST_FAILURE); // 委托状态：委托失败
            contractOrderEntrustService.save(orderEntrust);
        }
        if (retObj != null) {
            // 发送消息至Exchange系统
            //kafkaTemplate.send("swap-order-close", JSON.toJSONString(retObj));
            contractCoinMatchFactory.getContractCoinMatch(retObj.getSymbol()).trade(retObj);
            // 体验金仓位同步更新止盈止损价格
            if (memberContractPosition.getExperienceRecordId() != null) {
                experienceGiftRecordService.updateStopPrices(
                        memberContractPosition.getExperienceRecordId(), member.getId(), profitPrice, lossPrice);
            }

            //kafkaTemplate.send("follow-swap-order-close", JSON.toJSONString(retObj));


            //通知钱包变更
            //JSONObject jsonObj = new JSONObject();
            //jsonObj.put("symbol", contractCoin.getSymbol());
            //jsonObj.put("walletId", memberContractWallet.getId());
            //kafkaTemplate.send("member-wallet-change", JSON.toJSONString(jsonObj));

            log.info(">>>>>>>>>>订单提交完成>>>>>>>>>>");
            // 返回结果
            MessageResult result = MessageResult.success(msService.getMessage("SWAP_SUCCESS"));
            result.setData(retObj);
            return result;
        } else {
            // 返回结果
            MessageResult result = MessageResult.error(msService.getMessage("SWAP_FAILED"));
            result.setData(null);
            return result;
        }
    }

    @ApiOperation(value = "止盈止损单(整个仓位)", notes = "设置止盈止损单,整个仓位,不需要传数量")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "positionId", value = "仓位ID", required = true, paramType = "form", dataType = "int"),
            @ApiImplicitParam(name = "profitPrice", value = "止盈价格", required = true, paramType = "form", dataType = "float"),
            @ApiImplicitParam(name = "profitEntrustPrice", value = "止盈委托价格", required = false, paramType = "form", dataType = "float"),
            @ApiImplicitParam(name = "lossPrice", value = "止损价格", required = true, paramType = "form", dataType = "float"),
            @ApiImplicitParam(name = "lossEntrustPrice", value = "止损委托价格", required = false, paramType = "form", dataType = "float")
            //@ApiImplicitParam(name = "volume",value = "委托数量",required = true,paramType = "form",dataType = "float")
    })
    @PostMapping("cut-position")
    public MessageResult cutPositionOrder(@SessionAttribute(SESSION_MEMBER) AuthMember authMember,
                                          @RequestParam(value = "positionId") Long positionId,// 合约交易对
                                          @RequestParam(value = "profitPrice", required = false) BigDecimal profitPrice,// 触发价格
                                          @RequestParam(value = "profitEntrustPrice", required = false) BigDecimal profitEntrustPrice,// 触发价格
                                          @RequestParam(value = "lossPrice", required = false) BigDecimal lossPrice,// 触发价格
                                          @RequestParam(value = "lossEntrustPrice", required = false) BigDecimal lossEntrustPrice,// 触发价格
                                          //@RequestParam(value = "volume") BigDecimal volume,// 委托数量（张）
                                          Long entrustId
    ) {
        // 输入合法性检查
        if (null == positionId) {
            return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
        }
        // 检查合约是否存在
        MemberContractPosition memberContractPosition = memberContractPositionService.findOne(positionId);
        if (memberContractPosition.getMemberId().longValue() != authMember.getId()) {
            return MessageResult.error(500, msService.getMessage("ILLEGAL_OPERATION"));
        }
        ContractCoin contractCoin = memberContractPosition.getCoin();
        if (null == contractCoin) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_NOT_EXIST"));
        }
        if (memberContractPosition.getIsLock() != null && memberContractPosition.getIsLock() == 1) {
            return MessageResult.error(501, msService.getMessage("CANNOT_CLOSE"));
        }
        // 体验金仓位允许走此接口，价格校验后直接更新体验金记录的止盈止损价格（无需创建委托单）
        if (memberContractPosition.getFollowPositionId() != null) {
            return MessageResult.error(500, "跟单仓位不允许设置止盈止损");
        }
        if (memberContractPosition.getStatus() == ContractPositionStatus.DONE) {
            return MessageResult.error(500, msService.getMessage("CLOSED_POSITION"));
        }

        List<ContractOrderEntrust> cutList = contractOrderEntrustService.findPositionCuts(memberContractPosition.getId());
        if (cutList.size() > 0) {
            //return MessageResult.error(500, "不能下多个仓位止盈止损单");
            return modifyCutOrder(authMember, cutList.get(0).getId(), profitPrice, lossPrice);
        }

        ContractOrderDirection direction;
        if (memberContractPosition.getDirection() == ContractOrderDirection.BUY)
            direction = ContractOrderDirection.SELL;
        else
            direction = ContractOrderDirection.BUY;

        // 检查用户合法性
        Member member = memberService.findOne(authMember.getId());
        if (null == member) return MessageResult.error(500, msService.getMessage("ACCOUNT_NOT_EXIST"));
        // 用户是否被禁止交易
        if (member.getTransactionStatus().equals(BooleanEnum.IS_FALSE)) {
            return MessageResult.error(500, msService.getMessage("CANNOT_TRADE"));
        }
        if (member.getIsLock() == 1) {
            return MessageResult.error(501, msService.getMessage("CANNOT_CLOSE"));
        }

        //log.info("cut volume before scale={}", volume);
        //volume = volume.setScale(memberContractPosition.getDecimalScale(), BigDecimal.ROUND_DOWN);
        //log.info("cut volume after scale={}", volume);
        //if(volume.compareTo(BigDecimal.ZERO) <= 0)
        //    return MessageResult.error(500, msService.getMessage("INCORRECT_NUMBER_CLOSING_POSITIONS"));//小于1张

        //if(volume.compareTo(BigDecimal.ONE) < 0)
        //    return MessageResult.error(500, msService.getMessage("INCORRECT_NUMBER_CLOSING_POSITIONS"));//小于1张
        //if(BigDecimal.valueOf(volume.intValue()).compareTo(volume) != 0)
        //    return MessageResult.error(500, msService.getMessage("INCORRECT_NUMBER_CLOSING_POSITIONS")); // 含有小数


        //if (memberContractPosition.getAvailablePosition().compareTo(volume) < 0) {
        //    return MessageResult.error(500, msService.getMessage("WRONG_AMOUNT_COMMISSION"));
        //}

        BigDecimal currentPrice = contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol()).getNowPrice();

        if (currentPrice.compareTo(BigDecimal.ZERO) <= 0) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_ENGINE_ERROR"));
        }

        // 计划委托中的触发价格需要大于0
        if (profitPrice.compareTo(BigDecimal.ZERO) <= 0 && lossPrice.compareTo(BigDecimal.ZERO) <= 0) {
            return MessageResult.error(500, msService.getMessage("TRIGGER_PRICE1"));
        }
        //log.info("direction={}, profitPrice={}, lossPrice={}, currentPrice={}", direction, profitPrice, lossPrice, currentPrice);
        if (direction == ContractOrderDirection.BUY) {
            if (profitPrice.compareTo(BigDecimal.ZERO) > 0 && profitPrice.compareTo(currentPrice) >= 0)
                return MessageResult.error(500, msService.getMessage("ORDER_CUT_PROFIT_LESS_DEAL"));
            if (lossPrice.compareTo(BigDecimal.ZERO) > 0 && lossPrice.compareTo(currentPrice) <= 0)
                return MessageResult.error(500, msService.getMessage("ORDER_CUT_LOSS_GREATER_DEAL"));
        } else {
            if (profitPrice.compareTo(BigDecimal.ZERO) > 0 && profitPrice.compareTo(currentPrice) <= 0)
                return MessageResult.error(500, msService.getMessage("ORDER_CUT_PROFIT_GREATER_DEAL"));
            if (lossPrice.compareTo(BigDecimal.ZERO) > 0 && lossPrice.compareTo(currentPrice) >= 0)
                return MessageResult.error(500, msService.getMessage("ORDER_CUT_LOSS_LESS_DEAL"));
        }

        // 新建合约委托单
        ContractOrderEntrust orderEntrust = new ContractOrderEntrust();
        orderEntrust.setContractId(contractCoin.getId()); // 合约ID
        orderEntrust.setMemberId(member.getId()); // 用户ID
        orderEntrust.setSymbol(contractCoin.getSymbol()); // 交易对符号
        orderEntrust.setBaseSymbol(contractCoin.getSymbol().split("/")[1]); // 基币/结算币
        orderEntrust.setCoinSymbol(contractCoin.getSymbol().split("/")[0]); // 币种符号
        orderEntrust.setDirection(direction); // 平仓方向：平空/平多
        orderEntrust.setContractOrderEntrustId(GeneratorUtil.getOrderId("CE"));
        orderEntrust.setVolume(BigDecimal.ZERO); // 平仓张数
        orderEntrust.setTradedVolume(BigDecimal.ZERO); // 已交易数量
        orderEntrust.setTradedPrice(BigDecimal.ZERO); // 成交价格
        orderEntrust.setPrincipalUnit("USDT"); // 保证金单位
        orderEntrust.setPrincipalAmount(BigDecimal.ZERO); // 保证金数量
        orderEntrust.setCreateTime(DateUtil.getTimeMillis()); // 开仓时间
        orderEntrust.setType(ContractOrderType.SPOT_LIMIT);
        orderEntrust.setProfitPrice(profitPrice); // 触发价
        orderEntrust.setProfitEntrustPrice(profitEntrustPrice); // 委托价格
        orderEntrust.setLossPrice(lossPrice); // 触发价
        orderEntrust.setLossEntrustPrice(lossEntrustPrice); // 委托价格
        orderEntrust.setEntrustType(ContractOrderEntrustType.CLOSE); // 开仓
        orderEntrust.setTriggeringTime(0L); // 触发时间，暂时无效
        orderEntrust.setShareNumber(contractCoin.getShareNumber());
        orderEntrust.setProfitAndLoss(BigDecimal.ZERO); // 盈亏（仅平仓计算）
        orderEntrust.setPatterns(ContractOrderPattern.CROSSED); // 仓位模式
        orderEntrust.setCloseFee(BigDecimal.ZERO);
        orderEntrust.setPositionType(memberContractPosition.getType());
        orderEntrust.setCurrentPrice(currentPrice);
        orderEntrust.setIsBlast(0); // 不是爆仓单
        orderEntrust.setIsFromSpot(1);
        orderEntrust.setIsCut(1);
        orderEntrust.setPositionId(positionId);
        orderEntrust.setLeverage(memberContractPosition.getLeverage().intValue());
        orderEntrust.setUsdtOpenPrice(memberContractPosition.getOpenPrice());
        orderEntrust.setCloseType(CloseType.CUT);
        orderEntrust.setMoneyType(memberContractPosition.getMoneyType());
        orderEntrust.setDecimalScale(memberContractPosition.getDecimalScale());
        orderEntrust.setOpenType(memberContractPosition.getOpenType());
        if(memberContractPosition.getExperienceRecordId() != null){
            orderEntrust.setExperienceRecordId(memberContractPosition.getExperienceRecordId());
        }
        ContractOrderEntrust retObj = null;
        //int successCount = memberContractPositionService.freezePosition(positionId, volume);
        int successCount = 1;
        if (successCount == 1) {
            // 保存委托单
            orderEntrust.setStatus(ContractOrderEntrustStatus.ENTRUST_ING); // 委托状态：委托中
            retObj = contractOrderEntrustService.save(orderEntrust);
        } else {
            orderEntrust.setStatus(ContractOrderEntrustStatus.ENTRUST_FAILURE); // 委托状态：委托失败
            contractOrderEntrustService.save(orderEntrust);
        }
        if (memberContractPosition != null && memberContractPosition.getExperienceRecordId() != null) {
            experienceGiftRecordService.updateStopPrices(
                    memberContractPosition.getExperienceRecordId(), member.getId(), profitPrice, lossPrice);
        }
        if (retObj != null) {
            // 发送消息至Exchange系统
            //kafkaTemplate.send("swap-order-close", JSON.toJSONString(retObj));
            contractCoinMatchFactory.getContractCoinMatch(retObj.getSymbol()).trade(retObj);

            //kafkaTemplate.send("follow-swap-order-close", JSON.toJSONString(retObj));


            //通知钱包变更
            //JSONObject jsonObj = new JSONObject();
            //jsonObj.put("symbol", contractCoin.getSymbol());
            //jsonObj.put("walletId", memberContractWallet.getId());
            //kafkaTemplate.send("member-wallet-change", JSON.toJSONString(jsonObj));

            log.info(">>>>>>>>>>订单提交完成>>>>>>>>>>");
            // 返回结果
            MessageResult result = MessageResult.success(msService.getMessage("SWAP_SUCCESS"));
            result.setData(retObj);
            return result;
        } else {
            // 返回结果
            MessageResult result = MessageResult.error(msService.getMessage("SWAP_FAILED"));
            result.setData(null);
            return result;
        }
    }

    @ApiOperation(value = "修改止盈止损单", notes = "修改止盈止损单")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "entrustId", value = "订单ID", required = true, paramType = "form", dataType = "int"),
            @ApiImplicitParam(name = "profitPrice", value = "止盈价格", required = false, paramType = "form", dataType = "float"),
            @ApiImplicitParam(name = "lossPrice", value = "止损价格", required = false, paramType = "form", dataType = "float")
    })
    @PostMapping("modify-cut")
    public MessageResult modifyCutOrder(@SessionAttribute(SESSION_MEMBER) AuthMember authMember,
                                        Long entrustId,
                                        @RequestParam(value = "profitPrice", required = false) BigDecimal profitPrice,// 触发价格
                                        @RequestParam(value = "lossPrice", required = false) BigDecimal lossPrice
    ) {
        try {
            if (entrustId == null) {
                log.warn("修改止盈止损失败: entrustId 为空, memberId={}", authMember.getId());
                return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
            }

            if (lossPrice == null) {
                lossPrice = BigDecimal.ZERO;
            }
            if (profitPrice == null) {
                profitPrice = BigDecimal.ZERO;
            }
            if (lossPrice.compareTo(BigDecimal.ZERO) == 0 && profitPrice.compareTo(BigDecimal.ZERO) == 0) {
                log.warn("修改止盈止损失败: 止盈价和止损价不能同时为0, memberId={}, entrustId={}", authMember.getId(), entrustId);
                return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
            }

            ContractOrderEntrust order = contractOrderEntrustService.findOne(entrustId);
            if (order.getMemberId().longValue() != authMember.getId()) {
                return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
            }
            int result = contractCoinMatchFactory.getContractCoinMatch(order.getSymbol()).modifyContractOrderEntrust(order, profitPrice, lossPrice);
            if (result == 1) {
                log.warn("修改止盈止损失败[1]: 多仓止盈价不能高于当前价, memberId={}, entrustId={}, profitPrice={}", authMember.getId(), entrustId, profitPrice);
                return MessageResult.error(500, msService.getMessage("ORDER_CUT_PROFIT_LESS_DEAL"));
            } else if (result == 2) {
                log.warn("修改止盈止损失败[2]: 多仓止损价不能低于当前价, memberId={}, entrustId={}, lossPrice={}", authMember.getId(), entrustId, lossPrice);
                return MessageResult.error(500, msService.getMessage("ORDER_CUT_LOSS_GREATER_DEAL"));
            } else if (result == 3) {
                log.warn("修改止盈止损失败[3]: 空仓止盈价不能低于当前价, memberId={}, entrustId={}, profitPrice={}", authMember.getId(), entrustId, profitPrice);
                return MessageResult.error(500, msService.getMessage("ORDER_CUT_PROFIT_GREATER_DEAL"));
            } else if (result == 4) {
                log.warn("修改止盈止损失败[4]: 空仓止损价不能高于当前价, memberId={}, entrustId={}, lossPrice={}", authMember.getId(), entrustId, lossPrice);
                return MessageResult.error(500, msService.getMessage("ORDER_CUT_LOSS_LESS_DEAL"));
            } else if (result == 5) {
                log.warn("修改止盈止损失败[5]: 多仓止盈价不能低于开仓价, memberId={}, entrustId={}, profitPrice={}", authMember.getId(), entrustId, profitPrice);
                return MessageResult.error(500, msService.getMessage("ORDER_CUT_PROFIT_LESS"));
            } else if (result == 6) {
                log.warn("修改止盈止损失败[6]: 多仓止损价不能高于开仓价, memberId={}, entrustId={}, lossPrice={}", authMember.getId(), entrustId, lossPrice);
                return MessageResult.error(500, msService.getMessage("ORDER_CUT_LOSS_GREATER"));
            } else if (result == 7) {
                log.warn("修改止盈止损失败[7]: 空仓止盈价不能高于开仓价, memberId={}, entrustId={}, profitPrice={}", authMember.getId(), entrustId, profitPrice);
                return MessageResult.error(500, msService.getMessage("ORDER_CUT_PROFIT_GREATER"));
            } else if (result == 8) {
                log.warn("修改止盈止损失败[8]: 空仓止损价不能低于开仓价, memberId={}, entrustId={}, lossPrice={}", authMember.getId(), entrustId, lossPrice);
                return MessageResult.error(500, msService.getMessage("ORDER_CUT_LOSS_LESS"));
            } else if (result == 9) {
                log.warn("修改止盈止损失败[9]: 委托单未找到, memberId={}, entrustId={}", authMember.getId(), entrustId);
                return MessageResult.error(500, msService.getMessage("ORDER_CUT_ENTRUST_FIRST"));
            } else if (result == 10) {
                log.warn("修改止盈止损失败[10]: 非法操作, memberId={}, entrustId={}", authMember.getId(), entrustId);
                return MessageResult.error(500, msService.getMessage("ILLEGAL_OPERATION"));
            }

            // 体验金仓位：修改成功后同步更新止盈止损价格
            if (order.getPositionId() != null) {
                MemberContractPosition position = memberContractPositionService.findOne(order.getPositionId());
                if (position != null && position.getExperienceRecordId() != null) {
                    experienceGiftRecordService.updateStopPrices(
                            position.getExperienceRecordId(), order.getMemberId(), profitPrice, lossPrice);
                }
            }

            return MessageResult.success();
    	/*if(entrustId == null)
        	return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
        //return cutOrder(authMember,positionId,profitPrice,profitEntrustPrice,lossPrice,lossEntrustPrice,volume,1,entrustId);
    	ContractOrderEntrust order = contractOrderEntrustService.findOne(entrustId);
    	if(order.getMemberId().longValue() != authMember.getId()) {
    		return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
    	}
    	int result = contractCoinMatchFactory.getContractCoinMatch(order.getSymbol()).modifyContractOrderEntrust(order, profitPrice, lossPrice);
    	if(result == 1)
    		return MessageResult.error(500, msService.getMessage("ORDER_CUT_PROFIT_LESS_DEAL"));
    	else if(result == 2)
    		return MessageResult.error(500, msService.getMessage("ORDER_CUT_LOSS_GREATER_DEAL"));
    	else if(result == 3)
    		return MessageResult.error(500, msService.getMessage("ORDER_CUT_PROFIT_GREATER_DEAL"));
    	else if(result == 4)
    		return MessageResult.error(500, msService.getMessage("ORDER_CUT_LOSS_LESS_DEAL"));
    	else if(result == 10)
    		return MessageResult.error(500, msService.getMessage("ILLEGAL_OPERATION"));
    	
    	//order.setProfitPrice(profitPrice);
    	//order.setLossPrice(lossPrice);
    	//kafkaTemplate.send("follow-swap-order-modify", JSON.toJSONString(order));
    	
    	return MessageResult.success();*/
        }catch (Exception e){
            log.error("下单异常: {}", e.getMessage(), e);
            return MessageResult.error(msService.getMessage("SYSTEM_ERROR"));
        }
    }

    /**
     * 合约平仓
     * 四种操作类型：买入平空，卖出平多
     *
     * @param authMember
     * @return
     */
    @ApiOperation(value = "平仓订单", notes = "平仓订单")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "positionId", value = "仓位ID", required = true, paramType = "form", dataType = "int"),
            @ApiImplicitParam(name = "type", value = "类型 0：市价 1：限价 2：计划委托", required = true, paramType = "form", dataType = "int"),
            @ApiImplicitParam(name = "triggerPrice", value = "触发价格", required = false, paramType = "form", dataType = "float"),
            @ApiImplicitParam(name = "entrustPrice", value = "委托价格", required = true, paramType = "form", dataType = "float"),
            @ApiImplicitParam(name = "volume", value = "委托数量", required = true, paramType = "form", dataType = "float")
    })
    @PostMapping("close")
    public MessageResult closeOrder(@SessionAttribute(SESSION_MEMBER) AuthMember authMember,
                                    @RequestParam(value = "positionId") Long positionId,// 合约交易对
                                    @RequestParam(value = "type") ContractOrderType type,// 1：市价 2：限价 3：计划委托
                                    @RequestParam(value = "triggerPrice", required = false) BigDecimal triggerPrice,// 触发价格
                                    @RequestParam(value = "entrustPrice") BigDecimal entrustPrice,// 委托价格(计划委托时如为0：市价成交)
                                    @RequestParam(value = "volume") BigDecimal volume// 委托数量（张）
    ) {
        // 输入合法性检查
        if (positionId == null || type == null) {
            return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
        }
        if (volume == null) {
            return MessageResult.error(500, msService.getMessage("NUMBER_OF_ILLEGAL"));
        }
        MemberContractPosition memberContractPosition = memberContractPositionService.findOne(positionId);
        if (memberContractPosition.getMemberId().longValue() != authMember.getId()) {
            return MessageResult.error(500, msService.getMessage("ILLEGAL_OPERATION"));
        }
        if (memberContractPosition.getExperienceRecordId() != null) {
            return MessageResult.error(500, msService.getMessage("EG_USE_DEDICATED_INTERFACE"));
        }
        // 检查合约是否存在
        ContractCoin contractCoin = memberContractPosition.getCoin();
        if (null == contractCoin) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_NOT_EXIST"));
        }
        if (memberContractPosition.getIsLock() != null && memberContractPosition.getIsLock() == 1) {
            return MessageResult.error(501, msService.getMessage("CANNOT_CLOSE"));
        }

        ContractOrderDirection direction;
        if (memberContractPosition.getDirection() == ContractOrderDirection.BUY)
            direction = ContractOrderDirection.SELL;
        else
            direction = ContractOrderDirection.BUY;

        // 限价单及计划委托单需要输入委托价格
        if (type == ContractOrderType.LIMIT_PRICE || type == ContractOrderType.SPOT_LIMIT) {
            if (null == entrustPrice) {
                return MessageResult.error(500, msService.getMessage("INPUT_COMMISSION_PRICE"));
            }
        }
        // 检查用户合法性
        Member member = memberService.findOne(authMember.getId());
        if (null == member) return MessageResult.error(500, msService.getMessage("ACCOUNT_NOT_EXIST"));
        // 用户是否被禁止交易
        if (member.getTransactionStatus().equals(BooleanEnum.IS_FALSE)) {
            return MessageResult.error(500, msService.getMessage("CANNOT_TRADE"));
        }
        if (member.getIsLock() == 1) {
            return MessageResult.error(501, msService.getMessage("CANNOT_CLOSE"));
        }

        volume = volume.setScale(memberContractPosition.getDecimalScale(), BigDecimal.ROUND_DOWN);
        if (volume.compareTo(BigDecimal.ZERO) <= 0)
            return MessageResult.error(500, msService.getMessage("INCORRECT_NUMBER_CLOSING_POSITIONS"));//小于1张
        //if(BigDecimal.valueOf(volume.intValue()).compareTo(volume) != 0)
        //    return MessageResult.error(500, msService.getMessage("INCORRECT_NUMBER_CLOSING_POSITIONS")); // 含有小数


        if (memberContractPosition.getCurrentPosition().compareTo(volume) < 0) {
            return MessageResult.error(500, msService.getMessage("WRONG_AMOUNT_COMMISSION"));
        }


        BigDecimal currentPrice = contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol()).getNowPrice();

        // 计算滑点成交价（市价下单时用此价格）
        BigDecimal dealPrice = currentPrice;
        if (direction == ContractOrderDirection.BUY) { // 买入平空，滑点计算，更低价格
            if (contractCoin.getSpreadType() == 1) { // 滑点类型：百分比
                dealPrice = currentPrice.add(currentPrice.multiply(contractCoin.getSpread())); // 已当前价成交（或滑点价成交）
            } else { // 滑点类型：固定额
                dealPrice = currentPrice.add(contractCoin.getSpread());
            }
        } else { // 卖出，滑点计算，做空，更低价格成交
            if (contractCoin.getSpreadType() == 1) { // 滑点类型：百分比
                dealPrice = currentPrice.subtract(currentPrice.multiply(contractCoin.getSpread())); // 已当前价成交（或滑点价成交）
            } else { // 滑点类型：固定额
                dealPrice = currentPrice.subtract(contractCoin.getSpread());
            }
        }
        //
        if (dealPrice.compareTo(BigDecimal.ZERO) <= 0) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_ENGINE_ERROR"));
        }
        // 委托价格是否太高或太低(限价单需要在2%的价格范围内下单)
        /*if(type == ContractOrderType.LIMIT_PRICE) {
            if(entrustPrice.compareTo(currentPrice.multiply(BigDecimal.ONE.add(BigDecimal.valueOf(0.02)))) > 0
                    || entrustPrice.compareTo(currentPrice.multiply(BigDecimal.ONE.subtract(BigDecimal.valueOf(0.02)))) < 0) {
                return MessageResult.error(500, msService.getMessage("ORDER_PRICE_OVERRUN"));
            }
        }*/
        ContractOrderEntrust orderEntrust = new ContractOrderEntrust();

        BigDecimal tradedPrice = BigDecimal.ZERO;
        if (type == ContractOrderType.MARKET_PRICE) {
            tradedPrice = dealPrice;
        } else if (type == ContractOrderType.LIMIT_PRICE) {
            if (direction == ContractOrderDirection.BUY) {
                if (entrustPrice.compareTo(dealPrice) > 0) {
                    tradedPrice = dealPrice;
                    orderEntrust.setIsLimitPrice(1);
                }// else
                //	tradedPrice = entrustPrice;
            } else {
                if (entrustPrice.compareTo(dealPrice) < 0) {
                    tradedPrice = dealPrice;
                    orderEntrust.setIsLimitPrice(1);
                }// else
                //	tradedPrice = entrustPrice;
            }
        } else {
            //tradedPrice = entrustPrice;
            return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
        }

        // 新建合约委托单
        orderEntrust.setContractId(contractCoin.getId()); // 合约ID
        orderEntrust.setMemberId(member.getId()); // 用户ID
        orderEntrust.setSymbol(contractCoin.getSymbol()); // 交易对符号
        orderEntrust.setBaseSymbol(contractCoin.getSymbol().split("/")[1]); // 基币/结算币
        orderEntrust.setCoinSymbol(contractCoin.getSymbol().split("/")[0]); // 币种符号
        orderEntrust.setDirection(direction); // 平仓方向：平空/平多
        orderEntrust.setContractOrderEntrustId(GeneratorUtil.getOrderId("CE"));
        orderEntrust.setVolume(volume); // 平仓张数
        orderEntrust.setTradedVolume(BigDecimal.ZERO); // 已交易数量
        orderEntrust.setTradedPrice(tradedPrice); // 成交价格
        orderEntrust.setPrincipalUnit("USDT"); // 保证金单位
        orderEntrust.setPrincipalAmount(BigDecimal.ZERO); // 保证金数量
        orderEntrust.setCreateTime(DateUtil.getTimeMillis()); // 开仓时间
        orderEntrust.setType(type);
        orderEntrust.setTriggerPrice(triggerPrice); // 触发价
        orderEntrust.setEntrustPrice(entrustPrice); // 委托价格
        orderEntrust.setEntrustType(ContractOrderEntrustType.CLOSE); // 开仓
        orderEntrust.setTriggeringTime(0L); // 触发时间，暂时无效
        orderEntrust.setShareNumber(contractCoin.getShareNumber());
        orderEntrust.setProfitAndLoss(BigDecimal.ZERO); // 盈亏（仅平仓计算）
        orderEntrust.setPatterns(ContractOrderPattern.CROSSED); // 仓位模式
        orderEntrust.setCloseFee(BigDecimal.ZERO);
        orderEntrust.setCurrentPrice(currentPrice);
        orderEntrust.setIsBlast(0); // 不是爆仓单
        orderEntrust.setLeverage(memberContractPosition.getLeverage().intValue());
        orderEntrust.setCloseType(CloseType.CLOSE);
        orderEntrust.setPositionId(positionId);
        orderEntrust.setPositionType(memberContractPosition.getType());
        orderEntrust.setMoneyType(memberContractPosition.getMoneyType());
        orderEntrust.setDecimalScale(memberContractPosition.getDecimalScale());
        orderEntrust.setOpenType(memberContractPosition.getOpenType());
        if (type == ContractOrderType.SPOT_LIMIT) { // 是否是计划委托单
            orderEntrust.setIsFromSpot(1);
        } else {
            orderEntrust.setIsFromSpot(0);
        }

        if (memberContractPosition.getAvailablePosition().compareTo(volume) < 0) {
            contractCoinMatchFactory.getContractCoinMatch(orderEntrust.getSymbol()).cancelCloseOrdersByPositionId(positionId, volume.subtract(memberContractPosition.getAvailablePosition()));
        }

        //kafkaTemplate.send("swap-order-close", JSON.toJSONString(orderEntrust));
        //MessageResult result = MessageResult.success(msService.getMessage("SWAP_SUCCESS"));
        //result.setData(orderEntrust);
        //return result;

        //if(!redisLock.lockTradingAccount(member.getId()))
        //	return MessageResult.error(msService.getMessage("SWAP_FAILED"));

        try {
            ContractOrderEntrust retObj = null;
            if (memberContractPosition.getAvailablePosition().compareTo(volume) < 0) {
                contractCoinMatchFactory.getContractCoinMatch(orderEntrust.getSymbol()).cancelCloseOrdersByPositionId(positionId, volume.subtract(memberContractPosition.getAvailablePosition()));
            }

            kafkaTemplate.send("swap-order-close", JSON.toJSONString(retObj));
            int successCount = memberContractPositionService.freezePosition(memberContractPosition.getId(), volume);
            if (successCount == 1) {
                // 保存委托单
                orderEntrust.setStatus(ContractOrderEntrustStatus.ENTRUST_ING); // 委托状态：委托中
                retObj = contractOrderEntrustService.save(orderEntrust);
            } else {
                orderEntrust.setStatus(ContractOrderEntrustStatus.ENTRUST_FAILURE); // 委托状态：委托中
                contractOrderEntrustService.save(orderEntrust);
            }

            if (retObj != null) {
                // 发送消息至Exchange系统
                //kafkaTemplate.send("swap-order-close", JSON.toJSONString(retObj));
                contractCoinMatchFactory.getContractCoinMatch(retObj.getSymbol()).trade(retObj);

                //kafkaTemplate.send("follow-swap-order-close", JSON.toJSONString(retObj));

                //通知钱包变更
                //JSONObject jsonObj = new JSONObject();
                //jsonObj.put("symbol", contractCoin.getSymbol());
                //jsonObj.put("walletId", memberContractWallet.getId());
                //kafkaTemplate.send("member-wallet-change", JSON.toJSONString(jsonObj));

                log.info(">>>>>>>>>>订单提交完成>>>>>>>>>>");
                // 返回结果
                MessageResult result = MessageResult.success(msService.getMessage("SWAP_SUCCESS"));
                result.setData(retObj);
                return result;
            } else {
                // 返回结果
                MessageResult result = MessageResult.error(msService.getMessage("SWAP_FAILED"));
                result.setData(null);
                return result;
            }
        } catch (Exception e) {
            return MessageResult.error(msService.getMessage("SWAP_FAILED"));
        } finally {
            //redisLock.unlockTradingAccount(member.getId());
        }
    }

    @ApiOperation(value = "反向开仓", notes = "先平仓后再反向开同数量的仓")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "positionId", value = "仓位ID", required = true, paramType = "form", dataType = "long")
    })
    @PostMapping("close-and-open")
    public MessageResult closeAndOpenOrder(@ApiParam(hidden = true) @SessionAttribute(SESSION_MEMBER) AuthMember authMember, Long positionId
    ) throws ParseException {
        Member member = memberService.findOne(authMember.getId());
        if (null == member) return MessageResult.error(500, msService.getMessage("ACCOUNT_NOT_EXIST"));
        // 用户是否被禁止交易
        if (member.getTransactionStatus().equals(BooleanEnum.IS_FALSE)) {
            return MessageResult.error(500, msService.getMessage("CANNOT_TRADE"));
        }
        if (member.getIsLock() == 1) {
            return MessageResult.error(501, msService.getMessage("CANNOT_CLOSE"));
        }

        if (positionId == null)
            return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
        log.info("close-and-open positionId={}", positionId);
        MemberContractPosition position = memberContractPositionService.findOne(positionId);
        if (position == null)
            return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
        if (position.getMemberId().longValue() != authMember.getId()) {
            return MessageResult.error(500, msService.getMessage("ILLEGAL_OPERATION"));
        }
        if (position.getExperienceRecordId() != null) {
            return MessageResult.error(500, msService.getMessage("EG_USE_DEDICATED_INTERFACE"));
        }
        if (position.getIsLock() != null && position.getIsLock() == 1) {
            return MessageResult.error(501, msService.getMessage("CANNOT_CLOSE"));
        }
        ContractCoin contractCoin = position.getCoin();
        ContractOrderDirection direction;
        log.info("close-and-open position={}", JSON.toJSONString(position));
        BigDecimal volume = position.getCurrentPosition();
        //log.info("close-and-open volume={}", volume);
        if (position.getDirection() == ContractOrderDirection.BUY) {
            direction = ContractOrderDirection.SELL;
        } else {
            direction = ContractOrderDirection.BUY;
        }
        MessageResult messageResult = closeAll(authMember, positionId, true);
        if (messageResult.getCode() != 0)
            return messageResult;

        // 反向开仓沿用原仓位的仓位类型（全仓/分仓），带单员在 openOrder 内部仍会强制全仓
        return openOrder(authMember, contractCoin.getId(), direction, ContractOrderType.MARKET_PRICE, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, volume, null, 1, position.getType());
    }

    /**
     * 一键平仓（市价全平）
     *
     * @param authMember
     * @return
     * @throws ParseException
     */
    @ApiOperation(value = "市价平仓", notes = "市价平仓")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "positionId", value = "仓位ID", required = true, paramType = "form", dataType = "long")
    })
    @PostMapping("close-all")
    public MessageResult closeAll(@SessionAttribute(SESSION_MEMBER) AuthMember authMember, Long positionId, boolean isReversed) throws ParseException {
        Member member = memberService.findOne(authMember.getId());
        if (null == member) {
            return MessageResult.error(msService.getMessage("ACCOUNT_NOT_EXIST"));
        }
        // 用户是否被禁止交易
        if (member.getTransactionStatus().equals(BooleanEnum.IS_FALSE)) {
            return MessageResult.error(500, msService.getMessage("CANNOT_TRADE"));
        }
        if (member.getIsLock() == 1) {
            return MessageResult.error(501, msService.getMessage("CANNOT_CLOSE"));
        }

        MemberContractPosition position = memberContractPositionService.findOne(positionId);
        if (null == position) {
            return MessageResult.error(msService.getMessage("CONTRACT_ACCOUNT_NOT_EXIST"));
        }
        if (position.getMemberId().longValue() != member.getId().longValue()) {
            return MessageResult.error(501, msService.getMessage("CANNOT_CLOSE"));
        }
        if (position.getIsLock() != null && position.getIsLock() == 1) {
            return MessageResult.error(501, msService.getMessage("CANNOT_CLOSE"));
        }

        if (position.getStatus() == ContractPositionStatus.DONE) {
            return MessageResult.error(501, msService.getMessage("CANNOT_CLOSE"));
        }

        ContractCoin contractCoin = position.getCoin();
        if (null == contractCoin) {
            return MessageResult.error(msService.getMessage("CONTRACT_NOT_EXIST"));
        }

        //cancelOrdersBeforeAllClose(member.getId(), contractCoinId, contractCoin.getSymbol(), type==0?ContractOrderDirection.SELL:ContractOrderDirection.BUY);


        BigDecimal currentPrice = contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol()).getNowPrice();
        if (currentPrice.compareTo(BigDecimal.ZERO) <= 0) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_ENGINE_ERROR"));
        }

        // 体验金仓位不走引擎，直接平仓
        if (position.getExperienceRecordId() != null) {
            BigDecimal pnl;
            if (position.getMoneyType() == ContractMoneyType.COIN) {
                pnl = currentPrice.subtract(position.getOpenPrice()).multiply(position.getCurrentPosition());
            } else {
                pnl = currentPrice.subtract(position.getOpenPrice())
                        .multiply(position.getCurrentPosition())
                        .multiply(position.getShareNumber())
                        .divide(position.getOpenPrice(), 8, RoundingMode.HALF_UP);
            }
            if (position.getDirection() == ContractOrderDirection.SELL) {
                pnl = BigDecimal.ZERO.subtract(pnl);
            }
            if (!redisLock.lockExperienceGiftRecord(position.getExperienceRecordId())) {
                return MessageResult.error(501, msService.getMessage("REQUEST_REPEAT"));
            }
            // 平仓前查出待处理切单，平仓后从引擎内存 Map 移除
            List<ContractOrderEntrust> expCutOrders = contractOrderEntrustService.queryEntrustCutOrdersByPositionId(positionId);
            try {
                memberContractPositionService.closeExperienceGiftPosition(
                        positionId, currentPrice, CloseType.MARKET_CLOSE, pnl,
                        position.getExperienceRecordId(), ExperienceGiftCloseReason.USER_MANUAL);
            } catch (Exception e) {
                log.error("体验金一键平仓异常 positionId={}: {}", positionId, e.getMessage(), e);
            } finally {
                redisLock.unlockExperienceGiftRecord(position.getExperienceRecordId());
            }
            // 从引擎内存 Map 中移除已处理的切单
            ContractCoinMatch expCoinMatch = contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol());
            if (expCoinMatch != null) {
                for (ContractOrderEntrust cutOrder : expCutOrders) {
                    expCoinMatch.removeFromMap(cutOrder);
                }
            }
            kafkaTemplate.send("trading-account-change", member.getId() + "");
            tradingAccountMonitor.updateAccount(member.getId());
            return MessageResult.success();
        }

        CloseType closeType = CloseType.MARKET_CLOSE;
        if (isReversed)
            closeType = CloseType.REVERSE_OPEN;
        if (position.getFollowPositionId() != null)
            closeType = CloseType.FOLLOW_SELF_CLOSE;
        position.setCloseType(closeType);

        if (position.getFollowPositionId() != null && !redisLock.lockFollowAccount(position.getMemberId())) {
            return MessageResult.error(501, msService.getMessage("CANNOT_CLOSE"));
        }

        try {
            contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol()).dealAllCloseOrder(position);
            //kafkaTemplate.send("swap-order-close-all", JSON.toJSONString(position));
        } catch (Exception e) {
            log.info("closeAll error", e);
        } finally {
            if (position.getFollowPositionId() != null)
                redisLock.unlockFollowAccount(position.getMemberId());
        }

        return MessageResult.success();
    }

    /**
     * 一键平仓（市价全平）
     *
     * @param authMember
     * @throws ParseException
     */
    @ApiOperation(value = "一键全平", notes = "一键全平")
    @PostMapping("close-all-coins")
    public MessageResult closeAllCoins(@SessionAttribute(SESSION_MEMBER) AuthMember authMember) throws ParseException {

        Member member = memberService.findOne(authMember.getId());
        if (null == member) {
            return MessageResult.error(msService.getMessage("ACCOUNT_NOT_EXIST"));
        }
        // 用户是否被禁止交易
        if (member.getTransactionStatus().equals(BooleanEnum.IS_FALSE)) {
            return MessageResult.error(500, msService.getMessage("CANNOT_TRADE"));
        }
        if (member.getIsLock() == 1) {
            return MessageResult.error(501, msService.getMessage("CANNOT_CLOSE"));
        }

        List<MemberContractPosition> list = memberContractPositionService.findCurrentPositionsByMemberId(authMember.getId());
        log.info("list.size()=" + list.size());
        for (MemberContractPosition position : list) {
            if (null == position) {
                return MessageResult.error(msService.getMessage("CONTRACT_ACCOUNT_NOT_EXIST"));
            }

            if (position.getIsLock() != null && position.getIsLock() == 1) {
                continue;
            }

            ContractCoin contractCoin = position.getCoin();

            if (contractCoin == null || contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol()) == null) {
                log.info("contractCoin={}", contractCoin);
                continue;
            }
            BigDecimal currentPrice = contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol()).getNowPrice();
            if (currentPrice.compareTo(BigDecimal.ZERO) <= 0) {
                continue;
            }

            // 体验金仓位不走引擎，直接平仓
            if (position.getExperienceRecordId() != null) {
                BigDecimal pnl;
                if (position.getMoneyType() == ContractMoneyType.COIN) {
                    pnl = currentPrice.subtract(position.getOpenPrice()).multiply(position.getCurrentPosition());
                } else {
                    pnl = currentPrice.subtract(position.getOpenPrice())
                            .multiply(position.getCurrentPosition())
                            .multiply(position.getShareNumber())
                            .divide(position.getOpenPrice(), 8, RoundingMode.HALF_UP);
                }
                if (position.getDirection() == ContractOrderDirection.SELL) {
                    pnl = BigDecimal.ZERO.subtract(pnl);
                }
                if (redisLock.lockExperienceGiftRecord(position.getExperienceRecordId())) {
                    // 平仓前查出待处理切单，平仓后从引擎内存 Map 移除
                    List<ContractOrderEntrust> expCutOrders = contractOrderEntrustService.queryEntrustCutOrdersByPositionId(position.getId());
                    try {
                        memberContractPositionService.closeExperienceGiftPosition(
                                position.getId(), currentPrice, CloseType.ONE_KEY_CLOSE, pnl,
                                position.getExperienceRecordId(), ExperienceGiftCloseReason.USER_MANUAL);
                    } catch (Exception e) {
                        log.error("体验金一键全平异常 positionId={}: {}", position.getId(), e.getMessage(), e);
                    } finally {
                        redisLock.unlockExperienceGiftRecord(position.getExperienceRecordId());
                        kafkaTemplate.send("trading-account-change", member.getId() + "");
                        tradingAccountMonitor.updateAccount(member.getId());
                    }
                    // 从引擎内存 Map 中移除已处理的切单
                    ContractCoinMatch expCoinMatch = contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol());
                    if (expCoinMatch != null) {
                        for (ContractOrderEntrust cutOrder : expCutOrders) {
                            expCoinMatch.removeFromMap(cutOrder);
                        }
                    }
                }
                continue;
            }

            position.setCloseType(CloseType.ONE_KEY_CLOSE);
            contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol()).dealAllCloseOrder(position);
            //kafkaTemplate.send("swap-order-close-all", JSON.toJSONString(position));
        }

        //JSONObject jsonObject = new JSONObject();
        //jsonObject.put("memberId", authMember.getId());
        //kafkaTemplate.send("swap-order-close-all-coins", JSON.toJSONString(jsonObject));
        MessageResult result = MessageResult.success(msService.getMessage("SWAP_SUCCESS"));
        //result.setData(retList);
        return result;

        //return MessageResult.error(msService.getMessage("UNABLE_CLOSE_POSITION"));
    }

    @ApiOperation(value = "修改订单", notes = "修改订单")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "entrustId", value = "订单ID", required = true, dataType = "long"),
            @ApiImplicitParam(name = "lossPrice", value = "止损价格", required = false, dataType = "float"),
            @ApiImplicitParam(name = "profitPrice", value = "止盈价格", required = false, dataType = "float")
    })
    @PostMapping("modify")
    public MessageResult modifyOrder(@SessionAttribute(SESSION_MEMBER) AuthMember authMember,
                                     Long entrustId,
                                     @RequestParam(value = "lossPrice", required = false) BigDecimal lossPrice,
                                     @RequestParam(value = "profitPrice", required = false) BigDecimal profitPrice
    ) {
        if (entrustId == null)
            return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));

        if (lossPrice == null) {
            lossPrice = BigDecimal.ZERO;
        }
        if (profitPrice == null) {
            profitPrice = BigDecimal.ZERO;
        }
        if (lossPrice.compareTo(BigDecimal.ZERO) == 0 && profitPrice.compareTo(BigDecimal.ZERO) == 0) {
            return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
        }

        //return openOrder(authMember, contractCoinId, direction, type, triggerPrice, entrustPrice, lossPrice, profitPrice, leverage, volume, null, 0, 1, entrustId);
        ContractOrderEntrust order = contractOrderEntrustService.findOne(entrustId);
        if (order.getMemberId().longValue() != authMember.getId()) {
            return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
        }
        int result = contractCoinMatchFactory.getContractCoinMatch(order.getSymbol()).modifyContractOrderEntrust(order, profitPrice, lossPrice);
        if (result == 1)
            return MessageResult.error(500, msService.getMessage("ORDER_CUT_PROFIT_LESS_DEAL"));
        else if (result == 2)
            return MessageResult.error(500, msService.getMessage("ORDER_CUT_LOSS_GREATER_DEAL"));
        else if (result == 3)
            return MessageResult.error(500, msService.getMessage("ORDER_CUT_PROFIT_GREATER_DEAL"));
        else if (result == 4)
            return MessageResult.error(500, msService.getMessage("ORDER_CUT_LOSS_LESS_DEAL"));
        else if (result == 5)
            return MessageResult.error(500, msService.getMessage("ORDER_CUT_PROFIT_LESS"));
        else if (result == 6)
            return MessageResult.error(500, msService.getMessage("ORDER_CUT_LOSS_GREATER"));
        else if (result == 7)
            return MessageResult.error(500, msService.getMessage("ORDER_CUT_PROFIT_GREATER"));
        else if (result == 8)
            return MessageResult.error(500, msService.getMessage("ORDER_CUT_LOSS_LESS"));
        else if (result == 9)
            return MessageResult.error(500, msService.getMessage("ORDER_CUT_ENTRUST_FIRST"));
        else if (result == 10)
            return MessageResult.error(500, msService.getMessage("ILLEGAL_OPERATION"));

        //order.setProfitPrice(profitPrice);
        //order.setLossPrice(lossPrice);
        //kafkaTemplate.send("follow-swap-order-modify", JSON.toJSONString(order));

        return MessageResult.success();
    }

    /**
     * 合约撤销单
     *
     * @param authMember
     * @param entrustId
     * @return
     */
    @ApiOperation(value = "撤销订单", notes = "撤销订单")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "entrustId", value = "订单ID", required = true, paramType = "form", dataType = "int")
    })
    @PostMapping("cancel")
    public MessageResult cancelOrder(@SessionAttribute(SESSION_MEMBER) AuthMember authMember,
                                     Long entrustId
    ) {
        if (entrustId == null)
            return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
        ContractOrderEntrust entrustOrder = contractOrderEntrustService.findOne(entrustId);
        if (entrustOrder == null) {
            return MessageResult.error(500, msService.getMessage("ORDER_NO_EXIST"));
        }
        if (entrustOrder.getMemberId() != authMember.getId()) {
            return MessageResult.error(500, msService.getMessage("ILLEGAL_OPERATION"));
        }
        if (entrustOrder.getStatus() != ContractOrderEntrustStatus.ENTRUST_ING) {
            return MessageResult.error(500, msService.getMessage("DELEGATE_STATUS_ERROR"));
        }

        // 体验金止盈止损单不在撮合引擎队列中，需单独处理
        if (entrustOrder.getExperienceRecordId() != null
                && entrustOrder.getType() == ContractOrderType.SPOT_LIMIT
                && entrustOrder.getEntrustType() == ContractOrderEntrustType.CLOSE) {
            // DB 变更在 Service 事务内完成（委托单撤销 + 体验金止盈止损价格清零）
            memberContractPositionService.cancelExperienceGiftCutOrder(entrustOrder, authMember.getId());
            // 事务提交后：从引擎内存 Map 移除
            ContractCoinMatch coinMatch = contractCoinMatchFactory.getContractCoinMatch(entrustOrder.getSymbol());
            if (coinMatch != null) {
                coinMatch.removeFromMap(entrustOrder);
            }
            // 刷新账户缓存
            tradingAccountMonitor.updateAccount(authMember.getId());
            MessageResult result = MessageResult.success(msService.getMessage("CANCELLATION_SUCCESSFUL"));
            result.setData(entrustOrder);
            return result;
        }

        // 发送消息至Exchange系统
        //kafkaTemplate.send("swap-order-cancel", JSON.toJSONString(entrustOrder));
        BigDecimal releasedPosition = contractCoinMatchFactory.getContractCoinMatch(entrustOrder.getSymbol()).cancelContractOrderEntrust(entrustOrder, false);

        if (entrustOrder.getIsCut() == 1 || releasedPosition.compareTo(BigDecimal.ZERO) > 0) {
            //kafkaTemplate.send("follow-swap-order-cancel", JSON.toJSONString(entrustOrder));

            log.info(">>>>>>>>>>订单提交完成>>>>>>>>>>");
            // 返回结果
            MessageResult result = MessageResult.success(msService.getMessage("CANCELLATION_SUCCESSFUL"));
            result.setData(entrustOrder);
            return result;
        } else {
            return MessageResult.error(500, msService.getMessage("ORDER_NO_EXIST"));
        }
    }

    @ApiOperation(value = "批量撤销订单", notes = "批量撤销订单")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "type", value = "委托类型 0市价 1限价 2计划委托(平仓情况下就是止盈止损) [0,1] 传数组，空撤销所有", required = false, dataType = "string"),
            @ApiImplicitParam(name = "entrustType", value = "0 开仓 1 平仓 [0,1] 传数组，空撤销所有", required = false, dataType = "string"),
            @ApiImplicitParam(name = "contractCoinId", value = "合约对ID", required = false, dataType = "int")
    })
    @PostMapping("cancel-by-type")
    public MessageResult cancelAllOrderByType(@SessionAttribute(SESSION_MEMBER) AuthMember authMember,
                                              String type, String entrustType, Long contractCoinId
    ) {
        List<ContractOrderEntrust> orderList = contractOrderEntrustService.queryEntrustingOrdersByMemeberId(authMember.getId());
        for (ContractOrderEntrust order : orderList) {
            if (contractCoinId != null && order.getContractId().longValue() != contractCoinId.longValue()) {
                continue;
            }
            if (type != null) {
                List<Integer> typeList = JSON.parseArray(type, Integer.class);
                if (!typeList.contains(order.getType().ordinal())) {
                    continue;
                }
            }
            if (entrustType != null) {
                List<Integer> entrustTypeList = JSON.parseArray(entrustType, Integer.class);
                if (!entrustTypeList.contains(order.getEntrustType().ordinal())) {
                    continue;
                }
            }
            // 体验金止盈止损单不在撮合引擎队列中，需单独处理
            if (order.getExperienceRecordId() != null
                    && order.getType() == ContractOrderType.SPOT_LIMIT
                    && order.getEntrustType() == ContractOrderEntrustType.CLOSE) {
                // DB 变更在 Service 事务内完成
                memberContractPositionService.cancelExperienceGiftCutOrder(order, authMember.getId());
                // 事务提交后：从引擎内存 Map 移除
                ContractCoinMatch coinMatch = contractCoinMatchFactory.getContractCoinMatch(order.getSymbol());
                if (coinMatch != null) {
                    coinMatch.removeFromMap(order);
                }
            } else {
                contractCoinMatchFactory.getContractCoinMatch(order.getSymbol()).cancelContractOrderEntrust(order, false);
            }
        }
        // 刷新账户缓存
        tradingAccountMonitor.updateAccount(authMember.getId());
        MessageResult result = MessageResult.success(msService.getMessage("CANCELLATION_SUCCESSFUL"));
        return result;
    }

    @ApiOperation(value = "批量撤销订单(根据ID数组)", notes = "批量撤销订单(根据ID数组)")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "entrustIds", value = "订单ID数组 [0,1] 传数组字符串", required = false, dataType = "string")
    })
    @PostMapping("cancel-by-ids")
    public MessageResult cancelAllOrderByType(@SessionAttribute(SESSION_MEMBER) AuthMember authMember,
                                              String entrustIds
    ) {
        List<Long> entrustIdList = JSON.parseArray(entrustIds, Long.class);
        for (Long entrustId : entrustIdList) {
            ContractOrderEntrust entrustOrder = contractOrderEntrustService.findOne(entrustId);

            if (entrustOrder.getMemberId() != authMember.getId()) {
                return MessageResult.error(500, msService.getMessage("ILLEGAL_OPERATION"));
            }
            if (entrustOrder.getStatus() != ContractOrderEntrustStatus.ENTRUST_ING) {
                return MessageResult.error(500, msService.getMessage("DELEGATE_STATUS_ERROR"));
            }

            // 体验金止盈止损单不在撮合引擎队列中，需单独处理
            if (entrustOrder.getExperienceRecordId() != null
                    && entrustOrder.getType() == ContractOrderType.SPOT_LIMIT
                    && entrustOrder.getEntrustType() == ContractOrderEntrustType.CLOSE) {
                // DB 变更在 Service 事务内完成
                memberContractPositionService.cancelExperienceGiftCutOrder(entrustOrder, authMember.getId());
                // 事务提交后：从引擎内存 Map 移除
                ContractCoinMatch coinMatch = contractCoinMatchFactory.getContractCoinMatch(entrustOrder.getSymbol());
                if (coinMatch != null) {
                    coinMatch.removeFromMap(entrustOrder);
                }
            } else {
                // 发送消息至Exchange系统
                //kafkaTemplate.send("swap-order-cancel", JSON.toJSONString(entrustOrder));
                contractCoinMatchFactory.getContractCoinMatch(entrustOrder.getSymbol()).cancelContractOrderEntrust(entrustOrder, false);
            }
        }
        // 刷新账户缓存
        tradingAccountMonitor.updateAccount(authMember.getId());
        MessageResult result = MessageResult.success(msService.getMessage("CANCELLATION_SUCCESSFUL"));
        return result;
    }

    /**
     * 合约撤销单(撤销所有委托，限价+计划+市价)
     *
     * @param authMember
     * @param contractCoinId
     * @return
     */
    //@PostMapping("cancel-all")
    public MessageResult cancelAllOrder(@SessionAttribute(SESSION_MEMBER) AuthMember authMember,
                                        Long contractCoinId
    ) {
        List<ContractOrderEntrust> orderList = contractOrderEntrustService.findAllByMemberIdAndContractId(authMember.getId(), contractCoinId);
        if (orderList != null && orderList.size() > 0) {
            for (int i = 0; i < orderList.size(); i++) {
                ContractOrderEntrust entrustOrder = orderList.get(i);
                if (entrustOrder.getMemberId() != authMember.getId()) {
                    continue;
                }
                if (entrustOrder.getStatus() != ContractOrderEntrustStatus.ENTRUST_ING) {
                    continue;
                }

                // 发送消息至Exchange系统
                kafkaTemplate.send("swap-order-cancel", JSON.toJSONString(entrustOrder));
            }
        }
        log.info(">>>>>>>>>>撤销所有委托成功>>>>>>>>>>");
        // 返回结果
        MessageResult result = MessageResult.success(msService.getMessage("CANCELLATION_SUCCESSFUL"));
        return result;
    }

    /**
     * 获取当前持仓列表
     *
     * @param authMember
     * @return
     */
    //@RequestMapping("position-list")
    public MessageResult positionList(@SessionAttribute(SESSION_MEMBER) AuthMember authMember) {
        Member member = memberService.findOne(authMember.getId());
        if (null == member) {
            return MessageResult.error(500, msService.getMessage("ACCOUNT_NOT_EXIST"));
        }
        List<MemberContractWallet> list = memberContractWalletService.findAllByMemberId(authMember.getId());
        if (null == list) {
            return MessageResult.error(500, msService.getMessage("WALLET_NOT_IN"));
        }

        // 计算账户权益
        for (MemberContractWallet wallet : list) {
            BigDecimal currentPrice = contractCoinMatchFactory.getContractCoinMatch(wallet.getContractCoin().getSymbol()).getNowPrice();
            // 计算金本位权益（多仓 + 空仓）
            BigDecimal usdtTotalProfitAndLoss = BigDecimal.ZERO;
            // 多仓计算方法：（当前价格 / 开仓均价 - 1）* （可用仓位 + 冻结仓位） * 合约面值
            if (wallet.getUsdtBuyPrice().compareTo(BigDecimal.ZERO) > 0) {
                usdtTotalProfitAndLoss = usdtTotalProfitAndLoss.add(currentPrice.divide(wallet.getUsdtBuyPrice(), 4, BigDecimal.ROUND_DOWN).subtract(BigDecimal.ONE).multiply(wallet.getUsdtBuyPosition().add(wallet.getUsdtFrozenBuyPosition())).multiply(wallet.getUsdtShareNumber()));
            }

            // 空仓计算方法：（1 - 当前价格 / 开仓均价）* （可用仓位 + 冻结仓位） * 合约面值
            if (wallet.getUsdtSellPrice().compareTo(BigDecimal.ZERO) > 0) {
                usdtTotalProfitAndLoss = usdtTotalProfitAndLoss.add(BigDecimal.ONE.subtract(currentPrice.divide(wallet.getUsdtSellPrice(), 4, BigDecimal.ROUND_DOWN)).multiply(wallet.getUsdtSellPosition().add(wallet.getUsdtFrozenSellPosition())).multiply(wallet.getUsdtShareNumber()));
            }

            wallet.setUsdtTotalProfitAndLoss(usdtTotalProfitAndLoss);
        }

        MessageResult result = MessageResult.success("success");
        result.setData(list);
        return result;
    }

    /**
     * 获取当前持仓详情
     *
     * @param authMember
     * @return
     */
    //@RequestMapping("position-detail")
    public MessageResult positionDetail(@SessionAttribute(SESSION_MEMBER) AuthMember authMember, Long contractCoinId) {
        Member member = memberService.findOne(authMember.getId());
        if (null == member) {
            return MessageResult.error(500, msService.getMessage("ACCOUNT_NOT_EXIST"));
        }
        ContractCoin coin = contractCoinService.findOne(contractCoinId);
        if (null == coin) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_NOT_EXIST"));
        }
        MemberContractWallet wallet = memberContractWalletService.findByMemberIdAndContractCoin(authMember.getId(), coin);
        if (null == wallet) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_ACCOUNT_NOT_EXIST"));
        }

        // 计算账户权益
        BigDecimal currentPrice = contractCoinMatchFactory.getContractCoinMatch(coin.getSymbol()).getNowPrice();

        // 计算金本位权益（多仓 + 空仓）
        BigDecimal usdtTotalProfitAndLoss = BigDecimal.ZERO;
        // 多仓计算方法：（当前价格 / 开仓均价 - 1）* （可用仓位 + 冻结仓位） * 合约面值
        if (wallet.getUsdtBuyPrice().compareTo(BigDecimal.ZERO) > 0) {
            usdtTotalProfitAndLoss = usdtTotalProfitAndLoss.add(currentPrice.divide(wallet.getUsdtBuyPrice(), 4, BigDecimal.ROUND_DOWN).subtract(BigDecimal.ONE).multiply(wallet.getUsdtBuyPosition().add(wallet.getUsdtFrozenBuyPosition())).multiply(wallet.getUsdtShareNumber()));
        }
        // 空仓计算方法：（1 - 当前价格 / 开仓均价）* （可用仓位 + 冻结仓位） * 合约面值
        if (wallet.getUsdtSellPrice().compareTo(BigDecimal.ZERO) > 0) {
            usdtTotalProfitAndLoss = usdtTotalProfitAndLoss.add(BigDecimal.ONE.subtract(currentPrice.divide(wallet.getUsdtSellPrice(), 4, BigDecimal.ROUND_DOWN)).multiply(wallet.getUsdtSellPosition().add(wallet.getUsdtFrozenSellPosition())).multiply(wallet.getUsdtShareNumber()));
        }

        wallet.setUsdtTotalProfitAndLoss(usdtTotalProfitAndLoss);

        MessageResult result = MessageResult.success("success");
        result.setData(wallet);
        return result;
    }

    /**
     * 获取当前委托列表
     *
     * @param authMember
     * @return
     */
    @ApiOperation(value = "获取当前委托列表", notes = "获取当前委托列表")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "contractCoinId", value = "合约交易对ID(为空时查询所有合约)", required = true, paramType = "form", dataType = "int")
    })
    @GetMapping("current")
    public Page<ContractOrderEntrust> entrustList(@SessionAttribute(SESSION_MEMBER) AuthMember authMember,
                                                  Long contractCoinId, // 合约交易对
                                                  int pageNo,
                                                  int pageSize
    ) {
        pageNo = pageNo - 1;
        Pageable pageable = new PageRequest(pageNo, pageSize);

        // 1. 从撮合引擎内存队列获取普通委托（不含体验金订单）
        List<ContractOrderEntrust> list = ContractCoinMatch.getListByMemberId(authMember.getId());
        List<ContractOrderEntrust> copyList = new ArrayList<>();
        for (ContractOrderEntrust order : list) {
            ContractOrderEntrust copyOrder = new ContractOrderEntrust();
            copyList.add(copyOrder);
            BeanUtils.copyProperties(order, copyOrder);
            if (order.getIsCut() == 1 && order.getVolume().compareTo(BigDecimal.ZERO) == 0) {
                TradingAccount tradingAccount = tradingAccountMonitor.get(order.getMemberId());
                MemberContractPosition memberContractPosition = tradingAccount.findPositionById(order.getPositionId());
                if (memberContractPosition != null) {
                    copyOrder.setVolume(memberContractPosition.getAvailablePosition());
                }
            }
        }

//        // 2. 体验金委托单中非切单类型（市价/限价）不进入撮合引擎内存队列，需从数据库补充；
//        //    切单（SPOT_LIMIT）已在引擎启动/下单时入内存 Map，需排除以防重复
//        Set<Long> mapOrderIds = copyList.stream().map(ContractOrderEntrust::getId).collect(java.util.stream.Collectors.toSet());
//        List<ContractOrderEntrust> experienceOrders = contractOrderEntrustService.queryEntrustingExperienceOrders(authMember.getId(), contractCoinId);
//        for (ContractOrderEntrust expOrder : experienceOrders) {
//            if (!mapOrderIds.contains(expOrder.getId())) {
//                copyList.add(expOrder);
//            }
//        }

        // 3. 分页返回
        int total = copyList.size();
        if (pageNo * pageSize >= total) {
            return new PageImpl<ContractOrderEntrust>(new ArrayList<>(), pageable, total);
        }
        int toIndex = Math.min((pageNo + 1) * pageSize, total);
        return new PageImpl<ContractOrderEntrust>(copyList.subList(pageNo * pageSize, toIndex), pageable, total);
    }

    /**
     * 获取当前委托列表
     *
     * @param authMember
     * @return
     */
    @GetMapping("current-by-type")
    public Page<ContractOrderEntrust> entrustListByType(@SessionAttribute(SESSION_MEMBER) AuthMember authMember,
                                                        Long contractCoinId, // 合约交易对
                                                        int type,
                                                        int pageNo,
                                                        int pageSize
    ) {
        Page<ContractOrderEntrust> contractOrderEntrustOrders = contractOrderEntrustService.queryPageEntrustingOrdersBySymbol(authMember.getId(), contractCoinId, pageNo, pageSize);
        return contractOrderEntrustOrders;
    }

    @ApiOperation(value = "获取历史委托列表", notes = "获取历史委托列表")
    @GetMapping("history-open")
    public Page<ContractOrderEntrust> entrustOpenListHistory(@SessionAttribute(SESSION_MEMBER) AuthMember authMember,
                                                             OrderQueryForm orderQueryForm,
                                                             PageModel pageModel
    ) {
        orderQueryForm.setMemberId(authMember.getId());
        Page<ContractOrderEntrust> contractOrderEntrustOrders = contractOrderEntrustService.queryPageEntrustOrders(orderQueryForm, pageModel);
        return contractOrderEntrustOrders;
    }

    /**
     * 获取历史平仓委托列表
     *
     * @param authMember
     * @return
     */
    @ApiOperation(value = "获取历史平仓委托列表", notes = "获取历史平仓委托列表")
    @GetMapping("history")
    public Page<ContractOrderEntrust> entrustListHistory(@SessionAttribute(SESSION_MEMBER) AuthMember authMember,
                                                         OrderQueryForm orderQueryForm,
                                                         PageModel pageModel
    ) {
        orderQueryForm.setMemberId(authMember.getId());
        Page<ContractOrderEntrust> contractOrderEntrustOrders = contractOrderEntrustService.queryPageEntrustHistoryOrders(orderQueryForm, pageModel);
        return contractOrderEntrustOrders;
    }

    /**
     * 获取历史开仓+历史平仓列表
     */
    @ApiOperation(value = "获取历史全部委托记录", notes = "history-open 和 history 合并")
    @GetMapping("history-all")
    public Page<ContractOrderEntrust> entrustListHistoryAll(@SessionAttribute(SESSION_MEMBER) AuthMember authMember,
                                                            OrderQueryForm orderQueryForm,
                                                            PageModel pageModel
    ) {
        orderQueryForm.setMemberId(authMember.getId());
        return contractOrderEntrustService.queryPageEntrustHistoryAllOrders(orderQueryForm, pageModel);
    }

    /**
     * 是否能够切换持仓模式（全仓、逐仓）
     * 需要查询是否有逐仓/全仓订单（有则不能下单）
     *
     * @param authMember
     * @param targetPattern 目标模式（1：全仓， 2：逐仓）
     * @return
     */
    //@RequestMapping("can-switch-pattern")
    public MessageResult canSwitchPattern(@SessionAttribute(SESSION_MEMBER) AuthMember authMember, Long contractCoinId, ContractOrderPattern targetPattern) {

        ContractCoin contractCoin = contractCoinService.findOne(contractCoinId);
        if (null == contractCoin) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_NOT_EXIST"));
        }
        MemberContractWallet wallet = memberContractWalletService.findByMemberIdAndContractCoin(authMember.getId(), contractCoin);
        if (null == wallet) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_ACCOUNT_NOT_EXIST"));
        }

        ContractOrderPattern temPattern = targetPattern == ContractOrderPattern.CROSSED ? ContractOrderPattern.FIXED : ContractOrderPattern.CROSSED;
        // 查询所有委托的订单
        long sizeEntrustOrder = contractOrderEntrustService.queryEntrustingOrdersCountByContractCoinIdAndPattern(authMember.getId(), contractCoinId, temPattern);
        if (sizeEntrustOrder > 0) {
            return MessageResult.error(500, msService.getMessage("CLOSE_WITHDRAW"));
        }
        MessageResult result = MessageResult.success("success");
        result.setData(null);
        return result;
    }

    /**
     * 更改持仓模式
     * 因本合约是金本位合约，因此切换持仓模式会切换所有交易对的仓位模式，所以会查询所有非目标持仓模式的订单，从而进行判断是否能够切换仓位
     *
     * @param authMember
     * @param contractCoinId
     * @param targetPattern
     * @return
     */
    //@RequestMapping("switch-pattern")
    public MessageResult switchPattern(@SessionAttribute(SESSION_MEMBER) AuthMember authMember, Long contractCoinId, ContractOrderPattern targetPattern) {
        ContractCoin contractCoin = contractCoinService.findOne(contractCoinId);
        if (null == contractCoin) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_NOT_EXIST"));
        }
        MemberContractWallet memberContractWallet = memberContractWalletService.findByMemberIdAndContractCoin(authMember.getId(), contractCoin);
        if (null == memberContractWallet) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_ACCOUNT_NOT_EXIST"));
        }

        if (targetPattern != ContractOrderPattern.FIXED && targetPattern != ContractOrderPattern.CROSSED) {
            return MessageResult.error(500, msService.getMessage("ILLEGAL_ARGUMENT"));
        }
        ContractOrderPattern temPattern = targetPattern == ContractOrderPattern.CROSSED ? ContractOrderPattern.FIXED : ContractOrderPattern.CROSSED;
        // 查询当前合约是否有正在委托的单
        long sizeEntrustOrder = contractOrderEntrustService.queryEntrustingOrdersCountByContractCoinIdAndPattern(authMember.getId(), contractCoinId, temPattern);
        if (sizeEntrustOrder > 0) {
            return MessageResult.error(500, msService.getMessage("CANCEL_FIRST"));
        }

        memberContractWallet.setUsdtPattern(targetPattern);
        memberContractWallet = memberContractWalletService.save(memberContractWallet);

        if (memberContractWallet != null) {

            //通知钱包变更
            JSONObject jsonObj = new JSONObject();
            jsonObj.put("symbol", contractCoin.getSymbol());
            jsonObj.put("walletId", memberContractWallet.getId());
            kafkaTemplate.send("member-wallet-change", JSON.toJSONString(jsonObj));

            MessageResult result = MessageResult.success(msService.getMessage("POSITION_MODE1"));
            result.setData(memberContractWallet);
            return result;
        } else {
            return MessageResult.error(500, msService.getMessage("POSITION_MODE2"));
        }
    }

    /**
     * 修改指定交易对的合约倍数
     * 调整合约倍数理论上应该会将现有持仓订单中的多余保证金释放出来，但是本系统暂未实现此功能，留待后续需要再实现
     * 调整合约其实仅仅会影响用户相同保证金能开多少张合约的计算
     *
     * @param authMember
     * @param contractCoinId
     * @param leverage
     * @param direction
     * @return
     */
    //@RequestMapping("modify-leverage")
    public synchronized MessageResult modifyLeverage(@SessionAttribute(SESSION_MEMBER) AuthMember authMember,
                                                     Long contractCoinId,
                                                     BigDecimal leverage,
                                                     ContractOrderDirection direction) {
        ContractCoin contractCoin = contractCoinService.findOne(contractCoinId);
        if (null == contractCoin) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_NOT_EXIST"));
        }
        MemberContractWallet memberContractWallet = memberContractWalletService.findByMemberIdAndContractCoin(authMember.getId(), contractCoin);
        if (null == memberContractWallet) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_ACCOUNT_NOT_EXIST"));
        }
        if (leverage.compareTo(memberContractWallet.getUsdtBuyLeverage()) == 0) {
            return MessageResult.success();
        }

        BigDecimal buyPosition = memberContractWallet.getUsdtBuyPosition().add(memberContractWallet.getUsdtFrozenBuyPosition());
        BigDecimal sellPosition = memberContractWallet.getUsdtSellPosition().add(memberContractWallet.getUsdtFrozenSellPosition());
        /*if(buyPosition.compareTo(BigDecimal.ZERO)>0 || sellPosition.compareTo(BigDecimal.ZERO)>0) {
            	return MessageResult.error(500, "现有仓位不能调整杠杆");
        }*/

        // 合约倍数是否在规定范围
        if (contractCoin.getLeverageType() == 1) { // 分离倍数
            String[] leverageArr = contractCoin.getLeverage().split(",");
            boolean isExist = false;
            for (String str : leverageArr) {
                if (BigDecimal.valueOf(Integer.parseInt(str)).compareTo(leverage) == 0) {
                    isExist = true;
                }
            }
            if (!isExist) {
                return MessageResult.error(500, msService.getMessage("CONTRACT_MULTIPLE_NOT_EXIST"));
            }
        } else { // 范围倍数
            String[] leverageArr = contractCoin.getLeverage().split(",");
            if (leverageArr.length != 2)
                return MessageResult.error(500, msService.getMessage("CONTRACT_MULTIPLE_ERROR"));

            BigDecimal low = BigDecimal.valueOf(Integer.parseInt(leverageArr[0]));
            BigDecimal high = BigDecimal.valueOf(Integer.parseInt(leverageArr[1]));
            if (leverage.compareTo(low) < 0 || leverage.compareTo(high) > 0) {
                return MessageResult.error(500, msService.getMessage("CONTRACT_MULTIPLE_NOT_ALLOWED"));
            }
        }

        // 查询当前合约是否有正在委托的单
        long sizeEntrustOrder = contractOrderEntrustService.queryEntrustingOrdersCountByContractCoinId(authMember.getId(), contractCoinId);
        if (sizeEntrustOrder > 0) {
            return MessageResult.error(500, msService.getMessage("CANCEL_FIRST"));
        }

        // 如果当前是逐仓模式，需要追加保证金的场合（合约倍数增加），要检查保证金是否足够
        if (memberContractWallet.getUsdtPattern() == ContractOrderPattern.FIXED) {
            if (direction == ContractOrderDirection.BUY) { // 调整多仓合约倍数
                if (leverage.compareTo(memberContractWallet.getUsdtBuyLeverage()) > 0) { // 如果合约加大
                    // 计算保证金
                    BigDecimal needPrinAmount = memberContractWallet.getUsdtBuyPosition().multiply(memberContractWallet.getUsdtShareNumber()).divide(leverage, 8, BigDecimal.ROUND_DOWN);
                    if (needPrinAmount.compareTo(memberContractWallet.getUsdtBuyPrincipalAmount()) > 0) {
                        // 调整保证金(如果余额不足以支付保证金，报错)
                        if (memberContractWallet.getUsdtBalance().compareTo(needPrinAmount.subtract(memberContractWallet.getUsdtBuyPrincipalAmount())) < 0) {
                            return MessageResult.error(500, msService.getMessage("INSUFFICIENT_MARGIN"));
                        }
                        // 增加保证金
                        memberContractWalletService.increaseUsdtBuyPrincipalAmount(memberContractWallet.getId(), needPrinAmount.subtract(memberContractWallet.getUsdtBuyPrincipalAmount()));
                    }
                }
                // 合约倍数降低，不会减少保证金，此处有待商榷，其实无所谓
                // 调整合约倍数
                memberContractWalletService.modifyUsdtBuyAndSellLeverage(memberContractWallet.getId(), leverage);
            } else { // 调整空仓合约倍数
                if (leverage.compareTo(memberContractWallet.getUsdtSellLeverage()) > 0) { // 如果合约加大
                    // 计算保证金
                    BigDecimal needPrinAmount = memberContractWallet.getUsdtSellPosition().multiply(memberContractWallet.getUsdtShareNumber()).divide(leverage, 8, BigDecimal.ROUND_DOWN);
                    if (needPrinAmount.compareTo(memberContractWallet.getUsdtSellPrincipalAmount()) > 0) {
                        // 调整保证金(如果余额不足以支付保证金，报错)
                        if (memberContractWallet.getUsdtBalance().compareTo(needPrinAmount.subtract(memberContractWallet.getUsdtSellPrincipalAmount())) < 0) {
                            return MessageResult.error(500, msService.getMessage("INSUFFICIENT_MARGIN"));
                        }
                        // 增加保证金
                        memberContractWalletService.increaseUsdtSellPrincipalAmount(memberContractWallet.getId(), needPrinAmount.subtract(memberContractWallet.getUsdtSellPrincipalAmount()));
                    }
                }
                // 合约倍数降低，不会减少保证金，此处有待商榷，其实无所谓
                // 调整合约倍数
                memberContractWalletService.modifyUsdtBuyAndSellLeverage(memberContractWallet.getId(), leverage);
            }
            //通知钱包变更
            JSONObject jsonObj = new JSONObject();
            jsonObj.put("symbol", contractCoin.getSymbol());
            jsonObj.put("walletId", memberContractWallet.getId());
            kafkaTemplate.send("member-wallet-change", JSON.toJSONString(jsonObj));
        } else {// 如果当前是全仓模式，需要检查保证金够不够(根据最新价格计算)
            BigDecimal totalNeedPrin = BigDecimal.ZERO;
            BigDecimal currentPrice = contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol()).getNowPrice();
            BigDecimal newBuyMargin = buyPosition.multiply(memberContractWallet.getUsdtShareNumber()).divide(leverage, 8, BigDecimal.ROUND_DOWN);
            BigDecimal newSellMargin = sellPosition.multiply(memberContractWallet.getUsdtShareNumber()).divide(leverage, 8, BigDecimal.ROUND_DOWN);
            //if (direction == ContractOrderDirection.BUY) { // 调整多仓合约倍数
            totalNeedPrin = totalNeedPrin.add(newBuyMargin);
            //} else {
            totalNeedPrin = totalNeedPrin.add(newSellMargin);
            //}

            // 计算账户总权益
            // 计算金本位权益（多仓 + 空仓）
            BigDecimal usdtTotalProfitAndLoss = BigDecimal.ZERO;
            // 多仓计算方法：（当前价格 / 开仓均价 - 1）* （可用仓位 + 冻结仓位） * 合约面值
            if (memberContractWallet.getUsdtBuyPrice().compareTo(BigDecimal.ZERO) > 0) {
                usdtTotalProfitAndLoss = usdtTotalProfitAndLoss.add(currentPrice.divide(memberContractWallet.getUsdtBuyPrice(), 8, BigDecimal.ROUND_DOWN).subtract(BigDecimal.ONE).multiply(memberContractWallet.getUsdtBuyPosition()).multiply(memberContractWallet.getUsdtShareNumber()));
            }
            // 空仓计算方法：（1 - 当前价格 / 开仓均价）* （可用仓位 + 冻结仓位） * 合约面值
            if (memberContractWallet.getUsdtSellPrice().compareTo(BigDecimal.ZERO) > 0) {
                usdtTotalProfitAndLoss = usdtTotalProfitAndLoss.add(BigDecimal.ONE.subtract(currentPrice.divide(memberContractWallet.getUsdtSellPrice(), 8, BigDecimal.ROUND_DOWN)).multiply(memberContractWallet.getUsdtSellPosition()).multiply(memberContractWallet.getUsdtShareNumber()));
            }

            // 如果总共需要的保证金 大于 可用余额+多仓仓位保证金+空仓仓位保证金
            if (totalNeedPrin.compareTo(usdtTotalProfitAndLoss.add(memberContractWallet.getUsdtBalance()).add(memberContractWallet.getUsdtBuyPrincipalAmount()).add(memberContractWallet.getUsdtSellPrincipalAmount())) > 0) {
                return MessageResult.error(500, msService.getMessage("INSUFFICIENT_MARGIN"));
            }

            log.info("margin1={}, margin2={}", newBuyMargin, memberContractWallet.getUsdtBuyPrincipalAmount());
            memberContractWalletService.increaseUsdtBuyPrincipalAmount(memberContractWallet.getId(), newBuyMargin.subtract(memberContractWallet.getUsdtBuyPrincipalAmount()));
            memberContractWalletService.increaseUsdtSellPrincipalAmount(memberContractWallet.getId(), newSellMargin.subtract(memberContractWallet.getUsdtSellPrincipalAmount()));

            //合约调整倍率
            memberContractWalletService.modifyUsdtBuyAndSellLeverage(memberContractWallet.getId(), leverage);
            //通知钱包变更
            JSONObject jsonObj = new JSONObject();
            jsonObj.put("symbol", contractCoin.getSymbol());
            jsonObj.put("walletId", memberContractWallet.getId());
            kafkaTemplate.send("member-wallet-change", JSON.toJSONString(jsonObj));
        }

        MessageResult result = MessageResult.success("success");
        result.setData(null);
        return result;
    }

    /**
     * 调整保证金
     *
     * @param authMember
     * @param contractCoinId
     * @param principal
     * @param direction
     * @param type           0:增加  1：减少
     * @return
     */
    //@RequestMapping("ajust-principal")
    public MessageResult ajustPrincipal(@SessionAttribute(SESSION_MEMBER) AuthMember authMember,
                                        Long contractCoinId,
                                        BigDecimal principal,
                                        ContractOrderDirection direction,
                                        Integer type) {
        ContractCoin contractCoin = contractCoinService.findOne(contractCoinId);
        if (null == contractCoin) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_NOT_EXIST"));
        }
        MemberContractWallet memberContractWallet = memberContractWalletService.findByMemberIdAndContractCoin(authMember.getId(), contractCoin);
        if (null == memberContractWallet) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_ACCOUNT_NOT_EXIST"));
        }
        if (memberContractWallet.getUsdtPattern() == ContractOrderPattern.CROSSED) {
            return MessageResult.error(500, msService.getMessage("FULL_WAREHOUSE_MODE"));
        }

        // 获取当前价格
        BigDecimal currentPrice = contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol()).getNowPrice();
        if (direction == ContractOrderDirection.BUY) { // 调整多仓保证金
            if (type == 1) { // 减少保证金
                // 计算盈亏
                BigDecimal pL = currentPrice.divide(memberContractWallet.getUsdtBuyPrice(), 8, BigDecimal.ROUND_DOWN).subtract(BigDecimal.ONE).multiply(memberContractWallet.getUsdtBuyPosition()).multiply(memberContractWallet.getUsdtShareNumber());
                // 如果减少后的保证金 小于 持仓需要保证金
                if (memberContractWallet.getUsdtBuyPrincipalAmount().subtract(principal).compareTo(memberContractWallet.getUsdtBuyPrincipalAmount().add(pL)) < 0) {
                    return MessageResult.error(500, msService.getMessage("INSUFFICIENT_MARGIN_UNABLE_ADJUST"));
                } else {
                    memberContractWalletService.decreaseUsdtBuyPrincipalAmount(memberContractWallet.getId(), principal);
                    return MessageResult.success("success");
                }
            } else { // 直接增加保证金
                if (memberContractWallet.getUsdtBalance().compareTo(principal) < 0) {
                    return MessageResult.error(500, msService.getMessage("INSUFFICIENT_MARGIN_UNABLE_ADJUST"));
                }
                memberContractWalletService.increaseUsdtBuyPrincipalAmount(memberContractWallet.getId(), principal);
                return MessageResult.success("success");
            }
        } else {
            if (type == 1) { // 减少保证金
                // 计算盈亏
                BigDecimal pL = BigDecimal.ONE.subtract(currentPrice.divide(memberContractWallet.getUsdtSellPrice(), 8, BigDecimal.ROUND_DOWN)).multiply(memberContractWallet.getUsdtSellPosition()).multiply(memberContractWallet.getUsdtShareNumber());
                // 如果减少后的保证金 小于 持仓需要保证金
                if (memberContractWallet.getUsdtSellPrincipalAmount().subtract(principal).compareTo(memberContractWallet.getUsdtSellPrincipalAmount().add(pL)) < 0) {
                    return MessageResult.error(500, msService.getMessage("INSUFFICIENT_MARGIN_UNABLE_ADJUST"));
                } else {
                    memberContractWalletService.decreaseUsdtSellPrincipalAmount(memberContractWallet.getId(), principal);
                    return MessageResult.success("success");
                }
            } else { // 直接增加保证金
                if (memberContractWallet.getUsdtBalance().compareTo(principal) < 0) {
                    return MessageResult.error(500, msService.getMessage("INSUFFICIENT_MARGIN_UNABLE_ADJUST"));
                }
                memberContractWalletService.increaseUsdtSellPrincipalAmount(memberContractWallet.getId(), principal);
                return MessageResult.success("success");
            }
        }
    }

    // =====================================================================
    // 体验金开仓接口
    // =====================================================================

    /**
     * 体验金开仓（市价）
     * <p>
     * 不扣用户钱包；持仓量由体验金面额 × 杠杆全额换算；仓位类型固定为逐仓（SEPARATE）。
     *
     * @param contractCoinId     合约交易对 ID
     * @param direction          方向：0=做多 1=做空
     * @param experienceRecordId 体验金券 ID（experience_gift_record.id）
     */
    @ApiOperation(value = "体验金开仓", notes = "使用体验金开仓，不占用用户资金，仓量由面额全额换算")
    @PostMapping("experience/open")
    public MessageResult openExperienceOrder(
            @SessionAttribute(SESSION_MEMBER) AuthMember authMember,
            @RequestParam("contractCoinId") Long contractCoinId,
            @RequestParam("direction") ContractOrderDirection direction,
            @RequestParam("experienceRecordId") Long experienceRecordId) {

        // 1. 基础校验
        Member member = memberService.findOne(authMember.getId());
        if (member == null) {
            return MessageResult.error(500, msService.getMessage("ACCOUNT_NOT_EXIST"));
        }
        if (member.getTransactionStatus().equals(BooleanEnum.IS_FALSE)) {
            return MessageResult.error(500, msService.getMessage("CANNOT_TRADE"));
        }

        ContractCoin contractCoin = contractCoinService.findOne(contractCoinId);
        if (contractCoin == null) {
            return MessageResult.error(500, msService.getMessage("NONSUPPORT_COIN"));
        }
        if (contractCoin.getEnable() != 1 || contractCoin.getExchangeable() != 1) {
            return MessageResult.error(500, msService.getMessage("COIN_FORBIDDEN"));
        }
        if (!contractCoinMatchFactory.containsContractCoinMatch(contractCoin.getSymbol())) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_ENGINE_ERROR"));
        }

        // 2. 校验体验金券
        ExperienceGiftRecord record =
                experienceGiftRecordService.findByIdAndMemberId(experienceRecordId, member.getId())
                        .orElse(null);
        if (record == null) {
            return MessageResult.error(500, msService.getMessage("EG_RECORD_NOT_EXIST"));
        }
        if (record.getStatus() != ExperienceGiftRecordStatus.PENDING) {
            return MessageResult.error(500, msService.getMessage("EG_RECORD_INVALID"));
        }
        if (record.getExpireAt() != null && record.getExpireAt().before(new Date())) {
            return MessageResult.error(500, msService.getMessage("EG_RECORD_EXPIRED"));
        }

        // 3. 校验交易对是否在体验金产品配置中，并取对应杠杆倍数
        ExperienceGiftProductCoin productCoin =
                experienceGiftProductCoinService.findByProductIdAndCoinSymbol(record.getProductId(), contractCoin.getSymbol())
                        .orElse(null);
        if (productCoin == null) {
            return MessageResult.error(500, msService.getMessage("EG_COIN_NOT_SUPPORTED"));
        }
        int leverage = productCoin.getLeverage();

        // 4. 防套利：不允许同时持有反方向体验金仓位，同方向可开多个
        if (experienceGiftRecordService.hasOppositeActivePosition(member.getId(), contractCoin.getSymbol(), direction)) {
            return MessageResult.error(500, msService.getMessage("EG_DUPLICATE_POSITION"));
        }

        // 5. 计算市价（含滑点）
        BigDecimal currentPrice = contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol()).getNowPrice();
        if (currentPrice == null || currentPrice.compareTo(BigDecimal.ZERO) <= 0) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_ENGINE_ERROR"));
        }

        BigDecimal openPrice = currentPrice;
        if (direction == ContractOrderDirection.BUY) {
            openPrice = contractCoin.getSpreadType() == 1
                    ? currentPrice.add(currentPrice.multiply(contractCoin.getSpread()))
                    : currentPrice.add(contractCoin.getSpread());
        } else {
            openPrice = contractCoin.getSpreadType() == 1
                    ? currentPrice.subtract(currentPrice.multiply(contractCoin.getSpread()))
                    : currentPrice.subtract(contractCoin.getSpread());
        }

        // 6. 校验持仓量（预检：面额 × 杠杆 / 开仓价）
        BigDecimal leverageBd = BigDecimal.valueOf(leverage);
        int volScale = contractCoin.getBaseCoinScale() > 0 ? contractCoin.getBaseCoinScale() : 4;
        BigDecimal volume = record.getAmount().multiply(leverageBd)
                .divide(openPrice, volScale, RoundingMode.DOWN);
        if (volume.compareTo(BigDecimal.ZERO) <= 0) {
            return MessageResult.error(500, msService.getMessage("EG_AMOUNT_TOO_SMALL"));
        }

        // 7. 同事务：构建并保存订单/仓位，激活体验金券（状态改为持仓中）
        Map<String, Object> openResult;
        try {
            openResult = memberContractPositionService.openExperienceGiftPosition(
                    member.getId(), contractCoin, direction, record, leverage, openPrice);
        } catch (Exception e) {
            log.warn("体验金开仓失败，memberId={}, recordId={}, msg={}", member.getId(), experienceRecordId, e.getMessage());
            return MessageResult.error(500, msService.getMessage("EG_ACTIVATE_FAILED"));
        }
        ContractOrderEntrust savedOrder = (ContractOrderEntrust) openResult.get("order");
        MemberContractPosition savedPosition = (MemberContractPosition) openResult.get("position");

        // 8. 更新内存（与普通市价单一致）
        ContractCoinMatch coinMatch = contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol());
        coinMatch.updateTradingAccount(savedOrder);

        log.info("体验金开仓成功, memberId={}, recordId={}, positionId={}, volume={}, openPrice={}",
                member.getId(), experienceRecordId, savedPosition.getId(), volume, openPrice);

        MessageResult result = MessageResult.success(msService.getMessage("SWAP_SUCCESS"));
        result.setData(savedPosition);
        return result;
    }


    /**
     * 体验金仓位市价平仓（用户手动）
     * POST /order/experience/close
     */
    @PostMapping("experience/close")
    public MessageResult closeExperienceOrder(
            @SessionAttribute(SESSION_MEMBER) AuthMember authMember,
            @RequestParam("positionId") Long positionId) {

        Member member = memberService.findOne(authMember.getId());
        if (member == null) {
            return MessageResult.error(500, msService.getMessage("ACCOUNT_NOT_EXIST"));
        }
        if (member.getTransactionStatus().equals(BooleanEnum.IS_FALSE)) {
            return MessageResult.error(500, msService.getMessage("CANNOT_TRADE"));
        }

        MemberContractPosition position = memberContractPositionService.findOne(positionId);
        if (position == null) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_ACCOUNT_NOT_EXIST"));
        }
        if (position.getMemberId().longValue() != member.getId().longValue()) {
            return MessageResult.error(500, msService.getMessage("ILLEGAL_OPERATION"));
        }
        if (position.getExperienceRecordId() == null) {
            return MessageResult.error(500, msService.getMessage("EG_RECORD_NOT_EXIST"));
        }
        if (position.getStatus() != ContractPositionStatus.STARTED) {
            return MessageResult.error(500, msService.getMessage("CANNOT_CLOSE"));
        }

        ContractCoin contractCoin = position.getCoin();
        if (contractCoin == null || !contractCoinMatchFactory.containsContractCoinMatch(contractCoin.getSymbol())) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_ENGINE_ERROR"));
        }

        BigDecimal closePrice = contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol()).getNowPrice();
        if (closePrice == null || closePrice.compareTo(BigDecimal.ZERO) <= 0) {
            return MessageResult.error(500, msService.getMessage("CONTRACT_ENGINE_ERROR"));
        }

        // 计算盈亏
        BigDecimal pnl;
        if (position.getMoneyType() == ContractMoneyType.COIN) {
            pnl = closePrice.subtract(position.getOpenPrice()).multiply(position.getCurrentPosition());
        } else {
            pnl = closePrice.subtract(position.getOpenPrice())
                    .multiply(position.getCurrentPosition())
                    .multiply(position.getShareNumber())
                    .divide(position.getOpenPrice(), 8, RoundingMode.HALF_UP);
        }
        if (position.getDirection() == ContractOrderDirection.SELL) {
            pnl = BigDecimal.ZERO.subtract(pnl);
        }

        if (!redisLock.lockExperienceGiftRecord(position.getExperienceRecordId())) {
            return MessageResult.error(500, msService.getMessage("REQUEST_REPEAT"));
        }
        // 平仓前查出待处理切单，平仓后从引擎内存 Map 移除
        List<ContractOrderEntrust> expCutOrders = contractOrderEntrustService.queryEntrustCutOrdersByPositionId(positionId);
        try {
            boolean closed = memberContractPositionService.closeExperienceGiftPosition(
                    positionId, closePrice, CloseType.CLOSE, pnl,
                    position.getExperienceRecordId(), ExperienceGiftCloseReason.USER_MANUAL);
            if (!closed) {
                return MessageResult.error(500, msService.getMessage("CANNOT_CLOSE"));
            }
        } catch (Exception e) {
            log.error("体验金手动平仓异常 positionId={}: {}", positionId, e.getMessage(), e);
            return MessageResult.error(500, msService.getMessage("ERROR"));
        } finally {
            redisLock.unlockExperienceGiftRecord(position.getExperienceRecordId());
        }
        // 从引擎内存 Map 中移除已处理的切单
        ContractCoinMatch expCoinMatch = contractCoinMatchFactory.getContractCoinMatch(contractCoin.getSymbol());
        if (expCoinMatch != null) {
            for (ContractOrderEntrust cutOrder : expCutOrders) {
                expCoinMatch.removeFromMap(cutOrder);
            }
        }
        kafkaTemplate.send("trading-account-change", member.getId() + "");
        tradingAccountMonitor.updateAccount(member.getId());
        return MessageResult.success(msService.getMessage("SWAP_SUCCESS"));
    }
}