Quellcode durchsuchen

对接企业微信-获取收款记录接口

cgp vor 1 Monat
Ursprung
Commit
9de88ca252

+ 26 - 0
fs-company/src/main/java/com/fs/company/controller/qw/FsCompanyExternalPayReceiptController.java

@@ -0,0 +1,26 @@
+package com.fs.company.controller.qw;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.qw.service.IFsCompanyExternalPayReceiptService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/admin/pay/receipt")
+public class FsCompanyExternalPayReceiptController extends BaseController {
+
+    @Autowired
+    private IFsCompanyExternalPayReceiptService receiptService;
+
+    @GetMapping("/sync")
+    public AjaxResult sync(@RequestParam Long beginTime,
+                           @RequestParam Long endTime,
+                           @RequestParam(required = false) String payeeUserid) {
+        receiptService.syncReceipts(beginTime, endTime, payeeUserid);
+        return AjaxResult.success("同步完成");
+    }
+}

+ 118 - 0
fs-service/src/main/java/com/fs/qw/domain/FsCompanyExternalPayReceipt.java

@@ -0,0 +1,118 @@
+package com.fs.qw.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 企业对外收款记录实体类(仅收款记录)
+ * 表名:fs_company_external_pay_receipt
+ */
+@Data
+@TableName("fs_company_external_pay_receipt")
+public class FsCompanyExternalPayReceipt extends BaseEntity {
+
+    /**
+     * 主键ID,自增长
+     */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 交易单号(企业微信唯一标识)
+     */
+    private String transactionId;
+
+    /**
+     * 交易状态:1-已完成,3-已完成有退款
+     */
+    private Integer tradeState;
+
+    /**
+     * 交易时间(秒级时间戳)
+     */
+    private Long payTime;
+
+    /**
+     * 商户单号(外部订单号)
+     */
+    private String outTradeNo;
+
+    /**
+     * 付款人的企业微信 external_userid
+     */
+    private String externalUserid;
+
+    /**
+     * 收款总金额,单位:元
+     */
+    private BigDecimal totalFee;
+
+    /**
+     * 收款成员的企业微信 userid
+     */
+    private String payeeUserid;
+
+    /**
+     * 收款方式:0-聊天中收款,1-收款码收款,2-直播间收款,3-产品图册收款,14-转账,15-小程序
+     */
+    private Integer paymentType;
+
+    /**
+     * 收款商户号ID
+     */
+    private String mchId;
+
+    /**
+     * 收款备注(或退款备注,但本表只存收款记录,故仅为收款备注)
+     */
+    private String remark;
+
+    /**
+     * 退款总金额,单位:元(该笔收款已被退款的总额)
+     */
+    private BigDecimal totalRefundFee;
+
+    /**
+     * 商品信息 JSON 数组,格式:[{"description":"手机","amount":1}]
+     */
+    private String commodityList;
+
+    /**
+     * 联系人信息 JSON 对象,格式:{"name":"张三","phone":"138...","address":"..."}
+     * 注意:第三方应用不可获取此字段
+     */
+    private String contactInfo;
+
+    /**
+     * 小程序信息 JSON 对象,收款方式为小程序时返回,格式:{"appid":"wx...","name":"XX小程序"}
+     */
+    private String miniprogramInfo;
+
+    /**
+     * 创建时间(本地记录创建时间,自动填充)
+     */
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    /**
+     * 更新时间(本地记录更新时间,自动填充)
+     */
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Date updateTime;
+
+    // ========== 以下为查询条件扩展字段(非数据库字段) ==========
+
+    /**
+     * 查询条件:支付时间起始值(秒级时间戳)
+     */
+    private Long beginPayTime;
+
+    /**
+     * 查询条件:支付时间结束值(秒级时间戳)
+     */
+    private Long endPayTime;
+}

+ 39 - 0
fs-service/src/main/java/com/fs/qw/dto/ReceiptDto.java

