diff --git a/PC.openapi.yaml b/PC.openapi.yaml new file mode 100644 index 0000000..47117db --- /dev/null +++ b/PC.openapi.yaml @@ -0,0 +1,883 @@ +# 已废弃:本文件保留仅用于追溯此前的账号闭环方案。 +# 正式接口源为 PC.openapi2.json;代码、测试和后续开发不得再以本文件为依据。 +openapi: 3.0.3 +info: + title: 代代相传 PC 用户端 API + version: 1.1.0 + description: | + 本文档是当前 PC 用户端唯一接口契约。 + 范围仅包含账号认证、个人资料、安全设置、头像上传、验证码与行政区划。 + 家谱、社区、内容、会员、支付及平台运营能力不在本期范围内。 +servers: + - url: https://{host} + variables: + host: + default: api.example.com + description: 由部署环境配置的 HTTPS API 域名 +tags: + - name: 认证登录 + - name: 验证中心 + - name: 文件上传 + - name: 行政区划 +paths: + /genealogy/pc/auth/register: + post: + tags: [认证登录] + summary: PC 用户注册 + security: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/RegisterRequest' + responses: + '200': + description: 注册结果;客户端不保存注册响应中的令牌 + content: + application/json: + schema: + $ref: '#/components/schemas/LoginResponse' + /genealogy/pc/auth/login: + post: + tags: [认证登录] + summary: PC 密码登录 + security: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PasswordLoginRequest' + responses: + '200': + description: 登录成功并返回令牌 + content: + application/json: + schema: + $ref: '#/components/schemas/LoginResponse' + /genealogy/pc/auth/login/sms: + post: + tags: [认证登录] + summary: PC 短信登录 + security: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SmsLoginRequest' + responses: + '200': + description: 登录成功并返回令牌 + content: + application/json: + schema: + $ref: '#/components/schemas/LoginResponse' + /genealogy/pc/auth/sms/code: + post: + tags: [认证登录] + summary: 发送短信验证码 + security: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SmsCodeRequest' + responses: + '200': + description: 短信已受理 + content: + application/json: + schema: + $ref: '#/components/schemas/VoidResponse' + /genealogy/pc/auth/profile: + get: + tags: [认证登录] + summary: 获取当前用户资料 + security: + - BearerAuth: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + responses: + '200': + description: 当前登录用户资料 + content: + application/json: + schema: + $ref: '#/components/schemas/ProfileResponse' + put: + tags: [认证登录] + summary: 更新当前用户资料 + security: + - BearerAuth: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ProfileUpdateRequest' + responses: + '200': + description: 更新后的用户资料 + content: + application/json: + schema: + $ref: '#/components/schemas/ProfileResponse' + /genealogy/pc/auth/password: + put: + tags: [认证登录] + summary: 修改当前用户密码 + security: + - BearerAuth: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PasswordChangeRequest' + responses: + '200': + description: 密码修改成功 + content: + application/json: + schema: + $ref: '#/components/schemas/VoidResponse' + /genealogy/pc/auth/password/reset: + put: + tags: [认证登录] + summary: 找回密码 + security: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PasswordResetRequest' + responses: + '200': + description: 密码重置成功 + content: + application/json: + schema: + $ref: '#/components/schemas/VoidResponse' + /genealogy/pc/auth/phone: + put: + tags: [认证登录] + summary: 换绑手机号 + security: + - BearerAuth: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PhoneChangeRequest' + responses: + '200': + description: 换绑后的用户资料 + content: + application/json: + schema: + $ref: '#/components/schemas/ProfileResponse' + /genealogy/pc/auth/account/deactivate: + post: + tags: [认证登录] + summary: 注销当前账号 + security: + - BearerAuth: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AccountDeactivateRequest' + responses: + '200': + description: 注销申请已受理 + content: + application/json: + schema: + $ref: '#/components/schemas/VoidResponse' + /genealogy/pc/auth/logout: + delete: + tags: [认证登录] + summary: 退出登录 + security: + - BearerAuth: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + responses: + '200': + description: 服务端会话已退出 + content: + application/json: + schema: + $ref: '#/components/schemas/VoidResponse' + /captcha/require: + get: + tags: [验证中心] + summary: 查询场景是否需要验证码 + security: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + - name: tenantId + in: query + schema: + type: string + default: '000000' + - name: clientId + in: query + schema: + type: string + - $ref: '#/components/parameters/SceneCode' + - name: subject + in: query + description: 手机号或其他待验证主体 + schema: + type: string + responses: + '200': + description: 当前场景验证码策略 + content: + application/json: + schema: + $ref: '#/components/schemas/CaptchaRequirementResponse' + /captcha/challenge: + post: + tags: [验证中心] + summary: 创建验证码挑战 + security: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CaptchaChallengeRequest' + responses: + '200': + description: 验证码挑战 + content: + application/json: + schema: + $ref: '#/components/schemas/CaptchaChallengeResponse' + /captcha/verify: + post: + tags: [验证中心] + summary: 校验验证码并换取业务票据 + security: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CaptchaVerifyRequest' + responses: + '200': + description: 返回可提交给后续业务接口的 validToken + content: + application/json: + schema: + $ref: '#/components/schemas/CaptchaVerifyResponse' + /genealogy/pc/files/upload: + post: + tags: [文件上传] + summary: 上传个人头像文件 + security: + - BearerAuth: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [file] + properties: + file: + type: string + format: binary + responses: + '200': + description: 上传结果 + content: + application/json: + schema: + $ref: '#/components/schemas/FileUploadResponse' + /genealogy/region/children: + get: + tags: [行政区划] + summary: 查询下级行政区划 + security: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + - name: parentCode + in: query + schema: + type: string + responses: + '200': + description: 下级地区列表 + content: + application/json: + schema: + $ref: '#/components/schemas/RegionListResponse' + /genealogy/region/path/{regionCode}: + get: + tags: [行政区划] + summary: 查询地区完整路径 + security: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + - $ref: '#/components/parameters/RegionCode' + responses: + '200': + description: 根节点到目标地区的路径 + content: + application/json: + schema: + $ref: '#/components/schemas/RegionListResponse' + /genealogy/region/search: + get: + tags: [行政区划] + summary: 搜索行政区划 + security: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + - name: keyword + in: query + required: true + schema: + type: string + - name: limit + in: query + schema: + type: integer + default: 20 + maximum: 50 + responses: + '200': + description: 匹配的地区列表 + content: + application/json: + schema: + $ref: '#/components/schemas/RegionListResponse' + /genealogy/region/{regionCode}: + get: + tags: [行政区划] + summary: 查询行政区划详情 + security: [] + parameters: + - $ref: '#/components/parameters/ClientIdHeader' + - $ref: '#/components/parameters/RegionCode' + responses: + '200': + description: 地区详情 + content: + application/json: + schema: + $ref: '#/components/schemas/RegionResponse' +components: + parameters: + ClientIdHeader: + name: clientid + in: header + required: true + description: PC 客户端标识 + schema: + type: string + example: ced7e5f0498645c6ec642dcf450b036f + RegionCode: + name: regionCode + in: path + required: true + schema: + type: string + example: '110101' + SceneCode: + name: sceneCode + in: query + required: true + schema: + $ref: '#/components/schemas/CaptchaSceneCode' + schemas: + CaptchaSceneCode: + type: string + enum: + - WEB_H5_LOGIN + - WEB_H5_REGISTER + - WEB_H5_FORGOT_PASSWORD + - WEB_H5_CHANGE_PASSWORD + - WEB_H5_CHANGE_PHONE + RegisterRequest: + type: object + required: [grantType, tenantId, phone, password] + properties: + grantType: + type: string + enum: [password] + tenantId: + type: string + example: '000000' + clientId: + type: string + phone: + type: string + pattern: '^1[3-9]\\d{9}$' + password: + type: string + description: 32 位 MD5 值;传输层必须使用 HTTPS + pattern: '^[a-fA-F0-9]{32}$' + nickName: + type: string + maxLength: 32 + registerSource: + type: string + enum: [PC] + validToken: + type: string + PasswordLoginRequest: + type: object + required: [grantType, tenantId, phone, password] + properties: + grantType: + type: string + enum: [password] + tenantId: + type: string + clientId: + type: string + phone: + type: string + pattern: '^1[3-9]\\d{9}$' + password: + type: string + pattern: '^[a-fA-F0-9]{32}$' + validToken: + type: string + SmsLoginRequest: + type: object + required: [grantType, tenantId, phone, smsCode] + properties: + grantType: + type: string + enum: [sms] + tenantId: + type: string + clientId: + type: string + phone: + type: string + pattern: '^1[3-9]\\d{9}$' + smsCode: + type: string + pattern: '^\\d{4,8}$' + validToken: + type: string + SmsCodeRequest: + type: object + required: [tenantId, phone, sceneCode] + properties: + tenantId: + type: string + clientId: + type: string + phone: + type: string + pattern: '^1[3-9]\\d{9}$' + sceneCode: + $ref: '#/components/schemas/CaptchaSceneCode' + validToken: + type: string + PasswordChangeRequest: + type: object + required: [oldPassword, newPassword] + properties: + oldPassword: + type: string + pattern: '^[a-fA-F0-9]{32}$' + newPassword: + type: string + pattern: '^[a-fA-F0-9]{32}$' + validToken: + type: string + PasswordResetRequest: + type: object + required: [tenantId, phone, smsCode, newPassword] + properties: + tenantId: + type: string + phone: + type: string + pattern: '^1[3-9]\\d{9}$' + smsCode: + type: string + pattern: '^\\d{4,8}$' + newPassword: + type: string + pattern: '^[a-fA-F0-9]{32}$' + validToken: + type: string + PhoneChangeRequest: + type: object + required: [newPhone, smsCode] + properties: + newPhone: + type: string + pattern: '^1[3-9]\\d{9}$' + smsCode: + type: string + pattern: '^\\d{4,8}$' + validToken: + type: string + AccountDeactivateRequest: + type: object + required: [password] + properties: + password: + type: string + pattern: '^[a-fA-F0-9]{32}$' + reason: + type: string + maxLength: 200 + ProfileUpdateRequest: + type: object + properties: + nickName: + type: string + maxLength: 32 + avatarOssId: + type: integer + format: int64 + sex: + type: string + enum: ['0', '1', '2'] + birthday: + type: string + format: date + provinceCode: + type: string + cityCode: + type: string + districtCode: + type: string + addressDetail: + type: string + maxLength: 100 + Profile: + type: object + required: [userId, phone] + properties: + userId: + type: integer + format: int64 + phone: + type: string + nickName: + type: string + avatarOssId: + type: integer + format: int64 + nullable: true + avatarUrl: + type: string + format: uri + nullable: true + sex: + type: string + nullable: true + birthday: + type: string + format: date + nullable: true + provinceCode: + type: string + nullable: true + cityCode: + type: string + nullable: true + districtCode: + type: string + nullable: true + addressDetail: + type: string + nullable: true + LoginData: + type: object + required: [accessToken, userId, tenantId, clientId] + properties: + accessToken: + type: string + userId: + type: integer + format: int64 + tenantId: + type: string + clientId: + type: string + FileUpload: + type: object + required: [ossId, url, fileName, originalName] + properties: + ossId: + type: integer + format: int64 + url: + type: string + format: uri + thumbnailUrl: + type: string + format: uri + nullable: true + fileName: + type: string + originalName: + type: string + Region: + type: object + required: [regionCode, regionName] + properties: + regionCode: + type: string + regionName: + type: string + parentCode: + type: string + nullable: true + level: + type: integer + minimum: 0 + CaptchaRequirement: + type: object + required: [required, sceneCode] + properties: + required: + type: boolean + sceneCode: + $ref: '#/components/schemas/CaptchaSceneCode' + providerCode: + type: string + nullable: true + captchaType: + type: string + nullable: true + CaptchaChallengeRequest: + type: object + required: [tenantId, clientId, sceneCode] + properties: + tenantId: + type: string + clientId: + type: string + sceneCode: + $ref: '#/components/schemas/CaptchaSceneCode' + subject: + type: string + CaptchaChallenge: + type: object + required: [challengeId, providerCode, captchaType] + properties: + challengeId: + type: string + providerCode: + type: string + captchaType: + type: string + backgroundImage: + type: string + format: uri + nullable: true + templateImage: + type: string + format: uri + nullable: true + uuid: + type: string + nullable: true + image: + type: string + nullable: true + CaptchaVerifyRequest: + type: object + required: [tenantId, clientId, sceneCode, challengeId, providerCode, captchaType, payload] + properties: + tenantId: + type: string + clientId: + type: string + sceneCode: + $ref: '#/components/schemas/CaptchaSceneCode' + subject: + type: string + challengeId: + type: string + providerCode: + type: string + captchaType: + type: string + payload: + oneOf: + - $ref: '#/components/schemas/TianaiCaptchaPayload' + - $ref: '#/components/schemas/SystemImageCaptchaPayload' + TianaiCaptchaPayload: + type: object + required: [track] + properties: + track: + type: object + required: [trackList] + properties: + trackList: + type: array + items: + type: object + required: [x, y, t, type] + properties: + x: + type: integer + y: + type: integer + t: + type: integer + type: + type: string + SystemImageCaptchaPayload: + type: object + required: [uuid, code] + properties: + uuid: + type: string + code: + type: string + CaptchaVerify: + type: object + required: [validToken] + properties: + validToken: + type: string + expireAt: + type: string + format: date-time + nullable: true + VoidResponse: + type: object + required: [code, msg] + properties: + code: + type: integer + example: 200 + msg: + type: string + data: + nullable: true + LoginResponse: + type: object + required: [code, msg, data] + properties: + code: + type: integer + msg: + type: string + data: + $ref: '#/components/schemas/LoginData' + ProfileResponse: + type: object + required: [code, msg, data] + properties: + code: + type: integer + msg: + type: string + data: + $ref: '#/components/schemas/Profile' + FileUploadResponse: + type: object + required: [code, msg, data] + properties: + code: + type: integer + msg: + type: string + data: + $ref: '#/components/schemas/FileUpload' + RegionResponse: + type: object + required: [code, msg, data] + properties: + code: + type: integer + msg: + type: string + data: + $ref: '#/components/schemas/Region' + RegionListResponse: + type: object + required: [code, msg, data] + properties: + code: + type: integer + msg: + type: string + data: + type: array + items: + $ref: '#/components/schemas/Region' + CaptchaRequirementResponse: + type: object + required: [code, msg, data] + properties: + code: + type: integer + msg: + type: string + data: + $ref: '#/components/schemas/CaptchaRequirement' + CaptchaChallengeResponse: + type: object + required: [code, msg, data] + properties: + code: + type: integer + msg: + type: string + data: + $ref: '#/components/schemas/CaptchaChallenge' + CaptchaVerifyResponse: + type: object + required: [code, msg, data] + properties: + code: + type: integer + msg: + type: string + data: + $ref: '#/components/schemas/CaptchaVerify' + securitySchemes: + BearerAuth: + type: http + scheme: bearer + bearerFormat: JWT diff --git a/PC.openapi2.json b/PC.openapi2.json new file mode 100644 index 0000000..b6604c2 --- /dev/null +++ b/PC.openapi2.json @@ -0,0 +1,3580 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "PC", + "description": "家谱业务 APP/PC 用户侧接口文档,用于 Apifox 导入。\n\n导入方式:Apifox -> 导入 API 数据 -> OpenAPI/Swagger -> 选择本文件。\n\n约定:\n- APP 路径前缀为 `/genealogy/app`\n- PC/H5 路径前缀为 `/genealogy/pc`\n- 后台管理接口不包含在本文件内\n- 登录后接口需要携带 token,且请求 Header 需携带 `clientid`\n", + "version": "1.0.0" + }, + "tags": [ + { + "name": "认证登录" + }, + { + "name": "验证中心" + }, + { + "name": "文件上传" + }, + { + "name": "行政区划" + }, + { + "name": "家族圈" + } + ], + "paths": { + "/genealogy/pc/auth/register": { + "post": { + "summary": "PC 用户注册", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PasswordRegisterBody" + }, + "example": { + "grantType": "password", + "tenantId": "000000", + "phone": "13800000000", + "password": "e10adc3949ba59abbe56e057f20f883e", + "nickName": "测试用户", + "registerSource": "app", + "validToken": "captcha-ticket-or-sms-token" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/LoginResult", + "description": "登录结果" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/auth/login": { + "post": { + "summary": "PC 密码登录", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PasswordLoginBody" + }, + "example": { + "grantType": "password", + "tenantId": "000000", + "phone": "13800000000", + "password": "e10adc3949ba59abbe56e057f20f883e", + "validToken": "captcha-ticket" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/LoginResult", + "description": "登录结果" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/auth/login/sms": { + "post": { + "summary": "PC 短信登录", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SmsLoginBody" + }, + "example": { + "grantType": "sms", + "tenantId": "000000", + "phone": "13800000000", + "smsCode": "123456", + "validToken": "captcha-ticket" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/LoginResult", + "description": "登录结果" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/auth/sms/code": { + "post": { + "summary": "PC 发送短信验证码", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SmsCodeBody" + }, + "example": { + "tenantId": "000000", + "phone": "13800000000", + "sceneCode": "WEB_H5_LOGIN", + "validToken": "captcha-ticket" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/auth/profile": { + "get": { + "summary": "PC 当前用户资料", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + }, + "put": { + "summary": "PC 修改用户资料", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProfileUpdateBody" + }, + "example": { + "nickName": "张三", + "avatarOssId": "2060000000000000000", + "sex": "0", + "birthday": "1990-01-01", + "regionCode": "110101", + "addressDetail": "北京市东城区" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/auth/password": { + "put": { + "summary": "PC 修改密码", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PasswordChangeBody" + }, + "example": { + "oldPassword": "e10adc3949ba59abbe56e057f20f883e", + "newPassword": "25d55ad283aa400af464c76d713c07ad", + "validToken": "captcha-ticket" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/auth/password/reset": { + "put": { + "summary": "PC 找回密码", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PasswordResetBody" + }, + "example": { + "tenantId": "000000", + "phone": "13800000000", + "smsCode": "123456", + "newPassword": "25d55ad283aa400af464c76d713c07ad", + "validToken": "captcha-ticket" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/auth/phone": { + "put": { + "summary": "PC 换绑手机号", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PhoneChangeBody" + }, + "example": { + "newPhone": "13900000000", + "smsCode": "123456", + "validToken": "captcha-ticket" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/auth/account/deactivate": { + "post": { + "summary": "PC 注销账号", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountDeactivateBody" + }, + "example": { + "password": "e10adc3949ba59abbe56e057f20f883e", + "reason": "user request" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/auth/logout": { + "delete": { + "summary": "PC 退出登录", + "deprecated": false, + "description": "", + "tags": [ + "认证登录" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/captcha/require": { + "get": { + "summary": "查询当前场景是否需要验证", + "deprecated": false, + "description": "用于登录、注册、换绑手机等场景。按租户、客户端和场景编码匹配后台验证策略。", + "tags": [ + "验证中心" + ], + "parameters": [ + { + "name": "tenantId", + "in": "query", + "description": "租户ID,用于区分不同租户的数据和验证策略", + "required": false, + "example": "000000", + "schema": { + "type": "string" + } + }, + { + "name": "clientId", + "in": "query", + "description": "客户端ID,对应后台客户端管理中的APP/PC/小程序等终端", + "required": false, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + }, + { + "name": "sceneCode", + "in": "query", + "description": "验证场景编码,例如APP_LOGIN、PC_LOGIN、APP_REGISTER", + "required": true, + "example": "WEB_H5_LOGIN", + "schema": { + "type": "string" + } + }, + { + "name": "subject", + "in": "query", + "description": "验证主体,通常为手机号、用户ID或登录账号", + "required": false, + "example": "13800000000", + "schema": { + "type": "string" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID", + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string", + "default": "ced7e5f0498645c6ec642dcf450b036f" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VerificationRequireResult", + "description": "验证需求查询结果" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/captcha/challenge": { + "post": { + "summary": "生成验证挑战", + "deprecated": false, + "description": "统一生成验证码。策略为天爱时返回行为验证数据;策略为系统图形时返回 uuid 和 img。", + "tags": [ + "验证中心" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID", + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string", + "default": "ced7e5f0498645c6ec642dcf450b036f" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VerificationChallengeBody" + }, + "example": { + "tenantId": "000000", + "clientId": "ced7e5f0498645c6ec642dcf450b036f", + "sceneCode": "WEB_H5_LOGIN", + "subject": "13800000000" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/VerificationChallengeResult", + "description": "验证挑战生成结果" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/captcha/verify": { + "post": { + "summary": "校验验证结果并换取 validToken", + "deprecated": false, + "description": "验证通过后返回 validToken,登录、注册、发短信等业务接口将 validToken 随请求体一起提交。", + "tags": [ + "验证中心" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID", + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string", + "default": "ced7e5f0498645c6ec642dcf450b036f" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VerificationCheckBody" + }, + "examples": { + "tianai_behavior": { + "value": { + "tenantId": "000000", + "clientId": "ced7e5f0498645c6ec642dcf450b036f", + "sceneCode": "WEB_H5_LOGIN", + "subject": "13800000000", + "challengeId": "CAPTCHA_CHALLENGE_ID", + "providerCode": "tianai", + "captchaType": "SLIDER", + "payload": { + "track": { + "bgImageWidth": 340, + "bgImageHeight": 180, + "templateImageWidth": 50, + "templateImageHeight": 50, + "startTime": 1720000000000, + "stopTime": 1720000001500, + "left": 120, + "top": 0, + "trackList": [ + { + "x": 0, + "y": 0, + "t": 0, + "type": "down" + }, + { + "x": 120, + "y": 0, + "t": 650, + "type": "move" + }, + { + "x": 120, + "y": 0, + "t": 700, + "type": "up" + } + ] + } + } + }, + "summary": "天爱滑块/旋转/滑动还原/文字点选验证" + }, + "system_image": { + "value": { + "tenantId": "000000", + "clientId": "ced7e5f0498645c6ec642dcf450b036f", + "sceneCode": "WEB_H5_LOGIN", + "subject": "13800000000", + "challengeId": "CAPTCHA_CHALLENGE_ID", + "providerCode": "ruoyi_image", + "captchaType": "math", + "payload": { + "uuid": "captcha-uuid", + "code": "6" + } + }, + "summary": "系统图形验证码" + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/VerificationCheckResult", + "description": "验证校验结果" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/auth/code": { + "get": { + "summary": "兼容旧系统图形验证码", + "deprecated": false, + "description": "老版后台登录图形验证码入口。新的 APP/PC 业务优先使用 /captcha/require + /captcha/challenge + /captcha/verify。", + "tags": [ + "验证中心" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID", + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string", + "default": "ced7e5f0498645c6ec642dcf450b036f" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/LegacyCaptchaResult", + "description": "兼容旧图形验证码结果" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/files/upload": { + "post": { + "summary": "PC 单文件上传", + "deprecated": false, + "description": "", + "tags": [ + "文件上传" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "file": { + "type": "string", + "format": "binary", + "example": "" + } + }, + "required": [ + "file" + ] + }, + "example": { + "file": "" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/FileUploadResult", + "description": "文件上传结果" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/files/resumable/init": { + "post": { + "summary": "PC 分片上传初始化", + "deprecated": false, + "description": "", + "tags": [ + "文件上传" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResumableInitBody" + }, + "example": { + "fileName": "cover.jpg", + "fileSize": 9532, + "fileMd5": "d41d8cd98f00b204e9800998ecf8427e", + "contentType": "image/jpeg", + "chunkSize": 4194304, + "totalChunks": 1, + "bizType": "genealogy", + "usageScene": "cover" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/files/resumable/chunk": { + "post": { + "summary": "PC 上传分片", + "deprecated": false, + "description": "", + "tags": [ + "文件上传" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "uploadId": { + "type": "string", + "example": "" + }, + "chunkIndex": { + "type": "integer", + "example": 0 + }, + "chunkMd5": { + "type": "string", + "example": "d41d8cd98f00b204e9800998ecf8427e" + }, + "file": { + "type": "string", + "format": "binary", + "example": "" + } + }, + "required": [ + "uploadId", + "chunkIndex", + "chunkMd5", + "file" + ] + }, + "example": { + "uploadId": "", + "chunkIndex": 0, + "chunkMd5": "d41d8cd98f00b204e9800998ecf8427e", + "file": "" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/files/resumable/complete": { + "post": { + "summary": "PC 完成分片上传", + "deprecated": false, + "description": "", + "tags": [ + "文件上传" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResumableCompleteBody" + }, + "example": { + "uploadId": "UPLOAD202607090001", + "fileMd5": "d41d8cd98f00b204e9800998ecf8427e", + "fileSize": 9532 + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/FileUploadResult", + "description": "文件上传结果" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/files/reference": { + "post": { + "summary": "PC 绑定文件业务引用", + "deprecated": false, + "description": "", + "tags": [ + "文件上传" + ], + "parameters": [ + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FileReferenceBody" + }, + "example": { + "bizType": "family_feed", + "bizName": "family feed image", + "bizTable": "gen_family_feed", + "bizId": 900013001, + "bizField": "media_oss_ids", + "ossId": "2060000000000000000", + "usageScene": "feed_image" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + }, + "delete": { + "summary": "PC 释放文件业务引用", + "deprecated": false, + "description": "", + "tags": [ + "文件上传" + ], + "parameters": [ + { + "name": "bizTable", + "in": "query", + "description": "业务表名,用于文件引用绑定", + "required": true, + "example": "gen_family_feed", + "schema": { + "type": "string" + } + }, + { + "name": "bizId", + "in": "query", + "description": "业务数据ID,用于文件引用绑定", + "required": true, + "example": 900013001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "bizField", + "in": "query", + "description": "业务字段名,例如avatar_oss_id、cover_oss_id", + "required": true, + "example": "media_ids", + "schema": { + "type": "string" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "428a8310cd442757ae699df5d894f051", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/region/children": { + "get": { + "summary": "查询下级行政区划", + "deprecated": false, + "description": "", + "tags": [ + "行政区划" + ], + "parameters": [ + { + "name": "parentCode", + "in": "query", + "description": "父级行政区划编码,不传或传0查省级", + "required": false, + "example": "51", + "schema": { + "type": "string" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID", + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string", + "default": "ced7e5f0498645c6ec642dcf450b036f" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/region/path/{regionCode}": { + "get": { + "summary": "查询行政区划路径", + "deprecated": false, + "description": "", + "tags": [ + "行政区划" + ], + "parameters": [ + { + "name": "regionCode", + "in": "path", + "description": "行政区划编码", + "required": true, + "example": "510000", + "schema": { + "type": "string" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID", + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string", + "default": "ced7e5f0498645c6ec642dcf450b036f" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/region/search": { + "get": { + "summary": "搜索行政区划", + "deprecated": false, + "description": "", + "tags": [ + "行政区划" + ], + "parameters": [ + { + "name": "keyword", + "in": "query", + "description": "搜索关键词,支持地区名称或编码模糊查询", + "required": true, + "example": "北京", + "schema": { + "type": "string" + } + }, + { + "name": "level", + "in": "query", + "description": "行政区划级别,1省、2市、3区县、4乡镇街道、5村社区", + "required": false, + "example": 3, + "schema": { + "type": "integer" + } + }, + { + "name": "limit", + "in": "query", + "description": "返回数量上限", + "required": false, + "example": 20, + "schema": { + "type": "integer" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID", + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string", + "default": "ced7e5f0498645c6ec642dcf450b036f" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/region/{regionCode}": { + "get": { + "summary": "查询行政区划详情", + "deprecated": false, + "description": "", + "tags": [ + "行政区划" + ], + "parameters": [ + { + "name": "regionCode", + "in": "path", + "description": "行政区划编码", + "required": true, + "example": "510000", + "schema": { + "type": "string" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID", + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string", + "default": "ced7e5f0498645c6ec642dcf450b036f" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/genealogies/{genealogyId}/feeds": { + "get": { + "summary": "PC 家族圈动态列表", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + }, + "post": { + "summary": "PC 发布家族圈动态", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FamilyFeedBody" + }, + "example": { + "feedType": "text", + "feedContent": "今天上传一张老照片。", + "mediaOssIds": "2060000000000000001,2060000000000000002", + "sortOrder": 0, + "status": "0" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/genealogies/{genealogyId}/feeds/page": { + "get": { + "summary": "PC 家族圈动态分页", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "pageNum", + "in": "query", + "description": "页码,从1开始", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "description": "每页条数", + "required": false, + "schema": { + "type": "integer", + "default": 10 + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PageResult", + "description": "通用分页响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/genealogies/{genealogyId}/feeds/{feedId}": { + "get": { + "summary": "PC 家族圈动态详情", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "feedId", + "in": "path", + "description": "家族动态ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + }, + "put": { + "summary": "PC 修改家族圈动态", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "feedId", + "in": "path", + "description": "家族动态ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FamilyFeedBody" + }, + "example": { + "feedType": "text", + "feedContent": "今天上传一张老照片。", + "mediaOssIds": "2060000000000000001,2060000000000000002", + "sortOrder": 0, + "status": "0" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + }, + "delete": { + "summary": "PC 删除家族圈动态", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "feedId", + "in": "path", + "description": "家族动态ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/genealogies/{genealogyId}/feeds/{feedId}/likes": { + "post": { + "summary": "PC 点赞动态", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "feedId", + "in": "path", + "description": "家族动态ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + }, + "delete": { + "summary": "PC 取消点赞动态", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "feedId", + "in": "path", + "description": "家族动态ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/genealogies/{genealogyId}/feeds/{feedId}/comments": { + "get": { + "summary": "PC 动态评论列表", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "feedId", + "in": "path", + "description": "家族动态ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ListResult", + "description": "通用列表响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + }, + "post": { + "summary": "PC 评论动态", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "feedId", + "in": "path", + "description": "家族动态ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FamilyFeedCommentBody" + }, + "example": { + "parentCommentId": null, + "replyUserId": null, + "content": "这张照片很有年代感。" + } + } + }, + "required": true + }, + "responses": { + "200": { + "$ref": "#/components/responses/ObjectResult", + "description": "通用对象响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/genealogies/{genealogyId}/feeds/{feedId}/comments/page": { + "get": { + "summary": "PC 动态评论分页", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "feedId", + "in": "path", + "description": "家族动态ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "pageNum", + "in": "query", + "description": "页码,从1开始", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "description": "每页条数", + "required": false, + "schema": { + "type": "integer", + "default": 10 + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/PageResult", + "description": "通用分页响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + }, + "/genealogy/pc/genealogies/{genealogyId}/feeds/{feedId}/comments/{commentId}": { + "delete": { + "summary": "PC 删除动态评论", + "deprecated": false, + "description": "", + "tags": [ + "家族圈" + ], + "parameters": [ + { + "name": "genealogyId", + "in": "path", + "description": "家谱ID", + "required": true, + "example": 900001001, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "feedId", + "in": "path", + "description": "家族动态ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "commentId", + "in": "path", + "description": "评论ID", + "required": true, + "example": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "clientid", + "in": "header", + "description": "客户端ID,请求Header携带,用于区分APP/PC/小程序等终端", + "required": true, + "example": "ced7e5f0498645c6ec642dcf450b036f", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VoidResult", + "description": "通用成功响应" + } + }, + "security": [ + { + "SaToken1": [] + } + ] + } + } + }, + "components": { + "schemas": { + "PasswordRegisterBody": { + "type": "object", + "required": [ + "grantType", + "tenantId", + "phone", + "password" + ], + "properties": { + "clientId": { + "description": "客户端ID,对应后台客户端管理中的APP/PC/小程序等终端", + "type": "string" + }, + "grantType": { + "description": "授权类型,例如password、sms、wechat、apple", + "type": "string", + "example": "password" + }, + "tenantId": { + "description": "租户ID,用于区分不同租户的数据和验证策略", + "type": "string", + "example": "000000" + }, + "phone": { + "description": "手机号", + "type": "string", + "example": "13800000000" + }, + "password": { + "type": "string", + "description": "32 位 MD5", + "example": "e10adc3949ba59abbe56e057f20f883e" + }, + "nickName": { + "description": "用户昵称", + "type": "string" + }, + "registerSource": { + "description": "注册来源,例如APP、PC、H5、MINI_PROGRAM", + "type": "string" + }, + "validToken": { + "description": "验证中心通过后返回的票据,用于注册/登录/换绑等二次校验", + "type": "string" + } + } + }, + "PasswordLoginBody": { + "type": "object", + "required": [ + "grantType", + "tenantId", + "phone", + "password" + ], + "properties": { + "clientId": { + "description": "客户端ID,对应后台客户端管理中的APP/PC/小程序等终端", + "type": "string" + }, + "grantType": { + "description": "授权类型,例如password、sms、wechat、apple", + "type": "string", + "example": "password" + }, + "tenantId": { + "description": "租户ID,用于区分不同租户的数据和验证策略", + "type": "string", + "example": "000000" + }, + "phone": { + "description": "手机号", + "type": "string", + "example": "13800000000" + }, + "password": { + "type": "string", + "description": "32 位 MD5", + "example": "e10adc3949ba59abbe56e057f20f883e" + }, + "validToken": { + "description": "验证中心通过后返回的票据,用于注册/登录/换绑等二次校验", + "type": "string" + } + } + }, + "SmsLoginBody": { + "type": "object", + "required": [ + "grantType", + "tenantId", + "phone", + "smsCode" + ], + "properties": { + "clientId": { + "description": "客户端ID,对应后台客户端管理中的APP/PC/小程序等终端", + "type": "string" + }, + "grantType": { + "description": "授权类型,例如password、sms、wechat、apple", + "type": "string", + "example": "sms" + }, + "tenantId": { + "description": "租户ID,用于区分不同租户的数据和验证策略", + "type": "string", + "example": "000000" + }, + "phone": { + "description": "手机号", + "type": "string", + "example": "13800000000" + }, + "smsCode": { + "description": "短信验证码", + "type": "string", + "example": "1234" + } + } + }, + "SmsCodeBody": { + "type": "object", + "required": [ + "grantType", + "tenantId", + "sceneCode", + "phone", + "validToken" + ], + "properties": { + "clientId": { + "description": "客户端ID,对应后台客户端管理中的APP/PC/小程序等终端", + "type": "string" + }, + "grantType": { + "description": "授权类型,例如password、sms、wechat、apple", + "type": "string", + "example": "sms" + }, + "tenantId": { + "description": "租户ID,用于区分不同租户的数据和验证策略", + "type": "string", + "example": "000000" + }, + "sceneCode": { + "description": "验证场景编码,例如APP_LOGIN、PC_LOGIN、APP_REGISTER", + "type": "string", + "example": "WEB_H5_LOGIN" + }, + "phone": { + "description": "手机号", + "type": "string", + "example": "13800000000" + }, + "validToken": { + "description": "验证中心通过后返回的票据,用于注册/登录/换绑等二次校验", + "type": "string" + } + } + }, + "ProfileUpdateBody": { + "type": "object", + "properties": { + "nickName": { + "description": "用户昵称", + "type": "string" + }, + "avatarOssId": { + "description": "头像文件OSS ID", + "type": "integer", + "format": "int64" + }, + "sex": { + "description": "性别,建议使用系统字典值", + "type": "string" + }, + "birthday": { + "description": "生日,格式 yyyy-MM-dd", + "type": "string", + "format": "date" + }, + "provinceCode": { + "type": "string" + }, + "cityCode": { + "type": "string" + }, + "districtCode": { + "type": "string" + } + } + }, + "PasswordChangeBody": { + "type": "object", + "required": [ + "oldPassword", + "newPassword" + ], + "properties": { + "oldPassword": { + "type": "string", + "description": "32 位 MD5" + }, + "newPassword": { + "type": "string", + "description": "32 位 MD5" + } + } + }, + "PasswordResetBody": { + "type": "object", + "required": [ + "tenantId", + "phone", + "smsCode", + "newPassword" + ], + "properties": { + "tenantId": { + "description": "租户ID,用于区分不同租户的数据和验证策略", + "type": "string", + "example": "000000" + }, + "phone": { + "description": "手机号", + "type": "string" + }, + "smsCode": { + "description": "短信验证码", + "type": "string" + }, + "newPassword": { + "type": "string", + "description": "32 位 MD5" + }, + "validToken": { + "description": "验证中心通过后返回的票据,用于注册/登录/换绑等二次校验", + "type": "string" + } + } + }, + "PhoneChangeBody": { + "type": "object", + "required": [ + "newPhone", + "smsCode" + ], + "properties": { + "newPhone": { + "type": "string" + }, + "smsCode": { + "description": "短信验证码", + "type": "string" + }, + "validToken": { + "description": "验证中心通过后返回的票据,用于注册/登录/换绑等二次校验", + "type": "string" + } + } + }, + "AccountDeactivateBody": { + "type": "object", + "required": [ + "password" + ], + "properties": { + "password": { + "type": "string", + "description": "32 位 MD5" + }, + "reason": { + "description": "操作原因或说明", + "type": "string" + } + } + }, + "ResumableInitBody": { + "type": "object", + "required": [ + "fileName", + "fileSize", + "fileMd5", + "chunkSize", + "totalChunks" + ], + "properties": { + "fileName": { + "description": "原始文件名", + "type": "string" + }, + "fileSize": { + "description": "文件大小,单位字节", + "type": "integer", + "format": "int64" + }, + "fileMd5": { + "description": "完整文件MD5,用于秒传和完整性校验", + "type": "string" + }, + "contentType": { + "description": "文件MIME类型,例如 image/jpeg、video/mp4", + "type": "string" + }, + "chunkSize": { + "description": "单个分片大小,单位字节", + "type": "integer", + "example": 4194304 + }, + "totalChunks": { + "description": "总分片数", + "type": "integer" + }, + "bizType": { + "description": "业务类型,例如avatar、cover、feed_image", + "type": "string" + }, + "usageScene": { + "description": "文件使用场景,便于后续引用统计和清理", + "type": "string" + } + } + }, + "ResumableCompleteBody": { + "type": "object", + "required": [ + "uploadId", + "fileMd5", + "fileSize" + ], + "properties": { + "uploadId": { + "description": "分片上传任务ID", + "type": "string" + }, + "fileMd5": { + "description": "完整文件MD5,用于秒传和完整性校验", + "type": "string" + }, + "fileSize": { + "description": "文件大小,单位字节", + "type": "integer", + "format": "int64" + } + } + }, + "FileReferenceBody": { + "type": "object", + "required": [ + "bizType", + "bizTable", + "bizId", + "bizField" + ], + "properties": { + "bizType": { + "description": "业务类型,例如avatar、cover、feed_image", + "type": "string", + "example": "family_feed" + }, + "bizName": { + "description": "业务名称,用于后台展示文件引用来源", + "type": "string", + "example": "家族圈动态" + }, + "bizTable": { + "description": "业务表名,用于文件引用绑定", + "type": "string", + "example": "gen_family_feed" + }, + "bizId": { + "description": "业务数据ID,用于文件引用绑定", + "type": "integer", + "format": "int64" + }, + "bizField": { + "description": "业务字段名,例如avatar_oss_id、cover_oss_id", + "type": "string", + "example": "media_ids" + }, + "ossId": { + "description": "OSS文件ID,表单内部使用,用户侧应通过上传组件获取", + "type": "integer", + "format": "int64" + }, + "ossIds": { + "type": "string", + "example": "2060001,2060002" + }, + "usageScene": { + "description": "文件使用场景,便于后续引用统计和清理", + "type": "string" + }, + "usageName": { + "type": "string" + } + } + }, + "FamilyFeedBody": { + "type": "object", + "required": [ + "feedContent" + ], + "properties": { + "feedType": { + "description": "动态类型,未传时默认为 text", + "type": "string", + "example": "text" + }, + "feedContent": { + "description": "动态内容,不能为空", + "type": "string", + "example": "今天上传一张老照片。" + }, + "mediaOssIds": { + "description": "媒体文件 OSS ID 列表,多个 ID 使用英文逗号分隔", + "type": "string", + "example": "2060000000000000001,2060000000000000002" + }, + "sortOrder": { + "description": "排序值,未传时默认为 0", + "type": "integer", + "format": "int64", + "example": 0 + }, + "status": { + "description": "动态状态,未传时默认为正常状态", + "type": "string", + "example": "0" + } + } + }, + "FamilyFeedCommentBody": { + "type": "object", + "required": [ + "content" + ], + "properties": { + "parentCommentId": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "replyUserId": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "content": { + "type": "string" + } + } + }, + "VerificationChallengeBody": { + "type": "object", + "required": [ + "sceneCode" + ], + "properties": { + "tenantId": { + "type": "string", + "description": "租户 ID" + }, + "clientId": { + "type": "string", + "description": "客户端 ID" + }, + "sceneCode": { + "type": "string", + "description": "验证场景编码,如 ADMIN_LOGIN/APP_LOGIN/APP_REGISTER" + }, + "subject": { + "type": "string", + "description": "验证主体,如手机号/用户名/IP" + } + }, + "example": { + "tenantId": "000000", + "clientId": "ced7e5f0498645c6ec642dcf450b036f", + "sceneCode": "WEB_H5_LOGIN", + "subject": "13800000000" + } + }, + "VerificationCheckBody": { + "type": "object", + "required": [ + "sceneCode" + ], + "properties": { + "tenantId": { + "description": "租户ID,用于区分不同租户的数据和验证策略", + "type": "string" + }, + "clientId": { + "description": "客户端ID,对应后台客户端管理中的APP/PC/小程序等终端", + "type": "string" + }, + "sceneCode": { + "description": "验证场景编码,例如APP_LOGIN、PC_LOGIN、APP_REGISTER", + "type": "string" + }, + "subject": { + "description": "验证主体,通常为手机号、用户ID或登录账号", + "type": "string" + }, + "challengeId": { + "description": "验证挑战ID,由验证挑战接口返回", + "type": "string" + }, + "providerCode": { + "type": "string", + "description": "验证服务,tianai 或 ruoyi_image" + }, + "captchaType": { + "type": "string", + "description": "SLIDER/ROTATE/CONCAT/WORD_IMAGE_CLICK/math/char" + }, + "payload": { + "description": "验证结果载荷。天爱行为验证码提交 payload.track;系统图形验证码提交 payload.uuid 和 payload.code", + "oneOf": [ + { + "$ref": "#/components/schemas/TianaiVerificationPayload" + }, + { + "$ref": "#/components/schemas/SystemImageVerificationPayload" + } + ] + } + } + }, + "TianaiVerificationPayload": { + "type": "object", + "required": [ + "track" + ], + "properties": { + "track": { + "$ref": "#/components/schemas/TianaiCaptchaTrack" + } + } + }, + "TianaiCaptchaTrack": { + "type": "object", + "required": [ + "bgImageWidth", + "bgImageHeight", + "startTime", + "stopTime", + "trackList" + ], + "properties": { + "bgImageWidth": { + "type": "integer", + "description": "验证码背景图片在前端实际显示的宽度,单位像素" + }, + "bgImageHeight": { + "type": "integer", + "description": "验证码背景图片在前端实际显示的高度,单位像素" + }, + "templateImageWidth": { + "type": "integer", + "description": "滑块或旋转模板图片在前端实际显示的宽度,单位像素" + }, + "templateImageHeight": { + "type": "integer", + "description": "滑块或旋转模板图片在前端实际显示的高度,单位像素" + }, + "startTime": { + "type": "integer", + "format": "int64", + "description": "用户开始操作的时间戳,单位毫秒" + }, + "stopTime": { + "type": "integer", + "format": "int64", + "description": "用户结束操作的时间戳,单位毫秒" + }, + "left": { + "type": "integer", + "description": "滑块最终水平偏移量,单位像素;由天爱前端控件生成" + }, + "top": { + "type": "integer", + "description": "点选或模板最终垂直偏移量,单位像素;由天爱前端控件生成" + }, + "trackList": { + "type": "array", + "minItems": 1, + "description": "用户操作轨迹,不能为空,必须直接使用天爱前端控件产生的数据", + "items": { + "$ref": "#/components/schemas/TianaiCaptchaTrackPoint" + } + }, + "data": { + "type": "object", + "additionalProperties": true, + "description": "不同验证码类型附带的扩展数据,由天爱前端控件原样提交", + "properties": {}, + "nullable": true + } + } + }, + "TianaiCaptchaTrackPoint": { + "type": "object", + "required": [ + "x", + "y", + "t", + "type" + ], + "properties": { + "x": { + "type": "number", + "format": "float", + "description": "当前轨迹点的水平坐标" + }, + "y": { + "type": "number", + "format": "float", + "description": "当前轨迹点的垂直坐标" + }, + "t": { + "type": "number", + "format": "float", + "description": "当前轨迹点相对操作开始时的时间,单位毫秒" + }, + "type": { + "type": "string", + "description": "轨迹事件类型,例如 down、move、up" + } + } + }, + "SystemImageVerificationPayload": { + "type": "object", + "required": [ + "uuid", + "code" + ], + "properties": { + "uuid": { + "type": "string", + "description": "系统图形验证码接口返回的唯一标识" + }, + "code": { + "type": "string", + "description": "用户填写的图形验证码答案" + } + } + }, + "RVoid": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "example": 200 + }, + "msg": { + "type": "string", + "example": "操作成功" + }, + "data": { + "type": "null" + } + } + }, + "RObject": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "example": 200 + }, + "msg": { + "type": "string", + "example": "操作成功" + }, + "data": { + "type": "object", + "additionalProperties": true, + "properties": {} + } + } + }, + "RList": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "example": 200 + }, + "msg": { + "type": "string", + "example": "操作成功" + }, + "data": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "properties": {} + } + } + } + }, + "PageResult": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "example": 200 + }, + "msg": { + "type": "string", + "example": "操作成功" + }, + "rows": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "properties": {} + } + }, + "total": { + "type": "integer", + "example": 0 + } + } + }, + "RLogin": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "example": 200 + }, + "msg": { + "type": "string", + "example": "操作成功" + }, + "data": { + "$ref": "#/components/schemas/LoginVo" + } + } + }, + "LoginVo": { + "type": "object", + "properties": { + "token": { + "type": "string" + }, + "accessToken": { + "type": "string" + }, + "tokenValue": { + "type": "string" + }, + "userId": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "type": "string" + }, + "clientId": { + "type": "string" + }, + "clientKey": { + "type": "string" + } + } + }, + "RFileUpload": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "example": 200 + }, + "msg": { + "type": "string", + "example": "操作成功" + }, + "data": { + "$ref": "#/components/schemas/FileUploadVo" + } + } + }, + "FileUploadVo": { + "type": "object", + "properties": { + "ossId": { + "type": "integer", + "format": "int64" + }, + "url": { + "type": "string" + }, + "thumbnailUrl": { + "type": "string", + "nullable": true + }, + "fileName": { + "type": "string" + }, + "originalName": { + "type": "string" + } + } + }, + "RVoid1": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "type": "null" + } + } + }, + "RObject2": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "type": "object", + "additionalProperties": true, + "properties": {} + } + } + }, + "RList3": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "properties": {} + } + } + } + }, + "PageResult4": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "rows": { + "description": "分页数据列表", + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "properties": {} + } + }, + "total": { + "type": "integer", + "example": 0 + } + } + }, + "RLogin5": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "$ref": "#/components/schemas/LoginVo6" + } + } + }, + "LoginVo6": { + "type": "object", + "properties": { + "token": { + "type": "string" + }, + "accessToken": { + "type": "string" + }, + "tokenValue": { + "type": "string" + }, + "userId": { + "type": "integer", + "format": "int64" + }, + "tenantId": { + "description": "租户ID,用于区分不同租户的数据和验证策略", + "type": "string" + }, + "clientId": { + "description": "客户端ID,对应后台客户端管理中的APP/PC/小程序等终端", + "type": "string" + }, + "clientKey": { + "type": "string" + } + } + }, + "RFileUpload7": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "$ref": "#/components/schemas/FileUploadVo8" + } + } + }, + "FileUploadVo8": { + "type": "object", + "properties": { + "ossId": { + "description": "OSS文件ID,表单内部使用,用户侧应通过上传组件获取", + "type": "integer", + "format": "int64" + }, + "url": { + "type": "string" + }, + "thumbnailUrl": { + "type": "string", + "nullable": true + }, + "fileName": { + "description": "原始文件名", + "type": "string" + }, + "originalName": { + "type": "string" + } + } + }, + "RVerificationRequire": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "$ref": "#/components/schemas/VerificationRequireVo" + } + } + }, + "VerificationRequireVo": { + "type": "object", + "properties": { + "required": { + "description": "当前场景是否需要验证", + "type": "boolean" + }, + "providerCode": { + "description": "第三方服务商编码", + "type": "string" + }, + "captchaType": { + "description": "验证码类型,例如SLIDER、ROTATE、CONCAT、WORD_IMAGE_CLICK", + "type": "string" + }, + "sceneCode": { + "description": "验证场景编码,例如APP_LOGIN、PC_LOGIN、APP_REGISTER", + "type": "string" + }, + "ttlSeconds": { + "description": "验证或票据有效秒数", + "type": "integer" + } + }, + "example": { + "required": true, + "providerCode": "tianai", + "captchaType": "SLIDER", + "sceneCode": "APP_LOGIN", + "ttlSeconds": 300 + } + }, + "RVerificationChallenge": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "$ref": "#/components/schemas/VerificationChallengeVo" + } + } + }, + "VerificationChallengeVo": { + "type": "object", + "properties": { + "required": { + "description": "当前场景是否需要验证", + "type": "boolean" + }, + "providerCode": { + "description": "第三方服务商编码", + "type": "string" + }, + "captchaType": { + "description": "验证码类型,例如SLIDER、ROTATE、CONCAT、WORD_IMAGE_CLICK", + "type": "string" + }, + "challengeId": { + "description": "验证挑战ID,由验证挑战接口返回", + "type": "string" + }, + "uuid": { + "type": "string", + "description": "系统图形验证码 uuid" + }, + "img": { + "type": "string", + "description": "系统图形验证码 base64 图片" + }, + "payload": { + "type": "object", + "additionalProperties": true, + "description": "天爱验证码数据,包括背景图、模板图、尺寸等", + "properties": {} + }, + "expireSeconds": { + "description": "验证挑战过期秒数", + "type": "integer" + } + }, + "example": { + "required": true, + "providerCode": "tianai", + "captchaType": "SLIDER", + "challengeId": "CAPTCHA_CHALLENGE_ID", + "payload": { + "backgroundImage": "data:image/png;base64,...", + "templateImage": "data:image/png;base64,...", + "backgroundImageWidth": 340, + "backgroundImageHeight": 180 + }, + "expireSeconds": 300 + } + }, + "RVerificationCheck": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "$ref": "#/components/schemas/VerificationCheckVo" + } + } + }, + "VerificationCheckVo": { + "type": "object", + "properties": { + "passed": { + "description": "验证是否通过", + "type": "boolean" + }, + "validToken": { + "type": "string", + "description": "验证通过后交给业务接口的票据" + }, + "expireSeconds": { + "description": "验证挑战过期秒数", + "type": "integer" + }, + "message": { + "description": "返回消息", + "type": "string" + } + }, + "example": { + "passed": true, + "validToken": "captcha-ticket", + "expireSeconds": 300, + "message": "验证通过" + } + }, + "RLegacyCaptcha": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "$ref": "#/components/schemas/LegacyCaptchaVo" + } + } + }, + "LegacyCaptchaVo": { + "type": "object", + "properties": { + "captchaEnabled": { + "type": "boolean" + }, + "uuid": { + "description": "系统图形验证码UUID", + "type": "string" + }, + "img": { + "type": "string", + "description": "base64 图形验证码" + } + }, + "example": { + "captchaEnabled": true, + "uuid": "captcha-uuid", + "img": "data:image/png;base64,..." + } + }, + "RVerificationRequire9": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "$ref": "#/components/schemas/VerificationRequireVo10" + } + } + }, + "VerificationRequireVo10": { + "type": "object", + "properties": { + "required": { + "description": "当前场景是否需要验证", + "type": "boolean" + }, + "providerCode": { + "description": "第三方服务商编码", + "type": "string" + }, + "captchaType": { + "description": "验证码类型,例如SLIDER、ROTATE、CONCAT、WORD_IMAGE_CLICK", + "type": "string" + }, + "sceneCode": { + "description": "验证场景编码,例如APP_LOGIN、PC_LOGIN、APP_REGISTER", + "type": "string" + }, + "ttlSeconds": { + "description": "验证或票据有效秒数", + "type": "integer" + } + }, + "example": { + "required": true, + "providerCode": "tianai", + "captchaType": "SLIDER", + "sceneCode": "WEB_H5_LOGIN", + "ttlSeconds": 300 + } + }, + "RVerificationChallenge11": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "$ref": "#/components/schemas/VerificationChallengeVo12" + } + } + }, + "VerificationChallengeVo12": { + "type": "object", + "properties": { + "required": { + "description": "当前场景是否需要验证", + "type": "boolean" + }, + "providerCode": { + "description": "第三方服务商编码", + "type": "string" + }, + "captchaType": { + "description": "验证码类型,例如SLIDER、ROTATE、CONCAT、WORD_IMAGE_CLICK", + "type": "string" + }, + "challengeId": { + "description": "验证挑战ID,由验证挑战接口返回", + "type": "string" + }, + "uuid": { + "type": "string", + "description": "系统图形验证码 uuid" + }, + "img": { + "type": "string", + "description": "系统图形验证码 base64 图片" + }, + "payload": { + "type": "object", + "additionalProperties": true, + "description": "天爱验证码数据,包括背景图、模板图、尺寸等", + "properties": {} + }, + "expireSeconds": { + "description": "验证挑战过期秒数", + "type": "integer" + } + }, + "example": { + "required": true, + "providerCode": "tianai", + "captchaType": "SLIDER", + "challengeId": "CAPTCHA_CHALLENGE_ID", + "payload": { + "backgroundImage": "data:image/png;base64,...", + "templateImage": "data:image/png;base64,...", + "backgroundImageWidth": 340, + "backgroundImageHeight": 180 + }, + "expireSeconds": 300 + } + }, + "RVerificationCheck13": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "$ref": "#/components/schemas/VerificationCheckVo14" + } + } + }, + "VerificationCheckVo14": { + "type": "object", + "properties": { + "passed": { + "description": "验证是否通过", + "type": "boolean" + }, + "validToken": { + "type": "string", + "description": "验证通过后交给业务接口的票据" + }, + "expireSeconds": { + "description": "验证挑战过期秒数", + "type": "integer" + }, + "message": { + "description": "返回消息", + "type": "string" + } + }, + "example": { + "passed": true, + "validToken": "captcha-ticket", + "expireSeconds": 300, + "message": "验证通过" + } + }, + "RLegacyCaptcha15": { + "type": "object", + "properties": { + "code": { + "description": "系统图形验证码或响应状态码", + "type": "integer", + "example": 200 + }, + "msg": { + "description": "响应消息", + "type": "string", + "example": "操作成功" + }, + "data": { + "description": "响应数据主体", + "$ref": "#/components/schemas/LegacyCaptchaVo16" + } + } + }, + "LegacyCaptchaVo16": { + "type": "object", + "properties": { + "captchaEnabled": { + "type": "boolean" + }, + "uuid": { + "description": "系统图形验证码UUID", + "type": "string" + }, + "img": { + "type": "string", + "description": "base64 图形验证码" + } + }, + "example": { + "captchaEnabled": true, + "uuid": "captcha-uuid", + "img": "data:image/png;base64,..." + } + } + }, + "responses": { + "VoidResult": { + "description": "通用成功响应", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RVoid1" + } + } + } + }, + "ObjectResult": { + "description": "通用对象响应", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RObject2" + } + } + } + }, + "ListResult": { + "description": "通用列表响应", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RList3" + } + } + } + }, + "PageResult": { + "description": "通用分页响应", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PageResult4" + } + } + } + }, + "LoginResult": { + "description": "登录结果", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RLogin5" + } + } + } + }, + "FileUploadResult": { + "description": "文件上传结果", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RFileUpload7" + } + } + } + }, + "VerificationRequireResult": { + "description": "验证需求查询结果", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RVerificationRequire9" + } + } + } + }, + "VerificationChallengeResult": { + "description": "验证挑战生成结果", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RVerificationChallenge11" + } + } + } + }, + "VerificationCheckResult": { + "description": "验证校验结果", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RVerificationCheck13" + } + } + } + }, + "LegacyCaptchaResult": { + "description": "兼容旧图形验证码结果", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RLegacyCaptcha15" + } + } + } + } + }, + "securitySchemes": { + "SaToken1": { + "type": "apiKey", + "in": "header", + "name": "Authorization", + "description": "登录后返回的 token。若本地配置使用 sa-token 名称,请按项目实际 token-name 调整。" + } + } + }, + "servers": [], + "security": [ + { + "SaToken1": [] + } + ] +} \ No newline at end of file diff --git a/app.html b/app.html index 2ae45f6..b448289 100644 --- a/app.html +++ b/app.html @@ -147,7 +147,6 @@ - diff --git a/article-detail.html b/article-detail.html index 8cf1d17..e3f77a2 100644 --- a/article-detail.html +++ b/article-detail.html @@ -107,7 +107,6 @@ - diff --git a/children-privacy.html b/children-privacy.html new file mode 100644 index 0000000..772d10a --- /dev/null +++ b/children-privacy.html @@ -0,0 +1,72 @@ + + + + + + 儿童个人信息处理规则 - 代代相传 + + + + + +
+
+
+
+
Children Privacy
+

