8.2 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: 开放家谱申请加入、我的申请、撤销待审核申请,以及家谱管理员审核申请的完整 PC 页面闭环。
Architecture: utils/ApiClient.js 已拥有本批全部 PC path/body。新增 join-pages.js 负责可申请家谱选择、申请表单、我的申请和撤销;新增 join-review-pages.js 负责当前家谱待审核列表和通过/拒绝。两个 UMD 模块只使用响应中的字符串 ID,写入后重读对应列表。
Tech Stack: 静态 HTML、原生 JavaScript UMD、Axios、Node node:test。
Global Constraints
- 后端
D:\WorkSpace\Java\Genealogy全程只读。 - 只调用
/genealogy/pc/**。 - 家谱 ID 和申请 ID 必须来自 PC 响应或当前家谱上下文,不能手填。
inviterUserId没有安全 PC 来源,不展示、不发送。- 所有写操作防重复,401 清登录态,403 保留登录态。
- 每项生产行为先运行失败测试,再最小实现。
Task 1: 申请数据边界
Files:
- Create:
tests/join-pages.test.js - Create:
public/js/join-pages.js
Interfaces:
-
Consumes:
genealogyOptions(query)、applyToGenealogy(genealogyId, body)、myGenealogyJoinApplies()、cancelGenealogyJoinApply(applyId) -
Produces:
normalizeJoinGenealogy(item)buildJoinApplyBody(values)validateJoinApplyBody(body)normalizeJoinApply(item)normalizeJoinApplies(data)renderJoinGenealogyOptions(data)renderMyJoinApplies(data)
-
Step 1: 写失败的纯行为测试
用完整 AppGenealogyVo 和 GenealogyJoinApplyVo 字面量验证:
assert.deepEqual(JoinPages.buildJoinApplyBody({
applicantName: ' 叶子 ',
phone: ' <测试联系电话> ',
relationDesc: ' 族亲 ',
applyReason: ' 申请加入 ',
inviterUserId: 'must-drop'
}), {
applicantName: '叶子',
phone: '<测试联系电话>',
relationDesc: '族亲',
applyReason: '申请加入'
});
断言:
-
applicantName <= 50、phone <= 30、relationDesc <= 100、applyReason <= 500。 -
可申请家谱必须有稳定
genealogyId/genealogyName/surname,停用家谱拒绝。 -
申请必须有稳定
applyId/genealogyId和status;状态只允许0/1/2/3。 -
安全数字长 ID 只接受字符串;不安全 number 拒绝。
-
普通用户列表不展示内部用户 ID、邀请人 ID、审核人 ID或原始 JSON。
-
只有
status=0渲染撤销按钮。 -
Step 2: 运行 RED
Run:
node --test tests/join-pages.test.js
Expected: FAIL,模块不存在。
- Step 3: 最小实现纯函数
所有可见文本使用 escapeHtml;响应任一必需字段无效时拒绝该条,数组包含非法元素时整批返回空数组。
- Step 4: 运行 GREEN
Run:
node --test tests/join-pages.test.js
node --check public/js/join-pages.js
Expected: PASS。
Task 2: 申请页面与我的申请
Files:
- Modify:
join-genealogy.html - Modify:
profile-join-family.html - Modify:
public/js/join-pages.js - Modify:
tests/join-pages.test.js - Modify:
tests/pending-pages.test.js - Modify:
tests/pc-scope.test.js
Interfaces:
-
Produces:
initJoinApplyPage()、initMyJoinAppliesPage() -
Step 1: 写失败的页面行为测试
断言:
-
两页均不再 pending,并加载
join-pages.js。 -
join-genealogy.html有关键词搜索、后端家谱选项、申请资料表单;不存在genealogyId/inviterUserId/applyId文本输入。 -
profile-join-family.html有我的申请列表和刷新入口;申请 ID 只存在于响应渲染按钮属性。 -
页面没有邀请码输入或“分享码”伪语义。
-
Step 2: 运行 RED
Run:
node --test tests/join-pages.test.js tests/pending-pages.test.js tests/pc-scope.test.js
Expected: FAIL,页面仍 pending/缺少真实行为。
- Step 3: 实现申请和撤销
join-genealogy.html:
- 读取
genealogyOptions({keyword})。 - 用户只能点击响应卡片选择家谱,内部保存字符串
genealogyId。 - 构造并校验申请 body。
- 提交后重读
myGenealogyJoinApplies(),必须找到同一响应applyId。 - 跳转
profile-join-family.html。
profile-join-family.html:
- 读取并渲染我的申请。
- 只有待审核申请显示撤销。
- 二次确认后调用撤销接口并重读列表,确保该申请不再为待审核。
- Step 4: 运行 GREEN
Run:
node --test tests/join-pages.test.js tests/pending-pages.test.js tests/pc-scope.test.js
node --check public/js/join-pages.js
Expected: PASS。
Task 3: 管理员审核
Files:
- Create:
tests/join-review-pages.test.js - Create:
public/js/join-review-pages.js - Modify:
profile-join-review.html - Modify:
tests/pending-pages.test.js - Modify:
tests/pc-scope.test.js
Interfaces:
-
Consumes:
genealogyDetail(genealogyId)、pendingGenealogyJoinApplies(genealogyId)、auditGenealogyJoinApply(genealogyId, applyId, body) -
Produces:
getCurrentGenealogyId(search)buildJoinAuditBody(values)validateJoinAuditBody(body)renderPendingJoinApplies(data, canManage)initJoinReviewPage()
-
Step 1: 写失败的审核行为测试
assert.deepEqual(JoinReviewPages.buildJoinAuditBody({
status: '2',
auditRemark: ' 资料不一致 ',
applyId: 'must-drop'
}), {
status: '2',
auditRemark: '资料不一致'
});
断言:
-
status只允许1/2,auditRemark <= 500。 -
页面只从 URL/
ProfileUI读取家谱 ID。 -
只有
genealogyDetail.canManage=true显示审核动作。 -
待审核列表显示申请人名称、申请手机号、关系和原因,但隐藏内部用户 ID、邀请人/审核人 ID。
-
通过和拒绝按钮使用响应中的稳定
applyId。 -
Step 2: 运行 RED
Run:
node --test tests/join-review-pages.test.js
Expected: FAIL,模块不存在且页面仍 pending。
- Step 3: 实现审核页面
加载当前家谱详情和待审核列表;没有上下文时阻止请求并返回家谱选择入口。点击通过使用 {status:'1'};拒绝要求输入可选审核说明并使用 {status:'2', auditRemark}。审核成功后重读待审核列表,确保同一 applyId 不再存在。
- Step 4: 运行 GREEN
Run:
node --test tests/join-review-pages.test.js tests/pending-pages.test.js tests/pc-scope.test.js
node --check public/js/join-review-pages.js
Expected: PASS。
Task 4: 导航、规划与验证
Files:
-
Modify:
profile-families.html -
Modify:
profile-family-admin.html -
Modify:
profile.html -
Modify:
tests/stage6-navigation.test.js -
Modify:
docs/PC接口对接规划.md -
Modify:
docs/superpowers/plans/2026-07-29-genealogy-join-review.md -
Step 1: 写失败的导航测试
断言我的家谱入口可达申请页,家谱管理入口携带上下文进入审核页;不存在指向手填家谱 ID 或邀请码页面的入口。
- Step 2: 运行 RED
Run:
node --test tests/stage6-navigation.test.js tests/pending-pages.test.js
Expected: FAIL,审核导航尚未稳定携带家谱上下文。
- Step 3: 开放导航并更新规划
记录申请/审核 DTO、VO、权限、状态、字段来源、写后重读和 inviterUserId 阻断。
- Step 4: 聚焦和全量验证
Run:
node --test tests/join-pages.test.js tests/join-review-pages.test.js tests/api-client-contract.test.js tests/pending-pages.test.js tests/pc-scope.test.js tests/stage6-navigation.test.js
npm test
node --check public/js/join-pages.js
node --check public/js/join-review-pages.js
git -c safe.directory=D:/WorkSpace/Web/jiapu diff --check
Expected: 全部 PASS。
- Step 5: 浏览器验证并报告
真实账号验证可申请家谱列表、我的申请空状态、无家谱上下文审核分支、隐私和控制台错误。未经用户明确授权,不提交或审核真实申请。