---
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/<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.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/<module>/*.ts
```

要求：

- 使用 Zod。
- 输出类型用 `z.output<typeof Schema>`。
- 输入类型用 `z.input<typeof Schema>`。
- 对后端可能返回 `null` 的字段做防御性处理。
- 不要在 schema 中写 UI 逻辑。

示例：

```ts
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

在对应模块下新增或修改：

```text
src/data/dto/<module>/*.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/<module>_api.ts
```

统一调用方式：

```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

修改：

```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 DTO 构造
- 多接口组合
- 本地 storage 同步
- local model 与 DTO 转换

不要把 React / UI 逻辑放进 repository。

### 8. 更新 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,
  );
}
```

### 9. 更新 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 操作细节。

### 10. 更新 UI

只有在接口变更影响展示或用户明确要求时，才修改：

```text
src/app/**
```

原则：

- UI 只负责渲染和派发事件。
- 不在页面组件里直接写复杂业务编排。
- 全局监听逻辑优先放到 `stores/*/*-sync.tsx`。

### 11. 更新桶文件

如果新增 schema / DTO / component，更新对应 `index.ts`：

```text
src/data/schemas/<module>/index.ts
src/data/dto/<module>/index.ts
src/app/**/components/index.ts
```

如果项目使用 barrelsby 生成，不要手动破坏现有导出格式。

### 12. 更新 Mock 数据

如果新增或调整数据结构，按一个结构一个文件的原则放到：

```text
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 编排测试（如已有测试基础）

常见位置：

```text
src/data/dto/<module>/__tests__/*.test.ts
src/stores/<module>/__tests__/*.test.ts
```

### 14. 验证

至少运行：

```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. 新增 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. 运行验证。