@@ -0,0 +1,39 @@
+package com.fs.qw.dto;
+
+import lombok.Data;
+
+/**
+ * 企业微信对外收款记录 DTO(仅收款记录)
+ * 用于接收接口返回的原始数据
+ */
+@Data
+public class ReceiptDto {
+    /** 交易单号 */
+    private String transactionId;
+    /** 交易状态:1-已完成,3-已完成有退款 */
+    private Integer tradeState;
+    /** 交易时间(秒级时间戳) */
+    private Long payTime;
+    /** 商户单号 */
+    private String outTradeNo;
+    /** 付款人的 external_userid */
+    private String externalUserid;
+    /** 收款总金额(分) */
+    private Integer totalFee;
+    /** 收款成员 userid */
+    private String payeeUserid;
+    /** 收款方式:0聊天中,1收款码,2直播间,3产品图册,14转账,15小程序 */
+    private Integer paymentType;
+    /** 收款商户号id */
+    private String mchId;
+    /** 备注 */
+    private String remark;
+    /** 退款总金额(分) */
+    private Integer totalRefundFee;
+    /** 商品信息 JSON 字符串 */
+    private String commodityList;
+    /** 联系人信息 JSON 字符串 */
+    private String contactInfo;
+    /** 小程序信息 JSON 字符串 */
+    private String miniprogramInfo;
+}

+ 95 - 0
fs-service/src/main/java/com/fs/qw/enums/PaymentTypeEnum.java

