diff --git a/qy-lty-admin/.planning/phases/01-credential-slot-api/01-CONTEXT.md b/qy-lty-admin/.planning/phases/01-credential-slot-api/01-CONTEXT.md index 1e2c30f..fa23722 100644 --- a/qy-lty-admin/.planning/phases/01-credential-slot-api/01-CONTEXT.md +++ b/qy-lty-admin/.planning/phases/01-credential-slot-api/01-CONTEXT.md @@ -92,39 +92,39 @@ function mapBackendCredentialSlot(raw: BackendCredentialSlot): CredentialSlot { **前端→后端**(PUT 请求体正向): ```typescript -function toBackendUpdatePayload(payload: CredentialSlotUpdatePayload): BackendCredentialSlot { +function toBackendUpdatePayload(payload: CredentialSlotUpdatePayload): { app_id: string; access_token: string } { return { app_id: payload.appId, access_token: payload.accessToken, - updated_at: '', // 后端 auto_now=True 自动维护,前端传空字符串占位即可 + // 不带 updated_at —— 仓库现有约定(researcher 实测 ai-models.ts/outfits.ts 全部仅传业务字段) + // 后端 auto_now=True 自动维护 } - // 备选:直接 { app_id, access_token } 不带 updated_at;planner 在 read_first 阶段确认现有 mapBackend* 约定 } ``` -**Planner 决定**:是否在 PUT 适配器中携带 `updated_at: ''`,还是只传两个真实字段(让 axios 自动序列化为 JSON)。仓库现有 lib/api/*.ts 哪种风格更主流,照抄。 - ### API 函数签名 -**注意**:此处**不**使用 `apiClient.get('/v1/admin/credential-slot/')` 的相对路径硬编码 —— 沿用 `lib/api/` 现有模块的写法(researcher 必须 read_first 看 outfits.ts / songs.ts 等已有模块的路径风格): +**关键修正(researcher 实测)**: +- `apiClient` 响应拦截器**不解包**(仅 `console.log` 后透传 response) +- 仓库现有模块(`ai-models.ts` / `outfits.ts` 等)统一约定:调用方手写 `const data = response.data?.data || response.data` 兼容"标准壳层"与"裸响应"两种形态 +- 路径**不含 `/api` 前缀**(`API_BASE_URL` 已吃掉 `/api`),写 `/v1/admin/credential-slot/` ```typescript export async function getCredentialSlot(): Promise { - const response = await apiClient.get>('/v1/admin/credential-slot/') - // 拦截器已解包 success/code/message/data,到这里 response.data 就是 BackendCredentialSlot - // 但 axios 默认行为是把 HTTP body 整体放到 response.data —— 必须 read_first 确认 client.ts 的拦截器是否已经做了 .data 提取 - // 若拦截器已提取,则这里 response.data 就是 BackendCredentialSlot;否则需要 response.data.data - return mapBackendCredentialSlot(response.data) // 或 response.data.data,依拦截器行为而定 + const response = await apiClient.get('/v1/admin/credential-slot/') + const raw = response.data?.data || response.data // 兼容标准壳层与裸响应 + return mapBackendCredentialSlot(raw) } export async function updateCredentialSlot(payload: CredentialSlotUpdatePayload): Promise { const body = toBackendUpdatePayload(payload) - const response = await apiClient.put>('/v1/admin/credential-slot/', body) - return mapBackendCredentialSlot(response.data) + const response = await apiClient.put('/v1/admin/credential-slot/', body) + const raw = response.data?.data || response.data + return mapBackendCredentialSlot(raw) } ``` -**Planner 必须在 read_first 阶段确认 `apiClient` 拦截器的具体行为**(特别是 `client.ts` 的 response interceptor 是否已经把 `data.data` 提取出来),然后给出与现有模块 100% 一致的写法。 +**1:1 模板**:`lib/api/ai-models.ts` L1-85(`getAiModel(id)` 单资源 GET 形态最贴近)、L65-73(`updateAiModel` PATCH body 构造,仅传业务字段不带 `updated_at`)。Planner 应把这两段作为照抄起点。 ### `lib/api/index.ts` 导出 @@ -140,10 +140,13 @@ export { 如 `index.ts` 不存在或导出风格不同,按仓库现有约定(researcher 必须看 `lib/api/index.ts` 当前内容)。 -### `tsc --noEmit` 通过 +### Lint + 类型检查(researcher 修正:必须两条独立命令) -- 项目脚本是 `npm run lint`(实际跑 `next lint` + 某种 tsc 检查;planner 验证 `package.json` 的 `lint` script 实际行为) -- Phase 1 完成态:`npm run lint` 退出码 0,无 TypeScript 错误 +- `npm run lint` 实际**只跑 `next lint`(ESLint)**,**不跑 tsc**(researcher 实测) +- `next.config.mjs` 的 `typescript.ignoreBuildErrors: true` 仅影响 `next build`,**不**影响独立 `tsc` +- Phase 1 完成态需**两条**独立命令都退出码 0: + - `npm run lint` —— ESLint 检查 + - `npx tsc --noEmit` —— TypeScript 类型检查(独立运行,**不**省略) ### 修改记录