儿童个人信息处理规则

+

用于说明儿童个人信息的处理边界、监护人权利和保护措施。

+
+
+
+
+
+
+
+
+ 待法务确认:本页面仅建立儿童个人信息规则的访问入口,不构成已生效的规则。发布前必须由运营主体与法务确认适用年龄、监护人授权与完整文本。 +
+

正式文本应涵盖

+

+ 儿童信息处理目的、必要性、监护人同意方式、信息访问更正删除渠道,以及发生个人信息安全事件时的告知安排。 +

+
+ +
+
+
+ + + + diff --git a/config.js b/config.js index a67f8c9..9d68be2 100644 --- a/config.js +++ b/config.js @@ -9,12 +9,12 @@ })(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { 'use strict'; + // 这里只维护运行环境和后端基础地址,具体接口路径统一放在 ApiClient 中。 var ENVIRONMENTS = { development: { apiBaseUrl: 'http://182.61.18.23:8080' }, production: { - // 后端尚未提供正式生产域名,暂时保持用户指定的测试地址。 apiBaseUrl: 'http://182.61.18.23:8080' } }; diff --git a/create-genealogy.html b/create-genealogy.html index 4916994..acd0d3c 100644 --- a/create-genealogy.html +++ b/create-genealogy.html @@ -130,8 +130,6 @@ - - diff --git a/docs/pc-api-page-integration-tracker-2026-07-09.md b/docs/pc-api-page-integration-tracker-2026-07-09.md deleted file mode 100644 index 525a024..0000000 --- a/docs/pc-api-page-integration-tracker-2026-07-09.md +++ /dev/null @@ -1,354 +0,0 @@ -# PC 接口对接重新规划与状态追踪 - -创建时间:2026-07-09 15:14:58 +08:00 -接口依据:`genealogy-pc-openapi.yaml` -接口基础地址:`http://182.61.18.23:8080` -接口依据:`genealogy-pc-openapi.yaml` - -## 当前地址契约 - -- 运行时唯一来源:根目录 `config.js`。 -- 当前开发和生产地址:`http://182.61.18.23:8080`。 -- 地址变更记录:2026-07-11 已由历史测试域名切换为当前 IP 和端口;文档中出现的 `test-genealogy-api.ddxcjp.cn` 仅用于描述历史事件,不代表当前运行时配置。 - -## 执行规则 - -1. 所有 PC 接口对接必须先进入本追踪表,再改代码。 -2. 每一项开始前先标记“进行中”,记录年月日时分秒和时区。 -3. 每一项完成后记录完成时间、修改文件、接口路径、验证命令和结果。 -4. 只有完成验证后才能标记“已完成”。 -5. `genealogy-pc-openapi.yaml` 没有明确接口的页面,标记“暂缓”或“待确认”,不继续套用旧路径。 -6. 样式继续写入 CSS 文件,业务 JS 不注入样式。 -7. 新增或修改的 HTML、JS、CSS 注释使用中文,代码保持可读、不压缩。 - -## PC 文档当前接口范围 - -- 验证中心:`/captcha/require`、`/captcha/challenge`、`/captcha/verify`、`/auth/code` -- PC 认证:`/genealogy/pc/auth/register`、`/genealogy/pc/auth/login`、`/genealogy/pc/auth/login/sms`、`/genealogy/pc/auth/sms/code`、`/genealogy/pc/auth/profile`、`/genealogy/pc/auth/password`、`/genealogy/pc/auth/password/reset`、`/genealogy/pc/auth/phone`、`/genealogy/pc/auth/account/deactivate`、`/genealogy/pc/auth/logout` -- PC 文件:`/genealogy/pc/files/upload`、`/genealogy/pc/files/resumable/init`、`/genealogy/pc/files/resumable/chunk`、`/genealogy/pc/files/resumable/complete`、`/genealogy/pc/files/reference` -- 行政区划:`/genealogy/region/children`、`/genealogy/region/path/{regionCode}`、`/genealogy/region/search`、`/genealogy/region/{regionCode}` - -## 最新 PC YAML 分析记录 - -分析时间:2026-07-09 15:36:16 +08:00 -分析文件:`genealogy-pc-openapi.yaml` - -1. `paths` 中实际存在 25 个接口操作,分为验证码、PC 认证、PC 文件、行政区划四类。 -2. `servers` 仍是本地地址示例;当前运行时基础地址以 `config.js` 为准,为 `http://182.61.18.23:8080`。 -3. 登录后接口使用 `Authorization` 作为 token header,同时所有 PC 认证和文件接口都要求 header `clientid`;用户最新提供的 PC `clientid` 为 `ced7e5f0498645c6ec642dcf450b036f`,客户端 Key 为 `web_pc`。 -4. `components.tags` 和 `components.schemas/requestBodies` 中出现家谱、动态、世系、字辈等业务模型,但 `paths` 没有对应接口路径;实现时不能把这些 schema 当成可调用 PC 接口。 -5. 验证码场景以后台配置为准,当前明确可用的 PC/H5 场景为 `WEB_H5_LOGIN`、`WEB_H5_REGISTER`、`WEB_H5_FORGOT_PASSWORD`。 -6. `ProfileUpdate` 示例出现 `regionCode/addressDetail`,但 schema 实际字段是 `provinceCode/cityCode/districtCode`,页面资料提交时应以 schema 字段为准。 -7. `SmsLoginBody` 未声明 `validToken` 字段,但短信发送 `SmsCodeBody` 必填 `validToken`;短信登录流程应先通过验证码拿票据,再发短信验证码,最后用短信码登录。 -8. 文件引用 `FileReferenceBody` 必填 `bizType/bizTable/bizId/bizField`,`ossId/ossIds/usageScene/usageName` 可选;但文章、动态、相册等业务主体接口未提供,文件引用只能作为公共上传能力,不代表主业务已可提交。 - -## 验证码与 TAC 调用记录 - -- 后台验证码配置截图中,PC/H5 当前明确可用场景是 `WEB_H5_LOGIN`、`WEB_H5_REGISTER`、`WEB_H5_FORGOT_PASSWORD`,客户端 Key 为 `web_pc`,验证服务为“天爱行为验证码”,验证码类型为“滑块验证码”。 -- `ADMIN_LOGIN` 是后台管理登录,类型为旋转验证码,不归当前 PC/H5 页面使用。 -- PC/H5 登录页调用链: - 1. 页面加载 `public/tac/css/tac.css`、`public/tac/js/tac.min.js`、`public/js/captcha-pages.js`。 - 2. 表单提供 `input[name="validToken"]` 隐藏字段和 `[data-captcha-box]` 容器。 - 3. `auth-pages.js` 在登录提交前调用 `CaptchaPages.ensureToken(form, { sceneCode: 'WEB_H5_LOGIN', subject: 手机号 })`。 - 4. `captcha-pages.js` 先请求 `GET /captcha/require?tenantId=000000&clientId=ced7e5f0498645c6ec642dcf450b036f&sceneCode=WEB_H5_LOGIN&subject=手机号`。 - 5. 如果返回 `required=false`,业务表单可继续提交;如果需要验证,则创建 `new CaptchaConfig(...)` 和 `new TAC(config, ...)`。 - 6. TAC 内部请求 `POST /captcha/challenge` 获取 `captchaType/challengeId/payload`,适配为 TAC 需要的 `data.type/data.id/backgroundImage/templateImage`。 - 7. 用户拖动完成后,TAC 请求 `POST /captcha/verify`,提交 `challengeId/providerCode/captchaType/payload.track`,其中 `left/top` 由轨迹首尾点计算。 - 8. 验证成功后从返回 `data.validToken` 取票据,写回表单隐藏字段 `validToken`,再提交 PC 登录接口。 -- PC/H5 注册使用 `WEB_H5_REGISTER`,找回密码和找回密码短信验证码使用 `WEB_H5_FORGOT_PASSWORD`;换绑手机、注销账号暂未看到明确 PC/H5 场景编码,仍待后端补充后再接。 - -## 编号计划 - -| 编号 | 状态 | 页面范围 | 接口范围 | 计划与验证 | -| --- | --- | --- | --- | --- | -| PC-000 | 复核通过 | 全项目 | `genealogy-pc-openapi.yaml` | 历史记录保留;当前 PC YAML `paths` 再次核对通过,真实接口范围仍只有验证码、PC 认证、PC 文件和地区。 | -| PC-001 | 复核通过 | 根目录 `config.js` | 开发/生产环境、API 基础地址 | 当前 `config.js` 已收敛为环境和基础地址唯一入口;`node tests\config.test.js`、`node --check config.js` 通过。 | -| PC-002 | 复核通过 | `utils` 公共工具层 | Axios、请求封装、PC 接口客户端 | 当前公共层为 `utils/axios.js`、`utils/AxiosRequestUtil.js`、`utils/ApiClient.js`;旧 fetch 工具和旧 public API 客户端已删除。 | -| PC-003 | 本地复核通过 | 认证、资料、安全页面 | `/genealogy/pc/auth/*`、`/captcha/*` | 认证、资料、安全测试和脚本顺序检查均通过;真实登录业务仍依赖后端账户数据。 | -| PC-004 | 本地复核通过 | 文件上传页面 | `/genealogy/pc/files/*` | `node tests\upload-pages.test.js`、`node tests\api-client.test.js` 通过;文件上传仍需真实文件和后端授权进行端到端验证。 | -| PC-005 | 本地复核通过 | 地区选择页面 | `/genealogy/region/*` | `node tests\region-pages.test.js`、`node tests\profile-pages.test.js` 通过。 | -| PC-006 | 复核通过 | 页面覆盖矩阵 | PC YAML 覆盖边界 | 保持“公共能力不等于整页业务已对接”的边界,未覆盖业务继续受控失败。 | -| PC-007 | 复核通过 | 实施规划 | `genealogy-pc-openapi.yaml` | 当前计划与 YAML `paths` 一致,不使用旧路径 fallback。 | -| PC-008 | 复核通过 | PC YAML 未覆盖业务页面 | `PC_API_NOT_AVAILABLE` | `utils/ApiClient.js` 统一抛 `PC_API_NOT_AVAILABLE`,过期请求路径扫描无结果。 | -| PC-009 | 本地复核通过 | 登录、注册、找回密码 | PC auth、`/captcha/*` | 三页场景编码、表单契约、Axios 请求客户端和相关测试均通过;真实账号流程待业务数据联调。 | -| PC-010 | 已复核(当前地址已同步) | `config.js`、`tests/config.test.js`、`utils/ApiClient.js` | API 基础地址 | 当前开发和生产地址均为 `http://182.61.18.23:8080`;地址唯一运行时来源为 `config.js`,`tests/config.test.js` 已同步断言。 | -| PC-011 | 本地复核通过 | 三张认证页 TAC 弹窗 | `public/tac`、`/captcha/*` | 页面级挂载、移动端约束和结构测试通过;未修改 `tac.min.js`。 | - -## PC 页面覆盖矩阵草案 - -| 分类 | 页面/模块 | 当前判断 | 处理方式 | -| --- | --- | --- | --- | -| 完整可闭环 | `login.html` | PC YAML 有 `/genealogy/pc/auth/login`,后台验证码配置明确 `WEB_H5_LOGIN`,TAC 可完整串联。 | 可作为第一批执行页面。 | -| 接口存在但部分验证码场景待确认 | `register.html`、`forgot-password.html`、`profile-security.html` 中的换绑/注销相关动作 | PC YAML 有对应认证接口;注册场景为 `WEB_H5_REGISTER`,找回密码场景为 `WEB_H5_FORGOT_PASSWORD`,换绑和注销暂未提供 PC/H5 场景。 | 注册和找回密码可接 TAC;换绑、注销涉及验证码的提交先记录待确认,不套用未经确认的场景。 | -| 资料接口可对接 | `profile.html`、`profile-data.html` 中用户资料读取/修改 | PC YAML 有 `/genealogy/pc/auth/profile`。 | 只处理用户资料读写;页面里其他非资料业务不因此视为完成。 | -| 公共地区能力可保留 | `create-genealogy.html`、`profile-create-family.html`、`profile-data.html` 中地区选择控件 | PC YAML 有 `/genealogy/region/*`。 | 只保留和验证地区选择;创建家谱等主业务接口 PC YAML 未覆盖,仍待确认。 | -| 文件能力可切换 | 文章、动态、相册、成长、备忘、祭祀等页面里的上传控件 | PC YAML 有 `/genealogy/pc/files/*`。 | 只把上传、分片、文件引用切到 PC 文件接口;文章/动态/相册等主业务接口仍待确认。 | -| PC YAML 未覆盖 | 家谱主体、成员、字辈、世系、动态、文章、相册、祭祀、族务、VIP、通知、反馈、帮助、推广、姓氏、搜索等主业务页面 | 当前 PC YAML 没有这些业务路径。 | 等待补充 PC 接口后再逐页规划。 | - -## 流程记录 - -- 2026-07-09 15:14:58 +08:00:收到用户纠正,停止旧接口继续推进;读取 `genealogy-pc-openapi.yaml`,确认 PC 路径前缀为 `/genealogy/pc`,基础地址改用 `https://test-genealogy-api.ddxcjp.cn`。 -- 2026-07-09 15:14:58 +08:00:创建本 PC 专用追踪表,PC-001 标记为“进行中”。 -- 2026-07-09 15:17:57 +08:00:收到用户补充要求,重新规划 `config.js` 环境切换和 `utils` 公共 JS 分层;停止直接执行旧 PC-001,把 PC-001 到 PC-006 重排为待执行计划。 -- 2026-07-09 15:24:02 +08:00:收到用户补充验证码要求;记录 `public/tac` 调用链,确认 PC/H5 登录使用 `WEB_H5_LOGIN`,其他未配置 PC/H5 场景的认证验证码先待确认。 -- 2026-07-09 15:31:14 +08:00:根据用户质疑修正 PC-006;不再把“公共地区/文件接口可用”写成“整页可对接”,改为页面覆盖矩阵和缺口清单。 -- 2026-07-09 15:34:26 +08:00:同步修正详细规划文件 `docs/superpowers/plans/2026-07-09-pc-api-replan.md` 的 Task 6,并将 PC-006 标记为“已修正”。 -- 2026-07-09 15:41:40 +08:00:按最新 YAML 分析结果重写 `docs/superpowers/plans/2026-07-09-pc-api-replan.md`,新增 PC-007;明确旧路径不能作为 PC fallback,缺失业务接口统一降级为接口待确认。 -- 2026-07-09 15:44:18 +08:00:开始执行 PC-001/PC-002,先按 TDD 创建 `config.js` 和 `utils` 公共工具测试,再实现最小配置与工具层。 -- 2026-07-09 15:47:17 +08:00:完成 PC-001 和 PC-002 的公共工具层部分;`config.js` 位于根目录,`utils` 只放跨页面公共 JS,页面业务仍留在 `public/js`。 -- 2026-07-09 15:51:34 +08:00:完成 PC-002 的请求客户端切换;旧路径不再作为网络请求路径,未覆盖业务统一抛 `PC_API_NOT_AVAILABLE`。 -- 2026-07-09 15:55:28 +08:00:PC-003 认证页验证码阶段完成;登录使用 `WEB_H5_LOGIN`,未确认 PC/H5 场景的注册和找回密码不调用验证中心。 -- 2026-07-09 15:57:36 +08:00:收到用户补充截图,确认 PC `clientid` 为 `ced7e5f0498645c6ec642dcf450b036f`、客户端 Key 为 `web_pc`,新增注册场景 `WEB_H5_REGISTER` 和找回密码场景 `WEB_H5_FORGOT_PASSWORD`;同步修正配置、计划和认证页场景映射。 -- 2026-07-09 16:00:22 +08:00:PC-003 完成;认证页、资料页、安全页的 PC auth/captcha 接入和脚本顺序已验证。 -- 2026-07-09 16:03:23 +08:00:PC-004 完成;上传公共能力切换到 PC 文件接口,相关上传页已补齐公共层脚本顺序。 -- 2026-07-09 16:05:12 +08:00:PC-005 完成;地区选择只保留公共行政区划能力,创建家谱等主业务仍按 PC YAML 未覆盖处理。 -- 2026-07-09 16:06:35 +08:00:PC-008 完成;PC YAML 未覆盖业务不会继续发送过期请求,全量 Node 测试通过。 -- 2026-07-09 16:07:42 +08:00:收尾验证完成;全量 `tests/*.test.js` 通过,`public/js` 非压缩业务脚本 `node --check` 通过,过期请求路径、旧验证码场景、旧 PC clientId 扫描无输出;`git diff --check` 因当前目录不是 Git 仓库无法执行。 -- 2026-07-09 16:09:55 +08:00:开始登录、注册、找回密码三页专项核对;按 PC YAML 的 `PasswordLoginBody`、`PasswordRegisterBody`、`SmsCodeBody`、`PasswordResetBody` 修正页面字段、输入类型、验证码场景声明和文案。 -- 2026-07-09 16:12:10 +08:00:登录、注册、找回密码三页专项完成;三页表单已显式声明 `data-captcha-scene`,手机号输入改为 `type="tel"`、`inputmode="numeric"`、`maxlength="11"`,找回密码文案改为只支持注册手机号;`auth-pages.js` 优先读取页面 `data-captcha-scene`。验证:`node tests\auth-pages.test.js`、`node tests\captcha-pages.test.js`、`node tests\api-client.test.js` 通过;三页字段/脚本顺序扫描通过;旧邮箱文案和旧验证码场景扫描无输出。 -- 2026-07-09 16:19:11 +08:00:根据用户反馈“接口文档少了很多东西”重新核对 `genealogy-pc-openapi.yaml` 的真实 `paths`;确认当前 YAML 只有验证中心、认证登录、文件上传、行政区划四类共 25 个操作,虽然声明了家谱、成员、字辈、世系、家族圈、文章、相册、祭祀、族务、VIP、消息、反馈等标签和模型,但没有对应可调用路径。结论:这些业务页面不能继续视为已对接,只能保留 `PC_API_NOT_AVAILABLE`,等待后端补充 PC 接口文档。 -- 2026-07-09 16:48:05 +08:00:处理注册页滑动验证请求失败。确认失败不是注册字段问题,而是测试域名 HTTPS 握手失败;开发环境接口基础地址修正为 HTTP,并兼容清理旧 HTTPS 本地覆盖值。 -- 2026-07-09 16:56:22 +08:00:处理 TAC 验证码显示位置问题。确认不是后端验证码类型问题,也不是 `tac.min.js` 被改;问题来自前端挂载点在表单中,导致 TAC 相对定位弹窗被卡片布局吞进去。 -- 2026-07-09 17:02:35 +08:00:TAC 弹窗挂载修复完成。三张认证页改为页面级固定遮罩容器承载 TAC,表单内部不再放验证码挂载点;新增结构测试防止回退。 -- 2026-07-09 17:04:21 +08:00:复现用户反馈的 `503 Service Unavailable`。`curl.exe` 请求 `/captcha/require` 和域名根路径 `/` 均返回 `Server: SakuraFrp`、`503 Service Unavailable`,响应页提示检查 SakuraFrp 隧道节点、隧道配置、frpc 是否运行、隧道是否在线。结论:这是测试接口域名/内网穿透服务当前不可用,不是前端字段、TAC 样式或请求参数导致。 -## PC-012 认证页 layui 提示与 TAC 弹窗样式修正 - -- 状态:本地复核通过 -- 计划开始时间:2026-07-09 17:14:08 +08:00 -- 计划文件:`docs/superpowers/plans/2026-07-09-auth-layer-message.md` -- 页面范围:`login.html`、`register.html`、`forgot-password.html` -- 接口范围:不新增接口,只修正 `/captcha/*` 流程中的前端提示方式和弹窗展示方式。 -- 完成时间:2026-07-09 17:20:24 +08:00 -- 修改文件:`login.html`、`register.html`、`forgot-password.html`、`utils/MessageUtil.js`、`public/js/auth-pages.js`、`public/js/captcha-pages.js`、`public/css/public.css`、`tests/auth-page-structure.test.js`、`tests/auth-message.test.js`、`docs/superpowers/plans/2026-07-09-auth-layer-message.md` -- 验证结果: - - `node tests\auth-page-structure.test.js` 通过。 - - `node tests\auth-message.test.js` 通过。 - - `node tests\auth-pages.test.js` 通过。 - - `node tests\captcha-pages.test.js` 通过。 - - `node --check public\js\auth-pages.js` 通过。 - - `node --check public\js\captcha-pages.js` 通过。 - - `node --check utils\MessageUtil.js` 通过。 - - `Get-ChildItem -Path tests -Filter *.test.js | Sort-Object Name | ForEach-Object { node $_.FullName }` 全部通过。 -- 剩余风险:测试域名当前仍可能返回 SakuraFrp `503 Service Unavailable`,这是后端/隧道可用性问题,不是本次提示和样式改动导致。 -- 执行计划: - 1. 已完成失败测试:`tests/auth-page-structure.test.js`、`tests/auth-message.test.js`。 - 2. 已完成:三页加载 layui 样式和脚本。 - 3. 已完成:`auth-pages.js`、`captcha-pages.js` 提示统一走 `MessageUtil`/`layui.layer.msg`,不再使用浏览器原生 `alert`。 - 4. 已完成:`public/css/public.css` 增加认证页 layer 提示皮肤。 - 5. 已完成:焦点测试、相关测试、语法检查和全量 Node 测试通过。 - -## PC-013 TAC 取消或请求失败后的遮罩清理 - -- 状态:本地复核通过 -- 计划开始时间:2026-07-10 09:10:03 +08:00 -- 计划文件:`docs/superpowers/plans/2026-07-10-tac-modal-cleanup.md` -- 页面范围:`login.html`、`register.html`、`forgot-password.html` -- 接口范围:`/captcha/require`、`/captcha/challenge`、`/captcha/verify` -- 根因记录:现有 `.auth-captcha-modal` 仅依赖 `:empty` 判断隐藏;TAC 在取消、CORS 失败或 503 失败后仍可能保留子节点,导致外层遮罩层不再为空从而不会消失。 -- 处理计划:由 `captcha-pages.js` 显式管理 `is-active` 状态、`aria-hidden` 和残留 DOM;成功、取消、初始化异常、Promise 失败路径全部调用统一清理函数。 -- 完成时间:2026-07-10 09:15:55 +08:00 -- 修改文件:`public/js/captcha-pages.js`、`public/css/captcha-modal.css`、`login.html`、`register.html`、`forgot-password.html`、`tests/captcha-pages.test.js`、`tests/auth-page-structure.test.js`、`docs/superpowers/plans/2026-07-10-tac-modal-cleanup.md`。 -- 完成结果:`openCaptchaBox` 在每次初始化前清理旧 TAC DOM 并显示遮罩;`closeCaptchaBox` 在成功、取消、初始化异常、Promise 失败后清空 DOM、隐藏遮罩并更新 `aria-hidden`;三张认证页加载新的公共验证码弹窗 CSS。 -- 验证结果:`node tests\\captcha-pages.test.js`、`node tests\\auth-page-structure.test.js`、`node --check public\\js\\captcha-pages.js` 和 `Get-ChildItem -Path tests -Filter *.test.js | Sort-Object Name | ForEach-Object { node $_.FullName }` 均通过。 -- 剩余风险:`/captcha/*` 的 503、CORS 或 TLS 错误属于测试网关/服务端可用性问题;本项只保证它们触发取消或异常后,前端不会继续遗留遮罩。 - -## PC-014 已完成记录全量复核 - -- 状态:已完成 -- 开始时间:2026-07-10 09:20:38 +08:00 -- 计划文件:`docs/superpowers/plans/2026-07-10-pc-tracker-reverification.md` -- 复核范围:PC-000 至 PC-013 中所有标记为“已完成”或“已修正”的项目。 -- 复核规则:以当前 `genealogy-pc-openapi.yaml` 的 `paths`、当前代码、当前 Node 测试、语法检查和静态扫描为准;历史记录只作为待核对内容,不作为证明。 -- 状态规则:当前证据完整才保留“已完成”;代码或记录不一致改为“待修正”;仅受测试网关影响的项目标记“外部环境待验证”。 -- 暂停时间:2026-07-10 09:27:30 +08:00 -- 暂停原因:用户要求删除现有 fetch 请求层并整体切换为 Axios;重构前取得的复核结果不能作为重构后的最终证据。 -- 完成时间:2026-07-10 10:03:18 +08:00 -- 当前结论:上方 PC-000 至 PC-011 及下方 PC-012、PC-013 的复核状态为本次有效状态,历史文件名、历史命令和历史时间仅用于追溯。 -- 验证证据:当前 YAML 路径扫描、Axios 公共层测试、认证/验证码/资料/安全/上传/地区测试、45 页脚本顺序测试、全量 Node 测试、语法检查均通过;受限环境外 `curl.exe -I http://test-genealogy-api.ddxcjp.cn/captcha/require` 返回 `HTTP 200 OK`。 - -## PC-015 Axios 公共请求层重构 - -- 状态:已完成 -- 开始时间:2026-07-10 09:27:30 +08:00 -- 计划文件:`docs/superpowers/plans/2026-07-10-axios-request-layer-refactor.md` -- 目录边界:`utils/axios.js` 存第三方 Axios 非压缩浏览器构建;`utils/AxiosRequestUtil.js` 存公共请求封装;`utils/ApiClient.js` 存跨页面 PC 接口客户端;`public/js` 只存页面业务脚本。 -- 清理目标:删除 `utils/RequestUtil.js` 与 `public/js/api-client.js`,并清除 `fetch`、`fetchImpl`、`root.fetch` 和旧页面脚本引用;不提供兼容分支。 -- 范围修正:2026-07-10 09:27:30 +08:00 初始清单只按旧 RequestUtil 引用列出 14 页;本轮静态扫描发现总共有 45 页加载接口客户端,已修正为自动扫描全部相关 HTML 页面,剩余 31 页必须迁移后才能完成 PC-015。 -- 配置职责修正:2026-07-10 09:47:25 +08:00 参考 `C:\Users\Administrator\Desktop\job\3Dyjs\config.js` 后确认,根目录 `config.js` 只负责开发/生产环境与 API 基础地址;PC clientId、tenantId、token key 和 Authorization header 由 `utils/ApiClient.js` 负责。当前未提供生产地址,生产环境暂时使用用户指定的测试地址,待后端提供生产域名后单独替换。 -- 配置单一来源复核:2026-07-10 10:10:56 +08:00 已移除 `utils/ApiClient.js` 的 `DEFAULT_BASE_URL`;客户端现在只能消费根目录 `config.js` 的 `apiBaseUrl` 或调用方显式传入的 `baseUrl`,缺失时立即抛出明确错误。 -- 完成时间:2026-07-10 10:03:18 +08:00 -- 完成结果:下载 Axios 1.7.9 非压缩浏览器构建至 `utils/axios.js`;创建 `utils/AxiosRequestUtil.js` 与 `utils/ApiClient.js`;删除 `utils/RequestUtil.js` 与 `public/js/api-client.js`;45 页统一加载 `config.js`、`StorageUtil.js`、Axios、AxiosRequestUtil、ApiClient 后再加载页面业务脚本。 -- 验证结果:`node tests\config.test.js`、`node tests\utils.test.js`、`node tests\api-client.test.js`、`node tests\api-script-order.test.js`、全量 Node 测试、`node --check` 和旧 fetch/旧脚本扫描均通过;`git diff --check` 无空白错误。2026-07-10 10:10:56 +08:00 再次回归通过,并确认客户端不再导出 `DEFAULT_BASE_URL`。 - -## PC-016 TAC 原生样式调用清理 - -- 状态:已完成(由 PC-017 修正调用位置) -- 开始时间:2026-07-10 10:19:46 +08:00 -- 计划文件:`docs/superpowers/plans/2026-07-10-tac-native-style-cleanup.md` -- 页面范围:`login.html`、`register.html`、`forgot-password.html` -- 约束:只调用 `public/tac` 自带的 CSS 和 JS;移除项目自定义验证码遮罩、尺寸约束与视觉配置,不改动 TAC 第三方文件。 -- 完成时间:2026-07-10 10:32:20 +08:00 -- 修改结果:删除 `public/css/captcha-modal.css`;删除 `public/css/public.css` 中 35 行自定义验证码规则;三页仅保留 `public/tac/css/tac.css`,并使用无 class 的 `[data-captcha-box]`;`captcha-pages.js` 只调用 `new TAC(config)`。 -- 验证结果:`node tests\auth-page-structure.test.js`、`node tests\captcha-pages.test.js`、全量 Node 测试、`node --check public\js\captcha-pages.js`、自定义验证码视觉标识扫描和 `git diff --check` 均通过。 -- 外部边界:本项只恢复 TAC 原生视觉与调用边界;验证码背景尺寸校验失败仍需以后端挑战响应的原始图片尺寸为依据另行修复。 -- 复核时间:2026-07-10 10:38:10 +08:00 -- 复核结论:TAC 的 `init()` 只将组件追加到 `CaptchaConfig.bindEl`,不负责页面级定位;删除静态挂载点定位样式后,组件被追加到 `body` 末尾,因此渲染到页面底部。本项不能保留“已完成”。 - -## PC-017 TAC 默认弹层调用纠正 - -- 状态:已完成 -- 开始时间:2026-07-10 10:38:10 +08:00 -- 计划文件:`docs/superpowers/plans/2026-07-10-tac-layer-call-correction.md` -- 调用位置:`auth-pages.js` 表单提交前调用 `CaptchaPages.ensureToken`;验证码适配层使用 Layui 默认 `layer.open` 创建临时容器,容器传入 `CaptchaConfig.bindEl`,然后调用 `new TAC(config)`。 -- 功能边界:不限制后端下发的 `SLIDER`、`ROTATE`、`CONCAT`、`WORD_IMAGE_CLICK`、`DISABLED` 类型;刷新由 TAC `reloadCaptcha()` 处理;成功、关闭与异常路径由适配层关闭对应 Layui 层。 -- 完成时间:2026-07-10 10:44:11 +08:00 -- 修改结果:删除三页静态 `[data-captcha-box]`;`captcha-pages.js` 用 Layui 默认 `layer.open` 创建临时容器作为 `CaptchaConfig.bindEl`,并保持 `new TAC(config)` 原样调用;TAC 结束后关闭同一 Layui 层。 -- 验证结果:`node tests\auth-page-structure.test.js`、`node tests\captcha-pages.test.js`、全量 Node 测试、`node --check public\js\captcha-pages.js` 和 `git diff --check` 均通过;静态扫描确认三页、公共 CSS 和验证码适配层没有 `data-captcha-box`、`auth-captcha-modal`、`captcha-modal.css`、`bgUrl` 或 `logoUrl`。 -- 浏览器边界:尚未在真实测试网关完成有效轨迹验证;需在浏览器发起一次注册、登录或找回密码操作,确认 Layui 默认层居中和后端 `validToken` 流程。 - -## PC-018 TAC Layui content 契约修复 - -- 状态:已完成(由 PC-019 修正真实挂载点) -- 开始时间:2026-07-10 10:50:06 +08:00 -- 完成时间:2026-07-10 10:54:03 +08:00 -- 计划文件:`docs/superpowers/plans/2026-07-10-tac-layui-content-contract-fix.md` -- 页面范围:`login.html`、`register.html`、`forgot-password.html` -- 接口范围:`/captcha/require`、`/captcha/challenge`、`/captcha/verify` -- 根因记录:浏览器中 `/captcha/require` 已返回 `200`,但点击提交后提示 `d.parents is not a function` 且 TAC 不显示。根因是 `captcha-pages.js` 将原生 DOM 节点直接传入 `layui.layer.open({ content })`;当前 Layui 2.11.5 弹层内部会对 `content` 调用 `.parents()`,原生 DOM 没有该方法,导致弹层创建中断。 -- 修改文件:`public/js/captcha-pages.js`、`tests/captcha-pages.test.js`、`docs/superpowers/plans/2026-07-10-tac-layui-content-contract-fix.md`、`docs/pc-api-page-integration-tracker-2026-07-09.md` -- 修改结果:`createCaptchaLayer` 现在使用 `layui.$`、`$` 或 `jQuery` 包装临时 DOM 后传给 `layer.open`;返回给 TAC 的 `box` 仍是真实 DOM 节点,继续作为 `CaptchaConfig.bindEl`。未修改 `public/tac`,未新增验证码 CSS,未传 TAC 视觉配置。 -- 验证结果:先运行 `node tests\captcha-pages.test.js` 复现失败;修复后 `node tests\auth-page-structure.test.js`、`node tests\captcha-pages.test.js`、`node --check public\js\captcha-pages.js` 和 `git diff --check` 通过。`git diff --check` 仅输出既有 CRLF 行尾提示,没有空白错误。 - -## PC-020 TAC Layui 弹层居中修复 - -- 状态:已完成 -- 开始时间:2026-07-10 11:11:05 +08:00 -- 完成时间:2026-07-10 11:14:31 +08:00 -- 计划文件:`docs/superpowers/plans/2026-07-10-tac-layer-center-fix.md` -- 页面范围:`login.html`、`register.html`、`forgot-password.html` -- 接口范围:`/captcha/require`、`/captcha/challenge`、`/captcha/verify` -- 根因记录:Layui 打开弹层时验证码挂载点仍是空节点,TAC 后续异步创建自己的 `#tianai-captcha-parent`,原生尺寸为 `318px * 318px`,导致 Layui 先按空内容定位后,最终验证码窗口偏离视觉中心。 -- 修改文件:`public/js/captcha-pages.js`、`tests/captcha-pages.test.js`、`docs/superpowers/plans/2026-07-10-tac-layer-center-fix.md`、`docs/pc-api-page-integration-tracker-2026-07-09.md` -- 修改结果:`createCaptchaLayer` 使用 Layui 默认 `layer.open` 时增加 `area: ['318px', '318px']` 与 `offset: 'auto'`,尺寸来自 TAC 原生 `#tianai-captcha-parent`;未修改 `public/tac`,未新增验证码自定义 CSS,未传 TAC 视觉配置。 -- 验证结果:先运行 `node tests\captcha-pages.test.js` 复现 `layerOptions.area` 缺失失败;修复后 `node tests\captcha-pages.test.js`、`node tests\auth-page-structure.test.js`、`node --check public\js\captcha-pages.js` 和 `git diff --check` 通过。`git diff --check` 仅输出既有 LF/CRLF 换行提示,没有空白错误。 - -## PC-021 验证结果参数格式修正 - -- 状态:已完成 -- 开始时间:2026-07-10 14:31:14 +08:00 -- 完成时间:2026-07-10 14:34:28 +08:00 -- 计划文件:`docs/superpowers/plans/2026-07-10-captcha-verify-track-contract.md` -- 页面范围:`login.html`、`register.html`、`forgot-password.html` -- 接口范围:`POST /captcha/verify` -- 根因记录:后端最新示例要求验证轨迹提交为 `payload.track`,但前端仍按旧契约提交 `payload.id` 与 `payload.data`,并缺少 `left/top`。 -- 修改文件:`public/js/captcha-pages.js`、`tests/captcha-pages.test.js`、`docs/superpowers/plans/2026-07-10-captcha-verify-track-contract.md`、`docs/pc-api-page-integration-tracker-2026-07-09.md` -- 修改结果:`buildVerifyBody` 现在提交 `payload.track`;`left/top` 优先使用已有值,缺失时由轨迹首尾点差值计算;旧的 `payload.id` 与 `payload.data` 不再作为 `/captcha/verify` 请求体提交。 -- 验证结果:先运行 `node tests\captcha-pages.test.js` 复现旧请求体失败;修复后 `node tests\captcha-pages.test.js`、`node tests\auth-page-structure.test.js`、`node tests\auth-pages.test.js`、`node --check public\js\captcha-pages.js` 和 `git diff --check` 通过。`git diff --check` 仅输出 LF/CRLF 换行提示,没有空白错误。 - -## PC-022 登录页样式优化 - -- 状态:已完成 -- 开始时间:2026-07-10 14:42:32 +08:00 -- 完成时间:2026-07-10 14:50:23 +08:00 -- 计划文件:`docs/superpowers/plans/2026-07-10-login-auth-flow-polish.md` -- 页面范围:`login.html` -- 修改文件:`public/css/login.css` -- 修改结果:登录方式切换区改为圆角分段控件,选中项使用浅红底和红字;按钮与底部链接增加间距。样式全部写在登录页 CSS 中,未通过 JS 注入样式。 -- 验证结果:`node tests\auth-page-structure.test.js`、`node tests\auth-pages.test.js`、`node tests\captcha-pages.test.js`、`node --check public\js\auth-pages.js`、`node --check public\js\captcha-pages.js` 和 `git diff --check` 通过。`git diff --check` 仅输出 LF/CRLF 换行提示,没有空白错误。 - -## PC-023 注册跳转与验证码取票据流程复核 - -- 状态:已完成 -- 开始时间:2026-07-10 14:42:32 +08:00 -- 完成时间:2026-07-10 14:50:23 +08:00 -- 计划文件:`docs/superpowers/plans/2026-07-10-login-auth-flow-polish.md` -- 页面范围:`login.html`、`register.html`、`forgot-password.html` -- 接口范围:登录、注册、忘记密码提交前的 `/captcha/verify` -- 根因记录:`register.html` 仍把注册成功地址写为 `create-genealogy.html`,公共认证脚本也保留同样兜底地址;但创建家谱属于登录后的行为,注册完成应回到登录页。 -- 修改文件:`register.html`、`public/js/auth-pages.js`、`tests/auth-page-structure.test.js` -- 修改结果:注册页 `data-success-url` 改为 `login.html`,注册提交的兜底跳转也改为 `login.html`;结构测试补充三张认证页验证码场景和 `validToken` 隐藏字段检查。公共 `CaptchaPages` 仍统一用 `payload.track` 调 `/captcha/verify` 换取 `validToken`,登录、注册、忘记密码表单提交前共用这一入口。 -- 验证结果:先运行 `node tests\auth-page-structure.test.js` 复现注册跳转失败;修复后 `node tests\auth-page-structure.test.js`、`node tests\auth-pages.test.js`、`node tests\captcha-pages.test.js`、`node --check public\js\auth-pages.js`、`node --check public\js\captcha-pages.js` 和 `git diff --check` 通过。`git diff --check` 仅输出 LF/CRLF 换行提示,没有空白错误。 - -## PC-024 认证页表单校验和结构规范化 - -- 状态:已完成 -- 开始时间:2026-07-10 14:56:08 +08:00 -- 完成时间:2026-07-10 15:11:49 +08:00 -- 计划文件:`docs/superpowers/plans/2026-07-10-auth-form-validation-normalization.md` -- 页面范围:`login.html`、`register.html`、`forgot-password.html` -- 接口范围:登录、短信登录、注册、找回密码提交前的本地校验与验证码取票据 -- 结论记录:`form` 标签本身不是落后写法,认证提交页保留语义表单更合适;但 `data-captcha-scene`、`data-success-url`、`data-scene-code` 这类业务配置不应散在 HTML 上。 -- 修改文件:`login.html`、`register.html`、`forgot-password.html`、`public/js/auth-pages.js`、`tests/auth-pages.test.js`、`tests/auth-page-structure.test.js` -- 修改结果:三张认证页表单改用稳定 `id` 作为 JS 挂载点,移除认证业务配置型 `data-*`;登录方式切换改用标准 `aria-controls` 关联表单面板;`auth-pages.js` 新增 `FORM_CONFIG` 统一管理场景码和跳转地址;新增 `validateAuthValues` 统一校验手机号、密码、短信验证码和确认密码。登录、注册、忘记密码提交前仍统一走 `CaptchaPages.ensureToken`,由 `captcha-pages.js` 按 `payload.track` 调 `/captcha/verify` 换取 `validToken`。 -- 验证结果:先运行 `node tests\auth-page-structure.test.js` 与 `node tests\auth-pages.test.js` 复现失败;修复后 `node tests\auth-page-structure.test.js`、`node tests\auth-pages.test.js`、`node tests\captcha-pages.test.js`、`node --check public\js\auth-pages.js`、`node --check public\js\captcha-pages.js` 和 `git diff --check` 通过。扫描确认三张认证页和 `auth-pages.js` 无 `data-auth-form`、`data-captcha-scene`、`data-success-url`、`data-scene-code`、`data-login-mode` 残留;`git diff --check` 仅输出 LF/CRLF 换行提示,没有空白错误。 -- 复核结论:该项只解决 `d.parents is not a function`;用户 2026-07-10 10:59 左右浏览器复测显示 Layui 遮罩出现、`/captcha/challenge` 返回 200,但 TAC 本体未显示,因此真实挂载点问题进入 PC-019 修正。 - -## PC-019 TAC Layui 实际挂载点修复 - -- 状态:已完成 -- 开始时间:2026-07-10 10:59:52 +08:00 -- 完成时间:2026-07-10 11:04:18 +08:00 -- 计划文件:`docs/superpowers/plans/2026-07-10-tac-layer-real-mount-fix.md` -- 页面范围:`login.html`、`register.html`、`forgot-password.html` -- 接口范围:`/captcha/require`、`/captcha/challenge`、`/captcha/verify` -- 根因记录:用户浏览器截图显示 Layui 遮罩已经出现,`/captcha/require` 与 `/captcha/challenge` 都返回 `200`,但弹层内没有 TAC 验证窗口。根因是 PC-018 仍返回打开前的临时 DOM 给 `CaptchaConfig.bindEl`,不能保证该节点就是 Layui 弹层内容区里的真实节点。 -- 修改文件:`public/js/captcha-pages.js`、`tests/captcha-pages.test.js`、`docs/superpowers/plans/2026-07-10-tac-layer-real-mount-fix.md`、`docs/pc-api-page-integration-tracker-2026-07-09.md` -- 修改结果:`createCaptchaLayer` 现在用字符串 `content` 让 Layui 创建 `
`,弹层打开后再通过 `document.querySelector` 获取这个实际节点,并将它作为 TAC 的 `bindEl`。未修改 `public/tac`,未新增验证码 CSS,未传 TAC 视觉配置。 -- 验证结果:先运行 `node tests\captcha-pages.test.js` 复现失败;修复后 `node tests\auth-page-structure.test.js`、`node tests\captcha-pages.test.js`、`node --check public\js\captcha-pages.js` 和 `git diff --check` 通过。`git diff --check` 仅输出既有 CRLF 行尾提示,没有空白错误。 - -## PC-025 注册、登录到个人资料闭环 - -- 状态:已完成 -- 完成时间:2026-07-11 09:32:35 +08:00 -- 规划文件:`docs/superpowers/specs/2026-07-11-auth-to-profile-flow-design.md` -- 页面范围:`register.html`、`login.html`、`profile.html`、`profile-data.html` -- 接口范围:`/genealogy/pc/auth/register`、`/genealogy/pc/auth/login`、`/genealogy/pc/auth/login/sms`、`/genealogy/pc/auth/profile`、`/genealogy/pc/auth/logout` -- 地址契约:运行时唯一来源为 `config.js`;当前开发和生产地址均为 `http://182.61.18.23:8080`。 -- 流程结果:注册成功清除 token 并跳转 `login.html`;当前登录响应从 `data.access_token` 取 JWT,且兼容 `token`、`accessToken`、`tokenValue` 后跳转 `profile.html`;个人资料无 token 或收到 HTTP/业务 `401` 时清 token 并跳转 `index.html`;确认退出无论接口结果都清 token 并跳转 `index.html`。 -- 修改文件:`tests/config.test.js`、`tests/utils.test.js`、`utils/ApiClient.js`、`tests/api-client.test.js`、`public/js/profile-pages.js`、`public/js/profile-common.js`、`profile.html`、`tests/profile-pages.test.js`、`tests/profile-logout-structure.test.js`。 -- 验证结果:`node tests/config.test.js`、`node tests/utils.test.js`、`node tests/api-client.test.js`、`node tests/auth-pages.test.js`、`node tests/profile-pages.test.js`、`node tests/profile-logout-structure.test.js`、`node --check utils/ApiClient.js`、`node --check public/js/profile-pages.js`、`node --check public/js/profile-common.js` 和全量 Node 测试均通过。 - -## PC-026 安全页认证入口收敛 - -- 状态:部分完成 -- 完成时间:2026-07-11 -- 页面范围:`profile-security.html` -- 接口范围:`/genealogy/pc/auth/login/sms`、`/genealogy/pc/auth/phone`、`/genealogy/pc/auth/account/deactivate`、`/genealogy/pc/auth/logout` -- 契约结果:`login.html` 是密码和短信登录的唯一入口;登录成功跳转 `profile.html`。已登录的账号安全页不得重复发起登录流程。退出登录和账号注销完成后均跳转 `index.html`。 -- 修改结果:删除安全页遗留的短信登录表单、旧接口说明、请求体构造和提交分支;安全页退出无论接口请求结果都跳转首页,账号注销成功后也跳转首页。 -- 后端阻塞:换绑手机仍无法形成完整 PC/H5 流程。`POST /genealogy/pc/auth/sms/code` 必填 `sceneCode` 和 `validToken`,但当前未定义换绑手机的 PC/H5 场景码;不得使用未经确认的场景替代。待后端提供场景码后,补充滑动验证、发送短信验证码和换绑提交链路。 -- 验证结果:先运行 `node tests/security-pages.test.js`,确认旧安全页短信登录导出仍存在而按预期失败;修复后该测试通过。全量验证待本项后续记录补充。 - -## PC-027 PC-only 接口契约清理 - -- 状态:已完成 -- 完成时间:2026-07-11 -- 契约所有者:`genealogy-pc-openapi.yaml` 是 PC/H5 页面接口的唯一依据;运行时请求由 `utils/ApiClient.js` 统一发起。 -- 修改结果:删除旧接口文档和错误对接记录;清除页面、脚本、规划和 PC YAML 中遗留的旧路径、场景码与文档引用;PC YAML 的验证码示例统一使用 `WEB_H5_LOGIN`,注册来源示例为 `PC`。 -- 登录确认:`login.html` 的短信登录继续经 `public/js/auth-pages.js` 调用 `utils/ApiClient.js`,请求 `POST /genealogy/pc/auth/login/sms`,验证码场景为 `WEB_H5_LOGIN`。 -- 验证结果:新增 `tests/pc-api-contract.test.js`。该测试确认旧接口文档与错误记录不存在,扫描当前运行代码、PC YAML 和有效规划没有遗留旧契约,并断言短信登录使用 PC 路径和 `WEB_H5_LOGIN`。 - -## PC-028 当前 PC 接口页面覆盖 - -- 状态:部分完成 -- 完成时间:2026-07-11 -- 接口清单:`genealogy-pc-openapi.yaml` 当前有 25 个操作;`tests/pc-endpoint-coverage.test.js` 对每项操作断言页面调用或明确后端阻塞原因。 -- 已形成页面闭环的 21 项操作:验证码查询/挑战/校验,注册、密码登录、短信登录、短信验证码,资料读取/修改,修改密码、找回密码、注销账号、退出登录,单文件上传、分片初始化/上传/完成,地区下级查询/搜索/路径/详情。 -- 文件上传结果:`profile-data.html` 的头像上传不再要求手填 OSS ID。文件不超过 4 MB 时调用单文件上传;超过 4 MB 时依次调用分片初始化、每片上传和完成上传,完成后回填同一 `avatarOssId`,由“保存资料”提交资料接口。 -- 地区结果:`profile-data.html` 支持按地区名称或编码搜索;选择结果会查询地区详情和路径,再回填省、市、区县选择器后提交资料接口。 -- 后端契约阻塞的 4 项操作: - 1. `GET /auth/code`:接口是兼容旧图形验证码,但当前 PC 登录/注册请求体没有可提交的验证码字段。 - 2. `PUT /genealogy/pc/auth/phone`:发送短信前需要 PC/H5 换绑手机号验证码场景码,当前文档未提供。 - 3. `POST /genealogy/pc/files/reference`:缺少可由当前 PC 页面创建的业务表名和业务 ID 契约。 - 4. `DELETE /genealogy/pc/files/reference`:同样缺少业务表名、业务 ID 和业务字段的来源契约。 -- 浏览器验收顺序: - 1. `register.html`:完成注册,确认跳转 `login.html` 且不保留登录态。 - 2. `login.html` 密码登录:完成滑块验证和登录,确认跳转 `profile.html`。 - 3. `login.html` 短信登录:完成滑块验证、发送短信和登录,确认跳转 `profile.html`。 - 4. `profile.html`:确认资料成功加载;清空本地 token 后重新进入,确认跳转首页。 - 5. `profile-data.html`:修改昵称、性别、生日后保存;上传小于等于 4 MB 的头像并保存资料。 - 6. `profile-data.html`:上传大于 4 MB 的头像,确认网络依次出现 `resumable/init`、`resumable/chunk`、`resumable/complete`,再保存资料。 - 7. `profile-data.html`:搜索地区名称或编码,选择结果,确认省市区回填后保存。 - 8. `forgot-password.html`:完成验证码、短信码和密码重置。 - 9. `profile-security.html`:分别测试修改密码、账号注销和退出登录;退出与注销成功后均回首页。 -- 验证结果:`node tests/pc-endpoint-coverage.test.js`、`node tests/upload-pages.test.js`、`node tests/region-pages.test.js`、`node tests/profile-pages.test.js`、`node tests/auth-pages.test.js`、`node tests/captcha-pages.test.js`、`node tests/api-client.test.js`、`node tests/security-pages.test.js` 均通过。 diff --git a/docs/superpowers/plans/2026-07-09-auth-layer-message.md b/docs/superpowers/plans/2026-07-09-auth-layer-message.md deleted file mode 100644 index bf1de1e..0000000 --- a/docs/superpowers/plans/2026-07-09-auth-layer-message.md +++ /dev/null @@ -1,128 +0,0 @@ -# Auth Layer Message Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** 让登录、注册、忘记密码三页的提示统一使用 layui `layer.msg`,不再出现浏览器原生 alert,并补好认证页提示和 TAC 弹窗样式。 - -**Architecture:** 页面加载 `public/layui/css/layui.css` 和 `public/layui/layui.js`;`auth-pages.js` 与 `captcha-pages.js` 只负责调用提示,不写样式;提示皮肤与 TAC 弹窗样式写入 `public/css/public.css`。 - -**Tech Stack:** 原生 HTML/CSS/JavaScript、layui layer、public/tac、Node 断言测试。 - -## Global Constraints - -- 计划开始时间:2026-07-09 17:14:08 +08:00。 -- 只处理登录、注册、忘记密码三页的提示与弹窗样式问题。 -- 不修改 `public/tac/js/tac.min.js`。 -- 不使用 HTML 原生 `alert` 作为用户提示。 -- 样式写在 CSS 文件里,JS 只切 class 或调用组件。 -- 新增或修改的 HTML、CSS、JS 注释使用中文,代码保持可读。 - ---- - -### Task 1: 补充失败测试 - -**Files:** -- Modify: `tests/auth-page-structure.test.js` -- Create: `tests/auth-message.test.js` - -**Interfaces:** -- Consumes: 当前三页 HTML 与认证脚本。 -- Produces: 能捕获缺少 layui 和原生 alert 兜底的测试。 - -- [x] **Step 1: 添加页面结构断言** - -检查 `login.html`、`register.html`、`forgot-password.html` 必须加载 `public/layui/css/layui.css` 和 `public/layui/layui.js`,且 `layui.js` 在 `auth-pages.js` 前面。 - -- [x] **Step 2: 添加提示脚本断言** - -检查 `public/js/auth-pages.js` 与 `public/js/captcha-pages.js` 不再包含 `root.alert`,并检查 `public/css/public.css` 提供 `.auth-layer-message`。 - -- [x] **Step 3: 验证红灯** - -运行: - -```powershell -node tests\auth-page-structure.test.js -node tests\auth-message.test.js -``` - -预期:两个测试均失败,分别提示缺少 layui 和仍存在 `root.alert`。 - -### Task 2: 接入 layui 并替换原生提示 - -**Files:** -- Modify: `login.html` -- Modify: `register.html` -- Modify: `forgot-password.html` -- Modify: `public/js/auth-pages.js` -- Modify: `public/js/captcha-pages.js` - -**Interfaces:** -- Consumes: `layui.layer.msg(message, options)`。 -- Produces: `showMessage(message)` 统一使用 layer,缺少 layui 时只输出控制台警告,不再弹原生 alert。 - -- [x] **Step 1: 三页加载 layui 样式和脚本** - -在三页 `` 中加入 `public/layui/css/layui.css`,在 `auth-pages.js` 前加入 `public/layui/layui.js`。 - -- [x] **Step 2: 修改认证脚本提示** - -把 `auth-pages.js` 的 `showMessage` 改为优先调用 `layui.layer.msg(message, { skin: 'auth-layer-message' })`,没有 layui 时使用 `console.warn`。 - -- [x] **Step 3: 修改验证码脚本提示** - -把 `captcha-pages.js` 的 `showMessage` 同步改为同样的 layer 提示策略。 - -### Task 3: 补齐公共样式 - -**Files:** -- Modify: `public/css/public.css` - -**Interfaces:** -- Consumes: layui layer 生成的 `.auth-layer-message`。 -- Produces: 认证页统一提示皮肤和移动端更稳的 TAC 弹窗尺寸。 - -- [x] **Step 1: 添加 layui 消息皮肤** - -在公共 CSS 中添加 `.auth-layer-message`,控制圆角、背景、文字和阴影。 - -- [x] **Step 2: 微调 TAC 弹窗移动端尺寸** - -继续使用 `.auth-captcha-modal` 页面级挂载,限制 TAC 宽度不超过视口,避免盖层内容顶到边缘。 - -### Task 4: 验证并记录完成 - -**Files:** -- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md` - -**Interfaces:** -- Consumes: Task 1-3 的实现结果。 -- Produces: PC-012 完成记录。 - -- [x] **Step 1: 跑焦点测试** - -运行: - -```powershell -node tests\auth-page-structure.test.js -node tests\auth-message.test.js -``` - -预期:全部通过。 - -- [x] **Step 2: 跑相关测试和语法检查** - -运行: - -```powershell -node tests\auth-pages.test.js -node tests\captcha-pages.test.js -node --check public\js\auth-pages.js -node --check public\js\captcha-pages.js -``` - -预期:全部通过。 - -- [x] **Step 3: 更新追踪记录** - -把 PC-012 标记为已完成,写入完成时间、修改文件、验证命令和剩余风险。 diff --git a/docs/superpowers/plans/2026-07-09-pc-api-replan.md b/docs/superpowers/plans/2026-07-09-pc-api-replan.md deleted file mode 100644 index aa27ded..0000000 --- a/docs/superpowers/plans/2026-07-09-pc-api-replan.md +++ /dev/null @@ -1,761 +0,0 @@ -# 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 the PC auth/files contract** - -Run: `node tests\pc-api-contract.test.js` -Expected: PC 请求契约扫描通过。 - -- [ ] **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 the PC file contract** - -Run: `node tests\pc-api-contract.test.js` -Expected: PC 请求契约扫描通过。 - ---- - -### 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 obsolete business path is emitted from the API client** - -Run: `node tests\pc-api-contract.test.js` -Expected: PC 请求契约扫描通过。 - ---- - -### 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 } -node tests\pc-api-contract.test.js -git diff --check -``` - -Expected: -- All listed Node tests pass. -- Business JS syntax check passes. -- PC 请求契约扫描通过。 -- 认证和验证码代码/tests 不含过期场景默认值。 -- `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. diff --git a/docs/superpowers/plans/2026-07-09-pc-auth-three-pages.md b/docs/superpowers/plans/2026-07-09-pc-auth-three-pages.md deleted file mode 100644 index e875082..0000000 --- a/docs/superpowers/plans/2026-07-09-pc-auth-three-pages.md +++ /dev/null @@ -1,127 +0,0 @@ -# PC Auth Three Pages 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` 和 Apifox 截图,把登录、注册、忘记密码三页与 PC 认证接口、验证中心接口对齐。 - -**Architecture:** 根目录 `config.js` 继续作为环境、`clientid`、`tenantId` 的唯一配置入口;`utils` 继续存放跨页面公共工具;页面业务逻辑集中在 `public/js/auth-pages.js`,接口调用集中在 `public/js/api-client.js`,验证码适配集中在 `public/js/captcha-pages.js`。页面差异样式只写入对应 CSS 文件。 - -**Tech Stack:** 原生 HTML/CSS/JavaScript、`public/tac` 滑动验证、Node 断言测试。 - -## Global Constraints - -- 接口依据只使用 `genealogy-pc-openapi.yaml` 与用户补充的 Apifox 截图。 -- 开发环境接口地址为 `https://test-genealogy-api.ddxcjp.cn`。 -- PC `clientid` 为 `ced7e5f0498645c6ec642dcf450b036f`,租户 ID 为 `000000`。 -- 登录验证码场景为 `WEB_H5_LOGIN`,注册验证码场景为 `WEB_H5_REGISTER`,忘记密码验证码场景为 `WEB_H5_FORGOT_PASSWORD`。 -- 不在 JS 中注入样式;页面差异样式写入对应 CSS 文件,公共样式写入 `public/css/public.css`。 -- 新增或修改的 HTML、CSS、JS 注释使用中文,代码保持可读,不压缩。 - ---- - -### Task 1: 记录专项计划 - -**Files:** -- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md` -- Create: `docs/superpowers/plans/2026-07-09-pc-auth-three-pages.md` - -**Interfaces:** -- Consumes: 用户确认“可以”先做登录、注册、忘记密码三页。 -- Produces: PC-009 专项计划记录。 - -- [x] **Step 1: 写入本计划** - -创建本计划,明确页面、接口、验证码场景、样式和验证约束。 - -- [x] **Step 2: 写入追踪记录** - -在追踪文档中新增 `PC-009`,状态为“进行中”,开始时间为 `2026-07-09 16:32:17 +08:00`。 - -### Task 2: 登录页补齐短信登录 - -**Files:** -- Modify: `login.html` -- Modify: `public/css/login.css` -- Modify: `public/js/auth-pages.js` -- Modify: `tests/auth-pages.test.js` -- Modify: `tests/api-client.test.js` - -**Interfaces:** -- Consumes: `ApiClient.login(body)`、`ApiClient.loginBySms(body)`、`ApiClient.sendSmsCode(body)`、`CaptchaPages.ensureToken(form, options)`。 -- Produces: 登录页同时支持密码登录和短信登录。 - -- [x] **Step 1: 页面增加登录方式切换** - -在 `login.html` 的登录表单内增加模式切换按钮,密码登录区域保留 `phone/password/validToken`,短信登录区域增加 `phone/smsCode/validToken` 和发送验证码按钮。 - -- [x] **Step 2: CSS 写入登录页专属样式** - -在 `public/css/login.css` 增加模式切换、隐藏区域、验证码行样式,不在 JS 中写样式。 - -- [x] **Step 3: JS 支持短信登录提交** - -在 `public/js/auth-pages.js` 增加 `buildSmsLoginBody(values)`,提交时按当前模式调用 `/genealogy/pc/auth/login` 或 `/genealogy/pc/auth/login/sms`。 - -- [x] **Step 4: 发送短信复用场景码** - -登录页短信验证码按钮使用 `data-scene-code="WEB_H5_LOGIN"`,忘记密码页继续使用 `WEB_H5_FORGOT_PASSWORD`。 - -- [x] **Step 5: 更新测试** - -`tests/auth-pages.test.js` 增加短信登录 body 断言;`tests/api-client.test.js` 增加 `loginBySms` 和 `sendSmsCode` 路径/入参断言。 - -### Task 3: 注册和忘记密码复核 - -**Files:** -- Verify: `register.html` -- Verify: `forgot-password.html` -- Verify: `public/js/auth-pages.js` - -**Interfaces:** -- Consumes: `/genealogy/pc/auth/register`、`/genealogy/pc/auth/sms/code`、`/genealogy/pc/auth/password/reset`。 -- Produces: 注册和忘记密码页面确认继续按 PC 字段提交。 - -- [x] **Step 1: 注册页字段复核** - -确认注册页只提交 `phone/password/nickName/validToken`,`grantType/tenantId/clientId/registerSource` 由 `api-client.js` 统一补齐。 - -- [x] **Step 2: 忘记密码页字段复核** - -确认忘记密码页提交 `tenantId/phone/smsCode/newPassword/validToken`,不再出现邮箱找回文案。 - -### Task 4: 专项验证和完成记录 - -**Files:** -- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md` - -**Interfaces:** -- Consumes: Task 2 和 Task 3 的实现结果。 -- Produces: PC-009 完成记录和验证结果。 - -- [x] **Step 1: 运行测试** - -运行: - -```powershell -node tests\auth-pages.test.js -node tests\api-client.test.js -node tests\captcha-pages.test.js -``` - -期望全部输出 `passed`。 - -- [x] **Step 2: 运行语法检查** - -运行: - -```powershell -node --check public\js\auth-pages.js -node --check public\js\api-client.js -node --check public\js\captcha-pages.js -``` - -期望无输出且退出码为 0。 - -- [x] **Step 3: 更新追踪文档** - -把 `PC-009` 状态改为“已完成”,写入完成时间、修改文件、接口路径、验证码场景和验证结果。 diff --git a/docs/superpowers/plans/2026-07-09-tac-captcha-auth.md b/docs/superpowers/plans/2026-07-09-tac-captcha-auth.md deleted file mode 100644 index d499fc5..0000000 --- a/docs/superpowers/plans/2026-07-09-tac-captcha-auth.md +++ /dev/null @@ -1,95 +0,0 @@ -# TAC Captcha Auth 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:** 将 `public/tac` 滑动验证接入登录、注册、忘记密码三个认证页面,并按 `genealogy-pc-openapi.yaml` 的验证中心接口换取 `validToken` 后再提交业务接口。 - -**Architecture:** 新增 `public/js/captcha-pages.js` 作为 TAC 与验证中心接口之间的适配层;`auth-pages.js` 在提交登录、注册、发送短信、重置密码前调用验证码适配层。API 基础地址、clientId、tenantId 继续由 `api-client.js` 统一提供。 - -**Tech Stack:** 静态 HTML、原生 JS、现有 `public/tac/js/tac.min.js`、现有 `public/tac/css/tac.css`、Node 单元测试。 - -## Global Constraints - -- 严格依据 `genealogy-pc-openapi.yaml` 的 `/captcha/require`、`/captcha/challenge`、`/captcha/verify` 字段接入。 -- 业务 JS 不压缩,新增注释使用中文。 -- 自有样式写入 CSS 文件,不通过自有 JS 注入样式。 -- 第三方 `tac.min.js` 保持原样,不重写其内部样式和 DOM 生成逻辑。 -- 登录、注册、忘记密码三页都必须有隐藏 `validToken` 字段和验证码挂载容器。 - ---- - -### Task 1: API Client Captcha Helpers - -**Files:** -- Modify: `public/js/api-client.js` -- Modify: `tests/api-client.test.js` - -**Interfaces:** -- Produces: `client.buildApiUrl(path, query)` -- Produces: `client.captchaRequirement({ sceneCode, subject })` - -- [ ] **Step 1: Write failing tests** for captcha requirement URL, query, and public URL building. -- [ ] **Step 2: Run tests** and confirm missing methods fail. -- [ ] **Step 3: Implement minimal API client helpers**. -- [ ] **Step 4: Run tests** and confirm pass. - -### Task 2: TAC Adapter Module - -**Files:** -- Create: `public/js/captcha-pages.js` -- Create: `tests/captcha-pages.test.js` - -**Interfaces:** -- Produces: `CaptchaPages.buildChallengeBody(options, api)` -- Produces: `CaptchaPages.adaptChallengeResponse(response)` -- Produces: `CaptchaPages.buildVerifyBody(requestData, context, api)` -- Produces: `CaptchaPages.extractValidToken(response)` -- Produces: `CaptchaPages.ensureToken(form, options)` - -- [ ] **Step 1: Write failing tests** for OpenAPI-to-TAC mapping and validToken extraction. -- [ ] **Step 2: Run tests** and confirm module missing fails. -- [ ] **Step 3: Implement adapter with Chinese comments**. -- [ ] **Step 4: Run tests** and confirm pass. - -### Task 3: Auth Flow Integration - -**Files:** -- Modify: `public/js/auth-pages.js` -- Modify: `tests/auth-pages.test.js` - -**Interfaces:** -- Consumes: `CaptchaPages.ensureToken(form, { sceneCode, subject })` -- Updates: login/register/password reset/send code flows wait for captcha before API submission. - -- [ ] **Step 1: Write failing tests** for login body validToken and scene code mapping. -- [ ] **Step 2: Run tests** and confirm expected failure. -- [ ] **Step 3: Integrate captcha gate before submit/send code**. -- [ ] **Step 4: Run tests** and confirm pass. - -### Task 4: HTML/CSS Wiring - -**Files:** -- Modify: `login.html` -- Modify: `register.html` -- Modify: `forgot-password.html` -- Modify: `public/css/public.css` - -**Interfaces:** -- Adds: `` -- Adds: hidden `validToken` -- Adds: `data-captcha-box` -- Adds: `public/tac/js/tac.min.js` and `public/js/captcha-pages.js` - -- [ ] **Step 1: Add page hooks** with Chinese HTML comments where helpful. -- [ ] **Step 2: Add CSS-only layout for captcha container**. -- [ ] **Step 3: Run static checks** for scripts, hooks, no self JS style injection. - -### Task 5: Verification And Records - -**Files:** -- Modify: `docs/api-page-integration-2026-07-09.md` - -- [ ] **Step 1: Run all Node tests**. -- [ ] **Step 2: Run `node --check` for all business JS**. -- [ ] **Step 3: Run static checks for HTML hooks and style-injection patterns**. -- [ ] **Step 4: Append dated progress record**. diff --git a/docs/superpowers/plans/2026-07-10-auth-form-validation-normalization.md b/docs/superpowers/plans/2026-07-10-auth-form-validation-normalization.md deleted file mode 100644 index ac16a2a..0000000 --- a/docs/superpowers/plans/2026-07-10-auth-form-validation-normalization.md +++ /dev/null @@ -1,26 +0,0 @@ -# 2026-07-10 认证页表单校验和结构规范化计划 - -- 计划编号:PC-024 -- 开始时间:2026-07-10 14:56:08 +08:00 -- 页面范围:`login.html`、`register.html`、`forgot-password.html` -- 接口范围:登录、短信登录、注册、找回密码提交前的本地校验与验证码取票据 - -## 结论 - -- 当前三页只有很弱的前端校验,主要是必填判断和找回密码两次密码一致校验,不算完整表单验证。 -- `
` 标签不是落后写法,它是提交类页面的语义结构;但把 `data-captcha-scene`、`data-success-url` 这类业务配置写在 HTML 上不够集中,后续维护容易散。 - -## 执行清单 - -1. [x] 写失败测试:认证页不能继续把业务配置写在表单 `data-*` 属性上。 -2. [x] 写失败测试:认证页 JS 要提供统一表单校验函数。 -3. [x] HTML 表单改为使用 `id` 作为挂载点。 -4. [x] `auth-pages.js` 增加统一表单配置和校验函数。 -5. [x] 保持三页提交前统一走 `CaptchaPages.ensureToken`,并继续使用 `payload.track` 换取票据。 -6. [x] 运行结构测试、业务测试、验证码测试、语法检查和空白检查。 - -## 验证记录 - -- 2026-07-10 14:56:08 +08:00:开始记录 PC-024,并补充结构和校验测试。 -- 2026-07-10 14:56:08 +08:00:`node tests\auth-page-structure.test.js` 与 `node tests\auth-pages.test.js` 已按预期失败,失败点为 HTML 未使用表单 `id`、JS 缺少 `sms-login` 场景配置和统一校验函数。 -- 2026-07-10 15:11:49 +08:00:修复后 `node tests\auth-page-structure.test.js`、`node tests\auth-pages.test.js`、`node tests\captcha-pages.test.js`、`node --check public\js\auth-pages.js`、`node --check public\js\captcha-pages.js` 均通过;`git diff --check` 无空白错误,仅有 LF/CRLF 换行提示。 diff --git a/docs/superpowers/plans/2026-07-10-axios-request-layer-refactor.md b/docs/superpowers/plans/2026-07-10-axios-request-layer-refactor.md deleted file mode 100644 index 7e59bdc..0000000 --- a/docs/superpowers/plans/2026-07-10-axios-request-layer-refactor.md +++ /dev/null @@ -1,251 +0,0 @@ -# Axios 公共请求层重构 Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** 删除现有 `fetch` 请求实现,以本地 Axios 浏览器版和 `utils/AxiosRequestUtil.js` 替换为唯一公共请求层,页面业务 JS 不再依赖或实现底层网络请求。 - -**Architecture:** `utils/axios.js` 是固定版本的第三方浏览器依赖;`utils/AxiosRequestUtil.js` 负责 Axios 实例、请求头、token、参数、响应解包和错误转换;`utils/ApiClient.js` 负责所有 PC 接口契约与 token 生命周期。所有页面在页面业务脚本前加载三个 utils 文件,旧 `utils/RequestUtil.js` 和 `public/js/api-client.js` 被删除且不保留兼容入口。 - -**Tech Stack:** Axios 1.7.9 浏览器非压缩构建、原生 JavaScript、Node `assert` 测试、PowerShell 静态扫描。 - -## Global Constraints - -- 计划创建时间:2026-07-10 09:27:30 +08:00。 -- `utils` 只存通用依赖和公共请求封装;`public/js` 只存页面业务脚本,不存 API 客户端或底层请求实现。 -- 不保留 `fetch`、`fetchImpl`、`root.fetch`、`RequestUtil`、`public/js/api-client.js` 或旧脚本引用;不做 Axios/fetch 双实现兼容。 -- Axios 使用本地 `utils/axios.js`,页面运行时不依赖 CDN。 -- 普通 JSON 请求传对象给 Axios;文件上传传 `FormData`;不手写 JSON 字符串。 -- 每个请求都带 `clientid`;有 token 且 `auth !== false` 时带 `Authorization: Bearer `。 -- PC-014 在本重构完成后重新执行,重构前的复核结论不作为最终证据。 - ---- - -### Task 0: 收敛环境配置职责 - -**Files:** -- Modify: `config.js` -- Modify: `tests/config.test.js` -- Modify: `utils/ApiClient.js` - -**Interfaces:** -- Produces: `GenealogyConfig.getEnvironment()`,只返回 `development` 或 `production`。 -- Produces: `GenealogyConfig.getApiBaseUrl()`,只返回当前环境的接口基础地址。 -- Produces: `GenealogyConfig.getConfig()`,只返回 `{ environment, apiBaseUrl }`。 -- Consumes: `ApiClient` 自己定义 PC `clientId`、`tenantId`、token key 和 token header。 - -- [x] **Step 1: 写配置职责失败测试** - -```js -assert.deepStrictEqual(config.getConfig(), { - environment: 'development', - apiBaseUrl: 'http://test-genealogy-api.ddxcjp.cn' -}); -assert.strictEqual(config.getClientId, undefined); -assert.strictEqual(config.getTenantId, undefined); -assert.strictEqual(config.getTokenKey, undefined); -``` - -- [x] **Step 2: 运行测试确认旧配置职责过宽** - -Run: `node tests\\config.test.js` - -Expected: FAIL,旧配置仍暴露 clientId、tenantId、token key 或 token header。 - -- [x] **Step 3: 重写 config.js 为环境和域名唯一入口** - -```js -var environments = { - development: { apiBaseUrl: 'http://test-genealogy-api.ddxcjp.cn' }, - production: { apiBaseUrl: 'http://test-genealogy-api.ddxcjp.cn' } -}; - -function getConfig() { - return { - environment: getEnvironment(), - apiBaseUrl: getApiBaseUrl() - }; -} -``` - -- [x] **Step 4: 将 PC 身份和 token 常量保留在 ApiClient** - -```js -var DEFAULT_CLIENT_ID = 'ced7e5f0498645c6ec642dcf450b036f'; -var DEFAULT_TENANT_ID = '000000'; -var DEFAULT_TOKEN_KEY = 'genealogy_auth_token'; -var DEFAULT_TOKEN_HEADER_NAME = 'Authorization'; -``` - -补充约束:`ApiClient` 不定义接口基础地址;必须由根目录 `config.js` 的 `apiBaseUrl` 或调用方显式 `baseUrl` 提供。 - -- [x] **Step 5: 运行配置与 API 客户端测试** - -Run: `node tests\\config.test.js; node tests\\api-client.test.js; node --check config.js; node --check utils\\ApiClient.js` - -Expected: 所有命令通过。 - -### Task 1: 锁定 Axios 请求契约的失败测试 - -**Files:** -- Modify: `tests/utils.test.js` -- Modify: `tests/api-client.test.js` - -**Interfaces:** -- Produces: `AxiosRequestUtil.createRequester(options)`。 -- Consumes: `options.axiosInstance`,该对象实现 `request(config)`。 -- Produces: `request(method, path, { query, body, headers, auth })`,向 Axios 传入 `{ method, url, params, data, headers }`。 - -- [x] **Step 1: 把工具测试改为 Axios 假实例** - -```js -const axiosCalls = []; -const requester = AxiosRequestUtil.createRequester({ - baseUrl: 'https://api.example.test', - clientId: 'pc-client', - getToken: () => 'token-x', - axiosInstance: { - request: async (config) => { - axiosCalls.push(config); - return { status: 200, data: { code: 200, data: { ok: true } } }; - } - } -}); -``` - -- [x] **Step 2: 运行测试确认因旧 fetch 契约失败** - -Run: `node tests\\utils.test.js; node tests\\api-client.test.js` - -Expected: FAIL,提示缺少 `AxiosRequestUtil` 或仍使用 `fetchImpl`。 - -### Task 2: 创建 Axios 工具层并删除 fetch 工具层 - -**Files:** -- Create: `utils/axios.js` -- Create: `utils/AxiosRequestUtil.js` -- Create: `utils/ApiClient.js` -- Delete: `utils/RequestUtil.js` -- Delete: `public/js/api-client.js` - -**Interfaces:** -- `AxiosRequestUtil.createRequester(options)` 只接受 Axios 实例,不接受 `fetchImpl`。 -- `GenealogyApi.createClient(options)` 由 `utils/ApiClient.js` 提供,只接受 `axiosInstance` 测试注入;生产环境读取 `window.axios`。 - -- [x] **Step 1: 下载固定 Axios 非压缩浏览器构建** - -Run: `curl.exe --fail --location https://cdn.jsdelivr.net/npm/axios@1.7.9/dist/axios.js --output utils\\axios.js` - -Expected: `utils/axios.js` 存在且首行包含 Axios 版本信息。 - -- [x] **Step 2: 实现 AxiosRequestUtil 最小请求流程** - -```js -var response = await axiosInstance.request({ - method: method.toLowerCase(), - url: path, - params: requestOptions.query, - data: requestOptions.body, - headers: headers -}); - -return unwrapResponse(response.data); -``` - -- [x] **Step 3: 从零实现 utils/ApiClient.js 并只依赖 AxiosRequestUtil** - -```js -var requestUtil = getAxiosRequestUtil(); -var requester = requestUtil.createRequester({ - baseUrl: baseUrl, - clientId: clientId, - getToken: getToken, - axiosInstance: settings.axiosInstance || root.axios -}); -``` - -- [x] **Step 4: 删除旧 fetch 请求文件和旧公共 API 文件** - -Run: `Remove-Item -LiteralPath utils\\RequestUtil.js; Remove-Item -LiteralPath public\\js\\api-client.js` - -Expected: 两个文件不存在,后续扫描无 `RequestUtil`、`fetchImpl`、`root.fetch`、`fetch(` 或 `public/js/api-client.js` 匹配。 - -- [x] **Step 5: 运行工具和 API 客户端测试** - -Run: `node tests\\utils.test.js; node tests\\api-client.test.js; node --check utils\\AxiosRequestUtil.js; node --check utils\\ApiClient.js` - -Expected: 所有命令通过。 - -### Task 3: 更新页面公共脚本顺序 - -**Files:** -- Modify: 所有根目录中加载 `public/js/api-client.js` 或 `utils/ApiClient.js` 的 HTML 页面。 -- Test: `tests/auth-page-structure.test.js` -- Test: `tests/api-script-order.test.js` - -- [x] **Step 1: 以自动发现的脚本顺序测试锁定页面依赖** - -```js -assert.strictEqual(pages.length, 45); -assert.ok(axiosIndex < axiosRequestUtilIndex); -assert.ok(axiosRequestUtilIndex < apiClientIndex); -assert.strictEqual(html.includes('utils/RequestUtil.js'), false); -``` - -- [x] **Step 2: 运行测试确认旧脚本顺序不满足新契约** - -Run: `node tests\\auth-page-structure.test.js` - -Expected: FAIL,仍有页面加载旧 API 客户端或未加载 Axios 和 AxiosRequestUtil。 - -- [x] **Step 3: 逐页替换公共脚本引用** - -```html - - - -``` - -- [x] **Step 4: 运行结构测试和静态扫描** - -Run: `node tests\\auth-page-structure.test.js; rg -n "utils/RequestUtil\\.js|public/js/api-client\\.js|fetchImpl|root\\.fetch|fetch\\(" --glob "!public/tac/**" --glob "!utils/axios.js" .` - -Expected: 测试通过,旧请求层标识无匹配。 - -### Task 4: 重跑 PC 复核并更新记录 - -**Files:** -- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md` -- Modify: `docs/superpowers/plans/2026-07-10-axios-request-layer-refactor.md` -- Modify: `docs/superpowers/plans/2026-07-10-pc-tracker-reverification.md` - -- [x] **Step 1: 运行所有 Node 测试与语法检查** - -Run: `Get-ChildItem -Path tests -Filter *.test.js | Sort-Object Name | ForEach-Object { node $_.FullName }; node --check utils\\AxiosRequestUtil.js; node --check utils\\ApiClient.js; git diff --check` - -Expected: 所有测试通过,语法检查和差异检查无错误。 - -- [x] **Step 2: 执行 PC-014 复核命令并记录外部网关状态** - -Run: `curl.exe -I --max-time 15 http://test-genealogy-api.ddxcjp.cn/captcha/require` - -Expected: 记录当次结果;仅本地验证通过的项标为“本地复核通过”,网关不可用时标注“外部环境待验证”。 - -- [x] **Step 3: 写入完成时间和清理结论** - -Record: Axios 文件来源、删除的 `fetch` 文件、页面脚本替换范围、所有验证命令及结果。 - -## 自检 - -- 所有底层网络调用都从 `utils/AxiosRequestUtil.js` 发起。 -- `public/js` 中不含 `fetch`、Axios 实例创建或请求头拼装。 -- 所有页面在页面业务脚本前加载 `utils/axios.js`、`utils/AxiosRequestUtil.js` 和 `utils/ApiClient.js`。 -- `utils/RequestUtil.js`、`public/js/api-client.js`、`fetchImpl`、`root.fetch` 和旧页面脚本引用均不存在。 - -## 完成记录 - -- 完成时间:2026-07-10 10:10:56 +08:00。 -- Axios 来源:`axios@1.7.9/dist/axios.js` 的本地非压缩浏览器构建,存放于 `utils/axios.js`。 -- 请求契约:所有 PC 请求均由 `utils/AxiosRequestUtil.js` 通过 `axios.request` 发起;`utils/ApiClient.js` 仅封装 PC 接口和 token 生命周期。 -- 配置边界:根目录 `config.js` 是接口基础地址唯一来源;`ApiClient` 不再保存 `DEFAULT_BASE_URL`。 -- 页面范围:45 个加载接口客户端的 HTML 页面已统一脚本顺序;旧 `utils/RequestUtil.js` 和 `public/js/api-client.js` 已删除。 -- 验证:全量 Node 测试、语法检查、45 页脚本顺序检查、旧 fetch/旧脚本扫描和 `git diff --check` 均通过。 diff --git a/docs/superpowers/plans/2026-07-10-captcha-verify-track-contract.md b/docs/superpowers/plans/2026-07-10-captcha-verify-track-contract.md deleted file mode 100644 index f98cd76..0000000 --- a/docs/superpowers/plans/2026-07-10-captcha-verify-track-contract.md +++ /dev/null @@ -1,20 +0,0 @@ -# 2026-07-10 验证结果参数格式修正计划 - -- 计划编号:PC-021 -- 开始时间:2026-07-10 14:31:14 +08:00 -- 页面范围:`login.html`、`register.html`、`forgot-password.html` -- 接口范围:`POST /captcha/verify` -- 新契约:验证轨迹统一提交为 `payload.track`,不再提交旧的 `payload.data`。 - -## 执行清单 - -1. [x] 写失败测试:按后端最新示例断言 `payload.track`、`left`、`top` 和 `trackList`。 -2. [x] 修改 `captcha-pages.js`:把 TAC 轨迹转换成后端要求的 `payload.track`。 -3. [x] 更新追踪文档中旧的 `payload.data.trackList` 记录。 -4. [x] 运行焦点测试、语法检查和空白检查。 - -## 验证记录 - -- 2026-07-10 14:31:14 +08:00:开始按新请求体契约补失败测试。 -- 2026-07-10 14:31:14 +08:00:`node tests\captcha-pages.test.js` 已按预期失败,失败点为实际请求体仍提交 `payload.id` 和 `payload.data`。 -- 2026-07-10 14:34:28 +08:00:修复后 `node tests\captcha-pages.test.js`、`node tests\auth-page-structure.test.js`、`node tests\auth-pages.test.js`、`node --check public\js\captcha-pages.js` 均通过;`git diff --check` 无空白错误,仅有 LF/CRLF 换行提示。 diff --git a/docs/superpowers/plans/2026-07-10-login-auth-flow-polish.md b/docs/superpowers/plans/2026-07-10-login-auth-flow-polish.md deleted file mode 100644 index 757c3e1..0000000 --- a/docs/superpowers/plans/2026-07-10-login-auth-flow-polish.md +++ /dev/null @@ -1,26 +0,0 @@ -# 2026-07-10 登录页样式与认证流程修正计划 - -- 计划编号:PC-022 / PC-023 -- 开始时间:2026-07-10 14:42:32 +08:00 -- 页面范围:`login.html`、`register.html`、`forgot-password.html` -- 接口范围:登录、注册、忘记密码提交前的 `/captcha/verify` 取票据流程 - -## PC-022 登录页样式优化 - -1. [x] 优化 `public/css/login.css` 中登录方式切换区,让选中状态、文本居中和整体边框更自然。 -2. [x] 增加登录按钮与底部链接之间的间距。 -3. [x] 保持样式在 CSS 文件内,不用 JS 注入样式。 - -## PC-023 注册跳转与验证码流程复核 - -1. [x] 写失败测试:注册完成后必须跳转 `login.html`。 -2. [x] 写结构测试:三张认证页都必须声明验证码场景并携带 `validToken`。 -3. [x] 修改 `register.html` 的成功跳转。 -4. [x] 复核公共 `CaptchaPages` 仍统一按 `payload.track` 换取 `validToken`。 -5. [x] 运行相关测试、语法检查和空白检查。 - -## 验证记录 - -- 2026-07-10 14:42:32 +08:00:开始记录 PC-022 / PC-023,先补认证页结构测试。 -- 2026-07-10 14:42:32 +08:00:`node tests\auth-page-structure.test.js` 已按预期失败,失败点为 `register.html` 仍跳转 `create-genealogy.html`。 -- 2026-07-10 14:50:23 +08:00:修复后 `node tests\auth-page-structure.test.js`、`node tests\auth-pages.test.js`、`node tests\captcha-pages.test.js`、`node --check public\js\auth-pages.js`、`node --check public\js\captcha-pages.js` 均通过;`git diff --check` 无空白错误,仅有 LF/CRLF 换行提示。 diff --git a/docs/superpowers/plans/2026-07-10-pc-tracker-reverification.md b/docs/superpowers/plans/2026-07-10-pc-tracker-reverification.md deleted file mode 100644 index 8babde7..0000000 --- a/docs/superpowers/plans/2026-07-10-pc-tracker-reverification.md +++ /dev/null @@ -1,150 +0,0 @@ -# PC 接口对接记录全量复核 Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** 重新验证 `pc-api-page-integration-tracker-2026-07-09.md` 中 PC-000 至 PC-013 的完成状态,并以当前代码、当前 PC YAML 和本次命令结果修正记录。 - -**Architecture:** `genealogy-pc-openapi.yaml` 的 `paths` 是接口范围唯一来源;代码和 HTML 是当前实现来源;Node 测试、语法检查和静态扫描是可重复验证来源。无法由前端离线验证的测试网关可用性明确标记为“外部环境待验证”,不将其当作页面对接完成证据。 - -**Tech Stack:** OpenAPI YAML、原生 JavaScript、Node `assert` 测试、PowerShell 静态扫描、Git 差异检查。 - -## Global Constraints - -- 计划创建时间:2026-07-10 09:20:38 +08:00。 -- 范围仅限追踪表中状态为“已完成”或“已修正”的 PC-000 至 PC-013;不新增业务接口或页面功能。 -- 复核时不采用旧 APP 路径,不以历史测试文字代替当前命令结果。 -- 每个结论必须记录命令、结果、时间和证据边界;证据不足必须改为“复核失败/待修正”或“外部环境待验证”。 -- 仅在记录与事实不一致时修改文档;不改无关业务代码。 - ---- - -### Task 1: 复核契约和配置项 - -**Files:** -- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md` -- Read: `genealogy-pc-openapi.yaml` -- Read: `config.js` -- Read: `public/js/api-client.js` -- Test: `tests/config.test.js` -- Test: `tests/api-client.test.js` - -**Covers:** PC-000、PC-001、PC-007、PC-010。 - -- [x] **Step 1: 核对 YAML 路径与客户端配置** - -Run: `rg -n "^ /captcha/|^ /auth/code:|^ /genealogy/pc/auth/|^ /genealogy/pc/files/|^ /genealogy/region/" genealogy-pc-openapi.yaml; rg -n "test-genealogy-api|ced7e5f0498645c6ec642dcf450b036f|web_pc" config.js public/js/api-client.js` - -Expected: 当前 YAML 只包含验证码、PC 认证、PC 文件、地区路径;代码使用当前 PC clientId 和开发环境 HTTP 基础地址。 - -- [x] **Step 2: 运行配置与 API 客户端测试** - -Run: `node tests\\config.test.js; node tests\\api-client.test.js; node --check config.js; node --check public\\js\\api-client.js` - -Expected: 四条命令均通过。 - -### Task 2: 复核通用工具与未覆盖业务降级 - -**Files:** -- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md` -- Read: `utils/StorageUtil.js` -- Read: `utils/FormUtil.js` -- Read: `utils/RequestUtil.js` -- Read: `public/js/api-client.js` -- Test: `tests/utils.test.js` -- Test: `tests/api-client.test.js` - -**Covers:** PC-002、PC-008。 - -- [x] **Step 1: 运行工具和 API 降级测试** - -Run: `node tests\\utils.test.js; node tests\\api-client.test.js; node tests\\pc-api-contract.test.js` - -Expected: 三个测试通过,PC 请求契约扫描无遗留项。 - -- [x] **Step 2: 核对未覆盖业务的错误边界** - -Run: `rg -n "PC_API_NOT_AVAILABLE" public\\js\\api-client.js` - -Expected: 未被 YAML `paths` 覆盖的业务方法统一受控失败,不生成旧 APP 网络请求。 - -### Task 3: 复核认证、验证码与资料页面 - -**Files:** -- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md` -- Read: `login.html` -- Read: `register.html` -- Read: `forgot-password.html` -- Read: `public/js/auth-pages.js` -- Read: `public/js/captcha-pages.js` -- Read: `utils/MessageUtil.js` -- Test: `tests/auth-pages.test.js` -- Test: `tests/captcha-pages.test.js` -- Test: `tests/auth-page-structure.test.js` -- Test: `tests/auth-message.test.js` -- Test: `tests/profile-pages.test.js` -- Test: `tests/security-pages.test.js` - -**Covers:** PC-003、PC-009、PC-011、PC-012、PC-013。 - -- [x] **Step 1: 检查认证场景、接口路径、TAC 挂载和 layui 提示** - -Run: `rg -n "WEB_H5_LOGIN|WEB_H5_REGISTER|WEB_H5_FORGOT_PASSWORD|captcha-modal.css|auth-captcha-modal|MessageUtil|layer.msg" login.html register.html forgot-password.html public\\js\\auth-pages.js public\\js\\captcha-pages.js utils\\MessageUtil.js` - -Expected: 三张认证页分别使用正确场景;验证码使用页面级挂载点;提示由 `MessageUtil`/layui 提供。 - -- [x] **Step 2: 运行认证和验证码测试** - -Run: `node tests\\auth-pages.test.js; node tests\\captcha-pages.test.js; node tests\\auth-page-structure.test.js; node tests\\auth-message.test.js; node tests\\profile-pages.test.js; node tests\\security-pages.test.js; node --check public\\js\\auth-pages.js; node --check public\\js\\captcha-pages.js; node --check utils\\MessageUtil.js` - -Expected: 所有测试和语法检查通过。 - -- [x] **Step 3: 记录网关外部依赖边界** - -Run: `curl.exe -I --max-time 15 http://test-genealogy-api.ddxcjp.cn/captcha/require` - -Expected: 仅记录当次 HTTP 结果;无论结果如何,不将测试网关可用性视为前端实现的通过或失败。 - -### Task 4: 复核文件与地区公共能力 - -**Files:** -- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md` -- Read: `public/js/upload-pages.js` -- Read: `public/js/region-pages.js` -- Test: `tests/upload-pages.test.js` -- Test: `tests/region-pages.test.js` - -**Covers:** PC-004、PC-005、PC-006。 - -- [x] **Step 1: 运行文件和地区测试** - -Run: `node tests\\upload-pages.test.js; node tests\\region-pages.test.js; node --check public\\js\\upload-pages.js; node --check public\\js\\region-pages.js` - -Expected: 所有测试和语法检查通过。 - -- [x] **Step 2: 扫描旧路径与页面覆盖边界** - -Run: `rg -n "PC_API_NOT_AVAILABLE" utils\\ApiClient.js` - -Expected: 文件、地区旧 APP 路径无匹配;业务页面覆盖边界仍由 `PC_API_NOT_AVAILABLE` 保护。 - -### Task 5: 全量验证与追踪表修正 - -**Files:** -- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md` -- Modify: `docs/superpowers/plans/2026-07-10-pc-tracker-reverification.md` - -- [x] **Step 1: 运行全量测试和差异检查** - -Run: `Get-ChildItem -Path tests -Filter *.test.js | Sort-Object Name | ForEach-Object { node $_.FullName }; git diff --check` - -Expected: 所有测试通过,差异检查无空白错误。 - -- [x] **Step 2: 写入复核矩阵和最终状态** - -Record: PC-000 至 PC-013 每项的本次复核时间、验证命令、结论和外部环境边界;任何不一致项从“已完成”改为“待修正”。 - -## 自检 - -- 覆盖范围:PC-000 至 PC-013 的所有“已完成/已修正”项均被归入一个复核任务。 -- 来源一致性:接口范围只引用 `genealogy-pc-openapi.yaml` 的 `paths`,不引用旧 APP 文档。 -- 状态规则:只有当前命令和当前代码同时支持的结论才能保留“已完成”。 diff --git a/docs/superpowers/plans/2026-07-10-registration-token-state.md b/docs/superpowers/plans/2026-07-10-registration-token-state.md deleted file mode 100644 index 16c815f..0000000 --- a/docs/superpowers/plans/2026-07-10-registration-token-state.md +++ /dev/null @@ -1,128 +0,0 @@ -# Registration Token State 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:** Ensure registration never creates or retains a browser authentication token, while password and SMS login continue to persist theirs. - -**Architecture:** `utils/ApiClient.js` remains the single owner of browser token persistence. The `register()` method will leave its request and response contract unchanged, then clear the configured storage key after a successful response. `tests/api-client.test.js` will exercise the behavior through the public client methods and an in-memory storage adapter. - -**Tech Stack:** Browser JavaScript (UMD), Node.js `assert` tests, Axios request adapter. - -## Global Constraints - -- Only `login()` and `loginBySms()` may persist a response token under the configured token key. -- Successful `register()` clears the configured token key even when its response contains `token`, `accessToken`, or `tokenValue`. -- Failed registration leaves existing token storage unchanged. -- Registration page behavior remains a redirect to `login.html`. -- Do not refactor unrelated request, storage, page, or authentication behavior. - ---- - -### Task 1: Enforce registration token state in the API client - -**Files:** -- Modify: `tests/api-client.test.js:83-111` -- Modify: `utils/ApiClient.js:176-182` - -**Interfaces:** -- Consumes: `client.register(body)` and `client.loginBySms(body)` from `GenealogyApi.createClient()`. -- Produces: `client.register(body)` returns the API response while removing the configured token key after a successful request. - -- [ ] **Step 1: Write the failing regression test** - -Insert the following immediately after the existing `client.loginBySms(...)` assertions and before `client.sendSmsCode(...)` in `tests/api-client.test.js`: - -```js - assert.strictEqual(store.values['token-key'], 'server-token'); - - store.setItem('token-key', 'existing-token'); - const registerResponse = await client.register({ - phone: '13900000000', - password: 'register-md5', - nickName: 'new-user', - validToken: 'register-ticket' - }); - assert.deepStrictEqual(registerResponse, { - token: 'server-token', - url: '/genealogy/pc/auth/register' - }); - assert.strictEqual(calls[8].url, '/genealogy/pc/auth/register'); - assert.strictEqual(calls[8].headers.Authorization, undefined); - assert.strictEqual(calls[8].data.grantType, 'password'); - assert.strictEqual(calls[8].data.registerSource, 'PC'); - assert.strictEqual(store.getItem('token-key'), null); - - const failedRegisterStore = createStore({ - 'token-key': 'existing-token' - }); - const failedRegisterClient = GenealogyApi.createClient({ - baseUrl: 'https://test-genealogy-api.ddxcjp.cn', - clientId: 'client-x', - tenantId: 'tenant-x', - tokenKey: 'token-key', - tokenStore: failedRegisterStore, - axiosInstance: { - request: async () => { - throw new Error('register failed'); - } - } - }); - await assert.rejects( - failedRegisterClient.register({ - phone: '13900000001', - password: 'register-md5', - nickName: 'failed-user', - validToken: 'register-ticket' - }), - /register failed/ - ); - assert.strictEqual(failedRegisterStore.getItem('token-key'), 'existing-token'); -``` - -Move the existing `client.sendSmsCode(...)` assertions from index `8` to index `9`, then change the final call-count assertion from `9` to `10`. - -- [ ] **Step 2: Run the regression test and confirm the current failure** - -Run: `node tests/api-client.test.js` - -Expected: failure at `assert.strictEqual(store.getItem('token-key'), null)` because the current `register()` implementation writes `server-token` to `token-key`. The failed-registration assertion will already pass, documenting that the token must change only after a successful response. - -- [ ] **Step 3: Implement the minimal token-state change** - -In `utils/ApiClient.js`, replace the body of `register()` with: - -```js - async function register(body) { - var data = await request('POST', '/genealogy/pc/auth/register', { - auth: false, - body: Object.assign({ grantType: 'password', registerSource: 'PC' }, withTenant(body)) - }); - clearToken(); - return data; - } -``` - -- [ ] **Step 4: Run the focused test and confirm it passes** - -Run: `node tests/api-client.test.js` - -Expected: `api-client tests passed`. - -- [ ] **Step 5: Run the complete Node test suite** - -Run: `Get-ChildItem -Path tests -Filter *.test.js | ForEach-Object { node $_.FullName }` - -Expected: every test script prints its success message and PowerShell exits with code `0`. - -- [ ] **Step 6: Check the final diff and commit the implementation** - -Run: `git diff --check` - -Expected: exit code `0` with no whitespace errors. - -Then commit only the implementation files: - -```powershell -git add -- utils/ApiClient.js tests/api-client.test.js -git commit -m "fix: clear token after registration" -``` diff --git a/docs/superpowers/plans/2026-07-10-tac-layer-call-correction.md b/docs/superpowers/plans/2026-07-10-tac-layer-call-correction.md deleted file mode 100644 index 5416c8c..0000000 --- a/docs/superpowers/plans/2026-07-10-tac-layer-call-correction.md +++ /dev/null @@ -1,84 +0,0 @@ -# TAC 默认层调用纠正实施计划 - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**目标:** 在不写任何验证码 CSS、也不修改 TAC 第三方文件的前提下,将 TAC 挂载到 Layui 默认弹层中,不再渲染到认证页底部。 - -**架构:** `auth-pages.js` 仍在表单提交时调用 `CaptchaPages.ensureToken`;`captcha-pages.js` 通过已加载的 `layui.layer.open` 创建默认容器,将该容器传给 `CaptchaConfig.bindEl`,然后原样调用 `new TAC(config)`。TAC 继续根据后端下发的 `captchaType` 自己选择滑块、旋转、拼图、文字点击或禁用态。 - -**技术栈:** Layui 内置 `layer`、`public/tac/css/tac.css`、`public/tac/js/tac.min.js`、原生 JavaScript、Node `assert` 测试。 - -## 全局约束 - -- 计划创建时间:2026-07-10 10:38:10 +08:00。 -- 不修改 `public/tac/css/tac.css`、`public/tac/js/tac.min.js` 或 Layui 第三方文件。 -- 不创建或注入任何验证码 CSS;不传 `bgUrl`、`logoUrl`、尺寸、皮肤等 TAC 视觉配置。 -- 只使用 Layui 默认 `layer.open` 作为弹层容器,TAC 的内部外观完全由其自带文件负责。 -- 只修改认证三页、验证码适配、对应测试和记录。 - ---- - -### 任务 1:锁定正确的弹层挂载契约 - -**文件:** -- 修改:`tests/auth-page-structure.test.js` -- 修改:`tests/captcha-pages.test.js` - -- [x] **步骤 1:写页面结构失败测试。** - -断言 `login.html`、`register.html`、`forgot-password.html` 不再保留 `[data-captcha-box]` 静态挂载点,且 `public/layui/layui.js` 在 `captcha-pages.js` 前加载。 - -- [x] **步骤 2:写验证码适配失败测试。** - -断言适配层调用 `root.layui.layer.open`、在结束时调用 `root.layui.layer.close`,仍只调用 `new root.TAC(config)`,不存在自定义验证码视觉标识。 - -- [x] **步骤 3:运行测试并确认当前底部挂载实现失败。** - -运行:`node tests\auth-page-structure.test.js; node tests\captcha-pages.test.js` - -预期:因静态 `[data-captcha-box]` 与缺少 `layer.open` 而失败。 - -### 任务 2:使用 Layui 默认层承载 TAC - -**文件:** -- 修改:`login.html` -- 修改:`register.html` -- 修改:`forgot-password.html` -- 修改:`public/js/captcha-pages.js` - -- [x] **步骤 1:删除三页静态 `[data-captcha-box]`。** - -- [x] **步骤 2:在 `captcha-pages.js` 创建无样式的 DOM 容器,并以 `layui.layer.open({ type: 1, content: mount })` 打开默认弹层。** - -- [x] **步骤 3:将该 DOM 容器作为 `CaptchaConfig.bindEl`,并保持 `new TAC(config)` 原样调用。** - -- [x] **步骤 4:在 TAC 成功、关闭、初始化异常和 Promise 异常时销毁 TAC 并关闭对应 Layui 层。** - -- [x] **步骤 5:运行聚焦测试和语法检查。** - -运行:`node tests\auth-page-structure.test.js; node tests\captcha-pages.test.js; node --check public\js\captcha-pages.js` - -预期:全部通过。 - -### 任务 3:全量验证和记录 - -**文件:** -- 修改:`docs/pc-api-page-integration-tracker-2026-07-09.md` -- 修改:`docs/superpowers/plans/2026-07-10-tac-layer-call-correction.md` - -- [x] **步骤 1:运行全量 Node 测试、静态扫描和差异检查。** - -运行:`Get-ChildItem -Path tests -Filter *.test.js | Sort-Object Name | ForEach-Object { node $_.FullName }; rg -n "data-captcha-box|auth-captcha-modal|captcha-modal.css|bgUrl|logoUrl" login.html register.html forgot-password.html public\css public\js; git diff --check` - -预期:测试和差异检查通过;认证页中不再有静态验证码挂载点或自定义视觉标识。 - -- [x] **步骤 2:记录完成时间、TAC 功能边界和浏览器实测边界。** - -## 完成记录 - -- 完成时间:2026-07-10 10:44:11 +08:00。 -- 根因:TAC 的 `init()` 只向 `CaptchaConfig.bindEl` 追加组件;原先静态挂载点位于 `body` 末尾,移除定位样式后自然渲染在页面底部。 -- 修改:删除三页静态 `[data-captcha-box]`;验证码适配层使用 Layui 默认 `layer.open` 创建临时容器并传给 `bindEl`;成功、TAC 关闭、初始化异常与 Promise 异常都会清空容器并关闭同一层。 -- TAC 边界:保留原生 `new TAC(config)`、原生样式、原生刷新和后端类型分发;不传视觉配置,不写验证码 CSS,不改动任何 `public/tac` 文件。 -- 验证:失败测试已复现底部挂载;聚焦测试、全量 Node 测试、`node --check public/js/captcha-pages.js` 和 `git diff --check` 均通过。 -- 浏览器边界:未在真实测试网关完成一次有效轨迹验证;仍需在浏览器点击注册后确认 Layui 默认层居中显示及后端 `validToken` 返回。 diff --git a/docs/superpowers/plans/2026-07-10-tac-layer-center-fix.md b/docs/superpowers/plans/2026-07-10-tac-layer-center-fix.md deleted file mode 100644 index 242d043..0000000 --- a/docs/superpowers/plans/2026-07-10-tac-layer-center-fix.md +++ /dev/null @@ -1,23 +0,0 @@ -# 2026-07-10 TAC 弹层居中修复计划 - -- 计划编号:PC-020 -- 开始时间:2026-07-10 11:11:05 +08:00 -- 页面范围:`login.html`、`register.html`、`forgot-password.html` -- 接口范围:`/captcha/require`、`/captcha/challenge`、`/captcha/verify` -- 约束:不修改 `public/tac`,不新增验证码自定义样式,不传 TAC 视觉配置,只调整 Layui 弹层调用参数。 - -## 根因 - -Layui 打开页面层时,`content` 里的验证码挂载点还是空节点;TAC 后续异步创建自己的 `#tianai-captcha-parent`,该原生外层尺寸是 `318px * 318px`。Layui 已经按空内容完成定位,所以最终验证码窗口会偏离预期位置。 - -## 执行清单 - -1. [x] 写失败测试:要求验证码弹层通过 Layui `area` 预留 `318px * 318px`,并保持默认居中。 -2. [x] 修改 `captcha-pages.js`:只给 `layer.open` 增加 TAC 原生尺寸和默认居中参数。 -3. [x] 运行焦点测试、结构测试、语法检查和空白检查。 -4. [x] 将完成结果同步回 `pc-api-page-integration-tracker-2026-07-09.md`。 - -## 验证记录 - -- 2026-07-10 11:11:05 +08:00:`node tests\captcha-pages.test.js` 已按预期失败,失败点为 `layerOptions.area` 缺失。 -- 2026-07-10 11:14:31 +08:00:修复后 `node tests\captcha-pages.test.js`、`node tests\auth-page-structure.test.js`、`node --check public\js\captcha-pages.js` 均通过;`git diff --check` 无空白错误,仅有既有 LF/CRLF 换行提示。 diff --git a/docs/superpowers/plans/2026-07-10-tac-layer-real-mount-fix.md b/docs/superpowers/plans/2026-07-10-tac-layer-real-mount-fix.md deleted file mode 100644 index 7d45eec..0000000 --- a/docs/superpowers/plans/2026-07-10-tac-layer-real-mount-fix.md +++ /dev/null @@ -1,72 +0,0 @@ -# TAC Layui 实际挂载点修复实施计划 - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**目标:** 修复 Layui 遮罩已显示、验证码接口已返回 200,但 TAC 验证窗口仍不显示的问题。 - -**架构:** `CaptchaPages.createCaptchaLayer` 使用 Layui `layer.open` 的字符串 `content` 创建弹层内部真实挂载节点;弹层创建完成后再从 `document` 查询该实际节点,并把它交给 `CaptchaConfig.bindEl`。这样 Layui 负责弹层,TAC 挂到已经进入页面的真实 DOM。 - -**技术栈:** Layui 2.11.5、TAC 原生 CSS/JS、原生 JavaScript、Node `assert` 测试。 - -## 全局约束 - -- 计划创建时间:2026-07-10 10:59:52 +08:00。 -- 不修改 `public/tac/css/tac.css`、`public/tac/js/tac.min.js` 或 Layui 第三方文件。 -- 不新增验证码自定义 CSS,不注入样式,不传 TAC 视觉配置。 -- 保留 `auth-pages.js -> CaptchaPages.ensureToken -> TAC` 调用链,只修正弹层挂载点。 - ---- - -### 任务 1:复现“遮罩有但实际挂载点不可见” - -**文件:** -- 修改:`tests/captcha-pages.test.js` - -- [x] **步骤 1:写失败测试** - -断言 `layer.open` 的 `content` 是包含唯一 `data-captcha-layer-box` 的字符串;`createCaptchaLayer` 必须在弹层打开后从 `document.querySelector` 取到实际挂载节点并返回。 - -- [x] **步骤 2:运行失败测试** - -运行:`node tests\captcha-pages.test.js` - -预期:失败,当前实现仍返回打开前创建的原生 DOM。 - -### 任务 2:修正实际挂载点 - -**文件:** -- 修改:`public/js/captcha-pages.js` - -- [x] **步骤 1:最小实现** - -增加内部自增 ID,用字符串内容创建 `
`;`layer.open` 后查询该节点并作为 `box` 返回。 - -- [x] **步骤 2:运行聚焦测试** - -运行:`node tests\captcha-pages.test.js; node --check public\js\captcha-pages.js` - -预期:全部通过。 - -### 任务 3:记录和回归 - -**文件:** -- 修改:`docs/pc-api-page-integration-tracker-2026-07-09.md` -- 修改:`docs/superpowers/plans/2026-07-10-tac-layer-real-mount-fix.md` - -- [x] **步骤 1:补充 PC-019 记录** - -记录用户浏览器复现现象、根因、修正方式、验证命令。 - -- [x] **步骤 2:最终验证** - -运行:`node tests\auth-page-structure.test.js; node tests\captcha-pages.test.js; node --check public\js\captcha-pages.js; git diff --check` - -预期:全部通过;`git diff --check` 仅允许 Git 行尾提示,不允许空白错误。 - -## 完成记录 - -- 完成时间:2026-07-10 11:04:18 +08:00。 -- 根因:PC-018 只让 `layer.open` 不再因 `.parents()` 报错,但仍把打开前创建的临时 DOM 返回给 TAC;真实浏览器里 Layui 遮罩已出现,`/captcha/challenge` 也返回 200,但 TAC 没有挂到可见弹层内容区。 -- 修复:`layer.open` 使用字符串 `content` 创建弹层内部真实节点,随后通过 `document.querySelector('[data-captcha-layer-box="..."]')` 获取实际挂载点并交给 `CaptchaConfig.bindEl`。 -- 约束:未修改 `public/tac`,未新增验证码 CSS,未传 TAC 视觉配置。 -- 验证:失败测试已先复现;修复后 `node tests\captcha-pages.test.js` 和 `node --check public\js\captcha-pages.js` 通过。最终回归记录见 `docs/pc-api-page-integration-tracker-2026-07-09.md` 的 PC-019。 diff --git a/docs/superpowers/plans/2026-07-10-tac-layui-content-contract-fix.md b/docs/superpowers/plans/2026-07-10-tac-layui-content-contract-fix.md deleted file mode 100644 index 1cb9e06..0000000 --- a/docs/superpowers/plans/2026-07-10-tac-layui-content-contract-fix.md +++ /dev/null @@ -1,71 +0,0 @@ -# TAC Layui content 契约修复实施计划 - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**目标:** 修复认证页点击后只出现 `d.parents is not a function` 提示、TAC 弹层不显示的问题。 - -**架构:** `CaptchaPages.createCaptchaLayer` 继续只负责创建 Layui 默认弹层容器,不写验证码样式、不改 TAC 文件。Layui `layer.open` 的 `content` 使用 Layui/jQuery 包装对象满足弹层内部 `.parents()` 契约,TAC `bindEl` 继续使用对应的真实 DOM 节点。 - -**技术栈:** Layui 2.11.5、TAC 原生 CSS/JS、原生 JavaScript、Node `assert` 测试。 - -## 全局约束 - -- 计划创建时间:2026-07-10 10:50:06 +08:00。 -- 不修改 `public/tac/css/tac.css`、`public/tac/js/tac.min.js` 或 Layui 第三方文件。 -- 不新增验证码自定义 CSS,不注入样式,不传 TAC 视觉配置。 -- 只修正 Layui 弹层 content 契约、对应测试和记录文档。 - ---- - -### 任务 1:复现 Layui content 契约错误 - -**文件:** -- 修改:`tests/captcha-pages.test.js` - -- [x] **步骤 1:写失败测试** - -断言 `createCaptchaLayer` 传给 `layui.layer.open` 的 `content` 必须是包装对象,包装对象需要保留真实 DOM 节点,并具备 `.parents()` 方法。 - -- [x] **步骤 2:运行失败测试** - -运行:`node tests\captcha-pages.test.js` - -预期:失败,当前实现把原生 DOM 直接传给 `content`。 - -### 任务 2:修正 createCaptchaLayer - -**文件:** -- 修改:`public/js/captcha-pages.js` - -- [x] **步骤 1:最小实现** - -在 `createCaptchaLayer` 内用 `root.layui.$ || root.$ || root.jQuery` 包装真实 DOM 节点;`layer.open({ content })` 使用包装对象;返回值中的 `box` 仍为真实 DOM,供 TAC `bindEl` 使用。 - -- [x] **步骤 2:运行聚焦测试** - -运行:`node tests\captcha-pages.test.js; node --check public\js\captcha-pages.js` - -预期:全部通过。 - -### 任务 3:记录和回归 - -**文件:** -- 修改:`docs/pc-api-page-integration-tracker-2026-07-09.md` -- 修改:`docs/superpowers/plans/2026-07-10-tac-layui-content-contract-fix.md` - -- [x] **步骤 1:补充 PC-018 完成记录** - -记录根因、修改文件、验证命令和结果。 - -- [x] **步骤 2:运行最终验证** - -运行:`node tests\auth-page-structure.test.js; node tests\captcha-pages.test.js; node --check public\js\captcha-pages.js; git diff --check` - -预期:全部通过;`git diff --check` 仅允许 Git 行尾提示,不允许空白错误。 - -## 完成记录 - -- 完成时间:2026-07-10 10:54:03 +08:00。 -- 根因:Layui 2.11.5 的 `layer.open` 在处理 DOM 内容时会调用 `.parents()`;原实现将原生 DOM 节点直接传给 `content`,导致浏览器报 `d.parents is not a function`,弹层创建中断。 -- 修复:`createCaptchaLayer` 使用 `layui.$`、`$` 或 `jQuery` 包装临时 DOM 后传给 `layer.open({ content })`,同时继续把真实 DOM 节点传给 `CaptchaConfig.bindEl`。 -- 验证:失败测试已先复现;修复后 `node tests\captcha-pages.test.js` 和 `node --check public\js\captcha-pages.js` 通过。最终回归记录见 `docs/pc-api-page-integration-tracker-2026-07-09.md` 的 PC-018。 diff --git a/docs/superpowers/plans/2026-07-10-tac-modal-cleanup.md b/docs/superpowers/plans/2026-07-10-tac-modal-cleanup.md deleted file mode 100644 index bb6ee35..0000000 --- a/docs/superpowers/plans/2026-07-10-tac-modal-cleanup.md +++ /dev/null @@ -1,150 +0,0 @@ -# TAC 验证弹窗遮罩清理 Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** 在 TAC 验证被取消、初始化异常或接口失败时,主动关闭认证页遮罩并清空残留的验证码 DOM。 - -**Architecture:** `public/js/captcha-pages.js` 作为验证码弹窗生命周期的唯一拥有者,新增打开和关闭挂载容器的函数。由于原 `public.css` 不是 UTF-8,无法使用补丁安全编辑,`public/css/captcha-modal.css` 作为新增公共样式文件只根据 `is-active` 类决定遮罩显示;三张认证页继续复用同一个页面级挂载点。 - -**Tech Stack:** 原生 JavaScript、Node `assert` 测试、layui 提示、TAC `public/tac/js/tac.min.js`。 - -## Global Constraints - -- 计划创建时间:2026-07-10 09:10:03 +08:00。 -- 仅修改 TAC 遮罩生命周期,不修改 `public/tac/js/tac.min.js`,不改变后端验证码类型选择。 -- 新增或修改的 JS、CSS 注释使用中文;样式只写入 CSS 文件。 -- 失败提示继续使用 `MessageUtil`/layui,禁止回退到浏览器原生 `alert`。 -- 503/CORS 是服务端或跨域环境可用性问题;本任务只保证前端失败后不遗留遮罩。 - ---- - -### Task 1: 用例锁定与生命周期工具 - -**Files:** -- Modify: `tests/captcha-pages.test.js` -- Modify: `public/js/captcha-pages.js` - -**Interfaces:** -- Produces: `openCaptchaBox(box)`,清空旧内容、激活容器、设置 `aria-hidden="false"`。 -- Produces: `closeCaptchaBox(box)`,清空内容、取消激活、设置 `aria-hidden="true"`。 - -- [x] **Step 1: 写失败测试** - -```js -CaptchaPages.openCaptchaBox(box); -assert.strictEqual(box.classList.contains('is-active'), true); -assert.strictEqual(box.attributes['aria-hidden'], 'false'); - -CaptchaPages.closeCaptchaBox(box); -assert.strictEqual(box.classList.contains('is-active'), false); -assert.strictEqual(box.attributes['aria-hidden'], 'true'); -assert.strictEqual(box.innerHTML, ''); -``` - -- [x] **Step 2: 运行失败测试** - -Run: `node tests\\captcha-pages.test.js` -Expected: FAIL,提示 `openCaptchaBox is not a function`。 - -- [x] **Step 3: 实现最小生命周期函数** - -```js -function openCaptchaBox(box) { - if (!box) return; - box.innerHTML = ''; - box.classList.add('is-active'); - box.setAttribute('aria-hidden', 'false'); -} - -function closeCaptchaBox(box) { - if (!box) return; - box.innerHTML = ''; - box.classList.remove('is-active'); - box.setAttribute('aria-hidden', 'true'); -} -``` - -- [x] **Step 4: 将关闭函数接入 TAC 成功、取消、初始化异常和 Promise 捕获路径** - -```js -if (captchaInstance && captchaInstance.destroyWindow) captchaInstance.destroyWindow(); -closeCaptchaBox(box); -``` - -- [x] **Step 5: 运行单元测试与语法检查** - -Run: `node tests\\captcha-pages.test.js; node --check public\\js\\captcha-pages.js` -Expected: 两条命令均通过。 - -### Task 2: CSS 显示状态收口 - -**Files:** -- Create: `public/css/captcha-modal.css` -- Modify: `login.html` -- Modify: `register.html` -- Modify: `forgot-password.html` -- Test: `tests/auth-page-structure.test.js` - -**Interfaces:** -- Consumes: `.auth-captcha-modal.is-active`。 -- Produces: 默认隐藏、激活后显示的页面级遮罩。 - -- [x] **Step 1: 写失败结构测试** - -```js -assert.ok(publicCss.includes('.auth-captcha-modal.is-active')); -assert.ok(publicCss.includes('display: none;')); -``` - -- [x] **Step 2: 运行失败测试** - -Run: `node tests\\auth-page-structure.test.js` -Expected: FAIL,缺少 `.auth-captcha-modal.is-active` 规则。 - -- [x] **Step 3: 实现 CSS 状态规则** - -```css -.auth-captcha-modal { - display: none; -} - -.auth-captcha-modal.is-active { - display: grid; -} -``` - -- [x] **Step 4: 运行结构测试** - -Run: `node tests\\auth-page-structure.test.js` -Expected: PASS。 - -### Task 3: 全量验证与记录 - -**Files:** -- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md` -- Modify: `docs/superpowers/plans/2026-07-10-tac-modal-cleanup.md` - -- [x] **Step 1: 运行验证码相关和全量 Node 测试** - -Run: `Get-ChildItem -Path tests -Filter *.test.js | Sort-Object Name | ForEach-Object { node $_.FullName }` -Expected: 所有测试通过。 - -- [x] **Step 2: 记录完成时间、修改文件和验证结果** - -```markdown -- [x] 完成时间:YYYY-MM-DD HH:mm:ss +08:00 -- [x] 验证:相关测试、语法检查、全量 Node 测试均通过。 -``` - -## 自检 - -- 需求覆盖:取消后遮罩清理、失败后遮罩清理、成功后遮罩清理、CSS 显示状态和回归测试均有对应任务。 -- 占位扫描:无 `TODO`、`TBD` 或未定义接口。 -- 接口一致性:JS 导出名、测试调用名和 CSS 类名统一为 `openCaptchaBox`、`closeCaptchaBox`、`is-active`。 - -## 完成记录 - -- 完成时间:2026-07-10 09:15:55 +08:00。 -- 修改文件:`public/js/captcha-pages.js`、`public/css/captcha-modal.css`、`login.html`、`register.html`、`forgot-password.html`、`tests/captcha-pages.test.js`、`tests/auth-page-structure.test.js`。 -- 验证:`node tests\\captcha-pages.test.js`、`node tests\\auth-page-structure.test.js`、`node --check public\\js\\captcha-pages.js` 和全量 `tests/*.test.js` 均通过。 -- 已知边界:测试域名的 503、CORS 或 TLS 可用性问题仍需后端或网关处理;该前端修复保证取消或异常后不残留页面遮罩。 diff --git a/docs/superpowers/plans/2026-07-10-tac-native-style-cleanup.md b/docs/superpowers/plans/2026-07-10-tac-native-style-cleanup.md deleted file mode 100644 index a7cc2d9..0000000 --- a/docs/superpowers/plans/2026-07-10-tac-native-style-cleanup.md +++ /dev/null @@ -1,85 +0,0 @@ -# TAC 原生样式调用清理实施计划 - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**目标:** 登录、注册、找回密码页面只调用 `public/tac` 自带的验证码组件和样式,不再追加任何验证码视觉样式或视觉配置。 - -**架构:** 页面保留一个无 class 的 `[data-captcha-box]` 挂载点;`captcha-pages.js` 只负责验证码接口参数和成功、取消、异常后的 DOM 清理;TAC 的外观完全由 `public/tac/css/tac.css` 与 `tac.min.js` 决定。 - -**技术栈:** 原生 JavaScript、TAC 本地组件、Node `assert` 测试。 - -## 全局约束 - -- 计划创建时间:2026-07-10 10:19:46 +08:00。 -- 不修改 `public/tac/css/tac.css` 或 `public/tac/js/tac.min.js`。 -- 不为验证码新增、覆盖或注入 CSS;不向 `new TAC` 传入 `bgUrl`、`logoUrl` 等视觉配置。 -- 仅修改登录、注册、找回密码三页及其验证码适配、测试和记录。 - ---- - -### 任务 1:用测试锁定原生 TAC 调用边界 - -**文件:** -- 修改:`tests/auth-page-structure.test.js` -- 修改:`tests/captcha-pages.test.js` - -- [x] **步骤 1:将页面结构测试改为禁止自定义验证码 CSS 与容器 class。** - -断言三页加载 `public/tac/css/tac.css`,不加载 `public/css/captcha-modal.css`,且 `[data-captcha-box]` 不含 `auth-captcha-modal` class。 - -- [x] **步骤 2:将组件测试改为要求原生构造调用。** - -断言 `captcha-pages.js` 中不存在 `bgUrl`、`logoUrl` 与 `.auth-captcha-modal` 视觉控制。 - -- [x] **步骤 3:运行测试并确认当前实现失败。** - -运行:`node tests\auth-page-structure.test.js; node tests\captcha-pages.test.js` - -预期:因当前自定义验证码 CSS、容器 class 和 TAC 视觉配置而失败。 - -### 任务 2:删除自定义验证码视觉层 - -**文件:** -- 修改:`login.html` -- 修改:`register.html` -- 修改:`forgot-password.html` -- 修改:`public/css/public.css` -- 删除:`public/css/captcha-modal.css` -- 修改:`public/js/captcha-pages.js` - -- [x] **步骤 1:删除三页对 `captcha-modal.css` 的引用,并将挂载点改为无 class 的 `data-captcha-box`。** - -- [x] **步骤 2:删除 `public.css` 中 `.auth-captcha-modal` 及其 TAC 尺寸限制规则。** - -- [x] **步骤 3:删除 `captcha-modal.css`。** - -- [x] **步骤 4:将 `new TAC(config, { ... })` 改为 `new TAC(config)`,并将挂载点查询、打开和关闭逻辑改为无视觉状态的 DOM 清理。** - -- [x] **步骤 5:运行两项聚焦测试并确认通过。** - -运行:`node tests\auth-page-structure.test.js; node tests\captcha-pages.test.js; node --check public\js\captcha-pages.js` - -预期:全部通过。 - -### 任务 3:全量验证并记录 - -**文件:** -- 修改:`docs/pc-api-page-integration-tracker-2026-07-09.md` -- 修改:`docs/superpowers/plans/2026-07-10-tac-native-style-cleanup.md` - -- [x] **步骤 1:运行全量 Node 测试、验证码样式静态扫描和差异检查。** - -运行:`Get-ChildItem -Path tests -Filter *.test.js | Sort-Object Name | ForEach-Object { node $_.FullName }; rg -n "captcha-modal.css|auth-captcha-modal|bgUrl|logoUrl" login.html register.html forgot-password.html public\css public\js; git diff --check` - -预期:测试与差异检查通过;扫描没有自定义验证码视觉标识。 - -- [x] **步骤 2:记录完成时间、修改范围、验证结果和外部验证码实测边界。** - -## 完成记录 - -- 完成时间:2026-07-10 10:32:20 +08:00。 -- 删除 `public/css/captcha-modal.css`,并从 `public/css/public.css` 删除 35 行 `.auth-captcha-box`、`.auth-captcha-modal` 和 TAC 尺寸限制规则。 -- 三个认证页仅加载 `public/tac/css/tac.css`,无 class 的 `[data-captcha-box]` 只作为 TAC 挂载点。 -- `captcha-pages.js` 现在只执行 `new TAC(config)`;不再传入 `bgUrl`、`logoUrl` 或切换任何视觉 class。成功、取消、初始化异常后仍清空 TAC 第三方 DOM。 -- 验证:聚焦结构/验证码测试、全量 Node 测试、`node --check public/js/captcha-pages.js`、自定义样式标识扫描和 `git diff --check` 均通过。 -- 外部边界:尚未用有效验证码轨迹完成真实服务端验证;`/captcha/verify` 的尺寸校验问题仍需以后端挑战响应原始尺寸为准单独处理。 diff --git a/docs/superpowers/plans/2026-07-11-auth-to-profile-flow.md b/docs/superpowers/plans/2026-07-11-auth-to-profile-flow.md deleted file mode 100644 index 83d51d9..0000000 --- a/docs/superpowers/plans/2026-07-11-auth-to-profile-flow.md +++ /dev/null @@ -1,474 +0,0 @@ -# Auth To Profile Flow 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:** Complete the PC registration, login, profile, unauthorized, logout, and endpoint-address flow with one token owner and deterministic redirects. - -**Architecture:** `config.js` remains the sole runtime owner of the API base address. `utils/ApiClient.js` owns token validation, persistence, and cleanup; `profile-pages.js` decides whether a profile request is allowed and redirects unauthenticated users to the homepage; `profile-common.js` delegates confirmed logout to the API client. The integration tracker records the current address and verified behavior only after verification commands complete. - -**Tech Stack:** Browser JavaScript (UMD and jQuery), Axios, Node.js `assert` tests, static HTML, Markdown. - -## Global Constraints - -- The current API base address is `http://182.61.18.23:8080` for development and production, and `config.js` is its only runtime owner. -- Runtime-related tests use the same address; only documents explicitly marked historical may retain the former test domain. -- Successful registration clears `genealogy_auth_token` and redirects to `login.html`; it never persists a registration response token. -- Only successful password or SMS login with a nonempty response token persists `genealogy_auth_token` and redirects to `profile.html`. -- `profile.html` and `profile-data.html` redirect to `index.html` before a profile request when no token exists, and after HTTP or business `401` responses. -- Confirmed logout requests `/genealogy/pc/auth/logout`, clears the token regardless of the request outcome, and redirects to `index.html`. -- Do not add global Axios redirects, duplicate token storage in pages, alter API paths, or change password-reset, phone-change, account-deactivation, or profile pages that do not load the profile API. - ---- - -### Task 1: Synchronize the configured API address test - -**Files:** -- Modify: `tests/config.test.js:11-28` -- Modify: `tests/utils.test.js:31` - -**Interfaces:** -- Consumes: `configFactory(...).getConfig()` and `getApiBaseUrl()` from `config.js`. -- Produces: a test that asserts the current `config.js` address for both environments. - -- [ ] **Step 1: Confirm the existing address-contract test fails** - -The current test contains these stale assertions: - -```js -assert.deepStrictEqual(developmentConfig.getConfig(), { - environment: 'development', - apiBaseUrl: 'http://test-genealogy-api.ddxcjp.cn' -}); - -assert.strictEqual(productionConfig.getApiBaseUrl(), 'http://test-genealogy-api.ddxcjp.cn'); -``` - -Run: `node tests/config.test.js` - -Expected: failure because `config.js` already returns `http://182.61.18.23:8080`. - -- [ ] **Step 2: Update the stale test expectations** - -Replace the two expected URL values with: - -```js -assert.deepStrictEqual(developmentConfig.getConfig(), { - environment: 'development', - apiBaseUrl: 'http://182.61.18.23:8080' -}); - -assert.strictEqual(productionConfig.getApiBaseUrl(), 'http://182.61.18.23:8080'); -``` - -Replace the mocked requester address in `tests/utils.test.js` with: - -```js - baseUrl: 'http://182.61.18.23:8080', -``` - -- [ ] **Step 3: Run the focused configuration test** - -Run: `node tests/config.test.js` - -Expected: `config tests passed`. - -Run: `node tests/utils.test.js` - -Expected: `utils tests passed`. - -- [ ] **Step 4: Commit the address-test synchronization** - -```powershell -git add -- tests/config.test.js tests/utils.test.js -git commit -m "test: sync configured api address" -``` - -### Task 2: Enforce login-token and logout-cleanup contracts - -**Files:** -- Modify: `utils/ApiClient.js:52-60, 167-191, 270-274` -- Modify: `tests/api-client.test.js:83-177` - -**Interfaces:** -- Consumes: `getTokenFromResponse(data)`, `setToken(token)`, `clearToken()`, and public `client.login`, `client.loginBySms`, and `client.logout` methods. -- Produces: `client.login(body)` and `client.loginBySms(body)` reject with `ApiError('登录响应缺少 token')` when their response has no token; `client.logout()` clears the configured token key before resolving or rejecting. - -- [ ] **Step 1: Write failing API-client assertions** - -Append this after the registration assertions and before `client.sendSmsCode(...)` in `tests/api-client.test.js`: - -```js - const missingTokenStore = createStore(); - const missingTokenClient = GenealogyApi.createClient({ - baseUrl: 'http://182.61.18.23:8080', - clientId: 'client-x', - tenantId: 'tenant-x', - tokenKey: 'token-key', - tokenStore: missingTokenStore, - axiosInstance: { - request: async () => ({ - status: 200, - data: { code: 200, data: {} } - }) - } - }); - await assert.rejects( - missingTokenClient.login({ phone: '13800000000', password: 'md5' }), - (error) => error.message === '登录响应缺少 token' - ); - assert.strictEqual(missingTokenStore.getItem('token-key'), null); - - store.setItem('token-key', 'logout-token'); - await client.logout(); - assert.strictEqual(calls[9].url, '/genealogy/pc/auth/logout'); - assert.strictEqual(store.getItem('token-key'), null); - - const failedLogoutStore = createStore({ - 'token-key': 'logout-token' - }); - const failedLogoutClient = GenealogyApi.createClient({ - baseUrl: 'http://182.61.18.23:8080', - clientId: 'client-x', - tenantId: 'tenant-x', - tokenKey: 'token-key', - tokenStore: failedLogoutStore, - axiosInstance: { - request: async () => { - throw new Error('logout failed'); - } - } - }); - await assert.rejects(failedLogoutClient.logout(), /logout failed/); - assert.strictEqual(failedLogoutStore.getItem('token-key'), null); -``` - -Move the existing SMS-code assertions from `calls[9]` to `calls[10]` and change the final call-count assertion from `10` to `11`. - -Replace the two existing `baseUrl: 'https://test-genealogy-api.ddxcjp.cn'` fixtures in this file with: - -```js - baseUrl: 'http://182.61.18.23:8080', -``` - -- [ ] **Step 2: Run the focused API-client test and confirm failure** - -Run: `node tests/api-client.test.js` - -Expected: failure at the missing-token rejection because the current login method resolves without writing a token, before reaching the logout assertions. - -- [ ] **Step 3: Add the minimal token validation and logout cleanup** - -Add this helper after `getTokenFromResponse` in `utils/ApiClient.js`: - -```js - function requireLoginToken(data) { - var token = getTokenFromResponse(data); - - if (!token) throw new ApiError('登录响应缺少 token'); - return token; - } -``` - -Replace the token writes in `login()` and `loginBySms()` with: - -```js - setToken(requireLoginToken(data)); -``` - -Replace `logout` with: - -```js - logout: async function () { - try { - return await request('DELETE', '/genealogy/pc/auth/logout'); - } finally { - clearToken(); - } - }, -``` - -- [ ] **Step 4: Run the API-client test and confirm all token paths pass** - -Run: `node tests/api-client.test.js` - -Expected: `api-client tests passed`. - -- [ ] **Step 5: Commit the API-client contract** - -```powershell -git add -- utils/ApiClient.js tests/api-client.test.js -git commit -m "fix: complete auth token lifecycle" -``` - -### Task 3: Guard profile access and connect confirmed logout - -**Files:** -- Modify: `public/js/profile-pages.js:18-22, 165-198` -- Modify: `public/js/profile-common.js:9-82` -- Modify: `profile.html:80, 355` -- Modify: `tests/profile-pages.test.js:1-65` -- Create: `tests/profile-logout-structure.test.js` - -**Interfaces:** -- Consumes: public `api.getToken()`, `api.clearToken()`, `api.currentProfile()`, `api.updateProfile()`, and `api.logout()` methods. -- Produces: `ProfilePages.shouldRedirectToHome(api, error)` for token and unauthorized classification; profile reads and writes redirect to `index.html` when that helper returns true; the existing logout controls call `api.logout()` before navigating to `index.html`. - -- [ ] **Step 1: Write failing profile and logout tests** - -Add these assertions before the final `console.log` in `tests/profile-pages.test.js`: - -```js - assert.strictEqual(ProfilePages.shouldRedirectToHome({ - getToken: () => '' - }), true); - assert.strictEqual(ProfilePages.shouldRedirectToHome({ - getToken: () => 'token-x' - }), false); - assert.strictEqual(ProfilePages.shouldRedirectToHome({ - getToken: () => 'token-x' - }, { status: 401 }), true); - assert.strictEqual(ProfilePages.shouldRedirectToHome({ - getToken: () => 'token-x' - }, { code: 401 }), true); - assert.strictEqual(ProfilePages.shouldRedirectToHome({ - getToken: () => 'token-x' - }, { status: 500 }), false); -``` - -Create `tests/profile-logout-structure.test.js` with: - -```js -const assert = require('assert'); -const fs = require('fs'); -const path = require('path'); - -const profileHtml = fs.readFileSync(path.join(__dirname, '..', 'profile.html'), 'utf8'); -const profileCommon = fs.readFileSync(path.join(__dirname, '..', 'public', 'js', 'profile-common.js'), 'utf8'); - -assert.match(profileHtml, /data-logout-confirm/); -assert.match(profileHtml, //); -assert.match(profileCommon, /function performLogout\(targetUrl\)/); -assert.match(profileCommon, /api\.logout\(\)/); -assert.match(profileCommon, /targetUrl \|\| 'index\.html'/); - -console.log('profile logout structure tests passed'); -``` - -- [ ] **Step 2: Run the new focused tests and confirm failure** - -Run: `node tests/profile-pages.test.js` - -Expected: failure because `shouldRedirectToHome` is not exported. - -Run: `node tests/profile-logout-structure.test.js` - -Expected: failure because the logout confirmation markup and `performLogout` function do not exist. - -- [ ] **Step 3: Add profile token guard and unauthorized redirect** - -Add these helpers after `getApi()` in `public/js/profile-pages.js`: - -```js - function shouldRedirectToHome(api, error) { - var status = error && (error.status || error.code); - - return !api || !api.getToken || !api.getToken() || Number(status) === 401; - } - - function redirectToHome() { - if (!root.location) return; - if (typeof root.location.replace === 'function') { - root.location.replace('index.html'); - return; - } - root.location.href = 'index.html'; - } - - function redirectUnauthorized(api, error) { - if (!shouldRedirectToHome(api, error)) return false; - if (api && api.clearToken) api.clearToken(); - redirectToHome(); - return true; - } -``` - -Replace the existing combined early-return condition in `loadProfile()` with these two branches: - -```js - if (!api || !documentRef || !documentRef.querySelector('[data-profile-page]')) return; - if (redirectUnauthorized(api)) return; -``` - -Update its `catch` block to start with: - -```js - if (redirectUnauthorized(api, error)) return; -``` - -Update `submitProfileForm()` to place `if (redirectUnauthorized(api)) return;` after its existing null guard, and to start its `catch` block with `if (redirectUnauthorized(api, error)) return;` before setting an error status or message. - -Export the classifier with the existing public methods: - -```js - shouldRedirectToHome: shouldRedirectToHome, -``` - -- [ ] **Step 4: Connect both logout confirmations to the API client** - -Add this function before `bindLogout()` in `public/js/profile-common.js`: - -```js - function performLogout(targetUrl) { - var api = window.GenealogyApi && window.GenealogyApi.defaultClient; - var redirect = function () { - window.location.href = targetUrl || 'index.html'; - }; - - if (!api || !api.logout) { - redirect(); - return; - } - - api.logout().catch(function () {}).then(redirect); - } -``` - -In `bindLogout()`, change the default target URL to `index.html`, replace the Layui confirmation callback body with `performLogout(targetUrl);`, and add this event handler before the existing `[data-logout-close]` handler: - -```js - $(document).on('click', '[data-logout-confirm]', function (event) { - event.preventDefault(); - closeLegacyLogout(); - performLogout($(this).attr('href') || 'index.html'); - }); -``` - -In `profile.html`, change the legacy confirmation anchor to: - -```html -确认退出 -``` - -- [ ] **Step 5: Run the focused profile tests and syntax checks** - -Run: `node tests/profile-pages.test.js` - -Expected: `profile-pages tests passed`. - -Run: `node tests/profile-logout-structure.test.js` - -Expected: `profile logout structure tests passed`. - -Run: `node --check public/js/profile-pages.js` - -Expected: exit code `0`. - -Run: `node --check public/js/profile-common.js` - -Expected: exit code `0`. - -- [ ] **Step 6: Commit the profile and logout flow** - -```powershell -git add -- public/js/profile-pages.js public/js/profile-common.js profile.html tests/profile-pages.test.js tests/profile-logout-structure.test.js -git commit -m "fix: guard profile auth and logout" -``` - -### Task 4: Record the current flow and run end-to-end regression checks - -**Files:** -- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md:1-9, 32, 70, 163` -- Modify: `docs/superpowers/specs/2026-07-11-auth-to-profile-flow-design.md:3-5` - -**Interfaces:** -- Consumes: the verified results of Tasks 1 through 3. -- Produces: a tracker whose current address and authentication-flow status match the code and tests, and a spec marked approved for implementation. - -- [ ] **Step 1: Run all focused flow tests** - -Run: - -```powershell -node tests/config.test.js -node tests/api-client.test.js -node tests/auth-pages.test.js -node tests/profile-pages.test.js -node tests/profile-logout-structure.test.js -``` - -Expected: each command prints its success message and exits with code `0`. - -- [ ] **Step 2: Run syntax and complete regression checks** - -Run: - -```powershell -node --check utils/ApiClient.js -node --check public/js/profile-pages.js -node --check public/js/profile-common.js -Get-ChildItem -Path tests -Filter *.test.js | ForEach-Object { & node $_.FullName; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } } -``` - -Expected: all syntax checks exit `0`; every test script prints its success message; PowerShell exits `0` on the first test failure instead of masking it. - -- [ ] **Step 3: Update the tracker from verified evidence** - -At the top of `docs/pc-api-page-integration-tracker-2026-07-09.md`, replace the active base-address line with: - -```markdown -接口基础地址:`http://182.61.18.23:8080` -``` - -Add this section immediately after the introductory historical-record lines: - -```markdown -## Current Endpoint Contract - -- Runtime owner: `config.js`. -- Current development and production address: `http://182.61.18.23:8080`. -- Address change record: on 2026-07-11 the active address changed from the historical test domain to this IP address and port. Historical references to `test-genealogy-api.ddxcjp.cn` do not describe the current runtime configuration. -``` - -Replace the YAML-analysis item that says the project currently uses the test domain with: - -```markdown -2. `servers` 仍是本地地址示例;当前运行时基础地址以 `config.js` 为准,为 `http://182.61.18.23:8080`。 -``` - -Replace the PC-010 row with: - -```markdown -| PC-010 | 已复核(当前地址已同步) | `config.js`、`tests/config.test.js`、`utils/ApiClient.js` | API 基础地址 | 当前开发和生产地址均为 `http://182.61.18.23:8080`;地址唯一运行时来源为 `config.js`,`tests/config.test.js` 已同步断言。 | -``` - -After Steps 1 and 2 pass, run `Get-Date -Format "yyyy-MM-dd HH:mm:ss K"` and insert its output as the `完成时间` line immediately after the `状态` line in this record. Append the rest of this record after the existing PC entries: - -```markdown -## PC-025 注册、登录到个人资料闭环 - -- 状态:已完成 -- 规划文件:`docs/superpowers/specs/2026-07-11-auth-to-profile-flow-design.md` -- 页面范围:`register.html`、`login.html`、`profile.html`、`profile-data.html` -- 接口范围:`/genealogy/pc/auth/register`、`/genealogy/pc/auth/login`、`/genealogy/pc/auth/login/sms`、`/genealogy/pc/auth/profile`、`/genealogy/pc/auth/logout` -- 地址契约:运行时唯一来源为 `config.js`;当前开发和生产地址均为 `http://182.61.18.23:8080`。 -- 流程结果:注册成功清除 token 并跳转 `login.html`;密码和短信登录必须取得 token 后跳转 `profile.html`;个人资料无 token 或收到 HTTP/业务 `401` 时清 token 并跳转 `index.html`;确认退出无论接口结果都清 token 并跳转 `index.html`。 -- 修改文件:`tests/config.test.js`、`utils/ApiClient.js`、`tests/api-client.test.js`、`public/js/profile-pages.js`、`public/js/profile-common.js`、`profile.html`、`tests/profile-pages.test.js`、`tests/profile-logout-structure.test.js`。 -- 验证结果:`node tests/config.test.js`、`node tests/api-client.test.js`、`node tests/auth-pages.test.js`、`node tests/profile-pages.test.js`、`node tests/profile-logout-structure.test.js`、三个 `node --check` 命令和全量 Node 测试均通过。 -``` - -Change the spec status body to: - -```markdown -Approved for implementation. -``` - -- [ ] **Step 4: Check final whitespace and commit the record** - -Run: `git diff --check` - -Expected: exit code `0`; pre-existing CRLF conversion warnings are not whitespace errors. - -```powershell -git add -- docs/pc-api-page-integration-tracker-2026-07-09.md docs/superpowers/specs/2026-07-11-auth-to-profile-flow-design.md -git commit -m "docs: record auth profile flow" -``` diff --git a/docs/superpowers/plans/2026-07-11-content-page-state-closure.md b/docs/superpowers/plans/2026-07-11-content-page-state-closure.md new file mode 100644 index 0000000..6ae23a6 --- /dev/null +++ b/docs/superpowers/plans/2026-07-11-content-page-state-closure.md @@ -0,0 +1,129 @@ +# 内容与协作页面状态收口 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:** 让缺少正式接口的内容、协作、消息与提醒页面明确展示开发状态,同时保留家族圈动态的真实功能。 + +**Architecture:** 复用 `public/js/pending-pages.js` 的页面状态契约。每个待开发页面仅增加 `data-feature-status`、中文提示文案和状态脚本引用;脚本阻止主内容区未声明可用的业务链接,不新增接口调用、示例数据或业务脚本。 + +**Tech Stack:** 原生 JavaScript、静态 HTML/CSS、Node LTS `node:test`。 + +## Global Constraints + +- 保留现有页面、布局、视觉类名和导航结构,不删除用户设计。 +- `PC.openapi2.json` 中缺少正式接口的功能不得伪造请求或静态数据为真实业务。 +- 家族圈动态 `profile-feed.html` 与 `profile-feed-edit.html` 已接入正式接口,不能标记为待开发。 +- 所有新增注释使用中文;不写入测试账号或密码。 +- 当前工作区含用户未提交修改,不执行提交、重置或清理操作。 + +--- + +### Task 1: 扩展页面状态测试 + +**Files:** +- Modify: `tests/pending-pages.test.js` + +**Interfaces:** +- Consumes: 每页的 `data-feature-status="pending"` 与 `public/js/pending-pages.js`。 +- Produces: 内容与协作待开发页面的静态约束,以及家族圈动态未被误标记的约束。 + +- [x] **Step 1: 写入失败测试** + +在 `contentPendingPages` 中列出以下页面: + +```js +const contentPendingPages = [ + 'profile-content.html', 'profile-article.html', 'profile-article-edit.html', + 'profile-album.html', 'profile-video.html', 'profile-gift.html', + 'profile-gift-edit.html', 'profile-growth.html', 'profile-growth-edit.html', + 'profile-memo.html', 'profile-memo-edit.html', 'profile-merit.html', + 'profile-merit-edit.html', 'profile-messages.html', 'profile-feedback.html', + 'profile-admin-permissions.html', 'profile-data-reminders.html' +]; +``` + +同时断言 `profile-feed.html`、`profile-feed-edit.html` 不含待开发状态标记,并验证只有 `data-feature-link="available"` 的业务链接可跳转。 + +- [x] **Step 2: 运行失败测试** + +Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; node --test tests/pending-pages.test.js` + +Expected: 新增页面尚未标记,测试失败。 + +### Task 2: 标记内容与协作页面 + +**Files:** +- Modify: `profile-content.html` +- Modify: `profile-article.html` +- Modify: `profile-article-edit.html` +- Modify: `profile-album.html` +- Modify: `profile-video.html` +- Modify: `profile-gift.html` +- Modify: `profile-gift-edit.html` +- Modify: `profile-growth.html` +- Modify: `profile-growth-edit.html` +- Modify: `profile-memo.html` +- Modify: `profile-memo-edit.html` +- Modify: `profile-merit.html` +- Modify: `profile-merit-edit.html` +- Modify: `profile-messages.html` +- Modify: `profile-feedback.html` +- Modify: `profile-admin-permissions.html` +- Modify: `profile-data-reminders.html` +- Test: `tests/pending-pages.test.js` + +**Interfaces:** +- Consumes: `PageAvailability.init()` 读取的 ``。 +- Produces: 可见的“功能开发中”提示,并拦截主内容区提交与操作按钮。 + +- [x] **Step 1: 为每页增加状态契约** + +在目标页面的 `body` 增加 `data-feature-status="pending"` 和与业务相符的中文 `data-feature-message`。例如谱文页面使用“谱文服务正在开发中,当前页面仅保留设计预览。” + +- [x] **Step 2: 加载统一状态脚本** + +有 `ApiClient.js` 的页面在其后加载: + +```html + + +``` + +`profile-video.html` 与 `profile-data-reminders.html` 不依赖 `ApiClient.js`,在 `page-effects.js` 前加载状态脚本。 + +- [x] **Step 3: 收束业务入口跳转** + +待开发页面主内容区的业务链接由状态脚本拦截并显示当前页面提示;侧栏保留页面浏览导航。`profile-content.html` 的家族圈动态入口使用 `data-feature-link="available"`,继续跳转到 `profile-feed.html`。 + +- [x] **Step 4: 运行测试确认通过** + +Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; node --test tests/pending-pages.test.js` + +Expected: 待开发页面均加载状态脚本,家族圈动态页面未被标记。 + +### Task 3: 更新进度并做静态验证 + +**Files:** +- Modify: `docs/规划.md` +- Modify: `docs/superpowers/plans/2026-07-11-content-page-state-closure.md` +- Test: `tests/*.test.js` + +**Interfaces:** +- Consumes: 全部页面的本地脚本引用。 +- Produces: 已完成第二轮进度与可重复的验证结果。 + +- [x] **Step 1: 更新总规划的第二轮状态** + +将 `docs/规划.md` 中“第二轮:内容与协作页面状态收口”从未完成更新为已完成,并写明家族圈动态未受影响。 + +- [x] **Step 2: 执行完整测试** + +Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test` + +Expected: 所有 Node 测试通过。 + +- [x] **Step 3: 执行静态检查** + +Run: PowerShell 扫描全部 HTML 的 `src="*.js"`,随后运行 `git diff --check`。 + +Expected: 每个本地脚本文件存在,且没有空白格式错误。 diff --git a/docs/superpowers/plans/2026-07-11-family-feed-integration.md b/docs/superpowers/plans/2026-07-11-family-feed-integration.md new file mode 100644 index 0000000..1be5559 --- /dev/null +++ b/docs/superpowers/plans/2026-07-11-family-feed-integration.md @@ -0,0 +1,192 @@ +# 家族圈动态接口接入 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.openapi2.json` 中已交付的 12 个家族圈动态接口接入既有动态页面,并保持未开发的家谱业务不调用不存在的接口。 + +**Architecture:** `PC.openapi2.json` 是接口唯一依据;`ApiClient` 提供一一对应的私有请求封装,`feed-pages.js` 只协调页面事件和已定义的接口方法。动态页从 URL 查询参数读取真实 `genealogyId`,不写入示例家谱 ID。 + +**Tech Stack:** 原生 JavaScript、Axios、Node LTS `node:test`、静态 HTML/CSS。 + +## Global Constraints + +- 所有新增或修改的注释使用清晰中文。 +- 不删除现有页面和样式;不接入尚未出现在 `PC.openapi2.json` 的业务。 +- 不提交当前工作区:其中含用户已有的未提交修改。 +- 动态读取响应目前只有通用 `ListResult`/`PageResult`,页面仅使用 `feedId` 与 `feedContent`;后端返回缺少这两个字段时展示明确错误,不猜测字段名。 + +--- + +### Task 1: 更新接口边界测试和动态请求测试 + +**Files:** +- Modify: `tests/api-client-contract.test.js` +- Modify: `tests/pc-scope.test.js` +- Create: `tests/feed-pages.test.js` + +**Interfaces:** +- Consumes: `PC.openapi2.json` 的 12 个 `/genealogy/pc/genealogies/{genealogyId}/feeds` 操作。 +- Produces: 对客户端路径、HTTP 方法、请求体和页面请求体转换的自动化约束。 + +- [ ] **Step 1: 写入失败测试** + +```js +test('家族圈动态方法使用文档定义的路径和请求体', async () => { + const calls = []; + const client = createClientWithRecorder(calls); + await client.createFeed(900001001, { feedContent: '今天上传一张老照片。' }); + await client.feedCommentsPage(900001001, 7001, { pageNum: 1, pageSize: 10 }); + assert.deepEqual(calls.map((call) => [call.method, call.url]), [ + ['post', '/genealogy/pc/genealogies/900001001/feeds'], + ['get', '/genealogy/pc/genealogies/900001001/feeds/7001/comments/page'] + ]); +}); + +test('动态表单只构造 FamilyFeedBody 字段', () => { + assert.deepEqual(FeedPages.buildFeedBody({ + feedContent: '家谱动态', mediaOssIds: '101,102', status: '0' + }), { + feedType: 'text', feedContent: '家谱动态', mediaOssIds: '101,102', status: '0' + }); +}); +``` + +- [ ] **Step 2: 运行失败测试** + +Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test` + +Expected: 动态方法或 `feed-pages.js` 尚不存在导致失败。 + +- [ ] **Step 3: 调整范围检查** + +```js +assert.match(contract, /\/genealogy\/pc\/genealogies\/\{genealogyId\}\/feeds/); +assert.equal(read('profile-feed.html').includes('src="public/js/feed-pages.js"'), true); +assert.equal(read('profile-feed-edit.html').includes('src="public/js/feed-pages.js"'), true); +``` + +- [ ] **Step 4: 重新运行测试,确认仍处于预期失败状态** + +Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test` + +Expected: 仅动态实现相关断言失败。 + +### Task 2: 增加 12 个家族圈 API 方法 + +**Files:** +- Modify: `utils/ApiClient.js` +- Modify: `tests/api-client-contract.test.js` + +**Interfaces:** +- Consumes: `genealogyId:number|string`、`feedId:number|string`、`commentId:number|string`。 +- Produces: `feeds`、`feedsPage`、`feedDetail`、`createFeed`、`updateFeed`、`deleteFeed`、`likeFeed`、`unlikeFeed`、`feedComments`、`feedCommentsPage`、`createFeedComment`、`deleteFeedComment`。 + +- [ ] **Step 1: 保持失败测试不变** + +```js +await client.createFeed(900001001, { feedContent: '家谱动态' }); +assert.equal(calls[0].url, '/genealogy/pc/genealogies/900001001/feeds'); +assert.deepEqual(calls[0].data, { feedContent: '家谱动态' }); +``` + +- [ ] **Step 2: 实现最小路径构造** + +```js +function feedPath(genealogyId, suffix) { + return '/genealogy/pc/genealogies/' + encodeURIComponent(genealogyId) + '/feeds' + suffix; +} + +createFeed: function (genealogyId, body) { + return request('POST', feedPath(genealogyId, ''), { body: body }); +} +``` + +- [ ] **Step 3: 运行测试,确认通过** + +Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test` + +Expected: 动态客户端路径和请求体断言通过。 + +### Task 3: 恢复动态页的真实交互 + +**Files:** +- Create: `public/js/feed-pages.js` +- Modify: `profile-feed.html` +- Modify: `profile-feed-edit.html` +- Modify: `tests/feed-pages.test.js` + +**Interfaces:** +- Consumes: `GenealogyApi.defaultClient` 的 12 个动态方法和 URL 中的 `genealogyId`、可选 `feedId`。 +- Produces: 动态列表、发布/编辑、点赞/取消点赞、评论查看/发布/删除、动态删除事件。 + +- [ ] **Step 1: 使页面测试失败** + +```js +assert.equal(FeedPages.getCurrentGenealogyId('?genealogyId=900001001'), '900001001'); +assert.equal(FeedPages.getCurrentGenealogyId(''), ''); +assert.equal(FeedPages.getFeedId({ feedId: 7001 }), '7001'); +``` + +- [ ] **Step 2: 实现页面数据转换和事件** + +```js +function buildFeedBody(values) { + return { + feedType: 'text', + feedContent: String(values.feedContent || '').trim(), + mediaOssIds: trimOrUndefined(values.mediaOssIds), + status: trimOrUndefined(values.status) + }; +} +``` + +列表只读取 `feedId`、`feedContent`,并在缺少其中任一字段时显示“动态响应缺少 feedId 或 feedContent,请联系后端补充 DTO”。编辑页以 `feedId` 查询详情并调用 `updateFeed`;列表操作调用点赞、取消点赞、评论、删除接口后重新加载当前页。 + +- [ ] **Step 3: 挂载脚本和契约字段** + +```html + + +``` + +移除未在 `FamilyFeedBody` 中定义的“可见范围”提交字段;保持页面布局与既有视觉类名。 + +- [ ] **Step 4: 运行测试,确认通过** + +Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test` + +Expected: `feed-pages.test.js` 和已有测试全部通过。 + +### Task 4: 更新文档来源与最终验证 + +**Files:** +- Modify: `tests/pc-scope.test.js` +- Modify: `PC.openapi.yaml` + +**Interfaces:** +- Consumes: `PC.openapi2.json` 的接口路径。 +- Produces: 明确标记旧 YAML 不再为接口源,并使静态检查以 JSON 为准。 + +- [ ] **Step 1: 写入失败检查** + +```js +const contract = JSON.parse(read('PC.openapi2.json')); +assert.ok(contract.paths['/genealogy/pc/genealogies/{genealogyId}/feeds']); +``` + +- [ ] **Step 2: 标记旧 YAML 失效** + +在 `PC.openapi.yaml` 文件首行写入中文说明:该文件不再作为代码或测试接口源,正式接口源为 `PC.openapi2.json`。不复制或手改 JSON 中未定义的动态响应 DTO。 + +- [ ] **Step 3: 全量验证** + +Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test` + +Expected: 全部测试通过,且所有动态页面加载的脚本文件都存在。 + +## Self-review + +- 范围只覆盖最新文档已交付的家族圈动态接口,没有加入家谱创建、成员或世系树接口。 +- 所有请求体字段来自 `FamilyFeedBody` 或 `FamilyFeedCommentBody`。 +- 读取响应不使用历史字段兜底,不把未知字段猜成有效数据。 +- 任何 URL 缺少 `genealogyId` 的页面都会明确提示,不使用硬编码示例 ID。 diff --git a/docs/superpowers/plans/2026-07-11-family-page-state-closure.md b/docs/superpowers/plans/2026-07-11-family-page-state-closure.md new file mode 100644 index 0000000..01f4da7 --- /dev/null +++ b/docs/superpowers/plans/2026-07-11-family-page-state-closure.md @@ -0,0 +1,140 @@ +# 家谱基础页面状态收口 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:** 让尚未获得后端家谱接口的基础页面明确展示开发状态,并阻止表单和操作按钮伪造成功结果。 + +**Architecture:** 仅在家谱列表、创建/加入、审核、成员管理、邀请、世系和字辈页面加载一个轻量状态脚本。页面通过 `data-feature-status` 和 `data-feature-message` 提供文案;脚本统一插入提示条,并拦截页面主内容区的提交与按钮操作。 + +**Tech Stack:** 原生 JavaScript、静态 HTML/CSS、Node LTS `node:test`。 + +## Global Constraints + +- 保留现有页面、布局、视觉类名和导航结构,不删除用户设计。 +- 不调用 `PC.openapi2.json` 中不存在的家谱、成员、世系或字辈接口。 +- 所有新增注释使用中文;不写入测试账号或密码。 +- 当前工作区含用户未提交修改,不执行提交、重置或清理操作。 + +--- + +### Task 1: 建立失败测试 + +**Files:** +- Create: `tests/pending-pages.test.js` +- Modify: `tests/pc-scope.test.js` + +**Interfaces:** +- Consumes: `PageAvailability.buildBanner(message)` 与 `PageAvailability.isPendingPage(body)`。 +- Produces: 对状态脚本、页面标记和脚本加载的约束。 + +- [x] **Step 1: 写入失败测试** + +```js +test('待开发页面生成明确且转义后的状态提示', () => { + assert.match(PageAvailability.buildBanner('家谱基础服务正在开发中'), /家谱基础服务正在开发中/); + assert.match(PageAvailability.buildBanner(' + + +``` + +- [x] **Step 3: 运行完整测试** + +Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test` + +Expected: 全部测试通过。 + +### Task 4: 静态验证 + +**Files:** +- Test: `tests/pc-scope.test.js` + +**Interfaces:** +- Consumes: 8 个已标记页面。 +- Produces: 所有待开发页面有状态文案,所有页面脚本路径存在。 + +- [x] **Step 1: 执行脚本引用检查** + +Run: PowerShell 扫描全部 HTML 的 `src="*.js"`,确认每个本地脚本文件存在。 + +- [x] **Step 2: 执行格式检查** + +Run: `git diff --check` + +Expected: 无缺失脚本和空白格式错误。 diff --git a/docs/superpowers/plans/2026-07-11-pc-endpoint-page-coverage.md b/docs/superpowers/plans/2026-07-11-pc-endpoint-page-coverage.md deleted file mode 100644 index cf7765d..0000000 --- a/docs/superpowers/plans/2026-07-11-pc-endpoint-page-coverage.md +++ /dev/null @@ -1,278 +0,0 @@ -# PC Endpoint Page Coverage 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:** Make every callable operation in `genealogy-pc-openapi.yaml` traceable to a real PC page flow, an automated regression test, and a manual acceptance step. - -**Architecture:** `utils/ApiClient.js` remains the sole network contract owner. Page modules in `public/js` own input validation, loading and success/error state; `tests` prove their request and navigation behavior. The coverage matrix distinguishes a complete user flow from an endpoint that is documented but lacks the contract required to expose it safely. - -**Tech Stack:** Static HTML, browser JavaScript, Axios, Layui, Node `assert` tests. - -## Global Constraints - -- Runtime API base URL is owned only by `config.js`: `http://182.61.18.23:8080`. -- Only PC/H5 routes and scenes defined by `genealogy-pc-openapi.yaml` may be requested. -- Registration never persists a token; password and SMS login persist the returned token and navigate to `profile.html`. -- Logout, unauthenticated profile access and account deactivation navigate to `index.html`. -- Every behavior change starts with a failing Node test and ends with focused plus full test verification. -- No page invents a business table name, business ID, CAPTCHA scene or legacy captcha submission field that is not in the PC contract. - -## Endpoint Matrix - -| Operation | Page owner | Automated test | Manual acceptance order | Status target | -| --- | --- | --- | --- | --- | -| `GET /captcha/require` | `CaptchaPages` on auth forms | `tests/captcha-pages.test.js` | 1-4 | Complete | -| `POST /captcha/challenge` | `CaptchaPages` on auth forms | `tests/captcha-pages.test.js` | 1-4 | Complete | -| `POST /captcha/verify` | `CaptchaPages` on auth forms | `tests/captcha-pages.test.js` | 1-4 | Complete | -| `GET /auth/code` | no safe consumer | `tests/pc-endpoint-coverage.test.js` | blocked | Backend contract required | -| Register, password login, SMS login, SMS code | `register.html`, `login.html` | `tests/auth-pages.test.js`, `tests/api-client.test.js` | 1-3 | Complete | -| Profile read/update | `profile.html`, `profile-data.html` | `tests/profile-pages.test.js` | 4-5 | Complete | -| Password reset | `forgot-password.html` | `tests/auth-pages.test.js` | 6 | Complete | -| Password change, logout, account deactivation | `profile-security.html` | `tests/security-pages.test.js` | 7-9 | Complete | -| Phone change | `profile-security.html` | `tests/security-pages.test.js` | blocked | PC/H5 phone SMS scene required | -| Single upload | profile avatar and existing upload fields | `tests/upload-pages.test.js` | 10 | Complete | -| Resumable init/chunk/complete | same file input for large files | `tests/upload-pages.test.js`, `tests/api-client.test.js` | 11 | Complete | -| File reference bind/release | no safe business entity contract | `tests/pc-endpoint-coverage.test.js` | blocked | PC business table and ID contract required | -| Region children/path/search/detail | profile address picker and region search control | `tests/region-pages.test.js`, `tests/profile-pages.test.js` | 12 | Complete | - -## Task 1: Enforce Endpoint Coverage - -**Files:** -- Create: `tests/pc-endpoint-coverage.test.js` -- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md` - -**Interfaces:** -- Consumes: the 25 operations declared under `paths` in `genealogy-pc-openapi.yaml`. -- Produces: a test-owned `ENDPOINTS` list that assigns every operation one of `complete` or `blocked` with a page/test owner. - -- [ ] **Step 1: Write the failing coverage test** - -```js -assert.deepStrictEqual(missingOperations, []); -assert.deepStrictEqual(unownedOperations, []); -assert.deepStrictEqual(unsupportedBlockedOperations, []); -``` - -- [ ] **Step 2: Run the test to verify it fails** - -Run: `node tests\pc-endpoint-coverage.test.js` - -Expected: failure listing the endpoint operations without a page/test/blocker owner. - -- [ ] **Step 3: Add the explicit 25-operation matrix** - -```js -const ENDPOINTS = { - 'GET /captcha/require': { page: 'auth', test: 'captcha-pages', status: 'complete' }, - 'GET /auth/code': { status: 'blocked', reason: 'legacy captcha has no PC request field' } -}; -``` - -- [ ] **Step 4: Run the test to verify it passes** - -Run: `node tests\pc-endpoint-coverage.test.js` - -Expected: `pc-endpoint-coverage tests passed`. - -## Task 2: Complete and Test the Auth Journey - -**Files:** -- Modify: `public/js/auth-pages.js` -- Modify: `public/js/profile-pages.js` -- Modify: `public/js/security-pages.js` -- Modify: `tests/auth-pages.test.js` -- Modify: `tests/profile-pages.test.js` -- Modify: `tests/security-pages.test.js` - -**Interfaces:** -- Consumes: `GenealogyApi.login`, `loginBySms`, `register`, `sendSmsCode`, `resetPassword`, `getProfile`, `updateProfile`, `changePassword`, `deactivateAccount`, and `logout`. -- Produces: consistent form validation, token state and redirect behavior for the acceptance steps 1-9. - -- [ ] **Step 1: Write failing redirect and request tests** - -```js -assert.strictEqual(loginRoot.location.href, 'profile.html'); -assert.strictEqual(registerRoot.location.href, 'login.html'); -assert.strictEqual(logoutRoot.location.href, 'index.html'); -``` - -- [ ] **Step 2: Run focused tests to verify failure** - -Run: `node tests\auth-pages.test.js; node tests\profile-pages.test.js; node tests\security-pages.test.js` - -Expected: a failure naming the missing flow or redirect. - -- [ ] **Step 3: Implement only the missing flow branch in its page module** - -```js -await api.loginBySms(buildSmsLoginBody(values)); -root.location.href = 'profile.html'; -``` - -Use the corresponding documented endpoint method for each form; do not create a second token store or a page-local route string owner. - -- [ ] **Step 4: Run focused tests** - -Run: `node tests\auth-pages.test.js; node tests\profile-pages.test.js; node tests\security-pages.test.js` - -Expected: all three scripts print their pass messages. - -## Task 3: Make Region Search, Path and Detail Usable - -**Files:** -- Modify: `profile-data.html` -- Modify: `public/js/region-pages.js` -- Modify: `tests/region-pages.test.js` - -**Interfaces:** -- Consumes: `regionChildren(parentCode)`, `regionSearch({ keyword })`, `regionPath(regionCode)`, and `regionDetail(regionCode)`. -- Produces: a profile-address picker that can search by name/code, select a result, resolve its path and write province/city/district codes before profile save. - -- [ ] **Step 1: Write a failing result-normalization and selected-path test** - -```js -assert.deepStrictEqual(RegionPages.buildRegionSelection(result), { - provinceCode: '510000', - cityCode: '510100', - districtCode: '510104' -}); -``` - -- [ ] **Step 2: Run the focused test to verify failure** - -Run: `node tests\region-pages.test.js` - -Expected: failure because `buildRegionSelection` is not exported. - -- [ ] **Step 3: Add the profile search control and selection behavior** - -```js -var path = await api.regionPath(regionCode); -var selection = buildRegionSelection(path); -applyRegionSelection(picker, selection); -await api.regionDetail(regionCode); -``` - -- [ ] **Step 4: Run focused tests** - -Run: `node tests\region-pages.test.js; node tests\profile-pages.test.js` - -Expected: both scripts print their pass messages. - -## Task 4: Complete Single and Resumable File Upload - -**Files:** -- Modify: `public/js/upload-pages.js` -- Modify: `utils/ApiClient.js` -- Modify: `tests/upload-pages.test.js` -- Modify: `tests/api-client.test.js` - -**Interfaces:** -- Consumes: `uploadFile(file)`, `initResumableUpload(body)`, `uploadChunk(body)`, `completeResumableUpload(body)`. -- Produces: `uploadFileForPage(file, options)` which uses single upload for files at or below the configured threshold and the init/chunk/complete sequence for larger files. - -- [ ] **Step 1: Write failing upload-strategy tests** - -```js -assert.strictEqual(UploadPages.getUploadMode({ size: 1024 }, 4194304), 'single'); -assert.strictEqual(UploadPages.getUploadMode({ size: 4194305 }, 4194304), 'resumable'); -``` - -- [ ] **Step 2: Run focused tests to verify failure** - -Run: `node tests\upload-pages.test.js; node tests\api-client.test.js` - -Expected: failure because `getUploadMode` does not exist. - -- [ ] **Step 3: Implement sequential resumable upload** - -```js -var init = await api.initResumableUpload(initBody); -for (index = 0; index < chunks.length; index += 1) { - await api.uploadChunk({ uploadId: init.uploadId, chunkIndex: index, chunkMd5: chunkMd5, file: chunks[index] }); -} -return api.completeResumableUpload({ uploadId: init.uploadId, fileMd5: fileMd5, fileSize: file.size }); -``` - -- [ ] **Step 4: Run focused tests** - -Run: `node tests\upload-pages.test.js; node tests\api-client.test.js` - -Expected: both scripts print their pass messages. - -## Task 5: Record and Enforce Backend-Contract Blocks - -**Files:** -- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md` -- Modify: `tests/pc-endpoint-coverage.test.js` - -**Interfaces:** -- Consumes: the legacy captcha response, phone SMS scene requirements, and file-reference required business fields from the PC YAML. -- Produces: three named blocked operations with an exact missing backend input and no speculative page request. - -- [ ] **Step 1: Assert exact blocker reasons** - -```js -assert.strictEqual(ENDPOINTS['GET /auth/code'].reason, 'legacy captcha has no PC request field'); -assert.strictEqual(ENDPOINTS['PUT /genealogy/pc/auth/phone'].reason, 'PC/H5 phone SMS scene is not documented'); -assert.strictEqual(ENDPOINTS['POST /genealogy/pc/files/reference'].reason, 'PC business table and ID contract is not documented'); -``` - -- [ ] **Step 2: Run the test to verify failure** - -Run: `node tests\pc-endpoint-coverage.test.js` - -Expected: failure until all three reasons are explicit. - -- [ ] **Step 3: Update the tracker without adding a fake request** - -```md -| Blocked operation | Missing backend contract | Page behavior | -| --- | --- | --- | -| `GET /auth/code` | legacy code submission field | no page action | -``` - -- [ ] **Step 4: Run the test to verify it passes** - -Run: `node tests\pc-endpoint-coverage.test.js` - -Expected: `pc-endpoint-coverage tests passed`. - -## Task 6: Final Verification and User Test Script - -**Files:** -- Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md` - -- [ ] **Step 1: Run syntax checks** - -Run: `node --check utils\ApiClient.js` - -Run: `node --check public\js\auth-pages.js` - -Run: `node --check public\js\region-pages.js` - -Run: `node --check public\js\upload-pages.js` - -Expected: each command exits with code 0. - -- [ ] **Step 2: Run the complete Node suite** - -Run: `Get-ChildItem -Path tests -Filter *.test.js | ForEach-Object { & node $_.FullName; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } }` - -Expected: every test file prints its pass message. - -- [ ] **Step 3: Check the diff** - -Run: `git diff --check` - -Expected: exit code 0; CRLF conversion notices are informational only. - -- [ ] **Step 4: Record manual acceptance steps** - -Record the precise browser order: register, password login, SMS login, profile display/edit, reset password, password change, logout, account deactivation, address search, single upload, resumable upload, then each documented backend blocker. - -## Self-Review - -- Coverage: Tasks 1-5 assign every documented operation an implementation owner or an explicit backend block; Task 6 provides the requested sequential test script. -- Contract discipline: `ApiClient` remains the only route owner; page modules do not create undocumented paths, scene codes, business tables or identifiers. -- Scope: pages without callable PC paths remain outside implementation; their schemas are not treated as endpoints. diff --git a/docs/superpowers/plans/2026-07-11-public-static-pages.md b/docs/superpowers/plans/2026-07-11-public-static-pages.md new file mode 100644 index 0000000..4bf2f5e --- /dev/null +++ b/docs/superpowers/plans/2026-07-11-public-static-pages.md @@ -0,0 +1,139 @@ +# 官网静态页面补齐 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:** 补齐官网新闻、法律文档入口、通用状态与工单页面,使无接口的线上服务不再伪装为可用功能。 + +**Architecture:** 新增静态 HTML 页面共用 `public/css/site-info.css` 和现有官网头尾布局。新闻页只链接现有静态文章与公告详情;法律页面明确标明待法务确认;工单页面复用 `pending-pages.js` 阻止无接口提交。 + +**Tech Stack:** 静态 HTML、原生 CSS、原生 JavaScript、Node LTS `node:test`。 + +## Global Constraints + +- 保留现有页面、布局、视觉类名和导航结构,不删除用户设计。 +- 不新增 `PC.openapi2.json` 外的接口调用,不伪造新闻接口、法律协议、工单列表或工单详情数据。 +- 法律页面只能显示“待法务确认”说明,发布前必须替换为主体与法务确认的正式文本。 +- 所有新增注释使用中文;不写入测试账号或密码。 +- 当前工作区含用户未提交修改,不执行提交、重置或清理操作。 + +--- + +### Task 1: 建立静态页面约束测试 + +**Files:** +- Create: `tests/public-static-pages.test.js` + +**Interfaces:** +- Consumes: 静态页面文件、`data-feature-status="pending"` 和本地 `href`。 +- Produces: 页面存在、关键说明、状态脚本与本地链接的可重复校验。 + +- [x] **Step 1: 写入失败测试** + +测试必须断言存在以下文件: + +```js +const staticPages = [ + 'news.html', 'privacy.html', 'children-privacy.html', 'terms.html', + 'not-found.html', 'forbidden.html', 'maintenance.html', + 'my-tickets.html', 'ticket-detail.html' +]; +``` + +同时断言:`news.html` 含有新闻分类锚点并链接 `article-detail.html`、`notice-detail.html`;三个法律页面含“待法务确认”;`submit-ticket.html`、`my-tickets.html`、`ticket-detail.html` 有待开发状态和 `pending-pages.js`;新页面中的本地 `href` 全部存在。 + +- [x] **Step 2: 运行失败测试** + +Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; node --test tests/public-static-pages.test.js` + +Expected: 新页面不存在导致测试失败。 + +### Task 2: 建立通用静态页面外观与新闻、法律入口 + +**Files:** +- Create: `public/css/site-info.css` +- Create: `news.html` +- Create: `privacy.html` +- Create: `children-privacy.html` +- Create: `terms.html` +- Modify: `index.html` + +**Interfaces:** +- Consumes: 现有 `public/css/public.css` 的 `site-header`、`footer`、`container`、`btn` 与 `card` 样式。 +- Produces: 新闻分类和详情跳转,以及能从首页底部访问的法律入口。 + +- [x] **Step 1: 创建复用样式** + +`site-info.css` 提供 `.site-info-hero`、`.site-info-layout`、`.site-info-card`、`.site-info-status`、`.site-info-links`,仅处理本轮新增页面的排版和窄屏适配。 + +- [x] **Step 2: 创建新闻页面** + +`news.html` 必须有“平台公告”“家谱文化”分类锚点,并将两条静态内容分别链接到 `notice-detail.html` 与 `article-detail.html`。 + +- [x] **Step 3: 创建法律页面并连接首页** + +三个法律页面均写明“待法务确认”,并互相提供跳转链接。首页 `footer-legal` 增加三个页面链接。 + +### Task 3: 建立状态与工单边界 + +**Files:** +- Create: `not-found.html` +- Create: `forbidden.html` +- Create: `maintenance.html` +- Create: `my-tickets.html` +- Create: `ticket-detail.html` +- Modify: `submit-ticket.html` + +**Interfaces:** +- Consumes: `PageAvailability.init()` 读取的待开发页面属性。 +- Produces: 三种无接口提交的工单状态,和可返回首页、帮助中心的静态状态页。 + +- [x] **Step 1: 创建三个通用状态页** + +三个页面分别显示“页面不存在”“无访问权限”“系统维护中”,并提供 `index.html` 返回入口。 + +- [x] **Step 2: 创建工单列表与详情状态页** + +`my-tickets.html`、`ticket-detail.html` 均使用: + +```html + +``` + +并在 `public/js/pending-pages.js` 后仅放行帮助中心链接。 + +- [x] **Step 3: 收束提交工单表单** + +`submit-ticket.html` 增加: + +```html + +``` + +在 `ApiClient.js` 后加载状态脚本,并为“返回帮助中心”链接添加 `data-feature-link="available"`。 + +### Task 4: 回写进度与验证 + +**Files:** +- Modify: `docs/规划.md` +- Modify: `docs/superpowers/plans/2026-07-11-public-static-pages.md` +- Test: `tests/*.test.js` + +**Interfaces:** +- Consumes: 新增静态页面和全部 HTML 本地脚本引用。 +- Produces: 第四轮进度记录和验证结果。 + +- [x] **Step 1: 写入第四轮完成状态** + +在 `docs/规划.md` 的“实施进度”中增加静态官网页面补齐已完成,并注明法律文本需法务替换。 + +- [x] **Step 2: 执行完整测试** + +Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test` + +Expected: 所有 Node 测试通过。 + +- [x] **Step 3: 执行静态检查** + +Run: PowerShell 扫描全部 HTML 的 `src="*.js"`,随后运行 `git diff --check`。 + +Expected: 每个本地脚本文件存在,且没有空白格式错误。 diff --git a/docs/superpowers/plans/2026-07-11-residual-pc-entry-closure.md b/docs/superpowers/plans/2026-07-11-residual-pc-entry-closure.md new file mode 100644 index 0000000..3d493a4 --- /dev/null +++ b/docs/superpowers/plans/2026-07-11-residual-pc-entry-closure.md @@ -0,0 +1,112 @@ +# 剩余用户 PC 业务入口收口 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:** 复用 `public/js/pending-pages.js` 的状态契约和链接收束规则。仅标记 `profile-family-home.html`、`profile-share.html`、`profile-services.html`;账号主页、个人资料、安全设置和家族圈动态继续使用现有真实功能。 + +**Tech Stack:** 原生 JavaScript、静态 HTML/CSS、Node LTS `node:test`。 + +## Global Constraints + +- 保留现有页面、布局、视觉类名和导航结构,不删除用户设计。 +- `PC.openapi2.json` 中不存在家谱主页、邀请奖励和会员订单的正式接口,不得伪造请求或静态数据为真实业务。 +- `profile-family-home.html` 的家族圈动态入口继续跳转到 `profile-feed.html`。 +- 所有新增注释使用中文;不写入测试账号或密码。 +- 当前工作区含用户未提交修改,不执行提交、重置或清理操作。 + +--- + +### Task 1: 扩展入口状态测试 + +**Files:** +- Modify: `tests/pending-pages.test.js` + +**Interfaces:** +- Consumes: 目标页面的 `data-feature-status="pending"`、状态脚本引用与 `data-feature-link="available"`。 +- Produces: 剩余业务入口的静态约束,及真实账户页面不被误标记的约束。 + +- [x] **Step 1: 写入失败测试** + +新增以下数组并加入现有待开发页面检查: + +```js +const residualPendingPages = [ + 'profile-family-home.html', + 'profile-share.html', + 'profile-services.html' +]; +``` + +新增断言:`profile-family-home.html` 的 `profile-feed.html` 链接必须带有 `data-feature-link="available"`;`profile.html`、`profile-data.html`、`profile-security.html` 不得含待开发状态标记。 + +- [x] **Step 2: 运行失败测试** + +Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; node --test tests/pending-pages.test.js` + +Expected: 三个剩余入口尚未标记,测试失败。 + +### Task 2: 标记剩余业务入口 + +**Files:** +- Modify: `profile-family-home.html` +- Modify: `profile-share.html` +- Modify: `profile-services.html` +- Test: `tests/pending-pages.test.js` + +**Interfaces:** +- Consumes: `PageAvailability.init()` 读取的页面状态属性,和 `PageAvailability.shouldBlockLink(link)` 的可用入口约定。 +- Produces: 状态提示、业务链接拦截,以及唯一保留的动态入口。 + +- [x] **Step 1: 标记页面状态** + +分别在三个 `body` 中增加 `data-feature-status="pending"` 和对应中文提示:家谱主页使用“家谱概览与管理服务正在开发中,当前页面仅保留设计预览。”;应用分享使用“应用分享与邀请奖励服务正在开发中,当前页面仅保留设计预览。”;帮助与服务使用“会员与在线服务正在开发中,当前页面仅保留设计预览。” + +- [x] **Step 2: 加载统一状态脚本并放行动态入口** + +每页都在 `utils/ApiClient.js` 后增加: + +```html + + +``` + +在 `profile-family-home.html` 中将动态链接改为: + +```html +发布动态 +``` + +- [x] **Step 3: 运行测试确认通过** + +Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; node --test tests/pending-pages.test.js` + +Expected: 全部状态页面和真实账户页面断言通过。 + +### Task 3: 更新进度与验证 + +**Files:** +- Modify: `docs/规划.md` +- Modify: `docs/superpowers/plans/2026-07-11-residual-pc-entry-closure.md` +- Test: `tests/*.test.js` + +**Interfaces:** +- Consumes: 全部 HTML 的本地脚本引用。 +- Produces: 已完成第三轮进度和可重复验证记录。 + +- [x] **Step 1: 写入第三轮完成状态** + +在 `docs/规划.md` 的“实施进度”中增加已完成第三轮,说明三类剩余 PC 业务入口已收口,家族圈动态未受影响。 + +- [x] **Step 2: 执行完整测试** + +Run: `$env:Path = 'C:\\Program Files\\nodejs;' + $env:Path; npm.cmd test` + +Expected: 所有 Node 测试通过。 + +- [x] **Step 3: 执行静态检查** + +Run: PowerShell 扫描全部 HTML 的 `src="*.js"`,随后运行 `git diff --check`。 + +Expected: 每个本地脚本文件存在,且没有空白格式错误。 diff --git a/docs/superpowers/specs/2026-07-10-registration-token-state-design.md b/docs/superpowers/specs/2026-07-10-registration-token-state-design.md deleted file mode 100644 index ebf0357..0000000 --- a/docs/superpowers/specs/2026-07-10-registration-token-state-design.md +++ /dev/null @@ -1,29 +0,0 @@ -# Registration Token State Design - -## Status - -Pending written-spec review. - -## Goal - -Registration creates an account but never creates or retains a browser login state. Only a successful password login or SMS login may persist an authentication token. - -## Contract Owner - -`utils/ApiClient.js` owns browser token persistence. Pages invoke API methods and redirect according to their existing form configuration; they do not read, write, or clear the token directly. - -## Required Behavior - -1. `login()` and `loginBySms()` persist the token returned by their successful responses under `genealogy_auth_token`. -2. `register()` posts the existing registration request unchanged and returns its response unchanged. -3. After a successful `register()` response, `register()` clears `genealogy_auth_token`. This applies even when the response includes `token`, `accessToken`, or `tokenValue`. -4. If `register()` fails, token storage is not changed. -5. `register.html` continues redirecting to `login.html` after `register()` resolves. - -## Scope - -This change does not add a profile-page login guard, 401 redirect handling, or logout UI. Those are separate authentication-lifecycle steps that must consume the same `ApiClient` token contract. - -## Verification - -The API-client tests must prove that successful registration removes a previously stored token and does not store a token returned by registration. They must also retain coverage showing password and SMS login still store their returned tokens. diff --git a/docs/superpowers/specs/2026-07-11-auth-to-profile-flow-design.md b/docs/superpowers/specs/2026-07-11-auth-to-profile-flow-design.md deleted file mode 100644 index 1e74f97..0000000 --- a/docs/superpowers/specs/2026-07-11-auth-to-profile-flow-design.md +++ /dev/null @@ -1,47 +0,0 @@ -# Auth To Profile Flow Design - -## Status - -Approved for implementation. - -## Goal - -Complete the PC journey from registration through login and personal-profile retrieval, with one token owner, deterministic redirects, and a single recorded API base address. - -## Endpoint Configuration - -`config.js` is the sole runtime owner of the API base address. Both development and production currently use `http://182.61.18.23:8080`. `tests/config.test.js` must assert that value. The PC integration tracker must record the change on 2026-07-11 and label prior test-domain references as historical rather than current configuration. - -## Token Contract - -`utils/ApiClient.js` owns token persistence under `genealogy_auth_token`. - -1. Successful password login and SMS login must receive a nonempty response token, persist it, and then allow navigation to `profile.html`. The current backend response uses `data.access_token`; the client also accepts `token`, `accessToken`, and `tokenValue` for declared compatibility. -2. A login response without a token is an authentication failure. The login page remains in place and no profile navigation occurs. -3. Successful registration removes any existing token and navigates to `login.html`. Registration never persists a response token. -4. A failed registration leaves existing token storage unchanged. -5. `logout()` clears token storage in a `finally` path, so a failed or expired logout request cannot leave a local session behind. - -## Page Flow - -1. Registration validates fields, obtains `WEB_H5_REGISTER` captcha proof, posts `/genealogy/pc/auth/register`, clears token through `ApiClient.register()`, and redirects to `login.html`. -2. Password and SMS login validate fields, obtain `WEB_H5_LOGIN` captcha proof, post their respective login endpoint, persist the returned token through `ApiClient`, and redirect to `profile.html`. -3. `profile.html` and `profile-data.html` check for a token before requesting `/genealogy/pc/auth/profile`. Without one, they redirect to `index.html` without making the profile request. -4. A profile-read or profile-update error with HTTP status `401` or business code `401` clears token storage and redirects to `index.html`. Other errors remain on the current page and show the existing error message. -5. Confirmed logout calls `/genealogy/pc/auth/logout`, clears token regardless of the request outcome, and redirects to `index.html`. - -## Ownership - -- `config.js`: API base address. -- `utils/ApiClient.js`: login-token validation, persistence, clearing, and logout cleanup. -- `public/js/profile-pages.js`: profile-page token guard and unauthorized redirect. -- `public/js/profile-common.js`: confirmed logout action and redirect. -- `docs/pc-api-page-integration-tracker-2026-07-09.md`: current address record and end-to-end authentication status. - -## Verification - -Tests must prove the configured address, registration state, both login token paths, missing-token login rejection, logout cleanup after both success and failure, and profile unauthorized classification. Focused configuration, API-client, profile-page, and authentication tests must pass before the full Node test suite is run. - -## Scope - -This flow covers registration, password login, SMS login, profile retrieval and update on the two pages that load `/genealogy/pc/auth/profile`, and the existing profile logout action. Password reset, phone change, account deactivation, and profile pages without profile API loading remain outside this change. diff --git a/docs/规划.md b/docs/规划.md new file mode 100644 index 0000000..1479825 --- /dev/null +++ b/docs/规划.md @@ -0,0 +1,99 @@ +# 规划 + +## 项目目标 + +将“代代相传”建设为由官网、用户 PC 管理端、用户 APP 端和平台后台组成的家谱服务。四个端共享家谱、成员、内容和权限规则,但分别交付,不能用用户 PC 页面替代 APP 或平台后台。 + +## 当前基线 + +- 当前工作区共有 55 个 HTML 页面,现有页面之间没有指向不存在 HTML 页的站内链接。 +- `PC.openapi2.json` 是当前后端正式接口源,包含 37 个操作:认证登录 11、验证中心 4、文件上传 6、行政区划 4、家族圈 12。 +- 已完成真实接口闭环:注册、密码登录、短信登录、找回密码、个人资料、安全设置、头像上传、地区选择、家族圈动态。 +- 家谱列表、家谱创建/加入、成员、权限、世系、谱文、相册、视频、贺礼、成长、备忘、功德、消息、反馈等页面已保留设计,但尚无对应正式接口,不得伪造请求或静态数据为真实业务。 + +## 实施进度 + +- [x] 第一轮:家谱基础页面状态收口。已覆盖家谱列表、创建与加入、审核、家族管理、邀请、世系和字辈页面;未开发功能会显示统一提示并阻止伪造操作。 +- [x] 第二轮:内容与协作页面状态收口。已覆盖除家族圈动态外的内容、记录、消息、反馈、管理员权限与资料提醒页面;家族圈动态保持真实功能并保留跳转入口。 +- [x] 第三轮:剩余用户 PC 业务入口收口。已覆盖家谱主页、应用分享和帮助与会员服务页面;家族圈动态入口保持真实跳转。 +- [x] 第四轮:官网静态页面补齐。已新增新闻、法律文档入口、通用状态和工单页面;在线工单功能明确处于开发中,法律正文必须在发布前由法务替换确认。 + +## 产品边界 + +### 官网 + +保留首页、数字家谱、家谱广场、姓氏百科、家族文化、应用下载、帮助中心、关于我们、搜索、公告和工单入口。 + +需要补齐的独立页面: + +1. 新闻列表、新闻分类、新闻详情。 +2. 隐私政策、儿童个人信息处理规则、服务协议。 +3. 我的工单、工单详情。 +4. 404、无权限、系统维护等通用状态页。 + +### 用户 PC 管理端 + +现有页面覆盖了思维导图中的大部分视觉入口:我的家谱、家谱主页、世系图、字辈、谱文、相册、视频、家族圈、贺礼、成长日志、备忘录、功德录、家族管理、管理员权限、入谱审核、消息、个人资料和安全设置。 + +需要补齐的关键页面: + +1. 家族成员名册与成员详情,用于按成员筛选、查看资料、维护角色;世系图不能替代成员名册。 +2. 家族圈动态详情,用于承载可分享链接、完整评论和通知跳转。 +3. 无家谱首次引导、无权限、空列表等状态视图。 + +### 用户 APP 端 + +APP 是独立交付物,负责移动端查看家谱、上传照片/视频、发布内容、消息和个人中心。当前 PC 项目只需共享接口契约和视觉规范,不直接复制为 APP 页面。 + +### 平台后台 + +平台后台是独立项目,负责全站家谱审核、内容治理、会员/VIP、系统设置和运营数据。它不应通过隐藏按钮混入用户 PC 端。 + +## 后端接口开发顺序 + +### 第一阶段:家谱基础 + +1. 我的家谱列表、家谱详情、创建家谱、加入家谱、加入审核。 +2. 家谱成员列表、成员详情、邀请、移除、角色与管理员权限。 +3. 家谱入口必须返回真实 `genealogyId`,让世系、内容和家族圈能从同一上下文进入。 + +### 第二阶段:世系与资料 + +1. 世系树查询。 +2. 成员新增、编辑、删除。 +3. 父母、配偶、子女等关系维护。 +4. 字辈列表与维护。 + +### 第三阶段:内容与协作 + +1. 谱文及分类。 +2. 相册、照片、视频。 +3. 贺礼、成长日志、备忘录、功德录。 +4. 消息中心、点赞评论通知、生日提醒、入谱审核通知。 +5. 意见反馈、工单列表与详情。 + +### 第四阶段:官网内容与合规 + +1. 新闻资讯、公告、公开内容检索。 +2. 隐私、未成年人保护和服务协议。 +3. 客服工单的提交、查询和回复。 + +## 契约要求 + +每个业务对象都必须提供明确 DTO,至少包括列表、详情和写入请求模型。禁止前端依赖泛型 `object` 或猜测字段名。 + +最优先需要后端补充的模型: + +1. `GenealogyDTO`、`GenealogyMemberDTO`、`MemberRoleDTO`。 +2. `LineagePersonDTO`、`LineageRelationDTO`、`GenerationDTO`。 +3. `FeedDTO`、`CommentDTO`。 +4. `ArticleDTO`、`AlbumDTO`、`VideoDTO`、`CeremonyDTO`、`GrowthLogDTO`、`MemoDTO`、`MeritDTO`。 +5. `NotificationDTO`、`TicketDTO`、`NewsDTO`。 + +## 交付规则 + +1. 有正式接口和 DTO 的页面,才接入真实读写功能。 +2. 未开发接口对应的页面保留设计,但明确显示“功能开发中”或空状态,不伪造成功数据。 +3. 家谱业务全部从真实 `genealogyId` 进入,不写死示例家谱编号。 +4. 访客、普通成员、家谱管理员、平台管理员必须有独立权限边界。 +5. 测试账号仅用于运行时验收;账号和密码不写入仓库、文档、测试、日志或配置。 diff --git a/download.html b/download.html index 478c50f..e28cb4e 100644 --- a/download.html +++ b/download.html @@ -101,7 +101,6 @@ - diff --git a/family-detail.html b/family-detail.html index 04d1990..3fde3b3 100644 --- a/family-detail.html +++ b/family-detail.html @@ -136,7 +136,6 @@ - diff --git a/forbidden.html b/forbidden.html new file mode 100644 index 0000000..2897362 --- /dev/null +++ b/forbidden.html @@ -0,0 +1,53 @@ + + + + + + 无访问权限 - 代代相传 + + + + + +
+
+
+
+
!
+

无访问权限

+

+ 当前账号没有访问此内容的权限。请确认登录账号,或联系家谱管理员处理。 +

+ +
+
+
+
+
+
+ +
+
+ + + diff --git a/genealogy-pc-openapi.yaml b/genealogy-pc-openapi.yaml deleted file mode 100644 index 3c98b57..0000000 --- a/genealogy-pc-openapi.yaml +++ /dev/null @@ -1,1483 +0,0 @@ -openapi: 3.0.3 -info: - title: Genealogy PC/H5 API - version: 1.0.0 - description: | - 家谱业务 PC/H5 用户侧接口文档,用于 Apifox 导入。 - - 导入方式:Apifox -> 导入 API 数据 -> OpenAPI/Swagger -> 选择本文件。 - - 约定: - - PC/H5 路径前缀为 `/genealogy/pc` - - 后台管理接口不包含在本文件内 - - 登录后接口需要携带 token,且请求 Header 需携带 `clientid` -servers: - - url: http://182.61.18.23:8080 - description: Local Java backend - - url: http://182.61.18.23:8080/dev-api - description: Local frontend proxy -tags: - - name: 验证中心 - description: 统一验证码、天爱行为验证码和系统图形验证码 - - name: 认证登录 - description: PC/H5 登录注册与账号资料 - - name: 文件上传 - description: PC/H5 统一文件上传与业务引用绑定 - - name: 行政区划 - description: 行政区划 - - name: 家谱 - description: 家谱主体、加入申请与概览 - - name: 家谱成员 - description: 家谱成员 - - name: 字辈谱 - description: 字辈谱 - - name: 世系人物 - description: 世系人物与世系树 - - name: 家族圈 - description: 家族圈动态、点赞、评论 - - name: 内容文章 - description: 谱文、官网文章、帮助、推广 - - name: 相册 - description: 相册与照片 - - name: 祭祀 - description: 祭祀与献礼 - - name: 族务记录 - description: 成长、备忘、亲友、功德等记录 - - name: VIP - description: VIP 套餐与订单 - - name: 消息通知 - description: 消息通知 - - name: 意见反馈 - description: 意见反馈 -security: - - SaToken: [] -paths: - /captcha/require: - get: - tags: [验证中心] - summary: 查询当前场景是否需要验证 - description: 用于登录、注册、换绑手机等场景。按租户、客户端和场景编码匹配后台验证策略。 - security: [] - parameters: - - name: tenantId - description: 租户ID,用于区分不同租户的数据和验证策略 - in: query - schema: - type: string - example: "000000" - - name: clientId - description: 客户端ID,对应后台 PC/H5 客户端配置 - in: query - schema: - type: string - example: 428a8310cd442757ae699df5d894f051 - - name: sceneCode - description: 验证场景编码,例如WEB_H5_LOGIN、WEB_H5_REGISTER - in: query - required: true - schema: - type: string - example: WEB_H5_LOGIN - - name: subject - description: 验证主体,通常为手机号、用户ID或登录账号 - in: query - schema: - type: string - example: "13800000000" - responses: - '200': - $ref: '#/components/responses/VerificationRequireResult' - /captcha/challenge: - post: - tags: [验证中心] - summary: 生成验证挑战 - description: 统一生成验证码。策略为天爱时返回行为验证数据;策略为系统图形时返回 uuid 和 img。 - security: [] - requestBody: - $ref: '#/components/requestBodies/VerificationChallenge' - responses: - '200': - $ref: '#/components/responses/VerificationChallengeResult' - /captcha/verify: - post: - tags: [验证中心] - summary: 校验验证结果并换取 validToken - description: 验证通过后返回 validToken,登录、注册、发短信等业务接口将 validToken 随请求体一起提交。 - security: [] - requestBody: - $ref: '#/components/requestBodies/VerificationCheck' - responses: - '200': - $ref: '#/components/responses/VerificationCheckResult' - /auth/code: - get: - tags: [验证中心] - summary: 兼容旧系统图形验证码 - description: 老版后台登录图形验证码入口。新的 PC/H5 业务优先使用 /captcha/require + /captcha/challenge + /captcha/verify。 - security: [] - responses: - '200': - $ref: '#/components/responses/LegacyCaptchaResult' - /genealogy/pc/auth/register: - post: - tags: [认证登录] - summary: PC 用户注册 - security: [] - parameters: - - $ref: '#/components/parameters/ClientIdHeader' - requestBody: - $ref: '#/components/requestBodies/PasswordRegister' - responses: - '200': - $ref: '#/components/responses/LoginResult' - /genealogy/pc/auth/login: - post: - tags: [认证登录] - summary: PC 密码登录 - security: [] - parameters: - - $ref: '#/components/parameters/ClientIdHeader' - requestBody: - $ref: '#/components/requestBodies/PasswordLogin' - responses: - '200': - $ref: '#/components/responses/LoginResult' - /genealogy/pc/auth/login/sms: - post: - tags: [认证登录] - summary: PC 短信登录 - security: [] - parameters: - - $ref: '#/components/parameters/ClientIdHeader' - requestBody: - $ref: '#/components/requestBodies/SmsLogin' - responses: - '200': - $ref: '#/components/responses/LoginResult' - /genealogy/pc/auth/sms/code: - post: - tags: [认证登录] - summary: PC 发送短信验证码 - security: [] - parameters: - - $ref: '#/components/parameters/ClientIdHeader' - requestBody: - $ref: '#/components/requestBodies/SmsCode' - responses: - '200': - $ref: '#/components/responses/VoidResult' - /genealogy/pc/auth/profile: - get: - tags: [认证登录] - summary: PC 当前用户资料 - parameters: - - $ref: '#/components/parameters/ClientIdHeader' - responses: - '200': - $ref: '#/components/responses/ObjectResult' - put: - tags: [认证登录] - summary: PC 修改用户资料 - parameters: - - $ref: '#/components/parameters/ClientIdHeader' - requestBody: - $ref: '#/components/requestBodies/ProfileUpdate' - responses: - '200': - $ref: '#/components/responses/ObjectResult' - /genealogy/pc/auth/password: - put: - tags: [认证登录] - summary: PC 修改密码 - parameters: - - $ref: '#/components/parameters/ClientIdHeader' - requestBody: - $ref: '#/components/requestBodies/PasswordChange' - responses: - '200': - $ref: '#/components/responses/VoidResult' - /genealogy/pc/auth/password/reset: - put: - tags: [认证登录] - summary: PC 找回密码 - security: [] - parameters: - - $ref: '#/components/parameters/ClientIdHeader' - requestBody: - $ref: '#/components/requestBodies/PasswordReset' - responses: - '200': - $ref: '#/components/responses/VoidResult' - /genealogy/pc/auth/phone: - put: - tags: [认证登录] - summary: PC 换绑手机号 - parameters: - - $ref: '#/components/parameters/ClientIdHeader' - requestBody: - $ref: '#/components/requestBodies/PhoneChange' - responses: - '200': - $ref: '#/components/responses/ObjectResult' - /genealogy/pc/auth/account/deactivate: - post: - tags: [认证登录] - summary: PC 注销账号 - parameters: - - $ref: '#/components/parameters/ClientIdHeader' - requestBody: - $ref: '#/components/requestBodies/AccountDeactivate' - responses: - '200': - $ref: '#/components/responses/VoidResult' - /genealogy/pc/auth/logout: - delete: - tags: [认证登录] - summary: PC 退出登录 - parameters: - - $ref: '#/components/parameters/ClientIdHeader' - responses: - '200': - $ref: '#/components/responses/VoidResult' - - /genealogy/pc/files/upload: - post: - tags: [文件上传] - summary: PC 单文件上传 - parameters: - - $ref: '#/components/parameters/ClientIdHeader' - requestBody: - $ref: '#/components/requestBodies/FileUpload' - responses: - '200': - $ref: '#/components/responses/FileUploadResult' - /genealogy/pc/files/resumable/init: - post: - tags: [文件上传] - summary: PC 分片上传初始化 - parameters: - - $ref: '#/components/parameters/ClientIdHeader' - requestBody: - $ref: '#/components/requestBodies/ResumableInit' - responses: - '200': - $ref: '#/components/responses/ObjectResult' - /genealogy/pc/files/resumable/chunk: - post: - tags: [文件上传] - summary: PC 上传分片 - parameters: - - $ref: '#/components/parameters/ClientIdHeader' - requestBody: - $ref: '#/components/requestBodies/ChunkUpload' - responses: - '200': - $ref: '#/components/responses/VoidResult' - /genealogy/pc/files/resumable/complete: - post: - tags: [文件上传] - summary: PC 完成分片上传 - parameters: - - $ref: '#/components/parameters/ClientIdHeader' - requestBody: - $ref: '#/components/requestBodies/ResumableComplete' - responses: - '200': - $ref: '#/components/responses/FileUploadResult' - /genealogy/pc/files/reference: - post: - tags: [文件上传] - summary: PC 绑定文件业务引用 - parameters: - - $ref: '#/components/parameters/ClientIdHeader' - requestBody: - $ref: '#/components/requestBodies/FileReference' - responses: - '200': - $ref: '#/components/responses/VoidResult' - delete: - tags: [文件上传] - summary: PC 释放文件业务引用 - parameters: - - $ref: '#/components/parameters/ClientIdHeader' - - $ref: '#/components/parameters/BizTable' - - $ref: '#/components/parameters/BizId' - - $ref: '#/components/parameters/BizField' - responses: - '200': - $ref: '#/components/responses/VoidResult' - - /genealogy/region/children: - get: - tags: [行政区划] - summary: 查询下级行政区划 - security: [] - parameters: - - name: parentCode - description: 父级行政区划编码,不传或传0查省级 - in: query - required: false - schema: - type: string - example: "51" - responses: - '200': - $ref: '#/components/responses/ListResult' - /genealogy/region/path/{regionCode}: - get: - tags: [行政区划] - summary: 查询行政区划路径 - security: [] - parameters: - - $ref: '#/components/parameters/RegionCode' - responses: - '200': - $ref: '#/components/responses/ListResult' - /genealogy/region/search: - get: - tags: [行政区划] - summary: 搜索行政区划 - security: [] - parameters: - - name: keyword - description: 搜索关键词,支持地区名称或编码模糊查询 - in: query - required: true - schema: - type: string - example: 北京 - - name: level - description: 行政区划级别,1省、2市、3区县、4乡镇街道、5村社区 - in: query - required: false - schema: - type: integer - example: 3 - - name: limit - description: 返回数量上限 - in: query - required: false - schema: - type: integer - example: 20 - responses: - '200': - $ref: '#/components/responses/ListResult' - /genealogy/region/{regionCode}: - get: - tags: [行政区划] - summary: 查询行政区划详情 - security: [] - parameters: - - $ref: '#/components/parameters/RegionCode' - responses: - '200': - $ref: '#/components/responses/ObjectResult' - -components: - securitySchemes: - SaToken: - type: apiKey - in: header - name: Authorization - description: 登录后返回的 token。若本地配置使用 sa-token 名称,请按项目实际 token-name 调整。 - parameters: - ClientIdHeader: - name: clientid - description: 客户端ID,请求 Header 携带,用于识别 PC/H5 终端 - in: header - required: true - schema: - type: string - example: 428a8310cd442757ae699df5d894f051 - PageNum: - name: pageNum - description: 页码,从1开始 - in: query - required: false - schema: - type: integer - default: 1 - PageSize: - name: pageSize - description: 每页条数 - in: query - required: false - schema: - type: integer - default: 10 - GenealogyId: - name: genealogyId - description: 家谱ID - in: path - required: true - schema: - type: integer - format: int64 - example: 900001001 - MemberId: - name: memberId - description: 家谱成员ID - in: path - required: true - schema: - type: integer - format: int64 - PersonId: - name: personId - description: 世系人物ID - in: path - required: true - schema: - type: integer - format: int64 - FeedId: - name: feedId - description: 家族动态ID - in: path - required: true - schema: - type: integer - format: int64 - RegionCode: - name: regionCode - description: 行政区划编码 - in: path - required: true - schema: - type: string - example: "510000" - BizTable: - name: bizTable - description: 业务表名,用于文件引用绑定 - in: query - required: true - schema: - type: string - example: gen_family_feed - BizId: - name: bizId - description: 业务数据ID,用于文件引用绑定 - in: query - required: true - schema: - type: integer - format: int64 - example: 900013001 - BizField: - name: bizField - description: 业务字段名,例如avatar_oss_id、cover_oss_id - in: query - required: true - schema: - type: string - example: media_ids - requestBodies: - JsonObject: - required: true - content: - application/json: - schema: - type: object - additionalProperties: true - PasswordRegister: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PasswordRegisterBody' - example: - grantType: "password" - tenantId: "000000" - phone: "13800000000" - password: "e10adc3949ba59abbe56e057f20f883e" - nickName: "测试用户" - registerSource: "PC" - validToken: "captcha-ticket-or-sms-token" - PasswordLogin: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PasswordLoginBody' - example: - grantType: "password" - tenantId: "000000" - phone: "13800000000" - password: "e10adc3949ba59abbe56e057f20f883e" - validToken: "captcha-ticket" - SmsLogin: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/SmsLoginBody' - example: - grantType: "sms" - tenantId: "000000" - phone: "13800000000" - smsCode: "123456" - validToken: "captcha-ticket" - SmsCode: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/SmsCodeBody' - example: - tenantId: "000000" - phone: "13800000000" - sceneCode: "WEB_H5_LOGIN" - validToken: "captcha-ticket" - ProfileUpdate: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ProfileUpdateBody' - example: - nickName: "张三" - avatarOssId: 2060000000000000001 - sex: "0" - birthday: "1990-01-01" - regionCode: "110101" - addressDetail: "北京市东城区" - PasswordChange: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PasswordChangeBody' - example: - oldPassword: "e10adc3949ba59abbe56e057f20f883e" - newPassword: "25d55ad283aa400af464c76d713c07ad" - validToken: "captcha-ticket" - PasswordReset: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PasswordResetBody' - example: - tenantId: "000000" - phone: "13800000000" - smsCode: "123456" - newPassword: "25d55ad283aa400af464c76d713c07ad" - validToken: "captcha-ticket" - PhoneChange: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PhoneChangeBody' - example: - newPhone: "13900000000" - smsCode: "123456" - validToken: "captcha-ticket" - AccountDeactivate: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AccountDeactivateBody' - example: - password: "e10adc3949ba59abbe56e057f20f883e" - reason: "user request" - FileUpload: - required: true - content: - multipart/form-data: - schema: - type: object - required: [file] - properties: - file: - type: string - format: binary - ChunkUpload: - required: true - content: - multipart/form-data: - schema: - type: object - required: [uploadId, chunkIndex, chunkMd5, file] - properties: - uploadId: - type: string - chunkIndex: - type: integer - example: 0 - chunkMd5: - type: string - example: d41d8cd98f00b204e9800998ecf8427e - file: - type: string - format: binary - ResumableInit: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ResumableInitBody' - example: - fileName: "cover.jpg" - fileSize: 9532 - fileMd5: "d41d8cd98f00b204e9800998ecf8427e" - contentType: "image/jpeg" - chunkSize: 4194304 - totalChunks: 1 - bizType: "genealogy" - usageScene: "cover" - ResumableComplete: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ResumableCompleteBody' - example: - uploadId: "UPLOAD202607090001" - fileMd5: "d41d8cd98f00b204e9800998ecf8427e" - fileSize: 9532 - FileReference: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/FileReferenceBody' - example: - bizType: "family_feed" - bizName: "family feed image" - bizTable: "gen_family_feed" - bizId: 900013001 - bizField: "media_oss_ids" - ossId: 2060000000000000001 - usageScene: "feed_image" - GenerationPoemBatch: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/GenerationPoemBatchBody' - example: - genealogyId: 900001001 - poemText: "德承家亦\n忠厚传芳" - disableMissing: false - LineagePerson: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/LineagePersonBody' - example: - appUserId: 900000001 - personNo: "P202607090001" - personName: "彭德明" - aliasName: "德明" - sex: "0" - generationNo: 1 - generationName: "德" - avatarOssId: 2060000000000000001 - birthDate: "1950-01-01" - deathDate: null - introduction: "first generation introduction" - FamilyFeed: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/FamilyFeedBody' - example: - content: "今天上传一张老照片。" - mediaOssIds: "2060000000000000001,2060000000000000002" - visibility: "1" - status: "0" - FamilyFeedComment: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/FamilyFeedCommentBody' - example: - parentCommentId: null - replyUserId: null - content: "这张照片很有年代感。" - VerificationChallenge: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/VerificationChallengeBody' - example: - tenantId: "000000" - clientId: "428a8310cd442757ae699df5d894f051" - sceneCode: "WEB_H5_LOGIN" - subject: "13800000000" - VerificationCheck: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/VerificationCheckBody' - examples: - tianai_behavior: - summary: 天爱滑块/旋转/滑动还原/文字点选验证 - value: - tenantId: "000000" - clientId: "428a8310cd442757ae699df5d894f051" - sceneCode: "WEB_H5_LOGIN" - subject: "13800000000" - challengeId: "CAPTCHA_CHALLENGE_ID" - providerCode: "tianai" - captchaType: "SLIDER" - payload: - id: "tianai-captcha-id" - data: - bgImageWidth: 340 - bgImageHeight: 180 - startSlidingTime: 1720000000000 - endSlidingTime: 1720000001500 - trackList: [] - system_image: - summary: 系统图形验证码 - value: - tenantId: "000000" - clientId: "428a8310cd442757ae699df5d894f051" - sceneCode: "WEB_H5_LOGIN" - subject: "13800000000" - challengeId: "CAPTCHA_CHALLENGE_ID" - providerCode: "ruoyi_image" - captchaType: "math" - payload: - uuid: "captcha-uuid" - code: "6" - responses: - VoidResult: - description: 通用成功响应 - content: - application/json: - schema: - $ref: '#/components/schemas/RVoid' - ObjectResult: - description: 通用对象响应 - content: - application/json: - schema: - $ref: '#/components/schemas/RObject' - ListResult: - description: 通用列表响应 - content: - application/json: - schema: - $ref: '#/components/schemas/RList' - PageResult: - description: 通用分页响应 - content: - application/json: - schema: - $ref: '#/components/schemas/PageResult' - LoginResult: - description: 登录结果 - content: - application/json: - schema: - $ref: '#/components/schemas/RLogin' - FileUploadResult: - description: 文件上传结果 - content: - application/json: - schema: - $ref: '#/components/schemas/RFileUpload' - VerificationRequireResult: - description: 验证需求查询结果 - content: - application/json: - schema: - $ref: '#/components/schemas/RVerificationRequire' - VerificationChallengeResult: - description: 验证挑战生成结果 - content: - application/json: - schema: - $ref: '#/components/schemas/RVerificationChallenge' - VerificationCheckResult: - description: 验证校验结果 - content: - application/json: - schema: - $ref: '#/components/schemas/RVerificationCheck' - LegacyCaptchaResult: - description: 兼容旧图形验证码结果 - content: - application/json: - schema: - $ref: '#/components/schemas/RLegacyCaptcha' - schemas: - RVoid: - type: object - properties: - code: - description: 系统图形验证码或响应状态码 - type: integer - example: 200 - msg: - description: 响应消息 - type: string - example: 操作成功 - data: - description: 响应数据主体 - nullable: true - RObject: - type: object - properties: - code: - description: 系统图形验证码或响应状态码 - type: integer - example: 200 - msg: - description: 响应消息 - type: string - example: 操作成功 - data: - description: 响应数据主体 - type: object - additionalProperties: true - RList: - type: object - properties: - code: - description: 系统图形验证码或响应状态码 - type: integer - example: 200 - msg: - description: 响应消息 - type: string - example: 操作成功 - data: - description: 响应数据主体 - type: array - items: - type: object - additionalProperties: true - PageResult: - type: object - properties: - code: - description: 系统图形验证码或响应状态码 - type: integer - example: 200 - msg: - description: 响应消息 - type: string - example: 操作成功 - rows: - description: 分页数据列表 - type: array - items: - type: object - additionalProperties: true - total: - type: integer - example: 0 - RLogin: - type: object - properties: - code: - description: 系统图形验证码或响应状态码 - type: integer - example: 200 - msg: - description: 响应消息 - type: string - example: 操作成功 - data: - description: 响应数据主体 - $ref: '#/components/schemas/LoginVo' - RFileUpload: - type: object - properties: - code: - description: 系统图形验证码或响应状态码 - type: integer - example: 200 - msg: - description: 响应消息 - type: string - example: 操作成功 - data: - description: 响应数据主体 - $ref: '#/components/schemas/FileUploadVo' - LoginVo: - type: object - properties: - token: - type: string - accessToken: - type: string - tokenValue: - type: string - userId: - type: integer - format: int64 - tenantId: - description: 租户ID,用于区分不同租户的数据和验证策略 - type: string - clientId: - description: 客户端ID,对应后台 PC/H5 客户端配置 - type: string - clientKey: - type: string - FileUploadVo: - type: object - properties: - ossId: - description: OSS文件ID,表单内部使用,用户侧应通过上传组件获取 - type: integer - format: int64 - url: - type: string - thumbnailUrl: - type: string - nullable: true - fileName: - description: 原始文件名 - type: string - originalName: - type: string - PasswordRegisterBody: - type: object - required: [grantType, tenantId, phone, password] - properties: - clientId: - description: 客户端ID,对应后台 PC/H5 客户端配置 - type: string - grantType: - description: 授权类型,例如password、sms、wechat、apple - type: string - example: password - tenantId: - description: 租户ID,用于区分不同租户的数据和验证策略 - type: string - example: "000000" - phone: - description: 手机号 - type: string - example: "13800000000" - password: - type: string - description: 32 位 MD5 - example: e10adc3949ba59abbe56e057f20f883e - nickName: - description: 用户昵称 - type: string - registerSource: - description: 注册来源,例如PC、H5 - type: string - validToken: - description: 验证中心通过后返回的票据,用于注册/登录/换绑等二次校验 - type: string - PasswordLoginBody: - type: object - required: [grantType, tenantId, phone, password] - properties: - clientId: - description: 客户端ID,对应后台 PC/H5 客户端配置 - type: string - grantType: - description: 授权类型,例如password、sms、wechat、apple - type: string - example: password - tenantId: - description: 租户ID,用于区分不同租户的数据和验证策略 - type: string - example: "000000" - phone: - description: 手机号 - type: string - example: "13800000000" - password: - type: string - description: 32 位 MD5 - example: e10adc3949ba59abbe56e057f20f883e - validToken: - description: 验证中心通过后返回的票据,用于注册/登录/换绑等二次校验 - type: string - SmsLoginBody: - type: object - required: [grantType, tenantId, phone, smsCode] - properties: - clientId: - description: 客户端ID,对应后台 PC/H5 客户端配置 - type: string - grantType: - description: 授权类型,例如password、sms、wechat、apple - type: string - example: sms - tenantId: - description: 租户ID,用于区分不同租户的数据和验证策略 - type: string - example: "000000" - phone: - description: 手机号 - type: string - example: "13800000000" - smsCode: - description: 短信验证码 - type: string - example: "1234" - SmsCodeBody: - type: object - required: [grantType, tenantId, sceneCode, phone, validToken] - properties: - clientId: - description: 客户端ID,对应后台 PC/H5 客户端配置 - type: string - grantType: - description: 授权类型,例如password、sms、wechat、apple - type: string - example: sms - tenantId: - description: 租户ID,用于区分不同租户的数据和验证策略 - type: string - example: "000000" - sceneCode: - description: 验证场景编码,例如WEB_H5_LOGIN、WEB_H5_REGISTER - type: string - example: WEB_H5_LOGIN - phone: - description: 手机号 - type: string - example: "13800000000" - validToken: - description: 验证中心通过后返回的票据,用于注册/登录/换绑等二次校验 - type: string - ProfileUpdateBody: - type: object - properties: - nickName: - description: 用户昵称 - type: string - avatarOssId: - description: 头像文件OSS ID - type: integer - format: int64 - sex: - description: 性别,建议使用系统字典值 - type: string - birthday: - description: 生日,格式 yyyy-MM-dd - type: string - format: date - provinceCode: - type: string - cityCode: - type: string - districtCode: - type: string - PasswordChangeBody: - type: object - required: [oldPassword, newPassword] - properties: - oldPassword: - type: string - description: 32 位 MD5 - newPassword: - type: string - description: 32 位 MD5 - PasswordResetBody: - type: object - required: [tenantId, phone, smsCode, newPassword] - properties: - tenantId: - description: 租户ID,用于区分不同租户的数据和验证策略 - type: string - example: "000000" - phone: - description: 手机号 - type: string - smsCode: - description: 短信验证码 - type: string - newPassword: - type: string - description: 32 位 MD5 - validToken: - description: 验证中心通过后返回的票据,用于注册/登录/换绑等二次校验 - type: string - PhoneChangeBody: - type: object - required: [newPhone, smsCode] - properties: - newPhone: - type: string - smsCode: - description: 短信验证码 - type: string - validToken: - description: 验证中心通过后返回的票据,用于注册/登录/换绑等二次校验 - type: string - AccountDeactivateBody: - type: object - required: [password] - properties: - password: - type: string - description: 32 位 MD5 - reason: - description: 操作原因或说明 - type: string - ResumableInitBody: - type: object - required: [fileName, fileSize, fileMd5, chunkSize, totalChunks] - properties: - fileName: - description: 原始文件名 - type: string - fileSize: - description: 文件大小,单位字节 - type: integer - format: int64 - fileMd5: - description: 完整文件MD5,用于秒传和完整性校验 - type: string - contentType: - description: 文件MIME类型,例如 image/jpeg、video/mp4 - type: string - chunkSize: - description: 单个分片大小,单位字节 - type: integer - example: 4194304 - totalChunks: - description: 总分片数 - type: integer - bizType: - description: 业务类型,例如avatar、cover、feed_image - type: string - usageScene: - description: 文件使用场景,便于后续引用统计和清理 - type: string - ResumableCompleteBody: - type: object - required: [uploadId, fileMd5, fileSize] - properties: - uploadId: - description: 分片上传任务ID - type: string - fileMd5: - description: 完整文件MD5,用于秒传和完整性校验 - type: string - fileSize: - description: 文件大小,单位字节 - type: integer - format: int64 - FileReferenceBody: - type: object - required: [bizType, bizTable, bizId, bizField] - properties: - bizType: - description: 业务类型,例如avatar、cover、feed_image - type: string - example: family_feed - bizName: - description: 业务名称,用于后台展示文件引用来源 - type: string - example: 家族圈动态 - bizTable: - description: 业务表名,用于文件引用绑定 - type: string - example: gen_family_feed - bizId: - description: 业务数据ID,用于文件引用绑定 - type: integer - format: int64 - bizField: - description: 业务字段名,例如avatar_oss_id、cover_oss_id - type: string - example: media_ids - ossId: - description: OSS文件ID,表单内部使用,用户侧应通过上传组件获取 - type: integer - format: int64 - ossIds: - type: string - example: "2060001,2060002" - usageScene: - description: 文件使用场景,便于后续引用统计和清理 - type: string - usageName: - type: string - GenerationPoemBatchBody: - type: object - properties: - content: - type: string - example: 德承家亦 - stopMissingOldGeneration: - type: boolean - example: false - LineagePersonBody: - type: object - properties: - appUserId: - type: integer - format: int64 - personNo: - type: string - personName: - type: string - aliasName: - type: string - sex: - description: 性别,建议使用系统字典值 - type: string - example: "0" - generationNo: - description: 世代序号 - type: integer - generationName: - description: 字辈 - type: string - avatarOssId: - description: 头像文件OSS ID - type: integer - format: int64 - birthDate: - type: string - deathDate: - type: string - introduction: - type: string - FamilyFeedBody: - type: object - properties: - content: - type: string - mediaOssIds: - description: 媒体文件OSS ID列表,多个用逗号或数组传递以接口约定为准 - type: string - example: "2060001,2060002" - visibility: - description: 可见范围,例如0私密、1公开、2成员可见 - type: string - example: "1" - status: - description: 状态,建议使用对应字典值 - type: string - example: "0" - FamilyFeedCommentBody: - type: object - required: [content] - properties: - parentCommentId: - type: integer - format: int64 - nullable: true - replyUserId: - type: integer - format: int64 - nullable: true - content: - type: string - VerificationChallengeBody: - type: object - required: [sceneCode] - properties: - tenantId: - type: string - description: 租户 ID - clientId: - type: string - description: 客户端 ID - sceneCode: - type: string - description: 验证场景编码,例如WEB_H5_LOGIN、WEB_H5_REGISTER - subject: - type: string - description: 验证主体,如手机号/用户名/IP - example: - tenantId: "000000" - clientId: "428a8310cd442757ae699df5d894f051" - sceneCode: "WEB_H5_LOGIN" - subject: "13800000000" - VerificationCheckBody: - type: object - required: [sceneCode] - properties: - tenantId: - description: 租户ID,用于区分不同租户的数据和验证策略 - type: string - clientId: - description: 客户端ID,对应后台 PC/H5 客户端配置 - type: string - sceneCode: - description: 验证场景编码,例如WEB_H5_LOGIN、WEB_H5_REGISTER - type: string - subject: - description: 验证主体,通常为手机号、用户ID或登录账号 - type: string - challengeId: - description: 验证挑战ID,由验证挑战接口返回 - type: string - providerCode: - type: string - description: 验证服务,tianai 或 ruoyi_image - captchaType: - type: string - description: SLIDER/ROTATE/CONCAT/WORD_IMAGE_CLICK/math/char - payload: - type: object - additionalProperties: true - description: 前端验证控件返回的轨迹/点击/图形验证码数据 - VerificationRequireVo: - type: object - properties: - required: - description: 当前场景是否需要验证 - type: boolean - providerCode: - description: 第三方服务商编码 - type: string - captchaType: - description: 验证码类型,例如SLIDER、ROTATE、CONCAT、WORD_IMAGE_CLICK - type: string - sceneCode: - description: 验证场景编码,例如WEB_H5_LOGIN、WEB_H5_REGISTER - type: string - ttlSeconds: - description: 验证或票据有效秒数 - type: integer - example: - required: true - providerCode: "tianai" - captchaType: "SLIDER" - sceneCode: "WEB_H5_LOGIN" - ttlSeconds: 300 - VerificationChallengeVo: - type: object - properties: - required: - description: 当前场景是否需要验证 - type: boolean - providerCode: - description: 第三方服务商编码 - type: string - captchaType: - description: 验证码类型,例如SLIDER、ROTATE、CONCAT、WORD_IMAGE_CLICK - type: string - challengeId: - description: 验证挑战ID,由验证挑战接口返回 - type: string - uuid: - type: string - description: 系统图形验证码 uuid - img: - type: string - description: 系统图形验证码 base64 图片 - payload: - type: object - additionalProperties: true - description: 天爱验证码数据,包括背景图、模板图、尺寸等 - expireSeconds: - description: 验证挑战过期秒数 - type: integer - example: - required: true - providerCode: "tianai" - captchaType: "SLIDER" - challengeId: "CAPTCHA_CHALLENGE_ID" - payload: - backgroundImage: "data:image/png;base64,..." - templateImage: "data:image/png;base64,..." - backgroundImageWidth: 340 - backgroundImageHeight: 180 - expireSeconds: 300 - VerificationCheckVo: - type: object - properties: - passed: - description: 验证是否通过 - type: boolean - validToken: - type: string - description: 验证通过后交给业务接口的票据 - expireSeconds: - description: 验证挑战过期秒数 - type: integer - message: - description: 返回消息 - type: string - example: - passed: true - validToken: "captcha-ticket" - expireSeconds: 300 - message: "验证通过" - LegacyCaptchaVo: - type: object - properties: - captchaEnabled: - type: boolean - uuid: - description: 系统图形验证码UUID - type: string - img: - type: string - description: base64 图形验证码 - example: - captchaEnabled: true - uuid: "captcha-uuid" - img: "data:image/png;base64,..." - RVerificationRequire: - type: object - properties: - code: - description: 系统图形验证码或响应状态码 - type: integer - example: 200 - msg: - description: 响应消息 - type: string - example: "操作成功" - data: - description: 响应数据主体 - $ref: '#/components/schemas/VerificationRequireVo' - RVerificationChallenge: - type: object - properties: - code: - description: 系统图形验证码或响应状态码 - type: integer - example: 200 - msg: - description: 响应消息 - type: string - example: "操作成功" - data: - description: 响应数据主体 - $ref: '#/components/schemas/VerificationChallengeVo' - RVerificationCheck: - type: object - properties: - code: - description: 系统图形验证码或响应状态码 - type: integer - example: 200 - msg: - description: 响应消息 - type: string - example: "操作成功" - data: - description: 响应数据主体 - $ref: '#/components/schemas/VerificationCheckVo' - RLegacyCaptcha: - type: object - properties: - code: - description: 系统图形验证码或响应状态码 - type: integer - example: 200 - msg: - description: 响应消息 - type: string - example: "操作成功" - data: - description: 响应数据主体 - $ref: '#/components/schemas/LegacyCaptchaVo' diff --git a/help.html b/help.html index 55f95c1..ae2d181 100644 --- a/help.html +++ b/help.html @@ -124,7 +124,6 @@ - diff --git a/index.html b/index.html index 977021a..3afb623 100644 --- a/index.html +++ b/index.html @@ -603,7 +603,7 @@

文化内容

家族文化姓氏百科家谱知识平台公告 + >新闻资讯

联系我们

@@ -614,6 +614,9 @@