docs(01-01): 完成 Phase 1 Plan 01-01「凭据槽位 API 客户端」收尾

- 新增 01-01-SUMMARY.md:记录 Task 1/2 commits、关键串命中、字节产物对比、Self-Check PASSED
- STATE.md:当前位置切到 'Plan 01-01 完成 / 01-02 待执行',进度 50%(Phase 1 内部 1/2 plan),新增 Plan 执行记录表 + 2026-05-08 关键决策
- ROADMAP.md:Phase 1 进度 0/2 → 1/2,状态 Not started → In Progress
- REQUIREMENTS.md:CRED-FE-01 Active 段已勾选 [x];Traceability 状态切到  Done(commits a0d0b9c + c072bbe)

Plan 01-01 父仓库 commits:a0d0b9c (lib/api/credential-slot.ts) + c072bbe (lib/api/index.ts)
This commit is contained in:
pmc 2026-05-08 11:06:42 +08:00
parent c072bbec8c
commit ce0df098be
4 changed files with 186 additions and 19 deletions

View File

@ -87,7 +87,7 @@
### 通用凭据槽位前端集成CRED-FE
- [ ] **CRED-FE-01** API 客户端 `lib/api/credential-slot.ts`:导出 `getCredentialSlot()``updateCredentialSlot({ app_id, access_token })`;含响应适配器 `mapBackendCredentialSlot()`snake_case → camelCase共享类型 `CredentialSlot { appId, accessTokenMasked, updatedAt }`;从 `lib/api/index.ts` 导出
- [x] **CRED-FE-01** API 客户端 `lib/api/credential-slot.ts`:导出 `getCredentialSlot()``updateCredentialSlot({ app_id, access_token })`;含响应适配器 `mapBackendCredentialSlot()`snake_case → camelCase共享类型 `CredentialSlot { appId, accessTokenMasked, updatedAt }`;从 `lib/api/index.ts` 导出
- [ ] **CRED-FE-02** RBAC 模块声明:`lib/permissions.ts` 加入 `credential-slot` 模块 key`PermissionModule` 类型扩充);`PERMISSION_MATRIX` 把该模块分配给"超级管理员"和"AI模型管理员"两个角色;`getModuleFromPath()` 不需要新映射(凭据槽位是内嵌于 `/ai-model` 的子能力,不占独立路由)
- [ ] **CRED-FE-03** `/ai-model` 页面入口:在合适位置(如页头工具栏 / Header 右侧)渲染"凭据槽位"按钮或卡片;仅当 `hasPermission('credential-slot')` 为 true 时可见;点击触发对话框打开
- [ ] **CRED-FE-04** 编辑对话框组件 `components/ai-model/CredentialSlotDialog.tsx`:基于 `components/ui/dialog.tsx`;表单 React Hook Form + Zod 校验;预填态显示后端返回的 `app_id` 明文 + `access_token` 末 4 位掩码 + 不可改的 `updated_at`;表单语义为"留空保留旧值,重新输入才覆写"(避免把脱敏掩码当真值回写);提交触发 `updateCredentialSlot()`,仅提交用户实际输入的字段
@ -133,7 +133,7 @@
| Requirement | Phase | UI hint | Status |
|-------------|-------|---------|--------|
| CRED-FE-01 API 客户端 `lib/api/credential-slot.ts`(类型 + 适配器 + GET/PUT | Phase 1 凭据槽位 API 客户端 | — | Pending |
| CRED-FE-01 API 客户端 `lib/api/credential-slot.ts`(类型 + 适配器 + GET/PUT | Phase 1 凭据槽位 API 客户端 | — | ✅ Done (Plan 01-01, 2026-05-08, commits a0d0b9c + c072bbe) |
| CRED-FE-02 RBAC 模块声明(`lib/permissions.ts``credential-slot` key + 矩阵分配) | Phase 2 RBAC 收敛 + AI 模型页入口 | yes | Pending |
| CRED-FE-03 `/ai-model` 页面入口(受 `hasPermission('credential-slot')` 收敛) | Phase 2 RBAC 收敛 + AI 模型页入口 | yes | Pending |
| CRED-FE-04 编辑对话框 `CredentialSlotDialog.tsx`RHF + Zod留空保留旧值语义 | Phase 3 编辑对话框 + 提交反馈 | yes | Pending |
@ -146,3 +146,4 @@
---
*Last updated: 2026-05-07 — Milestone v1.0「通用凭据槽位前端集成」ROADMAP 生成Traceability 回填 5/5*
*2026-05-08 更新Plan 01-01 落地CRED-FE-01 状态切到 ✅ DoneActive 段已自动勾选 [x]*

View File

@ -34,7 +34,7 @@
3. `lib/api/index.ts` 导出新模块,`import { getCredentialSlot, updateCredentialSlot, type CredentialSlot } from '@/lib/api'` 在任一组件文件中均能解析通过 `tsc --noEmit`
4. 在浏览器开发态以 mock 后端或后端 Phase 2 联调环境调用 `getCredentialSlot()`,控制台可以看到一条带 `Authorization: Bearer ...` 的请求,且返回值字段名是前端 camelCase说明适配器生效未把后端原始 snake_case 直接透传)
**Plans**: 2 plans
- [ ] 01-01-PLAN.md — 新建 lib/api/credential-slot.ts类型 + adapter + GET/PUT+ lib/api/index.ts 末尾追加具名 re-export
- [x] 01-01-PLAN.md — 新建 lib/api/credential-slot.ts类型 + adapter + GET/PUT+ lib/api/index.ts 末尾追加具名 re-export
- [ ] 01-02-PLAN.md — docs/修改记录.md 顶部追加 Phase 1 条目 + 跑双重验证npm run lint + npx tsc --noEmit+ 探针验证 barrel 入口
### Phase 2: RBAC 收敛 + AI 模型页入口
@ -69,7 +69,7 @@ Phase 按数值顺序执行1 → 2 → 3如出现紧急插入记为 1.1
| Phase | Plans Complete | Status | Completed |
|-------|----------------|--------|-----------|
| 1. 凭据槽位 API 客户端 | 0/2 | Not started | - |
| 1. 凭据槽位 API 客户端 | 1/2 | In Progress | - |
| 2. RBAC 收敛 + AI 模型页入口 | 0/TBD | Not started | - |
| 3. 编辑对话框 + 提交反馈 | 0/TBD | Not started | - |

View File

@ -3,19 +3,19 @@ gsd_state_version: 1.0
milestone: v1.0
milestone_name: 通用凭据槽位前端集成
status: executing
last_updated: "2026-05-08T03:00:37.786Z"
last_updated: "2026-05-08T03:04:39.737Z"
last_activity: 2026-05-08
progress:
total_phases: 3
completed_phases: 0
total_plans: 2
completed_plans: 0
percent: 0
completed_plans: 1
percent: 50
---
# Project State — 洛天依应用管理后台qy-lty-admin
**最后更新**: 2026-05-07Milestone v1.0 通用凭据槽位前端集成 ROADMAP 落地Phase 1 待启动
**最后更新**: 2026-05-08Phase 1 Plan 01-01 已落地lib/api/credential-slot.ts + index.ts re-exportCRED-FE-01 已交付
## 项目引用
@ -30,19 +30,19 @@ progress:
```
Milestone: v1.0 通用凭据槽位前端集成
Phase: Phase 1「凭据槽位 API 客户端」
Plan: —(待 /gsd-plan-phase 1 生成)
Status: Ready to execute
Progress: [░░░░░░░░░░] 0%0/3 phase 完成)
Plan: 01-01 完成 ✅ / 01-02 待执行
Status: Phase 1 in progress (1/2 plans done)
Progress: [█████░░░░░] 50%Phase 1 内部 1/2 plan 完成)
Last activity: 2026-05-08
```
**下一步行动**运行 `/gsd-plan-phase 1` 把 Phase 1 拆解为可执行的 plan 节点
**下一步行动**执行 Plan 01-02`docs/修改记录.md` 顶部追加 Phase 1 条目 + `npm run lint` + `npx tsc --noEmit` 双重验证 + barrel 入口探针)
## Phase 概览
| Phase | 标题 | 需求 | UI hint | 状态 |
|-------|------|------|---------|------|
| 1 | 凭据槽位 API 客户端 | CRED-FE-01 | — | 待启动 |
| 1 | 凭据槽位 API 客户端 | CRED-FE-01 ✅ | — | 进行中1/2 plan|
| 2 | RBAC 收敛 + AI 模型页入口 | CRED-FE-02, CRED-FE-03 | yes | 未开始 |
| 3 | 编辑对话框 + 提交反馈 | CRED-FE-04, CRED-FE-05 | yes | 未开始 |
@ -58,10 +58,16 @@ Last activity: 2026-05-08
| 指标 | 数值 |
|------|------|
| 已完成 phase | 0 / 3 |
| 已完成 plan | 0 / TBD |
| Milestone 进度 | 0% |
| 已完成 plan | 1 / 2Phase 1 内部)|
| Milestone 进度 | ~17%1/6 plan 估算) |
| 启动日期 | 2026-05-07 |
| 最近活动 | 2026-05-07 ROADMAP.md 落地 |
| 最近活动 | 2026-05-08 Plan 01-01 落地commits a0d0b9c + c072bbe|
### Plan 执行记录
| Phase-Plan | 任务数 | 文件改动 | 耗时 | 完成日期 |
|------------|-------|---------|------|----------|
| 01-01 | 2 | 2 | ~76s | 2026-05-08 |
## 累积上下文
@ -70,6 +76,7 @@ Last activity: 2026-05-08
- **2026-05-07 phase 拆分Option B / 3 phase**API 客户端独立成 Phase 1无 UI权限矩阵 + 入口控件合并为 Phase 2UI编辑对话框 + 反馈合并为 Phase 3UI。理由Phase 1 是纯逻辑、可在后端联调前独立打磨Phase 2 一旦完成未授权角色即彻底看不到入口安全前置Phase 3 集中处理"留空保留旧值"语义这条最容易翻车的业务规则。
- **2026-05-07 跨项目依赖明确**:前端 phase 不阻塞代码编写,但端到端验收依赖 qy_lty 后端 Phase 2管理端读写接口落地本仓库 Phase 3 收尾节奏与后端 Phase 2 完工对齐。
- **2026-05-07 表单"留空保留旧值"语义**:后端 GET 返回的是末 4 位脱敏掩码,前端**绝不**能把掩码当真值再 PUT 回去Phase 3 success criteria #2 显式约束。
- **2026-05-08 Plan 01-01 落地**1:1 复刻 ai-models.ts 风格的 credential-slot.tsadapter + GET/PUTindex.ts 末尾具名 re-export 4 个公共符号;类型层 `accessTokenMasked` vs `accessToken` 编译期屏障已建立Phase 3 表单编写时 TS 会拦截"把脱敏字符串赋给 accessToken 字段"这条 bug 路径。
### 待办事项
@ -97,9 +104,9 @@ Last activity: 2026-05-08
## 会话连续性
**最近会话**2026-05-07
**最近动作**roadmapper 生成 ROADMAP.md3 个 phaseOption B 拆分)+ 回填 REQUIREMENTS.md Traceability5/5+ 切换 STATE.md 到 Phase 1 待启动
**下一会话起点**`/gsd-plan-phase 1` 启动 Phase 1 规划
**最近会话**2026-05-08
**最近动作**执行 Plan 01-01lib/api/credential-slot.ts 新建 + lib/api/index.ts 末尾 re-export父仓库 commits a0d0b9c + c072bbeCRED-FE-01 在 REQUIREMENTS.md 已勾选完成
**下一会话起点**执行 Plan 01-02docs/修改记录.md 追加 Phase 1 条目 + `npm run lint` + `npx tsc --noEmit` 双重验证 + barrel 入口 import 探针)
## 工作流配置
@ -132,3 +139,4 @@ CLAUDE.md 中两条强制规则,做任何 phase 时必须遵守:
---
*2026-05-07 由 gsd-roadmapper 切换到 Phase 1 待启动状态*
*2026-05-08 Plan 01-01 完成CRED-FE-01 已交付Phase 1 进度 1/2等待 Plan 01-02 收尾*

