From 1f19b78818609bf6d0849f8e58b349132a800538 Mon Sep 17 00:00:00 2001
From: zj <1772600164@qq.com>
Date: Mon, 01 Jun 2026 17:08:49 +0800
Subject: [PATCH] 1
---
src/main/java/com/nq/utils/TradeFeeUtil.java | 29 +++++
.idea/git_toolbox_prj.xml | 15 +++
src/main/resources/mapper/UserPositionMapper.xml | 11 ++
src/main/java/com/nq/controller/protol/UserController.java | 7
src/main/java/com/nq/vo/pay/OcocnPayVO.java | 13 ++
.idea/A-stock.iml | 9 +
src/main/java/com/nq/service/impl/UserPositionServiceImpl.java | 69 ++++++++-----
src/main/java/com/nq/service/IUserPositionService.java | 2
src/main/java/com/nq/utils/DateTimeUtil.java | 8 +
src/main/java/com/nq/utils/pay/OcocnPayUtil.java | 97 +++++++++++++++++++
10 files changed, 229 insertions(+), 31 deletions(-)
diff --git a/.idea/A-stock.iml b/.idea/A-stock.iml
new file mode 100644
index 0000000..d6ebd48
--- /dev/null
+++ b/.idea/A-stock.iml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$" />
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module>
\ No newline at end of file
diff --git a/.idea/git_toolbox_prj.xml b/.idea/git_toolbox_prj.xml
new file mode 100644
index 0000000..02b915b
--- /dev/null
+++ b/.idea/git_toolbox_prj.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="GitToolBoxProjectSettings">
+ <option name="commitMessageIssueKeyValidationOverride">
+ <BoolValueOverride>
+ <option name="enabled" value="true" />
+ </BoolValueOverride>
+ </option>
+ <option name="commitMessageValidationEnabledOverride">
+ <BoolValueOverride>
+ <option name="enabled" value="true" />
+ </BoolValueOverride>
+ </option>
+ </component>
+</project>
\ No newline at end of file
diff --git a/src/main/java/com/nq/controller/protol/UserController.java b/src/main/java/com/nq/controller/protol/UserController.java
index bfa1b4b..86c31c3 100644
--- a/src/main/java/com/nq/controller/protol/UserController.java
+++ b/src/main/java/com/nq/controller/protol/UserController.java
@@ -92,8 +92,7 @@
public ServerResponse buy(@RequestParam("stockId") Integer stockId, @RequestParam("buyNum") Integer buyNum, @RequestParam("buyType") Integer buyType, @RequestParam("lever") Integer lever,@RequestParam(value = "profitTarget",required = false) BigDecimal profitTarget,@RequestParam(value = "stopTarget",required = false) BigDecimal stopTarget, HttpServletRequest request) {
ServerResponse serverResponse = null;
try {
-// serverResponse = this.iUserPositionService.buy(stockId, buyNum, buyType, lever,profitTarget,stopTarget, request);
- serverResponse = this.iUserPositionService.pending(stockId, buyNum, buyType, lever,profitTarget,stopTarget, request);
+ serverResponse = this.iUserPositionService.buy(stockId, buyNum, buyType, lever, profitTarget, stopTarget, request);
} catch (Exception e) {
log.error("用户下单操作 = {}", e);
}
@@ -103,10 +102,10 @@
//用户下单买入股票
@RequestMapping({"fee.do"})
@ResponseBody
- public ServerResponse fee(@RequestParam("buyNum") Integer buyNum,@RequestParam("nowPrice") BigDecimal nowPrice,HttpServletRequest request) {
+ public ServerResponse fee(@RequestParam("buyNum") Integer buyNum, @RequestParam("nowPrice") BigDecimal nowPrice, @RequestParam(value = "lever", required = false, defaultValue = "1") Integer lever, HttpServletRequest request) {
ServerResponse serverResponse = null;
try {
- serverResponse = this.iUserPositionService.fee(buyNum,nowPrice);
+ serverResponse = this.iUserPositionService.fee(buyNum, nowPrice, lever);
} catch (Exception e) {
log.error("用户下单操作 = {}", e);
}
diff --git a/src/main/java/com/nq/service/IUserPositionService.java b/src/main/java/com/nq/service/IUserPositionService.java
index ee57f2b..2ae7002 100644
--- a/src/main/java/com/nq/service/IUserPositionService.java
+++ b/src/main/java/com/nq/service/IUserPositionService.java
@@ -26,7 +26,7 @@
ServerResponse lock(Integer paramInteger1, Integer paramInteger2, String paramString,Integer paramInteger3);
- ServerResponse fee(Integer buyNum,BigDecimal nowPrice);
+ ServerResponse fee(Integer buyNum, BigDecimal nowPrice, Integer lever);
ServerResponse del(Integer paramInteger);
diff --git a/src/main/java/com/nq/service/impl/UserPositionServiceImpl.java b/src/main/java/com/nq/service/impl/UserPositionServiceImpl.java
index 5162932..2da1050 100644
--- a/src/main/java/com/nq/service/impl/UserPositionServiceImpl.java
+++ b/src/main/java/com/nq/service/impl/UserPositionServiceImpl.java
@@ -299,10 +299,10 @@
BigDecimal ztRate = chaPrice.multiply(new BigDecimal("100")).divide(zsPrice, 2, 4);
log.info("当前涨跌幅 = {} % , 涨停幅度 = {} %", Double.valueOf(stock_crease), ztRate);
- if ((new BigDecimal(String.valueOf(stock_crease))).compareTo(ztRate) == 0 && buyType
- .intValue() == 0) {
- return ServerResponse.createByErrorMsg("当前股票已涨停不能买涨");
- }
+// if ((new BigDecimal(String.valueOf(stock_crease))).compareTo(ztRate) == 0 && buyType
+// .intValue() == 0) {
+// return ServerResponse.createByErrorMsg("当前股票已涨停不能买涨");
+// }
if (stock.getStockPlate() == null || StringUtils.isEmpty(stock.getStockPlate())) {
@@ -388,7 +388,7 @@
}
- BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFee(buy_amt);
+ BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFee(buy_amt_autual);
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);
@@ -479,6 +479,7 @@
userPosition.setOrderStayDays(Integer.valueOf(0));
userPosition.setOrderStayFee(new BigDecimal("0"));
+ userPosition.setStatus(1);
this.userPositionMapper.insert(userPosition);
if (userPosition.getId() != null && userPosition.getId() > 0) {
@@ -496,9 +497,10 @@
@Override
- public ServerResponse fee(Integer buyNum, BigDecimal nowPrice) {
+ public ServerResponse fee(Integer buyNum, BigDecimal nowPrice, Integer lever) {
+ int leverValue = (lever == null || lever <= 0) ? 1 : lever;
BigDecimal buy_amt = nowPrice.multiply(new BigDecimal(buyNum.intValue()));
- BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFee(buy_amt).setScale(2, 4);
+ BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFeeByNotional(buy_amt, leverValue).setScale(2, 4);
return ServerResponse.createBySuccess(buy_fee_amt);
}
@@ -788,7 +790,7 @@
.getBuyMaxAmtPercent().multiply(new BigDecimal("100")) + "%");
}
- BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFee(buy_amt);
+ BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFee(buy_amt_autual);
BigDecimal buy_debit = TradeFeeUtil.calcBuyDebit(buy_amt_autual, buy_fee_amt);
if (user_enable_amt.compareTo(buy_debit) == -1) {
return ServerResponse.createByErrorMsg("挂单失败,融资可用金额小于" + buy_debit + "元(含保证金及手续费)");
@@ -950,6 +952,20 @@
}
+ private ServerResponse validateT1BeforeSell(UserPosition userPosition, int doType) {
+ if (doType == 0 || userPosition == null || userPosition.getBuyOrderTime() == null) {
+ return null;
+ }
+ String stockGid = userPosition.getStockGid();
+ if (stockGid != null && (stockGid.contains("us") || stockGid.contains("hk"))) {
+ return null;
+ }
+ if (!DateTimeUtil.canSellByT1(userPosition.getBuyOrderTime())) {
+ return ServerResponse.createByErrorMsg("T+1交易制度,当日买入的股票需下一交易日才能平仓");
+ }
+ return null;
+ }
+
public ServerResponse sell(String positionSn, int doType) throws Exception {
log.info("【用戶交易平倉】 positionSn = {} , dotype = {}", positionSn, Integer.valueOf(doType));
@@ -1028,10 +1044,9 @@
return ServerResponse.createByErrorMsg("平仓失败,此订单已平仓");
}
- if (DateTimeUtil.isCanSellOneday(userPosition.getBuyOrderTime(), siteSetting.getCantSellTimes().intValue()) && siteProduct.getTranWithdrawDisplay()) {
- // return ServerResponse.createByErrorMsg(siteSetting.getCantSellTimes() + "分鐘內不能平倉");
-
- return ServerResponse.createByErrorMsg("当日成交不可平仓");
+ ServerResponse t1Check = validateT1BeforeSell(userPosition, doType);
+ if (t1Check != null) {
+ return t1Check;
}
if (1 == userPosition.getIsLock().intValue()) {
@@ -1110,10 +1125,10 @@
ztRate = ztRate.negate();
log.info("股票當前漲跌幅 = {} 跌停幅度 = {}", Double.valueOf(stock_crease), ztRate);
- if ((new BigDecimal(String.valueOf(stock_crease))).compareTo(ztRate) == 0 && "買漲"
- .equals(userPosition.getOrderDirection())) {
- return ServerResponse.createByErrorMsg("当前股票已跌停不能卖出");
- }
+// if ((new BigDecimal(String.valueOf(stock_crease))).compareTo(ztRate) == 0 && "買漲"
+// .equals(userPosition.getOrderDirection())) {
+// return ServerResponse.createByErrorMsg("当前股票已跌停不能卖出");
+// }
Integer buy_num = userPosition.getOrderNum();
@@ -1204,7 +1219,7 @@
ucd.setUserName(user.getRealName());
ucd.setDeType("总盈亏");
ucd.setDeAmt(all_profit);
- ucd.setDeSummary("卖出股票," + userPosition.getStockCode() + "/" + userPosition.getStockName() + ",占用本金:" + freez_amt + ",总手续费:" + all_fee_amt + ",递延费:" + orderStayFee + ",印花稅:" + orderSpread + ",盈亏:" + profitLoss + ",总盈亏:" + all_profit);
+ ucd.setDeSummary("卖出股票," + userPosition.getStockCode() + "/" + userPosition.getStockName() + ",占用本金:" + freez_amt + ",总手续费:" + all_fee_amt + ",盈亏:" + profitLoss + ",总盈亏:" + all_profit);
ucd.setAddTime(new Date());
ucd.setIsRead(Integer.valueOf(0));
@@ -1299,8 +1314,9 @@
if (1 == userPosition.getIsLock().intValue()) {
return ServerResponse.createByErrorMsg("平仓失败 " + userPosition.getLockMsg());
}
- if (!DateTimeUtil.isCanSell(userPosition.getBuyOrderTime(), siteSetting.getCantSellTimes().intValue())) {
- return ServerResponse.createByErrorMsg("当日成交不可平仓");
+ ServerResponse t1Check = validateT1BeforeSell(userPosition, 1);
+ if (t1Check != null) {
+ return t1Check;
}
BigDecimal now_price;
@@ -1364,7 +1380,9 @@
BigDecimal user_enable_amt = user.getEnableAmt();
log.info("用戶原本總資金 = {} , 可用 = {}", user_all_amt, user_enable_amt);
- BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFee(all_buy_amt);
+ BigDecimal partialMargin = all_buy_amt.divide(
+ new BigDecimal(userPosition.getOrderLever()), 2, RoundingMode.HALF_UP);
+ BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFee(partialMargin);
log.info("買入手續費 = {}", buy_fee_amt);
BigDecimal orderSpread = all_buy_amt.multiply(siteSetting.getDutyFee()).setScale(2, 4);
@@ -1921,7 +1939,7 @@
BigDecimal buy_amt_autual = buy_amt.divide(new BigDecimal(lever.intValue()), 2, 4);
- BigDecimal buy_fee_amt_check = TradeFeeUtil.calcBuyFee(buy_amt);
+ BigDecimal buy_fee_amt_check = TradeFeeUtil.calcBuyFee(buy_amt_autual);
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);
@@ -1982,8 +2000,8 @@
userPosition.setOrderStayDays(1);
- BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFee(buy_amt).setScale(2, 4);
- log.info("创建模拟持仓 手续费(配资后总资金 * 百分比) = {}", buy_fee_amt);
+ BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFee(buy_amt_autual).setScale(2, 4);
+ log.info("创建模拟持仓 手续费(保证金 * 百分比) = {}", buy_fee_amt);
userPosition.setOrderFee(buy_fee_amt);
@@ -2026,6 +2044,7 @@
userPosition.setOrderStayDays(Integer.valueOf(0));
userPosition.setOrderStayFee(new BigDecimal("0"));
userPosition.setSpreadRatePrice(new BigDecimal("0"));
+ userPosition.setStatus(1);
this.userPositionMapper.insert(userPosition);
if (userPosition.getId() == null || userPosition.getId() <= 0) {
@@ -2744,7 +2763,7 @@
}
- BigDecimal buy_fee_amt_dz = TradeFeeUtil.calcBuyFee(buy_amt);
+ BigDecimal buy_fee_amt_dz = TradeFeeUtil.calcBuyFee(buy_amt_autual);
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 + "元(含保证金及手续费)");
@@ -3052,7 +3071,7 @@
}
- BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFee(buy_amt);
+ BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFee(buy_amt_autual);
BigDecimal buy_debit = TradeFeeUtil.calcBuyDebit(buy_amt_autual, buy_fee_amt);
if (user_enable_amt.compareTo(buy_debit) == -1) {
return ServerResponse.createByErrorMsg("下單失敗,可用金額小於" + buy_debit + "元(含保证金及手续费)");
diff --git a/src/main/java/com/nq/utils/DateTimeUtil.java b/src/main/java/com/nq/utils/DateTimeUtil.java
index acd6263..6f77877 100644
--- a/src/main/java/com/nq/utils/DateTimeUtil.java
+++ b/src/main/java/com/nq/utils/DateTimeUtil.java
@@ -223,6 +223,14 @@
}
}
+ /** T+1:买入当日不可卖出,下一交易日方可平仓 */
+ public static boolean canSellByT1(Date buyDate) {
+ if (buyDate == null) {
+ return true;
+ }
+ return !sameDate(getCurrentDate(), buyDate);
+ }
+
/*日期年月日是否相同*/
public static boolean sameDate(Date d1, Date d2) {
SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd");
diff --git a/src/main/java/com/nq/utils/TradeFeeUtil.java b/src/main/java/com/nq/utils/TradeFeeUtil.java
new file mode 100644
index 0000000..195ed57
--- /dev/null
+++ b/src/main/java/com/nq/utils/TradeFeeUtil.java
@@ -0,0 +1,29 @@
+package com.nq.utils;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+public class TradeFeeUtil {
+
+ public static final BigDecimal BUY_FEE_RATE = new BigDecimal("0.03");
+
+ private TradeFeeUtil() {
+ }
+
+ public static BigDecimal calcBuyFee(BigDecimal buyAmt) {
+ if (buyAmt == null) {
+ return BigDecimal.ZERO;
+ }
+ return buyAmt.multiply(BUY_FEE_RATE).setScale(2, RoundingMode.HALF_UP);
+ }
+
+ public static BigDecimal calcBuyFeeByNotional(BigDecimal notional, Integer lever) {
+ return calcBuyFee(notional);
+ }
+
+ public static BigDecimal calcBuyDebit(BigDecimal margin, BigDecimal fee) {
+ BigDecimal marginAmt = margin == null ? BigDecimal.ZERO : margin;
+ BigDecimal feeAmt = fee == null ? BigDecimal.ZERO : fee;
+ return marginAmt.add(feeAmt).setScale(2, RoundingMode.HALF_UP);
+ }
+}
diff --git a/src/main/java/com/nq/utils/pay/OcocnPayUtil.java b/src/main/java/com/nq/utils/pay/OcocnPayUtil.java
new file mode 100644
index 0000000..8d0519e
--- /dev/null
+++ b/src/main/java/com/nq/utils/pay/OcocnPayUtil.java
@@ -0,0 +1,97 @@
+package com.nq.utils.pay;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class OcocnPayUtil {
+
+ private OcocnPayUtil() {
+ }
+
+ public static String encode(String value) {
+ if (value == null) {
+ return "";
+ }
+ try {
+ return URLEncoder.encode(value, StandardCharsets.UTF_8.name());
+ } catch (UnsupportedEncodingException e) {
+ return value;
+ }
+ }
+
+ public static String buildSubmitSign(String money, String productName, String notifyUrl,
+ String ordersn, String pid, String returnUrl,
+ String sitename, String payType, String key) {
+ Map<String, String> params = new TreeMap<>();
+ params.put("money", money);
+ params.put("name", productName);
+ params.put("notify_url", notifyUrl);
+ params.put("out_trade_no", ordersn);
+ params.put("pid", pid);
+ params.put("return_url", returnUrl);
+ if (StringUtils.isNotBlank(sitename)) {
+ params.put("sitename", sitename);
+ }
+ params.put("type", payType);
+ return md5Sign(params, key);
+ }
+
+ public static Map<String, String> parseRequestParams(HttpServletRequest request) {
+ Map<String, String> result = new HashMap<>();
+ if (request == null) {
+ return result;
+ }
+ Map<String, String[]> parameterMap = request.getParameterMap();
+ for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
+ String[] values = entry.getValue();
+ if (values != null && values.length > 0) {
+ result.put(entry.getKey(), values[0]);
+ }
+ }
+ return result;
+ }
+
+ public static boolean verifyNotifySign(Map<String, String> params, String key) {
+ if (params == null || StringUtils.isBlank(key)) {
+ return false;
+ }
+ String remoteSign = params.get("sign");
+ if (StringUtils.isBlank(remoteSign)) {
+ return false;
+ }
+ Map<String, String> signParams = new TreeMap<>();
+ for (Map.Entry<String, String> entry : params.entrySet()) {
+ String name = entry.getKey();
+ String value = entry.getValue();
+ if ("sign".equalsIgnoreCase(name) || "sign_type".equalsIgnoreCase(name)) {
+ continue;
+ }
+ if (StringUtils.isBlank(value)) {
+ continue;
+ }
+ signParams.put(name, value);
+ }
+ String localSign = md5Sign(signParams, key);
+ return remoteSign.equalsIgnoreCase(localSign);
+ }
+
+ private static String md5Sign(Map<String, String> params, String key) {
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<String, String> entry : params.entrySet()) {
+ if (sb.length() > 0) {
+ sb.append('&');
+ }
+ sb.append(entry.getKey()).append('=').append(entry.getValue());
+ }
+ sb.append(key);
+ return DigestUtils.md5Hex(sb.toString()).toLowerCase();
+ }
+}
diff --git a/src/main/java/com/nq/vo/pay/OcocnPayVO.java b/src/main/java/com/nq/vo/pay/OcocnPayVO.java
new file mode 100644
index 0000000..bf7c23a
--- /dev/null
+++ b/src/main/java/com/nq/vo/pay/OcocnPayVO.java
@@ -0,0 +1,13 @@
+package com.nq.vo.pay;
+
+public class OcocnPayVO {
+ private String payUrl;
+
+ public String getPayUrl() {
+ return payUrl;
+ }
+
+ public void setPayUrl(String payUrl) {
+ this.payUrl = payUrl;
+ }
+}
diff --git a/src/main/resources/mapper/UserPositionMapper.xml b/src/main/resources/mapper/UserPositionMapper.xml
index 6a99a68..86e2306 100644
--- a/src/main/resources/mapper/UserPositionMapper.xml
+++ b/src/main/resources/mapper/UserPositionMapper.xml
@@ -465,7 +465,16 @@
FROM user_position
<where>
user_id = #{uid}
- <if test="state != null">
+ <if test="state == 1">
+ and sell_order_id is null and (status = 1 or status is null)
+ </if>
+ <if test="state == 2">
+ and status = 2
+ </if>
+ <if test="state == 0">
+ and status = 0
+ </if>
+ <if test="state != null and state != 0 and state != 1 and state != 2">
and status = #{state, jdbcType=INTEGER}
</if>
<if test="stockCode != null and stockCode != '' ">
--
Gitblit v1.9.3