diff --git a/middleware/flow/src/api/cc/flow_cc_inst_api.rs b/middleware/flow/src/api/cc/flow_cc_inst_api.rs index 3cd4fbd19..ec0e05937 100644 --- a/middleware/flow/src/api/cc/flow_cc_inst_api.rs +++ b/middleware/flow/src/api/cc/flow_cc_inst_api.rs @@ -6,7 +6,7 @@ use tardis::web::web_resp::{TardisApiResult, TardisPage, TardisResp, Void}; use crate::dto::flow_inst_dto::{ FlowInstAbortReq, FlowInstDetailResp, FlowInstFindNextTransitionResp, FlowInstFindNextTransitionsReq, FlowInstFindStateAndTransitionsReq, FlowInstFindStateAndTransitionsResp, - FlowInstModifyAssignedReq, FlowInstStartReq, FlowInstSummaryResp, FlowInstTransferReq, FlowInstTransferResp, + FlowInstModifyAssignedReq, FlowInstModifyCurrentVarsReq, FlowInstStartReq, FlowInstSummaryResp, FlowInstTransferReq, FlowInstTransferResp, }; use crate::flow_constants; use crate::serv::flow_inst_serv::FlowInstServ; @@ -106,4 +106,14 @@ impl FlowCcInstApi { funs.commit().await?; TardisResp::ok(Void {}) } + + /// Modify list of variables / 同步当前变量列表 + #[oai(path = "/:flow_inst_id/modify_current_vars", method = "patch")] + async fn modify_current_vars(&self, flow_inst_id: Path, modify_req: Json, ctx: TardisContextExtractor) -> TardisApiResult { + let mut funs = flow_constants::get_tardis_inst(); + funs.begin().await?; + FlowInstServ::modify_current_vars(&flow_inst_id.0, &modify_req.0.vars, &funs, &ctx.0).await?; + funs.commit().await?; + TardisResp::ok(Void {}) + } } diff --git a/middleware/flow/src/dto/flow_inst_dto.rs b/middleware/flow/src/dto/flow_inst_dto.rs index 2d4f490c9..ae7c33589 100644 --- a/middleware/flow/src/dto/flow_inst_dto.rs +++ b/middleware/flow/src/dto/flow_inst_dto.rs @@ -9,7 +9,11 @@ use tardis::{ web::poem_openapi, }; -use super::{flow_state_dto::FlowSysStateKind, flow_transition_dto::FlowTransitionDoubleCheckInfo, flow_var_dto::FlowVarInfo}; +use super::{ + flow_state_dto::{FlowStateRelModelExt, FlowSysStateKind}, + flow_transition_dto::FlowTransitionDoubleCheckInfo, + flow_var_dto::FlowVarInfo, +}; #[derive(Serialize, Deserialize, Debug, poem_openapi::Object)] pub struct FlowInstStartReq { @@ -143,7 +147,6 @@ pub struct FlowInstFindNextTransitionResp { pub next_flow_state_color: String, pub vars_collect: Option>, - pub double_check: Option, } @@ -159,6 +162,7 @@ pub struct FlowInstFindStateAndTransitionsResp { pub current_flow_state_name: String, pub current_flow_state_kind: FlowSysStateKind, pub current_flow_state_color: String, + pub current_flow_state_ext: FlowStateRelModelExt, pub finish_time: Option>, pub next_flow_transitions: Vec, } @@ -178,6 +182,7 @@ pub struct FlowInstTransferResp { pub new_flow_state_id: String, pub new_flow_state_name: String, pub new_flow_state_color: String, + pub new_flow_state_ext: FlowStateRelModelExt, pub finish_time: Option>, pub vars: Option>, @@ -188,3 +193,8 @@ pub struct FlowInstTransferResp { pub struct FlowInstModifyAssignedReq { pub current_assigned: String, } + +#[derive(Serialize, Deserialize, Debug, poem_openapi::Object)] +pub struct FlowInstModifyCurrentVarsReq { + pub vars: HashMap, +} diff --git a/middleware/flow/src/dto/flow_transition_dto.rs b/middleware/flow/src/dto/flow_transition_dto.rs index 6290f2cc1..dc53bdd97 100644 --- a/middleware/flow/src/dto/flow_transition_dto.rs +++ b/middleware/flow/src/dto/flow_transition_dto.rs @@ -1,5 +1,6 @@ -use bios_basic::{dto::BasicQueryCondInfo, basic_enumeration::BasicQueryOpKind}; +use bios_basic::dto::BasicQueryCondInfo; use serde::{Deserialize, Serialize}; +use strum::Display; use tardis::{basic::field::TrimString, db::sea_orm, serde_json::Value, web::poem_openapi, TardisFuns}; use super::flow_var_dto::FlowVarInfo; @@ -322,17 +323,70 @@ pub struct FlowTransitionInitInfo { #[derive(Serialize, Deserialize, Clone, PartialEq, Debug, poem_openapi::Object)] pub struct FlowTransitionFrontActionInfo { - pub relevance_relation: BasicQueryOpKind, + pub relevance_relation: FlowTransitionFrontActionInfoRelevanceRelation, pub relevance_label: String, pub left_value: String, pub left_label: String, pub right_value: FlowTransitionFrontActionRightValue, pub select_field: Option, - pub change_content: Option, - pub real_time: Option, + pub select_field_label: Option, + pub change_content: Option, + pub change_content_label: Option, +} + +#[derive(Display, Clone, Debug, PartialEq, Eq, Deserialize, Serialize, poem_openapi::Enum)] +pub enum FlowTransitionFrontActionInfoRelevanceRelation { + #[serde(rename = "=")] + #[oai(rename = "=")] + Eq, + #[serde(rename = "!=")] + #[oai(rename = "!=")] + Ne, + #[serde(rename = ">")] + #[oai(rename = ">")] + Gt, + #[serde(rename = ">=")] + #[oai(rename = ">=")] + Ge, + #[serde(rename = "<")] + #[oai(rename = "<")] + Lt, + #[serde(rename = "<=")] + #[oai(rename = "<=")] + Le, + #[serde(rename = "like")] + #[oai(rename = "like")] + Like, + #[serde(rename = "not_like")] + #[oai(rename = "not_like")] + NotLike, + #[serde(rename = "in")] + #[oai(rename = "in")] + In, + #[serde(rename = "not_in")] + #[oai(rename = "not_in")] + NotIn, +} + +impl FlowTransitionFrontActionInfoRelevanceRelation { + pub fn check_conform(&self, left_value: String, right_value: String) -> bool { + match self { + FlowTransitionFrontActionInfoRelevanceRelation::Eq => left_value == right_value, + FlowTransitionFrontActionInfoRelevanceRelation::Ne => left_value != right_value, + FlowTransitionFrontActionInfoRelevanceRelation::Gt => left_value > right_value, + FlowTransitionFrontActionInfoRelevanceRelation::Ge => left_value >= right_value, + FlowTransitionFrontActionInfoRelevanceRelation::Lt => left_value < right_value, + FlowTransitionFrontActionInfoRelevanceRelation::Le => left_value <= right_value, + FlowTransitionFrontActionInfoRelevanceRelation::Like => left_value.contains(&right_value), + FlowTransitionFrontActionInfoRelevanceRelation::NotLike => !left_value.contains(&right_value), + FlowTransitionFrontActionInfoRelevanceRelation::In => TardisFuns::json.str_to_obj::>(&right_value).unwrap_or_default().contains(&left_value), + FlowTransitionFrontActionInfoRelevanceRelation::NotIn => !TardisFuns::json.str_to_obj::>(&right_value).unwrap_or_default().contains(&left_value), + } + } } #[derive(Serialize, Deserialize, Clone, PartialEq, Debug, poem_openapi::Enum)] +#[serde(rename_all = "snake_case")] pub enum FlowTransitionFrontActionRightValue { #[oai(rename = "select_field")] SelectField, diff --git a/middleware/flow/src/serv/flow_inst_serv.rs b/middleware/flow/src/serv/flow_inst_serv.rs index 95e2b5e50..c45adc9f0 100644 --- a/middleware/flow/src/serv/flow_inst_serv.rs +++ b/middleware/flow/src/serv/flow_inst_serv.rs @@ -39,16 +39,19 @@ use crate::{ FlowInstTransitionInfo, FlowOperationContext, }, flow_model_dto::{FlowModelDetailResp, FlowModelFilterReq}, - flow_state_dto::{FlowStateFilterReq, FlowSysStateKind}, + flow_state_dto::{FlowStateFilterReq, FlowStateRelModelExt, FlowSysStateKind}, flow_transition_dto::{ FlowTransitionActionByStateChangeInfo, FlowTransitionActionChangeAgg, FlowTransitionActionChangeInfo, FlowTransitionActionChangeKind, FlowTransitionDetailResp, - StateChangeConditionOp, + FlowTransitionFrontActionInfo, FlowTransitionFrontActionRightValue, StateChangeConditionOp, }, }, serv::{flow_model_serv::FlowModelServ, flow_state_serv::FlowStateServ}, }; -use super::flow_external_serv::FlowExternalServ; +use super::{ + flow_external_serv::FlowExternalServ, + flow_rel_serv::{FlowRelKind, FlowRelServ}, +}; pub struct FlowInstServ; @@ -758,6 +761,14 @@ impl FlowInstServ { prev_flow_state_id: prev_flow_state.id, prev_flow_state_name: prev_flow_state.name, prev_flow_state_color: prev_flow_state.color, + new_flow_state_ext: TardisFuns::json.str_to_obj::( + &FlowRelServ::find_from_simple_rels(&FlowRelKind::FlowModelState, &flow_model.id, None, None, funs, ctx) + .await? + .into_iter() + .find(|rel| next_flow_state.id == rel.rel_id) + .ok_or_else(|| funs.err().not_found("flow_inst", "do_find_next_transitions", "flow state is not found", "404-flow-state-not-found"))? + .ext, + )?, new_flow_state_id: next_flow_state.id, new_flow_state_name: next_flow_state.name, new_flow_state_color: next_flow_state.color, @@ -1116,14 +1127,23 @@ impl FlowInstServ { funs, ctx, ) - .await?; + .await? + .ok_or_else(|| funs.err().not_found("flow_inst", "do_find_next_transitions", "flow state is not found", "404-flow-state-not-found"))?; let state_and_next_transitions = FlowInstFindStateAndTransitionsResp { flow_inst_id: flow_inst.id.to_string(), finish_time: flow_inst.finish_time, current_flow_state_name: flow_inst.current_state_name.as_ref().unwrap_or(&"".to_string()).to_string(), - current_flow_state_kind: current_flow_state.as_ref().map(|state| state.sys_state.clone()).unwrap_or(FlowSysStateKind::Finish), - current_flow_state_color: current_flow_state.as_ref().map(|state| state.color.clone()).unwrap_or_default(), + current_flow_state_kind: current_flow_state.sys_state.clone(), + current_flow_state_color: current_flow_state.color.clone(), + current_flow_state_ext: TardisFuns::json.str_to_obj::( + &FlowRelServ::find_from_simple_rels(&FlowRelKind::FlowModelState, &flow_inst.rel_flow_model_id, None, None, funs, ctx) + .await? + .into_iter() + .find(|rel| current_flow_state.id == rel.rel_id) + .ok_or_else(|| funs.err().not_found("flow_inst", "do_find_next_transitions", "flow state is not found", "404-flow-state-not-found"))? + .ext, + )?, next_flow_transitions: next_transitions, }; Ok(state_and_next_transitions) @@ -1148,4 +1168,96 @@ impl FlowInstServ { Ok(false) } } + + pub async fn modify_current_vars(flow_inst_id: &str, current_vars: &HashMap, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + let flow_inst_detail = Self::get(flow_inst_id, funs, ctx).await?; + let mut new_vars: HashMap = HashMap::new(); + if let Some(old_current_vars) = &flow_inst_detail.current_vars { + new_vars.extend(old_current_vars.clone()); + } + new_vars.extend(current_vars.clone()); + let flow_inst = flow_inst::ActiveModel { + id: Set(flow_inst_id.to_string()), + current_vars: Set(Some(TardisFuns::json.obj_to_json(&new_vars)?)), + ..Default::default() + }; + funs.db().update_one(flow_inst, ctx).await?; + + Ok(()) + } + + async fn do_front_change(flow_inst_detail: &FlowInstDetailResp, ctx: &TardisContext, funs: &TardisFunsInst) -> TardisResult<()> { + let flow_model = FlowModelServ::get_item( + &flow_inst_detail.rel_flow_model_id, + &FlowModelFilterReq { + basic: RbumBasicFilterReq { + with_sub_own_paths: true, + own_paths: Some("".to_string()), + ..Default::default() + }, + ..Default::default() + }, + funs, + ctx, + ) + .await?; + let flow_transitions = flow_model + .transitions() + .into_iter() + .filter(|trans| trans.from_flow_state_id == flow_inst_detail.current_state_id && !trans.action_by_front_changes().is_empty()) + .collect_vec(); + if flow_transitions.is_empty() { + return Ok(()); + } + + Ok(()) + } + + async fn check_front_conditions( + flow_inst_detail: &FlowInstDetailResp, + conditions: Vec, + ctx: &TardisContext, + funs: &TardisFunsInst, + ) -> TardisResult { + if flow_inst_detail.current_vars.is_none() { + return Ok(false); + } + let current_vars = flow_inst_detail.current_vars.clone().unwrap(); + for condition in conditions { + if !Self::do_check_front_condition(¤t_vars, &condition)? { + return Ok(false); + } + } + + Ok(true) + } + + fn do_check_front_condition(current_vars: &HashMap, condition: &FlowTransitionFrontActionInfo) -> TardisResult { + match condition.right_value { + FlowTransitionFrontActionRightValue::ChangeContent => { + if let Some(left_value) = current_vars.get(&condition.left_value) { + Ok(condition.relevance_relation.check_conform(left_value.to_string(), condition.change_content.clone().unwrap_or_default().to_string())) + } else { + Ok(false) + } + } + FlowTransitionFrontActionRightValue::SelectField => { + if let (Some(left_value), Some(right_value)) = ( + current_vars.get(&condition.left_value), + current_vars.get(&condition.select_field.clone().unwrap_or_default()), + ) { + Ok(condition.relevance_relation.check_conform(left_value.to_string(), right_value.to_string())) + } else { + Ok(false) + } + } + FlowTransitionFrontActionRightValue::RealTime => { + if let Some(left_value) = current_vars.get(&condition.left_value) { + Ok(condition.relevance_relation.check_conform(left_value.to_string(), Utc::now().to_string())) + } else { + Ok(false) + } + } + } + } } diff --git a/middleware/flow/tests/test_flow_scenes_fsm.rs b/middleware/flow/tests/test_flow_scenes_fsm.rs index 4d3cdc947..3a7d8a7c0 100644 --- a/middleware/flow/tests/test_flow_scenes_fsm.rs +++ b/middleware/flow/tests/test_flow_scenes_fsm.rs @@ -14,8 +14,8 @@ use bios_mw_flow::dto::flow_model_dto::{ }; use bios_mw_flow::dto::flow_state_dto::{FlowStateAddReq, FlowStateSummaryResp, FlowSysStateKind}; use bios_mw_flow::dto::flow_transition_dto::{ - FlowTransitionActionChangeInfo, FlowTransitionActionChangeKind, FlowTransitionAddReq, FlowTransitionDoubleCheckInfo, FlowTransitionModifyReq, StateChangeCondition, - StateChangeConditionItem, StateChangeConditionOp, + FlowTransitionActionChangeInfo, FlowTransitionActionChangeKind, FlowTransitionAddReq, FlowTransitionDoubleCheckInfo, FlowTransitionModifyReq, FlowTransitionSortStateInfoReq, + FlowTransitionSortStatesReq, StateChangeCondition, StateChangeConditionItem, StateChangeConditionOp, }; use bios_mw_flow::dto::flow_var_dto::{FlowVarInfo, RbumDataTypeKind, RbumWidgetTypeKind}; @@ -179,6 +179,31 @@ pub async fn test(flow_client: &mut TestHttpClient, _kv_client: &mut TestHttpCli let trans_complate = req_model_agg.states.iter().find(|state| state.name == "进行中").unwrap().transitions.iter().find(|trans| trans.name == "完成").unwrap(); let trans_close = req_model_agg.states.iter().find(|state| state.name == "进行中").unwrap().transitions.iter().find(|trans| trans.name == "关闭").unwrap(); + let _: Void = flow_client + .patch( + &format!("/cc/model/{}", req_model_id), + &json!({ + "modify_transitions": [ + { + "id": trans_start.id.clone(), + "action_by_front_changes": [ + { + "relevance_relation": "=", + "relevance_label": "包含", + "left_value": "status", + "left_label": "状态", + "right_value": "select_field", + "select_field": "status", + "change_content": null, + "select_field_label": "status", + "change_content_label": null + } + ] + } + ] + }), + ) + .await; let _: Void = flow_client .patch( &format!("/cc/model/{}", req_model_id), @@ -218,7 +243,6 @@ pub async fn test(flow_client: &mut TestHttpClient, _kv_client: &mut TestHttpCli ]), action_by_pre_callback: None, action_by_post_callback: None, - action_by_front_changes: None, action_by_post_changes: Some(vec![FlowTransitionActionChangeInfo { kind: FlowTransitionActionChangeKind::State, describe: "".to_string(), @@ -242,6 +266,7 @@ pub async fn test(flow_client: &mut TestHttpClient, _kv_client: &mut TestHttpCli is_open: true, content: Some("再次确认该操作生效".to_string()), }), + action_by_front_changes: None, sort: None, }, FlowTransitionModifyReq { @@ -304,6 +329,23 @@ pub async fn test(flow_client: &mut TestHttpClient, _kv_client: &mut TestHttpCli }, ) .await; + let _: Void = flow_client + .post( + &format!("/cc/model/{}/resort_transition", &req_model_id), + &FlowTransitionSortStatesReq { + sort_states: vec![ + FlowTransitionSortStateInfoReq { + id: req_model_agg.states.iter().find(|state| state.name == "待开始").unwrap().transitions.iter().find(|trans| trans.name == "开始").unwrap().id.clone(), + sort: 2, + }, + FlowTransitionSortStateInfoReq { + id: req_model_agg.states.iter().find(|state| state.name == "待开始").unwrap().transitions.iter().find(|trans| trans.name == "关闭").unwrap().id.clone(), + sort: 1, + }, + ], + }, + ) + .await; // 3-3. check post action endless loop let proj_trans = proj_model_agg.states.iter().find(|state| state.name == "进行中").unwrap().transitions.iter().find(|trans| trans.name == "有风险").unwrap(); let _: Void = flow_client @@ -619,8 +661,10 @@ pub async fn test(flow_client: &mut TestHttpClient, _kv_client: &mut TestHttpCli let next_transitions: Vec = flow_client.put(&format!("/cc/inst/{}/transition/next", req_inst_id1), &FlowInstFindNextTransitionsReq { vars: None }).await; assert_eq!(next_transitions.len(), 2); - assert!(next_transitions.iter().any(|trans| trans.next_flow_transition_name.contains("开始"))); - assert!(next_transitions.iter().any(|trans| trans.next_flow_transition_name.contains("关闭"))); + assert_eq!(next_transitions[0].next_flow_transition_name, "关闭"); + assert_eq!(next_transitions[1].next_flow_transition_name, "开始-modify"); + // assert!(next_transitions.iter().any(|trans| trans.next_flow_transition_name.contains("开始"))); + // assert!(next_transitions.iter().any(|trans| trans.next_flow_transition_name.contains("关闭"))); // Find the state and transfer information of the specified instances in batch let state_and_next_transitions: Vec = flow_client .put( diff --git a/spi/spi-log/src/serv/pg/log_pg_initializer.rs b/spi/spi-log/src/serv/pg/log_pg_initializer.rs index 7b76df210..d5b7fc8ba 100644 --- a/spi/spi-log/src/serv/pg/log_pg_initializer.rs +++ b/spi/spi-log/src/serv/pg/log_pg_initializer.rs @@ -36,5 +36,5 @@ pub async fn init_table_and_conn(bs_inst: TypedSpiBsInst<'_, TardisRelDBClient>, None, None, ) - .await + .await } diff --git a/spi/spi-log/src/serv/pg/log_pg_item_serv.rs b/spi/spi-log/src/serv/pg/log_pg_item_serv.rs index c6412f133..cef2b198b 100644 --- a/spi/spi-log/src/serv/pg/log_pg_item_serv.rs +++ b/spi/spi-log/src/serv/pg/log_pg_item_serv.rs @@ -1,8 +1,8 @@ use tardis::{ basic::{dto::TardisContext, result::TardisResult}, db::{reldb_client::TardisRelDBClient, sea_orm::Value}, - TardisFuns, - TardisFunsInst, web::web_resp::TardisPage, + web::web_resp::TardisPage, + TardisFuns, TardisFunsInst, }; use bios_basic::{basic_enumeration::BasicQueryOpKind, dto::BasicQueryCondInfo, helper::db_helper, spi::spi_funs::SpiBsInst}; @@ -47,7 +47,7 @@ VALUES ), params, ) - .await?; + .await?; conn.commit().await?; Ok(id) } @@ -127,7 +127,7 @@ pub async fn find(find_req: &mut LogItemFindReq, funs: &TardisFunsInst, ctx: &Ta Err(funs.err().not_found( "item", "log", - &format!("The ext field=[{}] value=[{}] operation=[{}] is not legal.", &ext.field, ext.value, &ext.op, ), + &format!("The ext field=[{}] value=[{}] operation=[{}] is not legal.", &ext.field, ext.value, &ext.op,), "404-spi-log-op-not-legal", )) }; @@ -274,7 +274,7 @@ ORDER BY ts DESC where_fragments.join(" AND "), page_fragments ) - .as_str(), + .as_str(), sql_vals, ) .await?;