From 74f2dd24eb7241020319771c794d9e9f06264d7e Mon Sep 17 00:00:00 2001
From: zj <1772600164@qq.com>
Date: Tue, 13 Jan 2026 18:49:06 +0800
Subject: [PATCH] 1
---
src/main/java/com/nq/service/impl/UserStockSubscribeServiceImpl.java | 13
src/main/java/com/nq/utils/task/stock/CarryPositionTask.java | 9
src/main/resources/mapper/StockMapper.xml | 2
src/main/java/com/nq/service/impl/UserAssetsServices.java | 14 +
src/main/java/com/nq/controller/protol/UserController.java | 15 +
src/main/java/com/nq/controller/protol/UserWithdrawController.java | 8
src/main/java/com/nq/service/impl/UserPositionServiceImpl.java | 2
src/main/java/com/nq/utils/task/stock/PendingOrderTask.java | 53 ++++
src/main/resources/application.properties | 2
src/main/java/com/nq/ws/WebSocketClientBeanConfig.java | 36 +-
src/main/java/com/nq/enums/EUserAssets.java | 2
src/main/java/com/nq/service/impl/UserWithdrawServiceImpl.java | 17
src/main/java/com/nq/vo/position/UserPendingorderVO.java | 3
src/main/java/com/nq/service/impl/UserPendingorderServiceImpl.java | 465 +++++++++++++++++++++++++++--------------
src/main/resources/application.yml | 2
15 files changed, 432 insertions(+), 211 deletions(-)
diff --git a/src/main/java/com/nq/controller/protol/UserController.java b/src/main/java/com/nq/controller/protol/UserController.java
index 404d779..1b6461a 100644
--- a/src/main/java/com/nq/controller/protol/UserController.java
+++ b/src/main/java/com/nq/controller/protol/UserController.java
@@ -1,6 +1,9 @@
package com.nq.controller.protol;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.crypto.Mode;
+import cn.hutool.crypto.Padding;
import com.google.common.collect.Maps;
import com.nq.common.ServerResponse;
import com.nq.enums.EStockType;
@@ -18,6 +21,7 @@
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
+import com.nq.utils.SymmetricCryptoUtil;
import com.nq.vo.stock.UserStockSubscribeAddIn;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -425,6 +429,17 @@
return iApplyLeverServices.applyLever(applyLever,request);
}
+ @RequestMapping({"getPassword.do"})
+ @ResponseBody
+ public ServerResponse getPassword(HttpServletRequest request) {
+ User user = this.iUserService.getCurrentRefreshUser(request);
+ if(ObjectUtil.isEmpty(user)){
+ return ServerResponse.createByErrorMsg("获取用信息失败,请重新登录!",request);
+ }
+ String pwd = SymmetricCryptoUtil.decryptFromString(user.getUserPwd(), Mode.CBC, Padding.ZeroPadding);
+ return ServerResponse.createBySuccess(pwd);
+ }
+
/**
* 充值第三方支付
*/
diff --git a/src/main/java/com/nq/controller/protol/UserWithdrawController.java b/src/main/java/com/nq/controller/protol/UserWithdrawController.java
index fa607d8..5b8c926 100644
--- a/src/main/java/com/nq/controller/protol/UserWithdrawController.java
+++ b/src/main/java/com/nq/controller/protol/UserWithdrawController.java
@@ -61,9 +61,9 @@
requestTimestamps.put(requestId, System.currentTimeMillis());
try {
- if (!isIntegerGreaterThan100(amt)) {
- return ServerResponse.createByErrorMsg("请输入整数!",request);
- }
+// if (!isIntegerGreaterThan100(amt)) {
+// return ServerResponse.createByErrorMsg("请输入整数!",request);
+// }
synchronized (user.getId()){
serverResponse = this.iUserWithdrawService.outMoney(amt, user.getWithPwd(), accsetType,bankId,request);
}
@@ -87,7 +87,7 @@
public static boolean isIntegerGreaterThan100(String str) {
try {
int number = Integer.parseInt(str); // 尝试将字符串转换为整数
- return number > 100; // 判断是否大于100
+ return number >= 100; // 判断是否大于100
} catch (NumberFormatException e) {
return false; // 如果转换失败,说明不是整数
}
diff --git a/src/main/java/com/nq/enums/EUserAssets.java b/src/main/java/com/nq/enums/EUserAssets.java
index d4f6cf4..a751c3a 100644
--- a/src/main/java/com/nq/enums/EUserAssets.java
+++ b/src/main/java/com/nq/enums/EUserAssets.java
@@ -23,6 +23,8 @@
DK("DK","发放贷款"),
RT_DK("RT_DK","归还贷款"),
RT_DK_INT("RT_DK_INT","归还贷款利息"),
+ PENDING_ORDER_FREEZE("PENDING_ORDER_FREEZE","挂单冻结资金"),
+ PENDING_ORDER_UNFREEZE("PENDING_ORDER_UNFREEZE","取消挂单解冻资金"),
;
private String code;
diff --git a/src/main/java/com/nq/service/impl/UserAssetsServices.java b/src/main/java/com/nq/service/impl/UserAssetsServices.java
index 85bcaa8..975cc10 100644
--- a/src/main/java/com/nq/service/impl/UserAssetsServices.java
+++ b/src/main/java/com/nq/service/impl/UserAssetsServices.java
@@ -320,6 +320,16 @@
userAssets.setCumulativeProfitAndLoss(userAssets.getCumulativeProfitAndLoss().add(amount));
}
extracted(userAssets);
+ }else if(Objects.equals(eUserAssets.getCode(), EUserAssets.PENDING_ORDER_FREEZE.getCode())){
+ // 挂单冻结资金:从可用余额转到冻结金额
+ // amount 是负数,表示扣除
+ userAssets.setAvailableBalance(userAssets.getAvailableBalance().add(amount));
+ userAssets.setFreezeMoney(userAssets.getFreezeMoney().add(amount.negate()));
+ }else if(Objects.equals(eUserAssets.getCode(), EUserAssets.PENDING_ORDER_UNFREEZE.getCode())){
+ // 取消挂单解冻资金:从冻结金额转回可用余额
+ // amount 是正数,表示增加
+ userAssets.setAvailableBalance(userAssets.getAvailableBalance().add(amount));
+ userAssets.setFreezeMoney(userAssets.getFreezeMoney().subtract(amount));
}
/*if(null != userPosition){
@@ -431,8 +441,8 @@
@Override
public BigDecimal exchangeAmountByRate(String fromType, String toType, BigDecimal amount) throws Exception {
- EStockType stockType = EStockType.getEStockTypeByCode(fromType);
- EStockType toStockType = EStockType.getEStockTypeByCode(toType);
+ EStockType stockType = EStockType.getEStockTypeBySymbol(fromType);
+ EStockType toStockType = EStockType.getEStockTypeBySymbol(toType);
ExchangeRate exchangeRate = exchangeRateRepository.findExchangeRateByCurrencyAndConversionCurrency(
stockType.getSymbol(), toStockType.getSymbol()).orElse(null);
if (exchangeRate != null) {
diff --git a/src/main/java/com/nq/service/impl/UserPendingorderServiceImpl.java b/src/main/java/com/nq/service/impl/UserPendingorderServiceImpl.java
index 58a4c44..25a590f 100644
--- a/src/main/java/com/nq/service/impl/UserPendingorderServiceImpl.java
+++ b/src/main/java/com/nq/service/impl/UserPendingorderServiceImpl.java
@@ -1,6 +1,7 @@
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;
@@ -62,45 +63,139 @@
@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("Please log in first");
- }
- SiteSetting siteSetting = this.iSiteSettingService.getSiteSetting();
- if (buyNum.intValue() < siteSetting.getBuyMinNum().intValue()) {
- return ServerResponse.createByErrorMsg("The pending order failed, and the purchased quantity was less than" + siteSetting
- .getBuyMinNum() + "stocks");
- }
- if (buyNum.intValue() > siteSetting.getBuyMaxNum().intValue()) {
- return ServerResponse.createByErrorMsg("The pending order failed because the purchased quantity was greater than" + siteSetting
- .getBuyMaxNum() + "stocks");
- }
- UserPendingorder userPendingorder = userPendingorderMapper.selectOne(new QueryWrapper<UserPendingorder>().eq("user_id", user.getId()).eq("stock_id", stockId).eq("status", 0));
- if (userPendingorder != null) {
- return ServerResponse.createByErrorMsg("Please do not repeat the order");
+ return ServerResponse.createByErrorMsg("请先登录", request);
}
- 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) {
- return ServerResponse.createBySuccessMsg("If the pending order is successfully added, the order will be automatically placed if the order conditions are met");
- }
- return ServerResponse.createByErrorMsg("Add failure");
+ 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<com.nq.pojo.StockBuySetting>().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<UserPendingorder>()
+ .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
@@ -133,6 +228,7 @@
}
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());
@@ -154,157 +250,198 @@
@Override
public void orderTask() {
-
+ // 查询所有待处理的挂单
List<UserPendingorder> userPendingorders = userPendingorderMapper.selectList(new QueryWrapper<UserPendingorder>().eq("status", 0));
- log.info("当前有挂单的用户数量 为 {}", Integer.valueOf(userPendingorders.size()));
- for (int i = 0; i < userPendingorders.size(); i++) {
- Integer userId = (Integer) userPendingorders.get(i).getUserId();
- User user = this.userMapper.selectById(userId);
- if (user == null) {
- continue;
- }
- List<UserPendingorder> userPendingorderList = userPendingorderMapper.selectList(new QueryWrapper<UserPendingorder>().eq("user_id", userId).eq("status", 0));
- if (userPendingorderList == null) {
- continue;
- }
- log.info("用户id = {} 姓名 = {} 已挂单数: {}", new Object[]{userId, user.getRealName(), Integer.valueOf(userPendingorders.size())});
- BigDecimal all_freez_amt = new BigDecimal("0");
- String nowPrice = "";
- String code = "";
- Integer indexId = null;
- StockListVO stockListVO = new StockListVO();
- StockCoin stockCoin = new StockCoin();
- for (UserPendingorder userPendingorder : userPendingorderList) {
- //指数
- 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", ""));
- all_freez_amt = (new BigDecimal(model.getDepositAmt().intValue())).multiply(new BigDecimal(userPendingorder.getBuyNum())).divide(new BigDecimal(userPendingorder.getLever())).setScale(4, 2);
+ log.info("当前有挂单数量:{}", userPendingorders.size());
+
+ 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;
+ }
-// if (){
-//
-// }else {
- MarketVO marketVO = this.iStockIndexService.querySingleIndex(model.getIndexGid());
- nowPrice = marketVO.getNowPrice();
-// }
+ // 获取股票信息
+ Stock stock = stockMapper.findStockByCode(userPendingorder.getStockId());
+ if (stock == null) {
+ log.error("挂单转持仓失败,股票不存在:{}", userPendingorder.getStockId());
+ userPendingorder.setStatus(2);
+ this.userPendingorderMapper.updateById(userPendingorder);
+ continue;
+ }
- indexId = model.getId();
+ // 获取当前价格
+ BigDecimal nowPrice = priceServices.getNowPrice(stock.getStockCode());
+ if (nowPrice.compareTo(BigDecimal.ZERO) == 0) {
+ continue;
+ }
- } else {
- //股票
- Stock stock = stockMapper.findStockByCode(userPendingorder.getStockId());
- if ("hk".equals(stock.getStockType())) {
- String hk = RedisShardedPoolUtils.get(stock.getStockGid(), 1);
- stockListVO = StockApi.otherStockListVO(hk);
- // stockCoin = iStockCoinService.selectCoinByCode("HKD");
- ExchangeVO exchangeVO = this.iStockFuturesService.queryExchangeVO("HKD").getData();
- nowPrice = String.valueOf(new BigDecimal(stockListVO.getNowPrice()).multiply(new BigDecimal(exchangeVO.getNowPrice())));
- } else if ("us".equals(stock.getStockType())) {
- String us = RedisShardedPoolUtils.get(stock.getStockGid(), 2);
- stockListVO = StockApi.otherStockListVO(us);
- // stockCoin = iStockCoinService.selectCoinByCode("USD");
- ExchangeVO exchangeVO = this.iStockFuturesService.queryExchangeVO("USD").getData();
- nowPrice = String.valueOf(new BigDecimal(stockListVO.getNowPrice()).multiply(new BigDecimal(exchangeVO.getNowPrice())));
- } else {
- stockListVO = StockApi.getStockRealTime(stock);
- nowPrice = stockListVO.getNowPrice();
+ // 判断价格是否达到目标价格
+ int ret = userPendingorder.getBuyType().intValue() == 0
+ ? userPendingorder.getTargetPrice().compareTo(nowPrice)
+ : nowPrice.compareTo(userPendingorder.getTargetPrice());
+
+ if (ret > 0) {
+ // 价格未达到目标价格,继续等待
+ continue;
+ }
+
+ // 价格达到目标价格,执行买入逻辑
+ synchronized (userPendingorder.getUserId()) {
+ // 判断股票是否在可交易时间段
+ Boolean b = tradingHourService.timeCheck(stock.getStockCode(), stock.getStockType());
+ if (!b) {
+ continue;
}
-
- all_freez_amt = new BigDecimal(nowPrice).multiply(new BigDecimal(userPendingorder.getBuyNum())).divide(new BigDecimal(userPendingorder.getLever()), 2, 4);
- code = stock.getStockCode();
- }
- if (nowPrice == null) {
- nowPrice = String.valueOf(0);
- }
- if (userPendingorder.getUserId() != null && userPendingorder.getStockId() != null && userPendingorder.getBuyNum() != null && userPendingorder.getBuyType() != null && userPendingorder.getLever() != null && userPendingorder.getTargetPrice() != null) {
- int ret = userPendingorder.getBuyType().intValue() == 0 ? userPendingorder.getTargetPrice().compareTo(new BigDecimal(nowPrice)) : new BigDecimal(nowPrice).compareTo(userPendingorder.getTargetPrice());
- //当前时间String
- String buyTime = DateTimeUtil.dateToStr(new Date());
- if (ret <= 0) {
- if (code != null && !"".equals(code)) {
- try {
- this.iUserPositionService.create(userPendingorder.getUserId(), code, nowPrice, buyTime, userPendingorder.getBuyNum(), userPendingorder.getBuyType(), userPendingorder.getLever(), userPendingorder.getProfitTarget(), userPendingorder.getStopTarget());
- userPendingorder.setStatus(1);
- this.userPendingorderMapper.updateById(userPendingorder);
- SiteTaskLog siteTaskLog = new SiteTaskLog();
- siteTaskLog.setTaskType("股票挂单转持仓");
- String accountType = (user.getAccountType() == 0) ? "正式用户" : "模拟用户";
- String tasktarget = "此次挂单买入id:" + userPendingorder.getId();
- siteTaskLog.setTaskTarget(tasktarget);
- siteTaskLog.setAddTime(new Date());
- siteTaskLog.setIsSuccess(0);
- siteTaskLog.setErrorMsg("");
- int insertTaskCount = this.siteTaskLogMapper.insert(siteTaskLog);
- if (insertTaskCount > 0) {
- log.info("挂单task任务成功");
- } else {
- log.info("挂单task任务失败");
- }
- } catch (Exception e) {
- log.error("股票挂单任务失败...");
- userPendingorder.setStatus(2);
- this.userPendingorderMapper.updateById(userPendingorder);
- }
- } else if (indexId != null) {
- try {
- this.iUserIndexPositionService.buyIndexOrder(indexId, userPendingorder.getBuyNum(), userPendingorder.getBuyType(), userPendingorder.getLever(), userPendingorder.getProfitTarget(), userPendingorder.getStopTarget(), userPendingorder.getUserId());
- userPendingorder.setStatus(1);
- this.userPendingorderMapper.updateById(userPendingorder);
- SiteTaskLog siteTaskLog = new SiteTaskLog();
- siteTaskLog.setTaskType("指数挂单转持仓");
- String accountType = (user.getAccountType() == 0) ? "正式用户" : "模拟用户";
- String tasktarget = "此次挂单买入id:" + userPendingorder.getId();
- siteTaskLog.setTaskTarget(tasktarget);
- siteTaskLog.setAddTime(new Date());
- siteTaskLog.setIsSuccess(0);
- siteTaskLog.setErrorMsg("");
- int insertTaskCount = this.siteTaskLogMapper.insert(siteTaskLog);
- if (insertTaskCount > 0) {
- log.info("挂单task任务成功");
- userPendingorder.setStatus(1);
- } else {
- log.info("挂单task任务失败");
- }
- } catch (Exception e) {
- log.error("指数挂单任务失败...");
- userPendingorder.setStatus(2);
- this.userPendingorderMapper.updateById(userPendingorder);
- }
- }
-
+ // 检查股票是否被锁定
+ 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;
+ }
+
+ // 手续费率
+ 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("===========挂单结束==========");
+ log.info("===========挂单任务结束==========");
}
//删除
@Override
+ @org.springframework.transaction.annotation.Transactional(rollbackFor = Exception.class)
public ServerResponse delOrder(Integer id, HttpServletRequest request) {
- String property = PropertiesUtil.getProperty("user.cookie.name");
- String header = request.getHeader(property);
- if (header != null) {
- String userJson = RedisShardedPoolUtils.get(header);
- User user = (User) JsonUtil.string2Obj(userJson, User.class);
- UserPendingorder userPendingorder = this.userPendingorderMapper.selectById(id);
- if (userPendingorder == null) {
- return ServerResponse.createByErrorMsg("The pending order does not exist");
- }
- if (user.getId().intValue() != userPendingorder.getUserId().intValue()) {
- return ServerResponse.createByErrorMsg("The pending order does not belong to you");
- }
- int delCount = this.userPendingorderMapper.deleteById(id);
- if (delCount > 0) {
- return ServerResponse.createByErrorMsg("Successfully deleted");
- }
- return ServerResponse.createByErrorMsg("Deletion failure");
+ User user = this.iUserService.getCurrentRefreshUser(request);
+ if (user == null) {
+ return ServerResponse.createByErrorMsg("请先登录", request);
}
- return ServerResponse.createByErrorMsg("Please log in");
+ 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);
+ }
}
diff --git a/src/main/java/com/nq/service/impl/UserPositionServiceImpl.java b/src/main/java/com/nq/service/impl/UserPositionServiceImpl.java
index 6fc7309..8aeaa6b 100644
--- a/src/main/java/com/nq/service/impl/UserPositionServiceImpl.java
+++ b/src/main/java/com/nq/service/impl/UserPositionServiceImpl.java
@@ -171,7 +171,7 @@
StockBuySetting stockBuySetting = stockBuySettingMapper.selectOne(new QueryWrapper<StockBuySetting>().eq("accets_type", stock.getStockType()));
if (stockBuySetting != null && stockBuySetting.getHandsNum() != null && stockBuySetting.getStockNum() != null) {
if(buyNum < stockBuySetting.getHandsNum()){
- return ServerResponse.createByErrorMsg("最低购买手数" + stockBuySetting.getHandsNum(), request);
+ return ServerResponse.createByErrorMsg("最低购买数量" + stockBuySetting.getHandsNum(), request);
}
buyNum = buyNum * stockBuySetting.getStockNum();
}
diff --git a/src/main/java/com/nq/service/impl/UserStockSubscribeServiceImpl.java b/src/main/java/com/nq/service/impl/UserStockSubscribeServiceImpl.java
index a59282d..ceffc8f 100644
--- a/src/main/java/com/nq/service/impl/UserStockSubscribeServiceImpl.java
+++ b/src/main/java/com/nq/service/impl/UserStockSubscribeServiceImpl.java
@@ -282,9 +282,9 @@
if((model.getStatus() == 3 && model.getApplyNumber() == null) || (model.getStatus() == 3 && model.getApplyNumber() == 0) ){
return ServerResponse.createByErrorMsg("中签数量不能小于0");
}
- if(model.getStatus() == 3 && model.getApplyNumber()>userStockSubscribe.getApplyNums()){
- return ServerResponse.createByErrorMsg("配置中签数量不能超过申请数量",request);
- }
+// if(model.getStatus() == 3 && model.getApplyNumber()>userStockSubscribe.getApplyNums()){
+// return ServerResponse.createByErrorMsg("配置中签数量不能超过申请数量",request);
+// }
//客户中签直接扣除客户账户可用资金
UserAssets userAssets = iUserAssetsServices.assetsByTypeAndUserId(stockSubscribe.getStockType(), userStockSubscribe.getUserId());
@@ -298,7 +298,6 @@
}
model.setBond(bound);
model.setDbMoney(model.getDbMoney());
-
if(null == userAssets){
return ServerResponse.createByErrorMsg("客户资金账户不存在");
}
@@ -373,9 +372,9 @@
//iUserPositionService.newStockToPosition(model.getId(),userAssets.getAmountToBeCovered());//转持仓
//model.setStatus(5);
}else{
- if(model.getApplyNumber()>model.getApplyNums()){
- return ServerResponse.createByErrorMsg("配置中签数量不能超过申请数量",request);
- }
+// if(model.getApplyNumber()>model.getApplyNums()){
+// return ServerResponse.createByErrorMsg("配置中签数量不能超过申请数量",request);
+// }
BigDecimal cCount = new BigDecimal(model.getApplyNums()-model.getApplyNumber());
BigDecimal tMoney = ((stockSubscribe.getMinPrice() != null ? stockSubscribe.getMinPrice() : stockSubscribe.getPrice())).multiply(cCount);
iUserAssetsServices.availablebalanceChange(stockSubscribe.getStockType(),userStockSubscribe.getUserId(),
diff --git a/src/main/java/com/nq/service/impl/UserWithdrawServiceImpl.java b/src/main/java/com/nq/service/impl/UserWithdrawServiceImpl.java
index 6b65d9c..e0ac187 100644
--- a/src/main/java/com/nq/service/impl/UserWithdrawServiceImpl.java
+++ b/src/main/java/com/nq/service/impl/UserWithdrawServiceImpl.java
@@ -66,7 +66,7 @@
@Autowired
ISiteSettingService iSiteSettingService;
- @Autowired
+ @Autowired
UserBankMapper userBankMapper;
@Autowired
@@ -98,10 +98,10 @@
if (user.getIsActive() != 2) {
return ServerResponse.createByErrorMsg("未实名认证",request);
}
- UserBank userBank = this.userBankMapper.selectById(bankId);
- if (userBank == null) {
- return ServerResponse.createByErrorMsg("银行卡不存在",request);
- }
+// UserBank userBank = this.userBankMapper.selectById(bankId);
+// if (userBank == null) {
+// return ServerResponse.createByErrorMsg("银行卡不存在",request);
+// }
if (user.getAccountType().intValue() == 1) {
return ServerResponse.createByErrorMsg("模拟用户无法提取资金",request);
}
@@ -128,9 +128,10 @@
userWithdraw.setWithAmt(new BigDecimal(amt));
userWithdraw.setApplyTime(new Date());
userWithdraw.setWithName(user.getRealName());
- userWithdraw.setBankNo(userBank.getBankNo());
- userWithdraw.setBankName(userBank.getBankName());
- userWithdraw.setBankAddress(userBank.getBankAddress());
+ userWithdraw.setBankNo(bankId);
+// userWithdraw.setBankNo(userBank.getBankNo());
+// userWithdraw.setBankName(userBank.getBankName());
+// userWithdraw.setBankAddress(userBank.getBankAddress());
userWithdraw.setWithStatus(Integer.valueOf(0));
BigDecimal withfee = siteSetting.getWithFeePercent().multiply(new BigDecimal(amt)).add(new BigDecimal(siteSetting.getWithFeeSingle().intValue()));
userWithdraw.setWithFee(withfee);
diff --git a/src/main/java/com/nq/utils/task/stock/CarryPositionTask.java b/src/main/java/com/nq/utils/task/stock/CarryPositionTask.java
index 24e0727..7593c71 100644
--- a/src/main/java/com/nq/utils/task/stock/CarryPositionTask.java
+++ b/src/main/java/com/nq/utils/task/stock/CarryPositionTask.java
@@ -9,6 +9,7 @@
import com.nq.dao.UserAssetsMapper;
import com.nq.dao.UserMapper;
import com.nq.dao.UserStockSubscribeMapper;
+import com.nq.enums.EStockType;
import com.nq.pojo.StockSubscribe;
import com.nq.pojo.User;
import com.nq.pojo.UserAssets;
@@ -75,7 +76,7 @@
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, 3).in(UserStockSubscribe::getNewCode, codeList));
+ .eq(UserStockSubscribe::getStatus, 5).in(UserStockSubscribe::getNewCode, codeList));
//订单转持仓
userStockSubscribes.forEach(f -> {
ServerResponse serverResponse = iUserPositionService.newStockToPosition(f.getId(),BigDecimal.ZERO);//转持仓
@@ -101,8 +102,8 @@
private final AtomicBoolean subscription = new AtomicBoolean(false);
-// @Scheduled(cron = "0 0/1 * * * ?")
- /*public void subscription() {
+ @Scheduled(cron = "0 0/1 * * * ?")
+ public void subscription() {
if (subscription.get()) { // 判断任务是否在处理中
return;
}
@@ -129,6 +130,6 @@
} else {
log.info("自动转已认缴定时任务--------->上次任务还未执行完成,本次任务忽略");
}
- }*/
+ }
}
diff --git a/src/main/java/com/nq/utils/task/stock/PendingOrderTask.java b/src/main/java/com/nq/utils/task/stock/PendingOrderTask.java
new file mode 100644
index 0000000..84e53a9
--- /dev/null
+++ b/src/main/java/com/nq/utils/task/stock/PendingOrderTask.java
@@ -0,0 +1,53 @@
+package com.nq.utils.task.stock;
+
+import com.nq.service.UserPendingorderService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * 挂单定时任务
+ * 定时检查挂单条件,当价格达到目标价格时自动买入
+ */
+@Component
+@Slf4j
+public class PendingOrderTask {
+
+ @Autowired
+ private UserPendingorderService userPendingorderService;
+
+ private final Lock pendingOrderLock = new ReentrantLock();
+ private final AtomicBoolean isPendingOrderRunning = new AtomicBoolean(false);
+
+ /**
+ * 挂单任务,每分钟执行一次
+ */
+ @Scheduled(cron = "0 0/1 * * * ?")
+ public void pendingOrderTask() {
+ if (isPendingOrderRunning.get()) { // 判断任务是否在处理中
+ log.info("挂单定时任务--------->上次任务还未执行完成,本次任务忽略");
+ return;
+ }
+ if (pendingOrderLock.tryLock()) {
+ try {
+ isPendingOrderRunning.set(true); // 设置处理中标识为true
+ log.info("挂单定时任务--------->开始");
+ userPendingorderService.orderTask();
+ log.info("挂单定时任务--------->结束");
+ } catch (Exception e) {
+ log.error("挂单定时任务发生异常", e);
+ } finally {
+ pendingOrderLock.unlock();
+ isPendingOrderRunning.set(false); // 设置处理中标识为false
+ }
+ } else {
+ log.info("挂单定时任务--------->上次任务还未执行完成,本次任务忽略");
+ }
+ }
+}
+
diff --git a/src/main/java/com/nq/vo/position/UserPendingorderVO.java b/src/main/java/com/nq/vo/position/UserPendingorderVO.java
index 3e2017c..271ab83 100644
--- a/src/main/java/com/nq/vo/position/UserPendingorderVO.java
+++ b/src/main/java/com/nq/vo/position/UserPendingorderVO.java
@@ -19,6 +19,7 @@
private Integer id;
private String stockId;
private String stockName;
+ private String stockGid;
private Integer buyNum;
@@ -38,5 +39,7 @@
private Integer status;
+
+
private static final long serialVersionUID = 1L;
}
\ No newline at end of file
diff --git a/src/main/java/com/nq/ws/WebSocketClientBeanConfig.java b/src/main/java/com/nq/ws/WebSocketClientBeanConfig.java
index 6534221..53ac141 100644
--- a/src/main/java/com/nq/ws/WebSocketClientBeanConfig.java
+++ b/src/main/java/com/nq/ws/WebSocketClientBeanConfig.java
@@ -20,24 +20,24 @@
public Map<String, WebSocketClient> websocketRunClientMap() {
Map<String, WebSocketClient> retMap = new HashMap<>(2);
- try {
- JPWebsocketRunClient jpWebsocketRunClient = new JPWebsocketRunClient(new URI(PropertiesUtil.getProperty("JP_WS_URL")), EStockType.JP);
- jpWebsocketRunClient.connect();
- jpWebsocketRunClient.setConnectionLostTimeout(0);
- startHeartbeatThread(jpWebsocketRunClient);
- retMap.put(EStockType.JP.getStockKey(), jpWebsocketRunClient);
- } catch (Exception e) {
- log.error("jpWebsocketRunClient 异常: {}", e.getMessage());
- }
- try {
- USWebsocketRunClient usWebsocketRunClient = new USWebsocketRunClient(new URI(PropertiesUtil.getProperty("US_WS_URL")), EStockType.US);
- usWebsocketRunClient.connect();
- usWebsocketRunClient.setConnectionLostTimeout(0);
- startHeartbeatThread(usWebsocketRunClient);
- retMap.put(EStockType.US.getStockKey(), usWebsocketRunClient);
- } catch (Exception e) {
- log.error("usWebsocketRunClient 异常: {}", e.getMessage());
- }
+// try {
+// JPWebsocketRunClient jpWebsocketRunClient = new JPWebsocketRunClient(new URI(PropertiesUtil.getProperty("JP_WS_URL")), EStockType.JP);
+// jpWebsocketRunClient.connect();
+// jpWebsocketRunClient.setConnectionLostTimeout(0);
+// startHeartbeatThread(jpWebsocketRunClient);
+// retMap.put(EStockType.JP.getStockKey(), jpWebsocketRunClient);
+// } catch (Exception e) {
+// log.error("jpWebsocketRunClient 异常: {}", e.getMessage());
+// }
+// try {
+// USWebsocketRunClient usWebsocketRunClient = new USWebsocketRunClient(new URI(PropertiesUtil.getProperty("US_WS_URL")), EStockType.US);
+// usWebsocketRunClient.connect();
+// usWebsocketRunClient.setConnectionLostTimeout(0);
+// startHeartbeatThread(usWebsocketRunClient);
+// retMap.put(EStockType.US.getStockKey(), usWebsocketRunClient);
+// } catch (Exception e) {
+// log.error("usWebsocketRunClient 异常: {}", e.getMessage());
+// }
return retMap;
}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 00b84c7..cfe966b 100644
--- a/src/main/resources/application.properties
+++ b/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 = rST7RRMPgvW4ogcAppCx
+JP_KEY = 43zGhZNUYT5lwsmEenUO
#默认首页显示指数code
#us_home_indices_code=15882,15881,16571
#hk_home_indices_code=535606773,535606776,535606785
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index cb89d51..ee2094d 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -126,7 +126,7 @@
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
#url: jdbc:mysql://127.0.0.1:6306/stock_ci?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
- url: jdbc:mysql://127.0.0.1:3306/stock-dg?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+ url: jdbc:mysql://118.107.6.162:3306/stock-dg?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: stock-dg
password: 6eAcBiXTatc8L6hm
druid:
diff --git a/src/main/resources/mapper/StockMapper.xml b/src/main/resources/mapper/StockMapper.xml
index 182ec3c..d7c4bd4 100644
--- a/src/main/resources/mapper/StockMapper.xml
+++ b/src/main/resources/mapper/StockMapper.xml
@@ -319,7 +319,7 @@
<include refid="Base_Column_List"/>
FROM stock
- where stock_spell not like '%.st%' and stock_gid !='indices'
+ where 1=1
<if test="stockType != null and stockType != '' ">
and stock_type = #{stockType}
</if>
--
Gitblit v1.9.3