docs: define react pro integration foundation
This commit is contained in:
parent
e549d3e3fb
commit
df0bc8ceed
@ -0,0 +1,316 @@
|
||||
# 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。
|
||||
Loading…
x
Reference in New Issue
Block a user