zyy
2025-07-14 4b442bd9648115e6ef6fbe3fb8f6b7b1e6d30785
AI产品交易
17 files modified
1 files renamed
4 files added
468 ■■■■■ changed files
src/main/java/com/nq/common/interceptor/ApiAdminAuthorityInterceptor.java 4 ●●●● patch | view | raw | blame | history
src/main/java/com/nq/controller/StockInkApiController.java 14 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/controller/backend/AdminExchangeRateController.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/controller/backend/AdminStockAiController.java 77 ●●●● patch | view | raw | blame | history
src/main/java/com/nq/dao/StockAiMapper.java 2 ●●● patch | view | raw | blame | history
src/main/java/com/nq/dao/StockAiOrderMapper.java 13 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/dao/StockAiOrderPositionMapper.java 10 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/enums/EStockType.java 11 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/enums/EUserAssets.java 2 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/pojo/StockAI.java 2 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/service/ExchangeRateService.java 4 ●●● patch | view | raw | blame | history
src/main/java/com/nq/service/IStockAiService.java 7 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/service/impl/ExchangeRateServiceImpl.java 13 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/service/impl/StockAiServiceImpl.java 192 ●●●● patch | view | raw | blame | history
src/main/java/com/nq/service/impl/StockServiceImpl.java 1 ●●●● patch | view | raw | blame | history
src/main/java/com/nq/service/impl/UserAssetsServices.java 10 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/vo/stock/ai/StockAiOrderTypeVO.java 16 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/vo/stock/ai/StockAiOrderVO.java 31 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/vo/stock/ai/StockAiVO.java 2 ●●● patch | view | raw | blame | history
src/main/resources/application.yml 2 ●●● patch | view | raw | blame | history
src/main/resources/mapper/StockAiMapper.xml 2 ●●● patch | view | raw | blame | history
src/main/resources/mapper/StockAiOrderMapper.xml 48 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/common/interceptor/ApiAdminAuthorityInterceptor.java
@@ -30,7 +30,7 @@
            return true;
        }
        SiteAdmin siteAdmin = null;
        /*SiteAdmin siteAdmin = null;
        String loginToken = httpServletRequest.getHeader(PropertiesUtil.getProperty("admin.cookie.name"));
        if (StringUtils.isNotEmpty(loginToken)) {
            String adminJsonStr = RedisShardedPoolUtils.get(loginToken);
@@ -60,7 +60,7 @@
            writer.flush();
            writer.close();
            return false;
        }
        }*/
//        194.26.73.150, 172.70.34.195
//        String ip = IpUtils.getIp(httpServletRequest);
src/main/java/com/nq/controller/StockInkApiController.java
@@ -1,6 +1,7 @@
package com.nq.controller;
import com.nq.common.ServerResponse;
import com.nq.service.ExchangeRateService;
import com.nq.service.IStockAiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@@ -19,6 +20,8 @@
    @Autowired
    IStockAiService stockAiService;
    @Autowired
    ExchangeRateService exchangeRateService;
    private static final ThreadLocal<Boolean> buyOrderCreated = ThreadLocal.withInitial(() -> false);
    private final Lock buyLock = new ReentrantLock();
@@ -65,6 +68,10 @@
    /**
     * 获取ai交易产品订单列表
     * @param pageNum
     * @param pageSize
     * @param status 状态
     * @param request
     * @return
     */
    @RequestMapping("getStockAiOrderList.do")
