refactor(auth): 优化401处理逻辑并提取登录跳转函数
将重复的401处理逻辑提取为redirectToLogin函数 修复刷新接口401时的循环重试问题 docs: 更新项目结构文档和快速启动指南
This commit is contained in:
86
README.md
86
README.md
@ -2,6 +2,89 @@
|
|||||||
|
|
||||||
欢迎来到 UdminAI 项目文档中心。本文档集合提供了项目的完整技术文档,涵盖了架构设计、模块说明、API 文档和最佳实践等内容。
|
欢迎来到 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.<id>.<executor>)
|
||||||
|
│ │ ├─ 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/ROUTES.md)** - API 路由的设计规范、接口定义和中间件集成
|
||||||
- **[数据模型](docs/MODELS.md)** - 数据模型的设计原则、实体定义和关系映射
|
- **[数据模型](docs/MODELS.md)** - 数据模型的设计原则、实体定义和关系映射
|
||||||
- **[中间件](docs/MIDDLEWARES.md)** - 中间件系统的设计理念、核心组件和使用方式
|
- **[中间件](docs/MIDDLEWARES.md)** - 中间件系统的设计理念、核心组件和使用方式
|
||||||
|
- **[Flow 架构与执行图](docs/flow_architecture.md)** - Flow 端到端架构图与执行流程
|
||||||
|
|
||||||
### 🛠️ 基础设施
|
### 🛠️ 基础设施
|
||||||
|
|
||||||
@ -135,4 +219,4 @@
|
|||||||
**UdminAI 团队**
|
**UdminAI 团队**
|
||||||
*构建智能化的流程管理平台*
|
*构建智能化的流程管理平台*
|
||||||
|
|
||||||
> 💡 **提示**: 建议将本文档加入浏览器书签,方便随时查阅。文档会持续更新,请关注最新版本。
|
> 💡 **提示**: 建议将本文档加入浏览器书签,方便随时查阅。文档会持续更新,请关注最新版本。
|
||||||
|
|||||||
@ -63,11 +63,7 @@ async function tryRefreshAndRetry(original: RetryConfig, message?: string): Prom
|
|||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
pendingQueue.forEach(p => p.reject(e))
|
pendingQueue.forEach(p => p.reject(e))
|
||||||
pendingQueue = []
|
pendingQueue = []
|
||||||
clearToken()
|
return redirectToLogin((e && e.message) || message)
|
||||||
if (typeof window !== 'undefined' && window.location.pathname !== '/login') {
|
|
||||||
window.location.href = '/login'
|
|
||||||
}
|
|
||||||
return Promise.reject(e)
|
|
||||||
} finally {
|
} finally {
|
||||||
isRefreshing = false
|
isRefreshing = false
|
||||||
}
|
}
|
||||||
@ -104,6 +100,10 @@ api.interceptors.response.use(
|
|||||||
const msg = body?.message || '未登录或登录已过期'
|
const msg = body?.message || '未登录或登录已过期'
|
||||||
return Promise.reject(new Error(msg))
|
return Promise.reject(new Error(msg))
|
||||||
}
|
}
|
||||||
|
// 刷新接口返回业务 401:直接跳转登录,避免循环重试
|
||||||
|
if (reqUrl.includes('/auth/refresh')) {
|
||||||
|
return redirectToLogin(body?.message)
|
||||||
|
}
|
||||||
return tryRefreshAndRetry(original, body?.message)
|
return tryRefreshAndRetry(original, body?.message)
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
@ -120,6 +120,10 @@ api.interceptors.response.use(
|
|||||||
const msg = resp?.message || '用户名或密码错误'
|
const msg = resp?.message || '用户名或密码错误'
|
||||||
return Promise.reject(new Error(msg))
|
return Promise.reject(new Error(msg))
|
||||||
}
|
}
|
||||||
|
// 刷新接口返回 401:直接跳转登录,避免循环重试
|
||||||
|
if (reqUrl.includes('/auth/refresh')) {
|
||||||
|
return redirectToLogin(resp?.message)
|
||||||
|
}
|
||||||
|
|
||||||
// 已经重试过了,直接拒绝
|
// 已经重试过了,直接拒绝
|
||||||
if (original._retry) {
|
if (original._retry) {
|
||||||
@ -144,14 +148,10 @@ api.interceptors.response.use(
|
|||||||
pendingQueue = []
|
pendingQueue = []
|
||||||
return api(original)
|
return api(original)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
pendingQueue.forEach(p => p.reject(e))
|
pendingQueue.forEach(p => p.reject(e))
|
||||||
pendingQueue = []
|
pendingQueue = []
|
||||||
clearToken()
|
return redirectToLogin((e && e.message) || resp?.message)
|
||||||
if (typeof window !== 'undefined' && window.location.pathname !== '/login') {
|
|
||||||
window.location.href = '/login'
|
|
||||||
}
|
|
||||||
return Promise.reject(e)
|
|
||||||
} finally {
|
} finally {
|
||||||
isRefreshing = false
|
isRefreshing = false
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user