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