feat(flows): 新增流程编辑器基础功能与相关组件

feat(backend): 添加流程模型与服务支持
feat(frontend): 实现流程编辑器UI与交互
feat(assets): 添加流程节点图标资源
feat(plugins): 实现上下文菜单和运行时插件
feat(components): 新增基础节点和侧边栏组件
feat(routes): 添加流程相关路由配置
feat(models): 创建流程和运行日志数据模型
feat(services): 实现流程服务层逻辑
feat(migration): 添加流程相关数据库迁移
feat(config): 更新前端配置支持流程编辑器
feat(utils): 增强axios错误处理和工具函数
This commit is contained in:
2025-09-15 00:27:13 +08:00
parent 9da3978f91
commit b0963e5e37
291 changed files with 17947 additions and 86 deletions

View File

@ -0,0 +1,13 @@
use axum::{Router, routing::get, extract::{State, Query}, Json};
use crate::{db::Db, response::ApiResponse, services::flow_run_log_service};
pub fn router() -> Router<Db> {
Router::new().route("/flow_run_logs", get(list))
}
async fn list(State(db): State<Db>, Query(p): Query<flow_run_log_service::ListParams>) -> Json<ApiResponse<flow_run_log_service::PageResp<flow_run_log_service::RunLogItem>>> {
match flow_run_log_service::list(&db, p).await {
Ok(res) => Json(ApiResponse::ok(res)),
Err(e) => Json(ApiResponse::err(500, format!("{}", e))),
}
}

View File

@ -0,0 +1,71 @@
use axum::{Router, routing::{post, get}, extract::{State, Path, Query}, Json};
use crate::{db::Db, response::ApiResponse, services::flow_service, error::AppError};
use serde::Deserialize;
use tracing::{info, error};
use crate::middlewares::jwt::AuthUser;
pub fn router() -> Router<Db> {
Router::new()
.route("/flows", post(create).get(list))
.route("/flows/{id}", get(get_one).put(update).delete(remove))
.route("/flows/{id}/run", post(run))
}
#[derive(Deserialize)]
struct PageParams { page: Option<u64>, page_size: Option<u64>, keyword: Option<String> }
async fn list(State(db): State<Db>, Query(p): Query<PageParams>) -> Result<Json<ApiResponse<flow_service::PageResp<flow_service::FlowSummary>>>, AppError> {
let page = p.page.unwrap_or(1);
let page_size = p.page_size.unwrap_or(10);
let res = flow_service::list(&db, page, page_size, p.keyword).await.map_err(flow_service::ae)?;
Ok(Json(ApiResponse::ok(res)))
}
#[derive(Deserialize)]
struct CreateReq { yaml: Option<String>, name: Option<String>, design_json: Option<serde_json::Value>, code: Option<String>, remark: Option<String> }
#[derive(Deserialize)]
struct UpdateReq { yaml: Option<String>, design_json: Option<serde_json::Value>, name: Option<String>, code: Option<String>, remark: Option<String> }
async fn create(State(db): State<Db>, Json(req): Json<CreateReq>) -> Result<Json<ApiResponse<flow_service::FlowDoc>>, AppError> {
info!(target = "udmin", "routes.flows.create: start");
let res = match flow_service::create(&db, flow_service::FlowCreateReq { yaml: req.yaml, name: req.name, design_json: req.design_json, code: req.code, remark: req.remark }).await {
Ok(r) => { info!(target = "udmin", id = %r.id, "routes.flows.create: ok"); r }
Err(e) => {
error!(target = "udmin", error = ?e, "routes.flows.create: failed");
// 将错误恢复为统一映射,避免对外暴露内部细节
return Err(flow_service::ae(e));
}
};
Ok(Json(ApiResponse::ok(res)))
}
async fn update(State(db): State<Db>, Path(id): Path<String>, Json(req): Json<UpdateReq>) -> Result<Json<ApiResponse<flow_service::FlowDoc>>, AppError> {
let res = flow_service::update(&db, &id, flow_service::FlowUpdateReq { yaml: req.yaml, design_json: req.design_json, name: req.name, code: req.code, remark: req.remark }).await.map_err(flow_service::ae)?;
Ok(Json(ApiResponse::ok(res)))
}
async fn get_one(State(db): State<Db>, Path(id): Path<String>) -> Result<Json<ApiResponse<flow_service::FlowDoc>>, AppError> {
let res = flow_service::get(&db, &id).await.map_err(flow_service::ae)?;
Ok(Json(ApiResponse::ok(res)))
}
async fn remove(State(db): State<Db>, Path(id): Path<String>) -> Result<Json<ApiResponse<serde_json::Value>>, AppError> {
flow_service::delete(&db, &id).await.map_err(flow_service::ae)?;
Ok(Json(ApiResponse::ok(serde_json::json!({"deleted": true}))))
}
#[derive(Deserialize)]
struct RunReq { #[serde(default)] input: serde_json::Value }
async fn run(State(db): State<Db>, user: AuthUser, Path(id): Path<String>, Json(req): Json<RunReq>) -> Result<Json<ApiResponse<flow_service::RunResult>>, AppError> {
match flow_service::run(&db, &id, flow_service::RunReq { input: req.input }, Some((user.uid, user.username))).await {
Ok(r) => Ok(Json(ApiResponse::ok(r))),
Err(e) => {
// 同步执行:直接把后端错误详细信息返回给前端
let mut full = e.to_string();
for cause in e.chain().skip(1) { full.push_str(" | "); full.push_str(&cause.to_string()); }
Err(AppError::InternalMsg(full))
}
}
}

View File

@ -6,6 +6,8 @@ pub mod departments;
pub mod logs;
// 新增岗位
pub mod positions;
pub mod flows;
pub mod flow_run_logs;
use axum::Router;
use crate::db::Db;
@ -18,6 +20,7 @@ pub fn api_router() -> Router<Db> {
.merge(menus::router())
.merge(departments::router())
.merge(logs::router())
// 合并岗位路由
.merge(flows::router())
.merge(positions::router())
.merge(flow_run_logs::router())
}