1
zj
2026-01-15 2818327f844e65e1efc398271b7e4ebf9e8d8364
1
9 files modified
289 ■■■■■ changed files
src/main/java/com/nq/controller/StockApiController.java 2 ●●● patch | view | raw | blame | history
src/main/java/com/nq/service/IStockService.java 4 ●●● patch | view | raw | blame | history
src/main/java/com/nq/service/impl/StockServiceImpl.java 136 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/service/impl/UserPendingorderServiceImpl.java 61 ●●●● patch | view | raw | blame | history
src/main/java/com/nq/service/impl/UserStockSubscribeServiceImpl.java 4 ●●●● patch | view | raw | blame | history
src/main/java/com/nq/utils/stock/sina/StockApi.java 2 ●●●●● patch | view | raw | blame | history
src/main/java/com/nq/utils/task/stock/CarryPositionTask.java 76 ●●●● patch | view | raw | blame | history
src/main/resources/application.properties 2 ●●● patch | view | raw | blame | history
src/main/resources/mapper/StockMapper.xml 2 ●●● patch | view | raw | blame | history
src/main/java/com/nq/controller/StockApiController.java
@@ -179,7 +179,7 @@
    @RequestMapping("getPremarketStock.do")
    @ResponseBody
    public ServerResponse getPremarketStock() {
        return this.iStockService.getPremarketStock();
        return ServerResponse.createBySuccess(this.iStockService.getPremarketStock());
    }
}
src/main/java/com/nq/service/IStockService.java
@@ -3,6 +3,8 @@
import com.github.pagehelper.PageInfo;
import com.nq.common.ServerResponse;
import com.nq.pojo.Stock;
import com.nq.vo.stock.PremarketStockVO;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -127,5 +129,5 @@
  ServerResponse getStocksByKeyWords(int pageNum, int pageSize, String keyWords);
  ServerResponse getPremarketStock();
  List<PremarketStockVO>  getPremarketStock();
}
src/main/java/com/nq/service/impl/StockServiceImpl.java
@@ -2,6 +2,7 @@
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
@@ -36,6 +37,7 @@
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -85,6 +87,9 @@
    @Resource
    StockSettingMapper stockSettingMapper;
    @Autowired
    IStockService iStockService;
    public ServerResponse getMarket() {
        String market_url = PropertiesUtil.getProperty("sina.market.url");
@@ -167,6 +172,24 @@
        if (stockList.size() > 0) {
            stockListVOS.addAll(Objects.requireNonNull(StockApi.getStockReailTimes(stockList)));
        }
        List<PremarketStockVO> premarketStock = iStockService.getPremarketStock();
        Map<String, PremarketStockVO> stockMap = premarketStock.stream()
                .collect(Collectors.toMap(
                        PremarketStockVO::getCode,
                        stock -> stock
                ));
        stockListVOS.forEach(f->{
            PremarketStockVO p = stockMap.get(f.getCode());
            if (ObjectUtil.isNotEmpty(p)){
                f.setNowPrice(p.getPrice().toString());
                f.setHcrate(p.getHcrate());
                f.setHcrateP(p.getHcrateP());
            }
        });
        RPageInfo resultPageInfo = new RPageInfo();
        resultPageInfo.setList(stockListVOS);
@@ -913,68 +936,63 @@
    }
    @Override
    public ServerResponse getPremarketStock() {
        try {
            List<StockSetting> stockSettings = stockSettingMapper.selectList(new QueryWrapper<>());
            List<PremarketStockVO> resultList = new ArrayList<>();
            Date now = new Date();
            for (StockSetting setting : stockSettings) {
                String startTime = setting.getStartTime();
                String endTime = setting.getEndTime();
                // 判断当前时间是否在交易时间内(startTime和endTime是完整的日期时间格式)
                if (isTimeInRange(now, startTime, endTime)) {
                    Stock stock = stockMapper.selectOne(new QueryWrapper<Stock>().eq("stock_code", setting.getStockCode()));
                    if (stock == null) {
                        continue; // 股票不存在,跳过
                    }
                    // 获取真实数据,用于获取前收盘价(pc)
                    StockRealTimeBean realTimeStock = RedisKeyUtil.getCacheRealTimeStock(stock);
                    if (realTimeStock == null || realTimeStock.getPc() == null) {
                        continue; // 无法获取真实数据,跳过
                    }
                    // 获取前收盘价(昨收)
                    BigDecimal prevClose = new BigDecimal(realTimeStock.getPc());
                    // 计算盘前价格(根据StockSetting配置)
                    BigDecimal premarketPrice;
                    if ("0".equals(setting.getType())) {
                        // type=0: 直接指定价格
                        premarketPrice = new BigDecimal(setting.getPrice());
                    } else {
                        // type=1: 百分比调价格,基于昨收计算
                        premarketPrice = prevClose.multiply(new BigDecimal(setting.getPrice()));
                    }
                    // 根据盘前价格和昨收计算涨跌幅 = (盘前价格 - 昨收) / 昨收
                    BigDecimal hcrate = BigDecimal.ZERO;
                    if (prevClose.compareTo(BigDecimal.ZERO) > 0) {
                        hcrate = premarketPrice.subtract(prevClose)
                                .divide(prevClose, 4, RoundingMode.HALF_UP);
                    }
                    // 格式化涨跌幅为带百分号的字符串
                    String hcrateP = hcrate.multiply(new BigDecimal("100"))
                            .setScale(2, RoundingMode.HALF_UP) + "%";
                    PremarketStockVO vo = new PremarketStockVO();
                    vo.setCode(setting.getStockCode());
                    vo.setPrice(premarketPrice); // price返回盘前价格
                    vo.setHcrate(hcrate);
                    vo.setHcrateP(hcrateP);
                    resultList.add(vo);
    public List<PremarketStockVO> getPremarketStock() {
        List<StockSetting> stockSettings = stockSettingMapper.selectList(new QueryWrapper<>());
        List<PremarketStockVO> resultList = new ArrayList<>();
        Date now = new Date();
        for (StockSetting setting : stockSettings) {
            String startTime = setting.getStartTime();
            String endTime = setting.getEndTime();
            // 判断当前时间是否在交易时间内(startTime和endTime是完整的日期时间格式)
            if (isTimeInRange(now, startTime, endTime)) {
                Stock stock = stockMapper.selectOne(new QueryWrapper<Stock>().eq("stock_code", setting.getStockCode()));
                if (stock == null) {
                    continue; // 股票不存在,跳过
                }
                // 获取真实数据,用于获取前收盘价(pc)
                StockRealTimeBean realTimeStock = RedisKeyUtil.getCacheRealTimeStock(stock);
                if (realTimeStock == null || realTimeStock.getPc() == null) {
                    continue; // 无法获取真实数据,跳过
                }
                // 获取前收盘价(昨收)
                BigDecimal prevClose = new BigDecimal(realTimeStock.getPc());
                // 计算盘前价格(根据StockSetting配置)
                BigDecimal premarketPrice;
                if ("0".equals(setting.getType())) {
                    // type=0: 直接指定价格
                    premarketPrice = new BigDecimal(setting.getPrice());
                } else {
                    // type=1: 百分比调价格,基于昨收计算
                    premarketPrice = prevClose.multiply(new BigDecimal(setting.getPrice()));
                }
                // 根据盘前价格和昨收计算涨跌幅 = (盘前价格 - 昨收) / 昨收
                BigDecimal hcrate = BigDecimal.ZERO;
                if (prevClose.compareTo(BigDecimal.ZERO) > 0) {
                    hcrate = premarketPrice.subtract(prevClose)
                            .divide(prevClose, 4, RoundingMode.HALF_UP);
                }
                // 格式化涨跌幅为带百分号的字符串
                String hcrateP = hcrate.multiply(new BigDecimal("100"))
                        .setScale(2, RoundingMode.HALF_UP) + "%";
                PremarketStockVO vo = new PremarketStockVO();
                vo.setCode(setting.getStockCode());
                vo.setPrice(premarketPrice); // price返回盘前价格
                vo.setHcrate(hcrate);
                vo.setHcrateP(hcrateP);
                resultList.add(vo);
            }
            return ServerResponse.createBySuccess(resultList);
        } catch (Exception e) {
            log.error("getPremarketStock error", e);
            return ServerResponse.createByErrorMsg("查询盘前交易数据失败");
        }
        return resultList;
    }
    /**
src/main/java/com/nq/service/impl/UserPendingorderServiceImpl.java
@@ -5,7 +5,10 @@
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -23,6 +26,7 @@
import com.nq.vo.foreigncurrency.ExchangeVO;
import com.nq.vo.position.UserPendingorderVO;
import com.nq.vo.stock.MarketVO;
import com.nq.vo.stock.PremarketStockVO;
import com.nq.vo.stock.StockListVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@@ -83,6 +87,9 @@
    @Autowired
    private UserPositionMapper userPositionMapper;
    @Autowired
    IStockService iStockService;
    @Override
    @org.springframework.transaction.annotation.Transactional(rollbackFor = Exception.class)
@@ -253,7 +260,18 @@
        // 查询所有待处理的挂单
        List<UserPendingorder> userPendingorders = userPendingorderMapper.selectList(new QueryWrapper<UserPendingorder>().eq("status", 0));
        log.info("当前有挂单数量:{}", userPendingorders.size());
        List<PremarketStockVO> premarketStock = iStockService.getPremarketStock();
        Map<String, PremarketStockVO> stockMap = premarketStock.stream()
                .collect(Collectors.toMap(
                        PremarketStockVO::getCode,
                        stock -> stock
                ));
        orderLoop: for (UserPendingorder userPendingorder : userPendingorders) {
            try {
                // 参数校验
@@ -271,25 +289,36 @@
                    this.userPendingorderMapper.updateById(userPendingorder);
                continue;
            }
                PremarketStockVO p = stockMap.get(stock.getStockCode());
                // 获取当前价格
                BigDecimal nowPrice = priceServices.getNowPrice(stock.getStockCode());
                if (nowPrice.compareTo(BigDecimal.ZERO) == 0) {
                    continue;
                if(ObjectUtil.isEmpty(p)){
                    if (nowPrice.compareTo(BigDecimal.ZERO) == 0) {
                        continue;
                    }
                    // 判断价格是否达到目标价格:当前价格 <= 挂单价就买入
                    if (nowPrice.compareTo(userPendingorder.getTargetPrice()) > 0) {
                        // 当前价格大于挂单价,继续等待
                        continue;
                    }
                    // 价格达到目标价格,先进行验证
                    // 判断股票是否在可交易时间段
                    Boolean b = tradingHourService.timeCheck(stock.getStockCode(), stock.getStockType());
                    if (!b) {
                        continue;
                    }
                }else{
                    // 判断价格是否达到目标价格:当前价格 <= 挂单价就买入
                    if (p.getPrice().compareTo(userPendingorder.getTargetPrice()) > 0) {
                        // 当前价格大于挂单价,继续等待
                        continue;
                    }
                    nowPrice = p.getPrice();
                }
                // 判断价格是否达到目标价格:当前价格 <= 挂单价就买入
                if (nowPrice.compareTo(userPendingorder.getTargetPrice()) > 0) {
                    // 当前价格大于挂单价,继续等待
                    continue;
                }
                // 价格达到目标价格,先进行验证
                // 判断股票是否在可交易时间段
                Boolean b = tradingHourService.timeCheck(stock.getStockCode(), stock.getStockType());
                if (!b) {
                    continue;
                }
                // 检查股票是否被锁定
                if (stock.getIsLock() != 0) {
src/main/java/com/nq/service/impl/UserStockSubscribeServiceImpl.java
@@ -321,8 +321,8 @@
                    }
                    ret = userStockSubscribeMapper.update1(model);
                    userAssetsMapper.updateById(userAssets);
                    iUserPositionService.newStockToPosition(model.getId(),userAssets.getAmountToBeCovered());//转持仓
                    model.setStatus(5);
//                    iUserPositionService.newStockToPosition(model.getId(),userAssets.getAmountToBeCovered());//转持仓
//                    model.setStatus(5);
                }else{
                    if(model.getApplyNumber()>model.getApplyNums()){
                        return  ServerResponse.createByErrorMsg("配置中签数量不能超过申请数量",request);
src/main/java/com/nq/utils/stock/sina/StockApi.java
@@ -34,7 +34,9 @@
        }
        List<StockListVO> stockListVOs = new ArrayList<>();
        try {
            for (int i = 0; i < stocks.size(); i++) {
                Stock stock = stocks.get(i);
                StockRealTimeBean realTimeStock = RedisKeyUtil.getCacheRealTimeStock(stock);
                StockListVO stockListVO = new StockListVO();
src/main/java/com/nq/utils/task/stock/CarryPositionTask.java
@@ -61,44 +61,44 @@
    private final AtomicBoolean ballot = new AtomicBoolean(false);
    @Scheduled(cron = "0 0/1 * * * ?")
    public void ballot() {
        if (ballot.get()) { // 判断任务是否在处理中
            return;
        }
        if (ballotLock.tryLock()) {
            try {
                ballot.set(true); // 设置处理中标识为true
//                log.info("新股上市转持仓定时任务--------->开始");
                log.info("新股上市定时任务--------->开始");
                List<StockSubscribe> stockSubscribes = stockSubscribeMapper.selectList(new LambdaQueryWrapper<StockSubscribe>()
                        .lt(StockSubscribe::getListDate, DateUtil.date()));
                if (CollectionUtils.isNotEmpty(stockSubscribes)) {
                    List<String> codeList = stockSubscribes.stream().map(StockSubscribe::getCode).collect(Collectors.toList());
                    List<UserStockSubscribe> userStockSubscribes = userStockSubscribeMapper.selectList(new LambdaQueryWrapper<UserStockSubscribe>()
                            .eq(UserStockSubscribe::getStatus, 4).in(UserStockSubscribe::getNewCode, codeList));
                    //订单转持仓
                    userStockSubscribes.forEach(f -> {
                        ServerResponse serverResponse = iUserPositionService.newStockToPosition(f.getId(),BigDecimal.ZERO);//转持仓
                        if(serverResponse.isSuccess()){
                            f.setStatus(5);
                            userStockSubscribeMapper.updateById(f);
                        }else{
                            log.info("新股上市定时任务失败申购订单id:"+f.getId()+",失败原因:"+serverResponse.getMsg());
                        }
                    });
                }
                log.info("新股上市定时任务--------->结束");
            } catch (Exception e) {
                log.error("新股上市定时任务发生异常", e);
            } finally {
                ballotLock.unlock();
                ballot.set(false); // 设置处理中标识为false
            }
        } else {
            log.info("新股上市定时任务--------->上次任务还未执行完成,本次任务忽略");
        }
    }
//    @Scheduled(cron = "0 0/1 * * * ?")
//    public void ballot() {
//        if (ballot.get()) { // 判断任务是否在处理中
//            return;
//        }
//        if (ballotLock.tryLock()) {
//            try {
//                ballot.set(true); // 设置处理中标识为true
////                log.info("新股上市转持仓定时任务--------->开始");
//                log.info("新股上市定时任务--------->开始");
//                List<StockSubscribe> stockSubscribes = stockSubscribeMapper.selectList(new LambdaQueryWrapper<StockSubscribe>()
//                        .lt(StockSubscribe::getListDate, DateUtil.date()));
//                if (CollectionUtils.isNotEmpty(stockSubscribes)) {
//                    List<String> codeList = stockSubscribes.stream().map(StockSubscribe::getCode).collect(Collectors.toList());
//                    List<UserStockSubscribe> userStockSubscribes = userStockSubscribeMapper.selectList(new LambdaQueryWrapper<UserStockSubscribe>()
//                            .eq(UserStockSubscribe::getStatus, 4).in(UserStockSubscribe::getNewCode, codeList));
//                    //订单转持仓
//                    userStockSubscribes.forEach(f -> {
//                        ServerResponse serverResponse = iUserPositionService.newStockToPosition(f.getId(),BigDecimal.ZERO);//转持仓
//                        if(serverResponse.isSuccess()){
//                            f.setStatus(5);
//                            userStockSubscribeMapper.updateById(f);
//                        }else{
//                            log.info("新股上市定时任务失败申购订单id:"+f.getId()+",失败原因:"+serverResponse.getMsg());
//                        }
//                    });
//                }
//                log.info("新股上市定时任务--------->结束");
//            } catch (Exception e) {
//                log.error("新股上市定时任务发生异常", e);
//            } finally {
//                ballotLock.unlock();
//                ballot.set(false); // 设置处理中标识为false
//            }
//        } else {
//            log.info("新股上市定时任务--------->上次任务还未执行完成,本次任务忽略");
//        }
//    }
    private final AtomicBoolean subscription = new AtomicBoolean(false);
src/main/resources/application.properties
@@ -46,7 +46,7 @@
JP_HTTP_API = http://api-jp.js-stock.top/
JP_WS_URL = ws://api-jp-ws.js-stock.top
JP_KEY = 43zGhZNUYT5lwsmEenUO
JP_KEY = ZDX1s51GoJhoeuajaDpx
#默认首页显示指数code
#us_home_indices_code=15882,15881,16571
#hk_home_indices_code=535606773,535606776,535606785
src/main/resources/mapper/StockMapper.xml
@@ -319,7 +319,7 @@
        <include refid="Base_Column_List"/>
         FROM stock
    where 1=1
    where 1=1 and is_show = 0
    <if test="stockType != null and stockType != '' ">
      and stock_type = #{stockType}
    </if>