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