@@ -0,0 +1,762 @@
# PC API Replan 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: ** 按最新 `genealogy-pc-openapi.yaml` 重新整理 PC/H5 页面接口对接,只接入 `paths` 中真实存在的验证码、PC 认证、PC 文件和行政区划接口,并清除旧 APP 路径作为 PC 合同的风险。
**Architecture: ** 根目录 `config.js` 是接口基础地址、客户端 ID、租户、token key 的唯一配置入口;`utils` 只放跨页面公共 JS; `public/js/api-client.js` 只暴露 PC YAML `paths` 中存在的接口和受控的“PC 接口未提供”错误。页面业务 JS 继续放在 `public/js` ,页面只能调用 PC 已确认方法;PC YAML 未覆盖的页面保留静态/本地展示或显示接口待确认状态。
**Tech Stack: ** 静态 HTML、原生 JS、jQuery/Layui、Node 测试脚本、`public/tac` 天爱验证码静态资源。
## Global Constraints
- 接口依据只使用 `genealogy-pc-openapi.yaml` 的 `paths` 。
- 开发环境接口基础地址固定为 `https://test-genealogy-api.ddxcjp.cn` 。
- PC 默认 `clientid` 使用用户最新截图确认值:`ced7e5f0498645c6ec642dcf450b036f` ,客户端 Key 为 `web_pc` 。
- 登录后请求使用 `Authorization` header,PC 认证和文件请求同时携带 `clientid` header。
- 样式写入 CSS 文件,不用 JS 注入样式。
- 新增或修改的 HTML、JS、CSS 注释使用中文。
- `utils` 只放跨页面公共 JS;页面业务 JS 继续放 `public/js` 。
- PC YAML 没有明确接口的页面不套用 APP 路径,不把 schema/requestBodies 当成可调用接口。
- 验证码只使用后台已配置的 PC/H5 场景;当前明确场景为 `WEB_H5_LOGIN` 、`WEB_H5_REGISTER` 、`WEB_H5_FORGOT_PASSWORD` 。
- 换绑手机、注销账号的 PC/H5 验证码场景未确认,不套用 `APP_*` 场景。
---
## File Structure
- Create or Modify: `config.js`
- 唯一负责环境切换、开发接口地址、PC clientid、tenantId、token key、token header name。
- Create or Modify: `utils/StorageUtil.js`
- 负责 localStorage 安全读写。
- Create or Modify: `utils/FormUtil.js`
- 负责表单读取、空值清理、数字转换、布尔转换。
- Create or Modify: `utils/RequestUtil.js`
- 负责 URL 拼接、header 合并、FormData 判断、JSON 解包、业务错误对象。
- Modify: `public/js/api-client.js`
- 只把 PC YAML `paths` 中存在的接口作为真实请求方法;旧 APP 路径不得作为 fallback。
- Modify: `public/js/auth-pages.js`
- 登录使用 `WEB_H5_LOGIN` ;注册使用 `WEB_H5_REGISTER` ;找回密码使用 `WEB_H5_FORGOT_PASSWORD` 。
- Modify: `public/js/captcha-pages.js`
- 按 `/captcha/require` 、`/captcha/challenge` 、`/captcha/verify` 组织 TAC 调用。
- Modify: `public/js/profile-pages.js`
- 用户资料提交字段以 `ProfileUpdateBody` schema 为准:`provinceCode/cityCode/districtCode` 。
- Modify: `public/js/security-pages.js`
- 修改密码、换绑手机、注销账号只调用 PC 认证接口;涉及验证码的动作保留 PC/H5 场景待确认记录。
- Modify: `public/js/upload-pages.js`
- 上传、分片上传、文件引用只调用 `/genealogy/pc/files/*` 。
- Modify: `public/js/region-pages.js`
- 地区选择只调用 `/genealogy/region/*` 。
- Modify: related HTML files
- API 页面按顺序加载 `config.js` 、`utils/*.js` 、`public/js/api-client.js` 、页面业务脚本。
- Modify: `tests/*.test.js`
- 测试只验证 PC YAML 已确认接口;旧 APP 路径测试改为“不可作为 PC 合同”。
- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md`
- 每项开始/完成都回填时间、文件、接口、验证结果。
---
### Task 1: 配置入口与公共工具合同
**Files: **
- Create or Modify: `config.js`
- Create or Modify: `utils/StorageUtil.js`
- Create or Modify: `utils/FormUtil.js`
- Create or Modify: `utils/RequestUtil.js`
- Test: `tests/config.test.js`
- Test: `tests/utils.test.js`
**Interfaces: **
- Produces: `GenealogyConfig.getConfig(): { env, apiBaseUrl, clientId, tenantId, tokenKey, tokenHeaderName }`
- Produces: `GenealogyConfig.getApiBaseUrl(): string`
- Produces: `GenealogyConfig.getClientId(): string`
- Produces: `GenealogyConfig.getTenantId(): string`
- Produces: `GenealogyConfig.getTokenKey(): string`
- Produces: `StorageUtil.read(storage, key): string|null`
- Produces: `StorageUtil.write(storage, key, value): void`
- Produces: `StorageUtil.remove(storage, key): void`
- Produces: `FormUtil.trimOrUndefined(value): string|undefined`
- Produces: `FormUtil.toNumberOrUndefined(value): number|undefined`
- Produces: `FormUtil.toBoolean(value): boolean`
- Produces: `FormUtil.getFormValues(form): object`
- Produces: `RequestUtil.createRequester(options)(method, path, requestOptions): Promise<any>`
- [ ] **Step 1: Write config test **
Create or replace `tests/config.test.js` with:
``` js
const assert = require ( 'assert' ) ;
const configFactory = require ( '../config.js' ) ;
const store = { } ;
const config = configFactory ( {
localStorage : {
getItem : ( key ) => Object . prototype . hasOwnProperty . call ( store , key ) ? store [ key ] : null ,
setItem : ( key , value ) => { store [ key ] = String ( value ) ; }
}
} ) ;
assert . strictEqual ( config . getApiBaseUrl ( ) , 'https://test-genealogy-api.ddxcjp.cn' ) ;
assert . strictEqual ( config . getClientId ( ) , 'ced7e5f0498645c6ec642dcf450b036f' ) ;
assert . strictEqual ( config . getTenantId ( ) , '000000' ) ;
assert . strictEqual ( config . getTokenKey ( ) , 'genealogy_auth_token' ) ;
assert . strictEqual ( config . getConfig ( ) . tokenHeaderName , 'Authorization' ) ;
store . genealogy _api _base _url = 'https://custom.example.test' ;
assert . strictEqual ( config . getApiBaseUrl ( ) , 'https://custom.example.test' ) ;
console . log ( 'config tests passed' ) ;
```
- [ ] **Step 2: Run config test **
Run: `node tests\config.test.js`
Expected before implementation: FAIL if `config.js` is absent or lacks these methods.
Expected after implementation: PASS with `config tests passed` .
- [ ] **Step 3: Implement `config.js` **
Use this implementation:
``` js
( function ( root , factory ) {
// 全局配置同时支持浏览器页面和 Node 测试。
if ( typeof module === 'object' && module . exports ) {
module . exports = factory ;
return ;
}
root . GenealogyConfig = factory ( root ) ;
} ) ( typeof globalThis !== 'undefined' ? globalThis : window , function ( root ) {
'use strict' ;
var DEFAULT _ENV = 'development' ;
var API _BASE _URLS = {
development : 'https://test-genealogy-api.ddxcjp.cn' ,
production : ''
} ;
var CLIENT _ID = 'ced7e5f0498645c6ec642dcf450b036f' ;
var TENANT _ID = '000000' ;
var TOKEN _KEY = 'genealogy_auth_token' ;
var TOKEN _HEADER _NAME = 'Authorization' ;
var ENV _KEY = 'genealogy_env' ;
var BASE _URL _KEY = 'genealogy_api_base_url' ;
function readStorage ( key ) {
try {
return root . localStorage && root . localStorage . getItem ( key ) ;
} catch ( error ) {
return null ;
}
}
function getEnv ( ) {
return readStorage ( ENV _KEY ) || DEFAULT _ENV ;
}
function getApiBaseUrl ( ) {
return readStorage ( BASE _URL _KEY ) || API _BASE _URLS [ getEnv ( ) ] || API _BASE _URLS . development ;
}
function getConfig ( ) {
return {
env : getEnv ( ) ,
apiBaseUrl : getApiBaseUrl ( ) ,
clientId : CLIENT _ID ,
tenantId : TENANT _ID ,
tokenKey : TOKEN _KEY ,
tokenHeaderName : TOKEN _HEADER _NAME
} ;
}
return {
getConfig : getConfig ,
getApiBaseUrl : getApiBaseUrl ,
getClientId : function ( ) { return CLIENT _ID ; } ,
getTenantId : function ( ) { return TENANT _ID ; } ,
getTokenKey : function ( ) { return TOKEN _KEY ; } ,
getTokenHeaderName : function ( ) { return TOKEN _HEADER _NAME ; }
} ;
} ) ;
```
- [ ] **Step 4: Write utilities test **
Create or replace `tests/utils.test.js` with focused utility assertions:
``` js
const assert = require ( 'assert' ) ;
const StorageUtil = require ( '../utils/StorageUtil.js' ) ;
const FormUtil = require ( '../utils/FormUtil.js' ) ;
const RequestUtil = require ( '../utils/RequestUtil.js' ) ;
assert . strictEqual ( FormUtil . trimOrUndefined ( ' 张三 ' ) , '张三' ) ;
assert . strictEqual ( FormUtil . trimOrUndefined ( ' ' ) , undefined ) ;
assert . strictEqual ( FormUtil . toNumberOrUndefined ( '12' ) , 12 ) ;
assert . strictEqual ( FormUtil . toNumberOrUndefined ( '' ) , undefined ) ;
assert . strictEqual ( FormUtil . toBoolean ( 'on' ) , true ) ;
assert . strictEqual ( FormUtil . toBoolean ( '0' ) , false ) ;
const store = { } ;
const storage = {
getItem : ( key ) => Object . prototype . hasOwnProperty . call ( store , key ) ? store [ key ] : null ,
setItem : ( key , value ) => { store [ key ] = String ( value ) ; } ,
removeItem : ( key ) => { delete store [ key ] ; }
} ;
StorageUtil . write ( storage , 'token' , 'abc' ) ;
assert . strictEqual ( StorageUtil . read ( storage , 'token' ) , 'abc' ) ;
StorageUtil . remove ( storage , 'token' ) ;
assert . strictEqual ( StorageUtil . read ( storage , 'token' ) , null ) ;
const calls = [ ] ;
const requester = RequestUtil . createRequester ( {
baseUrl : 'https://test-genealogy-api.ddxcjp.cn' ,
clientId : 'pc-client' ,
tokenHeaderName : 'Authorization' ,
getToken : ( ) => 'token-x' ,
fetchImpl : async ( url , options ) => {
calls . push ( { url , options } ) ;
return {
ok : true ,
status : 200 ,
json : async ( ) => ( { code : 200 , data : { ok : true } } )
} ;
}
} ) ;
requester ( 'GET' , '/genealogy/pc/auth/profile' , {
query : { keyword : '汤 氏' }
} ) . then ( ( data ) => {
assert . deepStrictEqual ( data , { ok : true } ) ;
assert . strictEqual ( calls [ 0 ] . url , 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/auth/profile?keyword=%E6%B1%A4+%E6%B0%8F' ) ;
assert . strictEqual ( calls [ 0 ] . options . headers . clientid , 'pc-client' ) ;
assert . strictEqual ( calls [ 0 ] . options . headers . Authorization , 'Bearer token-x' ) ;
console . log ( 'utils tests passed' ) ;
} ) ;
```
- [ ] **Step 5: Implement utility files **
Implement the utilities with Chinese comments around storage try/catch, FormData detection, JSON response parsing and `code !== 200` error creation. `RequestUtil.createRequester` must omit `Authorization` when `requestOptions.auth === false` .
- [ ] **Step 6: Run utility tests **
Run: `node tests\utils.test.js`
Expected: PASS with `utils tests passed` .
---
### Task 2: API 客户端收紧到 PC YAML `paths`
**Files: **
- Modify: `public/js/api-client.js`
- Test: `tests/api-client.test.js`
**Interfaces: **
- Consumes: `GenealogyConfig`
- Consumes: `StorageUtil`
- Consumes: `RequestUtil.createRequester`
- Produces: `GenealogyApi.createClient(options)`
- Produces: `GenealogyApi.defaultClient`
- Produces PC methods:
- `login(body)` , `register(body)` , `loginBySms(body)` , `sendSmsCode(body)`
- `getProfile()` , `updateProfile(body)` , `changePassword(body)` , `resetPassword(body)` , `changePhone(body)` , `deactivateAccount(body)` , `logout()`
- `uploadFile(formData)` , `initResumableUpload(body)` , `uploadChunk(formData)` , `completeResumableUpload(body)` , `bindFileReference(body)` , `releaseFileReference(query)`
- `getRegionChildren(parentCode)` , `getRegionPath(regionCode)` , `searchRegions(query)` , `getRegion(regionCode)`
- `captchaRequire(query)` , `captchaChallenge(body)` , `captchaVerify(body)` , `legacyCaptcha()`
- Produces unsupported method behavior: methods for PC YAML 未覆盖业务 throw `Error` with `code === 'PC_API_NOT_AVAILABLE'` and no network request.
- [ ] **Step 1: Replace API client test **
Rewrite `tests/api-client.test.js` so it verifies only PC-confirmed paths and unsupported APP-era methods:
``` js
const assert = require ( 'assert' ) ;
const GenealogyApi = require ( '../public/js/api-client.js' ) ;
const calls = [ ] ;
const client = GenealogyApi . createClient ( {
baseUrl : 'https://test-genealogy-api.ddxcjp.cn' ,
clientId : 'client-x' ,
tenantId : 'tenant-x' ,
tokenKey : 'token-key' ,
tokenHeaderName : 'Authorization' ,
storage : { getItem : ( ) => 'abc-token' } ,
fetchImpl : async ( url , options ) => {
calls . push ( { url , options } ) ;
return {
ok : true ,
status : 200 ,
json : async ( ) => ( { code : 200 , data : { url } } )
} ;
}
} ) ;
( async ( ) => {
await client . login ( { tenantId : 'tenant-x' , phone : '13800000000' , password : 'md5' } ) ;
assert . strictEqual ( calls [ 0 ] . url , 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/auth/login' ) ;
assert . strictEqual ( calls [ 0 ] . options . headers . clientid , 'client-x' ) ;
assert . strictEqual ( calls [ 0 ] . options . headers . Authorization , undefined ) ;
await client . getProfile ( ) ;
assert . strictEqual ( calls [ 1 ] . url , 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/auth/profile' ) ;
assert . strictEqual ( calls [ 1 ] . options . headers . Authorization , 'Bearer abc-token' ) ;
await client . updateProfile ( { nickName : '张三' , provinceCode : '510000' , cityCode : '510100' , districtCode : '510104' } ) ;
assert . strictEqual ( calls [ 2 ] . url , 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/auth/profile' ) ;
assert . strictEqual ( calls [ 2 ] . options . method , 'PUT' ) ;
await client . uploadFile ( new FormData ( ) ) ;
assert . strictEqual ( calls [ 3 ] . url , 'https://test-genealogy-api.ddxcjp.cn/genealogy/pc/files/upload' ) ;
await client . getRegionChildren ( '51' ) ;
assert . strictEqual ( calls [ 4 ] . url , 'https://test-genealogy-api.ddxcjp.cn/genealogy/region/children?parentCode=51' ) ;
await client . captchaRequire ( { sceneCode : 'WEB_H5_LOGIN' , subject : '13800000000' } ) ;
assert . strictEqual ( calls [ 5 ] . url , 'https://test-genealogy-api.ddxcjp.cn/captcha/require?tenantId=tenant-x&clientId=client-x&sceneCode=WEB_H5_LOGIN&subject=13800000000' ) ;
assert . strictEqual ( calls [ 5 ] . options . headers . Authorization , undefined ) ;
assert . throws (
( ) => client . listGenealogies ( ) ,
( error ) => error . code === 'PC_API_NOT_AVAILABLE'
) ;
assert . strictEqual ( calls . length , 6 ) ;
console . log ( 'api-client tests passed' ) ;
} ) ( ) ;
```
- [ ] **Step 2: Run API client test **
Run: `node tests\api-client.test.js`
Expected before implementation: FAIL while old APP paths or old scene tests remain.
Expected after implementation: PASS with `api-client tests passed` .
- [ ] **Step 3: Implement confirmed PC methods **
In `public/js/api-client.js` , remove APP auth/files paths. Map methods exactly:
``` js
login : function ( body ) {
return request ( 'POST' , '/genealogy/pc/auth/login' , { body : body , auth : false } ) ;
} ,
register : function ( body ) {
return request ( 'POST' , '/genealogy/pc/auth/register' , { body : body , auth : false } ) ;
} ,
sendSmsCode : function ( body ) {
return request ( 'POST' , '/genealogy/pc/auth/sms/code' , { body : body , auth : false } ) ;
} ,
loginBySms : function ( body ) {
return request ( 'POST' , '/genealogy/pc/auth/login/sms' , { body : body , auth : false } ) ;
} ,
getProfile : function ( ) {
return request ( 'GET' , '/genealogy/pc/auth/profile' ) ;
} ,
updateProfile : function ( body ) {
return request ( 'PUT' , '/genealogy/pc/auth/profile' , { body : body } ) ;
}
```
Add the remaining PC auth/files/region/captcha methods with the same direct mapping from YAML `paths` .
- [ ] **Step 4: Implement unsupported method guard **
For old business methods whose PC paths do not exist, keep the method name only if current pages call it, but route it to:
``` js
function createUnavailableMethod ( name ) {
return function ( ) {
var error = new Error ( 'PC 接口文档未提供该业务接口:' + name ) ;
error . code = 'PC_API_NOT_AVAILABLE' ;
throw error ;
} ;
}
```
Use a Chinese comment above the mapping: `// PC YAML 未提供这些业务 paths,保留方法名只用于阻止旧 APP 路径继续发请求。`
- [ ] **Step 5: Verify no APP auth/files network paths remain **
Run: `Select-String -Path public\js\api-client.js -Pattern '/genealogy/app/auth|/genealogy/app/files'`
Expected: no matches.
- [ ] **Step 6: Run API client test again **
Run: `node tests\api-client.test.js`
Expected: PASS with `api-client tests passed` .
---
### Task 3: 登录验证码与 `public/tac` 调用
**Files: **
- Modify: `public/js/auth-pages.js`
- Modify: `public/js/captcha-pages.js`
- Modify: `login.html`
- Modify: `register.html`
- Modify: `forgot-password.html`
- Test: `tests/auth-pages.test.js`
- Test: `tests/captcha-pages.test.js`
**Interfaces: **
- Consumes: `public/tac/css/tac.css`
- Consumes: `public/tac/js/tac.min.js`
- Consumes: `GenealogyApi.defaultClient.captchaRequire/challenge/verify`
- Produces: `AuthPages.getCaptchaScene('login') === 'WEB_H5_LOGIN'`
- Produces: `AuthPages.getCaptchaScene('register') === 'WEB_H5_REGISTER'`
- Produces: `AuthPages.getCaptchaScene('password-reset') === 'WEB_H5_FORGOT_PASSWORD'`
- Produces: `CaptchaPages.buildChallengeBody(options, api)`
- Produces: `CaptchaPages.buildVerifyBody(options, api)`
- Produces: `CaptchaPages.ensureToken(form, options)`
- [ ] **Step 1: Replace auth scene tests **
In `tests/auth-pages.test.js` , require these expectations:
``` js
assert . strictEqual ( AuthPages . getCaptchaScene ( 'login' ) , 'WEB_H5_LOGIN' ) ;
assert . strictEqual ( AuthPages . getCaptchaScene ( 'register' ) , 'WEB_H5_REGISTER' ) ;
assert . strictEqual ( AuthPages . getCaptchaScene ( 'password-reset' ) , 'WEB_H5_FORGOT_PASSWORD' ) ;
```
- [ ] **Step 2: Replace captcha tests **
In `tests/captcha-pages.test.js` , all challenge/verify body tests must use `WEB_H5_LOGIN` :
``` js
assert . deepStrictEqual ( CaptchaPages . buildChallengeBody ( {
sceneCode : 'WEB_H5_LOGIN' ,
subject : '13800000000'
} , fakeApi ) , {
tenantId : 'tenant-x' ,
clientId : 'client-x' ,
sceneCode : 'WEB_H5_LOGIN' ,
subject : '13800000000'
} ) ;
```
- [ ] **Step 3: Run auth and captcha tests **
Run:
``` powershell
node tests \ auth-pages . test . js
node tests \ captcha-pages . test . js
```
Expected before implementation: FAIL if `PC_LOGIN` or `PC_REGISTER` remains.
Expected after implementation: PASS.
- [ ] **Step 4: Implement scene mapping **
In `public/js/auth-pages.js` :
``` js
function getCaptchaScene ( formType ) {
// PC/H5 认证场景来自后台验证码配置,客户端 Key 为 web_pc。
var map = {
login : 'WEB_H5_LOGIN' ,
register : 'WEB_H5_REGISTER' ,
'password-reset' : 'WEB_H5_FORGOT_PASSWORD'
} ;
return map [ formType ] || '' ;
}
```
If `sceneCode` is empty, `ensureCaptcha` must return `true` without calling `/captcha/require` and must not fabricate `validToken` .
- [ ] **Step 5: Verify TAC assets in auth pages **
Run:
``` powershell
Select-String -Path login . html , register . html , forgot-password . html -Pattern 'public/tac/css/tac.css|public/tac/js/tac.min.js|public/js/captcha-pages.js|name="validToken"|data-captcha-box'
```
Expected:
- `login.html` has TAC CSS, TAC JS, `captcha-pages.js` , hidden `validToken` , and `[data-captcha-box]` .
- `register.html` and `forgot-password.html` may keep TAC assets and hidden fields, but no PC/H5 scene is configured until backend confirms scene code.
---
### Task 4: 认证与资料页面只接 PC auth schema
**Files: **
- Modify: `public/js/auth-pages.js`
- Modify: `public/js/profile-pages.js`
- Modify: `public/js/security-pages.js`
- Modify: `login.html`
- Modify: `register.html`
- Modify: `forgot-password.html`
- Modify: `profile.html`
- Modify: `profile-data.html`
- Modify: `profile-security.html`
- Test: `tests/auth-pages.test.js`
- Test: `tests/profile-pages.test.js`
- Test: `tests/security-pages.test.js`
**Interfaces: **
- Consumes: `GenealogyApi.defaultClient`
- Produces request bodies:
- `PasswordLoginBody` : `grantType/tenantId/phone/password/validToken`
- `PasswordRegisterBody` : `grantType/tenantId/phone/password/nickName/registerSource/validToken`
- `PasswordResetBody` : `tenantId/phone/smsCode/newPassword/validToken`
- `ProfileUpdateBody` : `nickName/avatarOssId/sex/birthday/provinceCode/cityCode/districtCode`
- [ ] **Step 1: Update body-builder tests **
`tests/auth-pages.test.js` must assert `registerSource: 'PC'` for register body and no `APP_*` scene in any body.
`tests/profile-pages.test.js` must assert profile update uses `provinceCode/cityCode/districtCode` , not `regionCode/addressDetail` .
- [ ] **Step 2: Run focused tests **
Run:
``` powershell
node tests \ auth-pages . test . js
node tests \ profile-pages . test . js
node tests \ security-pages . test . js
```
Expected before implementation: FAIL where old scene/body fields remain.
Expected after implementation: PASS.
- [ ] **Step 3: Implement auth body builders **
Use PC YAML schema fields only. For register:
``` js
function buildRegisterBody ( values , config ) {
var body = {
grantType : 'password' ,
tenantId : config . tenantId ,
phone : values . phone ,
password : values . password ,
registerSource : 'PC'
} ;
if ( values . nickName ) body . nickName = values . nickName ;
if ( values . validToken ) body . validToken = values . validToken ;
return body ;
}
```
- [ ] **Step 4: Implement profile body builder **
Use schema fields:
``` js
function buildProfileUpdateBody ( values ) {
return removeEmpty ( {
nickName : values . nickName ,
avatarOssId : toNumberOrUndefined ( values . avatarOssId ) ,
sex : values . sex ,
birthday : values . birthday ,
provinceCode : values . provinceCode ,
cityCode : values . cityCode ,
districtCode : values . districtCode
} ) ;
}
```
- [ ] **Step 5: Verify page script order **
Run:
``` powershell
Select-String -Path login . html , register . html , forgot-password . html , profile . html , profile-data . html , profile-security . html -Pattern 'config.js|utils/StorageUtil.js|utils/FormUtil.js|utils/RequestUtil.js|public/js/api-client.js'
```
Expected: every page that uses `GenealogyApi` loads config and utils before `api-client.js` .
---
### Task 5: 文件上传和文件引用只切 PC files
**Files: **
- Modify: `public/js/upload-pages.js`
- Modify: upload-related page scripts that call `GenealogyApi.defaultClient.uploadFile`
- Test: `tests/upload-pages.test.js`
- Test: `tests/api-client.test.js`
**Interfaces: **
- Consumes: `GenealogyApi.defaultClient.uploadFile(formData)`
- Consumes: `GenealogyApi.defaultClient.initResumableUpload(body)`
- Consumes: `GenealogyApi.defaultClient.uploadChunk(formData)`
- Consumes: `GenealogyApi.defaultClient.completeResumableUpload(body)`
- Consumes: `GenealogyApi.defaultClient.bindFileReference(body)`
- Consumes: `GenealogyApi.defaultClient.releaseFileReference(query)`
- [ ] **Step 1: Add upload tests **
`tests/upload-pages.test.js` must verify:
- single upload calls `/genealogy/pc/files/upload`
- resumable init calls `/genealogy/pc/files/resumable/init`
- file reference body requires `bizType/bizTable/bizId/bizField`
- [ ] **Step 2: Run upload tests **
Run: `node tests\upload-pages.test.js`
Expected before implementation: FAIL if old APP file paths remain.
Expected after implementation: PASS.
- [ ] **Step 3: Implement upload mapping **
Only map file capability. Do not submit article/feed/album/ceremony/growth/memo business forms unless PC YAML provides their business endpoints.
- [ ] **Step 4: Verify no APP file path remains **
Run: `Select-String -Path public\js\api-client.js,public\js\upload-pages.js -Pattern '/genealogy/app/files'`
Expected: no matches.
---
### Task 6: 行政区划接口和资料地区字段
**Files: **
- Modify: `public/js/region-pages.js`
- Modify: `public/js/profile-pages.js`
- Test: `tests/region-pages.test.js`
- Test: `tests/profile-pages.test.js`
**Interfaces: **
- Consumes: `GET /genealogy/region/children`
- Consumes: `GET /genealogy/region/path/{regionCode}`
- Consumes: `GET /genealogy/region/search`
- Consumes: `GET /genealogy/region/{regionCode}`
- Produces profile fields: `provinceCode/cityCode/districtCode`
- [ ] **Step 1: Add region tests **
`tests/region-pages.test.js` must assert:
- empty parent loads province list with no required parent code.
- search requires `keyword` .
- selected province/city/district writes `provinceCode/cityCode/districtCode` to the form.
- [ ] **Step 2: Run region tests **
Run: `node tests\region-pages.test.js`
Expected before implementation: FAIL if form still expects `regionCode/addressDetail` .
Expected after implementation: PASS.
- [ ] **Step 3: Implement region mapping **
Use `GenealogyApi.defaultClient.getRegionChildren` , `getRegionPath` , `searchRegions` , `getRegion` . Do not create genealogy submit behavior here.
---
### Task 7: PC YAML 未覆盖页面降级为接口待确认
**Files: **
- Modify: page scripts that currently call APP-era business methods:
- `public/js/genealogy-pages.js`
- `public/js/join-apply-pages.js`
- `public/js/member-admin-pages.js`
- `public/js/article-pages.js`
- `public/js/feed-pages.js`
- `public/js/album-pages.js`
- `public/js/ceremony-pages.js`
- `public/js/growth-pages.js`
- `public/js/memo-pages.js`
- `public/js/generation-pages.js`
- `public/js/lineage-pages.js`
- `public/js/notification-pages.js`
- `public/js/help-pages.js`
- `public/js/promotion-pages.js`
- `public/js/feedback-pages.js`
- `public/js/vip-pages.js`
- Test: `tests/unsupported-pages.test.js`
**Interfaces: **
- Consumes: `PC_API_NOT_AVAILABLE` errors from `GenealogyApi`
- Produces: visible or console-safe page state that says PC interface is not configured, without issuing APP requests.
- [ ] **Step 1: Add unsupported-page smoke test **
Create `tests/unsupported-pages.test.js` to load each page script in Node with a fake client method that throws `{ code: 'PC_API_NOT_AVAILABLE' }` . Assert each script handles the error without rethrowing from its init function.
- [ ] **Step 2: Run unsupported-page test **
Run: `node tests\unsupported-pages.test.js`
Expected before implementation: FAIL for scripts that assume APP methods are valid.
Expected after implementation: PASS.
- [ ] **Step 3: Implement shared page fallback **
In each affected page script, catch `PC_API_NOT_AVAILABLE` and render a small existing-style empty state. Add a Chinese comment: `// PC YAML 未提供该业务接口,页面先展示接口待确认状态。`
- [ ] **Step 4: Verify no APP business path is emitted from API client **
Run: `Select-String -Path public\js\api-client.js -Pattern '/genealogy/app/'`
Expected: no network path matches.
---
### Task 8: 追踪表和执行记录
**Files: **
- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md`
**Interfaces: **
- Consumes: Task 1-7 verification results.
- Produces: updated status rows with exact timestamps, changed files, interface paths and verification commands.
- [ ] **Step 1: Mark task start before edits **
Before executing each PC task, update the row status from `待开始` to `进行中` with exact time from:
``` powershell
Get-Date -Format " yyyy-MM-dd HH:mm:ss zzz "
```
- [ ] **Step 2: Mark task completion after verification **
After the task's verification commands pass, update status to `已完成` and record:
- completion time
- modified files
- interface paths
- verification commands
- verification result
- [ ] **Step 3: Keep missing interfaces in coverage matrix **
Any page whose main business path is absent from `genealogy-pc-openapi.yaml` remains in `PC YAML 未覆盖` or `接口待确认` and must not be promoted to full integration.
---
## Verification Gate
Run these before marking the replan execution complete:
``` powershell
node tests \ config . test . js
node tests \ utils . test . js
node tests \ api-client . test . js
node tests \ auth-pages . test . js
node tests \ captcha-pages . test . js
node tests \ profile-pages . test . js
node tests \ security-pages . test . js
node tests \ upload-pages . test . js
node tests \ region-pages . test . js
node tests \ unsupported-pages . test . js
Get-ChildItem -Path public \ js -Filter * . js | Where-Object { $_ . Name -notin @ ( 'jquery360.js' , 'jquery.min.js' ) } | Sort-Object Name | ForEach-Object { node - -check $_ . FullName }
Select-String -Path public \ js \ api-client . js -Pattern '/genealogy/app/'
Select-String -Path public \ js \ auth-pages . js , public \ js \ captcha-pages . js , tests \ auth-pages . test . js , tests \ captcha-pages . test . js -Pattern 'PC_LOGIN|PC_REGISTER|PC_PASSWORD_RESET|APP_LOGIN|APP_REGISTER|APP_FORGOT_PASSWORD'
git diff - -check
```
Expected:
- All listed Node tests pass.
- Business JS syntax check passes.
- `api-client.js` contains no `/genealogy/app/` network path.
- Auth and captcha code/tests contain no stale APP/old PC scene defaults.
- `git diff --check` passes, or if this directory is not a Git repository, record the exact output.
## Self-Review
- Spec coverage: this plan covers latest PC YAML `paths` , test domain, config.js, utils/public-js separation, CSS/no style injection, Chinese comments, TAC login scene, profile schema mismatch, and PC-missing business interfaces.
- Placeholder scan: no implementation task asks the worker to invent missing PC endpoints or use APP paths as compatibility.
- Type consistency: `GenealogyConfig` , `StorageUtil` , `FormUtil` , `RequestUtil` , `GenealogyApi.createClient` , `AuthPages.getCaptchaScene` , and `CaptchaPages.ensureToken` names are consistent across tasks.