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

16 KiB
Raw Blame History

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. 目录与所有权

目标结构如下:

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 增加匿名例外。

验收请求:

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 普通请求

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 节全部行为。继续执行:

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 与命令门槛

第一阶段结束时,前端应提供以下稳定命令:

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。