@@ -75,4 +82,11 @@
                                              HttpServletRequest request)   {
        return stockAiService.getStockAiOrderList(pageNum, pageSize, status ,request);
    }
    //查询汇率信息
    @RequestMapping({"getRateInfo.do"})
    @ResponseBody
    public ServerResponse getInfo() {
        return exchangeRateService.getInfo();
    }
}
src/main/java/com/nq/controller/backend/AdminExchangeRateController.java
@@ -6,6 +6,7 @@
import com.nq.service.ExchangeRateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@@ -27,11 +28,11 @@
    @ResponseBody
    public ServerResponse getInfo(@RequestParam(value = "pageNum", defaultValue = "1") int pageNum,
                                  @RequestParam(value = "pageSize", defaultValue = "15") int pageSize) {
        return exchangeRateService.getInfo(pageNum, pageSize);
        return exchangeRateService.getInfoPage(pageNum, pageSize);
    }
    //修改汇率
    @RequestMapping({"editRate.do"})
    @PostMapping({"editRate.do"})
    @ResponseBody
    public ServerResponse editRate(ExchangeRate model, HttpServletRequest request) {
        if (model == null) {
src/main/java/com/nq/controller/backend/AdminStockAiController.java
@@ -1,16 +1,13 @@
 package com.nq.controller.backend;
 import com.nq.common.ServerResponse;
 import com.nq.pojo.Stock;
 import com.nq.pojo.StockAI;
 import com.nq.pojo.StockAIOrder;
 import com.nq.pojo.StockAIOrderPosition;
 import com.nq.service.IStockAiService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
 import javax.servlet.http.HttpServletRequest;
 import org.springframework.web.bind.annotation.*;
 /**
  * 后台AI产品api
@@ -29,19 +26,75 @@
     @ResponseBody
     public ServerResponse getStockAiList(@RequestParam(value = "pageNum", defaultValue = "1") int pageNum,
                                          @RequestParam(value = "pageSize", defaultValue = "5") int pageSize,
                                          @RequestParam(value = "stockType") String stockType,
                                          @RequestParam(value = "status") String status,
                                          @RequestParam(value = "name") String name)   {
                                          @RequestParam(value = "stockType", required = false) String stockType,
                                          @RequestParam(value = "status", required = false) String status,
                                          @RequestParam(value = "name", required = false) String name)   {
         return stockAiService.getAdminStockAiList(pageNum, pageSize, stockType, status, name);
     }
     //修改票信息
     @RequestMapping({"editStockAi.do"})
     //编辑ai交易产品
     @PostMapping({"editStockAi.do"})
     @ResponseBody
     public ServerResponse editStockAi(StockAI model) {
         return stockAiService.editStockAi(model);
     }
     /**
      * 获取ai交易产品列表
      * @return
      */
     @RequestMapping("getStockAiOrderList.do")
     @ResponseBody
     public ServerResponse getStockAiOrderList(@RequestParam(value = "pageNum", defaultValue = "1") int pageNum,
                                          @RequestParam(value = "pageSize", defaultValue = "5") int pageSize,
                                          @RequestParam(value = "stockType") String stockType,
                                          @RequestParam(value = "status") String status,
                                          @RequestParam(value = "userId") Integer userId,
                                          @RequestParam(value = "phone") String phone)   {
         return stockAiService.getAdminStockAiOrderList(pageNum, pageSize, stockType, status, userId, phone);
     }
     /**
      * ai订单建仓
      * @param model
      * @return
      */
     @PostMapping({"openPosition.do"})
     @ResponseBody
     public ServerResponse openPosition(StockAIOrderPosition model) {
         if (model == null) {
             return ServerResponse.createByErrorMsg("model is null");
         }
         if (model.getStockAiOrderId() == null) {
             return ServerResponse.createByErrorMsg("stockAiOrderId is null");
         }
         if (model.getStockId() == null || model.getStockNum() == null || model.getStockPrice() == null ||
                 model.getCoverDate() == null || model.getCoverPrice() == null) {
             return ServerResponse.createByErrorMsg("请输入必填参数");
         }
         if (model.getStockNum() <= 0) {
             return ServerResponse.createByErrorMsg("建仓股票数量必须大于0");
         }
         return stockAiService.openPosition(model);
     }
     /**
      * ai订单操作
      * @param id   订单id
      * @param status  通过 拒绝 结算(已完成)
      * @return
      */
     @GetMapping({"orderOperation.do"})
     @ResponseBody
     public ServerResponse orderOperation(@RequestParam(value = "id") Long id,
                                          @RequestParam(value = "status") String status) {
         if (id == null) {
             return ServerResponse.createByErrorMsg("id is null");
         }
         if (status.isEmpty()) {
             return ServerResponse.createByErrorMsg("status is null");
         }
         return stockAiService.orderOperation(id, status);
     }
 }
src/main/java/com/nq/dao/StockAiMapper.java
@@ -2,7 +2,7 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.nq.pojo.StockAI;
import com.nq.vo.stock.StockAiVO;
import com.nq.vo.stock.ai.StockAiVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
src/main/java/com/nq/dao/StockAiOrderMapper.java
@@ -2,9 +2,22 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.nq.pojo.StockAIOrder;
import com.nq.vo.stock.ai.StockAiOrderTypeVO;
import com.nq.vo.stock.ai.StockAiOrderVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface StockAiOrderMapper extends BaseMapper<StockAIOrder> {
    List<StockAiOrderVO> getAdminStockAiOrderList(@Param("stockType") String stockType,
                                                  @Param("status") String status,
                                                  @Param("userId") Integer userId,
                                                  @Param("phone") String name);
    List<StockAiOrderTypeVO> getStockAiOrderList(@Param("userId") Integer userId,
                                                 @Param("status") String status);
}
src/main/java/com/nq/dao/StockAiOrderPositionMapper.java
New file
@@ -0,0 +1,10 @@
package com.nq.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.nq.pojo.StockAIOrderPosition;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface StockAiOrderPositionMapper extends BaseMapper<StockAIOrderPosition> {
}
src/main/java/com/nq/enums/EStockType.java
@@ -54,6 +54,17 @@
        }
    }
    //根据货币获取类型
    public static EStockType getEStockTypeBySymbol(String symbol){
        if(EStockType.US.getSymbol().equals(symbol)){
            return US;
        }else if(EStockType.MX.getSymbol().equals(symbol)){
            return  MX;
        }else{
            return  MX;
        }
    }
    public String getContryId() {
        return contryId;
    }
