Browse Source

feat(payment): 新增退款审核流程及角色权限配置

- 新增销售经理、销售主管、数据运营等多个角色并配置数据权限
- 修改销售角色为个人数据权限并调整排序
- 优化角色菜单权限配置逻辑,增加异常处理
- 移除退款金额统计时对审核状态的限制条件
- 调整退款审核状态值,将审核完成状态从2改为1
- 在支付统计查询中增加appId和退款审核状态筛选条件
- 新增按日期范围查询支付记录的功能支持
- 完善退款统计逻辑,支持按appId进行筛选统计
- 新增普通员工退款申请接口及管理员审核退款接口
- 实现退款申请分页查询及统计功能
- 完善汇付支付退款逻辑,增加appId对应的huifuId配置查询
- 注释掉未实现的支付宝退款功能,提示需联系管理员配置
- 新增退款审核日志记录及事务回滚处理
xw 6 days ago
parent
commit
672878d6f1

+ 1 - 4
fs-company/src/main/java/com/fs/hisStore/controller/FsStorePaymentScrmController.java

@@ -96,13 +96,10 @@ public class FsStorePaymentScrmController extends BaseController
             }
             
             // 状态为-1(已退款)时,累加到总退款金额
-            // 根据规范,只统计退款审核状态为2(审核完成)的退款金额
             if (vo.getStatus() != null && vo.getStatus() == -1) {
-                if (vo.getRefundAuditStatus() != null && vo.getRefundAuditStatus() == 2) {
                     if (vo.getRefundMoney() != null) {
                         totalRefundAmount = totalRefundAmount.add(vo.getRefundMoney());
                     }
-                }
             }
         }
         
@@ -255,7 +252,7 @@ public class FsStorePaymentScrmController extends BaseController
                     payment.setRefundMoney(fsStorePayment.getRefundMoney());
                     payment.setStatus(-1);
                     payment.setRefundTime(new Date());
