build-flow.api/docs/superpowers/specs/2026-07-20-react-pro-integration-foundation-design.md
2026-07-20 08:34:52 +08:00

317 lines
16 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.

# React Pro 全面对接第一阶段:工程基线与 API 契约设计
## 1. 背景
BuildFlow 当前有三个与本设计直接相关的代码库:
- `build-flow.admin`:Ant Design Pro 6、Umi Max 4、React 19、antd 6。当前仍以模板页面为主,仅初步接入登录、当前用户和动态菜单。
- `build-flow.api`:.NET 10、FastEndpoints,所有业务 HTTP 入口位于 `BuildFlow.Host`。当前 Swagger 暴露 173 个路径,Host 下约有 223 个 Endpoint。
- `build-flow.web`:旧 Vue/Vben 前端。它不是新前端的代码来源,而是现有用户功能、字段映射和交互行为的验收参考。
后端当前动态菜单包含 30 个路由节点,权限接口返回 83 个权限码,业务横跨 Platform、File、RAG、Workflow、Scripting、IM、Monitoring 七个域。全面迁移必须先建立稳定的工程基线、可重复的 API 契约生成方式和统一传输层,否则逐页面迁移会持续产生接口漂移。
## 2. 本阶段目标
本阶段交付后,React Pro 应具备后续业务模块可直接复用的基础设施:
1. 前端依赖安装、类型检查、代码检查、单元测试和生产构建都有确定且可重复的命令。
2. 后端 Swagger 可匿名读取,并能生成提交到仓库的 OpenAPI 快照。
3. BuildFlow API 类型和普通 HTTP 客户端由 OpenAPI 生成,生成目录禁止手工编辑。
4. 登录、当前用户、用户菜单、权限码和刷新令牌使用统一的 BuildFlow 传输层。
5. JSON 信封、纯文本刷新、文件下载、上传、SSE 和 SignalR 有明确且互不干扰的处理边界。
6. 建立 API 合约测试、传输层单元测试、真实后端冒烟测试和浏览器登录冒烟测试。
7. 提供可以重复执行的第一阶段 SOP,并作为后续每个业务域的共同验收前置条件。
本阶段不实现用户、角色、文件、知识库、工作流等完整业务页面;这些页面在后续阶段依次交付。但本阶段必须把登录后的菜单、用户状态和权限状态加载通,证明基础链路真实可用。
## 3. 已确认的现状与问题
### 3.1 前端基线
- `npm run build` 当前可以生成 `dist`。
- `npm run lint` 当前失败:根 `tsconfig.json` 把独立的 `cloudflare-worker` 包含进主应用类型检查,同时缺少 `geojson` 类型声明。
- Jest 当前无法启动:`jest.config.ts` 直接读取 `node_modules/@utoo/pack/package.json`,但该包不是根项目的可靠直接依赖。
- `CLAUDE.md` 规定使用 npm 和 `package-lock.json`,实际目录却只有 `pnpm-lock.yaml` 和一个不含 package 列表的 `pnpm-workspace.yaml`。
- `config/oneapi.json` 仍是 Ant Design Pro 示例,仅有 7 条 demo 路径。
- `src/services/ant-design-pro/` 是生成目录,但登录和菜单代码已被手工改写,违反项目自身的生成代码规则。
- 当前没有自动刷新 access token 的实现。
### 3.2 后端基线
- `dotnet build BuildFlow.slnx --no-restore -m:1` 当前通过,0 warning、0 error。
- 完整测试基线为 769 通过、26 失败。其中 23 个失败与本地 MongoDB 未启动有关,另外 3 个分别来自 RAG 测试依赖注册和 Scripting 断言。
- `/swagger/v1/swagger.json` 当前被全局 fallback authorization 拦截,匿名请求返回 401;携带 admin token 后可读取 173 条路径和 312 个 schema。
- `POST /api/auth/login` 成功响应为 `{ code: 0, message: "ok", data: ... }`。
- 当前运行实例对空登录请求返回 HTTP 500 `internal_error`,而预期应是 HTTP 400 `invalid_param`。
- `/api/auth/refresh` 使用 HttpOnly `jwt` Cookie,成功时返回 `text/plain` access token,并轮换 refresh token。
## 4. 方案选择
采用“契约驱动、手写特殊协议适配器”的混合方案:
- 普通 JSON CRUD 和查询接口:从提交到仓库的 OpenAPI 快照生成类型和请求函数。
- 认证状态、刷新重放、错误映射:使用手写传输层,因为它们是跨接口策略,不能由生成器正确表达。
- 文件上传、下载、SSE、SignalR:使用手写适配器,因为这些接口不遵循普通 JSON 信封生命周期。
- 页面只依赖域适配器,不直接依赖生成函数。后端字段或生成器变化只影响适配器,不扩散到页面。
不采用纯手工 service 方案,因为 173 条路径会造成长期类型漂移;不采用全元数据 CRUD 方案,因为文件、聊天、流程设计器和流式 AI 不适合统一 CRUD 抽象。
## 5. 目录与所有权
目标结构如下:
```text
build-flow.admin/
config/
openapi/
buildflow.json # 已校验并提交的后端契约快照
scripts/
sync-openapi.mjs # 从运行中的后端抓取、校验、规范化快照
check-openapi.mjs # 检查快照和生成结果是否过期
src/services/buildflow/
generated/ # OpenAPI 生成,禁止手工编辑
transport/ # token、信封、refresh、请求重放
adapters/ # auth/menu/access 以及后续各业务域适配器
download.ts # 二进制下载与文件名提取
upload.ts # 普通/分片上传公共能力
sse.ts # 流式请求公共能力
signalr.ts # Hub 连接公共能力
tests/e2e/ # Playwright 真实浏览器冒烟测试
```
约束:
- `src/services/ant-design-pro/` 恢复为模板生成目录,不再承载 BuildFlow 业务代码。
- 只有 `adapters` 可以直接导入 `src/services/buildflow/generated/`;页面只能调用 `adapters` 或特殊协议入口。
- OpenAPI 同步只更新快照,代码生成只更新 `generated`。两者都由脚本执行,禁止人工修改输出。
- 后端 HTTP 变更继续只发生在 `BuildFlow.Host`,跨模块协作遵循现有 MediatR CQRS 约束。
## 6. 工程基线设计
### 6.1 包管理器
以 `build-flow.admin/CLAUDE.md` 为项目规则来源,统一使用 npm:
1. 补齐根项目所有直接使用的依赖,生成并提交 `package-lock.json`。
2. 删除根目录的 `pnpm-lock.yaml` 和无有效 package 声明的 `pnpm-workspace.yaml`。
3. CI 和 SOP 统一使用 `npm ci`,不得混用 pnpm 或 yarn。
4. `cloudflare-worker` 继续作为独立应用维护自己的 `package.json` 和安装命令,不加入根应用类型检查或测试门槛。
### 6.2 TypeScript、Biome 和生成目录
- 根 `tsconfig.json` 只包含 React Pro 主应用、配置、脚本和测试所需类型,排除 `cloudflare-worker`、`dist`、`.umi`、`.umi-production` 和其他生成目录。
- 为直接导入的库补齐类型依赖,不使用全局 `declare module '*'` 掩盖真实类型问题。
- Biome 只检查主应用拥有的源码;独立 Worker 使用自己的检查命令。
- 保留严格 TypeScript,不通过关闭 `strict`、`noImplicitReturns` 或扩大 `skipLibCheck` 来换取通过。
### 6.3 Jest
- Jest 读取包版本时使用 Node 的模块解析结果,或把构建时直接引用的包声明为直接依赖,不假设 pnpm 的虚拟目录布局。
- 测试环境统一初始化 `localStorage`、`history`、`fetch` 和必要的浏览器 API。
- 网络单元测试使用可控 mock,不连接真实后端;真实后端测试单独放在 smoke/e2e 层。
### 6.4 版本控制基线
`build-flow.admin` 当前没有 `.git` 元数据。实施代码变更前必须:
1. 初始化本地 Git 仓库,但不创建远程仓库、不推送代码。
2. 确认 `node_modules`、`dist`、`.umi`、`.umi-production` 和本地环境文件已被忽略。
3. 把当前用户已有源码作为原样 baseline 提交,不重写、不清理模板内容。
4. 后续每个阶段使用 Conventional Commits 独立提交,确保能区分用户基线与对接改动。
## 7. OpenAPI 契约设计
### 7.1 Swagger 可访问性
`/swagger/**` 由 Swagger middleware 在 authentication、authorization 和 FastEndpoints 之前处理。只允许开放 API 文档,不放宽 `/api/**` 的默认拒绝策略,也不为业务 Endpoint 增加匿名例外。
验收请求:
```bash
curl -fsS http://localhost:5667/swagger/v1/swagger.json
```
必须返回 HTTP 200,根对象包含 `openapi`、`info`、`paths` 和 `components`。
### 7.2 快照同步
`npm run api:sync` 执行以下步骤:
1. 从 `BUILDFLOW_API_ORIGIN` 读取 `/swagger/v1/swagger.json`,默认 origin 为 `http://localhost:5667`。
2. 校验文档类型、版本、已知关键路径和 schema 数量。
3. 对 JSON 键排序并规范化格式,避免无意义 diff。
4. 原子替换 `config/openapi/buildflow.json`。
5. 拒绝 Ant Design Pro demo 路径成为 BuildFlow 契约来源。
关键路径至少包含:
- `/api/auth/login`
- `/api/user/info`
- `/api/menu/all`
- `/api/auth/codes`
- `/api/users`
- `/api/knowledge-bases`
- `/api/workflow-definitions`
- `/api/scripts`
- `/api/monitoring/systems`
刷新接口被后端有意排除在 Swagger 外,由手写传输层维护并由独立合约测试覆盖。
### 7.3 代码生成
`npm run api:generate` 只读取本地快照,因此生产构建和 CI 不依赖后端在线。生成结果写入 `src/services/buildflow/generated/`,使用独立命名空间,避免与模板的 `API` 全局命名空间冲突。
`npm run api:check` 在临时目录重新生成并比较结果;如果快照或生成代码过期,命令失败。`npm run build` 不应隐式联网或静默重写生成代码。
## 8. 请求与认证数据流
### 8.1 普通请求
```text
page -> domain adapter -> generated function -> BuildFlow transport
-> Authorization + locale + credentials
-> /api endpoint
-> HTTP/status + envelope validation
-> typed business data or normalized error
```
统一约定:
- API 基地址为 `/api`,开发环境由 Umi proxy 转发,生产环境使用同源反向代理。
- 请求携带 `Authorization: Bearer <accessToken>`、`Accept-Language` 和 Cookie credentials。
- 只有 `code === 0` 或 `code === "0"` 是成功信封。
- HTTP 2xx 但业务 code 非 0、HTTP 4xx/5xx、非 JSON 错误都转换为同一种 `BuildFlowRequestError`。
- 页面不解析原始 response 或信封,也不自行拼接错误提示。
### 8.2 登录与初始状态
1. 登录调用 `/auth/login`,成功后只持久化 access token;refresh token 由 HttpOnly Cookie 管理。
2. 登录完成后加载 `/user/info`、`/menu/all` 和 `/auth/codes`。
3. `getInitialState` 保存当前用户、菜单树和权限码集合。
4. `access.ts` 提供 `can(code)`、`canAny(codes)`、`canAll(codes)`,并保留模板 `canAdmin` 兼容入口。
5. 菜单转换器规范化相对路径、图标、隐藏规则和权限信息;页面路由仍必须在编译期 route registry 中存在。
### 8.3 刷新与请求重放
- 401 时只允许创建一个共享 refresh promise;并发失败请求等待同一个刷新结果。
- `/auth/login`、`/auth/refresh`、匿名分享接口和已经重放过一次的请求不得再次触发 refresh。
- refresh 成功后更新 access token,并各自重放原请求一次。
- refresh 失败时只执行一次清理和登录跳转,保留当前 URL 作为 redirect。
- 请求取消信号、上传进度和调用方 options 在重放时必须保留。
- 不得把 refresh token 写入 `localStorage`、日志、错误对象或前端状态。
### 8.4 特殊协议
- `/auth/refresh`:`text/plain`,不走 JSON 信封解包。
- 下载和预览:返回 Blob/stream,按 `Content-Disposition` 提取文件名,不走 JSON 解包。
- SSE:使用 fetch/readable stream,保留取消能力,不进入响应缓冲。
- SignalR:使用 Hub 自身握手和重连机制,access token 通过 token factory 提供。
- 上传:支持 `FormData`,不得手工设置 multipart boundary;分片上传复用统一重试和取消策略。
## 9. 后端契约修复
第一阶段只修改支撑所有前端模块的后端横切契约:
1. Swagger JSON 和 UI 匿名可读,业务 API 仍默认要求认证。
2. 登录请求缺字段或字段不合法时返回 HTTP 400,信封 code 为 `invalid_param`。
3. 用户名或密码错误时返回 HTTP 401,信封 code 为 `unauthorized`,不泄露具体失败字段。
4. 成功登录返回 HTTP 200、code 0、access token,并设置 HttpOnly `jwt` Cookie。
5. refresh 成功返回 HTTP 200 `text/plain` access token并轮换 Cookie;失败返回 HTTP 401 错误信封。
6. `/user/info`、`/menu/all`、`/auth/codes` 的匿名访问均返回 HTTP 401。
这些行为必须由 Host 集成测试证明,不能只依赖前端兼容代码。
## 10. 测试设计
### 10.1 前端单元测试
至少覆盖:
- token 读取、写入和清理。
- code 0 成功解包,字符串/数字错误码映射。
- JSON、纯文本、Blob 响应分流。
- 20 个并发 401 只产生一次 refresh 请求。
- refresh 成功后的单次重放和 refresh 失败后的单次登出。
- `currentUser`、menu、access code 字段适配。
- 相对菜单路径递归规范化。
- OpenAPI 快照关键路径校验和生成结果过期检查。
### 10.2 后端测试
新增 Host 合约测试,覆盖第 9 节全部行为。继续执行:
```bash
dotnet build BuildFlow.slnx --no-restore -m:1
dotnet test tests/BuildFlow.Architecture.Tests --no-build
dotnet test tests/BuildFlow.Platform.Tests --no-build
```
完整 `dotnet test BuildFlow.slnx --no-build -m:1` 也必须执行并记录。依赖 MongoDB 的测试通过测试基础设施启动对应服务,而不是永久跳过。现有 RAG 和 Scripting 的非基础设施失败要进入缺陷清单并在最终全量回归前清零。
### 10.3 API 冒烟测试
对真实本地后端执行:
- 匿名 Swagger。
- 登录成功、参数错误、凭据错误。
- 当前用户、菜单、权限码。
- refresh rotation 和旧 refresh token 失效。
- 未认证访问被拒绝。
冒烟脚本不得输出 token、Cookie 或连接串。
### 10.4 浏览器 E2E
Playwright 使用真实后端和测试账号验证:
1. 打开登录页。
2. 错误密码显示统一错误且不离开页面。
3. 正确登录进入用户 home path。
4. 页面显示真实用户信息和后端菜单。
5. 刷新页面仍保持会话。
6. 模拟 access token 失效后只刷新一次并恢复请求。
7. refresh 失败后跳回登录页并携带 redirect。
## 11. SOP 与命令门槛
第一阶段结束时,前端应提供以下稳定命令:
```bash
npm ci
npm run api:sync
npm run api:generate
npm run api:check
npm run lint
npm test -- --runInBand
npx antd lint ./src
npm run build
npm run test:smoke
npm run test:e2e
npm run verify
```
`npm run verify` 依次执行不依赖在线服务的契约检查、lint、单元测试和构建。真实 API smoke 与 E2E 由单独命令执行,要求后端和测试基础设施已启动。
后端运行端口以 `src/BuildFlow.Host/appsettings.json` 的 5667 为本地默认值。`run-dev.sh`、前端代理和文档必须统一,不再同时声明 5200 与 5667。
## 12. 验收标准
本阶段只有同时满足以下条件才算完成:
1. 全新目录执行 `npm ci` 成功,仓库只存在 npm 根锁文件。
2. React Pro 已建立保留原始现状的 Git baseline,阶段改动使用独立 Conventional Commits。
3. 前端 `lint`、Jest、antd lint、build 全部退出 0。
4. 匿名 Swagger 返回 200;OpenAPI 快照和生成代码检查通过。
5. BuildFlow 业务代码不再手工修改 `src/services/ant-design-pro/`。
6. 登录、当前用户、30 个菜单节点和 83 个权限码由真实后端成功加载。
7. 并发 401 单次刷新、单次重放和失败登出均有自动化测试。
8. 后端 build、Architecture tests、Platform tests 和新增 Host 合约测试全部通过。
9. API smoke 与 Playwright 登录链路全部通过,且日志不泄露凭据。
10. 第一阶段 SOP 在一台未使用当前 `node_modules` 的环境中可重复执行。
## 13. 后续阶段接口
后续每个业务域都复用本阶段的 transport、生成客户端、域适配器模式和测试门槛。业务阶段只能新增域适配器与页面,不得各自实现 token、refresh、信封、下载、SSE 或 SignalR 的第二套基础逻辑。
阶段顺序保持为:Platform -> File -> RAG -> Workflow/BuildFlow -> Scripting -> IM -> Monitoring -> 全量回归与发布 SOP。