.idea/A-stock.iml
New file @@ -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> .idea/git_toolbox_prj.xml
New file @@ -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> 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()); } } } 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); } @@ -125,6 +124,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 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); } } 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); } 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; @@ -24,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); src/main/java/com/nq/service/IUserService.java
@@ -92,6 +92,7 @@ void updateUserAmt(Double amt, Integer user_id); /** 将数据库中最新的用户资金写回登录缓存 */ void syncUserCache(HttpServletRequest request); } 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"); 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); 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; } } 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(); } 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); 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 { @@ -296,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())) { @@ -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_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); 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); @@ -476,34 +479,13 @@ userPosition.setOrderStayDays(Integer.valueOf(0)); userPosition.setOrderStayFee(new BigDecimal("0")); userPosition.setStatus(1); 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 +496,112 @@ } public ServerResponse fee(Integer buyNum,BigDecimal nowPrice){ @Override 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())); SiteSetting siteSetting = this.iSiteSettingService.getSiteSetting(); BigDecimal buy_fee_amt = buy_amt.multiply(siteSetting.getBuyFee()).setScale(2, 4); BigDecimal buy_fee_amt = TradeFeeUtil.calcBuyFeeByNotional(buy_amt, leverValue).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 +790,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_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 + "元(含保证金及手续费)"); } if (user.getUserIndexAmt().compareTo(new BigDecimal("0")) == -1) { return ServerResponse.createByErrorMsg("失败,指数总资金小于0"); @@ -751,7 +832,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 +852,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("撤单成功"); } @@ -851,6 +951,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)); @@ -930,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()) { @@ -1012,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(); @@ -1055,8 +1168,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()); @@ -1106,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)); @@ -1201,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; @@ -1266,7 +1380,9 @@ 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 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); @@ -1281,9 +1397,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 +1540,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 +1939,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_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); return ServerResponse.createByErrorMsg("下单失败,用户可用金额小于" + buy_debit_check + "元(含保证金及手续费)"); } if (user.getUserIndexAmt().compareTo(new BigDecimal("0")) == -1) { @@ -1886,8 +2000,8 @@ userPosition.setOrderStayDays(1); BigDecimal buy_fee_amt = buy_amt.multiply(siteSetting.getBuyFee()).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); @@ -1930,21 +2044,21 @@ userPosition.setOrderStayDays(Integer.valueOf(0)); userPosition.setOrderStayFee(new BigDecimal("0")); userPosition.setSpreadRatePrice(new BigDecimal("0")); userPosition.setStatus(1); 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 +2489,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 +2763,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_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 + "元(含保证金及手续费)"); } // if (user.getUserIndexAmt().compareTo(new BigDecimal("0")) == -1) { @@ -2690,7 +2803,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 +2836,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 +3071,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_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 + "元(含保证金及手续费)"); } // if (user.getUserIndexAmt().compareTo(new BigDecimal("0")) == -1) { @@ -3038,7 +3128,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 +3161,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("【用戶交易下單】保存持倉記錄成功"); 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); 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); 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"); src/main/java/com/nq/utils/TradeFeeUtil.java
New file @@ -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); } } src/main/java/com/nq/utils/pay/OcocnPayUtil.java
New file @@ -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(); } } 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; } src/main/java/com/nq/vo/pay/OcocnPayVO.java
New file @@ -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; } } 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 #????? 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 != '' ">