Files
jiapu/docs/superpowers/plans/2026-07-29-family-home.md
T
fizzleaf 309598bfa6 feat(api): 添加帮助中心反馈系统和VIP服务功能
- 在ApiClient中新增submitFeedback、myFeedback、helpArticles、helpArticleDetail、
  siteArticles、promotions、vipPackages、createVipOrder、vipOrders等方法
- 添加帮助文章和站点资讯的参数验证逻辑
- 更新测试文件添加新的API方法测试用例
- 在HTML页面中添加反馈、帮助和VIP服务相关页面的脚本引用
- 更新加入家谱页面为完整的申请流程界面
- 修改资讯详情页面为站点资讯展示页面
- 更新AxiosRequestUtil中认证处理逻辑
- 添加世系树渲染的HTML生成函数用于页面复用
- 更新文档中的API契约说明和页面规划
2026-07-30 16:04:48 +08:00

12 KiB
Raw Blame History

家谱主页 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'、负数/非安全计数均拒绝;

  • 渲染转义全部文本,不出现 ownerUserIdcoverOssId 或原始 JSON

  • 只有布尔值 canManage===true 才开放管理入口;

  • 401 清理登录态,403 不清理登录态。

  • Step 2: 运行 RED

Run: node --test tests/family-home-pages.test.js

Expected: FAILfamily-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, /&lt;始祖&gt;/);
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: FAILrenderLineageTreeHtml 未导出。

  • 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.jslineage-pages.jsfamily-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);
  }
}

页面只展示:

  • 家谱名称、编号、姓氏、堂号、祖籍/地区;

  • memberCountpersonCount

  • 真实世系树及进入完整世系页的链接;

  • 后端已经接入的内容模块入口。

  • 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 13 的只读家谱主页闭环。

  • Step 1: 更新规划字段表

新增家谱主页小节,逐字段记录:

字段 分类 页面用途
genealogyId I/A 当前上下文、两条请求 path、响应一致性校验
genealogyNogenealogyNamesurname R 标题和基础信息
ancestralHalloriginPlaceregionFullName R 非空时展示
memberCountpersonCount R 非负只读计数
canManagecanEditContent I 权限显隐,不作为用户输入
ownerUserIdcoverOssId 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: 浏览器真实只读验证

使用真实登录态验证:

  1. 无家谱上下文时只跳转选择页,不发家谱业务请求;
  2. 有真实家谱时标题、概览计数和世系树来自 PC 响应;
  3. 普通成员看不到管理按钮,管理者可见;
  4. 所有入口透传同一 genealogyId
  5. 空世系、403、404、网络错误都有明确页面状态;
  6. 控制台无错误;
  7. 不创建或修改任何真实数据。