View File

@ -0,0 +1,158 @@
---
phase: 01-credential-slot-api
plan: 01
subsystem: api-client
tags: [api-client, credential-slot, milestone-v1.0, brownfield]
requires: []
provides:
- artifact: lib/api/credential-slot.ts
description: 凭据槽位 API 客户端模块(类型 + adapter + GET/PUT 函数)
- export: getCredentialSlot
from: lib/api/credential-slot.ts
via: lib/api/index.ts (具名 re-export)
- export: updateCredentialSlot
from: lib/api/credential-slot.ts
via: lib/api/index.ts (具名 re-export)
- type: CredentialSlot
from: lib/api/credential-slot.ts
via: lib/api/index.ts (具名 re-export)
- type: CredentialSlotUpdatePayload
from: lib/api/credential-slot.ts
via: lib/api/index.ts (具名 re-export)
affects:
- lib/api/index.ts
tech_stack:
added: []
patterns:
- "1:1 复刻 lib/api/ai-models.ts 风格mapBackend* + 双保险解包 + export const fn = async"
- "类型命名屏障accessTokenMasked vs accessToken 在 TS 编译期切断脱敏字符串回写 bug"
- "barrel re-exportlib/api/index.ts 末尾具名 re-export与现有 export * 风格混用合法"
key_files:
created:
- lib/api/credential-slot.ts
modified:
- lib/api/index.ts
decisions:
- 采用「具名 re-export」而非 `export *`:符合 RESEARCH 问题 6 + CONTEXT.md 锁定写法,可读性最高、未来重名冲突可控
- PUT body 不带 `updated_at`:沿用 updateAiModel / updateOutfit 约定,由后端 auto_now 维护
- `BackendCredentialSlot` 不导出:仅 adapter 入参类型,与 `mapBackend*` 模块级私有约定一致
- 路径写 `/v1/admin/credential-slot/`(不带 `/api` 前缀API_BASE_URL 已含 `/api`
metrics:
duration_seconds: 76
completed_date: 2026-05-08
tasks_completed: 2
files_changed: 2
requirements:
- CRED-FE-01
---
# Phase 1 Plan 01-01凭据槽位 API 客户端 Summary
**One-liner**1:1 复刻 ai-models.ts 风格落地 `lib/api/credential-slot.ts`,封装 GET/PUT + camelCase 类型 + adapter并在 `lib/api/index.ts` 末尾具名 re-export 4 个公共符号。
## 背景
Milestone v1.0「通用凭据槽位前端集成」启动 plan纯逻辑层无 UI为 Phase 2RBAC + AI 模型页入口)/ Phase 3编辑对话框 + Sonner 反馈)提供调用层基础。本 plan 同时建立类型层屏障:`CredentialSlot.accessTokenMasked`脱敏vs `CredentialSlotUpdatePayload.accessToken`(明文)字段名故意不同,让 TS 编译期切断「把脱敏掩码当真值回写 PUT」这条 bug 路径。
## Tasks Executed
### Task 1新建 lib/api/credential-slot.ts类型 + adapter + GET/PUT
- **状态**: ✅ 完成
- **Commit**: `a0d0b9c`(父级 Lila-Server 仓库)
- **文件**: `lib/api/credential-slot.ts`新增64 行 / 2620 字节)
- **产物**:
- `interface BackendCredentialSlot`snake_case模块级私有不导出
- `export interface CredentialSlot { appId, accessTokenMasked, updatedAt }`(公共响应类型)
- `export interface CredentialSlotUpdatePayload { appId, accessToken }`(公共提交载荷类型)
- `function mapBackendCredentialSlot(raw)`(模块级私有 adaptersnake → camel
- `export const getCredentialSlot = async (): Promise<CredentialSlot>` — 走 `apiClient.get('/v1/admin/credential-slot/')`
- `export const updateCredentialSlot = async (payload): Promise<CredentialSlot>` — 走 `apiClient.put('/v1/admin/credential-slot/', { app_id, access_token })`
- **关键串命中**:
- `response.data?.data || response.data` 双保险解包:**2 次**GET / PUT 各一)
- `apiClient.get('/v1/admin/credential-slot/')`1 次
- `apiClient.put('/v1/admin/credential-slot/'`1 次
- `/api/v1/admin/credential-slot`(重复 /api 前缀):**0 次** ✓(路径正确)
- PUT body 字面量不含 `updated_at` 键 ✓(仅注释行提及)
- **自动验证**: `node -e ...` 9 个 regex 检查 + 0 路径前缀检查 + 双保险解包计数 = 2 → 退出码 0打印 `OK`
### Task 2lib/api/index.ts 末尾追加具名 re-export
- **状态**: ✅ 完成
- **Commit**: `c072bbe`(父级 Lila-Server 仓库)
- **文件**: `lib/api/index.ts`修改197 → 204 行,+7 行内容diff 显示 8 insertions 含末尾 newline 重排)
- **追加位置**: `handleApiError` 函数定义L191-196之后L197 空行 + L198-204 新增块)
- **追加内容**7 行):
```typescript
// 凭据槽位Milestone v1.0 通用凭据槽位前端集成 — Phase 1 / CRED-FE-01
export {
getCredentialSlot,
updateCredentialSlot,
type CredentialSlot,
type CredentialSlotUpdatePayload,
} from './credential-slot'
```
- **关键串命中**:
- `from './credential-slot'`1 次
- `getCredentialSlot,`1 次
- `updateCredentialSlot,`1 次
- `type CredentialSlot,`1 次
- `type CredentialSlotUpdatePayload,`1 次
- `export * from './credential-slot'`(错误的 barrel 风格):**0 次** ✓
- **现有内容保留**: `import * as client from "./client"``export * from "./card"/"./upload"/"./food"``usersApi``rolesApi``handleApiError` 全部不变
- **自动验证**: `node -e ...` 5 个 regex 计数检查 + barrel 风格反向检查 + card 导出保留检查 + handleApiError 保留检查 → 退出码 0打印 `OK`
## 累计 Commit 列表
| # | Hash | Message | Files |
|---|------|---------|-------|
| 1 | `a0d0b9c` | feat(01-01): 新建 lib/api/credential-slot.ts 凭据槽位 API 客户端 | qy-lty-admin/lib/api/credential-slot.ts |
| 2 | `c072bbe` | feat(01-01): lib/api/index.ts 末尾追加凭据槽位具名 re-export | qy-lty-admin/lib/api/index.ts |
(最终 SUMMARY + STATE 提交另行追加,见底部)
## Success Criteria 自检
- [x] `lib/api/credential-slot.ts` 文件存在
- [x] 文件导出 `CredentialSlot` 类型 + `CredentialSlotUpdatePayload` 类型 + `getCredentialSlot` 函数 + `updateCredentialSlot` 函数共 4 个公共符号
- [x] `mapBackendCredentialSlot` 函数已定义(私有,未导出)
- [x] GET / PUT 路径**精确**为 `/v1/admin/credential-slot/`(不含重复 `/api`
- [x] GET 与 PUT 函数体内各含 1 次 `response.data?.data || response.data` 双保险解包(共 2 次)
- [x] PUT body 字面量**不含** `updated_at`
- [x] `lib/api/index.ts` 末尾通过具名 re-export 暴露 4 个符号,路径 `./credential-slot`
- [x] `lib/api/index.ts` 中现有 `export * from "./card"/"./upload"/"./food"``usersApi` / `rolesApi` / `handleApiError` 完全不变
- [x] 两个文件均为合法 UTF-8无 BOM 干扰、无残缺字符)
## Deviations from Plan
**无** — plan 执行 0 偏差。所有锁定写法路径、解包行、PUT body 不含 updated_at、私有 adapter、具名 re-export 风格、追加位置)均严格按 PLAN action 落地。
## 与后续 plan 的衔接
- **Plan 01-02**(同一 phase将处理`docs/修改记录.md` 顶部追加 Phase 1 条目 + 跑双重验证(`npm run lint` + `npx tsc --noEmit`+ 探针验证 barrel 入口
- **Phase 2** 起可用 `import { getCredentialSlot, updateCredentialSlot, type CredentialSlot, type CredentialSlotUpdatePayload } from '@/lib/api'` 直接消费本 plan 产物
- **本 plan 不写 `docs/修改记录.md`** — 集中由 Plan 01-02 落地,避免 Phase 1 内部多次写入
## Known Stubs
无 — 本 plan 是纯 API 客户端层所有产物类型、adapter、API 函数)都已完整实现并可直接消费。无任何占位或 TODO。
## 字节级关键产物对比
| 文件 | 状态 | 行数(前→后) | 字节 |
|------|------|--------------|------|
| `lib/api/credential-slot.ts` | 新增 | 0 → 64 | 2620 |
| `lib/api/index.ts` | 修改 | 197 → 204 | (追加 7 行) |
## Self-Check: PASSED
- [x] `lib/api/credential-slot.ts` 存在 (FOUND)
- [x] `lib/api/index.ts` 末尾包含具名 re-export 块 (FOUND)
- [x] commit `a0d0b9c` 在 git log 中 (FOUND, 父级 Lila-Server 仓库)
- [x] commit `c072bbe` 在 git log 中 (FOUND, 父级 Lila-Server 仓库)
- [x] Task 1 / Task 2 verify.automated 命令均退出码 0 + 打印 `OK`
---
*生成时间2026-05-08*
*执行 Agentgsd-executor (Opus 4.7)*
*父仓库 commit hasha0d0b9c (Task 1) / c072bbe (Task 2)*