feat(api): 添加帮助中心反馈系统和VIP服务功能
- 在ApiClient中新增submitFeedback、myFeedback、helpArticles、helpArticleDetail、 siteArticles、promotions、vipPackages、createVipOrder、vipOrders等方法 - 添加帮助文章和站点资讯的参数验证逻辑 - 更新测试文件添加新的API方法测试用例 - 在HTML页面中添加反馈、帮助和VIP服务相关页面的脚本引用 - 更新加入家谱页面为完整的申请流程界面 - 修改资讯详情页面为站点资讯展示页面 - 更新AxiosRequestUtil中认证处理逻辑 - 添加世系树渲染的HTML生成函数用于页面复用 - 更新文档中的API契约说明和页面规划
This commit is contained in:
@@ -0,0 +1,310 @@
|
||||
# 反馈与工单 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: 浏览器只读验证**
|
||||
|
||||
使用真实登录态验证四个页面、我的反馈列表、无记录详情、隐私和控制台错误。未经用户本轮明确授权,不提交真实反馈。
|
||||
Reference in New Issue
Block a user