Files
jiapu/docs/superpowers/plans/2026-07-11-family-feed-integration.md
T
2026-07-11 21:36:26 +08:00

193 lines
7.3 KiB
Markdown

# 家族圈动态接口接入 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.openapi2.json` 中已交付的 12 个家族圈动态接口接入既有动态页面,并保持未开发的家谱业务不调用不存在的接口。
**Architecture:** `PC.openapi2.json` 是接口唯一依据;`ApiClient` 提供一一对应的私有请求封装,`feed-pages.js` 只协调页面事件和已定义的接口方法。动态页从 URL 查询参数读取真实 `genealogyId`,不写入示例家谱 ID。
**Tech Stack:** 原生 JavaScript、Axios、Node LTS `node:test`、静态 HTML/CSS。
## Global Constraints
- 所有新增或修改的注释使用清晰中文。
- 不删除现有页面和样式;不接入尚未出现在 `PC.openapi2.json` 的业务。
- 不提交当前工作区:其中含用户已有的未提交修改。
- 动态读取响应目前只有通用 `ListResult`/`PageResult`,页面仅使用 `feedId``feedContent`;后端返回缺少这两个字段时展示明确错误,不猜测字段名。
---
### Task 1: 更新接口边界测试和动态请求测试
**Files:**
- Modify: `tests/api-client-contract.test.js`
- Modify: `tests/pc-scope.test.js`
- Create: `tests/feed-pages.test.js`
**Interfaces:**
- Consumes: `PC.openapi2.json` 的 12 个 `/genealogy/pc/genealogies/{genealogyId}/feeds` 操作。
- Produces: 对客户端路径、HTTP 方法、请求体和页面请求体转换的自动化约束。
- [ ] **Step 1: 写入失败测试**
```js
test('家族圈动态方法使用文档定义的路径和请求体', async () => {
const calls = [];
const client = createClientWithRecorder(calls);
await client.createFeed(900001001, { feedContent: '今天上传一张老照片。' });
await client.feedCommentsPage(900001001, 7001, { pageNum: 1, pageSize: 10 });
assert.deepEqual(calls.map((call) => [call.method, call.url]), [
['post', '/genealogy/pc/genealogies/900001001/feeds'],
['get', '/genealogy/pc/genealogies/900001001/feeds/7001/comments/page']
]);
});
test('动态表单只构造 FamilyFeedBody 字段', () => {
assert.deepEqual(FeedPages.buildFeedBody({
feedContent: '家谱动态', mediaOssIds: '101,102', status: '0'
}), {
feedType: 'text', feedContent: '家谱动态', mediaOssIds: '101,102', status: '0'
});
});
```
- [ ] **Step 2: 运行失败测试**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test`
Expected: 动态方法或 `feed-pages.js` 尚不存在导致失败。
- [ ] **Step 3: 调整范围检查**
```js
assert.match(contract, /\/genealogy\/pc\/genealogies\/\{genealogyId\}\/feeds/);
assert.equal(read('profile-feed.html').includes('src="public/js/feed-pages.js"'), true);
assert.equal(read('profile-feed-edit.html').includes('src="public/js/feed-pages.js"'), true);
```
- [ ] **Step 4: 重新运行测试,确认仍处于预期失败状态**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test`
Expected: 仅动态实现相关断言失败。
### Task 2: 增加 12 个家族圈 API 方法
**Files:**
- Modify: `utils/ApiClient.js`
- Modify: `tests/api-client-contract.test.js`
**Interfaces:**
- Consumes: `genealogyId:number|string``feedId:number|string``commentId:number|string`
- Produces: `feeds``feedsPage``feedDetail``createFeed``updateFeed``deleteFeed``likeFeed``unlikeFeed``feedComments``feedCommentsPage``createFeedComment``deleteFeedComment`
- [ ] **Step 1: 保持失败测试不变**
```js
await client.createFeed(900001001, { feedContent: '家谱动态' });
assert.equal(calls[0].url, '/genealogy/pc/genealogies/900001001/feeds');
assert.deepEqual(calls[0].data, { feedContent: '家谱动态' });
```
- [ ] **Step 2: 实现最小路径构造**
```js
function feedPath(genealogyId, suffix) {
return '/genealogy/pc/genealogies/' + encodeURIComponent(genealogyId) + '/feeds' + suffix;
}
createFeed: function (genealogyId, body) {
return request('POST', feedPath(genealogyId, ''), { body: body });
}
```
- [ ] **Step 3: 运行测试,确认通过**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test`
Expected: 动态客户端路径和请求体断言通过。
### Task 3: 恢复动态页的真实交互
**Files:**
- Create: `public/js/feed-pages.js`
- Modify: `profile-feed.html`
- Modify: `profile-feed-edit.html`
- Modify: `tests/feed-pages.test.js`
**Interfaces:**
- Consumes: `GenealogyApi.defaultClient` 的 12 个动态方法和 URL 中的 `genealogyId`、可选 `feedId`
- Produces: 动态列表、发布/编辑、点赞/取消点赞、评论查看/发布/删除、动态删除事件。
- [ ] **Step 1: 使页面测试失败**
```js
assert.equal(FeedPages.getCurrentGenealogyId('?genealogyId=900001001'), '900001001');
assert.equal(FeedPages.getCurrentGenealogyId(''), '');
assert.equal(FeedPages.getFeedId({ feedId: 7001 }), '7001');
```
- [ ] **Step 2: 实现页面数据转换和事件**
```js
function buildFeedBody(values) {
return {
feedType: 'text',
feedContent: String(values.feedContent || '').trim(),
mediaOssIds: trimOrUndefined(values.mediaOssIds),
status: trimOrUndefined(values.status)
};
}
```
列表只读取 `feedId``feedContent`,并在缺少其中任一字段时显示“动态响应缺少 feedId 或 feedContent,请联系后端补充 DTO”。编辑页以 `feedId` 查询详情并调用 `updateFeed`;列表操作调用点赞、取消点赞、评论、删除接口后重新加载当前页。
- [ ] **Step 3: 挂载脚本和契约字段**
```html
<textarea id="feedContent" name="feedContent" required></textarea>
<script src="public/js/feed-pages.js"></script>
```
移除未在 `FamilyFeedBody` 中定义的“可见范围”提交字段;保持页面布局与既有视觉类名。
- [ ] **Step 4: 运行测试,确认通过**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test`
Expected: `feed-pages.test.js` 和已有测试全部通过。
### Task 4: 更新文档来源与最终验证
**Files:**
- Modify: `tests/pc-scope.test.js`
- Modify: `PC.openapi.yaml`
**Interfaces:**
- Consumes: `PC.openapi2.json` 的接口路径。
- Produces: 明确标记旧 YAML 不再为接口源,并使静态检查以 JSON 为准。
- [ ] **Step 1: 写入失败检查**
```js
const contract = JSON.parse(read('PC.openapi2.json'));
assert.ok(contract.paths['/genealogy/pc/genealogies/{genealogyId}/feeds']);
```
- [ ] **Step 2: 标记旧 YAML 失效**
`PC.openapi.yaml` 文件首行写入中文说明:该文件不再作为代码或测试接口源,正式接口源为 `PC.openapi2.json`。不复制或手改 JSON 中未定义的动态响应 DTO。
- [ ] **Step 3: 全量验证**
Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test`
Expected: 全部测试通过,且所有动态页面加载的脚本文件都存在。
## Self-review
- 范围只覆盖最新文档已交付的家族圈动态接口,没有加入家谱创建、成员或世系树接口。
- 所有请求体字段来自 `FamilyFeedBody``FamilyFeedCommentBody`
- 读取响应不使用历史字段兜底,不把未知字段猜成有效数据。
- 任何 URL 缺少 `genealogyId` 的页面都会明确提示,不使用硬编码示例 ID。