# 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` - [ ] **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.