16 KiB
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 应具备后续业务模块可直接复用的基础设施:
- 前端依赖安装、类型检查、代码检查、单元测试和生产构建都有确定且可重复的命令。
- 后端 Swagger 可匿名读取,并能生成提交到仓库的 OpenAPI 快照。
- BuildFlow API 类型和普通 HTTP 客户端由 OpenAPI 生成,生成目录禁止手工编辑。
- 登录、当前用户、用户菜单、权限码和刷新令牌使用统一的 BuildFlow 传输层。
- JSON 信封、纯文本刷新、文件下载、上传、SSE 和 SignalR 有明确且互不干扰的处理边界。
- 建立 API 合约测试、传输层单元测试、真实后端冒烟测试和浏览器登录冒烟测试。
- 提供可以重复执行的第一阶段 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 400invalid_param。 /api/auth/refresh使用 HttpOnlyjwtCookie,成功时返回text/plainaccess token,并轮换 refresh token。
4. 方案选择
采用“契约驱动、手写特殊协议适配器”的混合方案:
- 普通 JSON CRUD 和查询接口:从提交到仓库的 OpenAPI 快照生成类型和请求函数。
- 认证状态、刷新重放、错误映射:使用手写传输层,因为它们是跨接口策略,不能由生成器正确表达。
- 文件上传、下载、SSE、SignalR:使用手写适配器,因为这些接口不遵循普通 JSON 信封生命周期。
- 页面只依赖域适配器,不直接依赖生成函数。后端字段或生成器变化只影响适配器,不扩散到页面。
不采用纯手工 service 方案,因为 173 条路径会造成长期类型漂移;不采用全元数据 CRUD 方案,因为文件、聊天、流程设计器和流式 AI 不适合统一 CRUD 抽象。
5. 目录与所有权
目标结构如下:
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:
- 补齐根项目所有直接使用的依赖,生成并提交
package-lock.json。 - 删除根目录的
pnpm-lock.yaml和无有效 package 声明的pnpm-workspace.yaml。 - CI 和 SOP 统一使用
npm ci,不得混用 pnpm 或 yarn。 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 元数据。实施代码变更前必须:
- 初始化本地 Git 仓库,但不创建远程仓库、不推送代码。
- 确认
node_modules、dist、.umi、.umi-production和本地环境文件已被忽略。 - 把当前用户已有源码作为原样 baseline 提交,不重写、不清理模板内容。
- 后续每个阶段使用 Conventional Commits 独立提交,确保能区分用户基线与对接改动。
7. OpenAPI 契约设计
7.1 Swagger 可访问性
/swagger/** 由 Swagger middleware 在 authentication、authorization 和 FastEndpoints 之前处理。只允许开放 API 文档,不放宽 /api/** 的默认拒绝策略,也不为业务 Endpoint 增加匿名例外。
验收请求:
curl -fsS http://localhost:5667/swagger/v1/swagger.json
必须返回 HTTP 200,根对象包含 openapi、info、paths 和 components。
7.2 快照同步
npm run api:sync 执行以下步骤:
- 从
BUILDFLOW_API_ORIGIN读取/swagger/v1/swagger.json,默认 origin 为http://localhost:5667。 - 校验文档类型、版本、已知关键路径和 schema 数量。
- 对 JSON 键排序并规范化格式,避免无意义 diff。
- 原子替换
config/openapi/buildflow.json。 - 拒绝 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 普通请求
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 登录与初始状态
- 登录调用
/auth/login,成功后只持久化 access token;refresh token 由 HttpOnly Cookie 管理。 - 登录完成后加载
/user/info、/menu/all和/auth/codes。 getInitialState保存当前用户、菜单树和权限码集合。access.ts提供can(code)、canAny(codes)、canAll(codes),并保留模板canAdmin兼容入口。- 菜单转换器规范化相对路径、图标、隐藏规则和权限信息;页面路由仍必须在编译期 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. 后端契约修复
第一阶段只修改支撑所有前端模块的后端横切契约:
- Swagger JSON 和 UI 匿名可读,业务 API 仍默认要求认证。
- 登录请求缺字段或字段不合法时返回 HTTP 400,信封 code 为
invalid_param。 - 用户名或密码错误时返回 HTTP 401,信封 code 为
unauthorized,不泄露具体失败字段。 - 成功登录返回 HTTP 200、code 0、access token,并设置 HttpOnly
jwtCookie。 - refresh 成功返回 HTTP 200
text/plainaccess token并轮换 Cookie;失败返回 HTTP 401 错误信封。 /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 节全部行为。继续执行:
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 使用真实后端和测试账号验证:
- 打开登录页。
- 错误密码显示统一错误且不离开页面。
- 正确登录进入用户 home path。
- 页面显示真实用户信息和后端菜单。
- 刷新页面仍保持会话。
- 模拟 access token 失效后只刷新一次并恢复请求。
- refresh 失败后跳回登录页并携带 redirect。
11. SOP 与命令门槛
第一阶段结束时,前端应提供以下稳定命令:
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. 验收标准
本阶段只有同时满足以下条件才算完成:
- 全新目录执行
npm ci成功,仓库只存在 npm 根锁文件。 - React Pro 已建立保留原始现状的 Git baseline,阶段改动使用独立 Conventional Commits。
- 前端
lint、Jest、antd lint、build 全部退出 0。 - 匿名 Swagger 返回 200;OpenAPI 快照和生成代码检查通过。
- BuildFlow 业务代码不再手工修改
src/services/ant-design-pro/。 - 登录、当前用户、30 个菜单节点和 83 个权限码由真实后端成功加载。
- 并发 401 单次刷新、单次重放和失败登出均有自动化测试。
- 后端 build、Architecture tests、Platform tests 和新增 Host 合约测试全部通过。
- API smoke 与 Playwright 登录链路全部通过,且日志不泄露凭据。
- 第一阶段 SOP 在一台未使用当前
node_modules的环境中可重复执行。
13. 后续阶段接口
后续每个业务域都复用本阶段的 transport、生成客户端、域适配器模式和测试门槛。业务阶段只能新增域适配器与页面,不得各自实现 token、refresh、信封、下载、SSE 或 SignalR 的第二套基础逻辑。
阶段顺序保持为:Platform -> File -> RAG -> Workflow/BuildFlow -> Scripting -> IM -> Monitoring -> 全量回归与发布 SOP。