package com.nq.service.impl; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; import java.util.Date; import java.util.List; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import com.nq.common.ServerResponse; import com.nq.dao.*; import com.nq.pojo.*; import com.nq.service.*; import com.nq.utils.timeutil.DateTimeUtil; import com.nq.utils.PropertiesUtil; import com.nq.utils.redis.JsonUtil; import com.nq.utils.redis.RedisShardedPoolUtils; import com.nq.utils.stock.sina.StockApi; import com.nq.vo.foreigncurrency.ExchangeVO; import com.nq.vo.position.UserPendingorderVO; import com.nq.vo.stock.MarketVO; import com.nq.vo.stock.StockListVO; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; /** * @author Administrator * @description 针对表【user_pendingorder】的数据库操作Service实现 * @createDate 2022-11-10 06:10:40 */ @Service @Slf4j public class UserPendingorderServiceImpl extends ServiceImpl implements UserPendingorderService { @Autowired private UserPendingorderMapper userPendingorderMapper; @Autowired private StockMapper stockMapper; @Autowired private IUserService iUserService; @Autowired private IUserPositionService iUserPositionService; @Autowired private SiteTaskLogMapper siteTaskLogMapper; @Autowired private UserMapper userMapper; @Autowired private StockIndexMapper stockIndexMapper; @Autowired private IStockIndexService iStockIndexService; @Autowired private IUserIndexPositionService iUserIndexPositionService; @Autowired private IStockFuturesService iStockFuturesService; @Autowired private ISiteSettingService iSiteSettingService; @Autowired private IUserAssetsServices iUserAssetsServices; @Autowired private ISiteProductService iSiteProductService; @Autowired private IStockConfigServices iStockConfigServices; @Autowired private StockBuySettingMapper stockBuySettingMapper; @Autowired private ITradingHourService tradingHourService; @Autowired private IPriceServices priceServices; @Autowired private UserPositionMapper userPositionMapper; @Override @org.springframework.transaction.annotation.Transactional(rollbackFor = Exception.class) public ServerResponse addOrder(String stockId, Integer buyNum, Integer buyType, Integer lever, BigDecimal profitTarget, BigDecimal stopTarget, BigDecimal targetPrice, HttpServletRequest request) { User user = this.iUserService.getCurrentRefreshUser(request); if (user == null) { return ServerResponse.createByErrorMsg("请先登录", request); } try { synchronized (user.getId()) { // 获取系统设置 SiteProduct siteProduct = iSiteProductService.getProductSetting(); if (siteProduct.getRealNameDisplay() && user.getIsActive() != 2) { return ServerResponse.createByErrorMsg("挂单失败,请先实名认证", request); } if (siteProduct.getRealNameDisplay() && user.getIsLock().intValue() == 1) { return ServerResponse.createByErrorMsg("挂单失败,帐户已被锁定", request); } // 验证目标价格 if (targetPrice == null || targetPrice.compareTo(BigDecimal.ZERO) <= 0) { return ServerResponse.createByErrorMsg("挂单失败,目标价格必须大于0", request); } // 获取股票信息 Stock stock = stockMapper.findStockByCode(stockId); if (stock == null) { return ServerResponse.createByErrorMsg("挂单失败,股票代码不存在", request); } // 判断股票是否被锁定 if (stock.getIsLock() != 0) { return ServerResponse.createByErrorMsg("挂单失败,股票被锁定", request); } // 手续费率 BigDecimal siteSettingBuyFee = new BigDecimal(iStockConfigServices.queryByKey(com.nq.enums.EConfigKey.BUY_HANDLING_CHARGE.getCode()).getCValue()); // 处理购买数量(手数转换) StockBuySetting stockBuySetting = stockBuySettingMapper.selectOne(new QueryWrapper().eq("accets_type", stock.getStockType())); if (stockBuySetting != null && stockBuySetting.getHandsNum() != null && stockBuySetting.getStockNum() != null) { if (buyNum < stockBuySetting.getHandsNum()) { return ServerResponse.createByErrorMsg("最低购买手数" + stockBuySetting.getHandsNum(), request); } buyNum = buyNum * stockBuySetting.getStockNum(); } // 获取用户资产 UserAssets userAssets = iUserAssetsServices.assetsByTypeAndUserId(stock.getStockType(), user.getId()); // 验证最高购买数量 com.nq.pojo.StockConfig maxBuyConfig = iStockConfigServices.queryByKey(com.nq.enums.EConfigKey.MAX_BUY.getCode()); if (buyNum > Integer.parseInt(maxBuyConfig.getCValue())) { return ServerResponse.createByErrorMsg("最高购买数量" + maxBuyConfig.getCValue(), request); } // 检查待补资金 if (userAssets.getAmountToBeCovered().compareTo(BigDecimal.ZERO) > 0) { return ServerResponse.createByErrorMsg("请先缴清待补资金", request); } // 使用目标价格计算需要冻结的金额 BigDecimal buyAmt = targetPrice.multiply(new BigDecimal(buyNum)).divide(new BigDecimal(lever), 4, RoundingMode.HALF_UP); // 手续费 BigDecimal orderFree = siteSettingBuyFee.multiply(buyAmt); BigDecimal needFreezeAmt = buyAmt.add(orderFree); // 资金校验(考虑配资比例) BigDecimal fundratio = new BigDecimal(user.getFundRatio()).divide(new BigDecimal(100)); BigDecimal availableBalance = fundratio.multiply(userAssets.getAvailableBalance()); if (availableBalance.compareTo(needFreezeAmt) < 0) { return ServerResponse.createByErrorMsg("挂单失败,配资不足", request); } // 检查是否已有相同股票的挂单 UserPendingorder existingOrder = userPendingorderMapper.selectOne(new QueryWrapper() .eq("user_id", user.getId()) .eq("stock_id", stockId) .eq("status", 0)); if (existingOrder != null) { return ServerResponse.createByErrorMsg("该股票已有挂单,请勿重复挂单", request); } // 创建挂单记录 UserPendingorder userPendingorder = new UserPendingorder(); userPendingorder.setUserId(user.getId()); userPendingorder.setStockId(stockId); userPendingorder.setBuyNum(buyNum); userPendingorder.setBuyType(buyType); userPendingorder.setLever(lever); userPendingorder.setProfitTarget(profitTarget); userPendingorder.setStopTarget(stopTarget); userPendingorder.setNowPrice(new BigDecimal(0)); userPendingorder.setTargetPrice(targetPrice); userPendingorder.setAddTime(new Date()); userPendingorder.setStatus(0); int ret = userPendingorderMapper.insert(userPendingorder); if (ret > 0) { // 冻结资金 iUserAssetsServices.availablebalanceChange(stock.getStockType(), user.getId(), com.nq.enums.EUserAssets.PENDING_ORDER_FREEZE, needFreezeAmt.negate(), "挂单冻结资金", ""); return ServerResponse.createBySuccessMsg("挂单成功,资金已冻结", request); } return ServerResponse.createByErrorMsg("挂单失败", request); } } catch (Exception e) { log.error("挂单异常:{}", e.getMessage(), e); return ServerResponse.createByErrorMsg("挂单异常:" + e.getMessage(), request); } } @Override public ServerResponse orderList(HttpServletRequest request) { String property = PropertiesUtil.getProperty("user.cookie.name"); String header = request.getHeader(property); // log.info("header:{}",header); if (header != null) { String userJson = RedisShardedPoolUtils.get(header); User user = (User) JsonUtil.string2Obj(userJson, User.class); // log.info("user:{}",user); if (user != null) { List userPendingorders = userPendingorderMapper.selectList(new QueryWrapper().eq("user_id", user.getId()).eq("status",0)); List UserPendingorderList = new ArrayList(); for (UserPendingorder userPendingorder : userPendingorders) { UserPendingorderVO userPendingorderVO = new UserPendingorderVO(); //挂单-指数 //挂单-股票 Stock stock = stockMapper.findStockByCode(userPendingorder.getStockId()); StockListVO stockListVO = new StockListVO(); stockListVO = StockApi.getStockRealTime( stock); String nowPrice = stockListVO.getNowPrice(); if (nowPrice == null) { nowPrice = String.valueOf(0); } userPendingorderVO.setNowPrice(new BigDecimal(nowPrice)); userPendingorderVO.setStockName(stock.getStockName()); userPendingorderVO.setStockGid(stock.getStockType()); userPendingorderVO.setStockId(stock.getStockCode()); userPendingorderVO.setBuyNum(userPendingorder.getBuyNum()); userPendingorderVO.setBuyType(userPendingorder.getBuyType()); userPendingorderVO.setLever(userPendingorder.getLever()); userPendingorderVO.setProfitTarget(userPendingorder.getProfitTarget()); userPendingorderVO.setStopTarget(userPendingorder.getStopTarget()); userPendingorderVO.setTargetPrice(userPendingorder.getTargetPrice()); userPendingorderVO.setAddTime(userPendingorder.getAddTime()); userPendingorderVO.setStatus(userPendingorder.getStatus()); userPendingorderVO.setId(userPendingorder.getId()); UserPendingorderList.add(userPendingorderVO); } return ServerResponse.createBySuccess(UserPendingorderList); } } return ServerResponse.createByErrorMsg("数据异常"); } @Override public void orderTask() { // 查询所有待处理的挂单 List userPendingorders = userPendingorderMapper.selectList(new QueryWrapper().eq("status", 0)); log.info("当前有挂单数量:{}", userPendingorders.size()); orderLoop: for (UserPendingorder userPendingorder : userPendingorders) { try { // 参数校验 if (userPendingorder.getUserId() == null || userPendingorder.getStockId() == null || userPendingorder.getBuyNum() == null || userPendingorder.getBuyType() == null || userPendingorder.getLever() == null || userPendingorder.getTargetPrice() == null) { continue; } // 获取股票信息 Stock stock = stockMapper.findStockByCode(userPendingorder.getStockId()); if (stock == null) { log.error("挂单转持仓失败,股票不存在:{}", userPendingorder.getStockId()); userPendingorder.setStatus(2); this.userPendingorderMapper.updateById(userPendingorder); continue; } // 获取当前价格 BigDecimal nowPrice = priceServices.getNowPrice(stock.getStockCode()); 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; } // 检查股票是否被锁定 if (stock.getIsLock() != 0) { log.info("挂单转持仓失败,股票被锁定:{}", stock.getStockCode()); userPendingorder.setStatus(2); this.userPendingorderMapper.updateById(userPendingorder); continue; } // 检查是否有配额 if (!priceServices.isLimitUpBuy(stock.getStockCode())) { continue; } // 获取用户信息 User user = this.userMapper.selectById(userPendingorder.getUserId()); if (user == null) { continue; } // 执行买入逻辑(在 synchronized 块内) synchronized (userPendingorder.getUserId()) { // 再次检查挂单状态,防止并发处理 UserPendingorder checkOrder = userPendingorderMapper.selectById(userPendingorder.getId()); if (checkOrder == null || checkOrder.getStatus() != 0) { // 挂单已被处理,跳过本次循环 continue orderLoop; } // 手续费率 BigDecimal siteSettingBuyFee = new BigDecimal(iStockConfigServices.queryByKey(com.nq.enums.EConfigKey.BUY_HANDLING_CHARGE.getCode()).getCValue()); // 计算买入金额和手续费(使用当前价格) BigDecimal buyAmt = nowPrice.multiply(new BigDecimal(userPendingorder.getBuyNum())).divide(new BigDecimal(userPendingorder.getLever()), 4, RoundingMode.HALF_UP); BigDecimal orderFree = siteSettingBuyFee.multiply(buyAmt); // 计算挂单时冻结的金额(用于解冻) BigDecimal targetBuyAmt = userPendingorder.getTargetPrice().multiply(new BigDecimal(userPendingorder.getBuyNum())).divide(new BigDecimal(userPendingorder.getLever()), 4, RoundingMode.HALF_UP); BigDecimal targetOrderFree = siteSettingBuyFee.multiply(targetBuyAmt); BigDecimal freezeAmt = targetBuyAmt.add(targetOrderFree); // 先解冻挂单冻结的资金 iUserAssetsServices.availablebalanceChange(stock.getStockType(), userPendingorder.getUserId(), com.nq.enums.EUserAssets.PENDING_ORDER_UNFREEZE, freezeAmt, "挂单触发解冻资金", ""); // 创建 UserPosition,与 buy.do 逻辑完全一致 UserPosition userPosition = new UserPosition(); if (userPendingorder.getProfitTarget() != null && userPendingorder.getProfitTarget().compareTo(BigDecimal.ZERO) > 0) { userPosition.setProfitTargetPrice(userPendingorder.getProfitTarget()); } if (userPendingorder.getStopTarget() != null && userPendingorder.getStopTarget().compareTo(BigDecimal.ZERO) > 0) { userPosition.setStopTargetPrice(userPendingorder.getStopTarget()); } userPosition.setPositionType(user.getAccountType()); userPosition.setPositionSn(com.nq.utils.KeyUtils.getUniqueKey()); userPosition.setUserId(userPendingorder.getUserId()); userPosition.setNickName(user.getRealName()); userPosition.setAgentId(user.getAgentId()); userPosition.setStockCode(stock.getStockCode()); userPosition.setStockName(stock.getStockName()); userPosition.setStockGid(stock.getStockType()); userPosition.setStockSpell(stock.getStockSpell()); userPosition.setBuyOrderId(com.nq.utils.stock.GeneratePosition.getPositionId()); userPosition.setBuyOrderTime(new Date()); userPosition.setBuyOrderPrice(nowPrice); userPosition.setOrderDirection((userPendingorder.getBuyType().intValue() == 0) ? "买涨" : "买跌"); userPosition.setOrderNum(userPendingorder.getBuyNum()); if (stock.getStockPlate() != null) { userPosition.setStockPlate(stock.getStockPlate()); } userPosition.setIsLock(Integer.valueOf(0)); userPosition.setOrderLever(userPendingorder.getLever()); userPosition.setOrderTotalPrice(buyAmt); userPosition.setOrderFee(orderFree); userPosition.setOrderSpread(BigDecimal.ZERO); userPosition.setSpreadRatePrice(BigDecimal.ZERO); BigDecimal profit_and_lose = new BigDecimal("0"); userPosition.setProfitAndLose(profit_and_lose); userPosition.setAllProfitAndLose(profit_and_lose.add(orderFree)); userPosition.setOrderStayDays(Integer.valueOf(0)); userPosition.setOrderStayFee(BigDecimal.ZERO); // 插入持仓记录 userPositionMapper.insert(userPosition); // 扣款和手续费,与 buy.do 完全一致 iUserAssetsServices.availablebalanceChange(stock.getStockType(), userPendingorder.getUserId(), com.nq.enums.EUserAssets.BUY, buyAmt.negate(), "", ""); iUserAssetsServices.availablebalanceChange(stock.getStockType(), userPendingorder.getUserId(), com.nq.enums.EUserAssets.HANDLING_CHARGE, orderFree, "", ""); // 更新挂单状态 userPendingorder.setStatus(1); this.userPendingorderMapper.updateById(userPendingorder); log.info("挂单转持仓成功,挂单id:{},股票:{}", userPendingorder.getId(), stock.getStockCode()); } } catch (Exception e) { log.error("挂单转持仓失败,挂单id:{}", userPendingorder.getId(), e); userPendingorder.setStatus(2); this.userPendingorderMapper.updateById(userPendingorder); } } log.info("===========挂单任务结束=========="); } //删除 @Override @org.springframework.transaction.annotation.Transactional(rollbackFor = Exception.class) public ServerResponse delOrder(Integer id, HttpServletRequest request) { User user = this.iUserService.getCurrentRefreshUser(request); if (user == null) { return ServerResponse.createByErrorMsg("请先登录", request); } try { UserPendingorder userPendingorder = this.userPendingorderMapper.selectById(id); if (userPendingorder == null) { return ServerResponse.createByErrorMsg("挂单不存在", request); } if (user.getId().intValue() != userPendingorder.getUserId().intValue()) { return ServerResponse.createByErrorMsg("该挂单不属于您", request); } // 检查挂单状态,只有待处理的挂单才能取消 if (userPendingorder.getStatus() != 0) { return ServerResponse.createByErrorMsg("该挂单已处理,无法取消", request); } // 获取股票信息以确定资产类型 Stock stock = stockMapper.findStockByCode(userPendingorder.getStockId()); if (stock == null) { return ServerResponse.createByErrorMsg("股票不存在", request); } // 计算需要解冻的金额(根据挂单参数重新计算) BigDecimal siteSettingBuyFee = new BigDecimal(iStockConfigServices.queryByKey(com.nq.enums.EConfigKey.BUY_HANDLING_CHARGE.getCode()).getCValue()); BigDecimal buyAmt = userPendingorder.getTargetPrice().multiply(new BigDecimal(userPendingorder.getBuyNum())).divide(new BigDecimal(userPendingorder.getLever()), 4, RoundingMode.HALF_UP); BigDecimal orderFree = siteSettingBuyFee.multiply(buyAmt); BigDecimal needUnfreezeAmt = buyAmt.add(orderFree); // 解冻资金 iUserAssetsServices.availablebalanceChange(stock.getStockType(), user.getId(), com.nq.enums.EUserAssets.PENDING_ORDER_UNFREEZE, needUnfreezeAmt, "取消挂单解冻资金", ""); // 删除挂单记录 int delCount = this.userPendingorderMapper.deleteById(id); if (delCount > 0) { return ServerResponse.createBySuccessMsg("取消挂单成功,资金已解冻", request); } return ServerResponse.createByErrorMsg("取消挂单失败", request); } catch (Exception e) { log.error("取消挂单异常:{}", e.getMessage(), e); return ServerResponse.createByErrorMsg("取消挂单异常:" + e.getMessage(), request); } } @Override public ServerResponse orderListByAdmin(int pageNum, int pageSize, String keywords, String status, HttpServletRequest request) { PageHelper.startPage(pageNum, pageSize); QueryWrapper queryWrapper = new QueryWrapper(); if (keywords != null && !keywords.equals("")) { queryWrapper.like("stock_id", keywords).or().like("user_id", keywords); } if (status != null && !status.equals("")) { queryWrapper.eq("status", status); } queryWrapper.orderByDesc("id"); List stockSubscribeList = this.userPendingorderMapper.selectList(queryWrapper); List UserPendingorderList = new ArrayList(); for (UserPendingorder userPendingorder : stockSubscribeList) { UserPendingorderVO userPendingorderVO = new UserPendingorderVO(); //挂单-指数 if (userPendingorder.getStockId().contains("sh") || userPendingorder.getStockId().contains("sz") || userPendingorder.getStockId().contains("hk") || userPendingorder.getStockId().contains("us")) { StockIndex model = stockIndexMapper.selectIndexByCode(userPendingorder.getStockId().replace("sh", "").replace("sz", "").replace("hk", "").replace("us", "")); MarketVO marketVO = this.iStockIndexService.querySingleIndex(model.getIndexGid()); userPendingorderVO.setNowPrice(new BigDecimal(marketVO.getNowPrice())); userPendingorderVO.setStockName(model.getIndexName()); userPendingorderVO.setStockId(model.getIndexGid()); } else { //挂单-股票 Stock stock = stockMapper.findStockByCode(userPendingorder.getStockId()); StockListVO stockListVO = new StockListVO(); if (stock.getStockType().equals("hk")) { String hk = RedisShardedPoolUtils.get(stock.getStockGid(), 1); stockListVO = StockApi.otherStockListVO(hk); } else if (stock.getStockType().equals("us")) { String us = RedisShardedPoolUtils.get(stock.getStockGid(), 2); stockListVO = StockApi.otherStockListVO(us); } else { stockListVO = StockApi.getStockRealTime( stock); } String nowPrice = stockListVO.getNowPrice(); if (nowPrice == null) { nowPrice = String.valueOf(0); } userPendingorderVO.setNowPrice(new BigDecimal(nowPrice)); userPendingorderVO.setStockName(stock.getStockName()); userPendingorderVO.setStockId(stock.getStockCode()); } userPendingorderVO.setBuyNum(userPendingorder.getBuyNum()); userPendingorderVO.setBuyType(userPendingorder.getBuyType()); userPendingorderVO.setLever(userPendingorder.getLever()); userPendingorderVO.setProfitTarget(userPendingorder.getProfitTarget()); userPendingorderVO.setStopTarget(userPendingorder.getStopTarget()); userPendingorderVO.setTargetPrice(userPendingorder.getTargetPrice()); userPendingorderVO.setAddTime(userPendingorder.getAddTime()); userPendingorderVO.setStatus(userPendingorder.getStatus()); userPendingorderVO.setId(userPendingorder.getId()); UserPendingorderList.add(userPendingorderVO); } PageInfo pageInfo = new PageInfo(stockSubscribeList); pageInfo.setList(UserPendingorderList); return ServerResponse.createBySuccess(pageInfo); } @Override public ServerResponse updateOrderByAdmin(UserPendingorder userPendingorder) { if (userPendingorder.getId() == null) { return ServerResponse.createByErrorMsg("id不能为空"); } int updateCount = this.userPendingorderMapper.updateById(userPendingorder); if (updateCount > 0) { return ServerResponse.createBySuccessMsg("修改成功"); } return ServerResponse.createByErrorMsg("修改失败"); } @Override public ServerResponse addOrderByAdmin(String phone, String buyNum, String code, String buyType, String lever, String targetPrice, HttpServletRequest request) { if (StringUtils.isBlank(phone) || StringUtils.isBlank(buyNum) || StringUtils.isBlank(buyType) || StringUtils.isBlank(lever) || StringUtils.isBlank(targetPrice)) { return ServerResponse.createByErrorMsg("参数不能为空"); } User user = this.userMapper.selectOne(new QueryWrapper().eq("phone", phone)); if (user == null) { return ServerResponse.createByErrorMsg("用户不存在"); } Stock stock = stockMapper.findStockByCode(code); if (stock == null) { return ServerResponse.createByErrorMsg("股票不存在"); } UserPendingorder userPendingorder = new UserPendingorder(); userPendingorder.setUserId(user.getId()); userPendingorder.setStockId(code); userPendingorder.setBuyNum(Integer.valueOf(buyNum)); userPendingorder.setBuyType(Integer.valueOf(buyType)); userPendingorder.setLever(Integer.valueOf(lever)); userPendingorder.setTargetPrice(new BigDecimal(targetPrice)); userPendingorder.setAddTime(new Date()); userPendingorder.setStatus(0); userPendingorder.setNowPrice(new BigDecimal(0)); int insert = this.userPendingorderMapper.insert(userPendingorder); if (insert > 0) { return ServerResponse.createBySuccessMsg("添加成功"); } return ServerResponse.createByErrorMsg("添加失败"); } @Override public ServerResponse delOrderByAdmin(Integer id) { if (id == null) { return ServerResponse.createByErrorMsg("id不能为空"); } int delete = this.userPendingorderMapper.deleteById(id); if (delete > 0) { return ServerResponse.createBySuccessMsg("删除成功"); } return ServerResponse.createByErrorMsg("删除失败"); } }