# 反馈与工单 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:** 开放意见反馈、提交工单、我的工单和工单详情,以同一套 PC 反馈记录形成提交、列表和详情闭环。 **Architecture:** `utils/ApiClient.js` 唯一拥有 `/genealogy/pc/feedback` 的 GET/POST 契约;新增 UMD 模块 `feedback-pages.js` 负责 DTO 构造、完整 VO 规范化、列表/详情渲染和四类页面初始化。“工单”只是反馈记录的帮助中心展示名称,详情通过 URL 中由列表响应产生的 `feedbackId` 在我的反馈列表中精确匹配。 **Tech Stack:** 静态 HTML、原生 JavaScript UMD、Axios、Node `node:test`。 ## Global Constraints - 后端 `D:\WorkSpace\Java\Genealogy` 全程只读。 - 只调用 `POST /genealogy/pc/feedback` 和 `GET /genealogy/pc/feedback`。 - 请求只发送 `feedbackType`、`feedbackContent`、`contactInfo`;不发送 `feedbackTitle`。 - `feedbackType` 只允许后端已确认字典值 `advice/bug/complaint/other`;空值省略并由后端默认 `advice`。 - `feedbackId` 只能来自提交或列表响应,不提供文本输入。 - 页面不展示 `appUserId`、`handlerId`、`appUserPhone`、原始 JSON。 - 所有提交防重复;提交成功后必须重读列表并匹配同一 `feedbackId`。 --- ### Task 1: ApiClient 反馈契约 **Files:** - Modify: `utils/ApiClient.js` - Modify: `tests/api-client-contract.test.js` **Interfaces:** - Produces: `submitFeedback(body)`、`myFeedback()` - [ ] **Step 1: 写失败的契约测试** 增加测试,使用完整请求字面量断言: ```js await client.submitFeedback({ feedbackType: 'bug', feedbackContent: '页面按钮无响应', contactInfo: '19181970173', feedbackTitle: 'must-drop', appUserId: 'must-drop' }); await client.myFeedback(); assert.deepEqual(calls, [ { method: 'POST', url: '/genealogy/pc/feedback', body: { feedbackType: 'bug', feedbackContent: '页面按钮无响应', contactInfo: '19181970173' } }, { method: 'GET', url: '/genealogy/pc/feedback' } ]); ``` - [ ] **Step 2: 运行 RED** Run: ```powershell node --test tests/api-client-contract.test.js ``` Expected: FAIL,`submitFeedback` 或 `myFeedback` 不存在。 - [ ] **Step 3: 最小实现** 在 `createClient()` 返回对象中增加: ```js submitFeedback: function (body) { return request('POST', '/genealogy/pc/feedback', { body: pickDefined(body, ['feedbackType', 'feedbackContent', 'contactInfo']) }); }, myFeedback: function () { return request('GET', '/genealogy/pc/feedback'); } ``` - [ ] **Step 4: 运行 GREEN** Run: ```powershell node --test tests/api-client-contract.test.js ``` Expected: PASS。 --- ### Task 2: 反馈 DTO、VO、列表和详情边界 **Files:** - Create: `public/js/feedback-pages.js` - Create: `tests/feedback-pages.test.js` **Interfaces:** - Produces: - `getFeedbackId(search)` - `buildFeedbackBody(values)` - `validateFeedbackBody(body)` - `normalizeFeedback(item)` - `normalizeFeedbackList(data)` - `findFeedbackById(data, feedbackId)` - `buildFeedbackDetailUrl(feedbackId)` - `renderFeedbackList(data, options)` - `renderFeedbackDetail(item)` - [ ] **Step 1: 写失败的纯行为测试** 用完整 `FeedbackVo` 字面量验证: ```js assert.deepEqual(FeedbackPages.buildFeedbackBody({ feedbackType: ' bug ', feedbackContent: ' 页面按钮无响应 ', contactInfo: ' 19181970173 ', feedbackTitle: 'must-drop', appUserId: 'must-drop' }), { feedbackType: 'bug', feedbackContent: '页面按钮无响应', contactInfo: '19181970173' }); ``` 同时断言: - `feedbackContent` 为空时报错; - 类型只允许 `advice/bug/complaint/other`,空值省略; - 不安全 number ID、空内容、`handleStatus` 非 `0/1/2/3`、`status` 非 `0/1` 的响应拒绝; - 数组包含一个非法元素时整批返回空数组; - 详情只精确匹配同一字符串 `feedbackId`,不存在时不回退第一条; - 列表和详情转义可见文本,不出现内部用户/处理人 ID、账号手机号或原始 JSON; - 详情 URL 使用 `ticket-detail.html?feedbackId=...`。 - [ ] **Step 2: 运行 RED** Run: ```powershell node --test tests/feedback-pages.test.js ``` Expected: FAIL,模块不存在。 - [ ] **Step 3: 最小实现纯函数** `normalizeFeedback()` 返回且只返回: ```js { feedbackId, feedbackType, feedbackContent, contactInfo, handleStatus, handleResult, handleTime, status, remark } ``` 状态展示固定为: ```js { '0': '待处理', '1': '处理中', '2': '已处理', '3': '已关闭' } ``` - [ ] **Step 4: 运行 GREEN** Run: ```powershell node --test tests/feedback-pages.test.js node --check public/js/feedback-pages.js ``` Expected: PASS。 --- ### Task 3: 四个页面的真实反馈闭环 **Files:** - Modify: `profile-feedback.html` - Modify: `submit-ticket.html` - Modify: `my-tickets.html` - Modify: `ticket-detail.html` - Modify: `public/js/feedback-pages.js` - Modify: `tests/feedback-pages.test.js` - Modify: `tests/pending-pages.test.js` - Modify: `tests/public-static-pages.test.js` - Modify: `tests/pc-scope.test.js` **Interfaces:** - Produces: `initFeedbackFormPage()`、`initFeedbackListPage()`、`initFeedbackDetailPage()`、`init()` - [ ] **Step 1: 写失败的页面行为测试** 断言四个页面: - 不再包含 `data-feature-status="pending"` 或 `pending-pages.js`; - 均加载 `feedback-pages.js` 和 ApiClient 依赖; - 两个提交页只提供 `feedbackType/feedbackContent/contactInfo`,不存在 `feedbackTitle` 或任意业务 ID 输入; - 列表页有 `data-feedback-list` 和刷新按钮; - 详情页只有 `data-feedback-detail`,不提供回复、追问、删除或关闭操作。 断言脚本提交后调用 `myFeedback()`,必须按提交响应的 `feedbackId` 重读匹配;详情按 URL `feedbackId` 精确匹配。 - [ ] **Step 2: 运行 RED** Run: ```powershell node --test tests/feedback-pages.test.js tests/pending-pages.test.js tests/public-static-pages.test.js tests/pc-scope.test.js ``` Expected: FAIL,四个页面仍为 pending。 - [ ] **Step 3: 实现页面初始化** 提交页: 1. 构造并校验请求; 2. `writePending` 锁和按钮禁用; 3. 调用 `submitFeedback(body)`; 4. 规范化提交响应; 5. 重读 `myFeedback()`,精确找到同一 `feedbackId`; 6. 个人反馈页刷新历史列表;工单页进入 `ticket-detail.html?feedbackId=...`。 列表页读取 `myFeedback()` 并生成详情链接。详情页从 URL 读取 ID、重读列表并精确匹配;未匹配显示“反馈记录不存在或无权查看”。 - [ ] **Step 4: 运行 GREEN** Run: ```powershell node --test tests/feedback-pages.test.js tests/pending-pages.test.js tests/public-static-pages.test.js tests/pc-scope.test.js node --check public/js/feedback-pages.js ``` Expected: PASS。 --- ### Task 4: 导航、规划和收尾验证 **Files:** - Modify: `help.html` - Modify: `profile-services.html` - Modify: `docs/PC接口对接规划.md` - Modify: `tests/stage6-navigation.test.js` - Modify: `docs/superpowers/plans/2026-07-29-feedback-tickets.md` **Interfaces:** - Consumes: Task 1–3 的真实反馈闭环。 - [ ] **Step 1: 写失败的导航测试** 断言: - 帮助中心可直接进入提交工单和我的工单; - 服务中心可进入意见反馈; - 所有入口不再被 pending 状态拦截; - 不存在独立 ticket API、反馈标题或手填 `feedbackId` 的入口。 - [ ] **Step 2: 运行 RED** Run: ```powershell node --test tests/stage6-navigation.test.js tests/public-static-pages.test.js ``` Expected: FAIL,帮助中心尚未开放我的工单入口或旧 pending 断言仍存在。 - [ ] **Step 3: 更新入口和规划** 规划记录: - `AppFeedbackBody` 三个字段来源与提交时机; - `FeedbackVo` 可见字段、内部隐藏字段和四种处理状态; - 无独立工单 path,四个页面共用反馈记录; - 提交后重读、详情精确匹配、401/403 和隐私边界; - 后端没有用户侧回复、追问、关闭或删除接口,继续阻断。 - [ ] **Step 4: 聚焦和全量验证** Run: ```powershell node --test tests/feedback-pages.test.js tests/api-client-contract.test.js tests/pending-pages.test.js tests/public-static-pages.test.js tests/pc-scope.test.js tests/stage6-navigation.test.js npm test node --check public/js/feedback-pages.js git -c safe.directory=D:/WorkSpace/Web/jiapu diff --check ``` Expected: 全部 PASS。 - [ ] **Step 5: 浏览器只读验证** 使用真实登录态验证四个页面、我的反馈列表、无记录详情、隐私和控制台错误。未经用户本轮明确授权,不提交真实反馈。