lty/qy-lty-admin/docs/修改记录.md
pmc 2be1f1d505 docs(02-02): docs/修改记录.md 顶部追加 Phase 2 条目
- 在 line 26 锚点注释之后、Phase 1 条目之前插入 [2026-05-08] Phase 2 条目
- 完整 7 字段结构:元信息行(配套服务端 Phase + 覆盖前端需求 CRED-FE-02/03)+ 文件路径 + 修改类型 + 修改内容 + 修改原因 + 跨项目联动 + 服务端联动
- 跨项目联动字段逐字与 02-CONTEXT.md 锁定一致(无新跨项目契约 / 后端 46d72b8 互引仍有效 / Phase 3 再评估)
- 纯追加 +32 行,0 删除,line 1-26 头部完全不变
- Phase 1 及更早历史条目逐字未动
2026-05-08 11:50:13 +08:00

134 lines
12 KiB
Markdown
Raw 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.

# 管理后台前端代码修改记录
本文档记录每次对管理后台前端(**qy-lty-admin**Next.js + React代码的修改方便追踪变更历史。
> **范围说明**:本仓库与服务端 [qy_lty](../../qy_lty/) 是独立项目,各自维护独立的修改记录。**仅记录本仓库(管理后台前端)改动**;跨项目联动改动需在 [qy_lty/docs/修改记录.md](../../qy_lty/docs/修改记录.md) 同期写一条相互引用的条目。
---
## 修改格式说明
每次修改按以下格式记录:
```
### [日期] 修改简述
- **文件路径**: 相对于项目根目录的文件路径
- **修改类型**: 新增 / 修改 / 删除 / 重构 / 修复Bug
- **修改内容**: 具体修改了什么
- **修改原因**: 为什么要做这个修改
```
---
## 修改历史
<!-- 新的修改记录添加在此处下方,最新的在最前面 -->
### [2026-05-08] Phase 2前端RBAC 收敛 + AI 模型页凭据槽位入口
配套服务端 Phase本 phase **不**触达服务端;与服务端 v1.0 Phase 2「管理端读写接口」commit `46d72b8` 既有契约保持兼容(不引入新契约)
覆盖前端需求CRED-FE-02、CRED-FE-03
- **文件路径**
- `lib/permissions.ts`(修改)
- `app/ai-model/page.tsx`(修改)
- **修改类型**: 修改(前端 RBAC 矩阵扩展 + 页面入口控件 + 占位 Dialog纯前端无新依赖、不动 lockfile
- **修改内容**:
- `lib/permissions.ts`
- `PermissionModule` union 末尾追加 `"credential-slot"`,扩为 14 项
- `PERMISSION_MATRIX["超级管理员"]` 数组末尾追加 `"credential-slot"`
- `PERMISSION_MATRIX["AI模型管理员"]` 数组末尾追加 `"credential-slot"`
- 其他 4 个角色(内容管理员 / 卡牌管理员 / 查看者 / 管理员)数组**逐字不变**
- `getModuleFromPath` 函数体完全不动(凭据槽位是 `/ai-model` 子能力,不占独立路由)
- 顶部「权限矩阵对照表」注释新增一行「凭据槽位」与代码同步
- `app/ai-model/page.tsx`
- 文件 line 1 顶部新增 `"use client"` 指令,从 Server Component 转为 Client Component
- 新增 import`useState` / `useEffect`react+ `Dialog` / `DialogContent` / `DialogDescription` / `DialogHeader` / `DialogTitle`@/components/ui/dialog+ `KeyRound`lucide-react+ `hasPermission`@/lib/permissions
- 函数体顶部新增 `mounted` + `isCredentialDialogOpen` 两个 `useState` + 1 个 `useEffect``mounted` 为 true复用 `components/sidebar.tsx` mounted 守卫模式避免 SSR 水合不匹配)
- `DashboardHeader` 内部用 `<div className="flex items-center gap-2">` 包两个 Button保留原有「添加新模型」+ 新增 `{mounted && hasPermission("credential-slot") && <Button variant="outline" onClick={() => setIsCredentialDialogOpen(true)}><KeyRound /> 凭据槽位</Button>}`
- `</Tabs>` 之后、`</DashboardShell>` 之前新增 controlled mode `<Dialog open={isCredentialDialogOpen} onOpenChange={setIsCredentialDialogOpen}>`,内含 `DialogTitle`「通用凭据槽位」+ `DialogDescription`「对话框真实内容由 Phase 3 落地」(占位,无表单)
- Tabs / TabsContent / Card / 卡片内的现有按钮等所有内容line 18-441逐字不变
- **修改原因**:
- 推进 Milestone v1.0「通用凭据槽位前端集成」第二步:让授权运营立即看到入口(已就位的 UX 收敛未授权角色彻底看不到DOM 中完全不存在的安全前置)
- 沿用 RBAC 单一来源原则(`lib/permissions.ts:hasPermission`+ shadcn Dialog primitive不重复造轮子
- 为 Phase 3 真实表单CRED-FE-04 + CRED-FE-05预留 Dialog 挂载点Dialog 用 controlled mode 让 Phase 3 可在打开瞬间触发 `getCredentialSlot()`
- 注意:前端 RBAC 仅是 UI 礼貌,最终安全闭环依赖后端 `/v1/admin/credential-slot/` 的 admin 鉴权PERM-06 / `qy_lty` 后端);本 phase 不消化该闭环
- **跨项目联动**: 无 — Phase 2 是纯前端 RBAC + UI 入口落地,不引入新跨项目契约;后端 commit 46d72b8 已建立的互引仍有效Phase 3 引入实质 PUT 调用时若涉及新契约再评估
- **服务端联动**: 同上「跨项目联动」字段;后端 commit `46d72b8` 已建立互引闭环,本 phase 无需再次互引
### [2026-05-08] Phase 1前端凭据槽位 API 客户端
配套服务端 Phase[../qy_lty/.planning/phases/02-admin-rest/](../../qy_lty/.planning/phases/02-admin-rest/)已落地commit 46d72b8
覆盖前端需求CRED-FE-01
- **文件路径**
- `lib/api/credential-slot.ts`(新增)
- `lib/api/index.ts`(修改)
- **修改类型**: 新增API 客户端层;纯逻辑,无 UI 改动)
- **修改内容**:
- 新建 `lib/api/credential-slot.ts`,封装:
- 类型 `CredentialSlot { appId, accessTokenMasked, updatedAt }`(脱敏掩码语义命名)
- 类型 `CredentialSlotUpdatePayload { appId, accessToken }`(明文语义命名)
- 适配器 `mapBackendCredentialSlot()`snake_case → camelCase
- API 函数 `getCredentialSlot()` / `updateCredentialSlot(payload)`,分别走 `apiClient.get` / `apiClient.put` 命中 `/v1/admin/credential-slot/`,沿用仓库统一的 `response.data?.data || response.data` 双保险解包PUT body 仅传 `{ app_id, access_token }`,不携 `updated_at`(与 `updateAiModel` / `updateOutfit` 风格一致)
- `lib/api/index.ts` 末尾追加具名 re-export让组件层可 `import { getCredentialSlot, type CredentialSlot } from '@/lib/api'`
- **修改原因**:
- 启动 Milestone v1.0「通用凭据槽位前端集成」,本 phase 为后续 Phase 2RBAC + 入口控件、Phase 3编辑对话框 + Sonner 反馈)提供调用层基础
- `accessTokenMasked` vs `accessToken` 故意命名不同,让 TS 编译期捕捉「把脱敏字符串当真值回写 PUT」的 bug
- **跨项目联动**: 无 — 后端 commit 46d72b8 已建立互引Phase 1 是纯 API client 层落地(无 UI 改动),调用的后端接口由 qy_lty 后端 Milestone v1.0 Phase 2 提供commit `46d72b8` 已建立前后端互引修改记录);本 phase 不引入新跨项目代码契约,无需再次互引。前端 UI 集成Phase 2 + 3引入实质用户能力时再评估是否需要新一轮互引
- **服务端联动**: 同上「跨项目联动」字段;后端 commit `46d72b8` 已建立互引闭环,本 phase 无需再次互引
### [2026-05-07] Phase 2 — 锁定后端通用凭据槽位 REST 接口契约(消费方文档化)
配套服务端 Phase[../qy_lty/.planning/phases/02-admin-rest/](../../qy_lty/.planning/phases/02-admin-rest/)
覆盖服务端需求CRED-03 + CRED-04本仓库消费方
- **文件路径**: `docs/修改记录.md`(仅文档新增条目;本仓库代码未改)
- **修改类型**: 新增(文档)
- **修改内容**:
- 文档化服务端在本日落地的 `/api/v1/admin/credential-slot/` REST 接口契约GET 脱敏读取 + PUT 全字段覆写 + admin token 鉴权),为后续 Web 管理后台前端(本仓库)的 CRED-FE-* phase 写 API client 与表单 UI 留下契约锚点
- 接口契约要点(消费方视角):
- URL`{NEXT_PUBLIC_API_BASE_URL}/v1/admin/credential-slot/`
- 鉴权:`Authorization: Bearer <admin_token>`(来源于 `/api/v1/admin/login/` 的现有 admin 登录返回值)
- GET 响应:`{ success: boolean, code: number, message: string, data: { app_id: string, access_token: string /* 末 4 位脱敏掩码,前缀 * */, updated_at: string /* ISO 8601 */ } }`
- PUT 请求体:`{ app_id?: string, access_token?: string }`(任一字段缺省时由后端兜底保留原值;写入是全字段覆写语义,建议前端 UI 始终提交两字段全集以避免歧义)
- PUT 响应同 GET 形态access_token 同样脱敏返回,前端**不应**用响应值回填明文输入框)
- 错误矩阵401无 token / token 失效、403持非 admin tokenmessage 含"需要管理员权限"、400参数无效
- **修改原因**:
- 服务端首次为本管理后台暴露受控的凭据读写接口;本仓库即将启动 CRED-FE-01API client + CRED-FE-02表单录入页面等 phase先把后端契约固化进本仓库修改记录便于反查
- 文档化"GET 与 PUT 响应均脱敏 access_token"避免前端工程师误以为可以从响应回填明文表单(实际明文仅存于 DB任何回填只能保留掩码或要求运营重新输入
- **服务端联动**: 后端联动条目 [../qy_lty/docs/修改记录.md](../../qy_lty/docs/修改记录.md) 同期 `[2026-05-07] Phase 2 — 管理端通用凭据槽位 REST 接口GET 脱敏 / PUT 覆写)`。本仓库代码未改,仅文档侧做契约固化;待本仓库 CRED-FE-01 phase 启动落地 API client + Hook 时再补一条独立条目并互引
### [2026-05-07] 修复 NEXT_PUBLIC_API_BASE_URL 注入时机错误(线上登录 Network Error
- **文件路径**:
- `qy-lty-admin/Dockerfile`
- `.gitea/workflows/deploy.yaml`(仓库根目录,与本前端构建/部署链直接相关)
- `k8s/admin-deployment-prod.yaml`(仓库根目录,与本前端构建/部署链直接相关)
- **修改类型**: 修复Bug
- **修改内容**:
- `qy-lty-admin/Dockerfile`:在 `COPY . .` 之后、`RUN yarn build` 之前新增 `ARG NEXT_PUBLIC_API_BASE_URL``ENV NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}`,让该值在 build 期可被注入
- `.gitea/workflows/deploy.yaml`:在 admin 镜像 `docker build` 命令上加 `--build-arg NEXT_PUBLIC_API_BASE_URL=https://${DOMAIN_API}/api`test 环境拼成 `https://qy-lty.test.airlabs.art/api`prod 环境拼成 `https://qy-lty.airlabs.art/api`);同时删除 `Replace domain placeholders by environment` 段中已失效的 `sed -i "s|https://qy-lty.airlabs.art|https://${DOMAIN_API}|g" k8s/admin-deployment-prod.yaml`
- `k8s/admin-deployment-prod.yaml`:删除运行时无效的 `env: NEXT_PUBLIC_API_BASE_URL=https://qy-lty.airlabs.art`,改为注释说明该变量必须在 docker build 时通过 `--build-arg` 注入
- **修改原因**:
- 线上 https://qy-lty-admin.test.airlabs.art/login 点登录弹 "Network Error"。DevTools 抓到 Request URL 是 `http://localhost:8000/api/v1/admin/login/`,对应 `lib/api/client.ts``process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000/api"` 的 fallback 值。
- 根因Next.js 的 `NEXT_PUBLIC_*` 变量在 `next build` 时被静态编译进客户端 JS 包,运行时再设置容器环境变量已经无效。原 Dockerfile 没有 `ARG/ENV`,原 deploy.yaml 没有 `--build-arg`,只有 `k8s/admin-deployment-prod.yaml` 在容器运行时设了变量——所以打包出的镜像里硬编码的是默认 fallback `http://localhost:8000/api`,前端 HTTPS 页面去打本机的 8000 端口,浏览器报 `ERR_CONNECTION_REFUSED`axios 包装为 "Network Error"。
- 修复后构建期会把正确的 `https://qy-lty.test.airlabs.art/api` / `https://qy-lty.airlabs.art/api` 编译进 JS 包,登录请求会正确打到后端。
- 备注:原 `k8s/admin-deployment-prod.yaml` 写的是 `https://qy-lty.airlabs.art`**缺少 `/api` 后缀**),即便注入时机正确,路径也会拼错(`/v1/admin/login/` 而非 `/api/v1/admin/login/`),双重 bug。本次修复一并纠正。
- **服务端联动**: 本次修复仅涉及前端构建链与部署配置,未改动 `qy_lty` 后端代码,无需在服务端写联动条目。
---
### [2026-04-30] 初始化 CLAUDE.md 与 docs/修改记录.md 骨架
- **文件路径**: `CLAUDE.md``docs/修改记录.md`
- **修改类型**: 新增
- **修改内容**:
- 新建 `CLAUDE.md`写明项目身份Next.js 15 App Router + React 19 后台)、技术栈、路由分组、鉴权与权限模型、与 `qy_lty` 后端的依赖关系,并嵌入"项目修改记录规则(重要 — 自动执行)"段落
- 新建本文件 `docs/修改记录.md`:以 `qy_lty/docs/修改记录.md` 同名文件为骨架,划清"仅记录管理后台前端改动"的边界
- **修改原因**:
- 此前本仓库无 CLAUDE.md 和修改记录文档,新会话进入工作时缺乏上下文锚点;与服务端 `qy_lty` 项目的改动追踪互不可见
- 配套服务端 `qy_lty/CLAUDE.md` 同日新增的"项目修改记录规则"段落,要求两端各自独立维护修改记录、跨项目联动改动相互引用,从源头避免漏记和混记
- 服务端同期条目:[qy_lty/docs/修改记录.md](../../qy_lty/docs/修改记录.md) 2026-04-30 "CLAUDE.md 新增项目修改记录规则段落"