“mitao”
2025-12-16 f894e386d81e43831ea5fda78e30a029eca6fda5
大礼包、实物商品下单、回调
1 文件已重命名
6个文件已添加
19个文件已修改
1470 ■■■■■ 已修改文件
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/HtmlConfigController.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/ICBCPayNotifyController.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/IndexController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/OrderController.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/UserIndexController.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/resources/application-prod.yml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/resources/application-test.yml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/TAppUserResp.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/config/ICBCConfig.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/enums/UnionPayStatusEnum.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/TBigGiftPackageMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TBanner.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TBigGiftPackageOrder.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TCommissionTransactionFlow.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ICBCNotifyService.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/TBigGiftPackageOrderService.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/TCommissionTransactionFlowService.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/TPhysicalGoodsOrderService.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ICBCNotifyServiceImpl.java 365 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TBigGiftPackageOrderServiceImpl.java 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TCommissionTransactionFlowServiceImpl.java 236 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TPhysicalGoodsOrderServiceImpl.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TPhysicalGoodsServiceImpl.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/utils/ICBCPayUtil.java 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/HtmlConfigController.java
New file
@@ -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);
    }
}
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/ICBCPayNotifyController.java
New file
@@ -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);
    }
}
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("获取大礼包列表")
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/OrderController.java
New file
@@ -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);
    }
}
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")
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;
    }
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"
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"
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;
}
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()
ruoyi-system/src/main/java/com/ruoyi/system/config/ICBCConfig.java
File was renamed from 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);
                }
ruoyi-system/src/main/java/com/ruoyi/system/enums/UnionPayStatusEnum.java
New file
@@ -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);
    }
}
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);
}
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;
}
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;
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;
ruoyi-system/src/main/java/com/ruoyi/system/service/ICBCNotifyService.java
New file
@@ -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);
}
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);
}
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);
}
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);
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ICBCNotifyServiceImpl.java
New file
@@ -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\":\"\"}";
        }
    }
}
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;
    }
}
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;
        }
    }
}
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,8 +34,18 @@
 * @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) {
@@ -32,4 +57,48 @@
    public List<TPhysicalGoodsOrder> export(BigGiftPackageQuery 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;
    }
}
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()));
    }
}
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;
   }
}