From 64bc81d5f7bd99b470422b329aaca2182b79531c Mon Sep 17 00:00:00 2001
From: dd <gitluke@outlook.com>
Date: Mon, 01 Jun 2026 09:11:45 +0800
Subject: [PATCH] 1
---
src/main/java/com/nq/service/impl/UserStockSubscribeServiceImpl.java | 2
src/main/java/com/nq/service/impl/StockServiceImpl.java | 59 ++-
src/main/java/com/nq/service/IPayService.java | 6
src/main/java/com/nq/controller/protol/UserController.java | 14 +
src/main/java/com/nq/service/impl/UserPositionServiceImpl.java | 359 ++++++++++++++-----------
src/main/java/com/nq/service/IUserPositionService.java | 2
src/main/java/com/nq/service/IUserService.java | 3
src/main/java/com/nq/service/impl/UserServiceImpl.java | 56 ++-
src/main/resources/application.properties | 11
src/main/java/com/nq/utils/stock/sina/SinaStockApi.java | 102 +++---
src/main/java/com/nq/service/impl/SiteSettingServiceImpl.java | 3
src/main/java/com/nq/service/impl/StockOptionServiceImpl.java | 41 ++
src/main/java/com/nq/controller/PayApiController.java | 27 +
src/main/java/com/nq/controller/protol/UserPayController.java | 7
src/main/java/com/nq/service/impl/PayServiceImpl.java | 136 +++++++++
src/main/java/com/nq/service/impl/UserFundsPositionServiceImpl.java | 2
16 files changed, 576 insertions(+), 254 deletions(-)
diff --git a/src/main/java/com/nq/controller/PayApiController.java b/src/main/java/com/nq/controller/PayApiController.java
index a572190..1c90d88 100644
--- a/src/main/java/com/nq/controller/PayApiController.java
+++ b/src/main/java/com/nq/controller/PayApiController.java
@@ -29,6 +29,8 @@
import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@@ -162,4 +164,29 @@
log.error("fly notify error Msg = {}", serverResponse.getMsg());
}
}
+
+ @RequestMapping(value = {"ococnReturn.do"}, method = {RequestMethod.GET, RequestMethod.POST})
+ public void ococnReturn(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ String redirectUrl = this.iPayService.ococnReturn(request);
+ response.setContentType("text/html;charset=UTF-8");
+ response.setCharacterEncoding("UTF-8");
+ String safeUrl = redirectUrl.replace("\\", "\\\\").replace("'", "\\'");
+ response.getWriter().write(
+ "<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><title>跳转中</title>"
+ + "<script>window.location.replace('" + safeUrl + "');</script>"
+ + "</head><body></body></html>"
+ );
+ }
+
+ @RequestMapping(value = {"ococnNotify.do"}, method = {RequestMethod.GET, RequestMethod.POST})
+ @ResponseBody
+ public void ococnNotify(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ ServerResponse serverResponse = this.iPayService.ococnNotify(request);
+ if (serverResponse.isSuccess()) {
+ response.getWriter().write("success");
+ log.info("ococn 支付渠道异步通知处理成功");
+ } else {
+ log.error("ococn notify error Msg = {}", serverResponse.getMsg());
+ }
+ }
}
diff --git a/src/main/java/com/nq/controller/protol/UserController.java b/src/main/java/com/nq/controller/protol/UserController.java
index 2a74f6a..bfa1b4b 100644
--- a/src/main/java/com/nq/controller/protol/UserController.java
+++ b/src/main/java/com/nq/controller/protol/UserController.java
@@ -125,6 +125,20 @@
}
return serverResponse;
}
+ //撤销委托单
+ @RequestMapping({"cancelOrder.do"})
+ @ResponseBody
+ public ServerResponse cancelOrder(HttpServletRequest request, @RequestParam("positionSn") String positionSn) {
+ ServerResponse serverResponse = null;
+ try {
+ serverResponse = this.iUserPositionService.cancelOrder(positionSn, request);
+ } catch (Exception e) {
+ log.error("用户撤单操作 = {}", e);
+ serverResponse = ServerResponse.createByErrorMsg("撤单失败");
+ }
+ return serverResponse;
+ }
+
//用户平仓操作
@RequestMapping({"sell.do"})
@ResponseBody
diff --git a/src/main/java/com/nq/controller/protol/UserPayController.java b/src/main/java/com/nq/controller/protol/UserPayController.java
index 6356f8c..fcdfac8 100644
--- a/src/main/java/com/nq/controller/protol/UserPayController.java
+++ b/src/main/java/com/nq/controller/protol/UserPayController.java
@@ -49,4 +49,11 @@
return this.iPayService.flyPay(payType, payAmt, currency, request);
}
+ @RequestMapping({"ococnPay.do"})
+ @ResponseBody
+ public ServerResponse ococnPay(@RequestParam("payType") String payType, @RequestParam("payAmt") String payAmt, HttpServletRequest request) {
+ log.info("发起 ococn 线上支付 payType = {} payAmt = {}", payType, payAmt);
+ return this.iPayService.ococnPay(payType, payAmt, request);
+ }
+
}
diff --git a/src/main/java/com/nq/service/IPayService.java b/src/main/java/com/nq/service/IPayService.java
index 2179583..0118f77 100644
--- a/src/main/java/com/nq/service/IPayService.java
+++ b/src/main/java/com/nq/service/IPayService.java
@@ -20,4 +20,10 @@
ServerResponse flyPay(String paramString1, String paramString2, String paramString3, HttpServletRequest paramHttpServletRequest);
ServerResponse flyNotify(HttpServletRequest paramHttpServletRequest);
+
+ ServerResponse ococnPay(String payType, String payAmt, HttpServletRequest request);
+
+ ServerResponse ococnNotify(HttpServletRequest request);
+
+ String ococnReturn(HttpServletRequest request);
}
diff --git a/src/main/java/com/nq/service/IUserPositionService.java b/src/main/java/com/nq/service/IUserPositionService.java
index 9f3da22..ee57f2b 100644
--- a/src/main/java/com/nq/service/IUserPositionService.java
+++ b/src/main/java/com/nq/service/IUserPositionService.java
@@ -16,6 +16,8 @@
ServerResponse pending(Integer paramInteger1, Integer paramInteger2, Integer paramInteger3, Integer paramInteger4,BigDecimal paramInteger5,BigDecimal paramInteger6, HttpServletRequest paramHttpServletRequest) throws Exception;
+ ServerResponse cancelOrder(String positionSn, HttpServletRequest request) throws Exception;
+
ServerResponse sell(String paramString, int paramInt) throws Exception;
ServerResponse calendar(String paramString,HttpServletRequest request) throws Exception;
diff --git a/src/main/java/com/nq/service/IUserService.java b/src/main/java/com/nq/service/IUserService.java
index 0518fd9..f282c63 100644
--- a/src/main/java/com/nq/service/IUserService.java
+++ b/src/main/java/com/nq/service/IUserService.java
@@ -92,6 +92,7 @@
void updateUserAmt(Double amt, Integer user_id);
-
+ /** 将数据库中最新的用户资金写回登录缓存 */
+ void syncUserCache(HttpServletRequest request);
}
diff --git a/src/main/java/com/nq/service/impl/PayServiceImpl.java b/src/main/java/com/nq/service/impl/PayServiceImpl.java
index 20d7e71..9372ad2 100644
--- a/src/main/java/com/nq/service/impl/PayServiceImpl.java
+++ b/src/main/java/com/nq/service/impl/PayServiceImpl.java
@@ -12,8 +12,10 @@
import com.nq.utils.PropertiesUtil;
import com.nq.utils.pay.CmcPayOuterRequestUtil;
import com.nq.utils.pay.CmcPayTool;
+import com.nq.utils.pay.OcocnPayUtil;
import com.nq.vo.pay.FlyPayVO;
import com.nq.vo.pay.GuoPayVO;
+import com.nq.vo.pay.OcocnPayVO;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
@@ -22,6 +24,7 @@
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.Map;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
@@ -375,6 +378,139 @@
}
@Override
+ public ServerResponse ococnPay(String payType, String payAmt, HttpServletRequest request) {
+ if (StringUtils.isBlank(payType) || StringUtils.isBlank(payAmt)) {
+ return ServerResponse.createByErrorMsg("参数不能为空");
+ }
+ BigDecimal payAmtBig = new BigDecimal(payAmt);
+ if (payAmtBig.compareTo(BigDecimal.ZERO) <= 0) {
+ return ServerResponse.createByErrorMsg("支付金额必须大于0");
+ }
+
+ User user = this.iUserService.getCurrentRefreshUser(request);
+ if (user == null) {
+ return ServerResponse.createByErrorMsg("请先登录");
+ }
+
+ String ordersn = KeyUtils.getRechargeOrderSn();
+ String money = payAmtBig.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString();
+ String pid = PropertiesUtil.getProperty("ococn.pay.pid");
+ String key = PropertiesUtil.getProperty("ococn.pay.key");
+ String submitUrl = PropertiesUtil.getProperty("ococn.pay.url");
+ String apiDomain = trimTrailingSlash(PropertiesUtil.getProperty("website.domain.url"));
+ if (StringUtils.isAnyBlank(pid, key, submitUrl, apiDomain)) {
+ return ServerResponse.createByErrorMsg("支付配置不完整,请联系管理员");
+ }
+ String notifyUrl = apiDomain + "/api/pay/ococnNotify.do";
+ String returnUrl = apiDomain + "/api/pay/ococnReturn.do";
+ String sitename = StringUtils.defaultString(PropertiesUtil.getProperty("ococn.pay.sitename", ""), "");
+ String productName = PropertiesUtil.getProperty("ococn.pay.name", "账户充值");
+
+ UserRecharge userRecharge = new UserRecharge();
+ userRecharge.setUserId(user.getId());
+ userRecharge.setNickName(user.getRealName());
+ userRecharge.setAgentId(user.getAgentId());
+ userRecharge.setOrderSn(ordersn);
+ userRecharge.setPayChannel(getOcocnChannelName(payType));
+ userRecharge.setPayAmt(payAmtBig);
+ userRecharge.setOrderStatus(Integer.valueOf(0));
+ userRecharge.setAddTime(new Date());
+
+ int insertCount = this.userRechargeMapper.insert(userRecharge);
+ if (insertCount <= 0) {
+ return ServerResponse.createByErrorMsg("创建支付订单失败");
+ }
+
+ String sign = OcocnPayUtil.buildSubmitSign(money, productName, notifyUrl, ordersn, pid, returnUrl, sitename, payType, key);
+ log.info("ococn支付 notifyUrl={} returnUrl={}", notifyUrl, returnUrl);
+ StringBuilder payUrlBuilder = new StringBuilder(submitUrl).append("?");
+ payUrlBuilder.append("pid=").append(pid)
+ .append("&type=").append(payType)
+ .append("&out_trade_no=").append(ordersn)
+ .append("¬ify_url=").append(OcocnPayUtil.encode(notifyUrl))
+ .append("&return_url=").append(OcocnPayUtil.encode(returnUrl))
+ .append("&name=").append(OcocnPayUtil.encode(productName))
+ .append("&money=").append(money);
+ if (StringUtils.isNotBlank(sitename)) {
+ payUrlBuilder.append("&sitename=").append(OcocnPayUtil.encode(sitename));
+ }
+ payUrlBuilder.append("&sign=").append(sign).append("&sign_type=MD5");
+ String payUrl = payUrlBuilder.toString();
+
+ OcocnPayVO ococnPayVO = new OcocnPayVO();
+ ococnPayVO.setPayUrl(payUrl);
+ log.info("ococn支付,创建订单成功 orderSn={}", ordersn);
+ return ServerResponse.createBySuccess(ococnPayVO);
+ }
+
+ @Override
+ public ServerResponse ococnNotify(HttpServletRequest request) {
+ Map<String, String> params = OcocnPayUtil.parseRequestParams(request);
+ log.info("ococn支付通知参数: {}", params);
+
+ String outTradeNo = params.get("out_trade_no");
+ String tradeNo = params.get("trade_no");
+ String money = params.get("money");
+ String tradeStatus = params.get("trade_status");
+ String sign = params.get("sign");
+
+ if (StringUtils.isAnyBlank(outTradeNo, tradeNo, money, tradeStatus, sign)) {
+ return ServerResponse.createByErrorMsg("回调参数不完整");
+ }
+
+ String key = PropertiesUtil.getProperty("ococn.pay.key");
+ if (!OcocnPayUtil.verifyNotifySign(params, key)) {
+ log.error("ococn支付通知签名校验失败, remoteSign={}, params={}", sign, params);
+ return ServerResponse.createByErrorMsg("签名校验失败");
+ }
+
+ if (!"TRADE_SUCCESS".equals(tradeStatus)) {
+ return ServerResponse.createByErrorMsg("支付未成功");
+ }
+
+ UserRecharge existing = this.userRechargeMapper.findUserRechargeByOrderSn(outTradeNo);
+ if (existing != null && existing.getOrderStatus().intValue() != 0) {
+ return ServerResponse.createBySuccessMsg("订单已处理");
+ }
+
+ return doSuccess(outTradeNo, money);
+ }
+
+ @Override
+ public String ococnReturn(HttpServletRequest request) {
+ Map<String, String> params = OcocnPayUtil.parseRequestParams(request);
+ log.info("ococn支付同步跳转: {}", params);
+ String frontendUrl = StringUtils.defaultIfBlank(
+ PropertiesUtil.getProperty("ococn.pay.frontend_redirect"),
+ trimTrailingSlash(PropertiesUtil.getProperty("frontend.domain.url", "")) + "/#/user"
+ );
+ return frontendUrl;
+ }
+
+ private String getOcocnChannelName(String payType) {
+ if ("alipay".equals(payType)) {
+ return "支付宝-线上";
+ }
+ if ("wxpay".equals(payType)) {
+ return "微信-线上";
+ }
+ if ("qqpay".equals(payType)) {
+ return "QQ钱包-线上";
+ }
+ if ("tenpay".equals(payType)) {
+ return "财付通-线上";
+ }
+ return "线上支付";
+ }
+
+ private String trimTrailingSlash(String url) {
+ if (url == null) {
+ return null;
+ }
+ return url.endsWith("/") ? url.substring(0, url.length() - 1) : url;
+ }
+
+ @Override
public ServerResponse juhenewpayNotify(HttpServletRequest request) throws UnsupportedEncodingException {
LinkedMap map = new LinkedMap();
String out_trade_no = request.getParameter("out_trade_no");
diff --git a/src/main/java/com/nq/service/impl/SiteSettingServiceImpl.java b/src/main/java/com/nq/service/impl/SiteSettingServiceImpl.java
index 78c5299..bc165ba 100644
--- a/src/main/java/com/nq/service/impl/SiteSettingServiceImpl.java
+++ b/src/main/java/com/nq/service/impl/SiteSettingServiceImpl.java
@@ -9,6 +9,7 @@
import com.nq.pojo.SiteSetting;
import com.nq.service.ISiteSettingService;
+import com.nq.utils.TradeFeeUtil;
import java.util.List;
@@ -34,6 +35,7 @@
if (list.size() > 0) {
siteSetting = (SiteSetting) list.get(0);
+ siteSetting.setBuyFee(TradeFeeUtil.BUY_FEE_RATE);
}
return siteSetting;
@@ -48,6 +50,7 @@
if (siteSetting == null) {
return ServerResponse.createByErrorMsg("查不到设置记录");
}
+ setting.setBuyFee(TradeFeeUtil.BUY_FEE_RATE);
int updateCount = this.siteSettingMapper.updateByPrimaryKeySelective(setting);
diff --git a/src/main/java/com/nq/service/impl/StockOptionServiceImpl.java b/src/main/java/com/nq/service/impl/StockOptionServiceImpl.java
index 51c50b7..787fdce 100644
--- a/src/main/java/com/nq/service/impl/StockOptionServiceImpl.java
+++ b/src/main/java/com/nq/service/impl/StockOptionServiceImpl.java
@@ -71,9 +71,14 @@
List<StockOptionListVO> stockOptionListVOS = Lists.newArrayList();
for (StockOption option : stockOptions) {
- StockOptionListVO stockOptionListVO = assembleStockOptionListVO(option);
- stockOptionListVO.setIsOption("1");
- stockOptionListVOS.add(stockOptionListVO);
+ try {
+ StockOptionListVO stockOptionListVO = assembleStockOptionListVO(option);
+ stockOptionListVO.setIsOption("1");
+ stockOptionListVOS.add(stockOptionListVO);
+ } catch (Exception e) {
+ log.error("自选列表单条行情组装失败, gid={}, code={}", option.getStockGid(), option.getStockCode(), e);
+ stockOptionListVOS.add(buildFallbackOptionVO(option));
+ }
}
PageInfo pageInfo = new PageInfo(stockOptions);
@@ -133,24 +138,42 @@
stockVO = SinaStockApi.assembleStockVO(SinaStockApi.getSinaStock(option.getStockGid()));
}
}
- stockOptionListVO.setNowPrice(stockVO.getNowPrice());
+ if (stockVO == null) {
+ stockVO = new StockVO();
+ }
+ stockOptionListVO.setNowPrice(stockVO.getNowPrice() == null ? "0" : stockVO.getNowPrice());
- stockOptionListVO.setHcrate(stockVO.getHcrate().toString());
+ stockOptionListVO.setHcrate(stockVO.getHcrate() == null ? "0" : stockVO.getHcrate().toString());
- stockOptionListVO.setPreclose_px(stockVO.getPreclose_px());
+ stockOptionListVO.setPreclose_px(stockVO.getPreclose_px() == null ? "0" : stockVO.getPreclose_px());
- stockOptionListVO.setOpen_px(stockVO.getOpen_px());
+ stockOptionListVO.setOpen_px(stockVO.getOpen_px() == null ? "0" : stockVO.getOpen_px());
stockOptionListVO.setType(stockVO.getType());
Stock stock = this.stockMapper.selectByPrimaryKey(option.getStockId());
- stockOptionListVO.setStock_plate(stock.getStockPlate()==null?"":stock.getStockPlate());
-
+ if (stock != null) {
+ stockOptionListVO.setStock_plate(stock.getStockPlate()==null?"":stock.getStockPlate());
stockOptionListVO.setStock_type(stock.getStockType());
+ }
return stockOptionListVO;
}
+
+ private StockOptionListVO buildFallbackOptionVO(StockOption option) {
+ StockOptionListVO vo = new StockOptionListVO();
+ vo.setId(option.getId().intValue());
+ vo.setStockName(option.getStockName());
+ vo.setStockCode(option.getStockCode());
+ vo.setStockGid(option.getStockGid());
+ vo.setNowPrice("0");
+ vo.setHcrate("0");
+ vo.setPreclose_px("0");
+ vo.setOpen_px("0");
+ vo.setIsOption("1");
+ return vo;
+ }
}
diff --git a/src/main/java/com/nq/service/impl/StockServiceImpl.java b/src/main/java/com/nq/service/impl/StockServiceImpl.java
index a4d61ea..561000c 100644
--- a/src/main/java/com/nq/service/impl/StockServiceImpl.java
+++ b/src/main/java/com/nq/service/impl/StockServiceImpl.java
@@ -116,34 +116,49 @@
PageHelper.startPage(pageNum, pageSize);
User user = iUserService.getCurrentUser(request);
List<Stock> stockList = this.stockMapper.findStockListByKeyWords(keyWords, stockPlate, stockType, Integer.valueOf(0));
+ boolean quickSearch = org.apache.commons.lang3.StringUtils.isNotBlank(keyWords);
List<StockListVO> stockListVOS = Lists.newArrayList();
- if (stockList.size() > 0)
- for (Stock stock : stockList) {
- StockListVO stockListVO = new StockListVO();
- stockListVO=SinaStockApi.assembleLideStockListVO(LiDeDataUtils.getStock(stock.getStockCode()));
- if(ObjectUtils.isEmpty(stockListVO)){
- stockListVO = SinaStockApi.assembleStockListVO(SinaStockApi.getSinaStock(stock.getStockGid()));
- }
- stockListVO.setCode(stock.getStockCode());
- stockListVO.setSpell(stock.getStockSpell());
- stockListVO.setGid(stock.getStockGid());
- BigDecimal day3Rate = (BigDecimal)selectRateByDaysAndStockCode(stock.getStockCode(), 3).getData();
- stockListVO.setDay3Rate(day3Rate);
- stockListVO.setStock_plate(stock.getStockPlate());
- stockListVO.setStock_type(stock.getStockType());
- //是否添加自选
- if(user == null){
- stockListVO.setIsOption("0");
- } else {
- stockListVO.setIsOption(iStockOptionService.isMyOption(user.getId(), stock.getStockCode()));
- }
- stockListVOS.add(stockListVO);
- }
+ if (stockList.size() > 0) {
+ Integer userId = user != null ? user.getId() : null;
+ stockListVOS = stockList.parallelStream().map(stock -> assembleStockListItem(stock, userId, quickSearch))
+ .collect(java.util.stream.Collectors.toList());
+ }
PageInfo pageInfo = new PageInfo(stockList);
pageInfo.setList(stockListVOS);
return ServerResponse.createBySuccess(pageInfo);
}
+
+ /** 搜索列表组装行情:关键词搜索走轻量路径,跳过三日涨幅等慢查询 */
+ private StockListVO assembleStockListItem(Stock stock, Integer userId, boolean quickSearch) {
+ StockListVO stockListVO = SinaStockApi.assembleLideStockListVO(LiDeDataUtils.getStock(stock.getStockCode()));
+ if (ObjectUtils.isEmpty(stockListVO)) {
+ stockListVO = SinaStockApi.assembleStockListVO(SinaStockApi.getSinaStock(stock.getStockGid()));
+ }
+ if (stockListVO == null) {
+ stockListVO = new StockListVO();
+ stockListVO.setName(stock.getStockName());
+ stockListVO.setNowPrice("0");
+ stockListVO.setHcrate(java.math.BigDecimal.ZERO);
+ }
+ stockListVO.setCode(stock.getStockCode());
+ stockListVO.setSpell(stock.getStockSpell());
+ stockListVO.setGid(stock.getStockGid());
+ if (!quickSearch) {
+ BigDecimal day3Rate = (BigDecimal) selectRateByDaysAndStockCode(stock.getStockCode(), 3).getData();
+ stockListVO.setDay3Rate(day3Rate);
+ }
+ stockListVO.setStock_plate(stock.getStockPlate());
+ stockListVO.setStock_type(stock.getStockType());
+ if (userId == null) {
+ stockListVO.setIsOption("0");
+ } else if (quickSearch) {
+ stockListVO.setIsOption("0");
+ } else {
+ stockListVO.setIsOption(iStockOptionService.isMyOption(userId, stock.getStockCode()));
+ }
+ return stockListVO;
+ }
public void z1() {
this.stockPoll.z1();
}
diff --git a/src/main/java/com/nq/service/impl/UserFundsPositionServiceImpl.java b/src/main/java/com/nq/service/impl/UserFundsPositionServiceImpl.java
index b2164d4..1c6c174 100644
--- a/src/main/java/com/nq/service/impl/UserFundsPositionServiceImpl.java
+++ b/src/main/java/com/nq/service/impl/UserFundsPositionServiceImpl.java
@@ -341,7 +341,7 @@
userPosition.setIsLock(Integer.valueOf(0));
userPosition.setOrderLever(lever);
userPosition.setOrderTotalPrice(buy_amt);
- BigDecimal buy_fee_amt = buy_amt.multiply(siteSetting.getBuyFee()).setScale(2, 4);
+ BigDecimal buy_fee_amt = com.nq.utils.TradeFeeUtil.calcBuyFee(buy_amt);
log.info("用户购买手续费(配资后总资金 * 百分比) = {}", buy_fee_amt);
userPosition.setOrderFee(buy_fee_amt);
diff --git a/src/main/java/com/nq/service/impl/UserPositionServiceImpl.java b/src/main/java/com/nq/service/impl/UserPositionServiceImpl.java
index 377b1fd..5162932 100644
--- a/src/main/java/com/nq/service/impl/UserPositionServiceImpl.java
+++ b/src/main/java/com/nq/service/impl/UserPositionServiceImpl.java
@@ -11,6 +11,8 @@
import com.google.common.collect.Lists;
import com.nq.common.ServerResponse;
import com.nq.utils.*;
+import com.nq.utils.TradeFeeUtil;
+import com.nq.utils.redis.JsonUtil;
import com.nq.utils.redis.RedisShardedPoolUtils;
import com.nq.utils.stock.BuyAndSellUtils;
import com.nq.utils.stock.GeneratePosition;
@@ -111,6 +113,7 @@
StockDzMapper stockDzMapper;
+ @Override
@Transactional
public ServerResponse buy(Integer stockId, Integer buyNum, Integer buyType, Integer lever, BigDecimal profitTarget, BigDecimal stopTarget, HttpServletRequest request) throws Exception {
@@ -385,11 +388,12 @@
}
- int compareUserAmtInt = user_enable_amt.compareTo(buy_amt_autual);
- log.info("用户可用金额 = {} 实际购买金额 = {}", user_enable_amt, buy_amt_autual);
- log.info("比较 用户金额 和 实际 购买金额 = {}", Integer.valueOf(compareUserAmtInt));
+ BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFee(buy_amt);
+ BigDecimal buy_debit = TradeFeeUtil.calcBuyDebit(buy_amt_autual, buy_fee_amt);
+ int compareUserAmtInt = user_enable_amt.compareTo(buy_debit);
+ log.info("用户可用金额 = {} 下单扣款(保证金+手续费) = {}", user_enable_amt, buy_debit);
if (compareUserAmtInt == -1) {
- return ServerResponse.createByErrorMsg("下单失败,融资可用金额小于" + buy_amt_autual + "元");
+ return ServerResponse.createByErrorMsg("下单失败,融资可用金额小于" + buy_debit + "元(含保证金及手续费)");
}
if (user.getUserIndexAmt().compareTo(new BigDecimal("0")) == -1) {
@@ -445,7 +449,6 @@
userPosition.setOrderStayDays(1);
- BigDecimal buy_fee_amt = buy_amt.multiply(siteSetting.getBuyFee()).setScale(2, 4);
log.info("用户购买手续费(配资后总资金 * 百分比) = {}", buy_fee_amt);
userPosition.setOrderFee(buy_fee_amt);
@@ -477,33 +480,11 @@
userPosition.setOrderStayDays(Integer.valueOf(0));
userPosition.setOrderStayFee(new BigDecimal("0"));
- int insertPositionCount = 0;
this.userPositionMapper.insert(userPosition);
- insertPositionCount = userPosition.getId();
- if (insertPositionCount > 0) {
- //修改用户可用余额= 当前余额-下单金额-买入手续费-印花税-点差费
- //BigDecimal reckon_enable = user_enable_amt.subtract(buy_amt_autual).subtract(buy_fee_amt).subtract(buy_yhs_amt).subtract(spread_rate_amt);
- //修改用户可用余额= 当前余额-下单总金额
- BigDecimal reckon_enable = user_enable_amt.subtract(buy_amt_autual);
- //修改用户可取余额=当前可取余额-下单总金额
- int compareUserWithdrawAmtInt = user_enable_withdraw_amt.compareTo(buy_amt_autual);
- if (compareUserWithdrawAmtInt == -1) {
- //若可取余额小于下单总额,但是可用余额充足,令可取余额为0
- user.setEnaleWithdrawAmt(BigDecimal.ZERO);
- } else {
- user_enable_withdraw_amt = user_enable_withdraw_amt.subtract(buy_amt_autual);
- user.setEnaleWithdrawAmt(user_enable_withdraw_amt);
- }
- user.setEnableAmt(reckon_enable);
- int updateUserCount = this.userMapper.updateByPrimaryKeySelective(user);
- if (updateUserCount > 0) {
- log.info("【用户交易下单】修改用户金额成功");
- } else {
- log.error("用户交易下单】修改用户金额出错");
- throw new Exception("用户交易下单】修改用户金额出错");
- }
- //核算代理收入-入仓手续费
+ if (userPosition.getId() != null && userPosition.getId() > 0) {
+ deductUserEnableOnBuy(user, buy_debit, buy_amt_autual, buy_fee_amt, userPosition);
iAgentAgencyFeeService.AgencyFeeIncome(1, userPosition.getPositionSn());
+ syncUserCacheAfterTrade(request);
log.info("【用户交易下单】保存持仓记录成功");
} else {
log.error("用户交易下单】保存持仓记录出错");
@@ -514,12 +495,111 @@
}
- public ServerResponse fee(Integer buyNum,BigDecimal nowPrice){
+ @Override
+ public ServerResponse fee(Integer buyNum, BigDecimal nowPrice) {
BigDecimal buy_amt = nowPrice.multiply(new BigDecimal(buyNum.intValue()));
- SiteSetting siteSetting = this.iSiteSettingService.getSiteSetting();
- BigDecimal buy_fee_amt = buy_amt.multiply(siteSetting.getBuyFee()).setScale(2, 4);
+ BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFee(buy_amt).setScale(2, 4);
return ServerResponse.createBySuccess(buy_fee_amt);
}
+
+ /** 下单从两融可用资金扣除:保证金 + 买入手续费 */
+ private void deductUserEnableOnBuy(User user, BigDecimal buyDebit, BigDecimal margin, BigDecimal fee,
+ UserPosition position) throws Exception {
+ User fresh = this.userMapper.selectByPrimaryKey(user.getId());
+ if (fresh == null) {
+ throw new Exception("用户不存在");
+ }
+ BigDecimal enableAmt = fresh.getEnableAmt() == null ? BigDecimal.ZERO : fresh.getEnableAmt();
+ if (enableAmt.compareTo(buyDebit) < 0) {
+ throw new Exception("扣除可用资金失败(保证金+手续费=" + buyDebit + "元),余额不足");
+ }
+ fresh.setEnableAmt(enableAmt.subtract(buyDebit));
+ BigDecimal withdrawAmt = fresh.getEnaleWithdrawAmt() == null ? BigDecimal.ZERO : fresh.getEnaleWithdrawAmt();
+ if (withdrawAmt.compareTo(buyDebit) < 0) {
+ fresh.setEnaleWithdrawAmt(BigDecimal.ZERO);
+ } else {
+ fresh.setEnaleWithdrawAmt(withdrawAmt.subtract(buyDebit));
+ }
+ BigDecimal userAmt = fresh.getUserAmt() == null ? BigDecimal.ZERO : fresh.getUserAmt();
+ fresh.setUserAmt(userAmt.subtract(buyDebit));
+ int rows = this.userMapper.updateByPrimaryKeySelective(fresh);
+ if (rows <= 0) {
+ throw new Exception("扣除可用资金失败(保证金+手续费=" + buyDebit + "元)");
+ }
+ saveBuyDebitCashDetail(user, position, margin, fee, buyDebit);
+ log.info("【用户交易下单】扣款成功,用户={},保证金={},手续费={},合计={}", user.getId(), margin, fee, buyDebit);
+ }
+
+ private void refundUserEnableOnCancel(User user, BigDecimal refundAmt, UserPosition position) throws Exception {
+ User fresh = this.userMapper.selectByPrimaryKey(user.getId());
+ if (fresh == null) {
+ throw new Exception("用户不存在");
+ }
+ BigDecimal enableAmt = fresh.getEnableAmt() == null ? BigDecimal.ZERO : fresh.getEnableAmt();
+ BigDecimal withdrawAmt = fresh.getEnaleWithdrawAmt() == null ? BigDecimal.ZERO : fresh.getEnaleWithdrawAmt();
+ BigDecimal userAmt = fresh.getUserAmt() == null ? BigDecimal.ZERO : fresh.getUserAmt();
+ fresh.setEnableAmt(enableAmt.add(refundAmt));
+ fresh.setEnaleWithdrawAmt(withdrawAmt.add(refundAmt));
+ fresh.setUserAmt(userAmt.add(refundAmt));
+ int rows = this.userMapper.updateByPrimaryKeySelective(fresh);
+ if (rows <= 0) {
+ throw new Exception("撤单退款失败");
+ }
+ UserCashDetail ucd = new UserCashDetail();
+ ucd.setPositionId(position.getId());
+ ucd.setAgentId(user.getAgentId());
+ ucd.setAgentName(user.getAgentName());
+ ucd.setUserId(user.getId());
+ ucd.setUserName(user.getRealName());
+ ucd.setDeType("撤单退款");
+ ucd.setDeAmt(refundAmt);
+ ucd.setDeSummary("撤销委托," + position.getStockCode() + "/" + position.getStockName()
+ + ",退还保证金+手续费:" + refundAmt);
+ ucd.setAddTime(new Date());
+ ucd.setIsRead(Integer.valueOf(0));
+ this.userCashDetailMapper.insert(ucd);
+ log.info("【用户撤单】退款成功,用户={},金额={}", user.getId(), refundAmt);
+ }
+
+ private void saveBuyDebitCashDetail(User user, UserPosition position, BigDecimal margin, BigDecimal fee,
+ BigDecimal buyDebit) {
+ UserCashDetail ucd = new UserCashDetail();
+ ucd.setPositionId(position.getId());
+ ucd.setAgentId(user.getAgentId());
+ ucd.setAgentName(user.getAgentName());
+ ucd.setUserId(user.getId());
+ ucd.setUserName(user.getRealName());
+ ucd.setDeType("买入扣款");
+ ucd.setDeAmt(buyDebit.negate());
+ ucd.setDeSummary("委托买入," + position.getStockCode() + "/" + position.getStockName()
+ + ",保证金:" + margin + ",手续费:" + fee + ",合计扣款:" + buyDebit);
+ ucd.setAddTime(new Date());
+ ucd.setIsRead(Integer.valueOf(0));
+ this.userCashDetailMapper.insert(ucd);
+ }
+
+ /** 下单/撤单后刷新 Redis 中的用户资金,避免页面仍显示旧可用余额 */
+ private void syncUserCacheAfterTrade(HttpServletRequest request) {
+ if (request == null) {
+ return;
+ }
+ String cookieName = PropertiesUtil.getProperty("user.cookie.name");
+ String loginToken = request.getHeader(cookieName);
+ if (StringUtils.isBlank(loginToken)) {
+ return;
+ }
+ String userJson = RedisShardedPoolUtils.get(loginToken);
+ User cached = (User) JsonUtil.string2Obj(userJson, User.class);
+ if (cached == null || cached.getId() == null) {
+ return;
+ }
+ User dbUser = this.userMapper.selectByPrimaryKey(cached.getId());
+ if (dbUser != null) {
+ RedisShardedPoolUtils.setEx(loginToken, JsonUtil.obj2String(dbUser), 9999);
+ }
+ }
+
+ @Override
@Transactional
public ServerResponse pending(Integer stockId, Integer buyNum, Integer buyType, Integer lever, BigDecimal profitTarget, BigDecimal stopTarget, HttpServletRequest request) throws Exception {
@@ -708,11 +788,10 @@
.getBuyMaxAmtPercent().multiply(new BigDecimal("100")) + "%");
}
- int compareUserAmtInt = user_enable_amt.compareTo(buy_amt_autual);
- log.info("用户可用金额 = {} 实际购买金额 = {}", user_enable_amt, buy_amt_autual);
- log.info("比较 用户金额 和 实际 购买金额 = {}", Integer.valueOf(compareUserAmtInt));
- if (compareUserAmtInt == -1) {
- return ServerResponse.createByErrorMsg("挂单失败,融资可用金额小于" + buy_amt_autual + "元");
+ BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFee(buy_amt);
+ BigDecimal buy_debit = TradeFeeUtil.calcBuyDebit(buy_amt_autual, buy_fee_amt);
+ if (user_enable_amt.compareTo(buy_debit) == -1) {
+ return ServerResponse.createByErrorMsg("挂单失败,融资可用金额小于" + buy_debit + "元(含保证金及手续费)");
}
if (user.getUserIndexAmt().compareTo(new BigDecimal("0")) == -1) {
return ServerResponse.createByErrorMsg("失败,指数总资金小于0");
@@ -751,7 +830,6 @@
BigDecimal allStayFee = stayFee.multiply(new BigDecimal(1));
userPosition.setOrderStayFee(allStayFee);
userPosition.setOrderStayDays(1);
- BigDecimal buy_fee_amt = buy_amt.multiply(siteSetting.getBuyFee()).setScale(2, 4);
log.info("用户购买手续费(配资后总资金 * 百分比) = {}", buy_fee_amt);
userPosition.setOrderFee(buy_fee_amt);
BigDecimal buy_yhs_amt = buy_amt.multiply(siteSetting.getDutyFee()).setScale(2, 4);
@@ -772,40 +850,60 @@
userPosition.setAllProfitAndLose(all_profit_and_lose);
userPosition.setOrderStayDays(Integer.valueOf(0));
userPosition.setOrderStayFee(new BigDecimal("0"));
- int insertPositionCount = 0;
this.userPositionMapper.insert(userPosition);
- insertPositionCount = userPosition.getId();
- if (insertPositionCount > 0) {
- //修改用户可用余额= 当前余额-下单金额-买入手续费-印花税-点差费
- //BigDecimal reckon_enable = user_enable_amt.subtract(buy_amt_autual).subtract(buy_fee_amt).subtract(buy_yhs_amt).subtract(spread_rate_amt);
- //修改用户可用余额= 当前余额-下单总金额
- BigDecimal reckon_enable = user_enable_amt.subtract(buy_amt_autual);
- //修改用户可取余额=当前可取余额-下单总金额
- int compareUserWithdrawAmtInt = user_enable_withdraw_amt.compareTo(buy_amt_autual);
- if (compareUserWithdrawAmtInt == -1) {
- //若可取余额小于下单总额,但是可用余额充足,令可取余额为0
- user.setEnaleWithdrawAmt(BigDecimal.ZERO);
- } else {
- user_enable_withdraw_amt = user_enable_withdraw_amt.subtract(buy_amt_autual);
- user.setEnaleWithdrawAmt(user_enable_withdraw_amt);
- }
- user.setEnableAmt(reckon_enable);
-// user.setDjzj(user.getDjzj().subtract(buy_amt_autual));
- int updateUserCount = this.userMapper.updateByPrimaryKeySelective(user);
- if (updateUserCount > 0) {
- log.info("【用户交易下单】修改用户金额成功");
- } else {
- log.error("用户交易下单】修改用户金额出错");
- throw new Exception("用户交易下单】修改用户金额出错");
- }
- //核算代理收入-入仓手续费
-// iAgentAgencyFeeService.AgencyFeeIncome(1, userPosition.getPositionSn());
+ if (userPosition.getId() != null && userPosition.getId() > 0) {
+ deductUserEnableOnBuy(user, buy_debit, buy_amt_autual, buy_fee_amt, userPosition);
+ syncUserCacheAfterTrade(request);
log.info("【用户交易下单】保存持仓记录成功");
} else {
log.error("用户交易下单】保存持仓记录出错");
throw new Exception("用户交易下单】保存持仓记录出错");
}
return ServerResponse.createBySuccess("挂单成功");
+ }
+
+ /**
+ * 撤销委托单(status=0),退还保证金+买入手续费
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public ServerResponse cancelOrder(String positionSn, HttpServletRequest request) throws Exception {
+ if (StringUtils.isBlank(positionSn)) {
+ return ServerResponse.createByErrorMsg("参数错误");
+ }
+ User user = this.iUserService.getCurrentRefreshUser(request);
+ if (user == null) {
+ return ServerResponse.createByErrorMsg("请先登录");
+ }
+ UserPosition userPosition = this.userPositionMapper.findPositionBySn(positionSn);
+ if (userPosition == null) {
+ return ServerResponse.createByErrorMsg("委托不存在");
+ }
+ if (!user.getId().equals(userPosition.getUserId())) {
+ return ServerResponse.createByErrorMsg("无权操作该委托");
+ }
+ if (userPosition.getStatus() == null || userPosition.getStatus().intValue() != 0) {
+ return ServerResponse.createByErrorMsg("当前订单不可撤单");
+ }
+ if (userPosition.getSellOrderId() != null) {
+ return ServerResponse.createByErrorMsg("当前订单不可撤单");
+ }
+ BigDecimal buyAmtActual = userPosition.getOrderTotalPrice()
+ .divide(new BigDecimal(userPosition.getOrderLever()), 2, 4);
+ BigDecimal buyFee = userPosition.getOrderFee() != null ? userPosition.getOrderFee() : BigDecimal.ZERO;
+ BigDecimal refundAmt = TradeFeeUtil.calcBuyDebit(buyAmtActual, buyFee);
+ User freshUser = this.userMapper.selectByPrimaryKey(user.getId());
+ if (freshUser == null) {
+ throw new Exception("用户不存在");
+ }
+ refundUserEnableOnCancel(freshUser, refundAmt, userPosition);
+ int delCount = this.userPositionMapper.deleteByPrimaryKey(userPosition.getId());
+ if (delCount <= 0) {
+ throw new Exception("撤单失败,删除委托记录失败");
+ }
+ syncUserCacheAfterTrade(request);
+ log.info("【用户撤单】positionSn={} 退还保证金+手续费={}", positionSn, refundAmt);
+ return ServerResponse.createBySuccessMsg("撤单成功");
}
@@ -1055,8 +1153,8 @@
BigDecimal sell_fee_amt = all_sell_amt.multiply(siteSetting.getSellFee()).setScale(2, 4);
log.info("卖出手续费 = {}", sell_fee_amt);
- //總手續費= 買入手續費+賣出手續費+印花稅+遞延費+點差費
- BigDecimal all_fee_amt = buy_fee_amt.add(sell_fee_amt).add(orderSpread).add(orderStayFee).add(spreadRatePrice);
+ // 买入手续费已在下单时扣除,平仓只结算卖出侧费用
+ BigDecimal all_fee_amt = sell_fee_amt.add(orderSpread).add(orderStayFee).add(spreadRatePrice);
log.info("总的手续费费用 = {}", all_fee_amt);
userPosition.setSellOrderId(GeneratePosition.getPositionId());
@@ -1266,7 +1364,7 @@
BigDecimal user_enable_amt = user.getEnableAmt();
log.info("用戶原本總資金 = {} , 可用 = {}", user_all_amt, user_enable_amt);
- BigDecimal buy_fee_amt = all_buy_amt.multiply(siteSetting.getBuyFee()).setScale(2,4);
+ BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFee(all_buy_amt);
log.info("買入手續費 = {}", buy_fee_amt);
BigDecimal orderSpread = all_buy_amt.multiply(siteSetting.getDutyFee()).setScale(2, 4);
@@ -1281,9 +1379,8 @@
BigDecimal sell_fee_amt = all_sell_amt.multiply(siteSetting.getSellFee()).setScale(2, 4);
log.info("賣出手續費 = {}", sell_fee_amt);
- //總手續費= 買入手續費+賣出手續費+印花稅+遞延費+點差費
-// BigDecimal all_fee_amt = buy_fee_amt.add(sell_fee_amt).add(orderSpread).add(orderStayFee).add(spreadRatePrice);
- BigDecimal all_fee_amt = buy_fee_amt.add(sell_fee_amt).add(orderSpread);
+ // 买入手续费已在下单时扣除
+ BigDecimal all_fee_amt = sell_fee_amt.add(orderSpread);
log.info("總的手續費費用 = {}", all_fee_amt);
//复制一条新订单
UserPosition userPositionNew = new UserPosition();
@@ -1425,7 +1522,8 @@
}
- userPosition.setMarginAdd(userPosition.getMarginAdd().add(marginAdd));
+ BigDecimal existMarginAdd = userPosition.getMarginAdd() == null ? BigDecimal.ZERO : userPosition.getMarginAdd();
+ userPosition.setMarginAdd(existMarginAdd.add(marginAdd));
int updatePositionCount = this.userPositionMapper.updateByPrimaryKeySelective(userPosition);
if (updatePositionCount > 0) {
@@ -1823,13 +1921,11 @@
BigDecimal buy_amt_autual = buy_amt.divide(new BigDecimal(lever.intValue()), 2, 4);
- int compareUserAmtInt = user_enable_amt.compareTo(buy_amt_autual);
- log.info("用户可用金额 = {} 实际购买金额 = {}", user_enable_amt, buy_amt_autual);
- log.info("比较 用户金额 和 实际 购买金额 = {}", Integer.valueOf(compareUserAmtInt));
- if (compareUserAmtInt == -1) {
- log.info("下单失败,用户可用金额小于" + buy_amt_autual + "元");
- return ServerResponse.createByErrorMsg("下单失败,用户可用金额小于" + buy_amt_autual + "元");
-
+ BigDecimal buy_fee_amt_check = TradeFeeUtil.calcBuyFee(buy_amt);
+ BigDecimal buy_debit_check = TradeFeeUtil.calcBuyDebit(buy_amt_autual, buy_fee_amt_check);
+ if (user_enable_amt.compareTo(buy_debit_check) < 0) {
+ log.info("下单失败,用户可用金额小于{}元(含保证金及手续费)", buy_debit_check);
+ return ServerResponse.createByErrorMsg("下单失败,用户可用金额小于" + buy_debit_check + "元(含保证金及手续费)");
}
if (user.getUserIndexAmt().compareTo(new BigDecimal("0")) == -1) {
@@ -1886,7 +1982,7 @@
userPosition.setOrderStayDays(1);
- BigDecimal buy_fee_amt = buy_amt.multiply(siteSetting.getBuyFee()).setScale(2, 4);
+ BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFee(buy_amt).setScale(2, 4);
log.info("创建模拟持仓 手续费(配资后总资金 * 百分比) = {}", buy_fee_amt);
userPosition.setOrderFee(buy_fee_amt);
@@ -1931,20 +2027,19 @@
userPosition.setOrderStayFee(new BigDecimal("0"));
userPosition.setSpreadRatePrice(new BigDecimal("0"));
- int insertPositionCount = this.userPositionMapper.insert(userPosition);
- if (insertPositionCount > 0) {
- log.info("【创建持仓】保存记录成功");
- } else {
+ this.userPositionMapper.insert(userPosition);
+ if (userPosition.getId() == null || userPosition.getId() <= 0) {
log.error("【创建持仓】保存记录出错");
+ return ServerResponse.createByErrorMsg("生成持仓失败");
}
- BigDecimal reckon_enable = user_enable_amt.subtract(buy_amt_autual);
- user.setEnableAmt(reckon_enable);
- int updateUserCount = this.userMapper.updateByPrimaryKeySelective(user);
- if (updateUserCount > 0) {
- log.info("【用户交易下单】修改用户金额成功");
- } else {
- log.error("用户交易下单】修改用户金额出错");
-
+ log.info("【创建持仓】保存记录成功");
+ BigDecimal buy_debit = TradeFeeUtil.calcBuyDebit(buy_amt_autual, buy_fee_amt);
+ try {
+ deductUserEnableOnBuy(user, buy_debit, buy_amt_autual, buy_fee_amt, userPosition);
+ } catch (Exception e) {
+ this.userPositionMapper.deleteByPrimaryKey(userPosition.getId());
+ log.error("【创建持仓】扣款失败,已回滚持仓记录", e);
+ return ServerResponse.createByErrorMsg("生成持仓失败:" + e.getMessage());
}
iAgentAgencyFeeService.AgencyFeeIncome(1, userPosition.getPositionSn());
return ServerResponse.createBySuccess("生成持仓成功");
@@ -2375,7 +2470,7 @@
userPosition.setOrderStayDays(1);
userPosition.setOrderTotalPrice(userStockSubscribe.getBond());
- // BigDecimal buy_fee_amt = buy_amt.multiply(siteSetting.getBuyFee()).setScale(2, 4);
+ // BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFee(buy_amt).setScale(2, 4);
BigDecimal buy_fee_amt = new BigDecimal(0);
log.info("用戶購買手續費(配資後總資金 * 百分比) = {}", buy_fee_amt);
userPosition.setOrderFee(buy_fee_amt);
@@ -2649,11 +2744,10 @@
}
- int compareUserAmtInt = user_enable_amt.compareTo(buy_amt_autual);
- log.info("用戶可用金額 = {} 實際購買金額 = {}", user_enable_amt, buy_amt_autual);
- log.info("比較 用戶金額 和 實際 購買金額 = {}", Integer.valueOf(compareUserAmtInt));
- if (compareUserAmtInt == -1) {
- return ServerResponse.createByErrorMsg("下单失败,融资可用金额小于" + buy_amt_autual + "元");
+ BigDecimal buy_fee_amt_dz = TradeFeeUtil.calcBuyFee(buy_amt);
+ BigDecimal buy_debit_dz = TradeFeeUtil.calcBuyDebit(buy_amt_autual, buy_fee_amt_dz);
+ if (user_enable_amt.compareTo(buy_debit_dz) == -1) {
+ return ServerResponse.createByErrorMsg("下单失败,融资可用金额小于" + buy_debit_dz + "元(含保证金及手续费)");
}
// if (user.getUserIndexAmt().compareTo(new BigDecimal("0")) == -1) {
@@ -2690,7 +2784,7 @@
userPosition.setOrderStayDays(1);
- BigDecimal buy_fee_amt = buy_amt.multiply(siteSetting.getBuyFee()).setScale(2, 4);
+ BigDecimal buy_fee_amt = buy_fee_amt_dz;
log.info("用戶購買手續費(配資後總資金 * 百分比) = {}", buy_fee_amt);
userPosition.setOrderFee(buy_fee_amt);
@@ -2723,36 +2817,14 @@
log.info("--------------购买逻辑股票数据 buyDz stock------" + new Gson().toJson(userPosition));
- int insertPositionCount = 0;
this.userPositionMapper.insert(userPosition);
- insertPositionCount = userPosition.getId();
- if (insertPositionCount > 0) {
+ if (userPosition.getId() != null && userPosition.getId() > 0) {
//修改大宗剩余
stockDz.setStockShare(stockDz.getStockShare() - num);
stockDz.setStockSurplus(stockDz.getStockSurplus() + num);
stockDzMapper.updateById(stockDz);
- //修改用戶可用余額= 當前余額-下單金額-買入手續費-印花稅-點差費
- //BigDecimal reckon_enable = user_enable_amt.subtract(buy_amt_autual).subtract(buy_fee_amt).subtract(buy_yhs_amt).subtract(spread_rate_amt);
- //修改用戶可用余額= 當前余額-下單總金額
- BigDecimal reckon_enable = user_enable_amt.subtract(buy_amt_autual);
- //修改用戶可取余額=當前可取余額-下單總金額
- int compareUserWithdrawAmtInt = user_enable_withdraw_amt.compareTo(buy_amt_autual);
- if (compareUserWithdrawAmtInt == -1) {
- //若可取余額小於下單總額,但是可用余額充足,令可取余額為0
- user.setEnaleWithdrawAmt(BigDecimal.ZERO);
- } else {
- user_enable_withdraw_amt = user_enable_withdraw_amt.subtract(buy_amt_autual);
- user.setEnaleWithdrawAmt(user_enable_withdraw_amt);
- }
- user.setEnableAmt(reckon_enable);
- int updateUserCount = this.userMapper.updateByPrimaryKeySelective(user);
- if (updateUserCount > 0) {
- log.info("【用戶交易下單】修改用戶金額成功");
- } else {
- log.error("用戶交易下單】修改用戶金額出錯");
- throw new Exception("用戶交易下單】修改用戶金額出錯");
- }
- //核算代理收入-入倉手續費
+ deductUserEnableOnBuy(user, buy_debit_dz, buy_amt_autual, buy_fee_amt_dz, userPosition);
+ syncUserCacheAfterTrade(request);
iAgentAgencyFeeService.AgencyFeeIncome(1, userPosition.getPositionSn());
log.info("【用戶交易下單】保存持倉記錄成功");
} else {
@@ -2980,11 +3052,10 @@
}
- int compareUserAmtInt = user_enable_amt.compareTo(buy_amt_autual);
- log.info("用戶可用金額 = {} 實際購買金額 = {}", user_enable_amt, buy_amt_autual);
- log.info("比較 用戶金額 和 實際 購買金額 = {}", Integer.valueOf(compareUserAmtInt));
- if (compareUserAmtInt == -1) {
- return ServerResponse.createByErrorMsg("下單失敗,可用金額小於" + buy_amt_autual + "元");
+ BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFee(buy_amt);
+ BigDecimal buy_debit = TradeFeeUtil.calcBuyDebit(buy_amt_autual, buy_fee_amt);
+ if (user_enable_amt.compareTo(buy_debit) == -1) {
+ return ServerResponse.createByErrorMsg("下單失敗,可用金額小於" + buy_debit + "元(含保证金及手续费)");
}
// if (user.getUserIndexAmt().compareTo(new BigDecimal("0")) == -1) {
@@ -3038,7 +3109,6 @@
userPosition.setOrderStayDays(1);
- BigDecimal buy_fee_amt = buy_amt.multiply(siteSetting.getBuyFee()).setScale(2, 4);
log.info("用戶購買手續費(配資後總資金 * 百分比) = {}", buy_fee_amt);
userPosition.setOrderFee(buy_fee_amt);
@@ -3072,31 +3142,10 @@
log.info("--------------购买逻辑股票数据 buyVipQc stock------" + new Gson().toJson(userPosition));
- int insertPositionCount = 0;
this.userPositionMapper.insert(userPosition);
- insertPositionCount = userPosition.getId();
- if (insertPositionCount > 0) {
- //修改用戶可用余額= 當前余額-下單金額-買入手續費-印花稅-點差費
- //BigDecimal reckon_enable = user_enable_amt.subtract(buy_amt_autual).subtract(buy_fee_amt).subtract(buy_yhs_amt).subtract(spread_rate_amt);
- //修改用戶可用余額= 當前余額-下單總金額
- BigDecimal reckon_enable = user_enable_amt.subtract(buy_amt_autual);
- //修改用戶可取余額=當前可取余額-下單總金額
- int compareUserWithdrawAmtInt = user_enable_withdraw_amt.compareTo(buy_amt_autual);
- if (compareUserWithdrawAmtInt < 0) {
- //若可取余額小於下單總額,但是可用余額充足,令可取余額為0
- user.setEnaleWithdrawAmt(BigDecimal.ZERO);
- } else {
- user_enable_withdraw_amt = user_enable_withdraw_amt.subtract(buy_amt_autual);
- user.setEnaleWithdrawAmt(user_enable_withdraw_amt);
- }
- user.setEnableAmt(reckon_enable);
- int updateUserCount = this.userMapper.updateByPrimaryKeySelective(user);
- if (updateUserCount > 0) {
- log.info("【用戶交易下單】修改用戶金額成功");
- } else {
- log.error("用戶交易下單】修改用戶金額出錯");
- throw new Exception("用戶交易下單】修改用戶金額出錯");
- }
+ if (userPosition.getId() != null && userPosition.getId() > 0) {
+ deductUserEnableOnBuy(user, buy_debit, buy_amt_autual, buy_fee_amt, userPosition);
+ syncUserCacheAfterTrade(request);
//核算代理收入-入倉手續費
//iAgentAgencyFeeService.AgencyFeeIncome(1, userPosition.getPositionSn());
log.info("【用戶交易下單】保存持倉記錄成功");
diff --git a/src/main/java/com/nq/service/impl/UserServiceImpl.java b/src/main/java/com/nq/service/impl/UserServiceImpl.java
index cfbc8b4..59bb0d5 100644
--- a/src/main/java/com/nq/service/impl/UserServiceImpl.java
+++ b/src/main/java/com/nq/service/impl/UserServiceImpl.java
@@ -310,18 +310,11 @@
stock.setStockName(stockFutures.getFuturesName());
stock.setIsLock(0);
}
- } else if(code.contains("sh") || code.contains("sz")){
- return ServerResponse.createByErrorMsg("添加失败,指数不支持自选");
-// StockIndex stockIndex = this.stockIndexMapper.selectIndexByCode(stockcode);
-// if(stockIndex != null){
-// stock.setId(stockIndex.getId());
-// stock.setStockCode(stockIndex.getIndexCode());
-// stock.setStockGid(stockIndex.getIndexGid()+"zs");
-// stock.setStockName(stockIndex.getIndexName());
-// stock.setIsLock(0);
-// }
} else {
- stock = this.stockMapper.findStockByCode(code);
+ stock = this.stockMapper.findStockByCode(stockcode);
+ if (stock == null && !code.equals(stockcode)) {
+ stock = this.stockMapper.findStockByCode(code);
+ }
}
if (stock == null) {
return ServerResponse.createByErrorMsg("添加失败,股票不存在");
@@ -400,6 +393,26 @@
return ServerResponse.createBySuccess(userInfoVO);
}
+ @Override
+ public void syncUserCache(HttpServletRequest request) {
+ if (request == null) {
+ return;
+ }
+ String cookieName = PropertiesUtil.getProperty("user.cookie.name");
+ String loginToken = request.getHeader(cookieName);
+ if (StringUtils.isBlank(loginToken)) {
+ return;
+ }
+ String userJson = RedisShardedPoolUtils.get(loginToken);
+ User cached = (User) JsonUtil.string2Obj(userJson, User.class);
+ if (cached == null || cached.getId() == null) {
+ return;
+ }
+ User dbUser = this.userMapper.selectByPrimaryKey(cached.getId());
+ if (dbUser != null) {
+ RedisShardedPoolUtils.setEx(loginToken, JsonUtil.obj2String(dbUser), 9999);
+ }
+ }
public ServerResponse updatePwd(String oldPwd, String newPwd, HttpServletRequest request) {
if (StringUtils.isBlank(oldPwd) || StringUtils.isBlank(newPwd)) {
@@ -1874,10 +1887,20 @@
BigDecimal allProfitAndLose = positionVO.getAllProfitAndLose();
userInfoVO.setAllProfitAndLose(allProfitAndLose);
-// BigDecimal userAllAmt = user.getUserAmt();
- BigDecimal userAllAmt = user.getEnableAmt();
- userAllAmt = userAllAmt.add(allProfitAndLose);
-
+ // 账户总资产 = 两融可用 + 冻结保证金 + 浮动盈亏
+ // 浮动盈亏里已扣过买入手续费,而买入手续费下单时已从 enableAmt 扣除,此处加回 openBuyFees 避免总资产「少扣手续费」
+ BigDecimal allFreezAmt = positionVO.getAllFreezAmt() == null ? BigDecimal.ZERO : positionVO.getAllFreezAmt();
+ BigDecimal enableAmt = user.getEnableAmt() == null ? BigDecimal.ZERO : user.getEnableAmt();
+ BigDecimal openBuyFees = BigDecimal.ZERO;
+ List<UserPosition> openPositions = this.iUserPositionService.findPositionByUserIdAndSellIdIsNull(user.getId());
+ if (openPositions != null) {
+ for (UserPosition position : openPositions) {
+ if (position.getOrderFee() != null) {
+ openBuyFees = openBuyFees.add(position.getOrderFee());
+ }
+ }
+ }
+ BigDecimal userAllAmt = enableAmt.add(allFreezAmt).add(allProfitAndLose).add(openBuyFees);
userInfoVO.setEnableIndexAmt(user.getEnableIndexAmt());
userInfoVO.setEnaleWithdrawAmt(user.getEnaleWithdrawAmt());
@@ -1897,8 +1920,7 @@
}
}
userInfoVO.setBuyAmtAutual(buyAmtAutual);
- userAllAmt = userAllAmt.add(buyAmtAutual);
- userInfoVO.setUserAmt(userAllAmt);
+ userInfoVO.setUserAmt(userAllAmt.setScale(2, RoundingMode.HALF_UP));
List<UserPosition> userPositions = this.userPositionMapper.findMyPositionByCodeAndSpell(user.getId(), "", "", 2);
diff --git a/src/main/java/com/nq/service/impl/UserStockSubscribeServiceImpl.java b/src/main/java/com/nq/service/impl/UserStockSubscribeServiceImpl.java
index 25d7cba..636d57c 100644
--- a/src/main/java/com/nq/service/impl/UserStockSubscribeServiceImpl.java
+++ b/src/main/java/com/nq/service/impl/UserStockSubscribeServiceImpl.java
@@ -814,7 +814,7 @@
userPosition.setOrderStayDays(1);
- BigDecimal buy_fee_amt = buy_amt.multiply(siteSetting.getBuyFee()).setScale(2, 4);
+ BigDecimal buy_fee_amt = com.nq.utils.TradeFeeUtil.calcBuyFee(buy_amt);
log.info("创建模拟持仓 手续费(配资后总资金 * 百分比) = {}", buy_fee_amt);
userPosition.setOrderFee(buy_fee_amt);
diff --git a/src/main/java/com/nq/utils/stock/sina/SinaStockApi.java b/src/main/java/com/nq/utils/stock/sina/SinaStockApi.java
index 8ed4485..9364539 100644
--- a/src/main/java/com/nq/utils/stock/sina/SinaStockApi.java
+++ b/src/main/java/com/nq/utils/stock/sina/SinaStockApi.java
@@ -37,17 +37,25 @@
public static String getSinaStock(String stockGid) {
String sina_result = "";
try {
-// System.out.println(sina_url + stockGid);
-// sina_result = HttpClientRequest.doGet(sina_url + stockGid);
-// System.out.println("请求返回:"+sina_result);
System.out.println(PropertiesUtil.getProperty("sina.single.stock.proxy.url") + stockGid);
sina_result = HttpClientRequest.doGet(PropertiesUtil.getProperty("sina.single.stock.proxy.url") + stockGid);
- System.out.println("请求返回:"+sina_result);
-// sina_result = "var hq_str_sz300270=\"中威电子,0.000,11.710,0.000,0.000,0.000,0.000,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,0,0.000,2025-12-03,09:10:06,00\";";
+ System.out.println("请求返回:" + sina_result);
} catch (Exception e) {
- log.error("获取股票行情出错,错误信息 = {}", e);
+ log.error("获取股票行情出错,gid={},错误信息 = {}", stockGid, e);
}
- return sina_result.substring(sina_result.indexOf("=") + 2);
+ if (StringUtils.isBlank(sina_result) || !sina_result.contains("=")) {
+ log.warn("新浪行情返回为空或格式异常, gid={}, raw={}", stockGid, StringUtils.abbreviate(sina_result, 200));
+ return "";
+ }
+ String body = sina_result.substring(sina_result.indexOf("=") + 2);
+ return body.replace("\"", "").replace(";", "").trim();
+ }
+
+ private static String hqField(String[] hqarr, int index) {
+ if (hqarr == null || index < 0 || index >= hqarr.length) {
+ return "0";
+ }
+ return StringUtils.defaultIfBlank(hqarr[index], "0");
}
@@ -198,57 +206,57 @@
public static StockVO assembleStockVO(String sinaResult) {
StockVO stockVO = new StockVO();
-
+ if (StringUtils.isBlank(sinaResult)) {
+ return stockVO;
+ }
String[] hqarr = sinaResult.split(",");
+ if (hqarr.length < 4) {
+ log.warn("新浪行情字段不足,无法解析,length={}, raw={}", hqarr.length, StringUtils.abbreviate(sinaResult, 200));
+ return stockVO;
+ }
- stockVO.setName(hqarr[0]);
-
- stockVO.setNowPrice(hqarr[3]);
+ stockVO.setName(hqField(hqarr, 0));
+ stockVO.setNowPrice(hqField(hqarr, 3));
BigDecimal chang_rate = new BigDecimal("0");
- if ((new BigDecimal(hqarr[2])).compareTo(new BigDecimal("0")) != 0 && new BigDecimal(hqarr[3]).compareTo(new BigDecimal("0")) != 0) {
-
- chang_rate = (new BigDecimal(hqarr[3])).subtract(new BigDecimal(hqarr[2]));
-
- chang_rate = chang_rate.multiply(new BigDecimal("100")).divide(new BigDecimal(hqarr[2]), 2, RoundingMode.HALF_UP);
+ BigDecimal preclose = new BigDecimal(hqField(hqarr, 2));
+ BigDecimal now = new BigDecimal(hqField(hqarr, 3));
+ if (preclose.compareTo(BigDecimal.ZERO) != 0 && now.compareTo(BigDecimal.ZERO) != 0) {
+ chang_rate = now.subtract(preclose);
+ chang_rate = chang_rate.multiply(new BigDecimal("100")).divide(preclose, 2, RoundingMode.HALF_UP);
}
stockVO.setHcrate(chang_rate);
- stockVO.setToday_max(hqarr[4]);
+ stockVO.setToday_max(hqField(hqarr, 4));
+ stockVO.setToday_min(hqField(hqarr, 5));
+ stockVO.setBusiness_amount(hqField(hqarr, 8));
+ stockVO.setBusiness_balance(hqField(hqarr, 9));
+ stockVO.setPreclose_px(hqField(hqarr, 2));
+ stockVO.setOpen_px(hqField(hqarr, 1));
- stockVO.setToday_min(hqarr[5]);
+ stockVO.setBuy1(hqField(hqarr, 6));
+ stockVO.setBuy2(hqField(hqarr, 13));
+ stockVO.setBuy3(hqField(hqarr, 15));
+ stockVO.setBuy4(hqField(hqarr, 17));
+ stockVO.setBuy5(hqField(hqarr, 19));
- stockVO.setBusiness_amount(hqarr[8]);
+ stockVO.setSell1(hqField(hqarr, 7));
+ stockVO.setSell2(hqField(hqarr, 23));
+ stockVO.setSell3(hqField(hqarr, 25));
+ stockVO.setSell4(hqField(hqarr, 27));
+ stockVO.setSell5(hqField(hqarr, 29));
- stockVO.setBusiness_balance(hqarr[9]);
+ stockVO.setBuy1_num(hqField(hqarr, 10));
+ stockVO.setBuy2_num(hqField(hqarr, 12));
+ stockVO.setBuy3_num(hqField(hqarr, 14));
+ stockVO.setBuy4_num(hqField(hqarr, 16));
+ stockVO.setBuy5_num(hqField(hqarr, 18));
- stockVO.setPreclose_px(hqarr[2]);
-
- stockVO.setOpen_px(hqarr[1]);
-
- stockVO.setBuy1(hqarr[6]);
- stockVO.setBuy2(hqarr[13]);
- stockVO.setBuy3(hqarr[15]);
- stockVO.setBuy4(hqarr[17]);
- stockVO.setBuy5(hqarr[19]);
-
- stockVO.setSell1(hqarr[7]);
- stockVO.setSell2(hqarr[23]);
- stockVO.setSell3(hqarr[25]);
- stockVO.setSell4(hqarr[27]);
- stockVO.setSell5(hqarr[29]);
-
- stockVO.setBuy1_num(hqarr[10]);
- stockVO.setBuy2_num(hqarr[12]);
- stockVO.setBuy3_num(hqarr[14]);
- stockVO.setBuy4_num(hqarr[16]);
- stockVO.setBuy5_num(hqarr[18]);
-
- stockVO.setSell1_num(hqarr[20]);
- stockVO.setSell2_num(hqarr[22]);
- stockVO.setSell3_num(hqarr[24]);
- stockVO.setSell4_num(hqarr[26]);
- stockVO.setSell5_num(hqarr[28]);
+ stockVO.setSell1_num(hqField(hqarr, 20));
+ stockVO.setSell2_num(hqField(hqarr, 22));
+ stockVO.setSell3_num(hqField(hqarr, 24));
+ stockVO.setSell4_num(hqField(hqarr, 26));
+ stockVO.setSell5_num(hqField(hqarr, 28));
return stockVO;
}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 1c434fb..4812e0d 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -217,7 +217,16 @@
#?????
-website.domain.url=http://www.huijuwang888.com
+# Ococn ??
+ococn.pay.pid=185583446
+ococn.pay.key=JcMJIbNUAcq0GyMf
+ococn.pay.url=https://pay.ococn.cn/submit.php
+ococn.pay.sitename=
+ococn.pay.name=账户充值
+ococn.pay.frontend_redirect=https://www.zhonghenginvest.com/#/user
+
+website.domain.url=https://api.zhonghenginvest.com
+frontend.domain.url=https://www.zhonghenginvest.com
website.token=0DC8F78384C7AAFF3192A9C60A473FEE7F89C62888689616B98A06910E86B510
#?????
--
Gitblit v1.9.3