- 在ApiClient中新增submitFeedback、myFeedback、helpArticles、helpArticleDetail、 siteArticles、promotions、vipPackages、createVipOrder、vipOrders等方法 - 添加帮助文章和站点资讯的参数验证逻辑 - 更新测试文件添加新的API方法测试用例 - 在HTML页面中添加反馈、帮助和VIP服务相关页面的脚本引用 - 更新加入家谱页面为完整的申请流程界面 - 修改资讯详情页面为站点资讯展示页面 - 更新AxiosRequestUtil中认证处理逻辑 - 添加世系树渲染的HTML生成函数用于页面复用 - 更新文档中的API契约说明和页面规划
12 KiB
家谱主页 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: 将 profile-family-home.html 从静态预览开放为真实 PC 家谱主页,展示当前家谱概览和世系树预览,并保留已接入业务页入口。
Architecture: profile-common.js 继续唯一拥有当前 genealogyId;页面只调用已有 genealogyOverview(genealogyId) 和 lineageTree(genealogyId)。lineage-pages.js 继续唯一拥有世系树节点规范化与 HTML 生成,新建 family-home-pages.js 只负责概览 DTO、权限显隐、并行读取和页面状态。
Tech Stack: 静态 HTML、原生 JavaScript UMD、Axios、Node node:test。
Status: 2026-07-29 已按 Task 1–4 实施;自动化与无家谱真实账号分支已验证,有家谱数据态等待具备真实家谱的账号复核。
Global Constraints
- 后端
D:\WorkSpace\Java\Genealogy全程只读。 - 只使用 PC 接口;不得调用
/genealogy/dashboard/overview、APP 或管理后台接口。 genealogyId只能来自ProfileUI.getGenealogyId(),始终按字符串处理。GET /genealogy/pc/genealogies/{genealogyId}/overview返回AppGenealogyVo,它不是内容统计接口;不得制造文章、相册、视频或活动数量。- 世系树只读调用
GET /genealogy/pc/genealogies/{genealogyId}/lineage/tree。 - 家谱管理入口仅在响应
canManage=true时显示;内容页自身继续负责更细权限。 - PC 没有成员邀请创建/分享接口;主页不得开放“邀请家人”操作。
- 本批次没有写接口,不创建、修改或删除任何真实业务数据。
Task 1: 家谱主页 DTO 与页面契约
Files:
- Create:
public/js/family-home-pages.js - Create:
tests/family-home-pages.test.js
Interfaces:
-
Consumes:
ProfileUI.getGenealogyId()、GenealogyApi.defaultClient -
Produces:
normalizeFamilyOverview(item, expectedGenealogyId)renderFamilyOverview(overview)shouldShowFamilyManagement(overview)shouldRedirectToLogin(api, error)
-
Step 1: 写失败数据边界测试
const overview = FamilyHomePages.normalizeFamilyOverview({
genealogyId: '2062179707935264769',
genealogyNo: 'G20260729001',
genealogyName: '叶氏家谱',
surname: '叶',
ancestralHall: '南阳堂',
originPlace: '四川成都',
regionFullName: '四川省 成都市',
memberCount: 12,
personCount: 36,
status: '0',
canManage: true,
canEditContent: true,
ownerUserId: 'must-not-render',
coverOssId: 'must-not-render'
}, '2062179707935264769');
assert.deepEqual(overview, {
genealogyId: '2062179707935264769',
genealogyNo: 'G20260729001',
genealogyName: '叶氏家谱',
surname: '叶',
ancestralHall: '南阳堂',
originPlace: '四川成都',
regionFullName: '四川省 成都市',
memberCount: 12,
personCount: 36,
canManage: true,
canEditContent: true
});
assert.equal(
FamilyHomePages.normalizeFamilyOverview(
{ genealogyId: Number.MAX_SAFE_INTEGER + 1, genealogyName: '非法', status: '0' },
'2062179707935264769'
),
null
);
assert.equal(
FamilyHomePages.normalizeFamilyOverview(
{ genealogyId: '2', genealogyName: '串谱', status: '0' },
'2062179707935264769'
),
null
);
同时断言:
-
genealogyName为空、status!='0'、负数/非安全计数均拒绝; -
渲染转义全部文本,不出现
ownerUserId、coverOssId或原始 JSON; -
只有布尔值
canManage===true才开放管理入口; -
401 清理登录态,403 不清理登录态。
-
Step 2: 运行 RED
Run: node --test tests/family-home-pages.test.js
Expected: FAIL,family-home-pages.js 不存在。
- Step 3: 最小实现概览规范化
function normalizeFamilyOverview(item, expectedGenealogyId) {
var source = item || {};
var genealogyId = normalizeId(source.genealogyId);
var expectedId = normalizeId(expectedGenealogyId);
var memberCount = normalizeCount(source.memberCount);
var personCount = normalizeCount(source.personCount);
if (!genealogyId || genealogyId !== expectedId ||
!text(source.genealogyName) || String(source.status) !== '0' ||
memberCount === null || personCount === null) return null;
return {
genealogyId: genealogyId,
genealogyNo: text(source.genealogyNo),
genealogyName: text(source.genealogyName),
surname: text(source.surname),
ancestralHall: text(source.ancestralHall),
originPlace: text(source.originPlace),
regionFullName: text(source.regionFullName),
memberCount: memberCount,
personCount: personCount,
canManage: source.canManage === true,
canEditContent: source.canEditContent === true
};
}
- Step 4: 运行 GREEN
Run:
node --test tests/family-home-pages.test.js
node --check public/js/family-home-pages.js
Expected: PASS。
Task 2: 复用世系树唯一渲染 owner
Files:
- Modify:
public/js/lineage-pages.js - Modify:
tests/lineage-pages.test.js - Modify:
tests/family-home-pages.test.js
Interfaces:
-
Produces:
LineagePages.renderLineageTreeHtml(data) -
Consumes: 现有
normalizeLineagePerson(item)、renderTreeNode(item, ancestry) -
Step 1: 写失败共享渲染测试
const tree = [{
personId: '2062179707935264770',
genealogyId: '2062179707935264769',
name: '<始祖>',
status: '0',
spouses: [],
children: []
}];
const html = LineagePages.renderLineageTreeHtml(tree);
assert.match(html, /<始祖>/);
assert.match(html, /data-lineage-person="2062179707935264770"/);
assert.doesNotMatch(html, /2062179707935264769/);
assert.match(LineagePages.renderLineageTreeHtml([]), /暂无世系树/);
- Step 2: 运行 RED
Run: node --test tests/lineage-pages.test.js tests/family-home-pages.test.js
Expected: FAIL,renderLineageTreeHtml 未导出。
- Step 3: 从现有
renderTree提取纯 HTML owner
function renderLineageTreeHtml(data) {
var nodes = normalizeList(data)
.map(function (item) { return renderTreeNode(item, {}); })
.filter(Boolean);
return nodes.length
? '<ul class="lineage-tree">' + nodes.join('') + '</ul>'
: '<div class="api-empty">暂无世系树</div>';
}
function renderTree(data) {
var container = query('[data-lineage-tree]');
if (container) container.innerHTML = renderLineageTreeHtml(data);
}
在 UMD 导出对象加入:
renderLineageTreeHtml: renderLineageTreeHtml
- Step 4: 运行 GREEN
Run:
node --test tests/lineage-pages.test.js tests/family-home-pages.test.js
node --check public/js/lineage-pages.js
Expected: PASS,现有世系管理页输出不变。
Task 3: 开放真实家谱主页
Files:
- Modify:
profile-family-home.html - Modify:
public/js/family-home-pages.js - Modify:
tests/family-home-pages.test.js - Modify:
tests/pending-pages.test.js - Modify:
tests/stage6-navigation.test.js
Interfaces:
-
Consumes:
api.genealogyOverview(genealogyId)api.lineageTree(genealogyId)LineagePages.renderLineageTreeHtml(data)
-
Produces:
initFamilyHomePage()、init() -
Step 1: 写失败页面测试
断言:
profile-family-home.html不再包含data-feature-status="pending"或pending-pages.js;- 加载顺序为
profile-common.js、lineage-pages.js、family-home-pages.js; - 标题、摘要、计数、管理入口、世系预览均有稳定
data-*hook; - 硬编码“四川武胜汤氏族”被删除;
- “邀请家人”不再是可点击业务入口,并明确提示“PC 暂未开放邀请”;
- 谱文、相册、视频、功德、祭祀、世系、动态入口继续携带
data-genealogy-context-link; - 初始化只并行调用:
Promise.all([
api.genealogyOverview(genealogyId),
api.lineageTree(genealogyId)
])
- Step 2: 运行 RED
Run:
node --test tests/family-home-pages.test.js tests/pending-pages.test.js tests/stage6-navigation.test.js
Expected: FAIL,主页仍为 pending 且包含硬编码家谱。
- Step 3: 实现只读初始化流程
async function initFamilyHomePage() {
var api = root.GenealogyApi && root.GenealogyApi.defaultClient;
var genealogyId = root.ProfileUI && root.ProfileUI.getGenealogyId();
var results;
var overview;
if (!genealogyId) {
root.location.replace('profile-families.html?next=profile-family-home.html');
return;
}
try {
results = await Promise.all([
api.genealogyOverview(genealogyId),
api.lineageTree(genealogyId)
]);
overview = normalizeFamilyOverview(results[0], genealogyId);
if (!overview) throw new Error('家谱概览响应无效');
renderFamilyOverview(overview);
renderManagementAccess(overview.canManage);
query('[data-lineage-home-tree]').innerHTML =
root.LineagePages.renderLineageTreeHtml(results[1]);
} catch (error) {
if (shouldRedirectToLogin(api, error)) return redirectToLogin(api);
renderFamilyHomeError(error);
}
}
页面只展示:
-
家谱名称、编号、姓氏、堂号、祖籍/地区;
-
memberCount、personCount; -
真实世系树及进入完整世系页的链接;
-
后端已经接入的内容模块入口。
-
Step 4: 运行 GREEN
Run:
node --test tests/family-home-pages.test.js tests/pending-pages.test.js tests/stage6-navigation.test.js
node --check public/js/family-home-pages.js
Expected: PASS。
Task 4: 规划记录与完整验收
Files:
- Modify:
docs/PC接口对接规划.md - Modify:
docs/superpowers/plans/2026-07-29-family-home.md
Interfaces:
-
Consumes: Task 1–3 的只读家谱主页闭环。
-
Step 1: 更新规划字段表
新增家谱主页小节,逐字段记录:
| 字段 | 分类 | 页面用途 |
|---|---|---|
genealogyId |
I/A | 当前上下文、两条请求 path、响应一致性校验 |
genealogyNo、genealogyName、surname |
R | 标题和基础信息 |
ancestralHall、originPlace、regionFullName |
R | 非空时展示 |
memberCount、personCount |
R | 非负只读计数 |
canManage、canEditContent |
I | 权限显隐,不作为用户输入 |
ownerUserId、coverOssId |
I | 当前主页不展示、不手填 |
LineagePersonTreeView.spouses/children |
R | 递归世系预览 |
同时记录:
-
/overview实际是详情别名,不包含内容聚合统计; -
/genealogy/dashboard/overview是后台权限接口,不进入 PC 前端; -
成员邀请没有 PC 接口,继续阻断。
-
Step 2: 聚焦验证
Run:
node --test tests/family-home-pages.test.js tests/lineage-pages.test.js tests/pending-pages.test.js tests/stage6-navigation.test.js tests/api-client-contract.test.js
node --check public/js/family-home-pages.js
node --check public/js/lineage-pages.js
Expected: 全部 PASS。
- Step 3: 全量和差异验证
Run:
npm test
git -c safe.directory=D:/WorkSpace/Web/jiapu diff --check
Expected: 0 failed,差异检查无错误。
- Step 4: 浏览器真实只读验证
使用真实登录态验证:
- 无家谱上下文时只跳转选择页,不发家谱业务请求;
- 有真实家谱时标题、概览计数和世系树来自 PC 响应;
- 普通成员看不到管理按钮,管理者可见;
- 所有入口透传同一
genealogyId; - 空世系、403、404、网络错误都有明确页面状态;
- 控制台无错误;
- 不创建或修改任何真实数据。