-                    payment.setRefundAuditStatus(2);
+                    payment.setRefundAuditStatus(1); // 1-审核完成
                     fsStorePaymentService.updateFsStorePayment(payment);
                     //收款 减去所有
                     if(payment.getCompanyId()!=null&&payment.getCompanyId()>0){

+ 80 - 7
fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java

@@ -281,15 +281,89 @@ public class CompanyServiceImpl implements ICompanyService
             adminRole.setStatus("0");
             roleMapper.insertCompanyRole(adminRole);
 
-            // 创建销售角色(新增部分,只有"我的"相关权限)
+            // 1. 创建销售经理角色(全部数据权限)
+            CompanyRole salesManagerRole = new CompanyRole();
+            salesManagerRole.setCompanyId(company.getCompanyId());
+            salesManagerRole.setRoleName("销售经理");
+            salesManagerRole.setRoleKey(company.getCompanyId()+"_sales_manager");
+            salesManagerRole.setRoleSort(1);
+            salesManagerRole.setDataScope("1"); // 全部数据权限
+            salesManagerRole.setStatus("0");
+            try {
+                String json = configService.selectConfigByKey("companymenu.config");
+                if (StringUtils.isNotEmpty(json) && !json.equals("")) {
+                    CompanyMenuConfig config = JSONUtil.toBean(json, CompanyMenuConfig.class);
+                    salesManagerRole.setMenuIds(config.getMenuIds());
+                    if(config.getMenuIds().length > 0){
+                        roleService.insertRole(salesManagerRole);
+                    }else {
+                        roleService.insertDefaultRole(salesManagerRole);
+                    }
+                }else {
+                    roleService.insertDefaultRole(salesManagerRole);
+                }
+            } catch (Exception e) {
+                logger.error("获取菜单配置失败-销售经理", e);
+            }
+
+            // 2. 创建销售主管角色(本部门数据权限)
+            CompanyRole salesSupervisorRole = new CompanyRole();
+            salesSupervisorRole.setCompanyId(company.getCompanyId());
+            salesSupervisorRole.setRoleName("销售主管");
+            salesSupervisorRole.setRoleKey(company.getCompanyId()+"_sales_supervisor");
+            salesSupervisorRole.setRoleSort(2);
+            salesSupervisorRole.setDataScope("2"); // 本部门数据权限
+            salesSupervisorRole.setStatus("0");
+            try {
+                String json = configService.selectConfigByKey("companymenu.config");
+                if (StringUtils.isNotEmpty(json) && !json.equals("")) {
+                    CompanyMenuConfig config = JSONUtil.toBean(json, CompanyMenuConfig.class);
+                    salesSupervisorRole.setMenuIds(config.getMenuIds());
+                    if(config.getMenuIds().length > 0){
+                        roleService.insertRole(salesSupervisorRole);
+                    }else {
+                        roleService.insertDefaultRole(salesSupervisorRole);
+                    }
+                }else {
+                    roleService.insertDefaultRole(salesSupervisorRole);
+                }
+            } catch (Exception e) {
+                logger.error("获取菜单配置失败-销售主管", e);
+            }
+
+            // 3. 创建数据运营角色(全部数据权限)
+            CompanyRole dataOperationRole = new CompanyRole();
+            dataOperationRole.setCompanyId(company.getCompanyId());
+            dataOperationRole.setRoleName("数据运营");
+            dataOperationRole.setRoleKey(company.getCompanyId()+"_data_operation");
+            dataOperationRole.setRoleSort(3);
+            dataOperationRole.setDataScope("1"); // 全部数据权限
+            dataOperationRole.setStatus("0");
+            try {
+                String json = configService.selectConfigByKey("companymenu.config");
+                if (StringUtils.isNotEmpty(json) && !json.equals("")) {
+                    CompanyMenuConfig config = JSONUtil.toBean(json, CompanyMenuConfig.class);
+                    dataOperationRole.setMenuIds(config.getMenuIds());
+                    if(config.getMenuIds().length > 0){
+                        roleService.insertRole(dataOperationRole);
+                    }else {
+                        roleService.insertDefaultRole(dataOperationRole);
+                    }
+                }else {
+                    roleService.insertDefaultRole(dataOperationRole);
+                }
+            } catch (Exception e) {
+                logger.error("获取菜单配置失败-数据运营", e);
+            }
+
+            // 4. 创建销售角色(个人数据权限)
             CompanyRole salesRole = new CompanyRole();
             salesRole.setCompanyId(company.getCompanyId());
-            salesRole.setRoleName(company.getCompanyName() + "_销售");
+            salesRole.setRoleName("销售");
             salesRole.setRoleKey(company.getCompanyId()+"_sales");
-            salesRole.setRoleSort(1);
-            salesRole.setDataScope("5");
+            salesRole.setRoleSort(4);
+            salesRole.setDataScope("5"); // 个人数据权限
             salesRole.setStatus("0");
-            //增加销售角色菜单权限
             try {
                 String json = configService.selectConfigByKey("companymenu.config");
                 if (StringUtils.isNotEmpty(json) && !json.equals("")) {
@@ -303,9 +377,8 @@ public class CompanyServiceImpl implements ICompanyService
                 }else {
                     roleService.insertDefaultRole(salesRole);
                 }
-
             } catch (Exception e) {
-                logger.error("获取菜单配置失败", e);
+                logger.error("获取菜单配置失败-销售", e);
             }
 
             //添加用户

+ 5 - 0
fs-service/src/main/java/com/fs/hisStore/param/FsStoreScanPaymentStatParam.java

@@ -34,6 +34,11 @@ public class FsStoreScanPaymentStatParam extends BaseQueryParam implements Seria
      * **/
     private String endDate;
 
+    /**
+     * 小程序appId
+     * **/
+    private String appId;
+
     /**
      * 将数字类型的 statType 转换为 StatTypeEnum 枚举
      * @return 对应的枚举实例

+ 11 - 1
fs-service/src/main/java/com/fs/hisStore/vo/FsStoreScanPaymentStatDetailsVo.java

@@ -30,10 +30,20 @@ public class FsStoreScanPaymentStatDetailsVo implements Serializable {
     private BigDecimal reductionAmount;
 
     /**
-     * 退款审核状态 0-待审核 1-退款审核中 2-已审核
+     * 退款审核状态 0-待审核 1-审核通过 2-已完成退款
      * **/
     private Integer refundAuditStatus;
 
+    /**
+     * 支付状态 0-未支付 1-已支付 -1-已退款
+     * **/
+    private Integer status;
+
+    /**
+     * 退款金额
+     * **/
+    private BigDecimal refundMoney;
+
     /**
      * 备注
      * **/

+ 62 - 18
fs-service/src/main/resources/mapper/hisStore/FsStorePaymentScrmMapper.xml

@@ -62,6 +62,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="bankSerialNo != null "> and bank_serial_no = #{bankSerialNo}</if>
             <if test="orderId != null "> and order_id = #{orderId}</if>
             <if test="businessCode != null and businessCode != ''"> and business_code = #{businessCode}</if>
+            <if test="appId != null and appId != ''"> and app_id = #{appId}</if>
+            <if test="refundAuditStatus != null "> and refund_audit_status = #{refundAuditStatus}</if>
+            <if test="params.beginTime != null and params.beginTime != ''"> and pay_time &gt;= #{params.beginTime}</if>
+            <if test="params.endTime != null and params.endTime != ''"> and pay_time &lt;= #{params.endTime}</if>
         </where>
     </select>
 
@@ -231,10 +235,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             COALESCE((
                 SELECT SUM(refund_money)
                 FROM fs_store_payment_scrm refund
-                WHERE refund.business_type = 3
-                  AND refund.`status` = 1
-                  AND refund.refund_audit_status = 2
+                WHERE refund.business_type = 1
+                  AND refund.`status` = -1
                   AND refund.company_id = #{dayParam.companyId}
+                  <if test="dayParam.appId != null and dayParam.appId != ''">
+                  AND refund.app_id = #{dayParam.appId}
+                  </if>
                   AND refund.pay_time >= DATE_FORMAT(NOW(), '%Y-%m-%d 00:00:00')
                   AND refund.pay_time &lt; DATE_FORMAT(
                       DATE_ADD(NOW(), INTERVAL 1 DAY),
@@ -245,8 +251,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             fs_store_payment_scrm
         WHERE
             business_type = 1
-          AND `status` = 1
+          AND `status` IN (1, -1)
           AND company_id = #{dayParam.companyId}
+        <if test="dayParam.appId != null and dayParam.appId != ''">
+          AND app_id = #{dayParam.appId}
+        </if>
         AND pay_time >= DATE_FORMAT(NOW(), '%Y-%m-%d 00:00:00')
         AND pay_time &lt; DATE_FORMAT(
         DATE_ADD(NOW(), INTERVAL 1 DAY),
@@ -264,13 +273,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         original_amount,
         reduction_amount,
         refund_audit_status,
+        status,
+        refund_money,
         remark
         FROM
         fs_store_payment_scrm
         WHERE
         business_type = 1
-        AND `status` = 1
+        AND `status` IN (1, -1)
           AND company_id = #{dayParam.companyId}
+        <if test="dayParam.appId != null and dayParam.appId != ''">
+          AND app_id = #{dayParam.appId}
+        </if>
         AND pay_time >= DATE_FORMAT(NOW(), '%Y-%m-%d 00:00:00')
         AND pay_time &lt; DATE_FORMAT(
         DATE_ADD(NOW(), INTERVAL 1 DAY),
@@ -287,10 +301,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             COALESCE((
                 SELECT SUM(refund_money)
                 FROM fs_store_payment_scrm refund
-                WHERE refund.business_type = 3
-                  AND refund.`status` = 1
-                  AND refund.refund_audit_status = 2
+                WHERE refund.business_type = 1
+                  AND refund.`status` = -1
                   AND refund.company_id = #{monthParam.companyId}
+                  <if test="monthParam.appId != null and monthParam.appId != ''">
+                  AND refund.app_id = #{monthParam.appId}
+                  </if>
                   AND refund.pay_time >= DATE_FORMAT(NOW(), '%Y-%m-01 00:00:00')
                   AND refund.pay_time &lt; DATE_FORMAT(DATE_ADD(NOW(), INTERVAL 1 MONTH), '%Y-%m-01 00:00:00')
             ), 0) AS totalReturn
@@ -298,8 +314,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             fs_store_payment_scrm
         WHERE
             business_type = 1
-        AND `status` = 1
+        AND `status` IN (1, -1)
         AND company_id = #{monthParam.companyId}
+        <if test="monthParam.appId != null and monthParam.appId != ''">
+        AND app_id = #{monthParam.appId}
+        </if>
         AND pay_time >= DATE_FORMAT(NOW(), '%Y-%m-01 00:00:00')
         AND pay_time &lt; DATE_FORMAT(DATE_ADD(NOW(), INTERVAL 1 MONTH), '%Y-%m-01 00:00:00')
         GROUP BY
@@ -314,13 +333,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             original_amount,
             reduction_amount,
             refund_audit_status,
+            status,
+            refund_money,
             remark
         FROM
         fs_store_payment_scrm
         WHERE
         business_type = 1
-        AND `status` = 1
+        AND `status` IN (1, -1)
         AND company_id = #{monthParam.companyId}
+        <if test="monthParam.appId != null and monthParam.appId != ''">
+        AND app_id = #{monthParam.appId}
+        </if>
         AND pay_time >= DATE_FORMAT(NOW(), '%Y-%m-01 00:00:00')
         AND pay_time &lt; DATE_FORMAT(DATE_ADD(NOW(), INTERVAL 1 MONTH), '%Y-%m-01 00:00:00')
         ORDER BY
@@ -335,10 +359,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         COALESCE((
             SELECT SUM(refund_money)
             FROM fs_store_payment_scrm refund
-            WHERE refund.business_type = 3
-              AND refund.`status` = 1
-              AND refund.refund_audit_status = 2
+            WHERE refund.business_type = 1
+              AND refund.`status` = -1
               AND refund.company_id = #{yearParam.companyId}
+              <if test="yearParam.appId != null and yearParam.appId != ''">
+              AND refund.app_id = #{yearParam.appId}
+              </if>
               AND refund.pay_time >= DATE_FORMAT(NOW(), '%Y-01-01 00:00:00')
               AND refund.pay_time &lt; DATE_FORMAT(DATE_ADD(NOW(), INTERVAL 1 YEAR), '%Y-01-01 00:00:00')
         ), 0) AS totalReturn
@@ -346,8 +372,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         fs_store_payment_scrm
         WHERE
         business_type = 1
-        AND `status` = 1
+        AND `status` IN (1, -1)
         AND company_id = #{yearParam.companyId}
+        <if test="yearParam.appId != null and yearParam.appId != ''">
+        AND app_id = #{yearParam.appId}
+        </if>
         AND pay_time >= DATE_FORMAT(NOW(), '%Y-01-01 00:00:00')
         AND pay_time &lt; DATE_FORMAT(DATE_ADD(NOW(), INTERVAL 1 YEAR), '%Y-01-01 00:00:00')
         GROUP BY
@@ -362,13 +391,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             original_amount,
             reduction_amount,
             refund_audit_status,
+            status,
+            refund_money,
             remark
         FROM
             fs_store_payment_scrm
         WHERE
             business_type = 1
-          AND `status` = 1
+          AND `status` IN (1, -1)
           AND company_id = #{yearParam.companyId}
+          <if test="yearParam.appId != null and yearParam.appId != ''">
+          AND app_id = #{yearParam.appId}
+          </if>
           AND pay_time >= DATE_FORMAT(NOW(), '%Y-01-01 00:00:00')
           AND pay_time &lt; DATE_FORMAT(DATE_ADD(NOW(), INTERVAL 1 YEAR), '%Y-01-01 00:00:00')
         ORDER BY
@@ -385,8 +419,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 FROM fs_store_payment_scrm refund
                 WHERE refund.business_type = 1
                   AND refund.`status` = -1
-                  AND refund.refund_audit_status = 2
                   AND refund.company_id = #{dateParam.companyId}
+                  <if test="dateParam.appId != null and dateParam.appId != ''">
+                  AND refund.app_id = #{dateParam.appId}
+                  </if>
                   AND refund.pay_time >= CONCAT(#{dateParam.startDate}, ' 00:00:00')
                   AND refund.pay_time &lt;= CONCAT(#{dateParam.endDate}, ' 23:59:59')
             ), 0) AS totalReturn
@@ -394,8 +430,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             fs_store_payment_scrm
         WHERE
             business_type = 1
-          AND `status` = 1
+          AND `status` IN (1, -1)
           AND company_id = #{dateParam.companyId}
+          <if test="dateParam.appId != null and dateParam.appId != ''">
+          AND app_id = #{dateParam.appId}
+          </if>
           AND pay_time >= CONCAT(#{dateParam.startDate}, ' 00:00:00')
           AND pay_time &lt;= CONCAT(#{dateParam.endDate}, ' 23:59:59')
     </select>
@@ -408,13 +447,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             original_amount,
             reduction_amount,
             refund_audit_status,
+            status,
+            refund_money,
             remark
         FROM
             fs_store_payment_scrm
         WHERE
             business_type = 1
-          AND `status` = 1
+          AND `status` IN (1, -1)
           AND company_id = #{dateParam.companyId}
+          <if test="dateParam.appId != null and dateParam.appId != ''">
+          AND app_id = #{dateParam.appId}
+          </if>
           AND pay_time >= CONCAT(#{dateParam.startDate}, ' 00:00:00')
           AND pay_time &lt;= CONCAT(#{dateParam.endDate}, ' 23:59:59')
         ORDER BY

+ 416 - 108
fs-user-app/src/main/java/com/fs/app/controller/store/FsStoreScanPaymentStatController.java

@@ -8,6 +8,8 @@ import com.fs.common.enums.BusinessType;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.exception.CustomException;
 import com.fs.company.service.ICompanyService;
+import com.fs.his.domain.FsHfpayConfig;
+import com.fs.his.mapper.FsHfpayConfigMapper;
 import com.fs.hisStore.config.StoreConfig;
 import com.fs.hisStore.domain.FsStorePaymentScrm;
 import com.fs.hisStore.param.FsStoreScanPaymentStatParam;
@@ -37,6 +39,8 @@ import java.math.BigDecimal;
 import java.text.SimpleDateFormat;
 import java.util.*;
 
+
+
 @Slf4j
 @Api("扫码流水统计接口")
 @RestController
@@ -55,6 +59,8 @@ public class FsStoreScanPaymentStatController extends AppBaseController {
     private ICompanyService companyService;
     @Autowired
     private ICompanyUserService companyUserService;
+    @Autowired
+    private FsHfpayConfigMapper fsHfpayConfigMapper;
 
     /**
      * 扫码支付统计接口
@@ -62,9 +68,9 @@ public class FsStoreScanPaymentStatController extends AppBaseController {
     @ApiOperation("销售收款码金额统计")
     @PostMapping("/getScanPayStat")
     public R getScanPayStat(@Valid @RequestBody FsStoreScanPaymentStatParam param) {
-        //统计
+        // 统计
         FsStoreScanPaymentStatVo scanPaymentStat = paymentService.getScanPaymentStat(param);
-        return R.ok().put("data",scanPaymentStat);
+        return R.ok().put("data", scanPaymentStat);
     }
 
     /**
@@ -94,14 +100,14 @@ public class FsStoreScanPaymentStatController extends AppBaseController {
     /**
      * 扫码支付分页查询接口
      */
-    @ApiOperation("销售收款码金额统计")
+    @ApiOperation("销售收款码分页查询")
     @PostMapping("/getScanPayStatPage")
     public R getScanPayStatPage(@Valid @RequestBody FsStoreScanPaymentStatParam param) {
-        //分页
+        // 分页
         PageHelper.startPage(param.getPageNum(), param.getPageSize());
-        List<FsStoreScanPaymentStatDetailsVo> statDetailsVos=paymentService.getScanPaymentStatPage(param);
-        PageInfo<FsStoreScanPaymentStatDetailsVo> listPageInfo=new PageInfo<>(statDetailsVos);
-        return R.ok().put("data",listPageInfo);
+        List<FsStoreScanPaymentStatDetailsVo> statDetailsVos = paymentService.getScanPaymentStatPage(param);
+        PageInfo<FsStoreScanPaymentStatDetailsVo> listPageInfo = new PageInfo<>(statDetailsVos);
+        return R.ok().put("data", listPageInfo);
     }
 
     /**
@@ -121,83 +127,348 @@ public class FsStoreScanPaymentStatController extends AppBaseController {
     }
 
     /**
-     * 扫码支付退款接口(统一入口)
-     * 根据当前用户权限决定调用直接退款还是退款审核
+     * 普通员工申请退款接口
      */
-    @ApiOperation("扫码支付退款统一接口")
-    @PostMapping("/refund")
-    @Transactional
-    public R refundPayment(@RequestParam("token") String token, 
-                           @RequestBody FsStorePaymentScrm fsStorePayment) {
+    @ApiOperation("申请退款接口(普通员工)")
+    @PostMapping("/applyRefund")
+    public R applyRefund(@RequestBody FsStorePaymentScrm fsStorePayment) {
         try {
-            // 从 Redis 中获取用户ID
-            Long companyUserId = redisCache.getCacheObject("company-user-token:" + token);
-            if (companyUserId == null) {
-                return R.error("用户未登录或登录已过期,请重新登录");
+            // 验证支付记录
+            FsStorePaymentScrm payment = fsStorePaymentService.selectFsStorePaymentById(fsStorePayment.getPaymentId());
+            if (payment == null) {
+                return R.error("支付记录不存在");
             }
             
-            // 查询用户信息
-            CompanyUser companyUser = companyUserService.selectCompanyUserById(companyUserId);
-            if (companyUser == null) {
-                return R.error("用户不存在");
+            if (payment.getStatus() != 1) {
+                return R.error("只有已支付的订单才能申请退款");
             }
             
-            if (!companyUser.getStatus().equals("0")) {
-                return R.error("用户已禁用");
+            // 检查是否已经有退款申请
+            if (payment.getRefundAuditStatus() != null && payment.getRefundAuditStatus() == 0) {
+                return R.error("该订单已有待审核的退款申请");
             }
             
-            // 根据用户权限调用不同的方法
-            if (companyUser.isAdmin()) {
-                // 管理员直接退款
-                log.info("管理员[{}]执行直接退款, 支付ID: {}", companyUser.getUserName(), fsStorePayment.getPaymentId());
-                return refundStorePayment(fsStorePayment);
-            } else {
-                // 非管理员提交退款审核
-                log.info("非管理员[{}]提交退款审核, 支付ID: {}", companyUser.getUserName(), fsStorePayment.getPaymentId());
+            if (payment.getStatus() == -1) {
+                return R.error("该订单已退款");
+            }
+            
+            // 验证退款金额
+            if (fsStorePayment.getRefundMoney() == null || fsStorePayment.getRefundMoney().compareTo(new BigDecimal(0)) < 1) {
+                return R.error("退款金额必须大于0");
+            }
+            
+            if (payment.getPayMoney().compareTo(fsStorePayment.getRefundMoney()) == -1) {
+                return R.error("退款金额必须小于等于付款金额");
+            }
+            
+            // 更新退款申请状态
+            payment.setRefundMoney(fsStorePayment.getRefundMoney());
+            payment.setRefundAuditStatus(0); // 0-待审核
+            payment.setRefundAuditBy(fsStorePayment.getRefundAuditBy());
+            payment.setRefundAuditTime(new Date());
+            payment.setRefundAuditRemark(fsStorePayment.getRefundAuditRemark() != null ? fsStorePayment.getRefundAuditRemark() : "申请退款");
+            
+            fsStorePaymentService.updateFsStorePayment(payment);
+            
+            log.info("提交退款申请成功, 支付ID: {}, 退款金额: {}", 
+                    payment.getPaymentId(), fsStorePayment.getRefundMoney());
+            
+            return R.ok("退款申请已提交,等待管理员审核");
+        } catch (Exception e) {
+            log.error("申请退款失败, 支付ID: {}", fsStorePayment.getPaymentId(), e);
+            return R.error("申请退款失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * @param auditStatus 查询类型:0-查询所有, 1-只查询待审核, 2-只查询已退款, 3-只查询已拒绝
+     */
+    @ApiOperation("查询退款申请列表(管理员)")
+    @GetMapping("/getPendingRefundList")
+    public R getPendingRefundList(@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
+                                    @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
+                                    @RequestParam(value = "companyId") Long companyId,
+                                    @RequestParam(value = "appId", required = false) String appId,
+                                    @RequestParam(value = "auditStatus", defaultValue = "1") Integer auditStatus,
+                                    @RequestParam(value = "startDate", required = false) String startDate,
+                                    @RequestParam(value = "endDate", required = false) String endDate) {
+        try {
+            log.info("查询退款申请列表 - companyId={}, appId={}, auditStatus={}, startDate={}, endDate={}", 
+                    companyId, appId, auditStatus, startDate, endDate);
+
+            // 设置时间范围查询参数
+            Map<String, Object> params = new HashMap<>();
+            if (startDate != null && !startDate.isEmpty()) {
+                params.put("beginTime", startDate + " 00:00:00");
+            }
+            if (endDate != null && !endDate.isEmpty()) {
+                params.put("endTime", endDate + " 23:59:59");
+            }
+            
+            // 构建查询条件
+            FsStorePaymentScrm query = new FsStorePaymentScrm();
+            query.setBusinessType(1); // 业务类型:收款
+            query.setCompanyId(companyId);
+            query.setAppId(appId);
+            query.setParams(params);
+            
+            // 根据 auditStatus 设置不同的查询条件
+            // refundAuditStatus: 0-待审核, 1-审核通过已退款, 2-审核拒绝
+            if (auditStatus == 1) {
+                // 只查询待审核的:status=1(已支付) AND refundAuditStatus=0(待审核)
+                query.setStatus(1);
+                query.setRefundAuditStatus(0);
+                
+            } else if (auditStatus == 2) {
+                // 只查询已退款的:status=-1(已退款) AND refundAuditStatus=1(审核通过已退款)
+                query.setStatus(-1);
+                query.setRefundAuditStatus(1);
+                
+            } else if (auditStatus == 3) {
+                // 只查询已拒绝的:status=1(已支付) AND refundAuditStatus=2(审核拒绝)
+                query.setStatus(1);
+                query.setRefundAuditStatus(2);
+                
+            } else if (auditStatus == 0) {
+                // 查询所有需要手动分页,因为需要多次查询合并
+                List<FsStorePaymentScrm> resultList = new ArrayList<>();
+                
+                // 1. 待审核:status=1 AND refundAuditStatus=0
+                query.setStatus(1);
+                query.setRefundAuditStatus(0);
+                resultList.addAll(fsStorePaymentService.selectFsStorePaymentList(query));
                 
-                Map<String, Object> auditData = new HashMap<>();
-                auditData.put("paymentId", fsStorePayment.getPaymentId());
-                auditData.put("refundAuditStatus", 1); // 1表示待审核
-                auditData.put("auditRemark", "用户提交退款申请");
-                auditData.put("auditor", companyUser.getUserName());
+                // 2. 已退款:status=-1 AND refundAuditStatus=1
+                query.setStatus(-1);
+                query.setRefundAuditStatus(1);
+                resultList.addAll(fsStorePaymentService.selectFsStorePaymentList(query));
                 
-                return auditRefund(auditData);
+                // 3. 已拒绝:status=1 AND refundAuditStatus=2
+                query.setStatus(1);
+                query.setRefundAuditStatus(2);
+                resultList.addAll(fsStorePaymentService.selectFsStorePaymentList(query));
+                
+                // 手动分页处理
+                int total = resultList.size();
+                int startIndex = (pageNum - 1) * pageSize;
+                int endIndex = Math.min(startIndex + pageSize, total);
+                
+                List<FsStorePaymentScrm> pageList = new ArrayList<>();
+                if (startIndex < total) {
+                    pageList = resultList.subList(startIndex, endIndex);
+                }
+                
+                // 构建分页结果
+                PageInfo<FsStorePaymentScrm> pageInfo = new PageInfo<>(pageList);
+                pageInfo.setTotal(total);
+                pageInfo.setPageNum(pageNum);
+                pageInfo.setPageSize(pageSize);
+                pageInfo.setSize(pageList.size());
+                pageInfo.setStartRow((long) startIndex);
+                pageInfo.setEndRow((long) (endIndex - 1));
+                int totalPages = (total + pageSize - 1) / pageSize;
+                pageInfo.setPages(totalPages);
+                pageInfo.setPrePage(pageNum > 1 ? pageNum - 1 : 0);
+                pageInfo.setNextPage(pageNum < totalPages ? pageNum + 1 : 0);
+                pageInfo.setIsFirstPage(pageNum == 1);
+                pageInfo.setIsLastPage(pageNum >= totalPages);
+                pageInfo.setHasPreviousPage(pageNum > 1);
+                pageInfo.setHasNextPage(pageNum < totalPages);
+                
+                return R.ok().put("data", pageInfo);
+                
+            } else {
+                return R.error("查询类型错误,0-所有, 1-待审核, 2-已退款, 3-已拒绝");
             }
+            
+            // 单一条件查询使用 PageHelper 分页
+            PageHelper.startPage(pageNum, pageSize);
+            List<FsStorePaymentScrm> resultList = fsStorePaymentService.selectFsStorePaymentList(query);
+            PageInfo<FsStorePaymentScrm> pageInfo = new PageInfo<>(resultList);
+            
+            return R.ok().put("data", pageInfo);
         } catch (Exception e) {
-            log.error("退款处理异常, token: {}, paymentId: {}", token, fsStorePayment.getPaymentId(), e);
-            return R.error("退款处理失败:" + e.getMessage());
+            log.error("查询退款申请列表失败", e);
+            return R.error("查询失败:" + e.getMessage());
         }
     }
 
     /**
-     * 扫码支付退款接口(管理员专用)
+     * 查询退款审核统计数据
+     * @param auditStatus 查询类型:0-查询所有, 1-只查询待审核, 2-只查询已退款, 3-只查询已拒绝
      */
+    @ApiOperation("查询退款审核统计数据")
+    @GetMapping("/getRefundAuditCount")
+    public R getRefundAuditCount(@RequestParam(value = "companyId") Long companyId,
+                                   @RequestParam(value = "appId", required = false) String appId,
+                                   @RequestParam(value = "auditStatus", defaultValue = "0") Integer auditStatus) {
+        try {
+            log.info("查询退款审核统计 - companyId={}, appId={}, auditStatus={}", 
+                    companyId, appId, auditStatus);
+            
+            int count = 0;
+            String statusDesc = "";
+            Map<String, Object> params = new HashMap<>();
+            
+            // 构建查询条件
+            FsStorePaymentScrm query = new FsStorePaymentScrm();
+            query.setBusinessType(1); // 业务类型:收款
+            query.setCompanyId(companyId);
+            query.setAppId(appId);
+            query.setParams(params);
+            
+            // refundAuditStatus: 0-待审核, 1-审核通过已退款, 2-审核拒绝
+            if (auditStatus == 1) {
+                // 只查询待审核的:status=1 AND refundAuditStatus=0
+                statusDesc = "待审核";
+                query.setStatus(1);
+                query.setRefundAuditStatus(0);
+                count = fsStorePaymentService.selectFsStorePaymentList(query).size();
+                
+            } else if (auditStatus == 2) {
+                // 只查询已退款的:status=-1 AND refundAuditStatus=1
+                statusDesc = "已退款";
+                query.setStatus(-1);
+                query.setRefundAuditStatus(1);
+                count = fsStorePaymentService.selectFsStorePaymentList(query).size();
+                
+            } else if (auditStatus == 3) {
+                // 只查询已拒绝的:status=1 AND refundAuditStatus=2
+                statusDesc = "已拒绝";
+                query.setStatus(1);
+                query.setRefundAuditStatus(2);
+                count = fsStorePaymentService.selectFsStorePaymentList(query).size();
+                
+            } else if (auditStatus == 0) {
+                // 查询所有:待审核 + 已退款 + 已拒绝
+                statusDesc = "所有状态";
+                
+                // 1. 待审核:status=1 AND refundAuditStatus=0
+                query.setStatus(1);
+                query.setRefundAuditStatus(0);
+                count = fsStorePaymentService.selectFsStorePaymentList(query).size();
+                
+                // 2. 已退款:status=-1 AND refundAuditStatus=1
+                query.setStatus(-1);
+                query.setRefundAuditStatus(1);
+                count += fsStorePaymentService.selectFsStorePaymentList(query).size();
+                
+                // 3. 已拒绝:status=1 AND refundAuditStatus=2
+                query.setStatus(1);
+                query.setRefundAuditStatus(2);
+                count += fsStorePaymentService.selectFsStorePaymentList(query).size();
+                
+            } else {
+                return R.error("查询类型错误,0-所有, 1-待审核, 2-已退款, 3-已拒绝");
+            }
+            
+            // 构建返回结果
+            Map<String, Object> result = new HashMap<>();
+            result.put("count", count);
+            result.put("auditStatus", auditStatus);
+            result.put("statusDesc", statusDesc);
+            
+            log.info("退款审核统计结果 - 查询类型: {}({}), 记录数: {}", 
+                    auditStatus, statusDesc, count);
+            
+            return R.ok().put("data", result);
+        } catch (Exception e) {
+            log.error("查询退款审核统计失败", e);
+            return R.error("查询失败:" + e.getMessage());
+        }
+    }
 
+    /**
+     * 扫码支付退款接口(管理员专用,单个退款)
+     */
     @ApiOperation("扫码支付退款接口(管理员专用)")
     @PostMapping("refundStorePayment")
     @Transactional
     public R refundStorePayment(@RequestBody FsStorePaymentScrm fsStorePayment)
+    {
+        try {
+            if (fsStorePayment == null || fsStorePayment.getPaymentId() == null) {
+                return R.error("支付记录ID不能为空");
+            }
+            
+            return singleRefundInternal(fsStorePayment);
+        } catch (Exception e) {
+            log.error("退款失败,支付ID: {}", fsStorePayment.getPaymentId(), e);
+            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+            return R.error("退款失败:" + e.getMessage());
+        }
+    }
+
+    
+    /**
+     * 将 Map 转换为 FsStorePaymentScrm 对象
+     */
+    private FsStorePaymentScrm convertToPayment(Map<String, Object> map) {
+        FsStorePaymentScrm payment = new FsStorePaymentScrm();
+        if (map.get("paymentId") != null) {
+            payment.setPaymentId(Long.valueOf(map.get("paymentId").toString()));
+        }
+        if (map.get("refundMoney") != null) {
+            payment.setRefundMoney(new BigDecimal(map.get("refundMoney").toString()));
+        }
+        return payment;
+    }
+    
+    /**
+     * 单个退款内部方法
+     */
+    private R singleRefundInternal(FsStorePaymentScrm fsStorePayment)
     {
         FsStorePaymentScrm payment=fsStorePaymentService.selectFsStorePaymentById(fsStorePayment.getPaymentId());
         if(payment.getStatus()!=1){
-            return R.error("非法操作");
+            return R.error("只有已支付的订单才能申请退款");
+        }
+        if(fsStorePayment.getRefundMoney().compareTo(new BigDecimal(0))<1){
+            return R.error("退款金额必须大于0");
+        }
+        if(payment.getPayMoney().compareTo(fsStorePayment.getRefundMoney())==-1){
+            return R.error("退款金额必须小于等于付款金额");
         }
         if(payment.getPayTypeCode().equals("weixin")){
+
             if (payment.getPayMode()!=null&&payment.getPayMode().equals("hf")){
+                // 根据 appId 获取对应的汇付商户号
+                String huifuId = "";
+                log.info("开始退款,支付记录ID:{},appId:{}", payment.getPaymentId(), payment.getAppId());
+
+                if (payment.getAppId() != null && !payment.getAppId().isEmpty()) {
+                    FsHfpayConfig fsHfpayConfig = fsHfpayConfigMapper.selectByAppId(payment.getAppId());
+                    log.info("根据appId查询汇付配置,appId:{},查询结果:{}", payment.getAppId(), fsHfpayConfig);
+                    if (fsHfpayConfig != null) {
+                        huifuId = fsHfpayConfig.getHuifuId();
+                        log.info("获取到huifuId:{}", huifuId);
+                    } else {
+                        log.error("未找到appId对应的汇付配置,appId:{}", payment.getAppId());
+                    }
+                } else {
+                    log.error("支付记录的appId为空,支付记录ID:{}", payment.getPaymentId());
+                }
+
+                // 如果没有获取到 huifuId,返回错误
+                if (huifuId == null || huifuId.isEmpty()) {
+                    return R.error("未找到对应的汇付配置信息,请检查支付记录的appId:" + payment.getAppId());
+                }
+
                 V2TradePaymentScanpayRefundRequest request = new V2TradePaymentScanpayRefundRequest();
+                request.setHuifuId(huifuId);
                 request.setOrdAmt(payment.getPayMoney().toString());
                 request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
                 request.setReqSeqId("refund-"+payment.getPayCode());
                 Map<String, Object> extendInfoMap = new HashMap<>();
-                extendInfoMap.put("org_party_order_id", payment.getBankSerialNo());
+                extendInfoMap.put("org_req_seq_id", "payment-"+payment.getPayCode());
                 request.setExtendInfo(extendInfoMap);
                 HuiFuRefundResult refund = huiFuService.refund(request);
                 log.info("退款:"+refund);
                 if((refund.getResp_code().equals("00000000")||refund.getResp_code().equals("00000100"))&&(refund.getTrans_stat().equals("S")||refund.getTrans_stat().equals("P"))){
                     payment.setRefundMoney(fsStorePayment.getRefundMoney());
                     payment.setStatus(-1);
-                    payment.setRefundAuditTime(new Date());
-                    payment.setRefundAuditStatus(2);
+                    payment.setRefundTime(new Date());
+                    payment.setRefundAuditStatus(1); // 1-审核通过已退款
                     fsStorePaymentService.updateFsStorePayment(payment);
                     //收款 减去所有
                     if(payment.getCompanyId()!=null&&payment.getCompanyId()>0){
@@ -224,60 +495,9 @@ public class FsStoreScanPaymentStatController extends AppBaseController {
 //            return payService.refundOrder(refundDTO);
         }
         else if(payment.getPayTypeCode().equals("alipay")){
-            try {
-                // 注意:这里的AliPayApiConfig、AliPayApiConfigKit、AliPayApi等类需要确保项目中有相应的依赖
-                // 如果没有,建议注释掉支付宝退款部分或添加相应依赖
-                /*
-                AliPayApiConfig aliPayApiConfig;
-                try {
-                    aliPayApiConfig = AliPayApiConfigKit.getApiConfig(aliPayBean.getAppId());
-                } catch (Exception e) {
-                    aliPayApiConfig = AliPayApiConfig.builder()
-                            .setAppId(aliPayBean.getAppId())
-                            .setAliPayPublicKey(aliPayBean.getPublicKey())
-                            .setAppCertPath(aliPayBean.getAppCertPath())
-                            .setAliPayCertPath(aliPayBean.getAliPayCertPath())
-                            .setAliPayRootCertPath(aliPayBean.getAliPayRootCertPath())
-                            .setCharset("UTF-8")
-                            .setPrivateKey(aliPayBean.getPrivateKey())
-                            .setServiceUrl(aliPayBean.getServerUrl())
-                            .setSignType("RSA2")
-                            // 普通公钥方式
-                            .build();
-                    // 证书模式
-//					.buildByCert();
-                }
-                AliPayApiConfigKit.putApiConfig(aliPayApiConfig);
-                AlipayTradeRefundModel model = new AlipayTradeRefundModel();
-                model.setOutTradeNo("payment-"+payment.getPayCode());
-                model.setTradeNo(payment.getTradeNo());
-                model.setRefundAmount(fsStorePayment.getRefundMoney().toString());
-                model.setRefundReason("退款");
-                String result= AliPayApi.tradeRefundToResponse(model).getBody();
-                cn.hutool.json.JSONObject json= JSONUtil.parseObj(result);
-                cn.hutool.json.JSONObject jsonInfo= (cn.hutool.json.JSONObject) json.get("alipay_trade_refund_response");
-                String code=(String)jsonInfo.get("code");
-                String msg=(String)jsonInfo.get("sub_msg");
-                //{"{"alipay_trade_refund_response":{"code":"10000","msg":"Success","buyer_logon_id":"270***@qq.com","buyer_user_id":"2088402776950529","fund_change":"Y","gmt_refund_pay":"2022-04-27 18:27:28","out_trade_no":"goods-202204271826530001","refund_fee":"0.10","send_back_fee":"0.00","trade_no":"2022042722001450521456255417"},"sign":"hU+dy17/juMYQEQpO7Yy7jxkx9h5ebSbN3xdZr58msfOfJEUknqra6w4L37pgoZSx1Vj00jK3Ds06vrph6mSEliq3PQ37PwbZiRK3ZHaaKlz+9ndjoBTFYAxP60zLASfTq+W+dczDq4KOrvwhprFGt7YwKXGU42PgfOPb5EOgWyYUP6ivP0r06OzTo0f2lB28w6AQ4m4IQjdIL/tWbDaKl+ld8MPMLIgw5k9BmMcP8LV8ENC6+Gl1u5GwgAMjwHfk0RfB/kTFTHUTa7fgaO22w3pT8YKFMnOdKA0cVuJ2LE4SdxepqlprWJdCtLOeoQUX6PkEtoJGywPWAyjgdJ6Pg=="}
-                if(code.equals("10000")){
-                    payment.setRefundMoney(fsStorePayment.getRefundMoney());
-                    fsStorePaymentService.updateFsStorePayment(payment);
-                    fsStorePaymentService.refund(payment.getPayCode());
-                    return R.ok("退款成功");
-
-                }
-                else{
-                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
-                    return R.error("退款失败:"+msg);
-
-                }
-                */
-                return R.error("支付宝退款功能暂未配置,请联系系统管理员");
-                //退款成功
-            } catch (Exception e) {
-                e.printStackTrace();
-                return R.error("支付宝退款异常:" + e.getMessage());
-            }
+            // 支付宝退款功能暂未配置
+            log.warn("支付宝退款功能暂未配置,支付记录ID:{}", payment.getPaymentId());
+            return R.error("支付宝退款功能暂未配置,请联系系统管理员");
         }
         return R.error("非法操作");
     }
@@ -285,18 +505,106 @@ public class FsStoreScanPaymentStatController extends AppBaseController {
 
 
 
-    @ApiOperation("退款审核接口(非管理员专用)")
+    /**
+     * 管理员审核退款申请接口(单个审核)
+     * @param auditData 包含 paymentId, refundStatus, refundRemark
+     */
+    @ApiOperation("管理员审核退款申请")
     @Log(title = "退款审核", businessType = BusinessType.UPDATE)
-    @PutMapping("/refund/audit")
-    public R auditRefund(@RequestBody Map<String, Object> auditData)
-    {
+    @PostMapping("/auditRefund")
+    @Transactional
+    public R auditRefund(@RequestBody Map<String, Object> auditData) {
+        try {
+            // 参数校验
+            if (auditData == null) {
+                return R.error("请求参数不能为空");
+            }
+            
+            if (auditData.get("paymentId") == null) {
+                return R.error("支付记录ID不能为空");
+            }
+            
+            if (auditData.get("refundStatus") == null) {
+                return R.error("退款状态不能为空");
+            }
+            
+            Integer refundStatus = Integer.valueOf(auditData.get("refundStatus").toString());
+            String refundRemark = auditData.get("refundRemark") != null ? auditData.get("refundRemark").toString() : "";
+            
+            // 审核状态:1-通过,2-拒绝
+            if (refundStatus != 1 && refundStatus != 2) {
+                return R.error("退款状态错误,1-通过,2-拒绝");
+            }
+            
+            return singleAuditRefundInternal(auditData, refundStatus, refundRemark);
+        } catch (NumberFormatException e) {
+            log.error("参数格式错误", e);
+            return R.error("参数格式错误,paymentId和refundStatus必须为数字");
+        } catch (Exception e) {
+            log.error("退款审核失败", e);
+            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+            return R.error("退款审核失败:" + e.getMessage());
+        }
+    }
+    
+    /**
+     * 单个审核内部方法
+     * refundStatus: 1-审核通过, 2-审核拒绝
+     */
+    private R singleAuditRefundInternal(Map<String, Object> auditData, Integer refundStatus, String refundRemark) {
         Long paymentId = Long.valueOf(auditData.get("paymentId").toString());
-        Integer auditStatus = Integer.valueOf(auditData.get("refundAuditStatus").toString());
-        String auditRemark = auditData.get("auditRemark") != null ? auditData.get("auditRemark").toString() : "";
-        String auditor = auditData.get("auditor") != null ? auditData.get("auditor").toString() : "";
-
-        return fsStorePaymentService.auditRefund(paymentId, auditStatus, auditRemark , auditor);
+        
+        // 查询支付记录
+        FsStorePaymentScrm payment = fsStorePaymentService.selectFsStorePaymentById(paymentId);
+        if (payment == null) {
+            return R.error("支付记录不存在");
+        }
+        
+        // 检查退款申请状态
+        if (payment.getRefundAuditStatus() == null || payment.getRefundAuditStatus() != 0) {
+            return R.error("该订单没有待审核的退款申请");
+        }
+        
+        if (payment.getStatus() == null || payment.getStatus() != 1) {
+            return R.error("只有已支付的订单才能退款");
+        }
+        
+        if (refundStatus == 1) {
+            // 审核通过,执行退款
+            log.info("审核通过退款申请, 支付ID: {}, 开始执行退款", paymentId);
+            
+            FsStorePaymentScrm refundRequest = new FsStorePaymentScrm();
+            refundRequest.setPaymentId(payment.getPaymentId());
+            refundRequest.setRefundMoney(payment.getRefundMoney());
+            
+            // 直接调用 singleRefundInternal
+            R refundResult = singleRefundInternal(refundRequest);
+            
+            if (refundResult.get("code").equals(200)) {
+                // singleRefundInternal 已经更新了 status=-1 和 refundAuditStatus=1
+                // 如果需要更新备注,重新查询最新数据
+                if (refundRemark != null && !refundRemark.isEmpty()) {
+                    FsStorePaymentScrm latestPayment = fsStorePaymentService.selectFsStorePaymentById(paymentId);
+                    latestPayment.setRefundAuditRemark(refundRemark);
+                    fsStorePaymentService.updateFsStorePayment(latestPayment);
+                }
+                log.info("退款审核通过并执行成功, 支付ID: {}", paymentId);
+                return R.ok("退款审核通过,退款成功");
+            } else {
+                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
+                return R.error("退款失败:" + refundResult.get("msg"));
+            }
+        } else {
+            // 审核拒绝:refundAuditStatus=2, status保持为1(已支付)
+            payment.setRefundAuditStatus(2);
+            payment.setRefundAuditRemark(refundRemark);
+            payment.setRefundAuditTime(new Date());
+            fsStorePaymentService.updateFsStorePayment(payment);
+            log.info("拒绝退款申请, 支付ID: {}, 原因: {}", paymentId, refundRemark);
+            return R.ok("已拒绝退款申请");
+        }
     }
+    
     /**
      * 验证立减配置是否有效
      * @param config 店铺配置对象