From f894e386d81e43831ea5fda78e30a029eca6fda5 Mon Sep 17 00:00:00 2001
From: “mitao” <“2763622819@qq.com”>
Date: 星期二, 16 十二月 2025 18:37:33 +0800
Subject: [PATCH] 大礼包、实物商品下单、回调
---
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/ICBCPayNotifyController.java | 69 ++
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBigGiftPackageMapper.java | 14
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/OrderController.java | 113 ++++
ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java | 2
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TPhysicalGoodsServiceImpl.java | 3
ruoyi-applet/src/main/resources/application-prod.yml | 8
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TCommissionTransactionFlowServiceImpl.java | 236 +++++++++
ruoyi-system/src/main/java/com/ruoyi/system/config/ICBCConfig.java | 8
ruoyi-system/src/main/java/com/ruoyi/system/model/TCommissionTransactionFlow.java | 4
ruoyi-system/src/main/java/com/ruoyi/system/service/ICBCNotifyService.java | 38 +
ruoyi-system/src/main/java/com/ruoyi/system/enums/UnionPayStatusEnum.java | 35 +
ruoyi-system/src/main/java/com/ruoyi/system/model/TBigGiftPackageOrder.java | 20
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TPhysicalGoodsOrderServiceImpl.java | 77 ++
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/HtmlConfigController.java | 61 ++
ruoyi-system/src/main/java/com/ruoyi/system/service/TPhysicalGoodsOrderService.java | 14
ruoyi-system/src/main/java/com/ruoyi/system/service/TCommissionTransactionFlowService.java | 14
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/TAppUserResp.java | 3
ruoyi-applet/src/main/resources/application-test.yml | 12
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/IndexController.java | 8
ruoyi-system/src/main/java/com/ruoyi/system/model/TBanner.java | 12
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/UserIndexController.java | 3
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java | 15
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ICBCNotifyServiceImpl.java | 365 ++++++++++++++
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBigGiftPackageOrderServiceImpl.java | 157 ++++++
ruoyi-system/src/main/java/com/ruoyi/system/utils/ICBCPayUtil.java | 160 +++---
ruoyi-system/src/main/java/com/ruoyi/system/service/TBigGiftPackageOrderService.java | 23
26 files changed, 1,343 insertions(+), 131 deletions(-)
diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/HtmlConfigController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/HtmlConfigController.java
new file mode 100644
index 0000000..2680537
--- /dev/null
+++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/HtmlConfigController.java
@@ -0,0 +1,61 @@
+package com.ruoyi.web.controller.api;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.system.model.THtmlConfig;
+import com.ruoyi.system.service.THtmlConfigService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * 协议、富文本配置相关接口
+ *
+ * @author mitao
+ * @since 2025-12-16
+ */
+@Slf4j
+@RestController
+@RequestMapping("/html-config")
+@Api(tags = "协议、富文本配置相关接口")
+@Validated
+@RequiredArgsConstructor(onConstructor_ = {@Lazy})
+public class HtmlConfigController {
+
+ private final THtmlConfigService htmlConfigService;
+
+ /**
+ * 根据类型获取富文本配置
+ *
+ * @param type 配置类型
+ * 1=用户注册隐私协议
+ * 2=大礼包购买协议
+ * 3=实物商品购买协议
+ * 4=关于我们
+ * 5=积分规则
+ * 6=金币兑换图文说明
+ * 7=普通商品模块展示配置
+ * 8=实物商品展示
+ * @return 富文本配置信息
+ */
+ @ApiOperation(value = "根据类型获取富文本配置", response = THtmlConfig.class)
+ @GetMapping("/{type}")
+ public R<THtmlConfig> getByType(
+ @ApiParam(name = "type", value = "配置类型:1=用户注册隐私协议,2=大礼包购买协议,3=实物商品购买协议,4=关于我们,5=积分规则,6=金币兑换图文说明,7=普通商品模块展示配置,8=实物商品展示", required = true)
+ @PathVariable @NotNull(message = "配置类型不能为空") Integer type) {
+ LambdaQueryWrapper<THtmlConfig> queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(THtmlConfig::getConfigType, type);
+ THtmlConfig htmlConfig = htmlConfigService.getOne(queryWrapper);
+ return R.ok(htmlConfig);
+ }
+}
diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/ICBCPayNotifyController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/ICBCPayNotifyController.java
new file mode 100644
index 0000000..ed70829
--- /dev/null
+++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/ICBCPayNotifyController.java
@@ -0,0 +1,69 @@
+package com.ruoyi.web.controller.api;
+
+import com.ruoyi.system.service.ICBCNotifyService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 工商银行支付回调接口
+ *
+ * @author mitao
+ * @since 2025-12-16
+ */
+@Slf4j
+@RestController
+@RequestMapping("/icbc-notify")
+@Api(tags = "工商银行支付回调接口")
+@RequiredArgsConstructor(onConstructor_ = {@Lazy})
+public class ICBCPayNotifyController {
+
+ private final ICBCNotifyService icbcNotifyService;
+
+ /**
+ * 大礼包商品订单支付回调(定金或全款)
+ *
+ * @param request HTTP请求对象
+ * @param response HTTP响应对象
+ */
+ @ApiOperation(value = "大礼包商品订单支付回调")
+ @PostMapping("/big-gift/pay-notify")
+ public void bigGiftPayNotify(HttpServletRequest request, HttpServletResponse response) {
+ log.info("【工商银行】收到大礼包支付回调请求");
+ icbcNotifyService.bigGiftPayNotify(request, response);
+ }
+
+ /**
+ * 大礼包商品尾款支付回调
+ *
+ * @param request HTTP请求对象
+ * @param response HTTP响应对象
+ */
+ @ApiOperation(value = "大礼包商品尾款支付回调")
+ @PostMapping("/big-gift/final-pay-notify")
+ public void bigGiftFinalPayNotify(HttpServletRequest request, HttpServletResponse response) {
+ log.info("【工商银行】收到大礼包尾款支付回调请求");
+ icbcNotifyService.bigGiftFinalPayNotify(request, response);
+ }
+
+ /**
+ * 实物商品订单支付回调
+ *
+ * @param request HTTP请求对象
+ * @param response HTTP响应对象
+ */
+ @ApiOperation(value = "实物商品订单支付回调")
+ @PostMapping("/physical/pay-notify")
+ public void physicalPayNotify(HttpServletRequest request, HttpServletResponse response) {
+ log.info("【工商银行】收到实物商品支付回调请求");
+ icbcNotifyService.physicalPayNotify(request, response);
+ }
+}
diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/IndexController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/IndexController.java
index a00f7d9..fe3e731 100644
--- a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/IndexController.java
+++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/IndexController.java
@@ -26,10 +26,13 @@
import java.util.List;
import java.util.Objects;
+/**
+ * 首页相关接口
+ */
@Slf4j
@RestController
@RequestMapping("/index")
-@Api(tags = "首页模块")
+@Api(tags = "首页相关接口")
@Validated
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
public class IndexController {
@@ -91,6 +94,9 @@
@GetMapping(value = "/getBannerDetail/{id}")
public R<TBanner> getBannerDetail(@PathVariable String id) {
TBanner tBanner = tBannerService.getById(id);
+ //增加阅读量
+ tBanner.setReadCount(tBanner.getReadCount() + 1);
+ tBannerService.updateById(tBanner);
return R.ok(tBanner);
}
@ApiOperation("获取大礼包列表")
diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/OrderController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/OrderController.java
new file mode 100644
index 0000000..295ff4f
--- /dev/null
+++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/OrderController.java
@@ -0,0 +1,113 @@
+package com.ruoyi.web.controller.api;
+
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.framework.web.service.TokenService;
+import com.ruoyi.system.dto.BigGiftPlaceOrderDTO;
+import com.ruoyi.system.dto.FinalPaymentDTO;
+import com.ruoyi.system.dto.PhysicalGoodsPlaceOrderDTO;
+import com.ruoyi.system.model.TAppUser;
+import com.ruoyi.system.service.TAppUserService;
+import com.ruoyi.system.service.TBigGiftPackageOrderService;
+import com.ruoyi.system.service.TPhysicalGoodsOrderService;
+import com.ruoyi.system.vo.PlaceOrderVO;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+
+/**
+ * 下单接口
+ *
+ * @author mitao
+ * @since 2025-12-16
+ */
+@Slf4j
+@RestController
+@RequestMapping("/order")
+@Api(tags = "下单模块")
+@Validated
+@RequiredArgsConstructor(onConstructor_ = {@Lazy})
+public class OrderController {
+
+ private final TokenService tokenService;
+ private final TAppUserService appUserService;
+ private final TBigGiftPackageOrderService bigGiftPackageOrderService;
+ private final TPhysicalGoodsOrderService physicalGoodsOrderService;
+
+ /**
+ * 大礼包商品下单
+ *
+ * @param dto 下单请求参数
+ * @return 下单结果(包含订单信息和支付参数)
+ */
+ @ApiOperation(value = "大礼包商品下单", response = PlaceOrderVO.class)
+ @PostMapping("/big-gift/place")
+ public R<PlaceOrderVO> placeBigGiftOrder(@Valid @RequestBody BigGiftPlaceOrderDTO dto) {
+ // 获取当前登录用户
+ Integer userId = tokenService.getLoginUserApplet().getUserId();
+ TAppUser appUser = appUserService.getById(userId);
+ if (appUser == null) {
+ return R.fail("用户不存在");
+ }
+ if (appUser.getOpenid() == null || appUser.getOpenid().isEmpty()) {
+ return R.fail("用户未绑定微信");
+ }
+
+ PlaceOrderVO result = bigGiftPackageOrderService.placeOrder(dto, userId, appUser.getOpenid());
+ return R.ok(result);
+ }
+
+ /**
+ * 实物商品下单
+ *
+ * @param dto 下单请求参数
+ * @return 下单结果(包含订单信息和支付参数)
+ */
+ @ApiOperation(value = "实物商品下单", response = PlaceOrderVO.class)
+ @PostMapping("/physical/place")
+ public R<PlaceOrderVO> placePhysicalGoodsOrder(@Valid @RequestBody PhysicalGoodsPlaceOrderDTO dto) {
+ // 获取当前登录用户
+ Integer userId = tokenService.getLoginUserApplet().getUserId();
+ TAppUser appUser = appUserService.getById(userId);
+ if (appUser == null) {
+ return R.fail("用户不存在");
+ }
+ if (appUser.getOpenid() == null || appUser.getOpenid().isEmpty()) {
+ return R.fail("用户未绑定微信");
+ }
+
+ PlaceOrderVO result = physicalGoodsOrderService.placeOrder(dto, userId, appUser.getOpenid());
+ return R.ok(result);
+ }
+
+ /**
+ * 大礼包尾款支付
+ *
+ * @param dto 尾款支付请求参数
+ * @return 支付结果(包含订单信息和支付参数)
+ */
+ @ApiOperation(value = "大礼包尾款支付", response = PlaceOrderVO.class)
+ @PostMapping("/big-gift/pay-final")
+ public R<PlaceOrderVO> payFinalPayment(@Valid @RequestBody FinalPaymentDTO dto) {
+ // 获取当前登录用户
+ Integer userId = tokenService.getLoginUserApplet().getUserId();
+ TAppUser appUser = appUserService.getById(userId);
+ if (appUser == null) {
+ return R.fail("用户不存在");
+ }
+ if (appUser.getOpenid() == null || appUser.getOpenid().isEmpty()) {
+ return R.fail("用户未绑定微信");
+ }
+
+ PlaceOrderVO result = bigGiftPackageOrderService.payFinalPayment(dto.getOrderId(), userId, appUser.getOpenid());
+ return R.ok(result);
+ }
+}
diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/UserIndexController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/UserIndexController.java
index 2f48785..06c4c8e 100644
--- a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/UserIndexController.java
+++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/UserIndexController.java
@@ -15,6 +15,9 @@
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.*;
+/**
+ * 个人中心相关接口
+ */
@Slf4j
@RestController
@RequestMapping("/userIndex")
diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
index e6a7770..8c0f5cf 100644
--- a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
+++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
@@ -117,7 +117,7 @@
// 生成令牌
LoginUserApplet loginUser = loginService.loginCodeApplet(loginBody.getUsername(), loginBody.getCode());
ajax.put(Constants.TOKEN, tokenService.createTokenApplet(loginUser));
- ajax.put("userInfo",loginUser);
+ ajax.put("info",loginUser);
return ajax;
}
diff --git a/ruoyi-applet/src/main/resources/application-prod.yml b/ruoyi-applet/src/main/resources/application-prod.yml
index 7214798..cd550ac 100644
--- a/ruoyi-applet/src/main/resources/application-prod.yml
+++ b/ruoyi-applet/src/main/resources/application-prod.yml
@@ -203,8 +203,8 @@
prefix: https://xzgt.test.591taxi.cn:${server.port}${server.servlet.context-path}
wx:
conf:
- appId: wxe91f1af7638aa5dd
- secretId: a787e1a462715604e0c9528b6d8960d1
+ appId: wx87744e4e4094a829
+ secretId: c322159083d844e63dff458b5e54c4c9
#OSS及短信配置
code:
config:
@@ -252,7 +252,7 @@
notify-url: ""
# 回调方法的路径
- notify-url-method: "/icbc/icbcNotify"
+ notify-url-method: "/icbc-notify/"
@@ -282,6 +282,6 @@
wx-gzh-app-id: ""
# 订单超时时间,单位为分钟
- expire-time: "300"
+ expire-time: "60"
diff --git a/ruoyi-applet/src/main/resources/application-test.yml b/ruoyi-applet/src/main/resources/application-test.yml
index 131273f..50762c2 100644
--- a/ruoyi-applet/src/main/resources/application-test.yml
+++ b/ruoyi-applet/src/main/resources/application-test.yml
@@ -65,7 +65,7 @@
devtools:
restart:
# 热部署开关
- enabled: true
+ enabled: false
# redis 配置
redis:
# 地址
@@ -193,8 +193,8 @@
allowExt: .jpg|.png|.gif|.jpeg|.doc|.docx|.apk|.MP4|.mp4|.pdf|.PDF|.xls|.xlsx|.rar|.zip|.rar|.m4a
wx:
conf:
- appId: wx618aa69186bf2ed6
- secretId: d4af107e0b11236c1db24c1453f28bb3
+ appId: wx87744e4e4094a829
+ secretId: c322159083d844e63dff458b5e54c4c9
payment:
wx:
# 微信appid
@@ -239,10 +239,10 @@
order-refund-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/refundqry/V1"
# 完整的回调通知地址,支付完成后通知商户的URL
- notify-url: ""
+ notify-url: "/icbc-notify/"
# 回调方法的路径
- notify-url-method: "/icbc/icbcNotify"
+ notify-url-method: "/icbc-notify/"
@@ -272,7 +272,7 @@
wx-gzh-app-id: ""
# 订单超时时间,单位为分钟
- expire-time: "300"
+ expire-time: "60"
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/TAppUserResp.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/TAppUserResp.java
index 06b2eaf..51f0de2 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/TAppUserResp.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/TAppUserResp.java
@@ -44,4 +44,7 @@
@ApiModelProperty(value = "状态 1=启用 2=禁用")
private Integer status;
+ @ApiModelProperty(value = "角色(1=游客,2=营销员,3=合伙人)")
+ private Integer userType;
+
}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
index f7a42bd..35dc66e 100644
--- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
@@ -116,22 +116,9 @@
"/login","/open/login","/applet/queryProtocolConfigByType",
"/register","/applet/getCode","/applet/loginCode",
"/applet/changepwd", "/captchaImage","/getCode","/loginCode",
- "/operations/getBySingleNum/**",
- "/user/getUserInfoByNumber/**",
"/index/**",
- "/employment/**",
- "/partyBuilding/**",
- "/convenience/**",
"/merchant/**",
- "/openIdByJsCode",
- "/t-appeal-services/pageListLarge","/t-large-dept/list",
- "/t-large-key-grid-user/pageListLarge","/t-large-key-personnel-type/pageListLarge",
- "/t-large-organization/pageListLarge","/t-large-resource/pageListLarge",
- "/t-large-resource-type/list","/t-large-social-worker/pageListLarge",
- "/t-large-user/pageListLarge","/t-large-volunteer/pageListLarge",
- "/pension/getAssistedDiningList",
- "/pension/getAssistedDiningDetail",
- "/statistics/**","/course/**","/wx/**"
+ "/openIdByJsCode","/wx/**","/html-config/*","/icbc-notify/**"
).permitAll()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/config/IcbcConfig.java b/ruoyi-system/src/main/java/com/ruoyi/system/config/ICBCConfig.java
similarity index 94%
rename from ruoyi-system/src/main/java/com/ruoyi/system/config/IcbcConfig.java
rename to ruoyi-system/src/main/java/com/ruoyi/system/config/ICBCConfig.java
index a6838e7..6a85ea9 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/config/IcbcConfig.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/config/ICBCConfig.java
@@ -11,9 +11,9 @@
* @author: yangchuan
* @create: 2024-11-18 17:10
*/
-//@Component
+@Component
@Data
-public class IcbcConfig {
+public class ICBCConfig {
/**
* 聚合支付B2C线上消费下单接口url (无界面)
@@ -120,9 +120,9 @@
private volatile static DefaultIcbcClient instance = null;
- public static DefaultIcbcClient getInstance(IcbcConfig icbcConfig) {
+ public static DefaultIcbcClient getInstance(ICBCConfig icbcConfig) {
if (instance == null) {
- synchronized (IcbcConfig.class) {
+ synchronized (ICBCConfig.class) {
if (instance == null) {
instance = new DefaultIcbcClient(icbcConfig.appId, IcbcConstants.SIGN_TYPE_RSA2, icbcConfig.myPrivateKey, icbcConfig.apiGwPublicKey);
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/enums/UnionPayStatusEnum.java b/ruoyi-system/src/main/java/com/ruoyi/system/enums/UnionPayStatusEnum.java
new file mode 100644
index 0000000..981f409
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/enums/UnionPayStatusEnum.java
@@ -0,0 +1,35 @@
+package com.ruoyi.system.enums;
+
+import lombok.Getter;
+
+@Getter
+public enum UnionPayStatusEnum {
+
+ TRADE_SUCCESS("TRADE_SUCCESS", "支付成功"),
+ TRADE_FAIL("TRADE_FAIL", "支付失败"),
+ TRADE_REFUND("TRADE_REFUND", "退款成功")
+ ;
+
+ private final String code;
+ private final String name;
+
+ UnionPayStatusEnum(String code, String name) {
+ this.code = code;
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return this.name;
+ }
+
+ public static UnionPayStatusEnum fromCode(String code) {
+ for (UnionPayStatusEnum action : UnionPayStatusEnum.values()) {
+ if (action.getCode().equals(code)) {
+ return action;
+ }
+ }
+ throw new IllegalArgumentException("No matching action for code " + code);
+ }
+
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBigGiftPackageMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBigGiftPackageMapper.java
index c2b16b2..0ff7398 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBigGiftPackageMapper.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBigGiftPackageMapper.java
@@ -1,7 +1,9 @@
package com.ruoyi.system.mapper;
-import com.ruoyi.system.model.TBigGiftPackage;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.system.model.TBigGiftPackage;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Update;
/**
* <p>
@@ -13,4 +15,14 @@
*/
public interface TBigGiftPackageMapper extends BaseMapper<TBigGiftPackage> {
+ /**
+ * 使用乐观锁扣减库存
+ *
+ * @param id 商品ID
+ * @param quantity 扣减数量
+ * @return 更新行数(0表示库存不足或并发冲突)
+ */
+ @Update("UPDATE t_big_gift_package SET inventory = inventory - #{quantity} WHERE id = #{id} AND inventory >= #{quantity} AND status = 1")
+ int deductInventory(@Param("id") Integer id, @Param("quantity") Integer quantity);
+
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/model/TBanner.java b/ruoyi-system/src/main/java/com/ruoyi/system/model/TBanner.java
index 01ede61..5d4e789 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/model/TBanner.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/model/TBanner.java
@@ -1,14 +1,14 @@
package com.ruoyi.system.model;
-import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.core.domain.BaseModel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
-
-import java.io.Serializable;
-import java.time.LocalDateTime;
/**
* <p>
@@ -41,4 +41,8 @@
@TableField("banner_detail")
private String bannerDetail;
+ @ApiModelProperty(value = "阅读数量")
+ @TableField("read_count")
+ private Integer readCount;
+
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/model/TBigGiftPackageOrder.java b/ruoyi-system/src/main/java/com/ruoyi/system/model/TBigGiftPackageOrder.java
index eb32f6b..aba6329 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/model/TBigGiftPackageOrder.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/model/TBigGiftPackageOrder.java
@@ -1,18 +1,18 @@
package com.ruoyi.system.model;
-import java.math.BigDecimal;
-
import cn.afterturn.easypoi.excel.annotation.Excel;
-import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableField;
-import java.io.Serializable;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
/**
* <p>
@@ -116,6 +116,10 @@
@TableField("final_payment_serial_number")
private String finalPaymentSerialNumber;
+ @ApiModelProperty(value = "支付订单号(用于工行支付,多订单合并支付时共用同一个支付订单号)")
+ @TableField("pay_order_code")
+ private String payOrderCode;
+
@ApiModelProperty(value = "删除状态(0=否,1=是)")
@TableField("del_flag")
private Integer delFlag;
@@ -162,6 +166,10 @@
@Excel(name = "审核状态",replace = {"待审核_1","已通过_2","已驳回_3"})
private Integer auditStatus;
+ @ApiModelProperty(value = "是否启用生效日")
+ @TableField("effect_day_flag")
+ private Boolean effectDayFlag;
+
@ApiModelProperty(value = "商品封面")
@TableField(exist = false)
private String bigGiftPackageCover;
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/model/TCommissionTransactionFlow.java b/ruoyi-system/src/main/java/com/ruoyi/system/model/TCommissionTransactionFlow.java
index 1922a07..5f48c40 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/model/TCommissionTransactionFlow.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/model/TCommissionTransactionFlow.java
@@ -48,6 +48,10 @@
@TableField("order_id")
private Integer orderId;
+ @ApiModelProperty(value = "订单类型(1=大礼包,2=实物商品)")
+ @TableField("order_type")
+ private Integer orderType;
+
@ApiModelProperty(value = "订单金额")
@TableField("order_price")
private BigDecimal orderPrice;
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ICBCNotifyService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ICBCNotifyService.java
new file mode 100644
index 0000000..1378a03
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ICBCNotifyService.java
@@ -0,0 +1,38 @@
+package com.ruoyi.system.service;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 工商银行支付回调服务
+ *
+ * @author mitao
+ * @since 2025-12-16
+ */
+public interface ICBCNotifyService {
+
+ /**
+ * 大礼包商品订单支付回调(定金或全款)
+ *
+ * @param request HTTP请求对象
+ * @param response HTTP响应对象
+ */
+ void bigGiftPayNotify(HttpServletRequest request, HttpServletResponse response);
+
+ /**
+ * 大礼包商品尾款支付回调
+ *
+ * @param request HTTP请求对象
+ * @param response HTTP响应对象
+ */
+ void bigGiftFinalPayNotify(HttpServletRequest request, HttpServletResponse response);
+
+ /**
+ * 实物商品订单支付回调
+ *
+ * @param request HTTP请求对象
+ * @param response HTTP响应对象
+ */
+ void physicalPayNotify(HttpServletRequest request, HttpServletResponse response);
+
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/TBigGiftPackageOrderService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/TBigGiftPackageOrderService.java
index d2608d9..2575cec 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/TBigGiftPackageOrderService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/TBigGiftPackageOrderService.java
@@ -1,9 +1,11 @@
package com.ruoyi.system.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.system.model.TBigGiftPackageOrder;
import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.system.dto.BigGiftPlaceOrderDTO;
+import com.ruoyi.system.model.TBigGiftPackageOrder;
import com.ruoyi.system.query.BigGiftPackageOrderQuery;
+import com.ruoyi.system.vo.PlaceOrderVO;
import java.util.List;
@@ -22,5 +24,24 @@
List<TBigGiftPackageOrder> export(BigGiftPackageOrderQuery query);
+ /**
+ * 大礼包下单
+ *
+ * @param dto 下单请求参数
+ * @param appUserId 用户ID
+ * @param openid 用户微信openid
+ * @return 下单结果(包含订单信息和支付参数)
+ */
+ PlaceOrderVO placeOrder(BigGiftPlaceOrderDTO dto, Integer appUserId, String openid);
+
+ /**
+ * 大礼包尾款支付
+ *
+ * @param orderId 订单ID
+ * @param appUserId 用户ID
+ * @param openid 用户微信openid
+ * @return 支付结果(包含订单信息和支付参数)
+ */
+ PlaceOrderVO payFinalPayment(Integer orderId, Integer appUserId, String openid);
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/TCommissionTransactionFlowService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/TCommissionTransactionFlowService.java
index 997c2aa..46fc273 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/TCommissionTransactionFlowService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/TCommissionTransactionFlowService.java
@@ -1,7 +1,9 @@
package com.ruoyi.system.service;
-import com.ruoyi.system.model.TCommissionTransactionFlow;
import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.system.model.TCommissionTransactionFlow;
+
+import java.math.BigDecimal;
/**
* <p>
@@ -13,4 +15,14 @@
*/
public interface TCommissionTransactionFlowService extends IService<TCommissionTransactionFlow> {
+ /**
+ * 计算并保存分佣
+ *
+ * @param orderId 订单ID
+ * @param orderType 订单类型(1=大礼包,2=实物商品)
+ * @param orderPrice 订单金额
+ * @param appUserId 购买用户ID
+ */
+ void calculateAndSaveCommission(Integer orderId, Integer orderType, BigDecimal orderPrice, Integer appUserId);
+
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/TPhysicalGoodsOrderService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/TPhysicalGoodsOrderService.java
index 9cc13c1..5b3e0f5 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/TPhysicalGoodsOrderService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/TPhysicalGoodsOrderService.java
@@ -1,9 +1,11 @@
package com.ruoyi.system.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.system.model.TPhysicalGoodsOrder;
import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.system.dto.PhysicalGoodsPlaceOrderDTO;
+import com.ruoyi.system.model.TPhysicalGoodsOrder;
import com.ruoyi.system.query.BigGiftPackageQuery;
+import com.ruoyi.system.vo.PlaceOrderVO;
import java.util.List;
@@ -22,4 +24,14 @@
List<TPhysicalGoodsOrder> export(BigGiftPackageQuery query);
+ /**
+ * 实物商品下单
+ *
+ * @param dto 下单请求参数
+ * @param appUserId 用户ID
+ * @param openid 用户微信openid
+ * @return 下单结果(包含订单信息和支付参数)
+ */
+ PlaceOrderVO placeOrder(PhysicalGoodsPlaceOrderDTO dto, Integer appUserId, String openid);
+
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ICBCNotifyServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ICBCNotifyServiceImpl.java
new file mode 100644
index 0000000..4a2763f
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ICBCNotifyServiceImpl.java
@@ -0,0 +1,365 @@
+package com.ruoyi.system.service.impl;
+
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.icbc.api.IcbcApiException;
+import com.icbc.api.IcbcConstants;
+import com.icbc.api.utils.IcbcSignature;
+import com.icbc.api.utils.WebUtils;
+import com.ruoyi.system.config.ICBCConfig;
+import com.ruoyi.system.model.TBigGiftPackageOrder;
+import com.ruoyi.system.model.TPhysicalGoodsOrder;
+import com.ruoyi.system.service.ICBCNotifyService;
+import com.ruoyi.system.service.TBigGiftPackageOrderService;
+import com.ruoyi.system.service.TCommissionTransactionFlowService;
+import com.ruoyi.system.service.TPhysicalGoodsOrderService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 工商银行支付回调服务实现
+ *
+ * @author mitao
+ * @since 2025-12-16
+ */
+@Service
+@Slf4j
+public class ICBCNotifyServiceImpl implements ICBCNotifyService {
+
+ @Resource
+ private ICBCConfig icbcConfig;
+
+ @Resource
+ private TBigGiftPackageOrderService bigGiftPackageOrderService;
+
+ @Resource
+ private TPhysicalGoodsOrderService physicalGoodsOrderService;
+
+ @Resource
+ private TCommissionTransactionFlowService commissionTransactionFlowService;
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void bigGiftPayNotify(HttpServletRequest request, HttpServletResponse response) {
+ boolean signResult = false;
+ String msgId = null;
+ try {
+ JSONObject bizContent = parseRequest(request);
+ if (bizContent == null) {
+ log.error("【工商银行】大礼包支付回调参数解析失败");
+ sendResponse(response, false, null);
+ return;
+ }
+ log.info("【工商银行】大礼包支付回调参数:{}", bizContent.toJSONString());
+
+ String payOrderCode = bizContent.getString("out_trade_no");
+ String returnCode = bizContent.getString("return_code");
+ msgId = bizContent.getString("msg_id");
+
+ // 验签
+ if (validateSignature(request, icbcConfig.getNotifyUrlMethod() + "/big-gift/pay-notify")) {
+ signResult = true;
+ log.info("支付订单号{}【工商银行】大礼包支付回调签名验证成功", payOrderCode);
+
+ if ("0".equals(returnCode)) {
+ log.info("支付订单号{}【工商银行】大礼包支付成功", payOrderCode);
+
+ // 根据支付订单号查询所有订单
+ LambdaQueryWrapper<TBigGiftPackageOrder> wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(TBigGiftPackageOrder::getPayOrderCode, payOrderCode);
+ List<TBigGiftPackageOrder> orders = bigGiftPackageOrderService.list(wrapper);
+
+ if (orders == null || orders.isEmpty()) {
+ log.error("支付订单号{}未找到对应的大礼包订单", payOrderCode);
+ sendResponse(response, signResult, msgId);
+ return;
+ }
+
+ // 处理每个订单
+ for (TBigGiftPackageOrder order : orders) {
+ processBigGiftPayment(order);
+ }
+ } else {
+ log.error("支付订单号{}【工商银行】大礼包支付失败,错误信息:{}", payOrderCode, bizContent.getString("return_msg"));
+ }
+ } else {
+ log.error("支付订单号{}【工商银行】大礼包支付回调签名验证失败", payOrderCode);
+ }
+
+ sendResponse(response, signResult, msgId);
+ } catch (Exception e) {
+ log.error("【工商银行】大礼包支付回调处理失败: {}", e.getMessage(), e);
+ sendResponse(response, false, msgId);
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void bigGiftFinalPayNotify(HttpServletRequest request, HttpServletResponse response) {
+ boolean signResult = false;
+ String msgId = null;
+ try {
+ JSONObject bizContent = parseRequest(request);
+ if (bizContent == null) {
+ log.error("【工商银行】大礼包尾款支付回调参数解析失败");
+ sendResponse(response, false, null);
+ return;
+ }
+ log.info("【工商银行】大礼包尾款支付回调参数:{}", bizContent.toJSONString());
+
+ String finalPayOrderCode = bizContent.getString("out_trade_no");
+ String returnCode = bizContent.getString("return_code");
+ msgId = bizContent.getString("msg_id");
+
+ // 验签
+ if (validateSignature(request, icbcConfig.getNotifyUrlMethod() + "/big-gift/final-pay-notify")) {
+ signResult = true;
+ log.info("尾款支付订单号{}【工商银行】大礼包尾款支付回调签名验证成功", finalPayOrderCode);
+
+ if ("0".equals(returnCode)) {
+ log.info("尾款支付订单号{}【工商银行】大礼包尾款支付成功", finalPayOrderCode);
+
+ // 根据尾款支付订单号查询订单(尾款支付是单个订单)
+ // 注意:这里需要根据实际业务逻辑查询订单,可能需要在订单表中添加尾款支付订单号字段
+ // 暂时使用订单编码查询
+ LambdaQueryWrapper<TBigGiftPackageOrder> wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(TBigGiftPackageOrder::getFinalPaymentSerialNumber, finalPayOrderCode);
+ TBigGiftPackageOrder order = bigGiftPackageOrderService.getOne(wrapper);
+
+ if (order == null) {
+ log.error("尾款支付订单号{}未找到对应的大礼包订单", finalPayOrderCode);
+ sendResponse(response, signResult, msgId);
+ return;
+ }
+
+ // 更新订单状态为"完成支付"
+ order.setStatus(3);
+ order.setFinalPaymentTime(LocalDateTime.now());
+ order.setFinalPaymentSerialNumber(finalPayOrderCode);
+ bigGiftPackageOrderService.updateById(order);
+ log.info("订单{}状态更新为完成支付", order.getCode());
+
+ // 尾款支付完成后,必须计算分佣
+ commissionTransactionFlowService.calculateAndSaveCommission(
+ order.getId(),
+ 1, // 订单类型:1=大礼包
+ order.getOrderPrice(),
+ order.getAppUserId()
+ );
+ log.info("订单{}分佣计算完成", order.getCode());
+ } else {
+ log.error("尾款支付订单号{}【工商银行】大礼包尾款支付失败,错误信息:{}", finalPayOrderCode, bizContent.getString("return_msg"));
+ }
+ } else {
+ log.error("尾款支付订单号{}【工商银行】大礼包尾款支付回调签名验证失败", finalPayOrderCode);
+ }
+
+ sendResponse(response, signResult, msgId);
+ } catch (Exception e) {
+ log.error("【工商银行】大礼包尾款支付回调处理失败: {}", e.getMessage(), e);
+ sendResponse(response, false, msgId);
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void physicalPayNotify(HttpServletRequest request, HttpServletResponse response) {
+ boolean signResult = false;
+ String msgId = null;
+ try {
+ JSONObject bizContent = parseRequest(request);
+ if (bizContent == null) {
+ log.error("【工商银行】实物商品支付回调参数解析失败");
+ sendResponse(response, false, null);
+ return;
+ }
+ log.info("【工商银行】实物商品支付回调参数:{}", bizContent.toJSONString());
+
+ String orderCode = bizContent.getString("out_trade_no");
+ String returnCode = bizContent.getString("return_code");
+ msgId = bizContent.getString("msg_id");
+
+ // 验签
+ if (validateSignature(request, icbcConfig.getNotifyUrlMethod() + "/physical/pay-notify")) {
+ signResult = true;
+ log.info("订单{}【工商银行】实物商品支付回调签名验证成功", orderCode);
+
+ if ("0".equals(returnCode)) {
+ log.info("订单{}【工商银行】实物商品支付成功", orderCode);
+
+ // 根据订单编码查询订单
+ LambdaQueryWrapper<TPhysicalGoodsOrder> wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(TPhysicalGoodsOrder::getCode, orderCode);
+ TPhysicalGoodsOrder order = physicalGoodsOrderService.getOne(wrapper);
+
+ if (order == null) {
+ log.error("订单{}未找到对应的实物商品订单", orderCode);
+ sendResponse(response, signResult, msgId);
+ return;
+ }
+
+ // 更新订单状态为"完成支付"
+ order.setStatus(2);
+ order.setPaymentTime(LocalDateTime.now());
+ order.setPaymentSerialNumber(orderCode);
+ physicalGoodsOrderService.updateById(order);
+ log.info("订单{}状态更新为完成支付", orderCode);
+
+ // 实物商品支付完成后,立即计算分佣
+ commissionTransactionFlowService.calculateAndSaveCommission(
+ order.getId(),
+ 2, // 订单类型:2=实物商品
+ order.getOrderPrice(),
+ order.getAppUserId()
+ );
+ log.info("订单{}分佣计算完成", orderCode);
+ } else {
+ log.error("订单{}【工商银行】实物商品支付失败,错误信息:{}", orderCode, bizContent.getString("return_msg"));
+ }
+ } else {
+ log.error("订单{}【工商银行】实物商品支付回调签名验证失败", orderCode);
+ }
+
+ sendResponse(response, signResult, msgId);
+ } catch (Exception e) {
+ log.error("【工商银行】实物商品支付回调处理失败: {}", e.getMessage(), e);
+ sendResponse(response, false, msgId);
+ }
+ }
+
+ /**
+ * 处理大礼包支付(定金或全款)
+ */
+ private void processBigGiftPayment(TBigGiftPackageOrder order) {
+ Integer paymentType = order.getPaymentType();
+
+ if (paymentType == 1) {
+ // 订金+尾款模式,更新状态为"已付订金"
+ order.setStatus(2);
+ order.setDepositTime(LocalDateTime.now());
+ order.setDepositPaymentSerialNumber(order.getPayOrderCode());
+ bigGiftPackageOrderService.updateById(order);
+ log.info("订单{}状态更新为已付订金,不计算分佣", order.getCode());
+ } else if (paymentType == 2) {
+ // 全款模式,更新状态为"完成支付"
+ order.setStatus(3);
+ order.setDepositTime(LocalDateTime.now());
+ order.setDepositPaymentSerialNumber(order.getPayOrderCode());
+ bigGiftPackageOrderService.updateById(order);
+ log.info("订单{}状态更新为完成支付", order.getCode());
+
+ // 判断是否启用生效日
+ Boolean effectDayFlag = order.getEffectDayFlag();
+ if (effectDayFlag == null || !effectDayFlag) {
+ // 未启用生效日,立即计算分佣
+ commissionTransactionFlowService.calculateAndSaveCommission(
+ order.getId(),
+ 1, // 订单类型:1=大礼包
+ order.getOrderPrice(),
+ order.getAppUserId()
+ );
+ log.info("订单{}未启用生效日,立即计算分佣", order.getCode());
+ } else {
+ // 启用生效日,不计算分佣(由定时任务处理)
+ log.info("订单{}启用生效日,延迟计算分佣", order.getCode());
+ }
+ }
+ }
+
+ /**
+ * 解析请求中的回调参数
+ *
+ * @param request HTTP请求对象
+ * @return 解析后的JSON对象
+ */
+ private JSONObject parseRequest(HttpServletRequest request) {
+ try {
+ String bizContentStr = request.getParameter("biz_content");
+ return JSON.parseObject(bizContentStr);
+ } catch (Exception e) {
+ log.error("【工商银行】解析回调参数失败: {}", e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * 验证签名
+ *
+ * @param request 回调请求
+ * @param path 回调URL路径
+ * @return 签名验证结果
+ */
+ private boolean validateSignature(HttpServletRequest request, String path) {
+ Map<String, String> params = new HashMap<>();
+ params.put("from", request.getParameter("from"));
+ params.put("api", request.getParameter("api"));
+ params.put("app_id", request.getParameter("app_id"));
+ params.put("charset", request.getParameter("charset"));
+ params.put("format", request.getParameter("format"));
+ params.put("timestamp", request.getParameter("timestamp"));
+ params.put("biz_content", request.getParameter("biz_content"));
+ params.put("sign_type", request.getParameter("sign_type"));
+ String sign = request.getParameter("sign");
+ String signStr = WebUtils.buildOrderedSignStr(path, params);
+ try {
+ return IcbcSignature.verify(signStr, IcbcConstants.SIGN_TYPE_RSA, icbcConfig.getApiGwPublicKey(), params.get("charset"), sign);
+ } catch (IcbcApiException e) {
+ log.error("【工商银行】签名验证异常: {}", e.getMessage(), e);
+ return false;
+ }
+ }
+
+ /**
+ * 发送回调响应
+ *
+ * @param response HTTP响应对象
+ * @param checkResult 签名验证结果
+ * @param msgId 消息ID
+ */
+ private void sendResponse(HttpServletResponse response, boolean checkResult, String msgId) {
+ String responseContent = buildNotifyResponse(checkResult, msgId);
+ response.setContentType("application/json; charset=utf-8");
+ try {
+ response.getWriter().write(responseContent);
+ log.info("【工商银行】支付回调应答成功");
+ } catch (IOException e) {
+ log.error("【工商银行】支付回调应答失败: {}", e.getMessage(), e);
+ }
+ }
+
+ /**
+ * 构建回调响应内容
+ * 工行要求保证返回参数的绝对顺序,故直接返回固定字符串不定义对象
+ * 接口文档地址 https://open.icbc.com.cn/icbc/apip/api_detail.html?apiId=10000000000000137000&baseUrl=%2Fcardbusiness%2Faggregatepay%2Fb2c%2Fonline&resUrl=%2Fconsumepurchase&version=V1&apiName=%E8%81%9A%E5%90%88%E6%94%AF%E4%BB%98B2C%E7%BA%BF%E4%B8%8A%E6%B6%88%E8%B4%B9%E4%B8%8B%E5%8D%95&serviceId=P0067&resourceId=10000000000000009800
+ *
+ * @param checkResult 签名验证结果
+ * @param msgId 消息ID
+ * @return 回调响应字符串
+ */
+ private String buildNotifyResponse(boolean checkResult, String msgId) {
+ String responseBizContent = checkResult
+ ? "{\"return_code\":0,\"return_msg\":\"success\",\"msg_id\":\"" + msgId + "\"}"
+ : "{\"return_code\":-1,\"return_msg\":\"icbc sign not pass.\"}";
+ String signStr = "\"response_biz_content\":" + responseBizContent + ",\"sign_type\":\"RSA2\"";
+ try {
+ String sign = IcbcSignature.sign(signStr, IcbcConstants.SIGN_TYPE_RSA2, icbcConfig.getMyPrivateKey(), "UTF-8", "");
+ return "{" + signStr + ",\"sign\":\"" + sign + "\"}";
+ } catch (IcbcApiException e) {
+ log.error("【工商银行】回调应答签名失败: {}", e.getMessage(), e);
+ return "{\"response_biz_content\":{\"return_code\":-1,\"return_msg\":\"response sign error\"},\"sign_type\":\"RSA2\",\"sign\":\"\"}";
+ }
+ }
+
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBigGiftPackageOrderServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBigGiftPackageOrderServiceImpl.java
index 0bdfe7f..f44ce30 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBigGiftPackageOrderServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBigGiftPackageOrderServiceImpl.java
@@ -1,18 +1,32 @@
package com.ruoyi.system.service.impl;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.system.config.ICBCConfig;
+import com.ruoyi.system.dto.BigGiftPlaceOrderDTO;
import com.ruoyi.system.mapper.TBigGiftPackageMapper;
+import com.ruoyi.system.mapper.TBigGiftPackageOrderMapper;
import com.ruoyi.system.mapper.TCompanyMapper;
import com.ruoyi.system.model.TBigGiftPackage;
import com.ruoyi.system.model.TBigGiftPackageOrder;
-import com.ruoyi.system.mapper.TBigGiftPackageOrderMapper;
import com.ruoyi.system.model.TCompany;
import com.ruoyi.system.query.BigGiftPackageOrderQuery;
import com.ruoyi.system.service.TBigGiftPackageOrderService;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.system.utils.ICBCPayUtil;
+import com.ruoyi.system.utils.OrderCodeGenerator;
+import com.ruoyi.system.vo.PlaceOrderVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@@ -25,6 +39,7 @@
* @author xiaochen
* @since 2025-12-08
*/
+@Slf4j
@Service
public class TBigGiftPackageOrderServiceImpl extends ServiceImpl<TBigGiftPackageOrderMapper, TBigGiftPackageOrder> implements TBigGiftPackageOrderService {
@@ -34,6 +49,11 @@
@Resource
private TBigGiftPackageMapper bigGiftPackageMapper;
+ @Autowired
+ private ICBCPayUtil icbcPayUtil;
+
+ @Autowired
+ private ICBCConfig icbcConfig;
@Override
public List<TBigGiftPackageOrder> pageList(Page<TBigGiftPackageOrder> tBigGiftPackageOrderPage, BigGiftPackageOrderQuery query) {
@@ -86,4 +106,137 @@
}
return tBigGiftPackageOrders ;
}
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public PlaceOrderVO placeOrder(BigGiftPlaceOrderDTO dto, Integer appUserId, String openid) {
+ // 1. 校验商品是否存在
+ TBigGiftPackage bigGiftPackage = bigGiftPackageMapper.selectById(dto.getBigGiftPackageId());
+ if (bigGiftPackage == null) {
+ throw new ServiceException("商品不存在");
+ }
+
+ // 2. 校验库存是否充足
+ if (bigGiftPackage.getInventory() == null || bigGiftPackage.getInventory() < dto.getQuantity()) {
+ throw new ServiceException("商品库存不足");
+ }
+
+ // 3. 校验支付方式
+ if (dto.getPaymentType() != 1 && dto.getPaymentType() != 2) {
+ throw new ServiceException("支付方式不正确");
+ }
+
+ // 4. 使用乐观锁扣减库存(防止超卖)
+ int updatedRows = bigGiftPackageMapper.deductInventory(dto.getBigGiftPackageId(), dto.getQuantity());
+ if (updatedRows == 0) {
+ throw new ServiceException("商品库存不足,请稍后重试");
+ }
+
+ // 5. 计算支付金额
+ BigDecimal paymentAmount;
+ if (dto.getPaymentType() == 1) {
+ // 订金+尾款模式,先支付订金
+ paymentAmount = bigGiftPackage.getDeposit().multiply(new BigDecimal(dto.getQuantity()));
+ } else {
+ // 全款模式
+ paymentAmount = bigGiftPackage.getTotalPrice().multiply(new BigDecimal(dto.getQuantity()));
+ }
+
+ // 6. 先生成支付订单号(所有订单共用同一个支付订单号,便于支付回调时查询)
+ String payOrderCode = OrderCodeGenerator.generateBigGiftPayOrderCode();
+
+ // 7. 批量创建订单
+ List<TBigGiftPackageOrder> orderList = new ArrayList<>();
+ List<String> orderCodes = new ArrayList<>();
+ String goodsJson = JSON.toJSONString(bigGiftPackage);
+ LocalDateTime now = LocalDateTime.now();
+
+ for (int i = 0; i < dto.getQuantity(); i++) {
+ TBigGiftPackageOrder order = new TBigGiftPackageOrder();
+ String orderCode = OrderCodeGenerator.generateBigGiftOrderCode();
+ order.setCode(orderCode);
+ order.setPayOrderCode(payOrderCode);
+ order.setAppUserId(appUserId);
+ order.setCreateTime(now);
+ order.setBigGiftPackageId(dto.getBigGiftPackageId());
+ order.setBigGiftPackage(goodsJson);
+ order.setOrderPrice(bigGiftPackage.getTotalPrice());
+ order.setDeposit(bigGiftPackage.getDeposit());
+ order.setFinalPayment(bigGiftPackage.getTotalPrice().subtract(bigGiftPackage.getDeposit()));
+ order.setPaymentType(dto.getPaymentType());
+ order.setEffectDayFlag(dto.getEffectDayFlag());
+ // 订单状态:1=待付款
+ order.setStatus(1);
+ order.setDelFlag(0);
+
+ orderList.add(order);
+ orderCodes.add(orderCode);
+ }
+
+ // 批量插入订单
+ this.saveBatch(orderList);
+
+ // 获取订单ID列表
+ List<Integer> orderIds = orderList.stream()
+ .map(TBigGiftPackageOrder::getId)
+ .collect(Collectors.toList());
+
+ // 8. 调用工行支付
+ log.info("大礼包下单,支付订单号:{},业务订单号:{},支付金额:{},用户ID:{}", payOrderCode, orderCodes, paymentAmount, appUserId);
+ JSONObject paymentParams = icbcPayUtil.wechatPay(payOrderCode, paymentAmount, bigGiftPackage.getName(), openid,
+ icbcConfig.getNotifyUrl() + "/big-gift/pay-notify");
+
+ // 9. 构建返回结果
+ PlaceOrderVO vo = new PlaceOrderVO();
+ vo.setOrderIds(orderIds);
+ vo.setOrderCodes(orderCodes);
+ vo.setPaymentParams(paymentParams);
+ return vo;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public PlaceOrderVO payFinalPayment(Integer orderId, Integer appUserId, String openid) {
+ // 1. 查询订单
+ TBigGiftPackageOrder order = this.getById(orderId);
+ if (order == null) {
+ throw new ServiceException("订单不存在");
+ }
+
+ // 2. 校验订单归属
+ if (!order.getAppUserId().equals(appUserId)) {
+ throw new ServiceException("无权操作此订单");
+ }
+
+ // 3. 校验支付方式(必须是订金+尾款模式)
+ if (order.getPaymentType() != 1) {
+ throw new ServiceException("该订单不支持尾款支付");
+ }
+
+ // 4. 校验订单状态(必须是已付订金状态)
+ if (order.getStatus() != 2) {
+ throw new ServiceException("订单状态不正确,无法支付尾款");
+ }
+
+ // 5. 获取尾款金额
+ BigDecimal finalPayment = order.getFinalPayment();
+ if (finalPayment == null || finalPayment.compareTo(BigDecimal.ZERO) <= 0) {
+ throw new ServiceException("尾款金额异常");
+ }
+
+ // 6. 生成尾款支付订单号
+ String finalPayOrderCode = OrderCodeGenerator.generateBigGiftFinalPayOrderCode();
+
+ // 7. 调用工行支付
+ log.info("大礼包尾款支付,订单ID:{},支付订单号:{},尾款金额:{},用户ID:{}", orderId, finalPayOrderCode, finalPayment, appUserId);
+ JSONObject paymentParams = icbcPayUtil.wechatPay(finalPayOrderCode, finalPayment, "大礼包尾款", openid,
+ icbcConfig.getNotifyUrl() + "/big-gift/final-pay-notify");
+
+ // 8. 构建返回结果
+ PlaceOrderVO vo = new PlaceOrderVO();
+ vo.setOrderIds(Collections.singletonList(orderId));
+ vo.setOrderCodes(Collections.singletonList(order.getCode()));
+ vo.setPaymentParams(paymentParams);
+ return vo;
+ }
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TCommissionTransactionFlowServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TCommissionTransactionFlowServiceImpl.java
index 6b59d59..9c1a94a 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TCommissionTransactionFlowServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TCommissionTransactionFlowServiceImpl.java
@@ -1,10 +1,21 @@
package com.ruoyi.system.service.impl;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.ruoyi.system.model.TAppUser;
import com.ruoyi.system.model.TCommissionTransactionFlow;
import com.ruoyi.system.mapper.TCommissionTransactionFlowMapper;
+import com.ruoyi.system.model.THtmlConfig;
+import com.ruoyi.system.service.TAppUserService;
import com.ruoyi.system.service.TCommissionTransactionFlowService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.system.service.THtmlConfigService;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDateTime;
/**
* <p>
@@ -14,7 +25,232 @@
* @author xiaochen
* @since 2025-12-08
*/
+@Slf4j
@Service
public class TCommissionTransactionFlowServiceImpl extends ServiceImpl<TCommissionTransactionFlowMapper, TCommissionTransactionFlow> implements TCommissionTransactionFlowService {
+ @Resource
+ private TAppUserService appUserService;
+
+ @Resource
+ private THtmlConfigService htmlConfigService;
+
+ @Override
+ public void calculateAndSaveCommission(Integer orderId, Integer orderType, BigDecimal orderPrice, Integer appUserId) {
+ try {
+ log.info("开始计算分佣,订单ID:{},订单类型:{},订单金额:{},用户ID:{}", orderId, orderType, orderPrice, appUserId);
+
+ // 获取购买用户信息
+ TAppUser buyer = appUserService.getById(appUserId);
+ if (buyer == null) {
+ log.error("用户不存在,用户ID:{}", appUserId);
+ return;
+ }
+
+ // 获取分佣比例配置
+ CommissionRate commissionRate = getCommissionRate();
+ if (commissionRate == null) {
+ log.error("分佣比例配置不存在");
+ return;
+ }
+
+ log.info("分佣比例配置:营销员={}%,合伙人={}%", commissionRate.getSalesRate(), commissionRate.getPartnerRate());
+
+ // 根据用户类型计算分佣
+ Integer buyerType = buyer.getUserType();
+ Integer referrerUserId = buyer.getReferrerUserId();
+
+ if (buyerType == null || buyerType == 1) {
+ // 顾客(无身份)购买
+ calculateForCustomer(orderId, orderType, orderPrice, referrerUserId, commissionRate);
+ } else if (buyerType == 2) {
+ // 营销员本人购买
+ calculateForSales(orderId, orderType, orderPrice, appUserId, referrerUserId, commissionRate);
+ } else if (buyerType == 3) {
+ // 合伙人本人购买
+ calculateForPartner(orderId, orderType, orderPrice, appUserId, commissionRate);
+ }
+
+ log.info("分佣计算完成,订单ID:{}", orderId);
+ } catch (Exception e) {
+ log.error("分佣计算失败,订单ID:{},错误信息:{}", orderId, e.getMessage(), e);
+ }
+ }
+
+ /**
+ * 顾客购买的分佣计算
+ */
+ private void calculateForCustomer(Integer orderId, Integer orderType, BigDecimal orderPrice,
+ Integer referrerUserId, CommissionRate commissionRate) {
+ if (referrerUserId == null) {
+ log.info("顾客无推荐人,无需分佣");
+ return;
+ }
+
+ // 获取推荐人信息
+ TAppUser referrer = appUserService.getById(referrerUserId);
+ if (referrer == null) {
+ log.error("推荐人不存在,推荐人ID:{}", referrerUserId);
+ return;
+ }
+
+ Integer referrerType = referrer.getUserType();
+ if (referrerType == null || referrerType == 1) {
+ log.info("推荐人不是营销员或合伙人,无需分佣");
+ return;
+ }
+
+ if (referrerType == 2) {
+ // 直接营销员
+ saveCommissionFlow(orderId, orderType, orderPrice, referrerUserId, 2,
+ commissionRate.getSalesRate());
+ log.info("顾客购买,直接营销员分佣:{}%", commissionRate.getSalesRate());
+
+ // 查找直接合伙人
+ Integer partnerUserId = findDirectPartner(referrer);
+ if (partnerUserId != null) {
+ saveCommissionFlow(orderId, orderType, orderPrice, partnerUserId, 3,
+ commissionRate.getPartnerRate());
+ log.info("顾客购买,直接合伙人分佣:{}%", commissionRate.getPartnerRate());
+ }
+ } else if (referrerType == 3) {
+ // 直接合伙人(无直接营销员)
+ BigDecimal totalRate = commissionRate.getSalesRate().add(commissionRate.getPartnerRate());
+ saveCommissionFlow(orderId, orderType, orderPrice, referrerUserId, 3, totalRate);
+ log.info("顾客购买,直接合伙人(无营销员)分佣:{}%", totalRate);
+ }
+ }
+
+ /**
+ * 营销员本人购买的分佣计算
+ */
+ private void calculateForSales(Integer orderId, Integer orderType, BigDecimal orderPrice,
+ Integer salesUserId, Integer referrerUserId, CommissionRate commissionRate) {
+ // 营销员本人获得 8%
+ saveCommissionFlow(orderId, orderType, orderPrice, salesUserId, 2,
+ commissionRate.getSalesRate());
+ log.info("营销员本人购买,本人分佣:{}%", commissionRate.getSalesRate());
+
+ // 查找直接合伙人
+ TAppUser sales = appUserService.getById(salesUserId);
+ if (sales != null) {
+ Integer partnerUserId = findDirectPartner(sales);
+ if (partnerUserId != null) {
+ saveCommissionFlow(orderId, orderType, orderPrice, partnerUserId, 3,
+ commissionRate.getPartnerRate());
+ log.info("营销员本人购买,直接合伙人分佣:{}%", commissionRate.getPartnerRate());
+ }
+ }
+ }
+
+ /**
+ * 合伙人本人购买的分佣计算
+ */
+ private void calculateForPartner(Integer orderId, Integer orderType, BigDecimal orderPrice,
+ Integer partnerUserId, CommissionRate commissionRate) {
+ // 合伙人本人获得 10% + 8% = 18%
+ BigDecimal totalRate = commissionRate.getSalesRate().add(commissionRate.getPartnerRate());
+ saveCommissionFlow(orderId, orderType, orderPrice, partnerUserId, 3, totalRate);
+ log.info("合伙人本人购买,本人分佣:{}%", totalRate);
+ }
+
+ /**
+ * 查找直接合伙人
+ */
+ private Integer findDirectPartner(TAppUser user) {
+ Integer referrerUserId = user.getReferrerUserId();
+ if (referrerUserId == null) {
+ return null;
+ }
+
+ TAppUser referrer = appUserService.getById(referrerUserId);
+ if (referrer == null) {
+ return null;
+ }
+
+ // 如果推荐人是合伙人,直接返回
+ if (referrer.getUserType() != null && referrer.getUserType() == 3) {
+ return referrerUserId;
+ }
+
+ // 如果推荐人是营销员,继续向上查找
+ if (referrer.getUserType() != null && referrer.getUserType() == 2) {
+ return findDirectPartner(referrer);
+ }
+
+ return null;
+ }
+
+ /**
+ * 保存佣金流水记录
+ */
+ private void saveCommissionFlow(Integer orderId, Integer orderType, BigDecimal orderPrice,
+ Integer userId, Integer userType, BigDecimal commissionRate) {
+ // 计算佣金金额
+ BigDecimal commissionAmount = orderPrice.multiply(commissionRate)
+ .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
+
+ TCommissionTransactionFlow flow = new TCommissionTransactionFlow();
+ flow.setAppUserId(userId);
+ flow.setAppUserType(userType);
+ flow.setCommissionRate(commissionRate);
+ flow.setOrderId(orderId);
+ flow.setOrderType(orderType);
+ flow.setOrderPrice(orderPrice);
+ flow.setCommissionAmount(commissionAmount);
+ flow.setCreateTime(LocalDateTime.now());
+
+ save(flow);
+ log.info("保存佣金流水成功,用户ID:{},佣金金额:{}", userId, commissionAmount);
+ }
+
+ /**
+ * 获取分佣比例配置
+ */
+ private CommissionRate getCommissionRate() {
+ LambdaQueryWrapper<THtmlConfig> wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(THtmlConfig::getConfigType, 5);
+ THtmlConfig config = htmlConfigService.getOne(wrapper);
+
+ if (config == null || config.getContent() == null || config.getContent().isEmpty()) {
+ return null;
+ }
+
+ try {
+ String[] rates = config.getContent().split(",");
+ if (rates.length != 2) {
+ log.error("分佣比例配置格式错误:{}", config.getContent());
+ return null;
+ }
+
+ BigDecimal salesRate = new BigDecimal(rates[0].trim());
+ BigDecimal partnerRate = new BigDecimal(rates[1].trim());
+
+ return new CommissionRate(salesRate, partnerRate);
+ } catch (Exception e) {
+ log.error("解析分佣比例配置失败:{}", config.getContent(), e);
+ return null;
+ }
+ }
+
+ /**
+ * 分佣比例配置类
+ */
+ private static class CommissionRate {
+ private final BigDecimal salesRate;
+ private final BigDecimal partnerRate;
+
+ public CommissionRate(BigDecimal salesRate, BigDecimal partnerRate) {
+ this.salesRate = salesRate;
+ this.partnerRate = partnerRate;
+ }
+
+ public BigDecimal getSalesRate() {
+ return salesRate;
+ }
+
+ public BigDecimal getPartnerRate() {
+ return partnerRate;
+ }
+ }
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TPhysicalGoodsOrderServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TPhysicalGoodsOrderServiceImpl.java
index d38ea76..0d55c84 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TPhysicalGoodsOrderServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TPhysicalGoodsOrderServiceImpl.java
@@ -1,13 +1,28 @@
package com.ruoyi.system.service.impl;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.system.model.TPhysicalGoodsOrder;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.system.config.ICBCConfig;
+import com.ruoyi.system.dto.PhysicalGoodsPlaceOrderDTO;
+import com.ruoyi.system.mapper.TPhysicalGoodsMapper;
import com.ruoyi.system.mapper.TPhysicalGoodsOrderMapper;
+import com.ruoyi.system.model.TPhysicalGoods;
+import com.ruoyi.system.model.TPhysicalGoodsOrder;
import com.ruoyi.system.query.BigGiftPackageQuery;
import com.ruoyi.system.service.TPhysicalGoodsOrderService;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.system.utils.ICBCPayUtil;
+import com.ruoyi.system.utils.OrderCodeGenerator;
+import com.ruoyi.system.vo.PlaceOrderVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
@@ -19,17 +34,71 @@
* @author xiaochen
* @since 2025-12-08
*/
+@Slf4j
@Service
public class TPhysicalGoodsOrderServiceImpl extends ServiceImpl<TPhysicalGoodsOrderMapper, TPhysicalGoodsOrder> implements TPhysicalGoodsOrderService {
+
+ @Resource
+ private TPhysicalGoodsMapper physicalGoodsMapper;
+
+ @Resource
+ private ICBCPayUtil icbcPayUtil;
+
+ @Autowired
+ private ICBCConfig icbcConfig;
@Override
public Page<TPhysicalGoodsOrder> pageList(BigGiftPackageQuery query) {
Page<TPhysicalGoodsOrder> page = new Page<>(query.getPageNum(), query.getPageSize());
- return this.baseMapper.pageList(page,query);
+ return this.baseMapper.pageList(page, query);
}
@Override
public List<TPhysicalGoodsOrder> export(BigGiftPackageQuery query) {
- return this.baseMapper.export(query);
+ return this.baseMapper.export(query);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public PlaceOrderVO placeOrder(PhysicalGoodsPlaceOrderDTO dto, Integer appUserId, String openid) {
+ // 1. 校验商品是否存在
+ TPhysicalGoods physicalGoods = physicalGoodsMapper.selectById(dto.getPhysicalGoodsId());
+ if (physicalGoods == null) {
+ throw new ServiceException("商品不存在");
+ }
+
+ // 2. 创建订单
+ TPhysicalGoodsOrder order = new TPhysicalGoodsOrder();
+ String orderCode = OrderCodeGenerator.generatePhysicalGoodsOrderCode();
+ order.setCode(orderCode);
+ order.setAppUserId(appUserId);
+ order.setCreateTime(LocalDateTime.now());
+ order.setPhysicalGoodsId(dto.getPhysicalGoodsId());
+ order.setPhysicalGoods(JSON.toJSONString(physicalGoods));
+ order.setOrderPrice(physicalGoods.getTotalPrice());
+ order.setPaymentPrice(physicalGoods.getTotalPrice());
+ // 支付方式:1=微信
+ order.setPaymentMethod(1);
+ // 订单状态:1=待付款
+ order.setStatus(1);
+ order.setDelFlag(0);
+
+ this.save(order);
+
+ // 3. 更新商品销量
+ Integer currentSales = physicalGoods.getSales() == null ? 0 : physicalGoods.getSales();
+ physicalGoods.setSales(currentSales + 1);
+ physicalGoodsMapper.updateById(physicalGoods);
+
+ // 4. 调用工行支付
+ log.info("实物商品下单,订单号:{},支付金额:{},用户ID:{}", orderCode, physicalGoods.getTotalPrice(), appUserId);
+ JSONObject paymentParams = icbcPayUtil.wechatPay(orderCode, physicalGoods.getTotalPrice(), physicalGoods.getName(), openid,icbcConfig.getNotifyUrl()+"/physical/pay-notify");
+
+ // 5. 构建返回结果
+ PlaceOrderVO vo = new PlaceOrderVO();
+ vo.setOrderIds(Collections.singletonList(order.getId()));
+ vo.setOrderCodes(Collections.singletonList(orderCode));
+ vo.setPaymentParams(paymentParams);
+ return vo;
}
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TPhysicalGoodsServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TPhysicalGoodsServiceImpl.java
index 1bc0fd1..3c541ca 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TPhysicalGoodsServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TPhysicalGoodsServiceImpl.java
@@ -7,6 +7,7 @@
import com.ruoyi.system.model.TPhysicalGoods;
import com.ruoyi.system.query.AppletPhysicalGoodsQuery;
import com.ruoyi.system.service.TPhysicalGoodsService;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/**
@@ -21,7 +22,7 @@
public class TPhysicalGoodsServiceImpl extends ServiceImpl<TPhysicalGoodsMapper, TPhysicalGoods> implements TPhysicalGoodsService {
@Override
public IPage<TPhysicalGoods> getPageList(AppletPhysicalGoodsQuery query) {
- return this.lambdaQuery().like(TPhysicalGoods::getName, query.getName())
+ return this.lambdaQuery().like(StringUtils.isNotBlank(query.getName()),TPhysicalGoods::getName, query.getName())
.page(new Page<>(query.getPageNum(), query.getPageSize()));
}
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/ICBCPayUtil.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/ICBCPayUtil.java
index 635c267..393e7d2 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/utils/ICBCPayUtil.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/ICBCPayUtil.java
@@ -1,80 +1,80 @@
-//package com.ruoyi.system.utils;
-//
-//import com.alibaba.fastjson2.JSON;
-//import com.alibaba.fastjson2.JSONObject;
-//import com.icbc.api.DefaultIcbcClient;
-//import com.icbc.api.IcbcApiException;
-//import com.icbc.api.request.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1;
-//import com.icbc.api.response.CardbusinessAggregatepayB2cOnlineConsumepurchaseResponseV1;
-//import com.ruoyi.common.exception.ServiceException;
-//import com.ruoyi.common.utils.DateUtils;
-//import com.ruoyi.common.utils.WebUtils;
-//import com.ruoyi.common.utils.ip.IpUtils;
-//import com.ruoyi.system.config.IcbcConfig;
-//import com.ruoyi.system.enums.ICBCRespOrderErrorEnum;
-//import lombok.extern.slf4j.Slf4j;
-//import org.springframework.beans.factory.annotation.Autowired;
-//import org.springframework.stereotype.Component;
-//
-//import java.math.BigDecimal;
-//
-//@Slf4j
-//@Component
-//public class ICBCPayUtil {
-//
-// @Autowired
-// private IcbcConfig icbcConfig;
-//
-// public JSONObject wechatPay(String outTradeNo, BigDecimal totalPrice, String goodsName,String openid){
-// CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1Biz bizContent = new
-// CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1Biz();
-// bizContent.setMer_id(icbcConfig.getMerId());
-// bizContent.setMer_prtcl_no(icbcConfig.getMerPrtclNo());
-// bizContent.setDecive_info(icbcConfig.getDeciveInfo());
-// bizContent.setOut_trade_no(outTradeNo);
-// bizContent.setOrig_date_time(DateUtils.getCurrentDateTimeWithT());
-// //交易币种,目前工行只支持使用人民币(001)支付
-// bizContent.setFee_type("001");
-// bizContent.setSpbill_create_ip(IpUtils.getIpAddr(WebUtils.request()));
-// //金额,单位分
-// bizContent.setTotal_fee(String.valueOf(totalPrice.multiply(new BigDecimal(100)).intValue()));
-// //支付成功后回调url
-// bizContent.setMer_url(icbcConfig.getNotifyUrl());
-// bizContent.setBody(goodsName);
-// //收单接入方式,5-APP,7-微信公众号,8-支付宝生活号,9-微信小程序
-// bizContent.setAccess_type("9");
-// //支付方式,9-微信;10-支付宝;
-// bizContent.setPay_mode("9");
-// //商户在微信开放平台注册的APPID,支付方式为微信时不能为空
-// bizContent.setShop_appid(icbcConfig.getWxAppId());
-// //第三方用户标识,商户在微信公众号内或微信小程序内接入时必送,即access_type为7或9时,上送用户在商户APPID下的唯一标识;商户通过支付宝生活号接入时不送
-// bizContent.setOpen_id(openid);
-// bizContent.setIcbc_appid(icbcConfig.getAppId());
-// bizContent.setExpire_time(icbcConfig.getExpireTime());
-// //通知类型,表示在交易处理完成后把交易结果通知商户的处理模式。取值“HS”:在交易完成后将通知信息,主动发送给商户,发送地址为notify_url指定地址; 取值“AG”:在交易完成后不通知商户。不送或送空,默认为"HS"
-// bizContent.setNotify_type("HS");
-// //结果发送类型,通知方式为HS时有效。取值“0”:无论支付成功或者失败,银行都向商户发送交易通知信息;取值“1”,银行只向商户发送交易成功的通知信息。默认是"0"
-// bizContent.setResult_type("0");
-// CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1 request = new CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1();
-// request.setServiceUrl(icbcConfig.getApiPayUrl());
-// request.setBizContent(bizContent);
-// DefaultIcbcClient client = IcbcConfig.getInstance(icbcConfig);
-// CardbusinessAggregatepayB2cOnlineConsumepurchaseResponseV1 response;
-// JSONObject responsePackage = new JSONObject();
-// try {
-// log.info("准备调用工行支付成功,请求参数:{}", JSON.toJSONString(request));
-// response = client.execute(request);
-// if (response.isSuccess()) {
-// log.info("调用工行支付成功,返回信息:{}", JSON.toJSONString(response));
-// responsePackage = JSONObject.parseObject(response.getWx_data_package());
-// } else {
-// log.info("调用工行支付失败,返回信息:{}", JSON.toJSONString(response));
-// throw new ServiceException(ICBCRespOrderErrorEnum.fromCode(String.valueOf(response.getReturnCode())).getErrorMsg());
-// }
-// } catch (IcbcApiException e) {
-// log.error(e.toString(), e);
-// }
-// return responsePackage;
-// }
-//
-//}
+package com.ruoyi.system.utils;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.icbc.api.DefaultIcbcClient;
+import com.icbc.api.IcbcApiException;
+import com.icbc.api.request.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1;
+import com.icbc.api.response.CardbusinessAggregatepayB2cOnlineConsumepurchaseResponseV1;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.WebUtils;
+import com.ruoyi.common.utils.ip.IpUtils;
+import com.ruoyi.system.config.ICBCConfig;
+import com.ruoyi.system.enums.ICBCRespOrderErrorEnum;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+
+@Slf4j
+@Component
+public class ICBCPayUtil {
+
+ @Autowired
+ private ICBCConfig icbcConfig;
+
+ public JSONObject wechatPay(String outTradeNo, BigDecimal totalPrice, String goodsName,String openid, String notifyUrl){
+ CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1Biz bizContent = new
+ CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1Biz();
+ bizContent.setMer_id(icbcConfig.getMerId());
+ bizContent.setMer_prtcl_no(icbcConfig.getMerPrtclNo());
+ bizContent.setDecive_info(icbcConfig.getDeciveInfo());
+ bizContent.setOut_trade_no(outTradeNo);
+ bizContent.setOrig_date_time(DateUtils.getCurrentDateTimeWithT());
+ //交易币种,目前工行只支持使用人民币(001)支付
+ bizContent.setFee_type("001");
+ bizContent.setSpbill_create_ip(IpUtils.getIpAddr(WebUtils.request()));
+ //金额,单位分
+ bizContent.setTotal_fee(String.valueOf(totalPrice.multiply(new BigDecimal(100)).intValue()));
+ //支付成功后回调url
+ bizContent.setMer_url(notifyUrl);
+ bizContent.setBody(goodsName);
+ //收单接入方式,5-APP,7-微信公众号,8-支付宝生活号,9-微信小程序
+ bizContent.setAccess_type("9");
+ //支付方式,9-微信;10-支付宝;
+ bizContent.setPay_mode("9");
+ //商户在微信开放平台注册的APPID,支付方式为微信时不能为空
+ bizContent.setShop_appid(icbcConfig.getWxAppId());
+ //第三方用户标识,商户在微信公众号内或微信小程序内接入时必送,即access_type为7或9时,上送用户在商户APPID下的唯一标识;商户通过支付宝生活号接入时不送
+ bizContent.setOpen_id(openid);
+ bizContent.setIcbc_appid(icbcConfig.getAppId());
+ bizContent.setExpire_time(icbcConfig.getExpireTime());
+ //通知类型,表示在交易处理完成后把交易结果通知商户的处理模式。取值“HS”:在交易完成后将通知信息,主动发送给商户,发送地址为notify_url指定地址; 取值“AG”:在交易完成后不通知商户。不送或送空,默认为"HS"
+ bizContent.setNotify_type("HS");
+ //结果发送类型,通知方式为HS时有效。取值“0”:无论支付成功或者失败,银行都向商户发送交易通知信息;取值“1”,银行只向商户发送交易成功的通知信息。默认是"0"
+ bizContent.setResult_type("0");
+ CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1 request = new CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1();
+ request.setServiceUrl(icbcConfig.getApiPayUrl());
+ request.setBizContent(bizContent);
+ DefaultIcbcClient client = ICBCConfig.getInstance(icbcConfig);
+ CardbusinessAggregatepayB2cOnlineConsumepurchaseResponseV1 response;
+ JSONObject responsePackage = new JSONObject();
+ try {
+ log.info("准备调用工行支付成功,请求参数:{}", JSON.toJSONString(request));
+ response = client.execute(request);
+ if (response.isSuccess()) {
+ log.info("调用工行支付成功,返回信息:{}", JSON.toJSONString(response));
+ responsePackage = JSONObject.parseObject(response.getWx_data_package());
+ } else {
+ log.info("调用工行支付失败,返回信息:{}", JSON.toJSONString(response));
+ throw new ServiceException(ICBCRespOrderErrorEnum.fromCode(String.valueOf(response.getReturnCode())).getErrorMsg());
+ }
+ } catch (IcbcApiException e) {
+ log.error(e.toString(), e);
+ }
+ return responsePackage;
+ }
+
+}
--
Gitblit v1.7.1