Files

349 lines
8.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: sync-backend-api
description: Sync this Next.js frontend with backend API documentation
---
# sync-backend-api
根据后端文档同步当前 Next.js / TypeScript 项目的前端代码。
## 使用场景
当后端 API 新增、删除、字段结构调整、响应语义变化时,使用本 skill 更新:
- 网络层
- schema / 不可变纯数据
- 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 Model | `src/data/schemas/<module>/*.ts` | Zod schema 与不可变纯数据类型,负责解析和归一化 |
| 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` | schema model、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 Model
在对应模块下新增或修改:
```text
src/data/schemas/<module>/*.ts
```
要求:
- 使用 Zod。
- 输出类型用 `z.output<typeof Schema>`
- 输入类型用 `z.input<typeof Schema>`
- 对后端可能返回 `null` 的字段做防御性处理。
- 不要在 schema 中写 UI 逻辑。
- Schema 输出必须是纯数据,不要定义 class 或工厂对象。
- 对象 Schema 使用 `.readonly()`;已知嵌套对象、数组和 Record 也必须逐层 readonly。
- transform 应先完成归一化,再在最终输出应用 `.readonly()`
- 容器默认值必须经过 Schema 解析;使用 `prefault` 或在最外层应用 readonly,不能让 `.default()` 返回未冻结对象。
- 后端返回但前端既不读取、也不透传的字段,不要加入 Schema。
- 仅用于请求序列化、响应兼容或内部透传的字段直接保留在 Schema 中。
- 统一使用 `XxxSchema.parse()` 构造请求与解析响应,不增加 `from()``fromJson()``toJson()` 包装。
- 不要再创建独立的数据传输对象层。
示例:
```ts
export const XxxResponseSchema = z
.object({
id: z.string(),
tags: z
.array(z.string())
.default(() => [])
.readonly(),
count: z.number().default(0),
})
.readonly();
export type XxxResponseInput = z.input<typeof XxxResponseSchema>;
export type XxxResponseData = z.output<typeof XxxResponseSchema>;
export type XxxResponse = XxxResponseData;
```
### 5. 更新 API Service
修改:
```text
src/data/services/api/<module>_api.ts
```
统一调用方式:
```ts
const env = await httpClient<ApiEnvelope<unknown>>(ApiPath.xxx, {
method: "POST",
body: request,
});
return XxxResponseSchema.parse(unwrap(env));
```
注意:
- 不要直接返回 `unknown`
- 不要在 API 层处理 UI 行为。
- multipart 仍使用 `FormData`
### 6. 更新 Repository
修改:
```text
src/data/repositories/interfaces/i<module>_repository.ts
src/data/repositories/<module>_repository.ts
```
Repository 返回项目通用 `Result<T>`
```ts
async getXxx(): Promise<Result<XxxResponse>> {
return Result.wrap(() => this.api.getXxx());
}
```
Repository 可以做轻量编排,例如:
- request schema 解析
- 多接口组合
- 本地 storage 同步
- 本地模型与接口模型转换
不要把 React / UI 逻辑放进 repository。
### 7. 更新 Storage
如果接口影响本地持久化,修改:
```text
src/data/storage/storage_keys.ts
src/data/storage/<module>/*
```
原则:
- 只持久化业务需要的最小字段。
- 不要为了接口响应完整而盲目存完整 payload。
- storage 使用 `SpAsyncUtil` 和 Zod schema 校验。
示例:
```ts
getEntitlementSnapshot(): Promise<Result<UserEntitlementSnapshotData | null>> {
return SpAsyncUtil.getJson(
StorageKeys.userEntitlementSnapshot,
UserEntitlementSnapshotSchema,
);
}
```
### 8. 更新 Store / 状态机
如果接口影响状态流,修改:
```text
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 操作细节。
### 9. 更新 UI
只有在接口变更影响展示或用户明确要求时,才修改:
```text
src/app/**
```
原则:
- UI 只负责渲染和派发事件。
- 不在页面组件里直接写复杂业务编排。
- 全局监听逻辑优先放到 `stores/*/*-sync.tsx`
### 10. 更新桶文件
如果新增 schema model 或 component,更新对应 `index.ts`
```text
src/data/schemas/<module>/index.ts
src/app/**/components/index.ts
```
如果项目使用 barrelsby 生成,不要手动破坏现有导出格式。
### 11. 更新 Mock 数据
如果新增或调整数据结构,按一个结构一个文件的原则放到:
```text
src/data/mock/<module>/requests
src/data/mock/<module>/responses
src/data/mock/<module>/websocket-events
```
不要把多个结构塞进一个大 JSON。
### 12. 更新测试
优先补充:
- schema model 解析与序列化测试
- helper 纯函数测试
- XState transition 测试
- repository 编排测试(如已有测试基础)
常见位置:
```text
src/data/schemas/<module>/__tests__/*.test.ts
src/stores/<module>/__tests__/*.test.ts
```
### 13. 验证
至少运行:
```bash
pnpm exec tsc --noEmit
pnpm exec eslint <changed files>
pnpm exec vitest run <related tests>
```
如果没有相关测试,需要说明未覆盖的风险。
## 常见任务模板
### 新增接口
1. 文档确认 path / method / payload。
2. `api_path.ts` 新增路径。
3. 在同一文件新增 readonly schema 与输入/输出类型。
4. API service 新增方法。
5. repository interface + implementation 新增方法。
6. 如需要,接入 store actor / event。
7. 补 mock 和测试。
8. 运行验证。
### 修改响应字段
1. 更新 schema。
2. 更新同文件输入/输出类型及相关 parse 调用。
3. 更新 mapper/helper。
4. 更新 UI 或状态机依赖字段。
5. 更新 mock 和测试。
6. 运行验证。
### 删除旧接口
1. 搜索所有引用。
2. 删除 API path / API method / repository method。
3. 删除 schema model、mock 和测试。
4. 更新状态机和 UI 调用。
5. 运行 `rg` 确认无残留引用。
6. 运行验证。