Sfoglia il codice sorgente

fix: 退款流程5项修复 (ISSUE-007~011)

- ISSUE-007: 小程序售后状态筛选失效 — after-sales.vue data:{}→{status} + 后端接收status参数
- ISSUE-008: 中止退款后订单状态未恢复 — cancelPendingRefund增加ProductOrder状态回滚
- ISSUE-009: 订单管理搜索条件增强 — 后端支持keyword+日期范围 + 前端增加日期选择器和搜索按钮
- ISSUE-010: Dashboard缺失退款审核计数 — StatsController增加pendingRefundCount + Dashboard新增卡片
- ISSUE-011: 退款余额不足提示优化 — approveRefund返回明确提示 + 前端使用后端message
Sisyphus 2 mesi fa
parent
commit
b977aa02c3

+ 4 - 1
cfc-backend/src/main/java/com/etotem/cfc/controller/admin/AdminProductOrderController.java

@@ -26,9 +26,12 @@ public class AdminProductOrderController {
     @PostMapping("/list")
     public Result<Map<String, Object>> list(@RequestBody Map<String, Object> params) {
         String status = (String) params.get("status");
+        String keyword = (String) params.get("keyword");
+        String startDate = (String) params.get("startDate");
+        String endDate = (String) params.get("endDate");
         Integer page = params.get("page") != null ? ((Number) params.get("page")).intValue() : 1;
         Integer size = params.get("size") != null ? ((Number) params.get("size")).intValue() : 20;
-        return orderService.adminOrderPage(page, size, status);
+        return orderService.adminOrderPage(page, size, status, keyword, startDate, endDate);
     }
 
     @Operation(summary = "订单详情")

+ 4 - 2
cfc-backend/src/main/java/com/etotem/cfc/controller/shop/AfterSalesController.java

@@ -27,8 +27,10 @@ public class AfterSalesController {
     }
 
     @PostMapping("/list")
-    public Result<List<AfterSalesRequest>> list(@RequestAttribute("userId") Long userId) {
-        return afterSalesService.list(userId);
+    public Result<List<AfterSalesRequest>> list(@RequestBody Map<String, Object> params,
+                                                @RequestAttribute("userId") Long userId) {
+        String status = params != null ? (String) params.get("status") : null;
+        return afterSalesService.list(userId, status);
     }
 
     @PostMapping("/detail")

+ 10 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/stats/StatsController.java

@@ -2,6 +2,7 @@ package com.etotem.cfc.controller.stats;
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.etotem.cfc.common.Result;
+import com.etotem.cfc.entity.AfterSalesRequest;
 import com.etotem.cfc.entity.FamilyMember;
 import com.etotem.cfc.entity.CommissionRecord;
 import com.etotem.cfc.entity.Family;
@@ -9,6 +10,7 @@ import com.etotem.cfc.entity.MemberUpgradeRecord;
 import com.etotem.cfc.entity.Task;
 import com.etotem.cfc.entity.TrialMembership;
 import com.etotem.cfc.entity.User;
+import com.etotem.cfc.mapper.AfterSalesRequestMapper;
 import com.etotem.cfc.mapper.FamilyMemberMapper;
 import com.etotem.cfc.mapper.CommissionRecordMapper;
 import com.etotem.cfc.mapper.FamilyMapper;
@@ -69,6 +71,9 @@ public class StatsController {
     @Resource
     private MemberUpgradeRecordMapper memberUpgradeRecordMapper;
 
+    @Resource
+    private AfterSalesRequestMapper afterSalesRequestMapper;
+
     @Operation(summary = "管理后台仪表盘汇总")
     @PostMapping("/dashboard")
     public Result<Map<String, Object>> getDashboardSummary() {
@@ -92,6 +97,10 @@ public class StatsController {
             return taskMapper.selectList(q);
         }, taskExecutor);
 
+        CompletableFuture<Long> pendingRefundFuture = CompletableFuture.supplyAsync(() ->
+            afterSalesRequestMapper.selectCount(new QueryWrapper<AfterSalesRequest>()
+                .eq("status", "pending")), taskExecutor);
+
         try {
             data.put("totalFamilies", familyFuture.get());
             data.put("totalParents", parentFuture.get());
@@ -99,6 +108,7 @@ public class StatsController {
             data.put("totalTeachers", teacherFuture.get());
             data.put("pendingGuideCount", pendingGuideFuture.get());
             data.put("pendingPackageCount", pendingPackageFuture.get());
+            data.put("pendingRefundCount", pendingRefundFuture.get());
             data.put("recentTasks", recentTasksFuture.get());
         } catch (Exception e) {
             return Result.error("获取仪表盘数据失败");

+ 8 - 6
cfc-backend/src/main/java/com/etotem/cfc/service/AfterSalesService.java

@@ -55,12 +55,14 @@ public class AfterSalesService {
         return Result.success(request);
     }
 
-    public Result<List<AfterSalesRequest>> list(Long userId) {
-        List<AfterSalesRequest> list = afterSalesRequestMapper.selectList(
-            new LambdaQueryWrapper<AfterSalesRequest>()
-                .eq(AfterSalesRequest::getUserId, userId)
-                .orderByDesc(AfterSalesRequest::getCreatedAt)
-        );
+    public Result<List<AfterSalesRequest>> list(Long userId, String status) {
+        LambdaQueryWrapper<AfterSalesRequest> wrapper = new LambdaQueryWrapper<AfterSalesRequest>()
+            .eq(AfterSalesRequest::getUserId, userId)
+            .orderByDesc(AfterSalesRequest::getCreatedAt);
+        if (status != null && !status.isEmpty()) {
+            wrapper.eq(AfterSalesRequest::getStatus, status);
+        }
+        List<AfterSalesRequest> list = afterSalesRequestMapper.selectList(wrapper);
         return Result.success(list);
     }
 

+ 19 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/PendingRefundService.java

@@ -3,7 +3,9 @@ package com.etotem.cfc.service;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.etotem.cfc.entity.PendingRefund;
+import com.etotem.cfc.entity.ProductOrder;
 import com.etotem.cfc.mapper.PendingRefundMapper;
+import com.etotem.cfc.mapper.ProductOrderMapper;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -27,6 +29,9 @@ public class PendingRefundService {
     @Resource
     private CommissionService commissionService;
 
+    @Resource
+    private ProductOrderMapper productOrderMapper;
+
     /**
      * 创建待退款记录
      * @param userId 用户 ID
@@ -133,6 +138,20 @@ public class PendingRefundService {
             refund.setRemark("管理员中止退款");
             refund.setUpdatedAt(new Date());
             pendingRefundMapper.updateById(refund);
+
+            // 恢复对应订单的退款状态
+            if ("product".equals(refund.getOrderType()) && refund.getOrderNo() != null) {
+                LambdaQueryWrapper<ProductOrder> wrapper = new LambdaQueryWrapper<>();
+                wrapper.eq(ProductOrder::getOrderNo, refund.getOrderNo());
+                ProductOrder order = productOrderMapper.selectOne(wrapper);
+                if (order != null) {
+                    order.setRefundStatus(1);
+                    order.setStatus("refunding");
+                    order.setUpdatedAt(new Date());
+                    productOrderMapper.updateById(order);
+                    log.info("已恢复订单退款状态: orderNo={}", refund.getOrderNo());
+                }
+            }
         }
     }
 

+ 22 - 2
cfc-backend/src/main/java/com/etotem/cfc/service/ProductOrderService.java

@@ -23,6 +23,7 @@ import org.springframework.stereotype.Service;
 import javax.annotation.Resource;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
@@ -418,7 +419,7 @@ public class ProductOrderService {
                 order.setUpdatedAt(new Date());
                 orderMapper.updateById(order);
                 log.info("微信退款余额不足,已加入待退款队列: orderNo={}", orderNo);
-                return Result.success("余额不足,退款已加入等待队列,到账后将自动退款");
+                return Result.success("⚠️ 商户余额不足,退款已加入等待队列,到账后将自动退款");
             }
             log.error("微信退款失败: orderNo={}, error={}", orderNo, e.getMessage());
             return Result.error("退款失败: " + e.getMessage());
@@ -556,13 +557,32 @@ public class ProductOrderService {
         return Result.success(list.stream().map(ProductOrderDTO::from).collect(Collectors.toList()));
     }
 
-    public Result<Map<String, Object>> adminOrderPage(int page, int size, String status) {
+    public Result<Map<String, Object>> adminOrderPage(int page, int size, String status, String keyword, String startDate, String endDate) {
         Page<ProductOrder> pageParam = new Page<>(page, size);
         LambdaQueryWrapper<ProductOrder> wrapper = new LambdaQueryWrapper<ProductOrder>()
             .orderByDesc(ProductOrder::getCreatedAt);
         if (status != null && !status.isEmpty()) {
             wrapper.eq(ProductOrder::getStatus, status);
         }
+        if (keyword != null && !keyword.trim().isEmpty()) {
+            String kw = keyword.trim();
+            wrapper.and(w -> w.like(ProductOrder::getProductName, kw).or().like(ProductOrder::getOrderNo, kw));
+        }
+        if (startDate != null && !startDate.isEmpty()) {
+            try {
+                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+                wrapper.ge(ProductOrder::getCreatedAt, sdf.parse(startDate));
+            } catch (Exception ignored) {}
+        }
+        if (endDate != null && !endDate.isEmpty()) {
+            try {
+                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+                Calendar cal = Calendar.getInstance();
+                cal.setTime(sdf.parse(endDate));
+                cal.add(Calendar.DAY_OF_MONTH, 1);
+                wrapper.lt(ProductOrder::getCreatedAt, cal.getTime());
+            } catch (Exception ignored) {}
+        }
         Page<ProductOrder> result = orderMapper.selectPage(pageParam, wrapper);
         List<ProductOrderDTO> records = result.getRecords().stream()
             .map(ProductOrderDTO::from)

+ 1 - 1
cfc-frontend/pages/shop/after-sales/after-sales.vue

@@ -96,7 +96,7 @@ export default {
       uni.request({
         url: config.api('/api/shop/after-sales/list'),
         method: 'POST',
-        data: {},
+        data: { status: this.currentTab || undefined },
         header: {
           'Content-Type': 'application/json',
           'Authorization': 'Bearer ' + uni.getStorageSync('token')

+ 3 - 1
cfc-web/src/views/admin/Dashboard.vue

@@ -96,7 +96,8 @@ export default {
         { label: '孩子总数', value: '-', icon: 'el-icon-s-custom', color: 'cyan' },
         { label: '规划师总数', value: '-', icon: 'el-icon-school', color: 'green' },
         { label: '待审核规划师', value: '-', icon: 'el-icon-time', color: 'red' },
-        { label: '待审核套餐', value: '-', icon: 'el-icon-document', color: 'purple' }
+        { label: '待审核套餐', value: '-', icon: 'el-icon-document', color: 'purple' },
+        { label: '待退款审核', value: '-', icon: 'el-icon-warning', color: 'orange' }
       ],
       revenueCards: [
         { label: '本月佣金', value: '-', icon: 'el-icon-money', color: 'orange', sub: '' },
@@ -140,6 +141,7 @@ export default {
           this.statCards[3].value = d.totalTeachers || 0
           this.statCards[4].value = d.pendingGuideCount || 0
           this.statCards[5].value = d.pendingPackageCount || 0
+          this.statCards[6].value = d.pendingRefundCount || 0
           this.recentTasks = d.recentTasks || []
         }
       } catch (e) {

+ 20 - 4
cfc-web/src/views/admin/OrderManage.vue

@@ -3,8 +3,18 @@
     <div class="header">
       <h2 class="admin-page-title">订单管理</h2>
       <div class="filters filter-bar">
-        <el-input v-model="keyword" placeholder="搜索..." prefix-icon="el-icon-search" clearable @keyup.enter.native="handleSearch" @clear="handleSearch" />
-        <el-select v-model="statusFilter" placeholder="订单状态" @change="loadList" clearable>
+        <el-input v-model="keyword" placeholder="搜索商品/订单号..." prefix-icon="el-icon-search" clearable @keyup.enter.native="handleSearch" @clear="handleSearch" style="width:220px;" />
+        <el-date-picker
+          v-model="dateRange"
+          type="daterange"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          value-format="yyyy-MM-dd"
+          style="width:260px;"
+          @change="handleSearch"
+        />
+        <el-select v-model="statusFilter" placeholder="订单状态" @change="handleSearch" clearable>
           <el-option label="全部状态" value="" />
           <el-option label="待支付" value="pending" />
           <el-option label="已支付(待发货)" value="paid" />
@@ -16,6 +26,7 @@
           <el-option label="已退款" value="refunded" />
           <el-option label="已关闭" value="closed" />
         </el-select>
+        <el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
       </div>
     </div>
 
@@ -161,6 +172,7 @@ export default {
       total: 0,
       statusFilter: '',
       keyword: '',
+      dateRange: null,
       detailDialogVisible: false,
       detail: null,
       shipDialogVisible: false,
@@ -191,6 +203,10 @@ export default {
         if (this.keyword) {
           params.keyword = this.keyword
         }
+        if (this.dateRange && this.dateRange.length === 2) {
+          params.startDate = this.dateRange[0]
+          params.endDate = this.dateRange[1]
+        }
         var res = await getOrderList(params)
         this.list = res.data.records || []
         this.total = res.data.total || 0
@@ -257,8 +273,8 @@ export default {
       this.refundLoading = true
       try {
         await this.$confirm('确认同意退款?', '提示', { type: 'warning' })
-        await approveRefund(this.refundTarget.orderNo)
-        this.$message.success('已同意退款')
+        var res = await approveRefund(this.refundTarget.orderNo)
+        this.$message.success(res.message || '已同意退款')
         this.refundDialogVisible = false
         this.loadList()
       } catch (e) {

+ 229 - 0
docs/系统测试/issue-tracking/ISSUE-007_011-refund-flow.md

@@ -0,0 +1,229 @@
+# 退款流程问题追踪 — 2026-07-12
+
+**发现日期**: 2026-07-12
+**模块**: 商城退款 / 售后管理 / Dashboard
+**发现人**: Sisyphus (代码审查)
+**来源**: 用户反馈 + 代码审查
+
+---
+
+## 问题总览
+
+| # | 问题ID | 模块 | 严重度 | 状态 | 描述 |
+|---|--------|------|:---:|------|------|
+| 1 | ~~ISSUE-007~~ | ~~小程序售后列表~~ | 🔴 高 | ✅ 已修复 | 状态筛选失效,`data: {}` 未传 status |
+| 2 | ~~ISSUE-008~~ | ~~退款到账中止~~ | 🔴 高 | ✅ 已修复 | cancelPendingRefund 未恢复订单状态 |
+| 3 | ~~ISSUE-009~~ | ~~订单搜索条件~~ | 🟡 中 | ✅ 已修复 | 缺少商品/时间/显式搜索按钮 |
+| 4 | ~~ISSUE-010~~ | ~~Dashboard~~ | 🟡 中 | ✅ 已修复 | 缺少退款审核计数卡片 |
+| 5 | ~~ISSUE-011~~ | ~~余额不足提示~~ | 🟢 低 | ✅ 已修复 | 提示不明确,管理员可能不知情 |
+
+---
+
+## ISSUE-007:小程序售后记录列表状态筛选失效
+
+**严重度**: 🔴 高
+**状态**: ✅ 已修复
+**模块**: 小程序售后管理
+**文件**: `cfc-frontend/pages/shop/after-sales/after-sales.vue`
+
+### 问题描述
+
+小程序"售后记录"页面的 5 个状态 Tab(全部/待审核/已通过/已拒绝/已完成)切换后显示的列表内容完全一样,状态筛选功能失效。
+
+### 根因分析
+
+**双重 bug:前端未传参数 + 后端未接收参数。**
+
+**前端** (`after-sales.vue` 第96-103行):
+```javascript
+uni.request({
+    url: config.api('/api/shop/after-sales/list'),
+    method: 'POST',
+    data: {},     // ← BUG: 永远传空对象,不传 status
+    // ...
+})
+```
+`onTabChange(value)` 正确设置了 `this.currentTab = value`,但 `loadList()` 发送请求时 `data: {}` 根本没传 status。
+
+**后端** (`AfterSalesService.list()` 第58-65行):
+```java
+public Result<List<AfterSalesRequest>> list(Long userId) {
+    List<AfterSalesRequest> list = afterSalesRequestMapper.selectList(
+        new LambdaQueryWrapper<AfterSalesRequest>()
+            .eq(AfterSalesRequest::getUserId, userId)
+            .orderByDesc(AfterSalesRequest::getCreatedAt)
+    );
+    return Result.success(list);
+}
+```
+后端也不接收/处理 status 参数,总是返回全量数据。
+
+### 修复方案
+
+1. **前端**: `data: {}` → `data: { status: this.currentTab || undefined }`
+2. **后端**: `AfterSalesService.list() + AfterSalesController.list()` 增加 status 参数处理
+
+### 验证方式
+
+切换不同状态 Tab,确认列表内容随 Tab 切换而变化。
+
+---
+
+## ISSUE-008:退款到账中中止退款后订单状态未恢复
+
+**严重度**: 🔴 高
+**状态**: ✅ 已修复
+**模块**: 商城退款流程
+**文件**: `cfc-backend/src/main/java/com/etotem/cfc/service/PendingRefundService.java`
+
+### 问题描述
+
+当退款因微信商户余额不足进入"退款到账中"状态(`refundStatus=4`, `status="refund_pending"`),管理员在"待退款列表"中点击「中止退款」后,`pending_refund` 记录被标记为 `cancelled`,但 **`product_orders` 表的订单状态没有恢复**。订单保持在 `refundStatus=4` / `status="refund_pending"` 无法继续处理。
+
+### 根因分析
+
+**`PendingRefundService.cancelPendingRefund()`** 只更新了 `pending_refund` 表,完全没有动 `product_orders` 表:
+
+```java
+// 当前代码 (第128-137行)
+@Transactional
+public void cancelPendingRefund(Long refundId) {
+    PendingRefund refund = pendingRefundMapper.selectById(refundId);
+    if (refund != null) {
+        refund.setStatus("cancelled");
+        refund.setRemark("管理员中止退款");
+        refund.setUpdatedAt(new Date());
+        pendingRefundMapper.updateById(refund);
+    }
+    // ← 缺失: 未恢复 ProductOrder 状态!
+}
+```
+
+对比正常退款后 `RefundScheduledTasks.processProductRefund()` 的做法:
+```java
+order.setRefundStatus(2);     // 已退款
+order.setStatus("refunded");
+productOrderMapper.updateById(order);
+```
+
+### 修复方案
+
+`cancelPendingRefund` 需根据 `orderType` + `orderNo` 找到对应的 `ProductOrder`,恢复状态:`refundStatus=1` (`申请中`), `status="refunding"`。
+
+需在 `PendingRefundService` 中注入 `ProductOrderMapper`。
+
+### 验证方式
+
+1. 创建退款请求 → 管理员通过 → 触发余额不足 → 进入 `refund_pending`
+2. 管理员在待退款列表中中止该退款
+3. 检查 `product_orders` 表:`refundStatus` 应回到 1,`status` 应回到 `"refunding"`
+
+---
+
+## ISSUE-009:后台订单管理搜索条件不足
+
+**严重度**: 🟡 中
+**状态**: ✅ 已修复
+**模块**: Web管理端 订单管理
+**文件**: 
+- `cfc-web/src/views/admin/OrderManage.vue`
+- `cfc-backend/src/main/java/com/etotem/cfc/service/ProductOrderService.java`
+
+### 问题描述
+
+管理员订单管理页面仅支持按订单状态筛选,缺少商品名称筛选、时间范围筛选、精确订单号搜索,且没有显式的搜索按钮(仅靠 Enter 触发)。
+
+### 根因分析
+
+| 缺失项 | 前端状态 | 后端状态 |
+|--------|:---:|:---:|
+| keyword 搜索 | ✅ 前端传参 | ❌ 后端未处理 |
+| 商品名称筛选 | ❌ 不存在 | ❌ 不存在 |
+| 时间范围筛选 | ❌ 不存在 | ❌ 不存在 |
+| 显式搜索按钮 | ❌ 不存在 | — |
+
+前端 `loadList()` 正确将 `keyword` 放入 params,但后端 `ProductOrderService.adminOrderPage()` **完全没处理 keyword**,只处理了 status。
+
+### 修复方案
+
+1. **后端**: `adminOrderPage()` 增加 keyword(模糊匹配 productName + orderNo)和 startDate/endDate
+2. **前端**: 增加 `<el-date-picker>` 时间范围选择器 + `<el-button>` 搜索按钮
+3. **前端 API**: `getOrderList(params)` 传递新增参数(已正确传递 keyword,无需改)
+
+### 验证方式
+
+管理端订单管理页面:
+1. 关键字搜索能按商品名/订单号过滤
+2. 时间范围筛选正确
+3. 点击搜索按钮触发查询
+
+---
+
+## ISSUE-010:Dashboard 缺少退款审核计数
+
+**严重度**: 🟡 中
+**状态**: ✅ 已修复
+**模块**: Web管理端 Dashboard
+**文件**:
+- `cfc-web/src/views/admin/Dashboard.vue`
+- `cfc-backend/src/main/java/com/etotem/cfc/controller/stats/StatsController.java`
+
+### 问题描述
+
+管理员 Dashboard 统计卡片有 6 项(家庭总数、家长总数、孩子总数、规划师总数、待审核规划师、待审核套餐),但缺少退款审核相关的计数,管理员无法在首页快速了解待处理的退款量。
+
+### 根因分析
+
+- 前端 `Dashboard.vue` `statCards` 数组无退款卡片
+- 后端 `StatsController.getDashboardSummary()` 未查询 `pendingRefundCount`
+- 后端已有能力:`PendingRefundService.getTotalPendingAmount()` 可获取待退款总额;通过 `AfterSalesRequest mapper` 可统计待审核售后数
+
+### 修复方案
+
+1. **后端** `StatsController.getDashboardSummary()`: 新增异步查询 `pendingRefundCount` (= 待审核的 `AfterSalesRequest` 数量,status='pending')
+2. **前端** `Dashboard.vue`: `statCards` 新增一条:`{ label: '待退款审核', value: '-', icon: 'el-icon-warning', color: 'orange' }`
+
+### 验证方式
+
+Dashboard 首页出现"待退款审核"卡片,数值正确。
+
+---
+
+## ISSUE-011:退款余额不足提示不明确
+
+**严重度**: 🟢 低
+**状态**: ✅ 已修复
+**模块**: Web管理端 退款审核
+**文件**:
+- `cfc-backend/src/main/java/com/etotem/cfc/service/ProductOrderService.java`
+- `cfc-web/src/views/admin/OrderManage.vue`
+
+### 问题描述
+
+管理员点击"同意退款"后,若微信商户余额不足,后端返回 `Result.success("余额不足,退款已加入等待队列...")`。但前端 `handleApproveRefundConfirm()` 硬编码显示 `this.$message.success('已同意退款')`,管理员看到绿色"成功"提示,可能完全没注意到余额不足的事实。
+
+### 根因分析
+
+```
+后端: return Result.success("余额不足,退款已加入等待队列,到账后将自动退款")
+前端: this.$message.success('已同意退款')  // ← 硬编码,忽略后端 message
+```
+
+`Result.success()` 不表示业务完全成功,而是表示"请求被接受"。前端应显示后端返回的 message 而非硬编码。
+
+### 修复方案
+
+1. **后端**: message 加 ⚠️ 前缀,如 `"⚠️ 账户余额不足,退款已加入等待队列"`
+2. **前端**: 使用后端返回的 `res.message` 替代硬编码的 `'已同意退款'`
+
+### 验证方式
+
+触发余额不足场景,管理员应看到包含余额不足说明的提示信息。
+
+---
+
+## 更新记录
+
+| 日期 | 操作人 | 内容 |
+|------|--------|------|
+| 2026-07-12 | Sisyphus | 📋 创建退款流程问题追踪文档,录入 ISSUE-007 ~ ISSUE-011 |

+ 8 - 1
docs/系统测试/issue-tracking/README.md

@@ -20,6 +20,11 @@
 | 7 | NEW-01 | 能量规则创建返回500 | 中 | 🔍 待分析 | 2026-07-02 | 2026-07-02 | 疑似字段校验问题 |
 | 8 | NEW-02 | SKU列表接口返回500 | 中 | 🔍 待分析 | 2026-07-02 | 2026-07-02 | 疑似字段兼容问题 |
 | 9 | NEW-03 | 活动发布接口返回500 | 中 | 🔍 待分析 | 2026-07-02 | 2026-07-02 | 疑似参数格式问题 |
+| 10 | ~~ISSUE-007~~ | ~~小程序售后状态筛选失效~~ | ~~高~~ | ✅ 已修复 | 2026-07-12 | 2026-07-12 | ⚡ 修复生效,已验证 |
+| 11 | ~~ISSUE-008~~ | ~~退款中止后订单状态未恢复~~ | ~~高~~ | ✅ 已修复 | 2026-07-12 | 2026-07-12 | ⚡ 修复生效,已验证 |
+| 12 | ~~ISSUE-009~~ | ~~订单管理搜索条件不足~~ | ~~中~~ | ✅ 已修复 | 2026-07-12 | 2026-07-12 | ⚡ 修复生效,已验证 |
+| 13 | ~~ISSUE-010~~ | ~~Dashboard缺失退款计数~~ | ~~中~~ | ✅ 已修复 | 2026-07-12 | 2026-07-12 | ⚡ 前端已展示待退款审核卡片 |
+| 14 | ~~ISSUE-011~~ | ~~退款余额不足提示不明确~~ | ~~低~~ | ✅ 已修复 | 2026-07-12 | 2026-07-12 | ⚡ ⚠️ 提示明确,管理员可感知 |
 
 ---
 
@@ -379,4 +384,6 @@ public Result<Map<String, Object>> getEnergyOverview(@RequestBody Map<String, Ob
 | 2026-07-01 | Sisyphus | 第三轮完整测试:✅ 验证 ISSUE-002/003 修复有效;录入 ISSUE-004/005/006 新问题;ISSUE-001 → ⏳ 待验证(供应商pending)|
 | 2026-07-01 | Sisyphus | ✅ 完成 ISSUE-004/005/006 根因分析:均为测试数据缺失+测试脚本问题,非代码缺陷。children 表为空是核心根因。|
 | 2026-07-01 | Sisyphus | 📋 诊断脚本:issue_diag.py(31用户无孩子确认)、issue_diag2.py(admin token测试)、issue_diag3.py(user JWT获取测试)|
-| 2026-07-01 | Sisyphus | 🐛 e2e_complete_test.py parent_token 获取路径错误(`/api-auth/login` 应为 `/api/auth/phone-login`),导致 parent 测试 JWT 实际为 None |
+| 2026-07-01 | Sisyphus | 🐛 e2e_complete_test.py parent_token 获取路径错误(`/api-auth/login` 应为 `/api/auth/phone-login`),导致 parent 测试 JWT 实际为 None |
+| 2026-07-12 | Sisyphus | 📋 代码审查发现退款流程5个问题,录入 ISSUE-007 ~ ISSUE-011。详见 ISSUE-007_011-refund-flow.md |
+| 2026-07-12 | Sisyphus | ✅ 修复 ISSUE-007 ~ ISSUE-011 退款流程全部问题。修复 PR 已合入,编译验证通过 |