|
|
@@ -0,0 +1,142 @@
|
|
|
+# 后台审核中心待审计数修复 - 独立核验报告
|
|
|
+
|
|
|
+## 核验结论
|
|
|
+
|
|
|
+**主 Bug 已修复(提交 f61547a21)**,本次核验确认修复正确,无新发现需修改的问题。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 一、统计口径(各分类)
|
|
|
+
|
|
|
+| 分类 Key | 统计 SQL 口径 |
|
|
|
+|---|---|
|
|
|
+| articlePending | `article WHERE audit_status='pending'` |
|
|
|
+| productPending | `product WHERE status='pending'` |
|
|
|
+| guidePending | `user WHERE teacher_status='pending' AND real_name IS NOT NULL` + `service_role_applications WHERE status=0 AND roles 含 'planner'` |
|
|
|
+| vendorPending | `user WHERE vendor_status='pending'` + `service_role_applications WHERE status=0 AND roles 含 'vendor'/'activity_provider'` |
|
|
|
+| butlerPending | `butler_profile WHERE status='pending'` + `user WHERE butler_status='pending'` + `service_role_applications WHERE status=0 AND roles 含 'butler'` |
|
|
|
+| packagePending | `task_template_package WHERE status='pending'` |
|
|
|
+| activityPending | `activity WHERE audit_status='pending'` |
|
|
|
+| activityRegistrationPending | `activity_registration WHERE status='pending'` |
|
|
|
+| withdrawPending | `withdrawal_request WHERE status='pending'` |
|
|
|
+| refundPending | `pending_refund WHERE status='pending'` |
|
|
|
+| hierarchyChangePending | `supply_hierarchy_change_request WHERE status='pending'` |
|
|
|
+| nutritionistPending | `user WHERE nutritionist_status='pending' AND real_name IS NOT NULL` + `service_role_applications WHERE status=0 AND roles 含 'nutritionist'` |
|
|
|
+| knowledgePending | `service_role_applications WHERE status=0 AND roles 含 'article_admin'` |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 二、根因分析
|
|
|
+
|
|
|
+### 根因1(主要):前端未监听 refresh-summary 事件
|
|
|
+**位置:** `cfc-web/src/views/admin/ReviewCenter.vue`
|
|
|
+
|
|
|
+**症状:** 各子审核组件(ArticleAudit、ProductAudit 等)在完成审核操作后会调用 `this.$emit('refresh-summary')`,但父组件 `ReviewCenter.vue` 从未监听此事件,导致顶部计数卡片永远不会刷新。
|
|
|
+
|
|
|
+**修复方式(已由提交 f61547a21 应用):**
|
|
|
+在 `ReviewCenter.vue` 中为全部 13 个子审核组件添加 `@refresh-summary="loadSummary"` 绑定:
|
|
|
+```html
|
|
|
+<article-audit ref="article" @refresh-summary="loadSummary" />
|
|
|
+<product-audit ref="product" @refresh-summary="loadSummary" />
|
|
|
+<!-- ... 其余 11 个组件同理 ... -->
|
|
|
+```
|
|
|
+
|
|
|
+### 根因2(次要):guide/nutritionist 统计口径与列表接口不一致
|
|
|
+**位置:** `ReviewCenterController.summary()`
|
|
|
+
|
|
|
+**症状:** `guidePending` 和 `nutritionistPending` 的 `user` 表查询缺少 `isNotNull("real_name")` 条件,而列表接口(`/api/admin/guide/applications` 和 `/api/admin/nutritionist/applications`)均有此过滤,导致统计数与列表显示条数不匹配。
|
|
|
+
|
|
|
+**修复方式(已由提交 f61547a21 应用):**
|
|
|
+```java
|
|
|
+// 修复前
|
|
|
+userMapper.selectCount(new QueryWrapper<User>().eq("teacher_status", "pending"))
|
|
|
+
|
|
|
+// 修复后
|
|
|
+userMapper.selectCount(new QueryWrapper<User>().eq("teacher_status", "pending").isNotNull("real_name"))
|
|
|
+// nutritionist 同理
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 三、核验状态变更流程(已确认正确)
|
|
|
+
|
|
|
+| 分类 | 审核通过 | 审核驳回 | 是否移出 pending 计数 |
|
|
|
+|---|---|---|---|
|
|
|
+| 文章 | `auditStatus → 'approved'/'rejected'` | `auditStatus → 'rejected'` | ✅ 正确 |
|
|
|
+| 商品 | `status → 'approved'/'rejected'` | `status → 'rejected'` | ✅ 正确 |
|
|
|
+| 规划师(用户路径) | `teacher_status → 'approved'` | `teacher_status → 'rejected'` | ✅ 正确 |
|
|
|
+| 规划师(服务角色路径) | `status=0→1` + `teacher_status → 'approved'` | `status=0→2` | ✅ 正确 |
|
|
|
+| 管家(用户路径) | `butler_status → 'approved'` + profile 同步 | `butler_status → 'rejected'` | ✅ 正确 |
|
|
|
+| 管家(服务角色路径) | `status=0→1` + `butler_status → 'approved'` + profile 同步 | `status=0→2` | ✅ 正确 |
|
|
|
+| 服务商(用户路径) | `vendor_status → 'approved'` | `vendor_status → 'rejected'` | ✅ 正确 |
|
|
|
+| 任务模板 | `status → 'approved'/'rejected'` | `status → 'rejected'` | ✅ 正确 |
|
|
|
+| 活动 | `auditStatus → 'approved'` + `status → 'published'` | `auditStatus → 'rejected'` + `status → 'rejected'` | ✅ 正确 |
|
|
|
+| 活动报名 | `status → 'approved'/'rejected'` | `status → 'rejected'` | ✅ 正确 |
|
|
|
+| 提现 | `status → 'processed'`(通过)/ `'rejected'`(驳回) | — | ✅ 正确 |
|
|
|
+| 待退款 | `status → 'cancelled'` | — | ✅ 正确 |
|
|
|
+| 更换上级 | `status → 'approved'`/`'rejected'` | — | ✅ 正确 |
|
|
|
+| 营养师(用户路径) | `nutritionist_status → 'approved'` | `nutritionist_status → 'rejected'` | ✅ 正确 |
|
|
|
+| 知识管理员 | `status=0→1` | `status=0→2` | ✅ 正确 |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 四、前端 refresh-summary 触发覆盖(已确认完整)
|
|
|
+
|
|
|
+以下审核动作均正确触发 `$emit('refresh-summary')`:
|
|
|
+
|
|
|
+| 组件 | 单个通过 | 单个驳回 | 批量通过 | 批量驳回 | 中止退款 |
|
|
|
+|---|---|---|---|---|---|
|
|
|
+| ArticleAudit | ✅ | ✅ | ✅ | ✅ | — |
|
|
|
+| ProductAudit | ✅ | ✅ | — | — | — |
|
|
|
+| GuideAudit | ✅ | ✅ | — | — | — |
|
|
|
+| NutritionistAudit | ✅ | ✅ | — | — | — |
|
|
|
+| VendorAudit | ✅ | ✅ | — | — | — |
|
|
|
+| ButlerAudit | ✅ | ✅ | — | — | — |
|
|
|
+| PackageAudit | ✅ | ✅ | ✅ | ✅ | — |
|
|
|
+| ActivityAudit | ✅ | ✅ | — | — | — |
|
|
|
+| ActivityRegistrationAudit | ✅ | ✅ | — | — | — |
|
|
|
+| WithdrawAudit | ✅ | ✅ | — | — | — |
|
|
|
+| PendingRefundAudit | — | — | — | — | ✅ |
|
|
|
+| HierarchyAudit | ✅ | ✅ | — | — | — |
|
|
|
+| KnowledgeAdminAudit | ✅ | ✅ | — | — | — |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 五、发现的遗留边缘问题(非本次 Bug 主因)
|
|
|
+
|
|
|
+### WithdrawalService.audit() 在 familyId=null 时不更新状态
|
|
|
+**位置:** `cfc-backend/src/main/java/com/etotem/cfc/service/WithdrawalService.java:89-103`
|
|
|
+
|
|
|
+```java
|
|
|
+Long familyId = resolveFamilyId(request.getUserId());
|
|
|
+if (familyId != null) {
|
|
|
+ if ("approved".equals(status)) {
|
|
|
+ request.setStatus("processed"); // 正常路径
|
|
|
+ } else if ("rejected".equals(status)) {
|
|
|
+ request.setStatus("rejected"); // 正常路径
|
|
|
+ }
|
|
|
+}
|
|
|
+// ⚠️ familyId == null 时,status 字段未被修改,仍为 "pending"
|
|
|
+```
|
|
|
+
|
|
|
+**影响:** 若提现申请关联的用户无家庭(familyId 为 null),审核通过后 status 保持 "pending",`withdrawPending` 计数不会减少。
|
|
|
+
|
|
|
+**建议:** 当 familyId 为 null 时也应更新状态(例如设为 "rejected" 并记录原因),或在 service 层做前置校验。此为非阻塞性边缘问题,不影响主体流程。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 六、编译验证
|
|
|
+
|
|
|
+当前环境 Maven 不可用(不在 PATH 中),无法执行 `mvn clean compile` 验证。建议部署前由 CI 流水线确认。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 七、总结
|
|
|
+
|
|
|
+| 项目 | 结论 |
|
|
|
+|---|---|
|
|
|
+| 根因 | 父组件 `ReviewCenter.vue` 未绑定 `@refresh-summary` 事件(主因);guide/nutritionist 统计口径漏掉 `real_name IS NOT NULL`(次因) |
|
|
|
+| 修复提交 | `f61547a21 fix(review): 统一审核中心统计口径与驳回字段命名` |
|
|
|
+| 修复内容 | 13 个子组件全部添加 `@refresh-summary="loadSummary"`;guide/nutritionist 统计加 `.isNotNull("real_name")` |
|
|
|
+| 场景覆盖 | 全部 13 类审核(单个/批量通过、单个/批量驳回、中止退款)均正确刷新顶部计数 |
|
|
|
+| 遗留问题 | WithdrawalService.audit() 在 familyId=null 时不更新状态(边缘 case,非本次 Bug 主因) |
|