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:
@ -11,6 +11,18 @@ mod m20220101_000008_add_keep_alive_to_menus;
|
||||
mod m20220101_000009_create_request_logs;
|
||||
// 新增岗位与用户岗位关联
|
||||
mod m20220101_000010_create_positions;
|
||||
// 占位:历史上已应用但缺失的工作流相关迁移
|
||||
mod m20220101_000011_create_workflows;
|
||||
mod m20220101_000012_create_workflow_executions;
|
||||
mod m20220101_000013_create_workflow_execution_logs;
|
||||
mod m20220101_000014_create_flows;
|
||||
// 新增 flows 的 code 与 remark 列
|
||||
mod m20220101_000015_add_code_and_remark_to_flows;
|
||||
mod m20220101_000016_dedup_flows_code;
|
||||
mod m20220101_000016_add_unique_index_to_flows_code;
|
||||
mod m20220101_000017_create_flow_run_logs;
|
||||
// 新增:为 flow_run_logs 添加 flow_code 列
|
||||
mod m20220101_000018_add_flow_code_to_flow_run_logs;
|
||||
|
||||
pub struct Migrator;
|
||||
|
||||
@ -29,6 +41,20 @@ impl MigratorTrait for Migrator {
|
||||
Box::new(m20220101_000009_create_request_logs::Migration),
|
||||
// 注册岗位迁移
|
||||
Box::new(m20220101_000010_create_positions::Migration),
|
||||
// 占位:历史上已应用但缺失的工作流相关迁移
|
||||
Box::new(m20220101_000011_create_workflows::Migration),
|
||||
Box::new(m20220101_000012_create_workflow_executions::Migration),
|
||||
Box::new(m20220101_000013_create_workflow_execution_logs::Migration),
|
||||
// 新增 flows 表
|
||||
Box::new(m20220101_000014_create_flows::Migration),
|
||||
// 新增 flows 的 code 与 remark 列
|
||||
Box::new(m20220101_000015_add_code_and_remark_to_flows::Migration),
|
||||
// 先去重再建唯一索引
|
||||
Box::new(m20220101_000016_dedup_flows_code::Migration),
|
||||
Box::new(m20220101_000016_add_unique_index_to_flows_code::Migration),
|
||||
Box::new(m20220101_000017_create_flow_run_logs::Migration),
|
||||
// 新增:为 flow_run_logs 添加 flow_code 列
|
||||
Box::new(m20220101_000018_add_flow_code_to_flow_run_logs::Migration),
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -24,9 +24,10 @@ impl MigrationTrait for Migration {
|
||||
)
|
||||
.await?;
|
||||
|
||||
// seed admin user (cross-DB)
|
||||
// seed admin user for all DB backends
|
||||
// NOTE: test default password is fixed to '123456' for local/dev testing
|
||||
let salt = SaltString::generate(&mut rand::thread_rng());
|
||||
let hash = Argon2::default().hash_password("Admin@123".as_bytes(), &salt).unwrap().to_string();
|
||||
let hash = Argon2::default().hash_password("123456".as_bytes(), &salt).unwrap().to_string();
|
||||
let backend = manager.get_database_backend();
|
||||
let conn = manager.get_connection();
|
||||
match backend {
|
||||
|
||||
16
backend/migration/src/m20220101_000011_create_workflows.rs
Normal file
16
backend/migration/src/m20220101_000011_create_workflows.rs
Normal file
@ -0,0 +1,16 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
// 占位迁移:历史上已应用,但当前代码库缺失实际文件
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
// 占位迁移:历史上已应用,但当前代码库缺失实际文件
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
// 占位迁移:历史上已应用,但当前代码库缺失实际文件
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
40
backend/migration/src/m20220101_000014_create_flows.rs
Normal file
40
backend/migration/src/m20220101_000014_create_flows.rs
Normal file
@ -0,0 +1,40 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(Flows::Table)
|
||||
.if_not_exists()
|
||||
.col(ColumnDef::new(Flows::Id).string_len(64).not_null().primary_key())
|
||||
.col(ColumnDef::new(Flows::Name).string().null())
|
||||
.col(ColumnDef::new(Flows::Yaml).text().null())
|
||||
.col(ColumnDef::new(Flows::DesignJson).text().null())
|
||||
.col(ColumnDef::new(Flows::CreatedAt).timestamp().not_null().default(Expr::current_timestamp()))
|
||||
.col(ColumnDef::new(Flows::UpdatedAt).timestamp().not_null().default(Expr::current_timestamp()))
|
||||
.to_owned()
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager.drop_table(Table::drop().table(Flows::Table).to_owned()).await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Iden)]
|
||||
enum Flows {
|
||||
Table,
|
||||
Id,
|
||||
Name,
|
||||
Yaml,
|
||||
DesignJson,
|
||||
CreatedAt,
|
||||
UpdatedAt,
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
// SQLite 不支持单条 ALTER 语句包含多个操作,拆分为两次执行
|
||||
manager
|
||||
.alter_table(
|
||||
Table::alter()
|
||||
.table(Flows::Table)
|
||||
.add_column(ColumnDef::new(Flows::Code).string().null())
|
||||
.to_owned()
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.alter_table(
|
||||
Table::alter()
|
||||
.table(Flows::Table)
|
||||
.add_column(ColumnDef::new(Flows::Remark).string().null())
|
||||
.to_owned()
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
// 与 up 相反顺序,依然需要拆分为两次执行
|
||||
manager
|
||||
.alter_table(
|
||||
Table::alter()
|
||||
.table(Flows::Table)
|
||||
.drop_column(Flows::Remark)
|
||||
.to_owned()
|
||||
)
|
||||
.await?;
|
||||
|
||||
manager
|
||||
.alter_table(
|
||||
Table::alter()
|
||||
.table(Flows::Table)
|
||||
.drop_column(Flows::Code)
|
||||
.to_owned()
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
enum Flows {
|
||||
Table,
|
||||
Code,
|
||||
Remark,
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_index(
|
||||
Index::create()
|
||||
.name("idx-unique-flows-code")
|
||||
.table(Flows::Table)
|
||||
.col(Flows::Code)
|
||||
.unique()
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_index(
|
||||
Index::drop()
|
||||
.name("idx-unique-flows-code")
|
||||
.table(Flows::Table)
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
enum Flows {
|
||||
Table,
|
||||
Code,
|
||||
}
|
||||
65
backend/migration/src/m20220101_000016_dedup_flows_code.rs
Normal file
65
backend/migration/src/m20220101_000016_dedup_flows_code.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
use sea_orm_migration::sea_orm::Statement;
|
||||
use sea_orm_migration::sea_orm::DatabaseBackend;
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
let db = manager.get_connection();
|
||||
match manager.get_database_backend() {
|
||||
DatabaseBackend::MySql => {
|
||||
// 将重复的 code 置为 NULL,仅保留每组中 id 最小的一条
|
||||
let sql = r#"
|
||||
UPDATE flows f
|
||||
JOIN (
|
||||
SELECT code, MIN(id) AS min_id
|
||||
FROM flows
|
||||
WHERE code IS NOT NULL
|
||||
GROUP BY code
|
||||
HAVING COUNT(*) > 1
|
||||
) t ON f.code = t.code AND f.id <> t.min_id
|
||||
SET f.code = NULL;
|
||||
"#;
|
||||
db.execute(Statement::from_string(DatabaseBackend::MySql, sql.to_string())).await?;
|
||||
Ok(())
|
||||
}
|
||||
DatabaseBackend::Postgres => {
|
||||
let sql = r#"
|
||||
WITH d AS (
|
||||
SELECT id, ROW_NUMBER() OVER(PARTITION BY code ORDER BY id) AS rn
|
||||
FROM flows
|
||||
WHERE code IS NOT NULL
|
||||
)
|
||||
UPDATE flows AS f
|
||||
SET code = NULL
|
||||
FROM d
|
||||
WHERE f.id = d.id AND d.rn > 1;
|
||||
"#;
|
||||
db.execute(Statement::from_string(DatabaseBackend::Postgres, sql.to_string())).await?;
|
||||
Ok(())
|
||||
}
|
||||
DatabaseBackend::Sqlite => {
|
||||
let sql = r#"
|
||||
WITH d AS (
|
||||
SELECT id, ROW_NUMBER() OVER(PARTITION BY code ORDER BY id) AS rn
|
||||
FROM flows
|
||||
WHERE code IS NOT NULL
|
||||
)
|
||||
UPDATE flows
|
||||
SET code = NULL
|
||||
WHERE id IN (SELECT id FROM d WHERE rn > 1);
|
||||
"#;
|
||||
db.execute(Statement::from_string(DatabaseBackend::Sqlite, sql.to_string())).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
// 数据清洗不可逆
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(FlowRunLogs::Table)
|
||||
.if_not_exists()
|
||||
.col(ColumnDef::new(FlowRunLogs::Id).big_integer().not_null().auto_increment().primary_key())
|
||||
.col(ColumnDef::new(FlowRunLogs::FlowId).string_len(64).not_null())
|
||||
.col(ColumnDef::new(FlowRunLogs::Input).text().null())
|
||||
.col(ColumnDef::new(FlowRunLogs::Output).text().null())
|
||||
.col(ColumnDef::new(FlowRunLogs::Ok).boolean().not_null().default(false))
|
||||
.col(ColumnDef::new(FlowRunLogs::Logs).text().null())
|
||||
.col(ColumnDef::new(FlowRunLogs::UserId).big_integer().null())
|
||||
.col(ColumnDef::new(FlowRunLogs::Username).string().null())
|
||||
.col(ColumnDef::new(FlowRunLogs::StartedAt).timestamp().not_null().default(Expr::current_timestamp()))
|
||||
.col(ColumnDef::new(FlowRunLogs::DurationMs).big_integer().not_null().default(0))
|
||||
.col(ColumnDef::new(FlowRunLogs::CreatedAt).timestamp().not_null().default(Expr::current_timestamp()))
|
||||
.to_owned()
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager.drop_table(Table::drop().table(FlowRunLogs::Table).to_owned()).await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Iden)]
|
||||
enum FlowRunLogs {
|
||||
Table,
|
||||
Id,
|
||||
FlowId,
|
||||
Input,
|
||||
Output,
|
||||
Ok,
|
||||
Logs,
|
||||
UserId,
|
||||
Username,
|
||||
StartedAt,
|
||||
DurationMs,
|
||||
CreatedAt,
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
#[derive(DeriveMigrationName)]
|
||||
pub struct Migration;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.alter_table(
|
||||
Table::alter()
|
||||
.table(FlowRunLogs::Table)
|
||||
.add_column(ColumnDef::new(FlowRunLogs::FlowCode).string().null())
|
||||
.to_owned()
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.alter_table(
|
||||
Table::alter()
|
||||
.table(FlowRunLogs::Table)
|
||||
.drop_column(FlowRunLogs::FlowCode)
|
||||
.to_owned()
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
enum FlowRunLogs {
|
||||
Table,
|
||||
#[sea_orm(iden = "flow_run_logs")]
|
||||
__N, // dummy to ensure table name when not default; but using Table alias is standard
|
||||
FlowCode,
|
||||
}
|
||||
Reference in New Issue
Block a user