feat(flow): 添加流程信息展示组件及后端支持

新增左上角流程信息展示组件,显示流程编码和名称
后端 FlowDoc 结构增加 name/code/remark 字段支持
添加从 YAML 提取名称的兜底逻辑
This commit is contained in:
2025-09-22 22:15:45 +08:00
parent cb0d829884
commit 681abeed45
3 changed files with 131 additions and 18 deletions

View File

@ -26,7 +26,14 @@ pub struct FlowSummary {
#[serde(skip_serializing_if = "Option::is_none")] pub last_modified_by: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FlowDoc { pub id: String, pub yaml: String, #[serde(skip_serializing_if = "Option::is_none")] pub design_json: Option<serde_json::Value> }
pub struct FlowDoc {
pub id: String,
pub yaml: String,
#[serde(skip_serializing_if = "Option::is_none")] pub design_json: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")] pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")] pub code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")] pub remark: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FlowCreateReq { pub yaml: Option<String>, pub name: Option<String>, pub design_json: Option<serde_json::Value>, pub code: Option<String>, pub remark: Option<String> }
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -98,9 +105,13 @@ pub async fn create(db: &Db, req: FlowCreateReq) -> anyhow::Result<FlowDoc> {
.or_else(|| req.yaml.as_deref().and_then(extract_name));
let now = Utc::now().with_timezone(&FixedOffset::east_opt(0).unwrap());
let design_json_str = match &req.design_json { Some(v) => serde_json::to_string(v).ok(), None => None };
// 克隆一份用于返回
let ret_name = name.clone();
let ret_code = req.code.clone();
let ret_remark = req.remark.clone();
let am = db_flow::ActiveModel {
id: Set(id.clone()),
name: Set(name),
name: Set(name.clone()),
yaml: Set(req.yaml.clone()),
design_json: Set(design_json_str),
// 新增: code 与 remark 入库
@ -115,7 +126,7 @@ pub async fn create(db: &Db, req: FlowCreateReq) -> anyhow::Result<FlowDoc> {
match db_flow::Entity::insert(am).exec(db).await {
Ok(_) => {
info!(target: "udmin", "flow.create: insert ok id={}", id);
Ok(FlowDoc { id, yaml: req.yaml.unwrap_or_default(), design_json: req.design_json })
Ok(FlowDoc { id, yaml: req.yaml.unwrap_or_default(), design_json: req.design_json, name: ret_name, code: ret_code, remark: ret_remark })
}
Err(DbErr::RecordNotInserted) => {
// Workaround for MySQL + non-auto-increment PK: verify by reading back
@ -123,7 +134,7 @@ pub async fn create(db: &Db, req: FlowCreateReq) -> anyhow::Result<FlowDoc> {
match db_flow::Entity::find_by_id(id.clone()).one(db).await {
Ok(Some(_)) => {
info!(target: "udmin", "flow.create: found inserted row by id={}, treating as success", id);
Ok(FlowDoc { id, yaml: req.yaml.unwrap_or_default(), design_json: req.design_json })
Ok(FlowDoc { id, yaml: req.yaml.unwrap_or_default(), design_json: req.design_json, name, code: req.code, remark: req.remark })
}
Ok(None) => Err(anyhow::anyhow!("insert flow failed").context("verify inserted row not found")),
Err(e) => Err(anyhow::Error::new(e).context("insert flow failed")),
@ -141,7 +152,12 @@ pub async fn get(db: &Db, id: &str) -> anyhow::Result<FlowDoc> {
let row = row.ok_or_else(|| anyhow::anyhow!("not found"))?;
let yaml = row.yaml.unwrap_or_default();
let design_json = row.design_json.and_then(|s| serde_json::from_str::<serde_json::Value>(&s).ok());
Ok(FlowDoc { id: row.id, yaml, design_json })
// 名称兜底:数据库 name 为空时,尝试从 YAML 提取
let name = row
.name
.clone()
.or_else(|| extract_name(&yaml));
Ok(FlowDoc { id: row.id, yaml, design_json, name, code: row.code, remark: row.remark })
}
pub async fn get_by_code(db: &Db, code: &str) -> anyhow::Result<FlowDoc> {
@ -152,7 +168,12 @@ pub async fn get_by_code(db: &Db, code: &str) -> anyhow::Result<FlowDoc> {
let row = row.ok_or_else(|| anyhow::anyhow!("flow not found with code: {}", code))?;
let yaml = row.yaml.unwrap_or_default();
let design_json = row.design_json.and_then(|s| serde_json::from_str::<serde_json::Value>(&s).ok());
Ok(FlowDoc { id: row.id, yaml, design_json })
// 名称兜底:数据库 name 为空时,尝试从 YAML 提取
let name = row
.name
.clone()
.or_else(|| extract_name(&yaml));
Ok(FlowDoc { id: row.id, yaml, design_json, name, code: row.code, remark: row.remark })
}
pub async fn update(db: &Db, id: &str, req: FlowUpdateReq) -> anyhow::Result<FlowDoc> {
@ -185,7 +206,7 @@ pub async fn update(db: &Db, id: &str, req: FlowUpdateReq) -> anyhow::Result<Flo
// return latest yaml
let got = db_flow::Entity::find_by_id(id.to_string()).one(db).await?.unwrap();
let dj = got.design_json.as_deref().and_then(|s| serde_json::from_str::<serde_json::Value>(&s).ok());
Ok(FlowDoc { id: id.to_string(), yaml: got.yaml.unwrap_or_default(), design_json: dj })
Ok(FlowDoc { id: id.to_string(), yaml: got.yaml.unwrap_or_default(), design_json: dj, name: got.name, code: got.code, remark: got.remark })
}
pub async fn delete(db: &Db, id: &str) -> anyhow::Result<()> {