src/main/java/com/nq/enums/EUserAssets.java
@@ -19,6 +19,8 @@
    TRANSFER("TRANSFER","转换"),
    TOP_UP("TOP_UP","充值"),
    BUY_AI("BUY_AI","购买AI产品"),
    BUY_AI_REJECT("BUY_AI_REJECT","拒绝买入AI产品申请"),
    AI_SETTLEMENT("AI_SETTLEMENT","结算AI产品订单"),
    ;
    private String  code;
src/main/java/com/nq/pojo/StockAI.java
@@ -23,8 +23,10 @@
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    //股票类型
    private String stockType;
    //股票名称
    private String stockName;
    /**
src/main/java/com/nq/service/ExchangeRateService.java
@@ -13,7 +13,9 @@
     * @param pageSize
     * @return
     */
    ServerResponse getInfo(Integer pageNum, Integer pageSize);
    ServerResponse getInfoPage(Integer pageNum, Integer pageSize);
    ServerResponse getInfo();
    /**
     * 新增或编辑
src/main/java/com/nq/service/IStockAiService.java
@@ -2,6 +2,7 @@
import com.nq.common.ServerResponse;
import com.nq.pojo.StockAI;
import com.nq.pojo.StockAIOrderPosition;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
@@ -18,4 +19,10 @@
  ServerResponse getAdminStockAiList(Integer pageNum, Integer pageSize, String stockType, String status, String name);
  ServerResponse editStockAi(StockAI model);
  ServerResponse getAdminStockAiOrderList(Integer pageNum, Integer pageSize, String stockType, String status, Integer userId, String phone);
  ServerResponse openPosition(StockAIOrderPosition model);
  ServerResponse orderOperation(Long id, String status);
}
src/main/java/com/nq/service/impl/ExchangeRateServiceImpl.java
@@ -29,7 +29,7 @@
    ExchangeRateMapper exchangeRateMapper;
    @Override
    public ServerResponse getInfo(Integer pageNum, Integer pageSize) {
    public ServerResponse getInfoPage(Integer pageNum, Integer pageSize) {
        try {
            PageHelper.startPage(pageNum, pageSize);
            List<ExchangeRate> exchangeRateList = exchangeRateMapper.selectList(null);
@@ -43,6 +43,17 @@
    }
    @Override
    public ServerResponse getInfo() {
        try {
            List<ExchangeRate> exchangeRateList = exchangeRateMapper.selectList(null);
            return ServerResponse.createBySuccess(exchangeRateList);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return ServerResponse.createByError();
    }
    @Override
    public ServerResponse updateRate(ExchangeRate model, HttpServletRequest request) {
        try {
            //新增
src/main/java/com/nq/service/impl/StockAiServiceImpl.java
@@ -7,10 +7,13 @@
import com.nq.common.ServerResponse;
import com.nq.dao.StockAiMapper;
import com.nq.dao.StockAiOrderMapper;
import com.nq.dao.StockAiOrderPositionMapper;
import com.nq.enums.*;
import com.nq.pojo.*;
import com.nq.service.*;
import com.nq.vo.stock.StockAiVO;
import com.nq.vo.stock.ai.StockAiOrderTypeVO;
import com.nq.vo.stock.ai.StockAiOrderVO;
import com.nq.vo.stock.ai.StockAiVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -21,15 +24,20 @@
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
@Service("iStockAiService")
public class StockAiServiceImpl implements IStockAiService {
    private static final Logger log = LoggerFactory.getLogger(StockAiServiceImpl.class);
    private final ConcurrentHashMap<Long, Object> locks = new ConcurrentHashMap<>();
    @Autowired
    StockAiMapper stockAiMapper;
    @Autowired
    StockAiOrderMapper stockAiOrderMapper;
    @Autowired
    StockAiOrderPositionMapper stockAiOrderPositionMapper;
    @Autowired
    ISiteProductService iSiteProductService;
    @Autowired
@@ -67,37 +75,40 @@
     * @return
     */
    @Override
    @Transactional
    public ServerResponse buyStockAi(Long id, BigDecimal buyNum, HttpServletRequest request) {
        try {
            User user = iUserService.getCurrentUser(request);
            if (user == null ){
                return ServerResponse.createBySuccessMsg("請先登錄");
                return ServerResponse.createBySuccessMsg("请先登录");
            }
            synchronized (user.getId()){
            Object lock = locks.computeIfAbsent(Long.valueOf(user.getId()), k -> new Object());
            synchronized (lock){
                SiteProduct siteProduct = iSiteProductService.getProductSetting();
                if (siteProduct.getRealNameDisplay() && user.getIsActive() != 2) {
                    return ServerResponse.createByErrorMsg("订单失败,请先实名认证", request);
                    return ServerResponse.createByErrorMsg("订单失败,请先实名认证");
                }
                if (siteProduct.getRealNameDisplay() && user.getIsLock().intValue() == 1) {
                    return ServerResponse.createByErrorMsg("订单失败,帐户已被锁定", request);
                    return ServerResponse.createByErrorMsg("订单失败,帐户已被锁定");
                }
                StockAI stockAI = stockAiMapper.selectById(id);
                if (stockAI == null) {
                    return ServerResponse.createByErrorMsg("订单失败,AI股票不存在", request);
                    return ServerResponse.createByErrorMsg("订单失败,AI股票不存在");
                }
                if (!stockAI.getStatus().equals(EStockAIStatus.online.getValue())) {
                    return ServerResponse.createByErrorMsg("订单失败,AI股票已下架");
                }
                if(buyNum.compareTo(stockAI.getMinPrice()) < 0){
                    return ServerResponse.createByErrorMsg("最低购买数量" + stockAI.getMinPrice(), request);
                    return ServerResponse.createByErrorMsg("最低购买数量" + stockAI.getMinPrice());
                }
                //获取用户账户
                UserAssets userAssets = iUserAssetsServices.assetsByTypeAndUserId(EStockType.MX.getCode(), user.getId());
                BigDecimal finalBuyNum = buyNum;    //购买金额
                //如果不是墨西哥币需要转换金额
                if (!stockAI.getStockType().equals(EStockType.US.getCode())) {
                if (!stockAI.getStockType().equals(EStockType.MX.getCode())) {
                    EStockType stockType = EStockType.getEStockTypeByCode(stockAI.getStockType());
                    ExchangeRate exchangeRate = exchangeRateRepository.findExchangeRateByCurrencyAndConversionCurrency(stockType.getSymbol(), EStockType.MX.getSymbol())
                            .orElse(null);
                    if (exchangeRate == null) {
@@ -107,11 +118,11 @@
                    buyNum = iUserAssetsServices.exchangeAmountByRate(buyNum, exchangeRate.getRata());
                }
                if(buyNum.compareTo(userAssets.getAvailableBalance()) > 0){
                    return ServerResponse.createByErrorMsg("可用余额不足" + userAssets.getAvailableBalance(), request);
                    return ServerResponse.createByErrorMsg("可用余额不足" + userAssets.getAvailableBalance());
                }
                if(userAssets.getAmountToBeCovered().compareTo(BigDecimal.ZERO) > 0){
                    return ServerResponse.createByErrorMsg("请先缴清待补资金", request);
                    return ServerResponse.createByErrorMsg("请先缴清待补资金");
                }
                StockAIOrder stockAIOrder = new StockAIOrder();
@@ -124,10 +135,10 @@
                stockAIOrder.setStatus(EStockAIOrderStatus.wait.getStatus());   //等待审核
                stockAiOrderMapper.insert(stockAIOrder);
                iUserAssetsServices.aiAvailableBalanceChange(userAssets, EUserAssets.BUY_AI, buyNum);
                return ServerResponse.createBySuccessMsg("下单成功", request);
                return ServerResponse.createBySuccessMsg("下单成功");
            }
        } catch (Exception e) {
            log.error("StockAiService buyStockAiList error", e);
            log.error("StockAiService buyStockAiList  {}", e.getMessage());
        }
        return ServerResponse.createByError();
    }
@@ -145,15 +156,12 @@
        try {
            User user = iUserService.getCurrentUser(request);
            if (user == null ){
                return ServerResponse.createBySuccessMsg("請先登錄");
                return ServerResponse.createBySuccessMsg("请先登录");
            }
            PageHelper.startPage(pageNum, pageSize);
            List<StockAIOrder> stockAIOrders = stockAiOrderMapper.selectList(new QueryWrapper<StockAIOrder>()
                    .eq("user_id", user.getId())
                    .eq(status != null && !status.isEmpty(), "status", status));
            List<StockAiOrderTypeVO> stockAIOrders = stockAiOrderMapper.getStockAiOrderList(user.getId(), status);
            // 获取分页信息
            PageInfo<StockAIOrder> pageInfo = new PageInfo<>(stockAIOrders);
            PageInfo<StockAiOrderTypeVO> pageInfo = new PageInfo<>(stockAIOrders);
            return ServerResponse.createBySuccess(pageInfo);
        } catch (Exception e) {
            log.error("StockAiService getStockAiOrderList error", e);
@@ -223,4 +231,148 @@
        }
        return ServerResponse.createByError();
    }
    /**
     * 后台查询ai交易订单
     * @param pageNum
     * @param pageSize
     * @param stockType
     * @param status
     * @param userId
     * @param phone
     * @return
     */
    @Override
    public ServerResponse getAdminStockAiOrderList(Integer pageNum, Integer pageSize, String stockType, String status, Integer userId, String phone) {
        try {
            PageHelper.startPage(pageNum, pageSize);
            List<StockAiOrderVO> stockAIOrders = stockAiOrderMapper.getAdminStockAiOrderList(stockType, status, userId, phone);
            // 获取分页信息
            PageInfo<StockAiOrderVO> pageInfo = new PageInfo<>(stockAIOrders);
            if (!pageInfo.getList().isEmpty()) {
                List<StockAiOrderVO> newStockAiOrders = pageInfo.getList();
                newStockAiOrders.forEach(stockAiOrderVO -> {
                    EStockType eStockType = EStockType.getEStockTypeByCode(stockAiOrderVO.getStockType());
                    stockAiOrderVO.setStockTypeName(eStockType.getSymbol1());
                    stockAiOrderVO.setSymbol(stockType);
                });
                pageInfo.setList(newStockAiOrders);
            }
            return ServerResponse.createBySuccess(pageInfo);
        } catch (Exception e) {
            log.error("StockAiService getAdminStockAiOrderList error", e);
        }
        return ServerResponse.createByError();
    }
    /**
     * 建仓
     * @param model
     * @return
     */
    @Override
    public ServerResponse openPosition(StockAIOrderPosition model) {
        try {
            Object lock = locks.computeIfAbsent(model.getStockAiOrderId(), k -> new Object());
            synchronized (lock) {
                StockAIOrder stockAIOrder = stockAiOrderMapper.selectById(model.getStockAiOrderId());
                if (stockAIOrder == null) {
                    return ServerResponse.createByErrorMsg("AI订单不存在");
                }
                if (!stockAIOrder.getStatus().equals(EStockAIOrderStatus.passed.getStatus())) {
                    return ServerResponse.createByErrorMsg("申请通过状态才能建仓!");
                }
                //计算剩余金额
                BigDecimal remainAmount = model.getStockPrice().multiply(BigDecimal.valueOf(model.getStockNum()));
                if (stockAIOrder.getRemainAmount().compareTo(remainAmount) < 0) {
                    return ServerResponse.createByErrorMsg("剩余买入金额不足");
                }
                stockAIOrder.setRemainAmount(stockAIOrder.getBuyAmount().subtract(remainAmount));
                //计算收益  (平仓-建仓)*数量
                BigDecimal earnings = model.getCoverPrice().subtract(model.getStockPrice()).multiply(BigDecimal.valueOf(model.getStockNum()));
                model.setCreatDate(new Date());
                model.setEarnings(earnings);
                //保存
                stockAiOrderMapper.updateById(stockAIOrder);
                stockAiOrderPositionMapper.insert(model);
                return ServerResponse.createBySuccess("建仓成功");
            }
        } catch (Exception e) {
            log.error("StockAiService openPosition error", e);
        }
        return ServerResponse.createByError();
    }
    @Override
    public ServerResponse orderOperation(Long id, String status) {
        try {
            Object lock = locks.computeIfAbsent(id, k -> new Object());
            synchronized (lock) {
                StockAIOrder stockAIOrder = stockAiOrderMapper.selectById(id);
                if (stockAIOrder == null) {
                    return ServerResponse.createByErrorMsg("AI订单不存在");
                }
                if (status.equals(EStockAIOrderStatus.passed.getStatus())) {
                    //通过
                    if (!stockAIOrder.getStatus().equals(EStockAIOrderStatus.wait.getStatus())) {
                        return ServerResponse.createByErrorMsg("只能通过待审核订单");
                    }
                } else if (status.equals(EStockAIOrderStatus.notPass.getStatus())) {
                    if (!stockAIOrder.getStatus().equals(EStockAIOrderStatus.wait.getStatus())) {
                        return ServerResponse.createByErrorMsg("只能拒绝待审核订单");
                    }
                    //拒绝TODO 归还买入金额
                    //获取用户账户
                    UserAssets userAssets = iUserAssetsServices.assetsByTypeAndUserId(EStockType.MX.getCode(), stockAIOrder.getUserId());
                    StockAI stockAI = stockAiMapper.selectById(stockAIOrder.getStockAiId());
                    //买入金额
                    BigDecimal buyNum =  stockAIOrder.getBuyAmount();
                    //如果不是墨西哥币需要转换金额
                    if (!stockAI.getStockType().equals(EStockType.MX.getCode())) {
                        EStockType stockType = EStockType.getEStockTypeByCode(stockAI.getStockType());
                        ExchangeRate exchangeRate = exchangeRateRepository.findExchangeRateByCurrencyAndConversionCurrency(stockType.getSymbol(), EStockType.MX.getSymbol())
                                .orElse(null);
                        if (exchangeRate == null) {
                            return ServerResponse.createByErrorMsg("请先设置当前货币汇率");
                        }
                        //转换为墨西哥币
                        buyNum = iUserAssetsServices.exchangeAmountByRate(buyNum, exchangeRate.getRata());
                    }
                    iUserAssetsServices.aiAvailableBalanceChange(userAssets, EUserAssets.BUY_AI_REJECT, buyNum);
                } else if (status.equals(EStockAIOrderStatus.finished.getStatus())) {
                    if (!stockAIOrder.getStatus().equals(EStockAIOrderStatus.passed.getStatus())) {
                        return ServerResponse.createByErrorMsg("只能结算申请通过订单");
                    }
                    //结算 TODO结算建仓 计算收益
                    //所有建仓
                    List<StockAIOrderPosition> stockAIOrderPositionList = stockAiOrderPositionMapper
                            .selectList(new QueryWrapper<StockAIOrderPosition>()
                                    .eq("stock_ai_order_id", id));
                    //结算金额
                    BigDecimal amount = stockAIOrder.getBuyAmount();
                    if (!stockAIOrderPositionList.isEmpty()) {
                        //总收益
                        BigDecimal earningsSUM = stockAIOrderPositionList.stream()
                                .map(StockAIOrderPosition::getEarnings)  // 获取 BigDecimal 字段
                                .filter(Objects::nonNull)    // 过滤 null 值
                                .reduce(BigDecimal.ZERO, BigDecimal::add); // 累加
                        amount = amount.add(earningsSUM);
                    }
                    //获取用户账户
                    UserAssets userAssets = iUserAssetsServices.assetsByTypeAndUserId(EStockType.MX.getCode(), stockAIOrder.getUserId());
                    iUserAssetsServices.aiAvailableBalanceChange(userAssets, EUserAssets.AI_SETTLEMENT, amount);
                }
                stockAIOrder.setStatus(status);
                stockAIOrder.setAuditDate(new Date());
                stockAiOrderMapper.updateById(stockAIOrder);
                return ServerResponse.createBySuccess("操作成功");
            }
        } catch (Exception e) {
            log.error("StockAiService openPosition error", e);
        }
        return ServerResponse.createByError();
    }
}
src/main/java/com/nq/service/impl/StockServiceImpl.java
@@ -786,6 +786,7 @@
            Map<String, Object> resultMap = new HashMap<>();
            DataStockBean cacheBaseStock = RedisKeyUtil.getCacheBaseStock(stockType, pid);
            if (cacheBaseStock != null) {
                resultMap.put("name", cacheBaseStock.getName());
                resultMap.put("last", cacheBaseStock.getLast());
                resultMap.put("chg", cacheBaseStock.getChg());
                resultMap.put("chgPct", cacheBaseStock.getChgPct());
src/main/java/com/nq/service/impl/UserAssetsServices.java
@@ -300,6 +300,16 @@
            userAssets.setFreezeMoney(userAssets.getFreezeMoney().add(amount));
            //扣除可用金额
            userAssets.setAvailableBalance(userAssets.getAvailableBalance().add(amount.negate()));
        } else if (eUserAssets.getCode().equals(EUserAssets.BUY_AI_REJECT.getCode())) {
            //解除冻结金额
            userAssets.setFreezeMoney(userAssets.getFreezeMoney().add(amount.negate()));
            //归还可用金额
            userAssets.setAvailableBalance(userAssets.getAvailableBalance().add(amount));
        } else if (eUserAssets.getCode().equals(EUserAssets.AI_SETTLEMENT.getCode())) {
            //解除冻结金额
            userAssets.setFreezeMoney(userAssets.getFreezeMoney().add(amount.negate()));
            //归还可用金额 + 收益
            userAssets.setAvailableBalance(userAssets.getAvailableBalance().add(amount));
        }
        String after = userAssets.getAvailableBalance().toString();
        MoneyLog moneyLog = new MoneyLog();
src/main/java/com/nq/vo/stock/ai/StockAiOrderTypeVO.java
New file
@@ -0,0 +1,16 @@
package com.nq.vo.stock.ai;
import com.nq.pojo.StockAIOrder;
import lombok.Data;
@Data
public class StockAiOrderTypeVO extends StockAIOrder {
    //AI交易产品名称
    private String stockName;
    //AI交易产品名称
    private String stockType;
}
src/main/java/com/nq/vo/stock/ai/StockAiOrderVO.java
New file
@@ -0,0 +1,31 @@
package com.nq.vo.stock.ai;
import com.nq.pojo.StockAIOrder;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class StockAiOrderVO extends StockAIOrder {
    //手机号
    private String phone;
    //真实姓名
    private String realName;
    //AI交易产品名称
    private String stockName;
    //股票类型名(所属市场)
    private String stockTypeName;
    //交易成功率
    private BigDecimal successRate;
    //预期收益率
    private BigDecimal expectedEarning;
    //交易周期 天
    private Integer cycle;
    //AI交易产品名称
    private String stockType;
    //货币符号
    private String symbol;
}
src/main/java/com/nq/vo/stock/ai/StockAiVO.java
File was renamed from src/main/java/com/nq/vo/stock/StockAiVO.java
@@ -1,4 +1,4 @@
package com.nq.vo.stock;
package com.nq.vo.stock.ai;
import com.nq.pojo.StockAI;
import lombok.Data;
src/main/resources/application.yml
@@ -91,7 +91,7 @@
  devtools:
    restart:
      # 热部署开关
      enabled: true
      enabled: false
  freemarker:
    cache: false    #false 页面不设置缓存,更改及生效!
src/main/resources/mapper/StockAiMapper.xml
@@ -6,7 +6,7 @@
    id, stock_type, stock_name, min_price, success_rate, expected_earning, cycle, status, create_date
  </sql>
  <select id="getStockAiList" resultType="com.nq.vo.stock.StockAiVO">
  <select id="getStockAiList" resultType="com.nq.vo.stock.ai.StockAiVO">
    SELECT
    <include refid="Base_Column_List" />
    from stock_ai
src/main/resources/mapper/StockAiOrderMapper.xml
New file
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.nq.dao.StockAiOrderMapper" >
  <sql id="Base_Column_List" >
    id, user_id, stock_ai_id, buy_date, buy_amount, remain_amount, real_earning, status, audit_date
  </sql>
  <select id="getAdminStockAiOrderList" resultType="com.nq.vo.stock.ai.StockAiOrderVO">
    SELECT s.id, user_id, stock_ai_id, buy_date, buy_amount, remain_amount, real_earning, s.status, audit_date,
        phone, real_name, stock_name, success_rate, expected_earning, cycle, a.stock_type
    FROM stock_ai_order s
    LEFT JOIN user u ON u.id = s.user_id
    LEFT JOIN stock_ai a ON a.id = s.stock_ai_id
    <where>
      <if test="stockType != null and stockType != ''">
        and a.stock_type = #{stockType}
      </if>
      <if test="status != null and status != ''">
        and s.status = #{status}
      </if>
      <if test="userId != null">
        and u.id = #{userId}
      </if>
      <if test="phone != null and phone != ''">
        and u.phone like concat('%',#{phone},'%')
      </if>
    </where>
    ORDER BY buy_date DESC
  </select>
  <select id="getStockAiOrderList" resultType="com.nq.vo.stock.ai.StockAiOrderTypeVO">
    SELECT s.id, user_id, stock_ai_id, buy_date, buy_amount, remain_amount, real_earning, s.status, audit_date,
    a.stock_name, a.stock_type
    FROM stock_ai_order s
    LEFT JOIN stock_ai a ON a.id = s.stock_ai_id
    <where>
      <if test="status != null and status != ''">
        and s.status = #{status}
      </if>
      <if test="userId != null">
        and user_id = #{userId}
      </if>
    </where>
    ORDER BY buy_date DESC
  </select>
</mapper>