|
|
@@ -0,0 +1,182 @@
|
|
|
+# 用户审核不通过原因 Implementation Plan
|
|
|
+
|
|
|
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
|
+
|
|
|
+**Goal:** 平台管理员可以将商家或骑手标记为审核不通过并填写必填原因,后端持久化该原因并随用户信息返回。
|
|
|
+
|
|
|
+**Architecture:** 沿用 `InfoUser`、`PUT /infouser/user` 和现有 `sys_user_audit` 字典,不增加新接口或审核历史表。Controller 负责状态与原因的一致性校验,MyBatis 在审核状态发生更新时同步写入或清空原因,平台两个审核页负责条件展示和前端必填校验。
|
|
|
+
|
|
|
+**Tech Stack:** Java 21、Spring Boot、MyBatis XML、MySQL、Vue 2、Element UI、vue-i18n、JUnit 5、Mockito。
|
|
|
+
|
|
|
+## Global Constraints
|
|
|
+
|
|
|
+- `info_user.audit_reject_reason` 使用 `TEXT NULL`,不设置应用层字符数限制。
|
|
|
+- 状态值固定为 `0=待审核`、`1=审核通过`、`2=审核不通过`。
|
|
|
+- 状态为 `2` 时原因去除首尾空白后必须非空;状态明确更新为其他值时原因保存为 `NULL`。
|
|
|
+- 只修改平台端商家页和骑手页,不修改 App 前端和夜市审核页。
|
|
|
+- 数据库变更只追加到 `updatesql/sql.md`,不得直接执行。
|
|
|
+- 平台端新增文案同步维护简中、繁中、英文、越南语,保持原 CRLF。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 1: 后端审核原因校验与持久化
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `ruoyi-admin/src/test/java/com/ruoyi/app/user/InfoUserControllerTest.java`
|
|
|
+- Modify: `ruoyi-admin/src/main/java/com/ruoyi/app/user/InfoUserController.java`
|
|
|
+- Modify: `ruoyi-system/src/main/java/com/ruoyi/system/domain/InfoUser.java`
|
|
|
+- Modify: `ruoyi-system/src/main/resources/mapper/infouser/InfoUserMapper.xml`
|
|
|
+- Modify: `ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties`
|
|
|
+- Modify: `ruoyi-admin/src/main/resources/i18n/messages_zh_TW.properties`
|
|
|
+- Modify: `ruoyi-admin/src/main/resources/i18n/messages_en_US.properties`
|
|
|
+- Modify: `ruoyi-admin/src/main/resources/i18n/messages_vi.properties`
|
|
|
+
|
|
|
+**Interfaces:**
|
|
|
+- Consumes: `PUT /infouser/user` 的现有 `InfoUser` JSON 请求。
|
|
|
+- Produces: `InfoUser.getAuditRejectReason()/setAuditRejectReason(String)`;现有用户信息响应新增 `auditRejectReason`。
|
|
|
+
|
|
|
+- [ ] **Step 1: 写失败测试**
|
|
|
+
|
|
|
+在 `InfoUserControllerTest` 增加三条行为测试:状态为 `2` 且原因只有空白时返回错误且不调用 Service;状态为 `2` 且原因有效时修剪后保存;状态为 `1` 时将旧原因清空后保存。
|
|
|
+
|
|
|
+```java
|
|
|
+@Test
|
|
|
+void rejectsAuditRejectionWithoutReason() {
|
|
|
+ InfoUser user = new InfoUser();
|
|
|
+ user.setAuditStatus("2");
|
|
|
+ user.setAuditRejectReason(" ");
|
|
|
+
|
|
|
+ AjaxResult result = controller.edit(user);
|
|
|
+
|
|
|
+ assertEquals(HttpStatus.ERROR, result.get(AjaxResult.CODE_TAG));
|
|
|
+ verify(infoUserService, never()).updateInfoUser(any(InfoUser.class));
|
|
|
+}
|
|
|
+
|
|
|
+@Test
|
|
|
+void trimsAndSavesAuditRejectionReason() {
|
|
|
+ InfoUser user = new InfoUser();
|
|
|
+ user.setAuditStatus("2");
|
|
|
+ user.setAuditRejectReason(" 证件照片模糊 ");
|
|
|
+ when(infoUserService.updateInfoUser(user)).thenReturn(1);
|
|
|
+
|
|
|
+ controller.edit(user);
|
|
|
+
|
|
|
+ assertEquals("证件照片模糊", user.getAuditRejectReason());
|
|
|
+ verify(infoUserService).updateInfoUser(user);
|
|
|
+}
|
|
|
+
|
|
|
+@Test
|
|
|
+void clearsOldReasonWhenAuditPasses() {
|
|
|
+ InfoUser user = new InfoUser();
|
|
|
+ user.setAuditStatus("1");
|
|
|
+ user.setAuditRejectReason("旧原因");
|
|
|
+ when(infoUserService.updateInfoUser(user)).thenReturn(1);
|
|
|
+
|
|
|
+ controller.edit(user);
|
|
|
+
|
|
|
+ assertEquals(null, user.getAuditRejectReason());
|
|
|
+ verify(infoUserService).updateInfoUser(user);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: 运行定向测试确认 RED**
|
|
|
+
|
|
|
+PowerShell 临时设置 JDK 21 后运行:
|
|
|
+
|
|
|
+```powershell
|
|
|
+$env:JAVA_HOME='C:\Users\qmj\.jdks\graalvm-jdk-21.0.7'
|
|
|
+$env:Path="$env:JAVA_HOME\bin;$env:Path"
|
|
|
+mvn -pl ruoyi-admin -am -Dtest=InfoUserControllerTest -Dsurefire.failIfNoSpecifiedTests=false test
|
|
|
+```
|
|
|
+
|
|
|
+预期:编译失败,提示 `InfoUser` 尚无 `auditRejectReason` 访问方法。
|
|
|
+
|
|
|
+- [ ] **Step 3: 实现最小后端改动**
|
|
|
+
|
|
|
+- `InfoUser` 新增 `String auditRejectReason` 及访问方法,并加入 `toString()`。
|
|
|
+- `InfoUserMapper.xml` 的 `resultMap`、insert 列和值补齐 `audit_reject_reason`。
|
|
|
+- update 中使用 `<if test="auditStatus != null">audit_reject_reason = #{auditRejectReason},</if>`,确保审核状态更新为非 `2` 时能写入 `NULL`。
|
|
|
+- `InfoUserController.edit` 在调用 Service 前完成状态校验、原因修剪和旧原因清空。
|
|
|
+- 四个后端资源文件新增 `no.user.audit.reject.reason.required`。
|
|
|
+
|
|
|
+- [ ] **Step 4: 运行定向测试确认 GREEN**
|
|
|
+
|
|
|
+运行 Step 2 命令,预期 `InfoUserControllerTest` 全部通过。
|
|
|
+
|
|
|
+### Task 2: 平台商家和骑手审核表单
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `E:/QtwCode/foodie/foodie-admin-vue/src/views/infouser/user/sjuser.vue`
|
|
|
+- Modify: `E:/QtwCode/foodie/foodie-admin-vue/src/views/infouser/user/peisus.vue`
|
|
|
+- Modify: `E:/QtwCode/foodie/foodie-admin-vue/src/api/language/language.zh_CN.js`
|
|
|
+- Modify: `E:/QtwCode/foodie/foodie-admin-vue/src/api/language/language.zh_TW.js`
|
|
|
+- Modify: `E:/QtwCode/foodie/foodie-admin-vue/src/api/language/language.en_US.js`
|
|
|
+- Modify: `E:/QtwCode/foodie/foodie-admin-vue/src/api/language/language.vi.js`
|
|
|
+
|
|
|
+**Interfaces:**
|
|
|
+- Consumes: `sys_user_audit` 字典新增的值 `2`;用户详情中的 `auditRejectReason`。
|
|
|
+- Produces: `PUT /infouser/user` 请求中的 `auditStatus` 与 `auditRejectReason`。
|
|
|
+
|
|
|
+- [ ] **Step 1: 添加审核不通过展示和输入控件**
|
|
|
+
|
|
|
+- 两个列表的审核状态列增加值 `2` 的 danger 标签。
|
|
|
+- 两个审核表单在 `form.auditStatus === '2'` 时显示 `type="textarea"` 的原因输入框。
|
|
|
+- `reset()` 增加 `auditRejectReason: null`。
|
|
|
+
|
|
|
+- [ ] **Step 2: 添加条件必填与提交归一化**
|
|
|
+
|
|
|
+- 两页的 `rules.auditRejectReason` 在状态为 `2` 时拒绝空值或纯空白。
|
|
|
+- `submitForm()` 验证通过后:状态为 `2` 时修剪原因,否则赋值 `null`,再调用现有 `updateUser`。
|
|
|
+
|
|
|
+- [ ] **Step 3: 补齐四语言文案**
|
|
|
+
|
|
|
+- `route` 增加“审核不通过”。
|
|
|
+- `infoussanhuser` 与 `infouspeisus` 分别增加“审核不通过原因”“请输入审核不通过原因”“审核不通过原因不能为空”。
|
|
|
+
|
|
|
+- [ ] **Step 4: 构建平台前端**
|
|
|
+
|
|
|
+```powershell
|
|
|
+npm run build:prod
|
|
|
+```
|
|
|
+
|
|
|
+工作目录:`E:\QtwCode\foodie\foodie-admin-vue`。预期构建成功;不得改动原有脏文件 `src/views/chanting/store/shangc.vue`。
|
|
|
+
|
|
|
+### Task 3: 数据库迁移与规格任务收口
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `updatesql/sql.md`
|
|
|
+- Create: `specs/021-user-audit-rejection/tasks.md`
|
|
|
+
|
|
|
+**Interfaces:**
|
|
|
+- Consumes: Task 1 的属性和 Task 2 的字典值。
|
|
|
+- Produces: 开发者可手动执行的数据库迁移 SQL 与可审计任务状态。
|
|
|
+
|
|
|
+- [ ] **Step 1: 追加迁移 SQL**
|
|
|
+
|
|
|
+```sql
|
|
|
+ALTER TABLE info_user
|
|
|
+ ADD COLUMN audit_reject_reason TEXT NULL COMMENT '用户审核不通过原因' AFTER audit_status;
|
|
|
+
|
|
|
+INSERT INTO sys_dict_data
|
|
|
+ (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark)
|
|
|
+SELECT 2, '审核不通过', '2', 'sys_user_audit', '', 'danger', 'N', '0', 'admin', NOW(), '用户审核不通过'
|
|
|
+WHERE NOT EXISTS (
|
|
|
+ SELECT 1 FROM sys_dict_data WHERE dict_type = 'sys_user_audit' AND dict_value = '2'
|
|
|
+);
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: 完成任务清单并执行后端验证**
|
|
|
+
|
|
|
+运行 Task 1 定向测试,再运行:
|
|
|
+
|
|
|
+```powershell
|
|
|
+mvn -pl ruoyi-admin -am -DskipTests package
|
|
|
+```
|
|
|
+
|
|
|
+预期后端模块构建成功。
|
|
|
+
|
|
|
+- [ ] **Step 3: 最终差异检查**
|
|
|
+
|
|
|
+- 后端仓库只包含本需求规格、测试、Java、Mapper、i18n 和 SQL 变更,以及用户原有的 `.claude/homunculus/observations.jsonl` 脏文件。
|
|
|
+- 平台仓库只新增本需求对两个审核页和四语言文件的修改,保留且不混入原有 `shangc.vue` 改动。
|
|
|
+- `git diff --check` 无空白错误,前端文件仍为 CRLF。
|