From cbbcd3121f544649afd924f010c0d1732ccebb5e Mon Sep 17 00:00:00 2001 From: ayou <550244300@qq.com> Date: Mon, 15 Dec 2025 23:46:36 +0800 Subject: [PATCH] =?UTF-8?q?refactor(auth):=20=E4=BC=98=E5=8C=96401?= =?UTF-8?q?=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=E5=B9=B6=E6=8F=90=E5=8F=96?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E8=B7=B3=E8=BD=AC=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将重复的401处理逻辑提取为redirectToLogin函数 修复刷新接口401时的循环重试问题 docs: 更新项目结构文档和快速启动指南 --- README.md | 86 ++++++++++++++++++++++++++++++++++++- frontend/src/utils/axios.ts | 22 +++++----- 2 files changed, 96 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 5bb6565..70fe958 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,89 @@ 欢迎来到 UdminAI 项目文档中心。本文档集合提供了项目的完整技术文档,涵盖了架构设计、模块说明、API 文档和最佳实践等内容。 +## 🗂 项目结构 + +``` +udmin_ai/ +├─ backend/ # Rust 后端(axum + tokio + sea-orm) +│ └─ src/ +│ ├─ flow/ # 流程执行核心 +│ │ ├─ mod.rs +│ │ ├─ domain.rs # ChainDef/Node/Link/Group 等领域模型 +│ │ ├─ context.rs # 执行选项/流式事件 +│ │ ├─ engine.rs # FlowEngine 引擎(并发/分支/屏障/事件) +│ │ ├─ dsl.rs # Design JSON/DSL 解析与构建 +│ │ ├─ mappers.rs # 设计映射到 ctx(nodes..) +│ │ ├─ log_handler.rs # 运行日志与事件推送抽象 +│ │ ├─ task.rs # 任务注册表与执行器抽象 +│ │ └─ executors/ # http/db/variable/script_* / condition +│ ├─ routes/ # API 路由层 +│ │ ├─ flows.rs # /flows CRUD 与运行(同步/SSE/WS) +│ │ └─ flow_run_logs.rs # /flow_run_logs 查询与删除 +│ ├─ services/ # 业务编排与持久化 +│ │ ├─ flow_service.rs # 流程加载/解析/驱动与日志 +│ │ └─ flow_run_log_service.rs # 运行日志分页与批量删除 +│ ├─ middlewares/ # 中间件服务 +│ │ ├─ sse.rs # SSE 服务与事件封装 +│ │ ├─ ws.rs # WebSocket 服务与转发 +│ │ ├─ jwt.rs # 认证鉴权与用户提取 +│ │ └─ http_client.rs # HTTP 客户端封装(reqwest) +│ ├─ models/ # SeaORM 数据模型 +│ │ ├─ flow.rs # flows 表 +│ │ └─ flow_run_log.rs # flow_run_logs 表 +│ ├─ db.rs # 数据库初始化与全局句柄 +│ └─ redis.rs # Redis 访问与令牌校验 +│ +├─ frontend/ # TypeScript 前端(React + Vite) +│ └─ src/ +│ ├─ flows/ # 可视化流程编辑器与运行面板 +│ │ ├─ editor.tsx # 设计器入口(自由布局) +│ │ ├─ services/custom-service.ts # 保存与运行(HTTP/SSE/WS) +│ │ └─ components/testrun/... # 测试运行面板与实时输出 +│ ├─ pages/ +│ │ ├─ FlowList.tsx # 流程列表/新建/编辑 +│ │ └─ FlowRunLogs.tsx # 运行日志列表 +│ ├─ utils/ +│ │ ├─ axios.ts # 统一请求拦截(401 刷新/跳转登录) +│ │ └─ token.ts # 本地令牌与用户信息管理 +│ ├─ App.tsx / main.tsx # 入口与路由 +│ └─ styles / layouts ... # 样式与布局 +│ +├─ docs/ # 文档 +│ ├─ flow_architecture.md # Flow 架构与执行图(Mermaid) +│ └─ ... # 其他专题文档(概览/后端/前端/示例) +│ +└─ .trae/rules/project_rules.md # 项目代码规范(Rust 文件顺序等) +``` + +## ⚙️ 快速启动 + +- 后端启动(默认读取 `.env`,端口:HTTP 9898,SSE 8866,WS 8855) + - `cd backend && ENV_FILE=dev cargo run` +- 前端启动(默认开发端口 8888) + - `cd frontend && npm install && npm run dev` + +## 🔌 主要接口 + +- 流程管理 + - `POST /api/flows` 新建流程(支持 YAML 与 design_json) + - `GET /api/flows` 分页查询 + - `GET/PUT/DELETE /api/flows/{id}` 详情/更新/删除 + - `POST /api/flows/{id}/run` 同步运行,返回 `{ ok, ctx, logs }` + - `POST /api/flows/{id}/run/stream` SSE 流式运行,推送节点事件与完成事件 + - `GET /api/flows/{id}/run/ws` WebSocket 流式运行 +- 运行日志 + - `GET /api/flow_run_logs` 分页查询(支持按 flow_id/flow_code/user/ok 过滤) + - `DELETE /api/flow_run_logs/{ids}` 批量删除 + +## 🧩 架构与执行 + +- 引擎:自研 `FlowEngine`(递归驱动图,合流屏障,分支并行,组等待) +- 模式:`sync`(同步)/ `async`(组内异步)/ `queued`(组队列)/ `bounded`(组限并发) +- 条件:JSON 条件集合或 `rhai` 表达式;无匹配时回退无条件边 +- 执行器:`http`、`db`、`variable`、`script_rhai/js/python`、`condition` +- 文档详见:`docs/flow_architecture.md` + ## 📚 文档导航 ### 🏗️ 架构文档 @@ -17,6 +100,7 @@ - **[路由层](docs/ROUTES.md)** - API 路由的设计规范、接口定义和中间件集成 - **[数据模型](docs/MODELS.md)** - 数据模型的设计原则、实体定义和关系映射 - **[中间件](docs/MIDDLEWARES.md)** - 中间件系统的设计理念、核心组件和使用方式 +- **[Flow 架构与执行图](docs/flow_architecture.md)** - Flow 端到端架构图与执行流程 ### 🛠️ 基础设施 @@ -135,4 +219,4 @@ **UdminAI 团队** *构建智能化的流程管理平台* -> 💡 **提示**: 建议将本文档加入浏览器书签,方便随时查阅。文档会持续更新,请关注最新版本。 \ No newline at end of file +> 💡 **提示**: 建议将本文档加入浏览器书签,方便随时查阅。文档会持续更新,请关注最新版本。 diff --git a/frontend/src/utils/axios.ts b/frontend/src/utils/axios.ts index e27e60c..df96c39 100644 --- a/frontend/src/utils/axios.ts +++ b/frontend/src/utils/axios.ts @@ -63,11 +63,7 @@ async function tryRefreshAndRetry(original: RetryConfig, message?: string): Prom } catch (e: any) { pendingQueue.forEach(p => p.reject(e)) pendingQueue = [] - clearToken() - if (typeof window !== 'undefined' && window.location.pathname !== '/login') { - window.location.href = '/login' - } - return Promise.reject(e) + return redirectToLogin((e && e.message) || message) } finally { isRefreshing = false } @@ -104,6 +100,10 @@ api.interceptors.response.use( const msg = body?.message || '未登录或登录已过期' return Promise.reject(new Error(msg)) } + // 刷新接口返回业务 401:直接跳转登录,避免循环重试 + if (reqUrl.includes('/auth/refresh')) { + return redirectToLogin(body?.message) + } return tryRefreshAndRetry(original, body?.message) } return r @@ -120,6 +120,10 @@ api.interceptors.response.use( const msg = resp?.message || '用户名或密码错误' return Promise.reject(new Error(msg)) } + // 刷新接口返回 401:直接跳转登录,避免循环重试 + if (reqUrl.includes('/auth/refresh')) { + return redirectToLogin(resp?.message) + } // 已经重试过了,直接拒绝 if (original._retry) { @@ -144,14 +148,10 @@ api.interceptors.response.use( pendingQueue = [] return api(original) } - } catch (e) { + } catch (e: any) { pendingQueue.forEach(p => p.reject(e)) pendingQueue = [] - clearToken() - if (typeof window !== 'undefined' && window.location.pathname !== '/login') { - window.location.href = '/login' - } - return Promise.reject(e) + return redirectToLogin((e && e.message) || resp?.message) } finally { isRefreshing = false }