@@ -0,0 +1,95 @@
+package com.fs.qw.enums;
+
+/**
+ * 收款方式枚举
+ */
+public enum PaymentTypeEnum {
+
+    /**
+     * 在聊天中收款
+     */
+    CHAT(0, "在聊天中收款"),
+
+    /**
+     * 收款码收款
+     */
+    QR_CODE(1, "收款码收款"),
+
+    /**
+     * 在直播间收款
+     */
+    LIVE_ROOM(2, "在直播间收款"),
+
+    /**
+     * 用产品图册收款
+     */
+    PRODUCT_ALBUM(3, "用产品图册收款"),
+
+    /**
+     * 转账
+     */
+    TRANSFER(14, "转账"),
+
+    /**
+     * 小程序收款
+     */
+    MINIPROGRAM(15, "小程序收款");
+
+    private final Integer code;
+    private final String description;
+
+    PaymentTypeEnum(Integer code, String description) {
+        this.code = code;
+        this.description = description;
+    }
+
+    /**
+     * 获取枚举code
+     */
+    public Integer getCode() {
+        return code;
+    }
+
+    /**
+     * 获取枚举描述
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * 根据code获取枚举对象
+     * @param code 收款方式code
+     * @return 枚举对象,如果不存在则返回null
+     */
+    public static PaymentTypeEnum getByCode(Integer code) {
+        if (code == null) {
+            return null;
+        }
+        for (PaymentTypeEnum value : PaymentTypeEnum.values()) {
+            if (value.code.equals(code)) {
+                return value;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 根据code获取描述
+     * @param code 收款方式code
+     * @return 描述,如果不存在则返回null或未知
+     */
+    public static String getDescriptionByCode(Integer code) {
+        PaymentTypeEnum e = getByCode(code);
+        return e == null ? null : e.description;
+    }
+
+    /**
+     * 判断code是否有效
+     * @param code 收款方式code
+     * @return true:有效 false:无效
+     */
+    public static boolean isValid(Integer code) {
+        return getByCode(code) != null;
+    }
+}

+ 67 - 0
fs-service/src/main/java/com/fs/qw/mapper/FsCompanyExternalPayReceiptMapper.java

@@ -0,0 +1,67 @@
+package com.fs.qw.mapper;
+
+import com.fs.qw.domain.FsCompanyExternalPayReceipt;
+import org.apache.ibatis.annotations.Param;
+import java.util.List;
+
+/**
+ * 企业对外收款记录 Mapper 接口
+ */
+public interface FsCompanyExternalPayReceiptMapper {
+
+    /**
+     * 条件查询收款记录列表
+     * @param receipt 查询条件(非空字段作为条件)
+     * @return 列表
+     */
+    List<FsCompanyExternalPayReceipt> selectFsCompanyExternalPayReceiptList(FsCompanyExternalPayReceipt receipt);
+
+    /**
+     * 根据主键 ID 查询
+     * @param id 主键
+     * @return 收款记录
+     */
+    FsCompanyExternalPayReceipt selectFsCompanyExternalPayReceiptById(Long id);
+
+    /**
+     * 根据交易单号查询(唯一键)
+     * @param transactionId 交易单号
+     * @return 收款记录
+     */
+    FsCompanyExternalPayReceipt selectByTransactionId(@Param("transactionId") String transactionId);
+
+    /**
+     * 新增收款记录
+     * @param receipt 收款记录
+     * @return 影响行数
+     */
+    int insertFsCompanyExternalPayReceipt(FsCompanyExternalPayReceipt receipt);
+
+    /**
+     * 批量新增
+     * @param receiptList 列表
+     * @return 影响行数
+     */
+    int insertBatchFsCompanyExternalPayReceipt(@Param("list") List<FsCompanyExternalPayReceipt> receiptList);
+
+    /**
+     * 修改收款记录
+     * @param receipt 收款记录(必须包含 id)
+     * @return 影响行数
+     */
+    int updateFsCompanyExternalPayReceipt(FsCompanyExternalPayReceipt receipt);
+
+    /**
+     * 根据主键物理删除
+     * @param id 主键
+     * @return 影响行数
+     */
+    int deleteFsCompanyExternalPayReceiptById(Long id);
+
+    /**
+     * 批量物理删除
+     * @param ids 主键数组
+     * @return 影响行数
+     */
+    int deleteFsCompanyExternalPayReceiptByIds(Long[] ids);
+}

+ 14 - 0
fs-service/src/main/java/com/fs/qw/service/IFsCompanyExternalPayReceiptService.java

@@ -0,0 +1,14 @@
+package com.fs.qw.service;
+
+import com.fs.qw.domain.FsCompanyExternalPayReceipt;
+
+import java.util.List;
+
+public interface IFsCompanyExternalPayReceiptService {
+
+    // 同步收款记录(定时任务同步或手动同步)
+    void syncReceipts(Long beginTime, Long endTime, String payeeUserid);
+
+    // 查询收款记录(查数据库)
+    List<FsCompanyExternalPayReceipt> queryReceipts(FsCompanyExternalPayReceipt receipt);
+}

+ 63 - 0
fs-service/src/main/java/com/fs/qw/service/impl/FsCompanyExternalPayReceiptServiceImpl.java

@@ -0,0 +1,63 @@
+package com.fs.qw.service.impl;
+
+import com.fs.qw.domain.FsCompanyExternalPayReceipt;
+import com.fs.qw.dto.ReceiptDto;
+import com.fs.qw.mapper.FsCompanyExternalPayReceiptMapper;
+import com.fs.qw.service.IFsCompanyExternalPayReceiptService;
+import com.fs.qw.utils.MoneyUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+
+import java.util.Collections;
+import java.util.List;
+
+@Slf4j
+@Service
+public class FsCompanyExternalPayReceiptServiceImpl implements IFsCompanyExternalPayReceiptService {
+
+    @Autowired
+    private WeWorkExternalPayService weWorkPayService;
+
+    @Autowired
+    private FsCompanyExternalPayReceiptMapper receiptMapper;
+
+    // 同步收款记录(定时任务同步或手动同步)
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void syncReceipts(Long beginTime, Long endTime, String payeeUserid) {
+        List<ReceiptDto> receiptList = weWorkPayService.fetchAllReceipts(beginTime, endTime, payeeUserid);
+        if (CollectionUtils.isEmpty(receiptList)) {
+            log.info("没有需要同步的收款记录");
+            return;
+        }
+
+        for (ReceiptDto dto : receiptList) {
+            FsCompanyExternalPayReceipt entity = new FsCompanyExternalPayReceipt();
+            BeanUtils.copyProperties(dto, entity);
+            // 同步时转换一下金额单位,微信官方返回的是分,数据库中存储的是元
+            entity.setTotalFee(MoneyUtils.centToYuan(dto.getTotalFee()));
+            entity.setTotalRefundFee(MoneyUtils.centToYuan(dto.getTotalRefundFee()));
+            FsCompanyExternalPayReceipt exist = receiptMapper.selectByTransactionId(dto.getTransactionId());
+            if (exist == null) {
+                receiptMapper.insertFsCompanyExternalPayReceipt(entity);
+            } else {
+                entity.setId(exist.getId());
+                receiptMapper.updateFsCompanyExternalPayReceipt(entity);
+            }
+        }
+        log.info("同步收款记录完成,共处理 {} 条", receiptList.size());
+    }
+
+    @Override
+    public List<FsCompanyExternalPayReceipt> queryReceipts(FsCompanyExternalPayReceipt receipt) {
+        List<FsCompanyExternalPayReceipt> fsCompanyExternalPayReceipts = receiptMapper.selectFsCompanyExternalPayReceiptList(receipt);
+        if (CollectionUtils.isEmpty(fsCompanyExternalPayReceipts)){
+            return Collections.emptyList();
+        }
+        return fsCompanyExternalPayReceipts;
+    }
+}

+ 134 - 0
fs-service/src/main/java/com/fs/qw/service/impl/WeWorkExternalPayService.java

@@ -0,0 +1,134 @@
+package com.fs.qw.service.impl;
+
+import cn.hutool.http.HttpUtil;
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.fs.qw.dto.ReceiptDto;
+import com.fs.wx.kf.service.IWeixinKfService;
+import com.fs.wx.kf.vo.WeixinKfTokenVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 企业微信对外收款服务(调用获取收款记录接口)
+ */
+@Slf4j
+@Service
+public class WeWorkExternalPayService {
+
+    @Autowired
+    private IWeixinKfService weixinKfService;
+
+    @Value("123")
+    private String corpId;
+
+    @Value("123")
+    private String corpSecret;
+
+    /**
+     * 获取 access_token
+     */
+    private String getAccessToken() {
+        WeixinKfTokenVO tokenVO = weixinKfService.getToken(corpId, corpSecret);
+        if (tokenVO.getErrcode() != null && tokenVO.getErrcode() != 0) {
+            throw new RuntimeException("获取企业微信access_token失败:" + tokenVO.getErrmsg());
+        }
+        return tokenVO.getAccess_token();
+    }
+
+    /**
+     * 拉取所有收款记录(自动分页)
+     * @param beginTime 开始时间戳(秒)
+     * @param endTime   结束时间戳(秒)
+     * @param payeeUserid 可选,收款成员userid
+     */
+    public List<ReceiptDto> fetchAllReceipts(Long beginTime, Long endTime, String payeeUserid) {
+        List<ReceiptDto> allReceipts = new ArrayList<>();
+        String cursor = null;
+
+        do {
+            Map<String, Object> params = new HashMap<>();
+            params.put("begin_time", beginTime);
+            params.put("end_time", endTime);
+            if (StringUtils.hasText(payeeUserid)) {
+                params.put("payee_userid", payeeUserid);
+            }
+            if (StringUtils.hasText(cursor)) {
+                params.put("cursor", cursor);
+            }
+            params.put("limit", 1000);
+
+            String accessToken = getAccessToken();
+            String url = "https://qyapi.weixin.qq.com/cgi-bin/externalpay/get_bill_list?access_token=" + accessToken;
+
+            // 发送 POST 请求
+            String body = HttpUtil.post(url, JSONUtil.toJsonStr(params));
+            JSONObject json = JSONUtil.parseObj(body);
+
+            Integer errcode = json.getInt("errcode");
+            if (errcode == null || errcode != 0) {
+                String errmsg = json.getStr("errmsg", "");
+                throw new RuntimeException("调用企业微信对外收款接口失败:" + errmsg);
+            }
+
+            cursor = json.getStr("next_cursor");
+            JSONArray billList = json.getJSONArray("bill_list");
+            if (billList != null && !billList.isEmpty()) {
+                for (Object obj : billList) {
+                    JSONObject item = (JSONObject) obj;
+                    // 只处理收款记录(bill_type=0)
+                    Integer billType = item.getInt("bill_type");
+                    if (billType != null && billType == 0) {
+                        ReceiptDto dto = convertToReceiptDto(item);
+                        allReceipts.add(dto);
+                    }
+                }
+            }
+        } while (StringUtils.hasText(cursor));
+
+        log.info("同步收款记录完成,时间范围:{} ~ {},共 {} 条", beginTime, endTime, allReceipts.size());
+        return allReceipts;
+    }
+
+    /**
+     * 转换 JSON 对象为 ReceiptDto
+     */
+    private ReceiptDto convertToReceiptDto(JSONObject item) {
+        ReceiptDto dto = new ReceiptDto();
+        dto.setTransactionId(item.getStr("transaction_id"));
+        dto.setTradeState(item.getInt("trade_state"));
+        dto.setPayTime(item.getLong("pay_time"));
+        dto.setOutTradeNo(item.getStr("out_trade_no"));
+        dto.setExternalUserid(item.getStr("external_userid"));
+        dto.setTotalFee(item.getInt("total_fee"));
+        dto.setPayeeUserid(item.getStr("payee_userid"));
+        dto.setPaymentType(item.getInt("payment_type"));
+        dto.setMchId(item.getStr("mch_id"));
+        dto.setRemark(item.getStr("remark"));
+        dto.setTotalRefundFee(item.getInt("total_refund_fee"));
+
+        // 嵌套对象转 JSON 字符串
+        Object commodityListObj = item.get("commodity_list");
+        if (commodityListObj != null) {
+            dto.setCommodityList(JSONUtil.toJsonStr(commodityListObj));
+        }
+        Object contactInfoObj = item.get("contact_info");
+        if (contactInfoObj != null) {
+            dto.setContactInfo(JSONUtil.toJsonStr(contactInfoObj));
+        }
+        Object miniprogramInfoObj = item.get("miniprogram_info");
+        if (miniprogramInfoObj != null) {
+            dto.setMiniprogramInfo(JSONUtil.toJsonStr(miniprogramInfoObj));
+        }
+        return dto;
+    }
+}

+ 56 - 0
fs-service/src/main/java/com/fs/qw/utils/MoneyUtils.java

@@ -0,0 +1,56 @@
+package com.fs.qw.utils;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+/**
+ * 金额单位转换工具
+ * 企业微信接口金额单位:分
+ * 商城系统内部金额单位:元(BigDecimal)
+ */
+public class MoneyUtils {
+
+    private static final int SCALE = 2; // 保留两位小数
+    private static final int CENT_FACTOR = 100;
+
+    /**
+     * 分 → 元(BigDecimal)
+     * @param cent 分(整数)
+     * @return 元(BigDecimal)
+     */
+    public static BigDecimal centToYuan(Integer cent) {
+        if (cent == null) {
+            return BigDecimal.ZERO;
+        }
+        return BigDecimal.valueOf(cent).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
+    }
+
+    /**
+     * 分 → 元(String,带两位小数)
+     */
+    public static String centToYuanStr(Integer cent) {
+        return centToYuan(cent).toString();
+    }
+
+    /**
+     * 元 → 分
+     * @param yuan 元(BigDecimal)
+     * @return 分(整数)
+     */
+    public static Integer yuanToCent(BigDecimal yuan) {
+        if (yuan == null) {
+            return 0;
+        }
+        return yuan.multiply(BigDecimal.valueOf(CENT_FACTOR)).intValue();
+    }
+
+    /**
+     * 元(字符串)→ 分
+     */
+    public static Integer yuanToCent(String yuanStr) {
+        if (yuanStr == null || yuanStr.isEmpty()) {
+            return 0;
+        }
+        return yuanToCent(new BigDecimal(yuanStr));
+    }
+}

+ 187 - 0
fs-service/src/main/resources/mapper/qw/FsCompanyExternalPayReceiptMapper.xml

@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.qw.mapper.FsCompanyExternalPayReceiptMapper">
+
+    <resultMap type="com.fs.qw.domain.FsCompanyExternalPayReceipt" id="FsCompanyExternalPayReceiptResult">
+        <result property="id"                column="id"                 />
+        <result property="transactionId"     column="transaction_id"     />
+        <result property="tradeState"        column="trade_state"        />
+        <result property="payTime"           column="pay_time"           />
+        <result property="outTradeNo"        column="out_trade_no"       />
+        <result property="externalUserid"    column="external_userid"    />
+        <result property="totalFee"          column="total_fee"          />
+        <result property="payeeUserid"       column="payee_userid"       />
+        <result property="paymentType"       column="payment_type"       />
+        <result property="mchId"             column="mch_id"             />
+        <result property="remark"            column="remark"             />
+        <result property="totalRefundFee"    column="total_refund_fee"   />
+        <result property="commodityList"     column="commodity_list"     />
+        <result property="contactInfo"       column="contact_info"       />
+        <result property="miniprogramInfo"   column="miniprogram_info"   />
+        <result property="createTime"        column="create_time"        />
+        <result property="updateTime"        column="update_time"        />
+    </resultMap>
+
+    <sql id="selectFsCompanyExternalPayReceiptVo">
+        select id, transaction_id, trade_state, pay_time, out_trade_no,
+               external_userid, total_fee, payee_userid, payment_type,
+               mch_id, remark, total_refund_fee, commodity_list,
+               contact_info, miniprogram_info, create_time, update_time
+        from fs_company_external_pay_receipt
+    </sql>
+
+    <!-- 条件查询列表 -->
+    <select id="selectFsCompanyExternalPayReceiptList" parameterType="com.fs.qw.domain.FsCompanyExternalPayReceipt" resultMap="FsCompanyExternalPayReceiptResult">
+        <include refid="selectFsCompanyExternalPayReceiptVo"/>
+        <where>
+            <if test="transactionId != null and transactionId != ''">
+                and transaction_id = #{transactionId}
+            </if>
+            <if test="tradeState != null">
+                and trade_state = #{tradeState}
+            </if>
+            <if test="payTime != null">
+                and pay_time = #{payTime}
+            </if>
+            <if test="outTradeNo != null and outTradeNo != ''">
+                and out_trade_no = #{outTradeNo}
+            </if>
+            <if test="externalUserid != null and externalUserid != ''">
+                and external_userid = #{externalUserid}
+            </if>
+            <if test="totalFee != null">
+                and total_fee = #{totalFee}
+            </if>
+            <if test="payeeUserid != null and payeeUserid != ''">
+                and payee_userid = #{payeeUserid}
+            </if>
+            <if test="paymentType != null">
+                and payment_type = #{paymentType}
+            </if>
+            <if test="mchId != null and mchId != ''">
+                and mch_id = #{mchId}
+            </if>
+            <if test="remark != null and remark != ''">
+                and remark like concat('%', #{remark}, '%')
+            </if>
+            <if test="totalRefundFee != null">
+                and total_refund_fee = #{totalRefundFee}
+            </if>
+            <if test="beginPayTime != null and beginPayTime != ''">
+                and pay_time &gt;= #{beginPayTime}
+            </if>
+            <if test="endPayTime != null and endPayTime != ''">
+                and pay_time &lt;= #{endPayTime}
+            </if>
+        </where>
+        order by pay_time desc
+    </select>
+
+    <!-- 根据主键 ID 查询 -->
+    <select id="selectFsCompanyExternalPayReceiptById" parameterType="Long" resultMap="FsCompanyExternalPayReceiptResult">
+        <include refid="selectFsCompanyExternalPayReceiptVo"/>
+        where id = #{id}
+    </select>
+
+    <!-- 根据交易单号查询 -->
+    <select id="selectByTransactionId" resultMap="FsCompanyExternalPayReceiptResult">
+        <include refid="selectFsCompanyExternalPayReceiptVo"/>
+        where transaction_id = #{transactionId}
+    </select>
+
+    <!-- 新增(动态字段) -->
+    <insert id="insertFsCompanyExternalPayReceipt" parameterType="com.fs.qw.domain.FsCompanyExternalPayReceipt" useGeneratedKeys="true" keyProperty="id">
+        insert into fs_company_external_pay_receipt
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="transactionId != null">transaction_id,</if>
+            <if test="tradeState != null">trade_state,</if>
+            <if test="payTime != null">pay_time,</if>
+            <if test="outTradeNo != null">out_trade_no,</if>
+            <if test="externalUserid != null">external_userid,</if>
+            <if test="totalFee != null">total_fee,</if>
+            <if test="payeeUserid != null">payee_userid,</if>
+            <if test="paymentType != null">payment_type,</if>
+            <if test="mchId != null">mch_id,</if>
+            <if test="remark != null">remark,</if>
+            <if test="totalRefundFee != null">total_refund_fee,</if>
+            <if test="commodityList != null">commodity_list,</if>
+            <if test="contactInfo != null">contact_info,</if>
+            <if test="miniprogramInfo != null">miniprogram_info,</if>
+            create_time, update_time
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="transactionId != null">#{transactionId},</if>
+            <if test="tradeState != null">#{tradeState},</if>
+            <if test="payTime != null">#{payTime},</if>
+            <if test="outTradeNo != null">#{outTradeNo},</if>
+            <if test="externalUserid != null">#{externalUserid},</if>
+            <if test="totalFee != null">#{totalFee},</if>
+            <if test="payeeUserid != null">#{payeeUserid},</if>
+            <if test="paymentType != null">#{paymentType},</if>
+            <if test="mchId != null">#{mchId},</if>
+            <if test="remark != null">#{remark},</if>
+            <if test="totalRefundFee != null">#{totalRefundFee},</if>
+            <if test="commodityList != null">#{commodityList},</if>
+            <if test="contactInfo != null">#{contactInfo},</if>
+            <if test="miniprogramInfo != null">#{miniprogramInfo},</if>
+            sysdate(), sysdate()
+        </trim>
+    </insert>
+
+    <!-- 批量新增 -->
+    <insert id="insertBatchFsCompanyExternalPayReceipt" parameterType="list">
+        insert into fs_company_external_pay_receipt (
+        transaction_id, trade_state, pay_time, out_trade_no,
+        external_userid, total_fee, payee_userid, payment_type,
+        mch_id, remark, total_refund_fee, commodity_list,
+        contact_info, miniprogram_info, create_time, update_time
+        ) values
+        <foreach collection="list" item="item" separator=",">
+            (
+            #{item.transactionId}, #{item.tradeState}, #{item.payTime}, #{item.outTradeNo},
+            #{item.externalUserid}, #{item.totalFee}, #{item.payeeUserid}, #{item.paymentType},
+            #{item.mchId}, #{item.remark}, #{item.totalRefundFee}, #{item.commodityList},
+            #{item.contactInfo}, #{item.miniprogramInfo}, sysdate(), sysdate()
+            )
+        </foreach>
+    </insert>
+
+    <!-- 修改(动态字段) -->
+    <update id="updateFsCompanyExternalPayReceipt" parameterType="com.fs.qw.domain.FsCompanyExternalPayReceipt">
+        update fs_company_external_pay_receipt
+        <set>
+            <if test="transactionId != null">transaction_id = #{transactionId},</if>
+            <if test="tradeState != null">trade_state = #{tradeState},</if>
+            <if test="payTime != null">pay_time = #{payTime},</if>
+            <if test="outTradeNo != null">out_trade_no = #{outTradeNo},</if>
+            <if test="externalUserid != null">external_userid = #{externalUserid},</if>
+            <if test="totalFee != null">total_fee = #{totalFee},</if>
+            <if test="payeeUserid != null">payee_userid = #{payeeUserid},</if>
+            <if test="paymentType != null">payment_type = #{paymentType},</if>
+            <if test="mchId != null">mch_id = #{mchId},</if>
+            <if test="remark != null">remark = #{remark},</if>
+            <if test="totalRefundFee != null">total_refund_fee = #{totalRefundFee},</if>
+            <if test="commodityList != null">commodity_list = #{commodityList},</if>
+            <if test="contactInfo != null">contact_info = #{contactInfo},</if>
+            <if test="miniprogramInfo != null">miniprogram_info = #{miniprogramInfo},</if>
+            update_time = sysdate()
+        </set>
+        where id = #{id}
+    </update>
+
+    <!-- 物理删除(单条) -->
+    <delete id="deleteFsCompanyExternalPayReceiptById" parameterType="Long">
+        delete from fs_company_external_pay_receipt where id = #{id}
+    </delete>
+
+    <!-- 批量物理删除 -->
+    <delete id="deleteFsCompanyExternalPayReceiptByIds" parameterType="String">
+        delete from fs_company_external_pay_receipt where id in
+        <foreach collection="array" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+</mapper>