--- name: sync-backend-api description: Sync this Next.js frontend with backend API documentation --- # sync-backend-api 根据后端文档同步当前 Next.js / TypeScript 项目的前端代码。 ## 使用场景 当后端 API 新增、删除、字段结构调整、响应语义变化时,使用本 skill 更新: - 网络层 - schema / DTO - repository - storage - XState store - UI 调用逻辑 - mock 数据 - 单元测试 ## 当前项目结构 | 层级 | 路径 | 说明 | | --- | --- | --- | | API Path | `src/data/services/api/api_path.ts` | 统一维护接口路径 | | API Service | `src/data/services/api/*_api.ts` | 调用 `httpClient` 并解析响应 | | Schema | `src/data/schemas//*.ts` | Zod schema,负责防御性解析 | | DTO | `src/data/dto//*.ts` | DTO class,封装 `fromJson/toJson` | | Repository Interface | `src/data/repositories/interfaces/i*_repository.ts` | 仓库接口 | | Repository | `src/data/repositories/*_repository.ts` | 业务仓库实现 | | Storage | `src/data/storage/*` | 本地持久化 | | Store | `src/stores//*` | XState 状态机、actors、helpers、sync | | UI | `src/app/**` | Next.js app router UI | | Mock | `src/data/mock//**` | 模拟请求 / 响应数据 | | Tests | `**/__tests__/*.test.ts` | DTO、helper、machine transition 测试 | ## 后端文档来源 优先读取用户指定的文档或文件片段。 常见文档: - `FRONTEND_VIP_CREDIT_API.md` - `PAYWALL_API.md` - `docs-change.md` - `docs/backend/openapi.json` 如果用户没有指定文档,先搜索相关 API 路径或模块名,再选择最相关文档。 ## 更新流程 ### 1. 检查工作区 开始前必须执行: ```bash git status --short ``` 如果存在无关未提交改动: - 不要回退。 - 不要覆盖。 - 修改前先确认本次任务需要触碰的文件。 - 提交时只暂存本次任务相关文件。 ### 2. 阅读后端文档 确认: - API method - API path - 请求参数 - 响应结构 - 字段是否可空 - 旧字段是否废弃 - 是否需要认证 token - 是否影响本地持久化 - 是否影响状态机或 UI 如果用户明确要求“不兼容旧字段,彻底更新”,不要保留旧字段 fallback。 否则 schema 应尽量防御性解析,例如: ```ts z.string().nullable().transform((v) => v ?? "").default("") ``` 项目已有通用 helper: ```ts src/data/schemas/nullable-defaults.ts ``` ### 3. 更新 API Path 新增或修改: ```ts src/data/services/api/api_path.ts ``` 示例: ```ts static readonly userEntitlements = `${ApiPath._user}/entitlements`; ``` ### 4. 更新 Schema 在对应模块下新增或修改: ```text src/data/schemas//*.ts ``` 要求: - 使用 Zod。 - 输出类型用 `z.output`。 - 输入类型用 `z.input`。 - 对后端可能返回 `null` 的字段做防御性处理。 - 不要在 schema 中写 UI 逻辑。 示例: ```ts export const XxxResponseSchema = z.object({ id: z.string(), count: z.number().default(0), }); export type XxxResponseInput = z.input; export type XxxResponseData = z.output; ``` ### 5. 更新 DTO 在对应模块下新增或修改: ```text src/data/dto//*.ts ``` 要求: - DTO class 的 `declare readonly` 只声明前端业务代码会直接访问的字段。 - 不要因为后端响应包含某字段,就机械地为它增加 class 属性、嵌套类型或类型别名。 - 后端返回但前端既不读取、也不透传的字段,可以不加入 schema 和 DTO。 - 仅用于请求序列化、响应兼容或内部透传的字段保留在 schema 中,不添加 class 属性声明。 - `Object.assign(this, data)` 会保留 schema 解析后的字段;即使 class 未声明对应属性,`toJson()` 仍可按 schema 正常序列化。 - 新增 DTO 字段前先搜索实际调用点;没有直接读取方时默认不声明。 DTO 统一模式: ```ts export const XxxResponseSchema = z.object({ id: z.string(), // 仅用于内部透传,不需要在 DTO class 中声明。 traceId: z.string(), }); export class XxxResponse { // 业务代码直接读取的字段才公开声明。 declare readonly id: string; private constructor(input: XxxResponseInput) { const data = XxxResponseSchema.parse(input); Object.assign(this, data); Object.freeze(this); } static from(input: XxxResponseInput): XxxResponse { return new XxxResponse(input); } static fromJson(json: unknown): XxxResponse { return XxxResponse.from(json as XxxResponseInput); } toJson(): XxxResponseData { return XxxResponseSchema.parse(this); } } ``` ### 6. 更新 API Service 修改: ```text src/data/services/api/_api.ts ``` 统一调用方式: ```ts const env = await httpClient>(ApiPath.xxx, { method: "POST", body: request.toJson(), }); return XxxResponse.fromJson(unwrap(env) as Record); ``` 注意: - 不要直接返回 `unknown`。 - 不要在 API 层处理 UI 行为。 - multipart 仍使用 `FormData`。 ### 7. 更新 Repository 修改: ```text src/data/repositories/interfaces/i_repository.ts src/data/repositories/_repository.ts ``` Repository 返回项目通用 `Result`: ```ts async getXxx(): Promise> { return Result.wrap(() => this.api.getXxx()); } ``` Repository 可以做轻量编排,例如: - request DTO 构造 - 多接口组合 - 本地 storage 同步 - local model 与 DTO 转换 不要把 React / UI 逻辑放进 repository。 ### 8. 更新 Storage 如果接口影响本地持久化,修改: ```text src/data/storage/storage_keys.ts src/data/storage//* ``` 原则: - 只持久化业务需要的最小字段。 - 不要为了接口响应完整而盲目存完整 payload。 - storage 使用 `SpAsyncUtil` 和 Zod schema 校验。 示例: ```ts getEntitlementSnapshot(): Promise> { return SpAsyncUtil.getJson( StorageKeys.userEntitlementSnapshot, UserEntitlementSnapshotSchema, ); } ``` ### 9. 更新 Store / 状态机 如果接口影响状态流,修改: ```text src/stores//-state.ts src/stores//-events.ts src/stores//-machine.ts src/stores//-machine.actors.ts src/stores//-machine.helpers.ts src/stores//-context.tsx src/stores//*-sync.tsx ``` 项目约定: - API 调用放在 `actors.ts`。 - 纯函数转换放在 `helpers.ts`。 - 跨 store 监听放在 `*-sync.tsx`。 - 页面组件不承担全局监听职责。 - 状态机事件应表达业务事实,而不是 UI 操作细节。 ### 10. 更新 UI 只有在接口变更影响展示或用户明确要求时,才修改: ```text src/app/** ``` 原则: - UI 只负责渲染和派发事件。 - 不在页面组件里直接写复杂业务编排。 - 全局监听逻辑优先放到 `stores/*/*-sync.tsx`。 ### 11. 更新桶文件 如果新增 schema / DTO / component,更新对应 `index.ts`: ```text src/data/schemas//index.ts src/data/dto//index.ts src/app/**/components/index.ts ``` 如果项目使用 barrelsby 生成,不要手动破坏现有导出格式。 ### 12. 更新 Mock 数据 如果新增或调整数据结构,按一个结构一个文件的原则放到: ```text src/data/mock//requests src/data/mock//responses src/data/mock//websocket-events ``` 不要把多个结构塞进一个大 JSON。 ### 13. 更新测试 优先补充: - DTO / schema parse 测试 - helper 纯函数测试 - XState transition 测试 - repository 编排测试(如已有测试基础) 常见位置: ```text src/data/dto//__tests__/*.test.ts src/stores//__tests__/*.test.ts ``` ### 14. 验证 至少运行: ```bash pnpm exec tsc --noEmit pnpm exec eslint pnpm exec vitest run ``` 如果没有相关测试,需要说明未覆盖的风险。 ## 常见任务模板 ### 新增接口 1. 文档确认 path / method / payload。 2. `api_path.ts` 新增路径。 3. 新增 schema。 4. 新增 DTO。 5. API service 新增方法。 6. repository interface + implementation 新增方法。 7. 如需要,接入 store actor / event。 8. 补 mock 和测试。 9. 运行验证。 ### 修改响应字段 1. 更新 schema。 2. 更新 DTO declare 字段和 `toJson`。 3. 更新 mapper/helper。 4. 更新 UI 或状态机依赖字段。 5. 更新 mock 和测试。 6. 运行验证。 ### 删除旧接口 1. 搜索所有引用。 2. 删除 API path / API method / repository method。 3. 删除 DTO/schema/mock/test。 4. 更新状态机和 UI 调用。 5. 运行 `rg` 确认无残留引用。 6. 运行验证。