Archived
文档:前端对接说明与 API 文档(鉴权 / 人员 / 角色→端);计划加 Task 12 按契约核对两份文档
前端对接说明:要改哪些文件、/api/me 与端、401/403/410 处置、SSE 与下载的 60s 票据、 运营中心员工页的新接口、接入完成判据。API 文档:通则与状态码、每条新接口的出入参与示例、 退役八条的替代、全部 95 条契约路由的端归属总表、schema 附录、curl 实录。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015kAzS35kMnZNt4YGgJXtfw
This commit is contained in:
@@ -55,6 +55,7 @@
|
||||
| 数据 | `db/migrations/0032_keycloak_identity.sql` + `meta/_journal.json` | 见 spec §6 |
|
||||
| 测试 | `org/__tests__/{claims,ends,roster-diff,test-account,keycloak-token-verifier,keycloak-admin-roster,org-service}.test.ts`;`app/__tests__/{route-registry,ticket,auth-middleware,config-keycloak,keycloak-check}.test.ts`;`app/__tests__/route-coverage.it.test.ts`;三份既有 `*.it.test.ts` 改造人 | |
|
||||
| 工具 / 文档 | `tools/e2e-a2.mjs`;`docs/adr/ADR-012-keycloak-identity.md`;`docs/编码规范.md`;`docs/debt.md`;`CLAUDE.md`;`api-report/` | |
|
||||
| 对外文档(已随设计写好,Task 12 核对) | `docs/前端对接说明-统一鉴权中心.md`;`docs/API文档-鉴权与人员.md` | 前端要改什么、后端给了什么;接口人话版 + 全部路由归属总表 |
|
||||
|
||||
---
|
||||
|
||||
@@ -4857,6 +4858,13 @@ git commit -m "e2e:改用 Keycloak 服务账号 client_credentials 拿 token,4
|
||||
绝不派到用户工作台;测完 `pnpm --filter @app/api reset-runs` 清运行数据(清跑出来的,留配置好的)。
|
||||
```
|
||||
|
||||
「文档权威地图」表在 `docs/debt.md` 那一行之前加两行:
|
||||
|
||||
```markdown
|
||||
| | `docs/前端对接说明-统一鉴权中心.md` | **前端接统一鉴权中心照这份**(ADR-012):要改哪些文件、`/api/me` 与端、401/403/410、SSE 与下载的票据 |
|
||||
| | `docs/API文档-鉴权与人员.md` | 鉴权 / 人员 / 角色→端接口的人话版 + 全部路由归属总表;契约不一致时以 `packages/contracts` 为准 |
|
||||
```
|
||||
|
||||
现状快照列表末尾(`## 文档权威地图` 之前)加一条:
|
||||
|
||||
```markdown
|
||||
@@ -4885,9 +4893,83 @@ git commit -m "文档:ADR-012 统一鉴权中心;编码规范鉴权定调改为
|
||||
|
||||
---
|
||||
|
||||
### Task 12: 前端对接说明与 API 文档 —— 与真实契约核对
|
||||
|
||||
两份文档是设计阶段按 spec 写好的(`docs/前端对接说明-统一鉴权中心.md` `docs/API文档-鉴权与人员.md`),
|
||||
实现落地后要用**契约本身**把它们核一遍:路由归属总表由契约重新生成、schema 字段名逐个对、示例尽量换成真实响应。
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/API文档-鉴权与人员.md`(§6 总表整段重生成;§7 字段核对;§8 示例)
|
||||
- Modify: `docs/前端对接说明-统一鉴权中心.md`(§2 文件指针、§7 接口名核对)
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes:`packages/contracts`(Task 1 之后每条路由都有 `access`)、`GET /api/me` 等已实现的路由(Task 8)。
|
||||
|
||||
- [ ] **Step 1: 用契约重新生成路由归属总表**
|
||||
|
||||
```bash
|
||||
node --experimental-strip-types --input-type=module -e "
|
||||
const m = await import('./packages/contracts/src/index.ts');
|
||||
const isRoute = (v) => v !== null && typeof v === 'object' && typeof v.method === 'string' && typeof v.path === 'string' && 'access' in v && 'output' in v;
|
||||
const order = ['public', 'any', 'workbench', 'design', 'ops', 'design|ops'];
|
||||
const rows = Object.entries(m).filter(([, v]) => isRoute(v)).map(([name, r]) => ({ access: Array.isArray(r.access) ? r.access.join('|') : r.access, method: r.method, path: r.path, name }));
|
||||
rows.sort((a, b) => order.indexOf(a.access) - order.indexOf(b.access) || a.path.localeCompare(b.path) || a.method.localeCompare(b.method));
|
||||
console.log('| access | 方法 | 路径 |');
|
||||
console.log('|---|---|---|');
|
||||
for (const r of rows) console.log('| \`' + r.access.replace('|', '\\\\|') + '\` | ' + r.method + ' | \`' + r.path + '\` |');
|
||||
console.log('\n计数:' + rows.length + ' 条契约路由');
|
||||
" > /tmp/route-access-table.md
|
||||
wc -l /tmp/route-access-table.md
|
||||
```
|
||||
Expected:打印 95 行路由 + 计数 `95 条契约路由`。若 `node --experimental-strip-types` 在本机版本上不认 `satisfies`(Node < 22.7),改用 `pnpm vitest run` 跑一个临时测试文件把同一段代码 `console.log` 出来,跑完删掉临时文件。
|
||||
|
||||
- [ ] **Step 2: 对 §6 总表**
|
||||
|
||||
把生成表与 `docs/API文档-鉴权与人员.md` §6 逐行比:方法 / 路径 / access 三者任一不同 → 以生成表为准改文档;
|
||||
文档里手写的备注(「定义归设计」「可用票据」等)保留。SSE `GET /api/stream` 不在契约里(`EXTRA_ROUTES`),文档单列一行。
|
||||
最后一行「计数」按生成结果改。
|
||||
|
||||
- [ ] **Step 3: 对 §7 字段名**
|
||||
|
||||
```bash
|
||||
grep -n "^export const \(staffSchema\|principalSchema\|roleAccessSchema\|rosterSyncOutcomeSchema\|inFlightWorkSchema\|workGroupSchema\|positionSchema\)" packages/contracts/src/org.ts
|
||||
grep -n "API_ERROR_CODES" -A 10 packages/contracts/src/api/http.ts | head -12
|
||||
```
|
||||
逐个 schema 打开看字段名与可选性,与文档 §7 的 TS 形状对齐(字段多了补、少了删、可选性以契约为准)。
|
||||
|
||||
- [ ] **Step 4: 示例换成真实响应(Keycloak 侧前置 1 与 4 就位时做;未就位就跳过并在文档末尾注明「示例按契约构造」)**
|
||||
|
||||
```bash
|
||||
export KEYCLOAK_URL=https://keycloak.akhr-ai.com KEYCLOAK_REALM=corp KEYCLOAK_CLIENT_ID=api-gateway
|
||||
pnpm dev:api & # 另开终端亦可;启动日志须见「✓ Keycloak 连通」
|
||||
TOKEN=$(curl -s -X POST "$KEYCLOAK_URL/realms/corp/protocol/openid-connect/token" -d grant_type=client_credentials -d client_id="$KEYCLOAK_E2E_CLIENT_ID" -d client_secret="$KEYCLOAK_E2E_CLIENT_SECRET" | jq -r .access_token)
|
||||
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8787/api/me | jq .
|
||||
curl -s http://localhost:8787/api/me | jq .
|
||||
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8787/api/auth/ticket | jq .
|
||||
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8787/api/gov/access/roles | jq .
|
||||
curl -s -X POST http://localhost:8787/api/login | jq .
|
||||
```
|
||||
把上面五个响应原样贴进文档 §1 / §4 / §5 / §8(sub、邮箱按真实值;secret 一个字都别贴)。
|
||||
|
||||
- [ ] **Step 5: 核前端对接说明里的文件指针**
|
||||
|
||||
```bash
|
||||
for f in shared/typed-fetch.ts app/main.tsx features/auth/internal/LoginPage.tsx shared/gov/surfaces.ts app/gov-shell.tsx app/app-shell.tsx app/subscribe.ts features/desk-task/internal/ViewRegion.tsx features/desk-task/internal/FileViewer.tsx shared/gov/gov-api.ts; do test -f "apps/web/src/$f" && echo "ok $f" || echo "MISSING $f"; done
|
||||
```
|
||||
Expected:全部 `ok`;有 `MISSING` 就改文档 §2 的指针(web 本轮没动,理论上不会)。§7 里提到的接口名与 §6 总表逐个对一遍。
|
||||
|
||||
- [ ] **Step 6: 提交**
|
||||
|
||||
```bash
|
||||
git add 'docs/API文档-鉴权与人员.md' 'docs/前端对接说明-统一鉴权中心.md'
|
||||
git commit -m "文档:前端对接说明与 API 文档按真实契约核对(路由归属总表由契约生成,示例换真实响应)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 自审记录(写完计划后对着 spec 过的一遍)
|
||||
|
||||
- **spec 覆盖**:§4 契约 → Task 1;§5.3 domain → Task 2;§5.5 两个 adapter → Task 3 / 4;§9.1 注册表 → Task 5;§9.3 票据 → Task 6;§9.2 中间件 → Task 7;§5.1 / 5.2 / 5.4 / 5.5 仓储与用例、§6 迁移、§11 连带改动(container / engines / message / 路由 / it-tests / tsup / package.json)→ Task 8;§10.2 启动自检 + §9.1 覆盖测试 → Task 9;§11 e2e → Task 10;§11 文档 + §13 债 → Task 11。§10.3 Keycloak 侧前置是管理员的事,写在 ADR-012「后果」与 Task 10 的说明里。
|
||||
- **spec 覆盖**:§4 契约 → Task 1;§5.3 domain → Task 2;§5.5 两个 adapter → Task 3 / 4;§9.1 注册表 → Task 5;§9.3 票据 → Task 6;§9.2 中间件 → Task 7;§5.1 / 5.2 / 5.4 / 5.5 仓储与用例、§6 迁移、§11 连带改动(container / engines / message / 路由 / it-tests / tsup / package.json)→ Task 8;§10.2 启动自检 + §9.1 覆盖测试 → Task 9;§11 e2e → Task 10;§11 文档 + §13 债 → Task 11;用户追加的前端对接说明与 API 文档(设计阶段已写)→ Task 12 用契约核对。§10.3 Keycloak 侧前置是管理员的事,写在 ADR-012「后果」与 Task 10 的说明里。
|
||||
- **类型一致性**:`OrgService` 的方法名在 Task 8 A2(契约)、A7(实现)、B3–B9(调用方)一致:`authenticate` `principalOf` `registerIdentity` `listStaff` `getStaff` `staffOfPosition` `setPosition` `syncRoster` `inFlightOf` `listRoleAccess` `setRoleAccess` `deleteRoleAccess` `staffWithEnd` `saveWorkGroup` `workGroupOf` `bindingOf` `ensureSeeded`;`Authenticator`(Task 7)是它的子集;`StaffRepository` 无 `findByAccount`(A3 / A6 / A7 / A1 的内存实现一致);`createContainer(config, overrides)`(B3)与 Task 9 的 it-test 调用一致;`EXTRA_ROUTES` / `appRouteRegistry`(B4)与 Task 9 覆盖测试一致。
|
||||
- **占位扫描**:无 TBD / TODO;每个改动步骤都给了代码或精确的替换文本;脚本化改写(B7 / B8)带断言与验收 grep。
|
||||
- **已知取舍**:Task 8 是一次提交(中间态 typecheck 必红,不拆);it-tests 与路由覆盖测试需要 PG,没库时自报跳过。
|
||||
|
||||
Reference in New Issue
Block a user