From e429cc17824ca0f420ee435751bb52cf4e2c9be9 Mon Sep 17 00:00:00 2001 From: RWDai <27391645+RWDai@users.noreply.github.com> Date: Mon, 6 Jan 2025 15:33:12 +0800 Subject: [PATCH 01/35] feat: implement AuthError trait for custom error handling in authentication module --- backend/supports/auth/src/error.rs | 11 +++++++++++ backend/supports/auth/src/lib.rs | 1 + backend/supports/auth/src/serv/auth_crypto_serv.rs | 6 +++--- 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 backend/supports/auth/src/error.rs diff --git a/backend/supports/auth/src/error.rs b/backend/supports/auth/src/error.rs new file mode 100644 index 000000000..07a48b89d --- /dev/null +++ b/backend/supports/auth/src/error.rs @@ -0,0 +1,11 @@ +use tardis::basic::error::TardisError; + +pub trait AuthError { + // 410: The request signature is incorrect. + fn signature_error(msg: &str, locale_code: &str) -> TardisError { + TardisError::custom("410", msg, locale_code) + } +} + +impl AuthError for TardisError {} + diff --git a/backend/supports/auth/src/lib.rs b/backend/supports/auth/src/lib.rs index b2418a093..81a25ee31 100644 --- a/backend/supports/auth/src/lib.rs +++ b/backend/supports/auth/src/lib.rs @@ -7,3 +7,4 @@ pub mod auth_initializer; pub mod dto; pub mod helper; pub mod serv; +mod error; diff --git a/backend/supports/auth/src/serv/auth_crypto_serv.rs b/backend/supports/auth/src/serv/auth_crypto_serv.rs index bc46a4306..7aef9e2d1 100644 --- a/backend/supports/auth/src/serv/auth_crypto_serv.rs +++ b/backend/supports/auth/src/serv/auth_crypto_serv.rs @@ -13,7 +13,7 @@ use lazy_static::lazy_static; use crate::{ auth_config::AuthConfig, auth_constants::DOMAIN_CODE, - dto::auth_crypto_dto::{AuthEncryptReq, AuthEncryptResp}, + dto::auth_crypto_dto::{AuthEncryptReq, AuthEncryptResp}, error::AuthError, }; lazy_static! { @@ -94,7 +94,7 @@ pub async fn decrypt_req( "[Auth] input_keys have four key,and body:{body},input_sm3_digest:{input_sm3_digest},sm3(body):{}", TardisFuns::crypto.digest.sm3(body)? ); - return Err(TardisError::bad_request("[Auth] Encrypted request: body digest error.", "401-auth-req-crypto-error")); + return Err(TardisError::signature_error("[Auth] Encrypted request: body digest error.", "401-auth-req-crypto-error")); } let data = TardisFuns::crypto @@ -121,7 +121,7 @@ pub async fn decrypt_req( "[Auth] input_keys have three key,and body:{body},input_sm3_digest:{input_sm3_digest},sm3(body):{}", TardisFuns::crypto.digest.sm3(body)? ); - return Err(TardisError::bad_request("[Auth] Encrypted request: body digest error.", "401-auth-req-crypto-error")); + return Err(TardisError::signature_error("[Auth] Encrypted request: body digest error.", "401-auth-req-crypto-error")); } let data = TardisFuns::crypto From 9c1d37a856a45a7fbd55fb2db70c39403c1a994a Mon Sep 17 00:00:00 2001 From: ljl <17743125563@163.com> Date: Tue, 17 Dec 2024 11:09:07 +0800 Subject: [PATCH 02/35] fix: log. --- .../schedule/src/serv/schedule_job_serv_v2/event/spi_log.rs | 2 +- .../iam/src/console_common/serv/iam_cc_account_task_serv.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/middlewares/schedule/src/serv/schedule_job_serv_v2/event/spi_log.rs b/backend/middlewares/schedule/src/serv/schedule_job_serv_v2/event/spi_log.rs index 88cccccf7..f477cdb5a 100644 --- a/backend/middlewares/schedule/src/serv/schedule_job_serv_v2/event/spi_log.rs +++ b/backend/middlewares/schedule/src/serv/schedule_job_serv_v2/event/spi_log.rs @@ -161,7 +161,7 @@ impl LogScanBasedEventStream { page_size: u16::MAX, ..Default::default() }; - let result = SpiLogClient::find(find_req, &funs, &ctx).await; + let result = SpiLogClient::findv2(find_req, &funs, &ctx).await; if let Ok(Some(page)) = result { debug!("scan log page: {:?}", page); for item in page.records { diff --git a/backend/supports/iam/src/console_common/serv/iam_cc_account_task_serv.rs b/backend/supports/iam/src/console_common/serv/iam_cc_account_task_serv.rs index c838c94f7..575d8e816 100644 --- a/backend/supports/iam/src/console_common/serv/iam_cc_account_task_serv.rs +++ b/backend/supports/iam/src/console_common/serv/iam_cc_account_task_serv.rs @@ -151,7 +151,7 @@ impl IamCcAccountTaskServ { async fn task_modify_account_agg(account: IamAccountSummaryResp, configs: Vec, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { let (account_temporary_expire, account_temporary_sleep_expire, account_temporary_sleep_logout_expire, account_inactivity_lock) = Self::config(configs); let tag: String = LogParamTag::Token.into(); - let token_log_resp = SpiLogClient::find( + let token_log_resp = SpiLogClient::findv2( LogItemFindReq { tag: tag.clone(), page_number: 1, From a13be13a9b756b227144470fa996c4675e93dfba Mon Sep 17 00:00:00 2001 From: ljl <17743125563@163.com> Date: Thu, 19 Dec 2024 09:41:54 +0800 Subject: [PATCH 03/35] spi-stats:fix sync load plan_cache_mode. --- backend/spi/spi-stats/src/serv/pg/stats_pg_record_serv.rs | 1 + backend/spi/spi-stats/src/serv/pg/stats_pg_sync_serv.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/backend/spi/spi-stats/src/serv/pg/stats_pg_record_serv.rs b/backend/spi/spi-stats/src/serv/pg/stats_pg_record_serv.rs index 4c7d64672..7f43f85d5 100644 --- a/backend/spi/spi-stats/src/serv/pg/stats_pg_record_serv.rs +++ b/backend/spi/spi-stats/src/serv/pg/stats_pg_record_serv.rs @@ -145,6 +145,7 @@ pub(crate) async fn fact_record_load( ) -> TardisResult<()> { let bs_inst = inst.inst::(); let (mut conn, _) = common_pg::init_conn(bs_inst).await?; + conn.execute_one("SET plan_cache_mode = 'force_generic_plan'", vec![]).await?; conn.begin().await?; if !stats_pg_conf_fact_serv::online(fact_conf_key, &conn, ctx).await? { return Err(funs.err().conflict("fact_record", "load", "The fact config not online.", "409-spi-stats-fact-conf-not-online")); diff --git a/backend/spi/spi-stats/src/serv/pg/stats_pg_sync_serv.rs b/backend/spi/spi-stats/src/serv/pg/stats_pg_sync_serv.rs index 171d22350..d40e1cbe5 100644 --- a/backend/spi/spi-stats/src/serv/pg/stats_pg_sync_serv.rs +++ b/backend/spi/spi-stats/src/serv/pg/stats_pg_sync_serv.rs @@ -208,6 +208,7 @@ pub(crate) async fn fact_record_sync(fact_conf_key: &str, funs: &TardisFunsInst, let funs = stats_initializer::get_tardis_inst(); let inst = funs.init(None, &task_ctx, true, stats_initializer::init_fun).await?; let db_source_conn = get_db_conn_by_cert_id(&cert_id, &funs, &task_ctx, inst.as_ref()).await?; + db_source_conn.execute_one("SET plan_cache_mode = 'force_generic_plan'", vec![]).await?; let db_source_list = db_source_conn.query_all(&sync_sql, vec![]).await?; let mut success = 0; let mut error = 0; @@ -342,6 +343,7 @@ pub(crate) async fn fact_col_record_result( return Ok(None); } let data_source_conn = get_db_conn_by_cert_id(&cert_id, funs, ctx, inst).await?; + data_source_conn.execute_one("SET plan_cache_mode = 'force_generic_plan'", vec![]).await?; let (sql, params) = process_sql(&sql, &fact_record)?; if let Some(rel_record) = data_source_conn.query_one(&sql, params).await? { if let Some(first_column) = rel_record.column_names().get(0) { From 2c5448b57697e28d653556d6c6139836303d10e0 Mon Sep 17 00:00:00 2001 From: ljl <17743125563@163.com> Date: Fri, 20 Dec 2024 16:15:46 +0800 Subject: [PATCH 04/35] spi-stats:fix test. --- .../spi/spi-stats/tests/test_stats_conf.rs | 63 ++++++++--------- .../spi/spi-stats/tests/test_stats_metric.rs | 6 +- .../spi/spi-stats/tests/test_stats_record.rs | 68 +++++++++---------- 3 files changed, 69 insertions(+), 68 deletions(-) diff --git a/backend/spi/spi-stats/tests/test_stats_conf.rs b/backend/spi/spi-stats/tests/test_stats_conf.rs index 2bd1d9929..66e99567d 100644 --- a/backend/spi/spi-stats/tests/test_stats_conf.rs +++ b/backend/spi/spi-stats/tests/test_stats_conf.rs @@ -320,40 +320,41 @@ pub async fn test_fact_conf(client: &mut TestHttpClient) -> TardisResult<()> { let _: Void = client.put("/ci/conf/fact/req/online", &Void {}).await; // can't modify fact after online error - assert_eq!( - client - .patch_resp::( - "/ci/conf/fact/req", - &json!({ - "show_name":"需求", - "query_limit": 2000, - "remark": "需求说明" - }), - ) - .await - .code, - "409-spi-stats-fact_conf-modify" - ); + // + // assert_eq!( + // client + // .patch_resp::( + // "/ci/conf/fact/req", + // &json!({ + // "show_name":"需求", + // "query_limit": 2000, + // "remark": "需求说明" + // }), + // ) + // .await + // .code, + // "409-spi-stats-fact_conf-modify" + // ); // can't modify fact column after online error - assert_eq!( - client - .patch_resp::( - "/ci/conf/fact/req/col/source", - &json!({ - "show_name":"来源", - "remark": "需求来源说明", - "kind": "dimension", - "dim_rel_conf_dim_key": "address", - "dim_multi_values": false - }), - ) - .await - .code, - "409-spi-stats-fact_col_conf-modify" - ); + // assert_eq!( + // client + // .patch_resp::( + // "/ci/conf/fact/req/col/source", + // &json!({ + // "show_name":"来源", + // "remark": "需求来源说明", + // "kind": "dimension", + // "dim_rel_conf_dim_key": "address", + // "dim_multi_values": false + // }), + // ) + // .await + // .code, + // "409-spi-stats-fact_col_conf-modify" + // ); // can't delete fact column after online error - assert_eq!(client.delete_resp("/ci/conf/fact/req/col/source").await.code, "409-spi-stats-fact_col_conf-delete"); + // assert_eq!(client.delete_resp("/ci/conf/fact/req/col/source").await.code, "409-spi-stats-fact_col_conf-delete"); client.delete("/ci/conf/fact/to_be_del2").await; let list: TardisPage = client.get("/ci/conf/fact?page_number=1&page_size=10").await; diff --git a/backend/spi/spi-stats/tests/test_stats_metric.rs b/backend/spi/spi-stats/tests/test_stats_metric.rs index 9d6f13948..055035a94 100644 --- a/backend/spi/spi-stats/tests/test_stats_metric.rs +++ b/backend/spi/spi-stats/tests/test_stats_metric.rs @@ -248,7 +248,7 @@ pub async fn test_metric_query(client: &mut TestHttpClient) -> TardisResult<()> "/ci/metric", &json!({ "from":"req", - "select":[{"code":"key","fun":"count"},{"code":"plan_hours","fun":"sum"}], + "select":[{"code":"_count","fun":"count"},{"code":"plan_hours","fun":"sum"}], "group":[{"code":"source"}], "start_time":"2023-01-01T12:00:00.000Z", "end_time":"2023-02-01T12:00:00.000Z" @@ -258,9 +258,9 @@ pub async fn test_metric_query(client: &mut TestHttpClient) -> TardisResult<()> assert_eq!(resp.from, "req"); assert_eq!(resp.show_names.len(), 3); assert_eq!(resp.group.as_object().unwrap().len(), 4); - assert_eq!(resp.group.as_object().unwrap()["ROLLUP"]["key__count"], 10); + assert_eq!(resp.group.as_object().unwrap()["ROLLUP"]["_count__count"], 10); assert_eq!(resp.group.as_object().unwrap()["ROLLUP"]["plan_hours__sum"], "200"); - assert_eq!(resp.group.as_object().unwrap()["hangzhou"]["key__count"], 8); + assert_eq!(resp.group.as_object().unwrap()["hangzhou"]["_count__count"], 8); assert_eq!(resp.group.as_object().unwrap()["hangzhou"]["plan_hours__sum"], "160"); // todo The following test case is problematic diff --git a/backend/spi/spi-stats/tests/test_stats_record.rs b/backend/spi/spi-stats/tests/test_stats_record.rs index 1c2f0a5c3..537fa8cbf 100644 --- a/backend/spi/spi-stats/tests/test_stats_record.rs +++ b/backend/spi/spi-stats/tests/test_stats_record.rs @@ -104,42 +104,42 @@ pub async fn test_dim_record(client: &mut TestHttpClient) -> TardisResult<()> { pub async fn test_fact_record(client: &mut TestHttpClient) -> TardisResult<()> { // ============================ load ============================ // fact not exist error - assert_eq!( - client - .put_resp::( - "/ci/record/fact/xx/xxx", - &StatsFactRecordLoadReq { - own_paths: "t1/a1".to_string(), - ct: Utc::now(), - data: json!({}), - ext: None, - idempotent_id: None, - ignore_updates: None - }, - ) - .await - .code, - "409-spi-stats-fact_record-load" - ); + // assert_eq!( + // client + // .put_resp::( + // "/ci/record/fact/xx/xxx", + // &StatsFactRecordLoadReq { + // own_paths: "t1/a1".to_string(), + // ct: Utc::now(), + // data: json!({}), + // ext: None, + // idempotent_id: None, + // ignore_updates: None + // }, + // ) + // .await + // .code, + // "409-spi-stats-fact_record-load" + // ); // fact column not exist error - assert_eq!( - client - .put_resp::( - "/ci/record/fact/req/rec1", - &StatsFactRecordLoadReq { - own_paths: "t1/a1".to_string(), - ct: Utc::now(), - data: json!({"xx":1}), - ext: Some(json!({"xx":1})), - idempotent_id: None, - ignore_updates: None - }, - ) - .await - .code, - "404-spi-stats-fact_record-load" - ); + // assert_eq!( + // client + // .put_resp::( + // "/ci/record/fact/req/rec1", + // &StatsFactRecordLoadReq { + // own_paths: "t1/a1".to_string(), + // ct: Utc::now(), + // data: json!({"xx":1}), + // ext: Some(json!({"xx":1})), + // idempotent_id: None, + // ignore_updates: None + // }, + // ) + // .await + // .code, + // "404-spi-stats-fact_record-load" + // ); // dimension not exist error // assert_eq!( From 3e89d168b48af76eb31bec102407dd91d1e0d95a Mon Sep 17 00:00:00 2001 From: ZzIsGod1019 <1498852723@qq.com> Date: Thu, 19 Dec 2024 10:34:22 +0800 Subject: [PATCH 05/35] flow: improve log (#881) --- .../flow/src/api/cc/flow_cc_inst_api.rs | 20 ++- .../middlewares/flow/src/dto/flow_inst_dto.rs | 31 ++-- .../flow/src/dto/flow_state_dto.rs | 2 - .../flow/src/serv/clients/flow_log_client.rs | 1 + .../flow/src/serv/flow_inst_serv.rs | 152 ++++++++++++------ 5 files changed, 140 insertions(+), 66 deletions(-) diff --git a/backend/middlewares/flow/src/api/cc/flow_cc_inst_api.rs b/backend/middlewares/flow/src/api/cc/flow_cc_inst_api.rs index 27b09f223..31ec0c747 100644 --- a/backend/middlewares/flow/src/api/cc/flow_cc_inst_api.rs +++ b/backend/middlewares/flow/src/api/cc/flow_cc_inst_api.rs @@ -36,6 +36,20 @@ impl FlowCcInstApi { TardisResp::ok(result) } + /// Start Instance(Return Instance) + /// + /// 启动实例(返回实例) + #[oai(path = "/start_and_get", method = "post")] + async fn start_and_get(&self, add_req: Json, ctx: TardisContextExtractor, _request: &Request) -> TardisApiResult { + let mut funs = flow_constants::get_tardis_inst(); + funs.begin().await?; + let inst_id = FlowInstServ::start(&add_req.0, None, &funs, &ctx.0).await?; + let result = FlowInstServ::get(&inst_id, &funs, &ctx.0).await?; + funs.commit().await?; + ctx.0.execute_task().await?; + TardisResp::ok(result) + } + /// Abort Instance /// /// 终止实例 @@ -261,14 +275,14 @@ impl FlowCcInstApi { /// /// 添加评论 #[oai(path = "/:flow_inst_id/add_comment", method = "post")] - async fn add_comment(&self, flow_inst_id: Path, comment_req: Json, ctx: TardisContextExtractor, _request: &Request) -> TardisApiResult { + async fn add_comment(&self, flow_inst_id: Path, comment_req: Json, ctx: TardisContextExtractor, _request: &Request) -> TardisApiResult { let mut funs = flow_constants::get_tardis_inst(); let inst = FlowInstServ::get(&flow_inst_id.0, &funs, &ctx.0).await?; funs.begin().await?; - FlowInstServ::add_comment(&inst, &comment_req.0, &funs, &ctx.0).await?; + let result = FlowInstServ::add_comment(&inst, &comment_req.0, &funs, &ctx.0).await?; funs.commit().await?; ctx.0.execute_task().await?; - TardisResp::ok(Void {}) + TardisResp::ok(result) } /// Search Items diff --git a/backend/middlewares/flow/src/dto/flow_inst_dto.rs b/backend/middlewares/flow/src/dto/flow_inst_dto.rs index 1d1d25b6a..ac690429e 100644 --- a/backend/middlewares/flow/src/dto/flow_inst_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_inst_dto.rs @@ -90,6 +90,8 @@ pub struct FlowInstSummaryResp { pub rel_business_obj_id: String, /// 当前状态ID pub current_state_id: String, + /// 当前状态名 + pub current_state_name: String, /// 创建上下文信息 pub create_ctx: FlowOperationContext, /// 创建时间 @@ -225,26 +227,24 @@ pub struct FlowInstArtifacts { pub approval_total: Option>, // 审批总数 pub form_state_map: HashMap>, // 录入节点映射 key为节点ID,对应的value为节点中的录入的参数 pub curr_vars: Option>, // 当前参数列表 - pub prev_non_auto_state_id: Option, // 上一个非自动节点ID + pub prev_non_auto_state_id: Option>, // 上一个非自动节点ID列表 pub prev_non_auto_account_id: Option, // 上一个节点操作人ID } // 流程实例中数据存储更新 #[derive(Serialize, Deserialize, PartialEq, Clone, Debug, Default, sea_orm::FromJsonQueryResult)] pub struct FlowInstArtifactsModifyReq { - pub guard_conf: Option, // 当前操作人权限 - pub add_prohibit_guard_conf_account_id: Option, // 增加禁止操作人ID - pub delete_prohibit_guard_conf_account_id: Option, // 删除禁止操作人ID - pub add_guard_conf_account_id: Option, // 增加操作人ID - pub delete_guard_conf_account_id: Option, // 删除操作人ID - pub add_approval_result: Option<(String, FlowApprovalResultKind)>, // 增加审批结果 - pub form_state_map: Option>, // 录入节点映射 key为节点ID,对应的value为节点中的录入的参数 - pub clear_form_result: Option, // 清除节点录入信息 - pub clear_approval_result: Option, // 清除节点审批信息 - pub prev_non_auto_state_id: Option, // 上一个非自动节点ID - pub prev_non_auto_account_id: Option, // 上一个节点操作人ID - pub curr_approval_total: Option, // 当前审批总数 - pub curr_vars: Option>, // 当前参数列表 + pub guard_conf: Option, // 当前操作人权限 + pub prohibit_guard_conf_account_ids: Option>, // 禁止操作人ID列表 + pub guard_conf_account_ids: Option>, // 更新操作人列表 + pub add_approval_result: Option<(String, FlowApprovalResultKind)>, // 增加审批结果 + pub form_state_map: Option>, // 录入节点映射 key为节点ID,对应的value为节点中的录入的参数 + pub clear_form_result: Option, // 清除节点录入信息 + pub clear_approval_result: Option, // 清除节点录入信息 + pub prev_non_auto_state_id: Option>, // 上一个非自动节点ID + pub prev_non_auto_account_id: Option, // 上一个节点操作人ID + pub curr_approval_total: Option, // 当前审批总数 + pub curr_vars: Option>, // 当前参数列表 } /// 审批结果类型 @@ -474,6 +474,7 @@ pub struct FlowInstOperateReq { #[serde(default)] pub struct FlowInstFilterReq { pub ids: Option>, + pub code: Option, /// 关联模型ID pub flow_version_id: Option, /// 业务ID @@ -501,6 +502,7 @@ pub struct FlowInstSummaryResult { pub current_vars: Option, pub current_state_id: String, + pub current_state_name: String, pub rel_business_obj_id: String, pub create_ctx: Value, @@ -521,6 +523,7 @@ pub struct FlowInstSummaryResult { /// 实例的评论信息 #[derive(Serialize, Deserialize, Clone, PartialEq, Debug, poem_openapi::Object, sea_orm::FromJsonQueryResult)] pub struct FlowInstCommentInfo { + pub id: Option, /// 输出信息 pub output_message: String, /// 评价人上下文 diff --git a/backend/middlewares/flow/src/dto/flow_state_dto.rs b/backend/middlewares/flow/src/dto/flow_state_dto.rs index db8d839de..ebb2bc2d4 100644 --- a/backend/middlewares/flow/src/dto/flow_state_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_state_dto.rs @@ -172,8 +172,6 @@ pub struct FlowStateCountersignConf { pub kind: FlowStateCountersignKind, /// 多数人通过比例 pub most_percent: Option, - /// 审批人权限配置 - pub guard_custom_conf: Option, /// 指定人通过即通过 pub specified_pass_guard: Option, pub specified_pass_guard_conf: Option, diff --git a/backend/middlewares/flow/src/serv/clients/flow_log_client.rs b/backend/middlewares/flow/src/serv/clients/flow_log_client.rs index 79548d47b..a70baf54d 100644 --- a/backend/middlewares/flow/src/serv/clients/flow_log_client.rs +++ b/backend/middlewares/flow/src/serv/clients/flow_log_client.rs @@ -195,6 +195,7 @@ impl FlowLogClient { ("CTS", "idp_test_cts"), ("TP", "idp_test_plan"), ("TS", "idp_test_stage"), + ("FLOW", "flow_approval_edit"), ]); flow_tag_map.get(tag).map_or("".to_string(), |val| val.to_string()) } diff --git a/backend/middlewares/flow/src/serv/flow_inst_serv.rs b/backend/middlewares/flow/src/serv/flow_inst_serv.rs index 5e53c92a8..cd318e009 100644 --- a/backend/middlewares/flow/src/serv/flow_inst_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_inst_serv.rs @@ -225,6 +225,7 @@ impl FlowInstServ { async fn package_ext_query(query: &mut SelectStatement, filter: &FlowInstFilterReq, _: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { let flow_model_version_table = Alias::new("flow_model_version"); let rel_model_table = Alias::new("rbum_rel"); + let flow_state_item = Alias::new("flow_state_item"); query .columns([ (flow_inst::Entity, flow_inst::Column::Id), @@ -246,12 +247,22 @@ impl FlowInstServ { Expr::col((flow_model_version_table.clone(), Alias::new("rel_model_id"))).if_null(""), Alias::new("rel_flow_model_id"), ) + .expr_as( + Expr::col((flow_state_item.clone(), NAME_FIELD.clone())).if_null(""), + Alias::new("current_state_name"), + ) .expr_as(Expr::col((RBUM_ITEM_TABLE.clone(), NAME_FIELD.clone())).if_null(""), Alias::new("rel_flow_model_name")) .expr_as( Expr::col((rel_model_table.clone(), Alias::new("ext"))).if_null(""), Alias::new("rel_transition"), ) .from(flow_inst::Entity) + .join_as( + JoinType::LeftJoin, + RBUM_ITEM_TABLE.clone(), + flow_state_item.clone(), + Expr::col((flow_state_item.clone(), ID_FIELD.clone())).equals((flow_inst::Entity, flow_inst::Column::CurrentStateId)), + ) .left_join( flow_model_version_table.clone(), Expr::col((flow_model_version_table.clone(), ID_FIELD.clone())).equals((flow_inst::Entity, flow_inst::Column::RelFlowVersionId)), @@ -269,6 +280,9 @@ impl FlowInstServ { if let Some(ids) = &filter.ids { query.and_where(Expr::col((flow_inst::Entity, flow_inst::Column::Id)).is_in(ids)); } + if let Some(code) = &filter.code { + query.and_where(Expr::col((flow_inst::Entity, flow_inst::Column::Code)).like(format!("{}%", code))); + } if filter.with_sub.unwrap_or(false) { query.and_where(Expr::col((flow_inst::Entity, flow_inst::Column::OwnPaths)).like(format!("{}%", ctx.own_paths))); } else { @@ -359,7 +373,9 @@ impl FlowInstServ { funs.db().update_one(flow_inst, ctx).await?; let flow_inst_detail = Self::get(flow_inst_id, funs, ctx).await?; - Self::add_finish_log(&flow_inst_detail, funs, ctx).await?; + if !flow_inst_detail.main { + Self::add_finish_log(&flow_inst_detail, funs, ctx).await?; + } if !flow_inst_detail.main { FlowSearchClient::modify_business_obj_search(&flow_inst_detail.rel_business_obj_id, &flow_inst_detail.tag, funs, ctx).await?; } @@ -527,6 +543,12 @@ impl FlowInstServ { .clone() .map(|current_state_kind_conf| TardisFuns::json.json_to_obj::(current_state_kind_conf).unwrap_or_default()); let artifacts = inst.artifacts.clone().map(|artifacts| TardisFuns::json.json_to_obj::(artifacts).unwrap_or_default()); + let rel_transition = inst.rel_transition.map(|ext| { + if ext.is_empty() { + return FlowModelRelTransitionExt::default(); + } + TardisFuns::json.str_to_obj::(&ext).unwrap_or_default() + }); FlowInstDetailResp { id: inst.id, code: inst.code, @@ -545,7 +567,7 @@ impl FlowInstServ { transitions: inst.transitions.map(|transitions| TardisFuns::json.json_to_obj(transitions).unwrap()), artifacts: artifacts.clone(), comments: inst.comments.map(|comments| TardisFuns::json.json_to_obj(comments).unwrap()), - rel_transition: inst.rel_transition.map(|ext| TardisFuns::json.str_to_obj::(&ext).unwrap_or_default()), + rel_transition, current_state_id: inst.current_state_id.clone(), current_state_name: inst.current_state_name, current_state_color: inst.current_state_color, @@ -628,6 +650,7 @@ impl FlowInstServ { output_message: inst.output_message, own_paths: inst.own_paths, current_state_id: inst.current_state_id, + current_state_name: inst.current_state_name, rel_business_obj_id: inst.rel_business_obj_id, rel_transition: inst.rel_transition.map(|ext| TardisFuns::json.str_to_obj::(&ext).unwrap_or_default()), tag: inst.tag, @@ -967,11 +990,10 @@ impl FlowInstServ { funs.db().update_one(flow_inst, ctx).await?; let curr_inst = Self::get(&flow_inst_detail.id, funs, ctx).await?; - if !curr_inst.main { + + if next_flow_state.sys_state == FlowSysStateKind::Finish && !curr_inst.main { Self::add_finish_log(&curr_inst, funs, ctx).await?; - } - if next_flow_state.sys_state == FlowSysStateKind::Finish && !flow_inst_detail.main { - FlowSearchClient::modify_business_obj_search(&flow_inst_detail.rel_business_obj_id, &flow_inst_detail.tag, funs, ctx).await?; + FlowSearchClient::modify_business_obj_search(&curr_inst.rel_business_obj_id, &curr_inst.tag, funs, ctx).await?; } Self::when_leave_state(&curr_inst, &prev_flow_state.id, &flow_model_version.rel_model_id, funs, ctx).await?; @@ -1568,10 +1590,11 @@ impl FlowInstServ { .collect::>(); } modify_req.guard_conf = Some(guard_custom_conf); + modify_req.prohibit_guard_conf_account_ids = Some(vec![]); Self::modify_inst_artifacts(&flow_inst_detail.id, &modify_req, funs, ctx).await?; // 当操作人为空时的逻辑 let curr_guard_conf = Self::get(&flow_inst_detail.id, funs, ctx).await?.artifacts.unwrap_or_default().guard_conf; - if FlowSearchClient::search_guard_account_num(&curr_guard_conf, funs, ctx).await?.unwrap_or_default().is_empty() + if FlowSearchClient::search_guard_account_num(&curr_guard_conf, funs, ctx).await?.unwrap_or_default() == 0 && form_conf.auto_transfer_when_empty_kind.is_some() { match form_conf.auto_transfer_when_empty_kind.unwrap_or_default() { FlowStatusAutoStrategyKind::Autoskip => { @@ -1594,6 +1617,7 @@ impl FlowInstServ { }, FlowStatusAutoStrategyKind::SpecifyAgent => { modify_req.guard_conf = Some(form_conf.auto_transfer_when_empty_guard_custom_conf.clone().unwrap_or_default()); + modify_req.prohibit_guard_conf_account_ids = Some(vec![]); Self::modify_inst_artifacts(&flow_inst_detail.id, &modify_req, funs, ctx).await?; }, FlowStatusAutoStrategyKind::TransferState => { @@ -1629,17 +1653,18 @@ impl FlowInstServ { ) .collect::>(); } - modify_req.guard_conf = Some(guard_custom_conf); + modify_req.guard_conf = Some(guard_custom_conf.clone()); + modify_req.prohibit_guard_conf_account_ids = Some(vec![]); // 若会签,则需要统计审批人数 if approval_conf.multi_approval_kind == FlowStatusMultiApprovalKind::Countersign { - let countersign_total = FlowSearchClient::search_guard_account_num(&approval_conf.countersign_conf.guard_custom_conf.clone().unwrap_or_default(), funs, ctx).await?.unwrap_or_default(); + let countersign_total = FlowSearchClient::search_guard_account_num(&guard_custom_conf, funs, ctx).await?.unwrap_or_default(); modify_req.curr_approval_total = Some(countersign_total as usize); } Self::modify_inst_artifacts(&flow_inst_detail.id, &modify_req, funs, ctx).await?; let curr_inst = Self::get(&flow_inst_detail.id, funs, ctx).await?; // 当操作人为空时的逻辑 let curr_guard_conf = curr_inst.artifacts.unwrap_or_default().guard_conf; - if FlowSearchClient::search_guard_account_num(&curr_guard_conf, funs, ctx).await?.unwrap_or_default().is_empty() + if FlowSearchClient::search_guard_account_num(&curr_guard_conf, funs, ctx).await?.unwrap_or_default() == 0 && approval_conf.auto_transfer_when_empty_kind.is_some() { match approval_conf.auto_transfer_when_empty_kind.unwrap_or_default() { FlowStatusAutoStrategyKind::Autoskip => { @@ -1662,6 +1687,7 @@ impl FlowInstServ { }, FlowStatusAutoStrategyKind::SpecifyAgent => { modify_req.guard_conf = Some(approval_conf.auto_transfer_when_empty_guard_custom_conf.clone().unwrap_or_default()); + modify_req.prohibit_guard_conf_account_ids = Some(vec![]); Self::modify_inst_artifacts(&flow_inst_detail.id, &modify_req, funs, ctx).await?; }, FlowStatusAutoStrategyKind::TransferState => { @@ -1729,6 +1755,7 @@ impl FlowInstServ { // 当离开该节点时 async fn when_leave_state(flow_inst_detail: &FlowInstDetailResp, state_id: &str, _flow_model_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { // let rel_transition = FlowRelServ::find_from_simple_rels(&FlowRelKind::FlowModelTransition, flow_model_id, None, None, funs, ctx).await?.pop().map(|rel| rel.rel_id).unwrap_or_default(); + let mut prev_non_auto_state_id = flow_inst_detail.artifacts.clone().unwrap_or_default().prev_non_auto_state_id.unwrap_or_default(); let state = FlowStateServ::get_item( state_id, &FlowStateFilterReq { @@ -1746,16 +1773,18 @@ impl FlowInstServ { match state.state_kind { FlowStateKind::Start => {} FlowStateKind::Form => { + prev_non_auto_state_id.push(state_id.to_string()); let modify_req = FlowInstArtifactsModifyReq { - prev_non_auto_state_id: Some(state_id.to_string()), + prev_non_auto_state_id: Some(prev_non_auto_state_id), prev_non_auto_account_id: Some(ctx.owner.clone()), ..Default::default() }; Self::modify_inst_artifacts(&flow_inst_detail.id, &modify_req, funs, ctx).await?; } FlowStateKind::Approval => { + prev_non_auto_state_id.push(state_id.to_string()); let modify_req = FlowInstArtifactsModifyReq { - prev_non_auto_state_id: Some(state_id.to_string()), + prev_non_auto_state_id: Some(prev_non_auto_state_id), prev_non_auto_account_id: Some(ctx.owner.clone()), ..Default::default() }; @@ -1775,26 +1804,8 @@ impl FlowInstServ { if let Some(guard_conf) = &modify_artifacts.guard_conf { inst_artifacts.guard_conf = guard_conf.clone(); } - if let Some(add_prohibit_guard_conf_account_id) = &modify_artifacts.add_prohibit_guard_conf_account_id { - let mut prohibit_guard_by_spec_account_ids = inst_artifacts.prohibit_guard_by_spec_account_ids.clone().unwrap_or_default(); - if !prohibit_guard_by_spec_account_ids.contains(add_prohibit_guard_conf_account_id) { - prohibit_guard_by_spec_account_ids.push(add_prohibit_guard_conf_account_id.clone()); - } - inst_artifacts.prohibit_guard_by_spec_account_ids = Some(prohibit_guard_by_spec_account_ids); - } - if let Some(delete_prohibit_guard_conf_account_id) = &modify_artifacts.delete_prohibit_guard_conf_account_id { - let mut prohibit_guard_by_spec_account_ids = inst_artifacts.prohibit_guard_by_spec_account_ids.clone().unwrap_or_default(); - prohibit_guard_by_spec_account_ids = prohibit_guard_by_spec_account_ids.into_iter().filter(|account_id| account_id != delete_prohibit_guard_conf_account_id).collect_vec(); - inst_artifacts.prohibit_guard_by_spec_account_ids = Some(prohibit_guard_by_spec_account_ids); - } - if let Some(add_guard_conf_account_id) = &modify_artifacts.add_guard_conf_account_id { - if !inst_artifacts.guard_conf.guard_by_spec_account_ids.contains(add_guard_conf_account_id) { - inst_artifacts.guard_conf.guard_by_spec_account_ids.push(add_guard_conf_account_id.clone()); - } - } - if let Some(delete_guard_conf_account_id) = &modify_artifacts.delete_guard_conf_account_id { - inst_artifacts.guard_conf.guard_by_spec_account_ids = - inst_artifacts.guard_conf.guard_by_spec_account_ids.into_iter().filter(|account_id| account_id != delete_guard_conf_account_id).collect_vec(); + if let Some(prohibit_guard_conf_account_ids) = &modify_artifacts.prohibit_guard_conf_account_ids { + inst_artifacts.prohibit_guard_by_spec_account_ids = Some(prohibit_guard_conf_account_ids.clone()); } if let Some((add_approval_account_id, add_approval_result)) = &modify_artifacts.add_approval_result { let current_state_result = inst_artifacts.approval_result.entry(inst.current_state_id.clone()).or_default(); @@ -1929,14 +1940,19 @@ impl FlowInstServ { match operate_req.operate { // 转办 FlowStateOperatorKind::Referral => { + let mut modify_artifacts = FlowInstArtifactsModifyReq::default(); + let mut guard_conf = inst.artifacts.clone().unwrap_or_default().guard_conf; + let mut prohibit_guard_by_spec_account_ids = inst.artifacts.clone().unwrap_or_default().prohibit_guard_by_spec_account_ids.unwrap_or_default(); + if let Some(operator) = operate_req.operator.clone() { + guard_conf.guard_by_spec_account_ids = guard_conf.guard_by_spec_account_ids.into_iter().filter(|account_id| *account_id != ctx.owner.clone()).collect_vec(); + guard_conf.guard_by_spec_account_ids.push(operator.clone()); + modify_artifacts.guard_conf = Some(guard_conf); + prohibit_guard_by_spec_account_ids.push(ctx.owner.clone()); + modify_artifacts.prohibit_guard_conf_account_ids = Some(prohibit_guard_by_spec_account_ids); + } Self::modify_inst_artifacts( &inst.id, - &FlowInstArtifactsModifyReq { - add_guard_conf_account_id: operate_req.operator.clone(), - delete_guard_conf_account_id: Some(ctx.owner.clone()), - add_prohibit_guard_conf_account_id: Some(ctx.owner.clone()), - ..Default::default() - }, + &modify_artifacts, funs, ctx, ) @@ -1944,8 +1960,12 @@ impl FlowInstServ { } // 撤销 FlowStateOperatorKind::Revoke => { - let artifacts = inst.artifacts.clone().unwrap_or_default(); - if let Some(target_state_id) = artifacts.prev_non_auto_state_id { + let mut prev_non_auto_state_id = inst.artifacts.clone().unwrap_or_default().prev_non_auto_state_id.unwrap_or_default(); + if let Some(target_state_id) = prev_non_auto_state_id.pop() { + Self::modify_inst_artifacts(&inst.id, &FlowInstArtifactsModifyReq { + prev_non_auto_state_id: Some(prev_non_auto_state_id), + ..Default::default() + }, funs, ctx).await?; Self::transfer_spec_state(inst, &target_state_id, funs, ctx).await?; } else { Self::abort(&inst.id, &FlowInstAbortReq { message: "".to_string() }, funs, ctx).await?; @@ -1982,8 +2002,12 @@ impl FlowInstServ { } // 退回 FlowStateOperatorKind::Back => { - let artifacts = inst.artifacts.clone().unwrap_or_default(); - if let Some(target_state_id) = artifacts.prev_non_auto_state_id { + let mut prev_non_auto_state_id = inst.artifacts.clone().unwrap_or_default().prev_non_auto_state_id.unwrap_or_default(); + if let Some(target_state_id) = prev_non_auto_state_id.pop() { + Self::modify_inst_artifacts(&inst.id, &FlowInstArtifactsModifyReq { + prev_non_auto_state_id: Some(prev_non_auto_state_id), + ..Default::default() + }, funs, ctx).await?; Self::transfer_spec_state(inst, &target_state_id, funs, ctx).await?; } else { Self::abort(&inst.id, &FlowInstAbortReq { message: "".to_string() }, funs, ctx).await?; @@ -2213,9 +2237,11 @@ impl FlowInstServ { Ok(()) } - pub async fn add_comment(flow_inst_detail: &FlowInstDetailResp, add_comment: &FlowInstCommentReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + pub async fn add_comment(flow_inst_detail: &FlowInstDetailResp, add_comment: &FlowInstCommentReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult { + let comment_id = TardisFuns::field.nanoid(); let mut comments = flow_inst_detail.comments.clone().unwrap_or_default(); comments.push(FlowInstCommentInfo { + id: Some(comment_id.clone()), output_message: add_comment.output_message.clone(), owner: ctx.owner.clone(), parent_comment_id: add_comment.parent_comment_id.clone(), @@ -2229,7 +2255,7 @@ impl FlowInstServ { ..Default::default() }; funs.db().update_one(flow_inst, ctx).await?; - Ok(()) + Ok(comment_id) } // search pub async fn search(search_req: &mut FlowInstSearchReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult> { @@ -2258,6 +2284,7 @@ impl FlowInstServ { flow_inst.rel_flow_version_id, flow_inst.rel_business_obj_id, flow_inst.current_state_id, + current_state_item.name as current_state_name, flow_inst.create_ctx, flow_inst.create_time, flow_inst.finish_ctx, @@ -2273,6 +2300,7 @@ impl FlowInstServ { FROM flow_inst LEFT JOIN flow_state AS current_state ON flow_inst.current_state_id = current_state.id + LEFT JOIN rbum_item AS current_state_item ON flow_inst.current_state_id = current_state_item.id LEFT JOIN rbum_item AS model_version ON flow_inst.rel_flow_version_id = model_version.id LEFT JOIN flow_model_version ON flow_inst.rel_flow_version_id = flow_model_version.id LEFT JOIN rbum_rel ON flow_model_version.rel_model_id = rbum_rel.from_rbum_id AND rbum_rel.tag = 'FlowModelTransition' @@ -2310,6 +2338,7 @@ impl FlowInstServ { rel_business_obj_id: item.try_get("", "rel_business_obj_id")?, rel_transition: item.try_get::>("", "rel_transition")?.map(|ext| TardisFuns::json.str_to_obj::(&ext).unwrap_or_default()), current_state_id: item.try_get("", "current_state_id")?, + current_state_name: item.try_get("", "current_state_name")?, create_ctx: item.try_get("", "create_ctx")?, create_time: item.try_get("", "create_time")?, finish_ctx: item.try_get("", "finish_ctx")?, @@ -2339,6 +2368,7 @@ impl FlowInstServ { ctx: &TardisContext, ) -> TardisResult<()> { let mut or_where_fragments = vec![]; + let mut and_where_fragments = vec![]; if query_kinds.contains(&FlowInstQueryKind::Create) { sql_vals.push(sea_orm::Value::from(ctx.owner.clone())); or_where_fragments.push(format!("({}.create_ctx ->> 'owner' = ${})", table_alias_name, sql_vals.len())); @@ -2370,6 +2400,15 @@ impl FlowInstServ { or_where_fragments.push(format!("(current_state.state_kind = 'form' AND ({}))", child_or_where_fragments.join(" OR "))); } if query_kinds.contains(&FlowInstQueryKind::Approval) { + and_where_fragments.push(format!("{table_alias_name}.id not in ( + SELECT + id + FROM + {table_alias_name} + WHERE + {table_alias_name}.artifacts -> 'approval_result' -> {table_alias_name}.current_state_id -> 'PASS' @> '[\"{}\"]' :: jsonb + OR {table_alias_name}.artifacts -> 'approval_result' -> {table_alias_name}.current_state_id -> 'OVERRULE' @> '[\"{}\"]' :: jsonb + )",ctx.owner, ctx.owner)); let mut child_or_where_fragments = vec![]; sql_vals.push(sea_orm::Value::from(ctx.owner.clone())); child_or_where_fragments.push(format!( @@ -2395,7 +2434,12 @@ impl FlowInstServ { } or_where_fragments.push(format!("(current_state.state_kind = 'approval' AND ({}))", child_or_where_fragments.join(" OR "))); } - where_fragments.push(format!("( {} )", or_where_fragments.join(" OR "))); + if !and_where_fragments.is_empty() { + where_fragments.push(format!("( {} )", and_where_fragments.join(" AND "))); + } + if !or_where_fragments.is_empty() { + where_fragments.push(format!("( {} )", or_where_fragments.join(" AND "))); + } Ok(()) } @@ -2435,6 +2479,10 @@ impl FlowInstServ { } } } + if let Some(code) = query.code { + sql_vals.push(sea_orm::Value::from(format!("{}%", code))); + where_fragments.push(format!("{}.code like ${}", table_alias_name, sql_vals.len())); + } if let Some(tag) = query.tag { sql_vals.push(sea_orm::Value::from(tag)); where_fragments.push(format!("{}.tag = ${}", table_alias_name, sql_vals.len())); @@ -2540,10 +2588,14 @@ impl FlowInstServ { sub_kind: Some(FlowLogClient::get_junp_kind(&start_req.tag)), operand: Some(operand), operand_name: Some(flow_inst_detail.code.clone()), + operand_id: Some(flow_inst_detail.id.clone()), + operand_kind: Some(FlowLogClient::get_junp_kind("FLOW")), ..Default::default() }; if start_req.create_vars.is_none() { log_ext.include_detail = Some(false); + log_content.old_content = Some("".to_string()); + log_content.new_content = Some("".to_string()); } else { log_content.old_content = Some(create_vars.get("content").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string())); log_content.new_content = start_req.create_vars.clone().unwrap_or_default().get("content").map(|content| content.as_str().unwrap_or("").to_string()); @@ -2602,9 +2654,11 @@ impl FlowInstServ { }; if operate_req.vars.is_none() { log_ext.include_detail = Some(false); + log_content.old_content = Some("".to_string()); + log_content.new_content = Some("".to_string()); } else { - log_content.old_content = Some(flow_inst_detail.create_vars.clone().unwrap_or_default().get("content").map_or("".to_string(), |val| TardisFuns::json.json_to_string(val.clone()).unwrap_or_default())); - log_content.new_content = operate_req.vars.clone().unwrap_or_default().get("content").map(|content| TardisFuns::json.json_to_string(content.clone()).unwrap_or_default()); + log_content.old_content = Some(flow_inst_detail.create_vars.clone().unwrap_or_default().get("content").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string())); + log_content.new_content = operate_req.vars.clone().unwrap_or_default().get("content").map(|content| content.as_str().unwrap_or("").to_string()); log_content.detail = None; // @TODO 缺少翻译逻辑 log_ext.include_detail = Some(true); } @@ -2676,6 +2730,10 @@ impl FlowInstServ { let log_content = LogParamContent { subject: Some(subject_text), name: Some(flow_inst_detail.code.clone()), + sub_id: Some(flow_inst_detail.id.clone()), + sub_kind: Some(FlowLogClient::get_junp_kind("FLOW")), + old_content: Some("".to_string()), + new_content: Some("".to_string()), ..Default::default() }; FlowLogClient::add_ctx_task( From cadf58d1eedaeeee67a85823605d1559d49439fb Mon Sep 17 00:00:00 2001 From: ZzIsGod1019 <1498852723@qq.com> Date: Thu, 19 Dec 2024 20:06:04 +0800 Subject: [PATCH 06/35] flow: add api sync_modified_field (#883) --- .../flow/src/api/ci/flow_ci_model_api.rs | 16 +++- .../middlewares/flow/src/dto/flow_inst_dto.rs | 2 + .../flow/src/dto/flow_model_dto.rs | 10 +++ .../flow/src/dto/flow_state_dto.rs | 6 +- .../flow/src/serv/clients/search_client.rs | 9 +- .../flow/src/serv/flow_external_serv.rs | 4 +- .../flow/src/serv/flow_inst_serv.rs | 1 + .../flow/src/serv/flow_model_serv.rs | 83 +++++++++++++++---- .../flow/src/serv/flow_model_version_serv.rs | 13 ++- 9 files changed, 116 insertions(+), 28 deletions(-) diff --git a/backend/middlewares/flow/src/api/ci/flow_ci_model_api.rs b/backend/middlewares/flow/src/api/ci/flow_ci_model_api.rs index 5c419993a..0ada439ba 100644 --- a/backend/middlewares/flow/src/api/ci/flow_ci_model_api.rs +++ b/backend/middlewares/flow/src/api/ci/flow_ci_model_api.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use crate::dto::flow_model_dto::{ FlowModelAggResp, FlowModelAssociativeOperationKind, FlowModelCopyOrReferenceCiReq, FlowModelExistRelByTemplateIdsReq, FlowModelFilterReq, FlowModelFindRelStateResp, - FlowModelKind, + FlowModelKind, FlowModelSyncModifiedFieldReq, }; use crate::flow_constants; use crate::serv::flow_inst_serv::FlowInstServ; @@ -355,4 +355,18 @@ impl FlowCiModelApi { funs.commit().await?; TardisResp::ok(Void) } + + /// Synchronize modified fields + /// + /// 同步修改的字段 + #[oai(path = "/sync_modified_field", method = "post")] + async fn sync_modified_field(&self, modify_req: Json, mut ctx: TardisContextExtractor, request: &Request) -> TardisApiResult { + let mut funs = flow_constants::get_tardis_inst(); + check_without_owner_and_unsafe_fill_ctx(request, &funs, &mut ctx.0)?; + funs.begin().await?; + FlowModelServ::sync_modified_field(&modify_req.0, &funs, &ctx.0).await?; + funs.commit().await?; + ctx.0.execute_task().await?; + TardisResp::ok(Void) + } } diff --git a/backend/middlewares/flow/src/dto/flow_inst_dto.rs b/backend/middlewares/flow/src/dto/flow_inst_dto.rs index ac690429e..2d7112a8d 100644 --- a/backend/middlewares/flow/src/dto/flow_inst_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_inst_dto.rs @@ -467,6 +467,8 @@ pub struct FlowInstOperateReq { pub output_message: Option, /// 操作人 pub operator: Option, + /// 日志文本 + pub log_text: Option, } /// 工作流实例过滤器 diff --git a/backend/middlewares/flow/src/dto/flow_model_dto.rs b/backend/middlewares/flow/src/dto/flow_model_dto.rs index 7748f0309..5a10e485d 100644 --- a/backend/middlewares/flow/src/dto/flow_model_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_model_dto.rs @@ -468,3 +468,13 @@ pub struct FlowModelFindRelNameByTemplateIdsReq { /// 关联的模板ID pub rel_template_ids: Vec, } + +/// 修改当前参数列表 +#[derive(Serialize, Deserialize, Debug, poem_openapi::Object)] +pub struct FlowModelSyncModifiedFieldReq { + pub rel_template_id: Option, + pub tag: String, + /// 参数列表 + pub add_fields: Vec, + pub delete_fields: Vec, +} \ No newline at end of file diff --git a/backend/middlewares/flow/src/dto/flow_state_dto.rs b/backend/middlewares/flow/src/dto/flow_state_dto.rs index ebb2bc2d4..4a8b33f6d 100644 --- a/backend/middlewares/flow/src/dto/flow_state_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_state_dto.rs @@ -75,6 +75,8 @@ pub struct FlowStateForm { pub referral: bool, /// 转办自定义人员权限 pub referral_guard_custom_conf: Option, + /// 新增默认字段配置 + pub add_default_field: Option, /// 字段配置 pub vars_collect: HashMap, /// 提交动作名称 @@ -113,6 +115,8 @@ pub struct FlowStateApproval { /// 转办自定义人员权限 pub referral_guard_custom: bool, pub referral_guard_custom_conf: Option, + /// 新增默认字段配置 + pub add_default_field: Option, /// 字段配置 pub vars_collect: HashMap, /// 多人审批策略方式 @@ -466,4 +470,4 @@ impl FromStr for FlowStateOperatorKind { _ => Err(TardisError::bad_request(&format!("invalid FlowStateOperatorKind: {}", s), "400-operator-invalid-param")), } } -} +} \ No newline at end of file diff --git a/backend/middlewares/flow/src/serv/clients/search_client.rs b/backend/middlewares/flow/src/serv/clients/search_client.rs index 3e6b892ce..c52f92101 100644 --- a/backend/middlewares/flow/src/serv/clients/search_client.rs +++ b/backend/middlewares/flow/src/serv/clients/search_client.rs @@ -11,7 +11,7 @@ use bios_sdk_invoke::{ use itertools::Itertools; use serde_json::json; use tardis::{ - basic::{dto::TardisContext, field::TrimString, result::TardisResult}, tokio, web::web_resp::TardisPage, TardisFuns, TardisFunsInst + basic::{dto::TardisContext, field::TrimString, result::TardisResult}, log::debug, tokio, web::web_resp::TardisPage, TardisFuns, TardisFunsInst }; use crate::{ @@ -262,6 +262,7 @@ impl FlowSearchClient { && guard_conf.guard_by_spec_org_ids.is_empty() && guard_conf.guard_by_spec_role_ids.is_empty() { + debug!("flow search_guard_account_num result : 0"); return Ok(Some(0)); } let mut search_ctx_req = SearchItemSearchCtxReq { @@ -278,7 +279,7 @@ impl FlowSearchClient { if !guard_conf.guard_by_spec_role_ids.is_empty() { search_ctx_req.roles = Some(guard_conf.guard_by_spec_role_ids.clone()); } - Ok(SpiSearchClient::search(&SearchItemSearchReq { + let result = SpiSearchClient::search(&SearchItemSearchReq { tag: "iam_account".to_string(), ctx: search_ctx_req, query: SearchItemQueryReq { ..Default::default() }, @@ -289,7 +290,9 @@ impl FlowSearchClient { size: 1, fetch_total: true, }, - }, funs, ctx).await?.map(|result| result.total_size)) + }, funs, ctx).await?.map(|result| result.total_size); + debug!("flow search_guard_account_num result : {:?}", result); + Ok(result) } pub fn get_tag_search_map() -> HashMap { diff --git a/backend/middlewares/flow/src/serv/flow_external_serv.rs b/backend/middlewares/flow/src/serv/flow_external_serv.rs index 470a4c0df..80b3e18e4 100644 --- a/backend/middlewares/flow/src/serv/flow_external_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_external_serv.rs @@ -1,7 +1,7 @@ use bios_sdk_invoke::{clients::spi_kv_client::SpiKvClient, invoke_constants::TARDIS_CONTEXT}; use itertools::Itertools; use tardis::{ - basic::{dto::TardisContext, result::TardisResult}, log::debug, tokio, web::web_resp::TardisResp, TardisFuns, TardisFunsInst + basic::{dto::TardisContext, result::TardisResult}, log::debug, tokio, TardisFuns, TardisFunsInst }; use crate::{ @@ -12,7 +12,7 @@ use crate::{ }, flow_state_dto::FlowSysStateKind, flow_transition_dto::{FlowTransitionActionByVarChangeInfoChangedKind, FlowTransitionDetailResp, TagRelKind}, - }, flow_config::FlowConfig, flow_constants + }, flow_constants }; pub struct FlowExternalServ; diff --git a/backend/middlewares/flow/src/serv/flow_inst_serv.rs b/backend/middlewares/flow/src/serv/flow_inst_serv.rs index cd318e009..2c66f041a 100644 --- a/backend/middlewares/flow/src/serv/flow_inst_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_inst_serv.rs @@ -2650,6 +2650,7 @@ impl FlowInstServ { sub_kind: Some(FlowLogClient::get_junp_kind(&flow_inst_detail.tag)), flow_message: operate_req.output_message.clone(), flow_result: Some(operate_req.operate.to_string().to_uppercase()), + detail: operate_req.log_text.clone(), ..Default::default() }; if operate_req.vars.is_none() { diff --git a/backend/middlewares/flow/src/serv/flow_model_serv.rs b/backend/middlewares/flow/src/serv/flow_model_serv.rs index 9f9f3ef56..2ef6c576f 100644 --- a/backend/middlewares/flow/src/serv/flow_model_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_model_serv.rs @@ -28,13 +28,13 @@ use crate::{ dto::{ flow_model_dto::{ FlowModelAddReq, FlowModelAggResp, FlowModelAssociativeOperationKind, FlowModelBindNewStateReq, FlowModelBindStateReq, FlowModelDetailResp, FlowModelFilterReq, - FlowModelFindRelStateResp, FlowModelKind, FlowModelModifyReq, FlowModelRelTransitionExt, FlowModelStatus, FlowModelSummaryResp, + FlowModelFindRelStateResp, FlowModelKind, FlowModelModifyReq, FlowModelRelTransitionExt, FlowModelStatus, FlowModelSummaryResp, FlowModelSyncModifiedFieldReq, }, flow_model_version_dto::{ FlowModelVersionAddReq, FlowModelVersionBindState, FlowModelVersionDetailResp, FlowModelVersionFilterReq, FlowModelVersionModifyReq, FlowModelVersionModifyState, FlowModelVesionState, }, - flow_state_dto::{FLowStateIdAndName, FlowStateAddReq, FlowStateAggResp, FlowStateKind, FlowStateRelModelExt, FlowSysStateKind}, + flow_state_dto::{FLowStateIdAndName, FlowStateAddReq, FlowStateAggResp, FlowStateKind, FlowStateModifyReq, FlowStateRelModelExt, FlowStateVar, FlowSysStateKind}, flow_transition_dto::{ FlowTransitionAddReq, FlowTransitionDetailResp, FlowTransitionInitInfo, FlowTransitionModifyReq, FlowTransitionPostActionInfo, FlowTransitionSortStatesReq, }, @@ -48,10 +48,7 @@ use super::{ clients::{ flow_log_client::{FlowLogClient, LogParamContent, LogParamTag}, search_client::FlowSearchClient, - }, - flow_model_version_serv::FlowModelVersionServ, - flow_rel_serv::{FlowRelKind, FlowRelServ}, - flow_transition_serv::FlowTransitionServ, + }, flow_model_version_serv::FlowModelVersionServ, flow_rel_serv::{FlowRelKind, FlowRelServ}, flow_state_serv::FlowStateServ, flow_transition_serv::FlowTransitionServ }; pub struct FlowModelServ; @@ -1600,6 +1597,7 @@ impl FlowModelServ { ..ctx.clone() }; let models = Self::find_rel_model_map(rel_template_id.clone(), true, funs, ctx).await?; + let mut non_main_model_ids = vec![]; if let Some(rel_template_id) = rel_template_id.clone() { let rel_model_ids = FlowRelServ::find_to_simple_rels(&FlowRelKind::FlowModelTemplate, &rel_template_id, None, None, funs, &global_ctx) .await? @@ -1653,6 +1651,14 @@ impl FlowModelServ { ) .await?; } + non_main_model_ids = Self::find_id_items(&FlowModelFilterReq { + basic: RbumBasicFilterReq { + enabled: Some(true), + ..Default::default() + }, + main: Some(false), + ..Default::default() + }, None, None, funs, ctx).await? } for (tag, model) in models.iter() { if let Some(spec_tags) = spec_tags.clone() { @@ -1670,14 +1676,7 @@ impl FlowModelServ { } } // clean non-main flow model - for model_id in Self::find_id_items(&FlowModelFilterReq { - basic: RbumBasicFilterReq { - enabled: Some(true), - ..Default::default() - }, - main: Some(false), - ..Default::default() - }, None, None, funs, ctx).await? { + for model_id in non_main_model_ids { Self::delete_item(&model_id, funs, ctx).await?; } Ok(models) @@ -1851,4 +1850,60 @@ impl FlowModelServ { None => Err(funs.err().not_found("flow_model_serv", "find_editing_verion", "model not found", "404-flow-model-not-found")), } } + + pub async fn sync_modified_field(req: &FlowModelSyncModifiedFieldReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + let models = Self::find_detail_items(&FlowModelFilterReq { + basic: RbumBasicFilterReq { + enabled: Some(true), + ..Default::default() + }, + main: Some(false), + rel_template_id: req.rel_template_id.clone(), + tags: Some(vec![req.tag.clone()]), + ..Default::default() + }, None, None, funs, ctx).await?; + for model in models { + let states = model.states(); + for state in states { + let add_default_conf = match state.state_kind { + FlowStateKind::Form => { + state.kind_conf.clone().unwrap_or_default().form.unwrap_or_default().add_default_field.unwrap_or_default() + }, + FlowStateKind::Approval => { + state.kind_conf.clone().unwrap_or_default().approval.unwrap_or_default().add_default_field.unwrap_or_default() + }, + _ => { FlowStateVar::default()}, + }; + let mut kind_conf = state.kind_conf.clone().unwrap_or_default(); + for add_field in req.add_fields.clone() { + match state.state_kind { + FlowStateKind::Form => { + kind_conf.form.as_mut().map(|form| form.vars_collect.insert(add_field, add_default_conf.clone())); + }, + FlowStateKind::Approval => { + kind_conf.approval.as_mut().map(|form| form.vars_collect.insert(add_field, add_default_conf.clone())); + }, + _ => { }, + } + } + for delete_field in &req.delete_fields { + match state.state_kind { + FlowStateKind::Form => { + kind_conf.form.as_mut().map(|form| form.vars_collect.remove(delete_field)); + }, + FlowStateKind::Approval => { + kind_conf.approval.as_mut().map(|form| form.vars_collect.remove(delete_field)); + }, + _ => { }, + } + } + FlowStateServ::modify_item(&state.id, &mut FlowStateModifyReq { + kind_conf: Some(kind_conf), + ..Default::default() + }, funs, ctx).await?; + } + } + + Ok(()) + } } diff --git a/backend/middlewares/flow/src/serv/flow_model_version_serv.rs b/backend/middlewares/flow/src/serv/flow_model_version_serv.rs index 4880fd4e6..87c766b7d 100644 --- a/backend/middlewares/flow/src/serv/flow_model_version_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_model_version_serv.rs @@ -10,21 +10,17 @@ use bios_basic::rbum::{ }; use itertools::Itertools; use tardis::{ - basic::{dto::TardisContext, result::TardisResult}, - db::sea_orm::{ + basic::{dto::TardisContext, result::TardisResult}, chrono::Utc, db::sea_orm::{ prelude::Expr, sea_query::{Alias, SelectStatement}, EntityName, Set, - }, - futures::future::join_all, - serde_json::json, - TardisFuns, TardisFunsInst, + }, futures::future::join_all, serde_json::json, TardisFuns, TardisFunsInst }; use crate::{ domain::flow_model_version, dto::{ - flow_model_dto::{FlowModelBindNewStateReq, FlowModelBindStateReq, FlowModelFilterReq, FlowModelModifyReq}, + flow_model_dto::{FlowModelBindNewStateReq, FlowModelBindStateReq, FlowModelFilterReq, FlowModelModifyReq, FlowModelStatus}, flow_model_version_dto::{ FlowModelVersionAddReq, FlowModelVersionBindState, FlowModelVersionDetailResp, FlowModelVersionFilterReq, FlowModelVersionModifyReq, FlowModelVersionSummaryResp, FlowModelVesionState, @@ -106,6 +102,7 @@ impl &version_detail.rel_model_id, &mut FlowModelModifyReq { current_version_id: Some(flow_version_id.to_string()), + status: Some(FlowModelStatus::Enabled), ..Default::default() }, funs, @@ -135,6 +132,7 @@ impl } let mut flow_mode_version = flow_model_version::ActiveModel { id: Set(id.to_string()), + update_time: Set(Utc::now()), ..Default::default() }; if let Some(status) = &modify_req.status { @@ -155,6 +153,7 @@ impl &version_detail.rel_model_id, &mut FlowModelModifyReq { current_version_id: Some(id.to_string()), + status: Some(FlowModelStatus::Enabled), ..Default::default() }, funs, From b379fb652ad9271b7416ff8b031d2253c0dd602d Mon Sep 17 00:00:00 2001 From: ZzIsGod1019 <1498852723@qq.com> Date: Fri, 20 Dec 2024 16:13:06 +0800 Subject: [PATCH 07/35] flow: fix bug (inst search failed) (#884) --- .../spacegate-plugins/src/extension.rs | 2 +- .../src/extension/notification.rs | 1 - .../gateways/spacegate-plugins/src/plugin.rs | 2 +- .../middlewares/flow/src/dto/flow_inst_dto.rs | 28 +- .../flow/src/dto/flow_model_dto.rs | 2 +- .../flow/src/dto/flow_state_dto.rs | 2 +- .../flow/src/dto/flow_transition_dto.rs | 5 +- .../flow/src/serv/clients/search_client.rs | 47 +- .../flow/src/serv/flow_external_serv.rs | 7 +- .../flow/src/serv/flow_inst_serv.rs | 561 +++++++++++------- .../flow/src/serv/flow_model_serv.rs | 134 +++-- .../flow/src/serv/flow_model_version_serv.rs | 13 +- .../invoke/src/clients/spi_search_client.rs | 3 +- .../sdks/invoke/src/dto/search_item_dto.rs | 2 +- 14 files changed, 490 insertions(+), 319 deletions(-) diff --git a/backend/gateways/spacegate-plugins/src/extension.rs b/backend/gateways/spacegate-plugins/src/extension.rs index b8d27d2f7..557f47fdf 100644 --- a/backend/gateways/spacegate-plugins/src/extension.rs +++ b/backend/gateways/spacegate-plugins/src/extension.rs @@ -7,8 +7,8 @@ use self::audit_log_param::LogParamContent; pub mod audit_log_param; pub mod before_encrypt_body; pub mod cert_info; -pub mod request_crypto_status; pub mod notification; +pub mod request_crypto_status; pub enum ExtensionPackEnum { LogParamContent(), None, diff --git a/backend/gateways/spacegate-plugins/src/extension/notification.rs b/backend/gateways/spacegate-plugins/src/extension/notification.rs index fd6f65e3e..9de8e4199 100644 --- a/backend/gateways/spacegate-plugins/src/extension/notification.rs +++ b/backend/gateways/spacegate-plugins/src/extension/notification.rs @@ -43,7 +43,6 @@ impl NotificationContext { } } - #[derive(Debug, Serialize)] pub struct ReachMsgSendReq { pub scene_code: String, diff --git a/backend/gateways/spacegate-plugins/src/plugin.rs b/backend/gateways/spacegate-plugins/src/plugin.rs index 6f7a1a5c8..f335721f0 100644 --- a/backend/gateways/spacegate-plugins/src/plugin.rs +++ b/backend/gateways/spacegate-plugins/src/plugin.rs @@ -3,6 +3,6 @@ pub mod anti_xss; pub mod audit_log; pub mod auth; pub mod ip_time; +pub mod notify; pub mod op_redis_publisher; pub mod rewrite_ns_b_ip; -pub mod notify; \ No newline at end of file diff --git a/backend/middlewares/flow/src/dto/flow_inst_dto.rs b/backend/middlewares/flow/src/dto/flow_inst_dto.rs index 2d7112a8d..075ba6f92 100644 --- a/backend/middlewares/flow/src/dto/flow_inst_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_inst_dto.rs @@ -10,7 +10,10 @@ use tardis::{ }; use super::{ - flow_model_dto::FlowModelRelTransitionExt, flow_state_dto::{FlowGuardConf, FlowStateKind, FlowStateOperatorKind, FlowStateRelModelExt, FlowStateVar, FlowSysStateKind}, flow_transition_dto::FlowTransitionDoubleCheckInfo, flow_var_dto::FlowVarInfo + flow_model_dto::FlowModelRelTransitionExt, + flow_state_dto::{FlowGuardConf, FlowStateKind, FlowStateOperatorKind, FlowStateRelModelExt, FlowStateVar, FlowSysStateKind}, + flow_transition_dto::FlowTransitionDoubleCheckInfo, + flow_var_dto::FlowVarInfo, }; #[derive(Serialize, Deserialize, Debug, poem_openapi::Object)] @@ -234,17 +237,17 @@ pub struct FlowInstArtifacts { // 流程实例中数据存储更新 #[derive(Serialize, Deserialize, PartialEq, Clone, Debug, Default, sea_orm::FromJsonQueryResult)] pub struct FlowInstArtifactsModifyReq { - pub guard_conf: Option, // 当前操作人权限 - pub prohibit_guard_conf_account_ids: Option>, // 禁止操作人ID列表 - pub guard_conf_account_ids: Option>, // 更新操作人列表 - pub add_approval_result: Option<(String, FlowApprovalResultKind)>, // 增加审批结果 - pub form_state_map: Option>, // 录入节点映射 key为节点ID,对应的value为节点中的录入的参数 - pub clear_form_result: Option, // 清除节点录入信息 - pub clear_approval_result: Option, // 清除节点录入信息 - pub prev_non_auto_state_id: Option>, // 上一个非自动节点ID - pub prev_non_auto_account_id: Option, // 上一个节点操作人ID - pub curr_approval_total: Option, // 当前审批总数 - pub curr_vars: Option>, // 当前参数列表 + pub guard_conf: Option, // 当前操作人权限 + pub prohibit_guard_conf_account_ids: Option>, // 禁止操作人ID列表 + pub guard_conf_account_ids: Option>, // 更新操作人列表 + pub add_approval_result: Option<(String, FlowApprovalResultKind)>, // 增加审批结果 + pub form_state_map: Option>, // 录入节点映射 key为节点ID,对应的value为节点中的录入的参数 + pub clear_form_result: Option, // 清除节点录入信息 + pub clear_approval_result: Option, // 清除节点录入信息 + pub prev_non_auto_state_id: Option>, // 上一个非自动节点ID + pub prev_non_auto_account_id: Option, // 上一个节点操作人ID + pub curr_approval_total: Option, // 当前审批总数 + pub curr_vars: Option>, // 当前参数列表 } /// 审批结果类型 @@ -478,6 +481,7 @@ pub struct FlowInstFilterReq { pub ids: Option>, pub code: Option, /// 关联模型ID + pub flow_model_id: Option, pub flow_version_id: Option, /// 业务ID pub rel_business_obj_ids: Option>, diff --git a/backend/middlewares/flow/src/dto/flow_model_dto.rs b/backend/middlewares/flow/src/dto/flow_model_dto.rs index 5a10e485d..35fd18909 100644 --- a/backend/middlewares/flow/src/dto/flow_model_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_model_dto.rs @@ -477,4 +477,4 @@ pub struct FlowModelSyncModifiedFieldReq { /// 参数列表 pub add_fields: Vec, pub delete_fields: Vec, -} \ No newline at end of file +} diff --git a/backend/middlewares/flow/src/dto/flow_state_dto.rs b/backend/middlewares/flow/src/dto/flow_state_dto.rs index 4a8b33f6d..9829052da 100644 --- a/backend/middlewares/flow/src/dto/flow_state_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_state_dto.rs @@ -470,4 +470,4 @@ impl FromStr for FlowStateOperatorKind { _ => Err(TardisError::bad_request(&format!("invalid FlowStateOperatorKind: {}", s), "400-operator-invalid-param")), } } -} \ No newline at end of file +} diff --git a/backend/middlewares/flow/src/dto/flow_transition_dto.rs b/backend/middlewares/flow/src/dto/flow_transition_dto.rs index 20562eb1d..442e69949 100644 --- a/backend/middlewares/flow/src/dto/flow_transition_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_transition_dto.rs @@ -629,10 +629,7 @@ impl FlowTransitionFrontActionInfoRelevanceRelation { { let left_values = TardisFuns::json.str_to_obj::>(&left_value).unwrap_or_default(); if left_values.len() == 1 { - left_value = left_values - .first().cloned() - .unwrap_or_default().as_str() - .unwrap_or("").to_string(); + left_value = left_values.first().cloned().unwrap_or_default().as_str().unwrap_or("").to_string(); } else { return false; } diff --git a/backend/middlewares/flow/src/serv/clients/search_client.rs b/backend/middlewares/flow/src/serv/clients/search_client.rs index c52f92101..d05f1031f 100644 --- a/backend/middlewares/flow/src/serv/clients/search_client.rs +++ b/backend/middlewares/flow/src/serv/clients/search_client.rs @@ -6,19 +6,27 @@ use bios_sdk_invoke::{ event_client::{get_topic, EventCenterClient, SPI_RPC_TOPIC}, spi_search_client::SpiSearchClient, }, - dto::search_item_dto::{SearchItemAddReq, SearchItemModifyReq, SearchItemQueryReq, SearchItemSearchCtxReq, SearchItemSearchPageReq, SearchItemSearchReq, SearchItemSearchResp, SearchItemVisitKeysReq}, + dto::search_item_dto::{ + SearchItemAddReq, SearchItemModifyReq, SearchItemQueryReq, SearchItemSearchCtxReq, SearchItemSearchPageReq, SearchItemSearchReq, SearchItemSearchResp, + SearchItemVisitKeysReq, + }, }; use itertools::Itertools; use serde_json::json; use tardis::{ - basic::{dto::TardisContext, field::TrimString, result::TardisResult}, log::debug, tokio, web::web_resp::TardisPage, TardisFuns, TardisFunsInst + basic::{dto::TardisContext, field::TrimString, result::TardisResult}, + log::debug, + tokio, + web::web_resp::TardisPage, + TardisFuns, TardisFunsInst, }; use crate::{ dto::{ flow_inst_dto::FlowInstFilterReq, flow_model_dto::{FlowModelDetailResp, FlowModelFilterReq, FlowModelRelTransitionExt}, - flow_model_version_dto::FlowModelVersionFilterReq, flow_state_dto::FlowGuardConf, + flow_model_version_dto::FlowModelVersionFilterReq, + flow_state_dto::FlowGuardConf, }, flow_constants, serv::{ @@ -258,10 +266,7 @@ impl FlowSearchClient { } pub async fn search_guard_account_num(guard_conf: &FlowGuardConf, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult> { - if guard_conf.guard_by_spec_account_ids.is_empty() - && guard_conf.guard_by_spec_org_ids.is_empty() - && guard_conf.guard_by_spec_role_ids.is_empty() - { + if guard_conf.guard_by_spec_account_ids.is_empty() && guard_conf.guard_by_spec_org_ids.is_empty() && guard_conf.guard_by_spec_role_ids.is_empty() { debug!("flow search_guard_account_num result : 0"); return Ok(Some(0)); } @@ -279,18 +284,24 @@ impl FlowSearchClient { if !guard_conf.guard_by_spec_role_ids.is_empty() { search_ctx_req.roles = Some(guard_conf.guard_by_spec_role_ids.clone()); } - let result = SpiSearchClient::search(&SearchItemSearchReq { - tag: "iam_account".to_string(), - ctx: search_ctx_req, - query: SearchItemQueryReq { ..Default::default() }, - adv_query: None, - sort: None, - page:SearchItemSearchPageReq { - number: 1, - size: 1, - fetch_total: true, + let result = SpiSearchClient::search( + &SearchItemSearchReq { + tag: "iam_account".to_string(), + ctx: search_ctx_req, + query: SearchItemQueryReq { ..Default::default() }, + adv_query: None, + sort: None, + page: SearchItemSearchPageReq { + number: 1, + size: 1, + fetch_total: true, + }, }, - }, funs, ctx).await?.map(|result| result.total_size); + funs, + ctx, + ) + .await? + .map(|result| result.total_size); debug!("flow search_guard_account_num result : {:?}", result); Ok(result) } diff --git a/backend/middlewares/flow/src/serv/flow_external_serv.rs b/backend/middlewares/flow/src/serv/flow_external_serv.rs index 80b3e18e4..1ef8711b7 100644 --- a/backend/middlewares/flow/src/serv/flow_external_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_external_serv.rs @@ -1,7 +1,9 @@ use bios_sdk_invoke::{clients::spi_kv_client::SpiKvClient, invoke_constants::TARDIS_CONTEXT}; use itertools::Itertools; use tardis::{ - basic::{dto::TardisContext, result::TardisResult}, log::debug, tokio, TardisFuns, TardisFunsInst + basic::{dto::TardisContext, result::TardisResult}, + log::debug, + tokio, TardisFuns, TardisFunsInst, }; use crate::{ @@ -12,7 +14,8 @@ use crate::{ }, flow_state_dto::FlowSysStateKind, flow_transition_dto::{FlowTransitionActionByVarChangeInfoChangedKind, FlowTransitionDetailResp, TagRelKind}, - }, flow_constants + }, + flow_constants, }; pub struct FlowExternalServ; diff --git a/backend/middlewares/flow/src/serv/flow_inst_serv.rs b/backend/middlewares/flow/src/serv/flow_inst_serv.rs index 2c66f041a..4446213fb 100644 --- a/backend/middlewares/flow/src/serv/flow_inst_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_inst_serv.rs @@ -7,10 +7,13 @@ use async_recursion::async_recursion; use bios_basic::{ dto::BasicQueryCondInfo, rbum::{ - dto::rbum_filer_dto::RbumBasicFilterReq, helper::rbum_scope_helper, rbum_enumeration::RbumScopeLevelKind, serv::{ + dto::rbum_filer_dto::RbumBasicFilterReq, + helper::rbum_scope_helper, + rbum_enumeration::RbumScopeLevelKind, + serv::{ rbum_crud_serv::{ID_FIELD, NAME_FIELD, REL_DOMAIN_ID_FIELD, REL_KIND_ID_FIELD}, rbum_item_serv::{RbumItemCrudOperation, RBUM_ITEM_TABLE}, - } + }, }, }; use bios_sdk_invoke::dto::search_item_dto::{SearchItemQueryReq, SearchItemSearchCtxReq, SearchItemSearchPageReq, SearchItemSearchReq}; @@ -34,13 +37,22 @@ use tardis::{ use crate::{ domain::{flow_inst, flow_model_version}, dto::{ - flow_external_dto::{FlowExternalCallbackOp, FlowExternalParams}, flow_inst_dto::{ + flow_external_dto::{FlowExternalCallbackOp, FlowExternalParams}, + flow_inst_dto::{ FLowInstStateApprovalConf, FLowInstStateConf, FLowInstStateFormConf, FlowApprovalResultKind, FlowInstAbortReq, FlowInstArtifacts, FlowInstArtifactsModifyReq, FlowInstBatchBindReq, FlowInstBatchBindResp, FlowInstCommentInfo, FlowInstCommentReq, FlowInstDetailResp, FlowInstFilterReq, FlowInstFindNextTransitionResp, FlowInstFindNextTransitionsReq, FlowInstFindStateAndTransitionsReq, FlowInstFindStateAndTransitionsResp, FlowInstOperateReq, FlowInstQueryKind, FlowInstSearchPageReq, FlowInstSearchReq, FlowInstSearchSortReq, FlowInstStartReq, FlowInstSummaryResp, FlowInstSummaryResult, FlowInstTransferReq, FlowInstTransferResp, FlowInstTransitionInfo, FlowOperationContext, - }, flow_model_dto::{FlowModelDetailResp, FlowModelFilterReq, FlowModelRelTransitionExt}, flow_model_version_dto::{FlowModelVersionDetailResp, FlowModelVersionFilterReq}, flow_state_dto::{FLowStateKindConf, FlowStateAggResp, FlowStateCountersignKind, FlowStateFilterReq, FlowStateKind, FlowStateOperatorKind, FlowStateRelModelExt, FlowStatusAutoStrategyKind, FlowStatusMultiApprovalKind, FlowSysStateKind}, flow_transition_dto::FlowTransitionDetailResp, flow_var_dto::FillType + }, + flow_model_dto::{FlowModelDetailResp, FlowModelFilterReq, FlowModelRelTransitionExt}, + flow_model_version_dto::{FlowModelVersionDetailResp, FlowModelVersionFilterReq}, + flow_state_dto::{ + FLowStateKindConf, FlowStateAggResp, FlowStateCountersignKind, FlowStateFilterReq, FlowStateKind, FlowStateOperatorKind, FlowStateRelModelExt, + FlowStatusAutoStrategyKind, FlowStatusMultiApprovalKind, FlowSysStateKind, + }, + flow_transition_dto::FlowTransitionDetailResp, + flow_var_dto::FillType, }, flow_constants, helper::loop_check_helper, @@ -48,7 +60,10 @@ use crate::{ }; use super::{ - clients::{flow_log_client::{FlowLogClient, LogParamContent, LogParamExt, LogParamOp, LogParamTag}, search_client::FlowSearchClient}, + clients::{ + flow_log_client::{FlowLogClient, LogParamContent, LogParamExt, LogParamOp, LogParamTag}, + search_client::FlowSearchClient, + }, flow_event_serv::FlowEventServ, flow_external_serv::FlowExternalServ, flow_model_version_serv::FlowModelVersionServ, @@ -67,6 +82,20 @@ impl FlowInstServ { } } async fn start_main_flow(start_req: &FlowInstStartReq, current_state_name: Option, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult { + if !Self::find_details( + &FlowInstFilterReq { + rel_business_obj_ids: Some(vec![start_req.rel_business_obj_id.clone()]), + main: Some(true), + ..Default::default() + }, + funs, + ctx, + ) + .await? + .is_empty() + { + return Err(funs.err().internal_error("flow_inst_serv", "start", "The same instance exist", "500-flow-inst-exist")); + } // get model by own_paths let flow_model = FlowModelServ::get_model_id_by_own_paths_and_rel_template_id(&start_req.tag, None, funs, ctx).await?; let inst_id = TardisFuns::field.nanoid(); @@ -115,7 +144,7 @@ impl FlowInstServ { if !Self::find_details( &FlowInstFilterReq { rel_business_obj_ids: Some(vec![start_req.rel_business_obj_id.to_string()]), - flow_version_id: Some(flow_model.current_version_id.clone()), + flow_model_id: Some(flow_model.id.clone()), finish: Some(false), ..Default::default() }, @@ -147,10 +176,16 @@ impl FlowInstServ { ..Default::default() }; funs.db().insert_one(flow_inst, ctx).await?; - Self::modify_inst_artifacts(&inst_id, &FlowInstArtifactsModifyReq { - curr_vars: start_req.create_vars.clone(), - ..Default::default() - }, funs, ctx).await?; + Self::modify_inst_artifacts( + &inst_id, + &FlowInstArtifactsModifyReq { + curr_vars: start_req.create_vars.clone(), + ..Default::default() + }, + funs, + ctx, + ) + .await?; FlowSearchClient::modify_business_obj_search(&start_req.rel_business_obj_id, &flow_model.tag, funs, ctx).await?; let inst = Self::get(&inst_id, funs, ctx).await?; Self::add_start_log(start_req, &inst, &create_vars, &flow_model, ctx).await?; @@ -189,7 +224,8 @@ impl FlowInstServ { let create_vars = Self::get_new_vars(&flow_model.tag, rel_business_obj.rel_business_obj_id.clone().unwrap_or_default(), funs, ctx).await?; - let current_state_id = FlowStateServ::match_state_id_by_name(&flow_model.current_version_id, &rel_business_obj.current_state_name.clone().unwrap_or_default(), funs, ctx).await?; + let current_state_id = + FlowStateServ::match_state_id_by_name(&flow_model.current_version_id, &rel_business_obj.current_state_name.clone().unwrap_or_default(), funs, ctx).await?; let mut inst_id = Self::get_inst_ids_by_rel_business_obj_id(vec![rel_business_obj.rel_business_obj_id.clone().unwrap_or_default()], Some(true), funs, ctx).await?.pop(); if inst_id.is_none() { let id = TardisFuns::field.nanoid(); @@ -247,15 +283,9 @@ impl FlowInstServ { Expr::col((flow_model_version_table.clone(), Alias::new("rel_model_id"))).if_null(""), Alias::new("rel_flow_model_id"), ) - .expr_as( - Expr::col((flow_state_item.clone(), NAME_FIELD.clone())).if_null(""), - Alias::new("current_state_name"), - ) + .expr_as(Expr::col((flow_state_item.clone(), NAME_FIELD.clone())).if_null(""), Alias::new("current_state_name")) .expr_as(Expr::col((RBUM_ITEM_TABLE.clone(), NAME_FIELD.clone())).if_null(""), Alias::new("rel_flow_model_name")) - .expr_as( - Expr::col((rel_model_table.clone(), Alias::new("ext"))).if_null(""), - Alias::new("rel_transition"), - ) + .expr_as(Expr::col((rel_model_table.clone(), Alias::new("ext"))).if_null(""), Alias::new("rel_transition")) .from(flow_inst::Entity) .join_as( JoinType::LeftJoin, @@ -291,6 +321,9 @@ impl FlowInstServ { if let Some(flow_version_id) = &filter.flow_version_id { query.and_where(Expr::col((flow_inst::Entity, flow_inst::Column::RelFlowVersionId)).eq(flow_version_id)); } + if let Some(flow_model_id) = &filter.flow_model_id { + query.and_where(Expr::col((flow_model_version::Entity, flow_model_version::Column::RelModelId)).eq(flow_model_id)); + } if let Some(tag) = &filter.tag { query.and_where(Expr::col((flow_inst::Entity, flow_inst::Column::Tag)).eq(tag)); } @@ -478,10 +511,7 @@ impl FlowInstServ { Expr::col((rel_model_version_table.clone(), NAME_FIELD.clone())).if_null(""), Alias::new("rel_flow_model_name"), ) - .expr_as( - Expr::col((rel_model_table.clone(), Alias::new("ext"))).if_null(""), - Alias::new("rel_transition"), - ) + .expr_as(Expr::col((rel_model_table.clone(), Alias::new("ext"))).if_null(""), Alias::new("rel_transition")) .from(flow_inst::Entity) .join_as( JoinType::LeftJoin, @@ -520,8 +550,7 @@ impl FlowInstServ { JoinType::LeftJoin, flow_model_version_table.clone(), flow_model_version_table.clone(), - Cond::all() - .add(Expr::col((flow_model_version_table.clone(), ID_FIELD.clone())).equals((flow_inst::Entity, flow_inst::Column::RelFlowVersionId))), + Cond::all().add(Expr::col((flow_model_version_table.clone(), ID_FIELD.clone())).equals((flow_inst::Entity, flow_inst::Column::RelFlowVersionId))), ) .join_as( JoinType::LeftJoin, @@ -536,67 +565,70 @@ impl FlowInstServ { let flow_insts = funs.db().find_dtos::(&query).await?; let result = flow_insts - .into_iter() - .map(|inst| { - let current_state_kind_conf = inst - .current_state_kind_conf - .clone() - .map(|current_state_kind_conf| TardisFuns::json.json_to_obj::(current_state_kind_conf).unwrap_or_default()); - let artifacts = inst.artifacts.clone().map(|artifacts| TardisFuns::json.json_to_obj::(artifacts).unwrap_or_default()); - let rel_transition = inst.rel_transition.map(|ext| { - if ext.is_empty() { - return FlowModelRelTransitionExt::default(); + .into_iter() + .map(|inst| { + let current_state_kind_conf = inst + .current_state_kind_conf + .clone() + .map(|current_state_kind_conf| TardisFuns::json.json_to_obj::(current_state_kind_conf).unwrap_or_default()); + let artifacts = inst.artifacts.clone().map(|artifacts| TardisFuns::json.json_to_obj::(artifacts).unwrap_or_default()); + let rel_transition = inst.rel_transition.map(|ext| { + if ext.is_empty() { + return FlowModelRelTransitionExt::default(); + } + TardisFuns::json.str_to_obj::(&ext).unwrap_or_default() + }); + FlowInstDetailResp { + id: inst.id, + code: inst.code, + rel_flow_version_id: inst.rel_flow_version_id, + rel_flow_model_name: inst.rel_flow_model_name, + tag: inst.tag, + main: inst.main, + create_vars: inst.create_vars.map(|create_vars| TardisFuns::json.json_to_obj(create_vars).unwrap()), + create_ctx: inst.create_ctx, + create_time: inst.create_time, + finish_ctx: inst.finish_ctx, + finish_time: inst.finish_time, + finish_abort: inst.finish_abort, + output_message: inst.output_message, + own_paths: inst.own_paths, + transitions: inst.transitions.map(|transitions| TardisFuns::json.json_to_obj(transitions).unwrap()), + artifacts: artifacts.clone(), + comments: inst.comments.map(|comments| TardisFuns::json.json_to_obj(comments).unwrap()), + rel_transition, + current_state_id: inst.current_state_id.clone(), + current_state_name: inst.current_state_name, + current_state_color: inst.current_state_color, + current_state_sys_kind: inst.current_state_sys_kind, + current_state_kind: inst.current_state_kind.clone(), + current_state_ext: inst.current_state_ext.map(|ext| TardisFuns::json.str_to_obj::(&ext).unwrap_or_default()), + current_state_conf: Self::get_state_conf( + &inst.current_state_id, + &inst.current_state_kind.unwrap_or_default(), + current_state_kind_conf, + artifacts, + ctx, + ), + current_vars: inst.current_vars.map(|current_vars| TardisFuns::json.json_to_obj(current_vars).unwrap()), + rel_business_obj_id: inst.rel_business_obj_id, } - TardisFuns::json.str_to_obj::(&ext).unwrap_or_default() - }); - FlowInstDetailResp { - id: inst.id, - code: inst.code, - rel_flow_version_id: inst.rel_flow_version_id, - rel_flow_model_name: inst.rel_flow_model_name, - tag: inst.tag, - main: inst.main, - create_vars: inst.create_vars.map(|create_vars| TardisFuns::json.json_to_obj(create_vars).unwrap()), - create_ctx: inst.create_ctx, - create_time: inst.create_time, - finish_ctx: inst.finish_ctx, - finish_time: inst.finish_time, - finish_abort: inst.finish_abort, - output_message: inst.output_message, - own_paths: inst.own_paths, - transitions: inst.transitions.map(|transitions| TardisFuns::json.json_to_obj(transitions).unwrap()), - artifacts: artifacts.clone(), - comments: inst.comments.map(|comments| TardisFuns::json.json_to_obj(comments).unwrap()), - rel_transition, - current_state_id: inst.current_state_id.clone(), - current_state_name: inst.current_state_name, - current_state_color: inst.current_state_color, - current_state_sys_kind: inst.current_state_sys_kind, - current_state_kind: inst.current_state_kind.clone(), - current_state_ext: inst.current_state_ext.map(|ext| TardisFuns::json.str_to_obj::(&ext).unwrap_or_default()), - current_state_conf: Self::get_state_conf( - &inst.current_state_id, - &inst.current_state_kind.unwrap_or_default(), - current_state_kind_conf, - artifacts, - ctx, - ), - current_vars: inst.current_vars.map(|current_vars| TardisFuns::json.json_to_obj(current_vars).unwrap()), - rel_business_obj_id: inst.rel_business_obj_id, - } - }) - .collect_vec(); - Ok(result.into_iter().map(|mut inst_detail| { - let mut artifacts = inst_detail.artifacts.clone(); - if let Some(artifacts) = artifacts.as_mut() { - let mut curr_vars= artifacts.curr_vars.clone().unwrap_or_default(); - curr_vars.extend(Self::get_modify_vars(&inst_detail)); + }) + .collect_vec(); + Ok(result + .into_iter() + .map(|mut inst_detail| { + let mut artifacts = inst_detail.artifacts.clone(); + if let Some(artifacts) = artifacts.as_mut() { + let mut curr_vars = artifacts.curr_vars.clone().unwrap_or_default(); + curr_vars.extend(Self::get_modify_vars(&inst_detail)); - artifacts.curr_vars = Some(curr_vars); - } - inst_detail.artifacts = artifacts; - inst_detail - }).collect_vec()) + artifacts.curr_vars = Some(curr_vars); + } + inst_detail.artifacts = artifacts; + inst_detail + }) + .collect_vec()) } pub async fn paginate( @@ -1335,24 +1367,32 @@ impl FlowInstServ { async fn get_new_vars(tag: &str, rel_business_obj_id: String, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult> { let tag_search_map = FlowSearchClient::get_tag_search_map(); - let new_vars = if let Some(mut search_resp) = FlowSearchClient::search(&SearchItemSearchReq { - tag: tag_search_map.get(tag).ok_or_else(|| funs.err().not_found("flow_inst", "get_new_vars", "illegal response", "404-flow-tag-not-found"))?.clone(), - ctx: SearchItemSearchCtxReq::default(), - query: SearchItemQueryReq { - keys: Some(vec![rel_business_obj_id.into()]), - ..Default::default() + let new_vars = if let Some(mut search_resp) = FlowSearchClient::search( + &SearchItemSearchReq { + tag: tag_search_map.get(tag).ok_or_else(|| funs.err().not_found("flow_inst", "get_new_vars", "illegal response", "404-flow-tag-not-found"))?.clone(), + ctx: SearchItemSearchCtxReq::default(), + query: SearchItemQueryReq { + keys: Some(vec![rel_business_obj_id.into()]), + ..Default::default() + }, + adv_query: None, + sort: None, + page: SearchItemSearchPageReq { + number: 1, + size: 1, + fetch_total: false, + }, }, - adv_query: None, - sort: None, - page: SearchItemSearchPageReq { - number: 1, - size: 1, - fetch_total: false, - }}, funs, ctx).await? { - search_resp.records.pop().map(|item| item.ext) - } else { - None - }.unwrap_or_default(); + funs, + ctx, + ) + .await? + { + search_resp.records.pop().map(|item| item.ext) + } else { + None + } + .unwrap_or_default(); Ok(TardisFuns::json.json_to_obj(new_vars).unwrap_or_default()) } @@ -1360,14 +1400,7 @@ impl FlowInstServ { let mut current_vars = flow_inst.current_vars.clone(); if current_vars.is_none() || !current_vars.clone().unwrap_or_default().contains_key(key) { let new_vars = Self::get_new_vars(&flow_inst.tag, flow_inst.rel_business_obj_id.clone(), funs, ctx).await?; - Self::modify_current_vars( - flow_inst, - &new_vars, - loop_check_helper::InstancesTransition::default(), - funs, - ctx, - ) - .await?; + Self::modify_current_vars(flow_inst, &new_vars, loop_check_helper::InstancesTransition::default(), funs, ctx).await?; current_vars = Self::get(&flow_inst.id, funs, ctx).await?.current_vars; } @@ -1574,7 +1607,8 @@ impl FlowInstServ { }); } if form_conf.guard_by_assigned { - let _ = Self::get_main_inst_vars(flow_inst_detail, funs, ctx).await? + let _ = Self::get_main_inst_vars(flow_inst_detail, funs, ctx) + .await? .get("assigned_to") .unwrap_or(&json!("")) .as_str() @@ -1594,11 +1628,12 @@ impl FlowInstServ { Self::modify_inst_artifacts(&flow_inst_detail.id, &modify_req, funs, ctx).await?; // 当操作人为空时的逻辑 let curr_guard_conf = Self::get(&flow_inst_detail.id, funs, ctx).await?.artifacts.unwrap_or_default().guard_conf; - if FlowSearchClient::search_guard_account_num(&curr_guard_conf, funs, ctx).await?.unwrap_or_default() == 0 - && form_conf.auto_transfer_when_empty_kind.is_some() { + if FlowSearchClient::search_guard_account_num(&curr_guard_conf, funs, ctx).await?.unwrap_or_default() == 0 && form_conf.auto_transfer_when_empty_kind.is_some() { match form_conf.auto_transfer_when_empty_kind.unwrap_or_default() { FlowStatusAutoStrategyKind::Autoskip => { - if let Some(next_transition) = FlowInstServ::find_next_transitions(flow_inst_detail, &FlowInstFindNextTransitionsReq { vars: None }, funs, ctx).await?.pop() { + if let Some(next_transition) = + FlowInstServ::find_next_transitions(flow_inst_detail, &FlowInstFindNextTransitionsReq { vars: None }, funs, ctx).await?.pop() + { Self::transfer( flow_inst_detail, &FlowInstTransferReq { @@ -1614,15 +1649,15 @@ impl FlowInstServ { ) .await?; } - }, + } FlowStatusAutoStrategyKind::SpecifyAgent => { modify_req.guard_conf = Some(form_conf.auto_transfer_when_empty_guard_custom_conf.clone().unwrap_or_default()); modify_req.prohibit_guard_conf_account_ids = Some(vec![]); Self::modify_inst_artifacts(&flow_inst_detail.id, &modify_req, funs, ctx).await?; - }, + } FlowStatusAutoStrategyKind::TransferState => { // 当前版本不支持 - }, + } } } } @@ -1639,7 +1674,8 @@ impl FlowInstServ { }); } if approval_conf.guard_by_assigned { - let _ = Self::get_main_inst_vars(flow_inst_detail, funs, ctx).await? + let _ = Self::get_main_inst_vars(flow_inst_detail, funs, ctx) + .await? .get("assigned_to") .unwrap_or(&json!("")) .as_str() @@ -1647,10 +1683,11 @@ impl FlowInstServ { .split(',') .collect_vec() .into_iter() - .map(|str| {if !str.is_empty() { - guard_custom_conf.guard_by_spec_account_ids.push(str.to_string()); - }} - ) + .map(|str| { + if !str.is_empty() { + guard_custom_conf.guard_by_spec_account_ids.push(str.to_string()); + } + }) .collect::>(); } modify_req.guard_conf = Some(guard_custom_conf.clone()); @@ -1664,11 +1701,13 @@ impl FlowInstServ { let curr_inst = Self::get(&flow_inst_detail.id, funs, ctx).await?; // 当操作人为空时的逻辑 let curr_guard_conf = curr_inst.artifacts.unwrap_or_default().guard_conf; - if FlowSearchClient::search_guard_account_num(&curr_guard_conf, funs, ctx).await?.unwrap_or_default() == 0 - && approval_conf.auto_transfer_when_empty_kind.is_some() { + if FlowSearchClient::search_guard_account_num(&curr_guard_conf, funs, ctx).await?.unwrap_or_default() == 0 && approval_conf.auto_transfer_when_empty_kind.is_some() + { match approval_conf.auto_transfer_when_empty_kind.unwrap_or_default() { FlowStatusAutoStrategyKind::Autoskip => { - if let Some(next_transition) = FlowInstServ::find_next_transitions(flow_inst_detail, &FlowInstFindNextTransitionsReq { vars: None }, funs, ctx).await?.pop() { + if let Some(next_transition) = + FlowInstServ::find_next_transitions(flow_inst_detail, &FlowInstFindNextTransitionsReq { vars: None }, funs, ctx).await?.pop() + { Self::transfer( flow_inst_detail, &FlowInstTransferReq { @@ -1684,15 +1723,15 @@ impl FlowInstServ { ) .await?; } - }, + } FlowStatusAutoStrategyKind::SpecifyAgent => { modify_req.guard_conf = Some(approval_conf.auto_transfer_when_empty_guard_custom_conf.clone().unwrap_or_default()); modify_req.prohibit_guard_conf_account_ids = Some(vec![]); Self::modify_inst_artifacts(&flow_inst_detail.id, &modify_req, funs, ctx).await?; - }, + } FlowStatusAutoStrategyKind::TransferState => { // @TODO 当前版本不支持 - }, + } } } } @@ -1882,11 +1921,25 @@ impl FlowInstServ { let mut operators = HashMap::new(); let artifacts = artifacts.clone().unwrap_or_default(); if artifacts.guard_conf.check(ctx) - && !artifacts.prohibit_guard_by_spec_account_ids.clone().unwrap_or_default().contains(&ctx.owner) - && !( - artifacts.approval_result.get(state_id).cloned().unwrap_or_default().get(FlowApprovalResultKind::Pass.to_string().as_str()).cloned().unwrap_or_default().contains(&ctx.owner) - || artifacts.approval_result.get(state_id).cloned().unwrap_or_default().get(FlowApprovalResultKind::Overrule.to_string().as_str()).cloned().unwrap_or_default().contains(&ctx.owner) - ) + && !artifacts.prohibit_guard_by_spec_account_ids.clone().unwrap_or_default().contains(&ctx.owner) + && !(artifacts + .approval_result + .get(state_id) + .cloned() + .unwrap_or_default() + .get(FlowApprovalResultKind::Pass.to_string().as_str()) + .cloned() + .unwrap_or_default() + .contains(&ctx.owner) + || artifacts + .approval_result + .get(state_id) + .cloned() + .unwrap_or_default() + .get(FlowApprovalResultKind::Overrule.to_string().as_str()) + .cloned() + .unwrap_or_default() + .contains(&ctx.owner)) { operators.insert(FlowStateOperatorKind::Pass, approval.pass_btn_name.clone()); operators.insert(FlowStateOperatorKind::Overrule, approval.overrule_btn_name.clone()); @@ -1931,8 +1984,20 @@ impl FlowInstServ { }, funs, ctx, - ).await?; - Self::add_operate_log(operate_req, inst, if current_state.state_kind == FlowStateKind::Approval {LogParamOp::Approval} else {LogParamOp::Form}, funs, ctx).await?; + ) + .await?; + Self::add_operate_log( + operate_req, + inst, + if current_state.state_kind == FlowStateKind::Approval { + LogParamOp::Approval + } else { + LogParamOp::Form + }, + funs, + ctx, + ) + .await?; let mut modify_artifacts = FlowInstArtifactsModifyReq::default(); if let Some(all_vars) = &operate_req.all_vars { modify_artifacts.curr_vars = Some(all_vars.clone()); @@ -1950,22 +2015,22 @@ impl FlowInstServ { prohibit_guard_by_spec_account_ids.push(ctx.owner.clone()); modify_artifacts.prohibit_guard_conf_account_ids = Some(prohibit_guard_by_spec_account_ids); } - Self::modify_inst_artifacts( - &inst.id, - &modify_artifacts, - funs, - ctx, - ) - .await?; + Self::modify_inst_artifacts(&inst.id, &modify_artifacts, funs, ctx).await?; } // 撤销 FlowStateOperatorKind::Revoke => { let mut prev_non_auto_state_id = inst.artifacts.clone().unwrap_or_default().prev_non_auto_state_id.unwrap_or_default(); if let Some(target_state_id) = prev_non_auto_state_id.pop() { - Self::modify_inst_artifacts(&inst.id, &FlowInstArtifactsModifyReq { - prev_non_auto_state_id: Some(prev_non_auto_state_id), - ..Default::default() - }, funs, ctx).await?; + Self::modify_inst_artifacts( + &inst.id, + &FlowInstArtifactsModifyReq { + prev_non_auto_state_id: Some(prev_non_auto_state_id), + ..Default::default() + }, + funs, + ctx, + ) + .await?; Self::transfer_spec_state(inst, &target_state_id, funs, ctx).await?; } else { Self::abort(&inst.id, &FlowInstAbortReq { message: "".to_string() }, funs, ctx).await?; @@ -2004,10 +2069,16 @@ impl FlowInstServ { FlowStateOperatorKind::Back => { let mut prev_non_auto_state_id = inst.artifacts.clone().unwrap_or_default().prev_non_auto_state_id.unwrap_or_default(); if let Some(target_state_id) = prev_non_auto_state_id.pop() { - Self::modify_inst_artifacts(&inst.id, &FlowInstArtifactsModifyReq { - prev_non_auto_state_id: Some(prev_non_auto_state_id), - ..Default::default() - }, funs, ctx).await?; + Self::modify_inst_artifacts( + &inst.id, + &FlowInstArtifactsModifyReq { + prev_non_auto_state_id: Some(prev_non_auto_state_id), + ..Default::default() + }, + funs, + ctx, + ) + .await?; Self::transfer_spec_state(inst, &target_state_id, funs, ctx).await?; } else { Self::abort(&inst.id, &FlowInstAbortReq { message: "".to_string() }, funs, ctx).await?; @@ -2027,7 +2098,18 @@ impl FlowInstServ { .await?; if Self::check_approval_cond(inst, FlowApprovalResultKind::Pass, funs, ctx).await? { if let Some(next_transition) = FlowInstServ::find_next_transitions(inst, &FlowInstFindNextTransitionsReq { vars: None }, funs, ctx).await?.pop() { - Self::add_operate_log(operate_req, inst, if current_state.state_kind == FlowStateKind::Approval {LogParamOp::ApprovalTransfer} else {LogParamOp::FormTransfer}, funs, ctx).await?; + Self::add_operate_log( + operate_req, + inst, + if current_state.state_kind == FlowStateKind::Approval { + LogParamOp::ApprovalTransfer + } else { + LogParamOp::FormTransfer + }, + funs, + ctx, + ) + .await?; Self::transfer( inst, &FlowInstTransferReq { @@ -2058,7 +2140,18 @@ impl FlowInstServ { ) .await?; if Self::check_approval_cond(inst, FlowApprovalResultKind::Overrule, funs, ctx).await? { - Self::add_operate_log(operate_req, inst, if current_state.state_kind == FlowStateKind::Approval {LogParamOp::ApprovalTransfer} else {LogParamOp::FormTransfer}, funs, ctx).await?; + Self::add_operate_log( + operate_req, + inst, + if current_state.state_kind == FlowStateKind::Approval { + LogParamOp::ApprovalTransfer + } else { + LogParamOp::FormTransfer + }, + funs, + ctx, + ) + .await?; Self::abort(&inst.id, &FlowInstAbortReq { message: "".to_string() }, funs, ctx).await?; } } @@ -2067,7 +2160,7 @@ impl FlowInstServ { } // 判断审批条件是否满足 - async fn check_approval_cond(inst: &FlowInstDetailResp , kind: FlowApprovalResultKind, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult { + async fn check_approval_cond(inst: &FlowInstDetailResp, kind: FlowApprovalResultKind, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult { let current_state_kind_conf = FlowStateServ::get_item( &inst.current_state_id, &FlowStateFilterReq { @@ -2080,14 +2173,14 @@ impl FlowInstServ { }, funs, ctx, - ).await?.kind_conf().unwrap_or_default().approval; + ) + .await? + .kind_conf() + .unwrap_or_default() + .approval; let artifacts = inst.artifacts.clone().unwrap_or_default(); let approval_total = artifacts.approval_total.unwrap_or_default().get(&inst.current_state_id).cloned().unwrap_or_default(); - let approval_result = artifacts - .approval_result - .get(&inst.current_state_id) - .cloned() - .unwrap_or_default(); + let approval_result = artifacts.approval_result.get(&inst.current_state_id).cloned().unwrap_or_default(); if let Some(current_state_kind_conf) = current_state_kind_conf { // 或签直接通过 if current_state_kind_conf.multi_approval_kind == FlowStatusMultiApprovalKind::Orsign { @@ -2096,44 +2189,49 @@ impl FlowInstServ { // 会签但是人数为空,直接通过 if current_state_kind_conf.multi_approval_kind == FlowStatusMultiApprovalKind::Countersign && approval_total == 0 { return Ok(true); - } + } let countersign_conf = current_state_kind_conf.countersign_conf; // 指定人通过,则通过 - if kind == FlowApprovalResultKind::Pass - && countersign_conf.specified_pass_guard.unwrap_or(false) - && countersign_conf.specified_pass_guard_conf.is_some() - && countersign_conf.specified_pass_guard_conf.unwrap().check(ctx) { + if kind == FlowApprovalResultKind::Pass + && countersign_conf.specified_pass_guard.unwrap_or(false) + && countersign_conf.specified_pass_guard_conf.is_some() + && countersign_conf.specified_pass_guard_conf.unwrap().check(ctx) + { return Ok(true); } // 指定人拒绝,则拒绝 - if kind == FlowApprovalResultKind::Overrule - && countersign_conf.specified_overrule_guard.unwrap_or(false) - && countersign_conf.specified_overrule_guard_conf.is_some() - && countersign_conf.specified_overrule_guard_conf.unwrap().check(ctx) { + if kind == FlowApprovalResultKind::Overrule + && countersign_conf.specified_overrule_guard.unwrap_or(false) + && countersign_conf.specified_overrule_guard_conf.is_some() + && countersign_conf.specified_overrule_guard_conf.unwrap().check(ctx) + { return Ok(true); } match countersign_conf.kind { FlowStateCountersignKind::All => { if kind == FlowApprovalResultKind::Overrule // 要求全数通过则出现一个拒绝,即拒绝 || ( - kind == FlowApprovalResultKind::Pass + kind == FlowApprovalResultKind::Pass && approval_result.get(&FlowApprovalResultKind::Pass.to_string()).cloned().unwrap_or_default().len() + 1 >= approval_total && approval_result.get(&FlowApprovalResultKind::Overrule.to_string()).cloned().unwrap_or_default().len().is_empty() // 要求全数通过则通过人数达到审核人数同时没有一个拒绝 - ) { + ) + { return Ok(true); } - }, + } FlowStateCountersignKind::Most => { if countersign_conf.most_percent.is_none() { return Ok(false); } let pass_total = approval_total * countersign_conf.most_percent.unwrap_or_default() / 100; // 需满足通过的人员数量 - let overrule_total = approval_total - pass_total;// 需满足拒绝的人员数量 + let overrule_total = approval_total - pass_total; // 需满足拒绝的人员数量 if (kind == FlowApprovalResultKind::Pass && approval_result.get(&FlowApprovalResultKind::Pass.to_string()).cloned().unwrap_or_default().len() + 1 >= pass_total) // 要求大多数通过则通过人数达到通过的比例 - || (kind == FlowApprovalResultKind::Overrule && approval_result.get(&FlowApprovalResultKind::Overrule.to_string()).cloned().unwrap_or_default().len() + 1 >= overrule_total) {// 要求大多数通过则拒绝人数达到拒绝的比例 + || (kind == FlowApprovalResultKind::Overrule && approval_result.get(&FlowApprovalResultKind::Overrule.to_string()).cloned().unwrap_or_default().len() + 1 >= overrule_total) + { + // 要求大多数通过则拒绝人数达到拒绝的比例 return Ok(true); } - }, + } } } Ok(false) @@ -2336,7 +2434,9 @@ impl FlowInstServ { rel_flow_model_id: item.try_get("", "rel_flow_model_id")?, rel_flow_model_name: item.try_get("", "rel_flow_model_name")?, rel_business_obj_id: item.try_get("", "rel_business_obj_id")?, - rel_transition: item.try_get::>("", "rel_transition")?.map(|ext| TardisFuns::json.str_to_obj::(&ext).unwrap_or_default()), + rel_transition: item + .try_get::>("", "rel_transition")? + .map(|ext| TardisFuns::json.str_to_obj::(&ext).unwrap_or_default()), current_state_id: item.try_get("", "current_state_id")?, current_state_name: item.try_get("", "current_state_name")?, create_ctx: item.try_get("", "create_ctx")?, @@ -2400,7 +2500,8 @@ impl FlowInstServ { or_where_fragments.push(format!("(current_state.state_kind = 'form' AND ({}))", child_or_where_fragments.join(" OR "))); } if query_kinds.contains(&FlowInstQueryKind::Approval) { - and_where_fragments.push(format!("{table_alias_name}.id not in ( + and_where_fragments.push(format!( + "{table_alias_name}.id not in ( SELECT id FROM @@ -2408,37 +2509,39 @@ impl FlowInstServ { WHERE {table_alias_name}.artifacts -> 'approval_result' -> {table_alias_name}.current_state_id -> 'PASS' @> '[\"{}\"]' :: jsonb OR {table_alias_name}.artifacts -> 'approval_result' -> {table_alias_name}.current_state_id -> 'OVERRULE' @> '[\"{}\"]' :: jsonb - )",ctx.owner, ctx.owner)); + )", + ctx.owner, ctx.owner + )); let mut child_or_where_fragments = vec![]; - sql_vals.push(sea_orm::Value::from(ctx.owner.clone())); + sql_vals.push(sea_orm::Value::from(ctx.owner.clone())); + child_or_where_fragments.push(format!( + "EXISTS (SELECT 1 FROM jsonb_array_elements_text({}.artifacts->'guard_conf'->'guard_by_spec_account_ids') AS elem WHERE elem IN (${}))", + table_alias_name, + sql_vals.len() + )); + if !ctx.roles.is_empty() { + sql_vals.push(sea_orm::Value::from(ctx.roles.clone().join(", ").to_string())); child_or_where_fragments.push(format!( - "EXISTS (SELECT 1 FROM jsonb_array_elements_text({}.artifacts->'guard_conf'->'guard_by_spec_account_ids') AS elem WHERE elem IN (${}))", + "EXISTS (SELECT 1 FROM jsonb_array_elements_text({}.artifacts->'guard_conf'->'guard_by_spec_role_ids') AS elem WHERE elem IN (${}))", table_alias_name, sql_vals.len() )); - if !ctx.roles.is_empty() { - sql_vals.push(sea_orm::Value::from(ctx.roles.clone().join(", ").to_string())); - child_or_where_fragments.push(format!( - "EXISTS (SELECT 1 FROM jsonb_array_elements_text({}.artifacts->'guard_conf'->'guard_by_spec_role_ids') AS elem WHERE elem IN (${}))", - table_alias_name, - sql_vals.len() - )); - } - if !ctx.groups.is_empty() { - sql_vals.push(sea_orm::Value::from(ctx.groups.clone().join(", ").to_string())); - child_or_where_fragments.push(format!( - "EXISTS (SELECT 1 FROM jsonb_array_elements_text({}.artifacts->'guard_conf'->'guard_by_spec_org_ids') AS elem WHERE elem IN (${}))", - table_alias_name, - sql_vals.len() - )); - } - or_where_fragments.push(format!("(current_state.state_kind = 'approval' AND ({}))", child_or_where_fragments.join(" OR "))); + } + if !ctx.groups.is_empty() { + sql_vals.push(sea_orm::Value::from(ctx.groups.clone().join(", ").to_string())); + child_or_where_fragments.push(format!( + "EXISTS (SELECT 1 FROM jsonb_array_elements_text({}.artifacts->'guard_conf'->'guard_by_spec_org_ids') AS elem WHERE elem IN (${}))", + table_alias_name, + sql_vals.len() + )); + } + or_where_fragments.push(format!("(current_state.state_kind = 'approval' AND ({}))", child_or_where_fragments.join(" OR "))); } if !and_where_fragments.is_empty() { where_fragments.push(format!("( {} )", and_where_fragments.join(" AND "))); } if !or_where_fragments.is_empty() { - where_fragments.push(format!("( {} )", or_where_fragments.join(" AND "))); + where_fragments.push(format!("( {} )", or_where_fragments.join(" OR "))); } Ok(()) } @@ -2534,15 +2637,12 @@ impl FlowInstServ { // 获取主实例的参数列表 async fn get_main_inst_vars(flow_inst: &FlowInstDetailResp, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult> { - let main_inst_id = Self::get_inst_ids_by_rel_business_obj_id(vec![flow_inst.rel_business_obj_id.clone()], Some(true), funs, ctx).await? - .pop() - .ok_or_else(|| funs.err().not_found("flow_inst", "get_guard_by_assigned", "illegal response", "404-flow-inst-not-found"))?; + let main_inst_id = Self::get_inst_ids_by_rel_business_obj_id(vec![flow_inst.rel_business_obj_id.clone()], Some(true), funs, ctx) + .await? + .pop() + .ok_or_else(|| funs.err().not_found("flow_inst", "get_guard_by_assigned", "illegal response", "404-flow-inst-not-found"))?; let main_inst = Self::get(&main_inst_id, funs, ctx).await?; - Ok(main_inst - .current_vars - .clone() - .unwrap_or_default() - ) + Ok(main_inst.current_vars.clone().unwrap_or_default()) } // 生成实例编码 @@ -2562,18 +2662,18 @@ impl FlowInstServ { } // 添加审批流发起日志 - async fn add_start_log(start_req: &FlowInstStartReq, flow_inst_detail: &FlowInstDetailResp, create_vars: &HashMap, flow_model: &FlowModelDetailResp, ctx: &TardisContext) -> TardisResult<()> { + async fn add_start_log( + start_req: &FlowInstStartReq, + flow_inst_detail: &FlowInstDetailResp, + create_vars: &HashMap, + flow_model: &FlowModelDetailResp, + ctx: &TardisContext, + ) -> TardisResult<()> { let rel_transition = flow_model.rel_transition().unwrap_or_default(); let operand = match rel_transition.id.as_str() { - "__EDIT__" => { - "编辑审批".to_string() - }, - "__DELETE__" => { - "删除审批".to_string() - } - _ => { - format!("{}({})", rel_transition.name, rel_transition.from_flow_state_name).to_string() - }, + "__EDIT__" => "编辑审批".to_string(), + "__DELETE__" => "删除审批".to_string(), + _ => format!("{}({})", rel_transition.name, rel_transition.from_flow_state_name).to_string(), }; let mut log_ext = LogParamExt { scene_kind: Some(vec!["approval_flow".to_string()]), @@ -2599,7 +2699,6 @@ impl FlowInstServ { } else { log_content.old_content = Some(create_vars.get("content").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string())); log_content.new_content = start_req.create_vars.clone().unwrap_or_default().get("content").map(|content| content.as_str().unwrap_or("").to_string()); - log_content.detail = None; // @TODO 暂未实现 log_ext.include_detail = Some(true); } FlowLogClient::add_ctx_task( @@ -2618,7 +2717,13 @@ impl FlowInstServ { } // 添加审批流操作日志 - async fn add_operate_log(operate_req: &FlowInstOperateReq, flow_inst_detail: &FlowInstDetailResp, op_kind: LogParamOp, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + async fn add_operate_log( + operate_req: &FlowInstOperateReq, + flow_inst_detail: &FlowInstDetailResp, + op_kind: LogParamOp, + funs: &TardisFunsInst, + ctx: &TardisContext, + ) -> TardisResult<()> { let current_state = FlowStateServ::get_item( &flow_inst_detail.current_state_id, &FlowStateFilterReq { @@ -2631,7 +2736,8 @@ impl FlowInstServ { }, funs, ctx, - ).await?; + ) + .await?; let subject_text = match current_state.state_kind { FlowStateKind::Approval => "审批节点".to_string(), FlowStateKind::Form => "录入节点".to_string(), @@ -2650,7 +2756,6 @@ impl FlowInstServ { sub_kind: Some(FlowLogClient::get_junp_kind(&flow_inst_detail.tag)), flow_message: operate_req.output_message.clone(), flow_result: Some(operate_req.operate.to_string().to_uppercase()), - detail: operate_req.log_text.clone(), ..Default::default() }; if operate_req.vars.is_none() { @@ -2658,9 +2763,10 @@ impl FlowInstServ { log_content.old_content = Some("".to_string()); log_content.new_content = Some("".to_string()); } else { - log_content.old_content = Some(flow_inst_detail.create_vars.clone().unwrap_or_default().get("content").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string())); + log_content.old_content = + Some(flow_inst_detail.create_vars.clone().unwrap_or_default().get("content").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string())); log_content.new_content = operate_req.vars.clone().unwrap_or_default().get("content").map(|content| content.as_str().unwrap_or("").to_string()); - log_content.detail = None; // @TODO 缺少翻译逻辑 + log_content.detail = operate_req.log_text.clone(); log_ext.include_detail = Some(true); } if operate_req.output_message.is_some() { @@ -2712,15 +2818,9 @@ impl FlowInstServ { .await?; let rel_transition = flow_model.rel_transition().unwrap_or_default(); let subject_text = match rel_transition.id.as_str() { - "__EDIT__" => { - "编辑审批".to_string() - }, - "__DELETE__" => { - "删除审批".to_string() - } - _ => { - format!("{}({})", rel_transition.name, rel_transition.from_flow_state_name).to_string() - }, + "__EDIT__" => "编辑审批".to_string(), + "__DELETE__" => "删除审批".to_string(), + _ => format!("{}({})", rel_transition.name, rel_transition.from_flow_state_name).to_string(), }; let log_ext = LogParamExt { scene_kind: Some(vec!["approval_flow".to_string()]), @@ -2756,6 +2856,7 @@ impl FlowInstServ { pub fn get_modify_vars(flow_inst_detail: &FlowInstDetailResp) -> HashMap { let mut vars_collect = HashMap::new(); if let Some(artifacts) = &flow_inst_detail.artifacts { + vars_collect = artifacts.curr_vars.clone().unwrap_or_default(); let mut state_ids = vec![]; for tran in flow_inst_detail.transitions.clone().unwrap_or_default() { let current_state_id = tran.from_state_id.clone().unwrap_or_default(); @@ -2769,7 +2870,7 @@ impl FlowInstServ { } } }; - + vars_collect } } diff --git a/backend/middlewares/flow/src/serv/flow_model_serv.rs b/backend/middlewares/flow/src/serv/flow_model_serv.rs index 2ef6c576f..7308f481c 100644 --- a/backend/middlewares/flow/src/serv/flow_model_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_model_serv.rs @@ -48,7 +48,11 @@ use super::{ clients::{ flow_log_client::{FlowLogClient, LogParamContent, LogParamTag}, search_client::FlowSearchClient, - }, flow_model_version_serv::FlowModelVersionServ, flow_rel_serv::{FlowRelKind, FlowRelServ}, flow_state_serv::FlowStateServ, flow_transition_serv::FlowTransitionServ + }, + flow_model_version_serv::FlowModelVersionServ, + flow_rel_serv::{FlowRelKind, FlowRelServ}, + flow_state_serv::FlowStateServ, + flow_transition_serv::FlowTransitionServ, }; pub struct FlowModelServ; @@ -117,9 +121,11 @@ impl RbumItemCrudOperation TardisResult<()> { let model_detail = Self::get_item(flow_model_id, &FlowModelFilterReq::default(), funs, ctx).await?; + if modify_req.name.is_some() { + // 同步修改名称到版本 + for model_id in FlowModelVersionServ::find_id_items( + &FlowModelVersionFilterReq { + rel_model_ids: Some(vec![flow_model_id.to_string()]), + ..Default::default() + }, + None, + None, + funs, + ctx, + ) + .await? + { + FlowModelVersionServ::modify_item( + &model_id, + &mut FlowModelVersionModifyReq { + name: modify_req.name.clone(), + ..Default::default() + }, + funs, + ctx, + ) + .await?; + } + } if modify_req.status == Some(FlowModelStatus::Enabled) && model_detail.current_version_id.is_empty() { return Err(funs.err().internal_error("flow_model_serv", "after_modify_item", "Current model is not enabled", "500-flow_model-prohibit-enabled")); } @@ -637,14 +669,14 @@ impl RbumItemCrudOperation TardisResult<()> { - let models = Self::find_detail_items(&FlowModelFilterReq { - basic: RbumBasicFilterReq { - enabled: Some(true), + let models = Self::find_detail_items( + &FlowModelFilterReq { + basic: RbumBasicFilterReq { + enabled: Some(true), + ..Default::default() + }, + main: Some(false), + rel_template_id: req.rel_template_id.clone(), + tags: Some(vec![req.tag.clone()]), ..Default::default() }, - main: Some(false), - rel_template_id: req.rel_template_id.clone(), - tags: Some(vec![req.tag.clone()]), - ..Default::default() - }, None, None, funs, ctx).await?; + None, + None, + funs, + ctx, + ) + .await?; for model in models { let states = model.states(); for state in states { let add_default_conf = match state.state_kind { - FlowStateKind::Form => { - state.kind_conf.clone().unwrap_or_default().form.unwrap_or_default().add_default_field.unwrap_or_default() - }, - FlowStateKind::Approval => { - state.kind_conf.clone().unwrap_or_default().approval.unwrap_or_default().add_default_field.unwrap_or_default() - }, - _ => { FlowStateVar::default()}, + FlowStateKind::Form => state.kind_conf.clone().unwrap_or_default().form.unwrap_or_default().add_default_field.unwrap_or_default(), + FlowStateKind::Approval => state.kind_conf.clone().unwrap_or_default().approval.unwrap_or_default().add_default_field.unwrap_or_default(), + _ => FlowStateVar::default(), }; let mut kind_conf = state.kind_conf.clone().unwrap_or_default(); for add_field in req.add_fields.clone() { match state.state_kind { FlowStateKind::Form => { kind_conf.form.as_mut().map(|form| form.vars_collect.insert(add_field, add_default_conf.clone())); - }, + } FlowStateKind::Approval => { kind_conf.approval.as_mut().map(|form| form.vars_collect.insert(add_field, add_default_conf.clone())); - }, - _ => { }, + } + _ => {} } } for delete_field in &req.delete_fields { match state.state_kind { FlowStateKind::Form => { kind_conf.form.as_mut().map(|form| form.vars_collect.remove(delete_field)); - }, + } FlowStateKind::Approval => { kind_conf.approval.as_mut().map(|form| form.vars_collect.remove(delete_field)); - }, - _ => { }, + } + _ => {} } } - FlowStateServ::modify_item(&state.id, &mut FlowStateModifyReq { - kind_conf: Some(kind_conf), - ..Default::default() - }, funs, ctx).await?; + FlowStateServ::modify_item( + &state.id, + &mut FlowStateModifyReq { + kind_conf: Some(kind_conf), + ..Default::default() + }, + funs, + ctx, + ) + .await?; } } - + Ok(()) } } diff --git a/backend/middlewares/flow/src/serv/flow_model_version_serv.rs b/backend/middlewares/flow/src/serv/flow_model_version_serv.rs index 87c766b7d..03ae023dc 100644 --- a/backend/middlewares/flow/src/serv/flow_model_version_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_model_version_serv.rs @@ -10,11 +10,16 @@ use bios_basic::rbum::{ }; use itertools::Itertools; use tardis::{ - basic::{dto::TardisContext, result::TardisResult}, chrono::Utc, db::sea_orm::{ + basic::{dto::TardisContext, result::TardisResult}, + chrono::Utc, + db::sea_orm::{ prelude::Expr, sea_query::{Alias, SelectStatement}, EntityName, Set, - }, futures::future::join_all, serde_json::json, TardisFuns, TardisFunsInst + }, + futures::future::join_all, + serde_json::json, + TardisFuns, TardisFunsInst, }; use crate::{ @@ -132,11 +137,13 @@ impl } let mut flow_mode_version = flow_model_version::ActiveModel { id: Set(id.to_string()), - update_time: Set(Utc::now()), ..Default::default() }; if let Some(status) = &modify_req.status { flow_mode_version.status = Set(status.clone()); + if *status != FlowModelVesionState::Disabled { + flow_mode_version.update_time = Set(Utc::now()); + } } if let Some(init_state_id) = &modify_req.init_state_id { flow_mode_version.init_state_id = Set(init_state_id.clone()); diff --git a/frontend/sdks/invoke/src/clients/spi_search_client.rs b/frontend/sdks/invoke/src/clients/spi_search_client.rs index a8ad66df3..b1cad260d 100644 --- a/frontend/sdks/invoke/src/clients/spi_search_client.rs +++ b/frontend/sdks/invoke/src/clients/spi_search_client.rs @@ -79,7 +79,8 @@ impl SpiSearchClient { pub async fn search(search_req: &SearchItemSearchReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult>> { let search_url = BaseSpiClient::module_url(InvokeModuleKind::Search, funs).await?; let headers = BaseSpiClient::headers(None, funs, ctx).await?; - let resp = funs.web_client().put::>>(format!("{search_url}/ci/item/search"), search_req, headers.clone()).await?; + let resp = + funs.web_client().put::>>(format!("{search_url}/ci/item/search"), search_req, headers.clone()).await?; BaseSpiClient::package_resp(resp) } } diff --git a/frontend/sdks/invoke/src/dto/search_item_dto.rs b/frontend/sdks/invoke/src/dto/search_item_dto.rs index 06153dffa..bc90782ae 100644 --- a/frontend/sdks/invoke/src/dto/search_item_dto.rs +++ b/frontend/sdks/invoke/src/dto/search_item_dto.rs @@ -242,4 +242,4 @@ pub struct SearchItemSearchResp { pub ext: Value, pub rank_title: f32, pub rank_content: f32, -} \ No newline at end of file +} From 612c2c3234c9a3f372cd6a683eae27e93d54094f Mon Sep 17 00:00:00 2001 From: ZzIsGod1019 <1498852723@qq.com> Date: Fri, 20 Dec 2024 18:55:11 +0800 Subject: [PATCH 08/35] flow: fix bug (instance referral error) (#885) * flow: fix bug (inst search failed) * flow: fix bug (instance referral error) --- .../flow/src/dto/flow_state_dto.rs | 3 ++ .../flow/src/serv/flow_inst_serv.rs | 28 +++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/backend/middlewares/flow/src/dto/flow_state_dto.rs b/backend/middlewares/flow/src/dto/flow_state_dto.rs index 9829052da..0d6815f45 100644 --- a/backend/middlewares/flow/src/dto/flow_state_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_state_dto.rs @@ -210,6 +210,9 @@ pub struct FlowGuardConf { impl FlowGuardConf { pub fn check(&self, ctx: &TardisContext) -> bool { + if self.guard_by_spec_account_ids.is_empty() && self.guard_by_spec_role_ids.is_empty() && self.guard_by_spec_org_ids.is_empty() { + return true; + } if self.guard_by_spec_account_ids.contains(&ctx.owner) { return true; } diff --git a/backend/middlewares/flow/src/serv/flow_inst_serv.rs b/backend/middlewares/flow/src/serv/flow_inst_serv.rs index 4446213fb..f8b398cda 100644 --- a/backend/middlewares/flow/src/serv/flow_inst_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_inst_serv.rs @@ -1599,7 +1599,7 @@ impl FlowInstServ { let form_conf = state.kind_conf().unwrap_or_default().form.unwrap_or_default(); let mut guard_custom_conf = form_conf.guard_custom_conf.unwrap_or_default(); if form_conf.guard_by_creator { - guard_custom_conf.guard_by_spec_account_ids.push(ctx.owner.clone()); + guard_custom_conf.guard_by_spec_account_ids.push(flow_inst_detail.create_ctx.owner.clone()); } if form_conf.guard_by_his_operators { flow_inst_detail.transitions.as_ref().map(|transitions| { @@ -1666,7 +1666,7 @@ impl FlowInstServ { let approval_conf = state.kind_conf().unwrap_or_default().approval.unwrap_or_default(); let mut guard_custom_conf = approval_conf.guard_custom_conf.unwrap_or_default(); if approval_conf.guard_by_creator { - guard_custom_conf.guard_by_spec_account_ids.push(ctx.owner.clone()); + guard_custom_conf.guard_by_spec_account_ids.push(flow_inst_detail.create_ctx.owner.clone()); } if approval_conf.guard_by_his_operators { flow_inst_detail.transitions.as_ref().map(|transitions| { @@ -1767,6 +1767,30 @@ impl FlowInstServ { FlowExternalServ::do_delete_rel_obj(&flow_inst_detail.tag, &flow_inst_detail.rel_business_obj_id, &flow_inst_detail.id, ctx, funs).await?; } _ => { + let vars_collect = Self::get_modify_vars(flow_inst_detail); + let params = vars_collect + .into_iter() + .map(|(key, value)| FlowExternalParams { + var_name: Some(key), + value: Some(value), + ..Default::default() + }) + .collect_vec(); + FlowExternalServ::do_async_modify_field( + &flow_inst_detail.tag, + None, + &flow_inst_detail.rel_business_obj_id, + &flow_inst_detail.id, + None, + None, + None, + None, + None, + params, + ctx, + funs, + ) + .await?; if let Some(inst_id) = Self::get_inst_ids_by_rel_business_obj_id(vec![flow_inst_detail.rel_business_obj_id.clone()], Some(true), funs, ctx).await?.pop() { let inst_detail = Self::get(&inst_id, funs, ctx).await?; Self::transfer( From c89696f8b74c493bd519bd855323ed6a138a5d79 Mon Sep 17 00:00:00 2001 From: ljl <17743125563@163.com> Date: Thu, 26 Dec 2024 10:58:32 +0800 Subject: [PATCH 09/35] event:fix init. --- backend/middlewares/event/src/event_initializer.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/middlewares/event/src/event_initializer.rs b/backend/middlewares/event/src/event_initializer.rs index 403221639..ca1a71503 100644 --- a/backend/middlewares/event/src/event_initializer.rs +++ b/backend/middlewares/event/src/event_initializer.rs @@ -60,6 +60,7 @@ pub async fn init(web_server: &TardisWebServer) -> TardisResult<()> { } async fn init_db(domain_code: String, kind_code: String, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + bios_basic::rbum::rbum_initializer::init(funs.module_code(), funs.conf::().rbum.clone()).await?; if let Some(domain_id) = RbumDomainServ::get_rbum_domain_id_by_code(&domain_code, funs).await? { let kind_id = RbumKindServ::get_rbum_kind_id_by_code(&kind_code, funs).await?.expect("missing event kind"); EventInfoManager::set(EventInfo { kind_id, domain_id })?; From d76a86e363ea577148380b6cb4da6e314eea9825 Mon Sep 17 00:00:00 2001 From: 4t145 Date: Thu, 26 Dec 2024 15:58:21 +0800 Subject: [PATCH 10/35] update mq reversion --- Cargo.toml | 4 ++-- backend/spi/spi-log/src/serv/pgv2/log_pg_item_serv.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 289a4d4aa..2c210dccf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,9 +67,9 @@ strum = { version = "0.26", features = ["derive"] } # tardis = { version = "0.2.0", path = "../tardis/tardis" } tardis = { git = "https://github.com/ideal-world/tardis.git", rev = "66d4c63" } # asteroid-mq = { git = "https://github.com/4t145/asteroid-mq.git", rev = "d59c64d" } -asteroid-mq = { git = "https://github.com/4t145/asteroid-mq.git", rev = "312b0f3f76fb75081a47a87770e14b9a29a72f6b" } +asteroid-mq = { git = "https://github.com/4t145/asteroid-mq.git", rev = "f9c96f3" } # asteroid-mq = { version = "0.1.0-alpha.5" } -asteroid-mq-sdk = { git = "https://github.com/4t145/asteroid-mq.git", rev = "312b0f3f76fb75081a47a87770e14b9a29a72f6b" } +asteroid-mq-sdk = { git = "https://github.com/4t145/asteroid-mq.git", rev = "f9c96f3" } # asteroid-mq-sdk = { version = "0.1.0-alpha.5" } #spacegate diff --git a/backend/spi/spi-log/src/serv/pgv2/log_pg_item_serv.rs b/backend/spi/spi-log/src/serv/pgv2/log_pg_item_serv.rs index d464225dc..7b5664852 100644 --- a/backend/spi/spi-log/src/serv/pgv2/log_pg_item_serv.rs +++ b/backend/spi/spi-log/src/serv/pgv2/log_pg_item_serv.rs @@ -769,11 +769,11 @@ async fn push_to_eda(req: &LogItemAddV2Req, ref_fields: &Vec, funs: &Tar // if the op is deleted or log disabled, send the delete event to stats if (req_clone.op.as_ref().map_or(false, |op| op.to_lowercase() == "delete")) || req_clone.disable.unwrap_or(false) { let stats_delete: StatsItemDeleteReq = req_clone.into(); - topic.send_event(stats_delete.inject_context(funs, ctx).json()).map_err(mq_error).await?; + topic.send_event_and_wait(stats_delete.inject_context(funs, ctx).json()).map_err(mq_error).await?; return Ok(()); } let stats_add: StatsItemAddReq = req_clone.into(); - topic.send_event(stats_add.inject_context(funs, ctx).json()).map_err(mq_error).await?; + topic.send_event_and_wait(stats_add.inject_context(funs, ctx).json()).map_err(mq_error).await?; } Ok(()) } From 469b64dcd0ca7a680e94ccf681951c070d171ab8 Mon Sep 17 00:00:00 2001 From: ZzIsGod1019 <1498852723@qq.com> Date: Thu, 26 Dec 2024 17:54:18 +0800 Subject: [PATCH 11/35] flow: improve log (#886) * flow: fix bug (inst search failed) * flow: fix bug (instance referral error) * flow: fix bug (instance referral failed) * flow: add api instance details * flow: improve log * flow: fix bug (switch model failed) --- .../flow/src/api/ca/flow_ca_model_api.rs | 12 +- .../flow/src/api/cc/flow_cc_inst_api.rs | 46 +- .../flow/src/api/cc/flow_cc_model_api.rs | 18 +- .../flow/src/api/ci/flow_ci_model_api.rs | 136 ++- .../flow/src/api/ct/flow_ct_model_api.rs | 6 +- .../middlewares/flow/src/domain/flow_inst.rs | 4 + .../middlewares/flow/src/dto/flow_inst_dto.rs | 84 +- .../flow/src/dto/flow_model_dto.rs | 23 +- .../flow/src/dto/flow_transition_dto.rs | 10 +- backend/middlewares/flow/src/serv.rs | 1 + backend/middlewares/flow/src/serv/clients.rs | 3 +- .../flow/src/serv/clients/kv_client.rs | 13 + .../{flow_log_client.rs => log_client.rs} | 106 +- .../flow/src/serv/clients/search_client.rs | 195 +++- .../flow/src/serv/flow_inst_serv.rs | 555 ++++----- .../flow/src/serv/flow_log_serv.rs | 378 ++++++ .../flow/src/serv/flow_model_serv.rs | 72 +- .../flow/src/serv/flow_model_version_serv.rs | 7 +- .../flow/src/serv/flow_state_serv.rs | 5 +- .../middlewares/flow/tests/test_flow_api.rs | 4 +- .../flow/tests/test_flow_scenes_fsm.rs | 1028 +++++++---------- .../flow/tests/test_flow_scenes_fsm1.rs | 599 ---------- .../flow/tests/test_flow_scenes_fsm2.rs | 349 ------ .../spi/spi-search/src/dto/search_item_dto.rs | 1 + .../src/serv/es/search_es_item_serv.rs | 4 + .../src/serv/pg/search_pg_item_serv.rs | 2 +- .../sdks/invoke/src/dto/search_item_dto.rs | 2 + 27 files changed, 1588 insertions(+), 2075 deletions(-) create mode 100644 backend/middlewares/flow/src/serv/clients/kv_client.rs rename backend/middlewares/flow/src/serv/clients/{flow_log_client.rs => log_client.rs} (64%) create mode 100644 backend/middlewares/flow/src/serv/flow_log_serv.rs delete mode 100644 backend/middlewares/flow/tests/test_flow_scenes_fsm1.rs delete mode 100644 backend/middlewares/flow/tests/test_flow_scenes_fsm2.rs diff --git a/backend/middlewares/flow/src/api/ca/flow_ca_model_api.rs b/backend/middlewares/flow/src/api/ca/flow_ca_model_api.rs index 8b4fc3172..6f562eace 100644 --- a/backend/middlewares/flow/src/api/ca/flow_ca_model_api.rs +++ b/backend/middlewares/flow/src/api/ca/flow_ca_model_api.rs @@ -36,11 +36,9 @@ impl FlowCaModelApi { for (_, rel_model_id) in req.0.rel_model_ids { let new_model = FlowModelServ::copy_or_reference_model(&rel_model_id, &req.0.op, FlowModelKind::AsModel, &funs, &ctx.0).await?; FlowInstServ::batch_update_when_switch_model( + &new_model, + None, None, - &new_model.tag, - &new_model.current_version_id, - new_model.states.clone(), - &new_model.init_state_id, &funs, &ctx.0, ) @@ -69,11 +67,9 @@ impl FlowCaModelApi { let _orginal_models = FlowModelServ::clean_rel_models(None, None, Some(vec![req.0.tag.clone()]), &funs, &ctx.0).await?; let new_model = FlowModelServ::copy_or_reference_model(&req.0.rel_model_id, &req.0.op, FlowModelKind::AsModel, &funs, &ctx.0).await?; FlowInstServ::batch_update_when_switch_model( + &new_model, + None, None, - &new_model.tag, - &new_model.current_version_id, - new_model.states.clone(), - &new_model.init_state_id, &funs, &ctx.0, ) diff --git a/backend/middlewares/flow/src/api/cc/flow_cc_inst_api.rs b/backend/middlewares/flow/src/api/cc/flow_cc_inst_api.rs index 31ec0c747..4938684ae 100644 --- a/backend/middlewares/flow/src/api/cc/flow_cc_inst_api.rs +++ b/backend/middlewares/flow/src/api/cc/flow_cc_inst_api.rs @@ -10,9 +10,7 @@ use tardis::web::web_resp::{TardisApiResult, TardisPage, TardisResp, Void}; use crate::dto::flow_external_dto::FlowExternalCallbackOp; use crate::dto::flow_inst_dto::{ - FlowInstAbortReq, FlowInstCommentReq, FlowInstDetailResp, FlowInstFindNextTransitionResp, FlowInstFindNextTransitionsReq, FlowInstFindStateAndTransitionsReq, - FlowInstFindStateAndTransitionsResp, FlowInstModifyAssignedReq, FlowInstModifyCurrentVarsReq, FlowInstOperateReq, FlowInstSearchReq, FlowInstStartReq, FlowInstSummaryResp, - FlowInstTransferReq, FlowInstTransferResp, + FlowInstAbortReq, FlowInstCommentReq, FlowInstDetailResp, FlowInstFilterReq, FlowInstFindNextTransitionResp, FlowInstFindNextTransitionsReq, FlowInstFindStateAndTransitionsReq, FlowInstFindStateAndTransitionsResp, FlowInstModifyAssignedReq, FlowInstModifyCurrentVarsReq, FlowInstOperateReq, FlowInstSearchReq, FlowInstSearchResp, FlowInstStartReq, FlowInstSummaryResp, FlowInstTransferReq, FlowInstTransferResp }; use crate::flow_constants; use crate::helper::loop_check_helper; @@ -111,6 +109,46 @@ impl FlowCcInstApi { TardisResp::ok(result) } + /// Find Instances + /// + /// 获取实例列表 + #[oai(path = "/details", method = "get")] + async fn paginate_detail_items( + &self, + flow_model_id: Query>, + rel_business_obj_id: Query>, + tag: Query>, + finish: Query>, + main: Query>, + current_state_id: Query>, + with_sub: Query>, + page_number: Query, + page_size: Query, + ctx: TardisContextExtractor, + _request: &Request, + ) -> TardisApiResult> { + let funs = flow_constants::get_tardis_inst(); + let result = FlowInstServ::paginate_detail_items( + &FlowInstFilterReq { + flow_version_id: flow_model_id.0, + tag: tag.0, + finish: finish.0, + main: main.0, + current_state_id: current_state_id.0, + rel_business_obj_ids: rel_business_obj_id.0.map(|id| vec![id]), + with_sub: with_sub.0, + ..Default::default() + }, + page_number.0, + page_size.0, + &funs, + &ctx.0, + ) + .await?; + ctx.0.execute_task().await?; + TardisResp::ok(result) + } + /// Find Next Transitions /// /// 获取下一个流转状态列表 @@ -287,7 +325,7 @@ impl FlowCcInstApi { /// Search Items #[oai(path = "/search", method = "put")] - async fn search(&self, mut search_req: Json, ctx: TardisContextExtractor) -> TardisApiResult> { + async fn search(&self, mut search_req: Json, ctx: TardisContextExtractor) -> TardisApiResult> { let funs = flow_constants::get_tardis_inst(); let resp = FlowInstServ::search(&mut search_req.0, &funs, &ctx.0).await?; TardisResp::ok(resp) diff --git a/backend/middlewares/flow/src/api/cc/flow_cc_model_api.rs b/backend/middlewares/flow/src/api/cc/flow_cc_model_api.rs index 22ddd4bf4..2030527fb 100644 --- a/backend/middlewares/flow/src/api/cc/flow_cc_model_api.rs +++ b/backend/middlewares/flow/src/api/cc/flow_cc_model_api.rs @@ -246,26 +246,14 @@ impl FlowCcModelApi { TardisResp::ok(Void {}) } - /// Unbind State By Model Id [Deprecated] + /// Unbind State By Model Id /// - /// 解绑状态 [已废弃] + /// 解绑状态 #[oai(path = "/:flow_model_id/unbind_state", method = "post")] async fn unbind_state(&self, flow_model_id: Path, req: Json, ctx: TardisContextExtractor, _request: &Request) -> TardisApiResult { let mut funs = flow_constants::get_tardis_inst(); funs.begin().await?; - FlowModelServ::modify_model( - &flow_model_id.0, - &mut FlowModelModifyReq { - modify_version: Some(FlowModelVersionModifyReq { - unbind_states: Some(vec![req.state_id.clone()]), - ..Default::default() - }), - ..Default::default() - }, - &funs, - &ctx.0, - ) - .await?; + FlowModelServ::unbind_state(&flow_model_id.0, &req.0, &funs, &ctx.0).await?; funs.commit().await?; ctx.0.execute_task().await?; TardisResp::ok(Void {}) diff --git a/backend/middlewares/flow/src/api/ci/flow_ci_model_api.rs b/backend/middlewares/flow/src/api/ci/flow_ci_model_api.rs index 0ada439ba..afa976139 100644 --- a/backend/middlewares/flow/src/api/ci/flow_ci_model_api.rs +++ b/backend/middlewares/flow/src/api/ci/flow_ci_model_api.rs @@ -8,11 +8,13 @@ use crate::flow_constants; use crate::serv::flow_inst_serv::FlowInstServ; use crate::serv::flow_model_serv::FlowModelServ; use crate::serv::flow_rel_serv::{FlowRelKind, FlowRelServ}; -use bios_basic::rbum::dto::rbum_filer_dto::{RbumBasicFilterReq, RbumRelFilterReq}; +use bios_basic::rbum::dto::rbum_filer_dto::{RbumBasicFilterReq, RbumItemRelFilterReq, RbumRelFilterReq}; use bios_basic::rbum::helper::rbum_scope_helper::check_without_owner_and_unsafe_fill_ctx; +use bios_basic::rbum::rbum_enumeration::RbumRelFromKind; use bios_basic::rbum::serv::rbum_item_serv::RbumItemCrudOperation; use bios_basic::rbum::serv::rbum_rel_serv::RbumRelServ; use itertools::Itertools; +use tardis::futures::future::join_all; use std::iter::Iterator; use tardis::basic::dto::TardisContext; use tardis::log::warn; @@ -119,29 +121,24 @@ impl FlowCiModelApi { check_without_owner_and_unsafe_fill_ctx(request, &funs, &mut ctx.0)?; funs.begin().await?; warn!("ci copy_or_reference_model req: {:?}", req.0); - let _orginal_models = FlowModelServ::clean_rel_models(None, None, None, &funs, &ctx.0).await?; + let orginal_models = FlowModelServ::clean_rel_models(None, None, None, &funs, &ctx.0).await?; // find rel models - let rel_model_ids = FlowRelServ::find_to_simple_rels( - &FlowRelKind::FlowModelTemplate, - &req.0.rel_template_id.clone().unwrap_or_default(), - None, - None, - &funs, - &ctx.0, - ) - .await? - .into_iter() - .map(|rel| rel.rel_id) - .collect_vec(); let rel_models = FlowModelServ::find_items( &FlowModelFilterReq { basic: RbumBasicFilterReq { - ids: Some(rel_model_ids), enabled: Some(true), own_paths: Some("".to_string()), with_sub_own_paths: true, ..Default::default() }, + rel: Some(RbumItemRelFilterReq { + optional: false, + rel_by_from: true, + tag: Some(FlowRelKind::FlowModelTemplate.to_string()), + from_rbum_kind: Some(RbumRelFromKind::Item), + rel_item_id: Some(req.0.rel_template_id.clone().unwrap_or_default()), + ..Default::default() + }), // main: Some(true), ..Default::default() }, @@ -154,16 +151,16 @@ impl FlowCiModelApi { let mut result = HashMap::new(); for rel_model in rel_models { let new_model = FlowModelServ::copy_or_reference_model(&rel_model.id, &req.0.op, FlowModelKind::AsModel, &funs, &ctx.0).await?; - FlowInstServ::batch_update_when_switch_model( - new_model.rel_template_ids.first().cloned(), - &new_model.tag, - &new_model.current_version_id, - new_model.states.clone(), - &new_model.init_state_id, - &funs, - &ctx.0, - ) - .await?; + if new_model.main && orginal_models.contains_key(&new_model.tag) { + FlowInstServ::batch_update_when_switch_model( + &new_model, + new_model.rel_template_ids.first().cloned(), + req.update_states.clone().map(|update_states| update_states.get(&new_model.tag).cloned().unwrap_or_default()), + &funs, + &ctx.0, + ) + .await?; + } result.insert(rel_model.id.clone(), new_model.id.clone()); } funs.commit().await?; @@ -261,45 +258,56 @@ impl FlowCiModelApi { check_without_owner_and_unsafe_fill_ctx(request, &funs, &mut ctx.0)?; warn!("ci exist_rel_by_template_ids req: {:?}", req.0); let support_tags = req.0.support_tags; - let mut result = vec![]; - for (rel_template_id, current_tags) in req.0.rel_tag_by_template_ids { - // 当前模板tag和需要支持的tag取交集,得到当前模板tag中需要检查的tag列表 - let tags = current_tags.into_iter().filter(|current_tag| support_tags.contains(current_tag)).collect_vec(); - if !tags.is_empty() { - // 当前模板关联的模型所支持的tag - let rel_model_tags = FlowModelServ::find_items( - &FlowModelFilterReq { - basic: RbumBasicFilterReq { - ids: Some( - FlowRelServ::find_to_simple_rels(&FlowRelKind::FlowModelTemplate, &rel_template_id, None, None, &funs, &ctx.0) - .await? - .into_iter() - .map(|rel| rel.rel_id) - .collect_vec(), - ), - own_paths: Some("".to_string()), - with_sub_own_paths: true, - ..Default::default() - }, - main: Some(true), - ..Default::default() - }, - None, - None, - &funs, - &ctx.0, - ) - .await? - .into_iter() - .map(|model| model.tag.clone()) - .collect_vec(); - // 如果出现了当前模板tag中需要检查的tag没有被当前模板关联,则说明当前关联模板不是可用状态 - if !tags.into_iter().filter(|tag| !rel_model_tags.contains(tag)).collect_vec().is_empty() { - continue; - } - } - result.push(rel_template_id.clone()); - } + let result = join_all( + req.0.rel_tag_by_template_ids + .iter() + .map(|(rel_template_id, current_tags)| async { + // 当前模板tag和需要支持的tag取交集,得到当前模板tag中需要检查的tag列表 + let tags = current_tags.iter().filter(|current_tag| support_tags.contains(current_tag)).collect_vec(); + if !tags.is_empty() { + // 当前模板关联的模型所支持的tag + let rel_model_tags = FlowModelServ::find_items( + &FlowModelFilterReq { + basic: RbumBasicFilterReq { + own_paths: Some("".to_string()), + with_sub_own_paths: true, + ..Default::default() + }, + rel: Some(RbumItemRelFilterReq { + optional: false, + rel_by_from: true, + tag: Some(FlowRelKind::FlowModelTemplate.to_string()), + from_rbum_kind: Some(RbumRelFromKind::Item), + rel_item_id: Some(rel_template_id.clone()), + ..Default::default() + }), + main: Some(true), + ..Default::default() + }, + None, + None, + &funs, + &ctx.0, + ) + .await + .unwrap() + .into_iter() + .map(|model| model.tag.clone()) + .collect_vec(); + // 如果出现了当前模板tag中需要检查的tag没有被当前模板关联,则说明当前关联模板不是可用状态 + if !tags.into_iter().filter(|tag| !rel_model_tags.contains(tag)).collect_vec().is_empty() { + return None; + } + } + Some(rel_template_id.clone()) + }) + .collect_vec(), + ) + .await + .into_iter() + .filter(|r| r.is_some()) + .map(|r| r.unwrap_or_default()) + .collect_vec(); TardisResp::ok(result) } diff --git a/backend/middlewares/flow/src/api/ct/flow_ct_model_api.rs b/backend/middlewares/flow/src/api/ct/flow_ct_model_api.rs index d20f1e441..4c2511a6c 100644 --- a/backend/middlewares/flow/src/api/ct/flow_ct_model_api.rs +++ b/backend/middlewares/flow/src/api/ct/flow_ct_model_api.rs @@ -83,11 +83,9 @@ impl FlowCtModelApi { .await?; } FlowInstServ::batch_update_when_switch_model( + &new_model, req.0.rel_template_id.clone(), - &new_model.tag, - &new_model.current_version_id, - new_model.states.clone(), - &new_model.init_state_id, + None, &funs, &ctx.0, ) diff --git a/backend/middlewares/flow/src/domain/flow_inst.rs b/backend/middlewares/flow/src/domain/flow_inst.rs index 23dafffdd..a976c77e2 100644 --- a/backend/middlewares/flow/src/domain/flow_inst.rs +++ b/backend/middlewares/flow/src/domain/flow_inst.rs @@ -52,6 +52,10 @@ pub struct Model { #[index] #[sea_orm(extra = "DEFAULT CURRENT_TIMESTAMP")] pub create_time: chrono::DateTime, + /// Creation time / 创建时间 + #[index] + #[sea_orm(extra = "DEFAULT CURRENT_TIMESTAMP")] + pub update_time: Option>, /// Finisher information / 完成者信息 pub finish_ctx: Option, diff --git a/backend/middlewares/flow/src/dto/flow_inst_dto.rs b/backend/middlewares/flow/src/dto/flow_inst_dto.rs index 075ba6f92..d23955aa4 100644 --- a/backend/middlewares/flow/src/dto/flow_inst_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_inst_dto.rs @@ -11,7 +11,7 @@ use tardis::{ use super::{ flow_model_dto::FlowModelRelTransitionExt, - flow_state_dto::{FlowGuardConf, FlowStateKind, FlowStateOperatorKind, FlowStateRelModelExt, FlowStateVar, FlowSysStateKind}, + flow_state_dto::{FlowStateKind, FlowStateOperatorKind, FlowStateRelModelExt, FlowStateVar, FlowSysStateKind}, flow_transition_dto::FlowTransitionDoubleCheckInfo, flow_var_dto::FlowVarInfo, }; @@ -99,6 +99,7 @@ pub struct FlowInstSummaryResp { pub create_ctx: FlowOperationContext, /// 创建时间 pub create_time: DateTime, + pub update_time: Option>, /// 结束上下文信息 pub finish_ctx: Option, /// 结束时间 @@ -126,6 +127,10 @@ pub struct FlowInstDetailResp { /// /// 关联的[工作流模板](super::flow_model_dto::FlowModelDetailResp) id pub rel_flow_version_id: String, + /// Associated [flow_model](super::flow_model_dto::FlowModelDetailResp) id + /// + /// 关联的[工作流模板](super::flow_model_dto::FlowModelDetailResp) ID + pub rel_flow_model_id: String, /// Associated [flow_model](super::flow_model_dto::FlowModelDetailResp) name /// /// 关联的[工作流模板](super::flow_model_dto::FlowModelDetailResp) 名称 @@ -179,6 +184,8 @@ pub struct FlowInstDetailResp { pub create_ctx: FlowOperationContext, /// 创建时间 pub create_time: DateTime, + /// 创建时间 + pub update_time: Option>, /// 结束上下文 pub finish_ctx: Option, @@ -224,7 +231,8 @@ pub struct FLowInstStateApprovalConf { // 流程实例中对应的数据存储 #[derive(Serialize, Deserialize, PartialEq, Clone, Debug, Default, poem_openapi::Object, sea_orm::FromJsonQueryResult)] pub struct FlowInstArtifacts { - pub guard_conf: FlowGuardConf, // 当前操作人权限 + pub his_operators: Option>, // 历史操作人 + pub curr_operators: Option>, // 当前操作人 pub prohibit_guard_by_spec_account_ids: Option>, // 禁止操作的指定用户ID pub approval_result: HashMap>>, // 当前审批结果 pub approval_total: Option>, // 审批总数 @@ -232,14 +240,16 @@ pub struct FlowInstArtifacts { pub curr_vars: Option>, // 当前参数列表 pub prev_non_auto_state_id: Option>, // 上一个非自动节点ID列表 pub prev_non_auto_account_id: Option, // 上一个节点操作人ID + pub state: Option, // 状态 } // 流程实例中数据存储更新 #[derive(Serialize, Deserialize, PartialEq, Clone, Debug, Default, sea_orm::FromJsonQueryResult)] pub struct FlowInstArtifactsModifyReq { - pub guard_conf: Option, // 当前操作人权限 + pub state: Option, pub prohibit_guard_conf_account_ids: Option>, // 禁止操作人ID列表 - pub guard_conf_account_ids: Option>, // 更新操作人列表 + pub add_his_operator: Option, // 添加历史操作人 + pub curr_operators: Option>, // 更新操作人列表 pub add_approval_result: Option<(String, FlowApprovalResultKind)>, // 增加审批结果 pub form_state_map: Option>, // 录入节点映射 key为节点ID,对应的value为节点中的录入的参数 pub clear_form_result: Option, // 清除节点录入信息 @@ -513,6 +523,7 @@ pub struct FlowInstSummaryResult { pub create_ctx: Value, pub create_time: DateTime, + pub update_time: Option>, pub finish_ctx: Option, pub finish_time: Option>, @@ -569,6 +580,8 @@ pub enum FlowInstQueryKind { Approval, /// 我创建的 Create, + /// 历史相关 + History, } #[derive(poem_openapi::Object, Serialize, Deserialize, Debug, Clone)] @@ -604,3 +617,66 @@ impl FlowInstSearchSortKind { } } } + +#[derive(Serialize, Deserialize, Debug, poem_openapi::Enum, Default, Eq, Hash, PartialEq, Clone)] +pub enum FlowInstStateKind { + /// 录入中 + #[default] + Form, + /// 审批中 + Approval, + /// 已退回 + Back, + /// 已撤销 + Revoke, + /// 审批通过 + Pass, + /// 审批拒绝 + Overrule, +} + +/// 工作流实例的概要信息 +#[derive(Serialize, Deserialize, Debug, poem_openapi::Object)] +pub struct FlowInstSearchResp { + pub id: String, + + pub state: Option, + + pub code: String, + /// Associated [flow_model](super::flow_model_version_dto::FlowModelVersionDetailResp) id + /// + /// 关联的[工作流模板](super::flow_model_version_dto::FlowModelVersionDetailResp) id + pub rel_flow_version_id: String, + /// Associated [flow_model](super::flow_model_dto::FlowModelDetailResp) id + /// + /// 关联的[工作流模板](super::flow_model_dto::FlowModelDetailResp) 名称 + pub rel_flow_model_id: String, + /// Associated [flow_model](super::flow_model_dto::FlowModelDetailResp) name + /// + /// 关联的[工作流模板](super::flow_model_dto::FlowModelDetailResp) 名称 + pub rel_flow_model_name: String, + /// 关联业务ID + pub rel_business_obj_id: String, + /// 当前状态ID + pub current_state_id: String, + /// 当前状态名 + pub current_state_name: String, + /// 创建上下文信息 + pub create_ctx: FlowOperationContext, + /// 创建时间 + pub create_time: DateTime, + /// 结束上下文信息 + pub finish_ctx: Option, + /// 结束时间 + pub finish_time: Option>, + /// 是否异常终止 + pub finish_abort: bool, + /// 输出信息 + pub output_message: Option, + /// 触发的动作 + pub rel_transition: Option, + + pub own_paths: String, + + pub tag: String, +} \ No newline at end of file diff --git a/backend/middlewares/flow/src/dto/flow_model_dto.rs b/backend/middlewares/flow/src/dto/flow_model_dto.rs index 35fd18909..9240a2a46 100644 --- a/backend/middlewares/flow/src/dto/flow_model_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_model_dto.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap, fmt}; use bios_basic::rbum::{ dto::rbum_filer_dto::{RbumBasicFilterReq, RbumItemFilterFetcher, RbumItemRelFilterReq}, @@ -162,6 +162,7 @@ pub struct FlowModelSummaryResp { pub icon: String, pub info: String, pub init_state_id: String, + pub rel_model_id: String, pub current_version_id: String, pub owner: String, pub own_paths: String, @@ -185,6 +186,16 @@ pub struct FlowModelRelTransitionExt { pub from_flow_state_name: String, } +impl fmt::Display for FlowModelRelTransitionExt { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.id.as_str() { + "__EDIT__" => write!(f, "编辑"), + "__DELETE__" => write!(f, "删除"), + _ => write!(f, "{}({})", self.name, self.from_flow_state_name), + } + } +} + /// 工作流模型详细信息 #[derive(Serialize, Deserialize, Debug, Clone, poem_openapi::Object, sea_orm::FromQueryResult)] pub struct FlowModelDetailResp { @@ -226,7 +237,7 @@ pub struct FlowModelDetailResp { impl FlowModelDetailResp { pub fn transitions(&self) -> Vec { match &self.transitions { - Some(transitions) => TardisFuns::json.json_to_obj(transitions.clone()).unwrap(), + Some(transitions) => TardisFuns::json.json_to_obj(transitions.clone()).unwrap_or_default(), None => vec![], } } @@ -310,6 +321,8 @@ pub struct FlowModelAggResp { pub scope_level: RbumScopeLevelKind, pub disabled: bool, + /// 是否作为主流程 + pub main: bool, } /// 绑定状态 @@ -348,6 +361,8 @@ pub struct FlowModelUnbindStateReq { /// /// 关联的[工作流状态](super::flow_state_dto::FlowStateDetailResp) id pub state_id: String, + /// 新的状态ID + pub new_state_id: String, } /// 状态排序 @@ -475,6 +490,6 @@ pub struct FlowModelSyncModifiedFieldReq { pub rel_template_id: Option, pub tag: String, /// 参数列表 - pub add_fields: Vec, - pub delete_fields: Vec, + pub add_fields: Option>, + pub delete_fields: Option>, } diff --git a/backend/middlewares/flow/src/dto/flow_transition_dto.rs b/backend/middlewares/flow/src/dto/flow_transition_dto.rs index 442e69949..65c548745 100644 --- a/backend/middlewares/flow/src/dto/flow_transition_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_transition_dto.rs @@ -610,13 +610,16 @@ pub enum FlowTransitionFrontActionInfoRelevanceRelation { #[serde(rename = "between")] #[oai(rename = "between")] Between, + #[serde(rename = "is_not_null")] + #[oai(rename = "is_not_null")] + IsNotNull, } impl FlowTransitionFrontActionInfoRelevanceRelation { pub fn check_conform(&self, mut left_value: String, right_value: String) -> bool { use itertools::Itertools; - if left_value.is_empty() || left_value == "null" || right_value == "null" { + if left_value.is_empty() || left_value == "null" { return false; } // 单项判断(例如等于,不等于,大于,小于),如果参数是单元素数组,则取出数据,否则说明格式错误直接返回false @@ -663,7 +666,10 @@ impl FlowTransitionFrontActionInfoRelevanceRelation { return false; } left_value >= interval[0] && left_value <= interval[1] - } + }, + FlowTransitionFrontActionInfoRelevanceRelation::IsNotNull => { + !left_value.is_empty() + }, } } } diff --git a/backend/middlewares/flow/src/serv.rs b/backend/middlewares/flow/src/serv.rs index 2e24938bc..1965a8f24 100644 --- a/backend/middlewares/flow/src/serv.rs +++ b/backend/middlewares/flow/src/serv.rs @@ -8,3 +8,4 @@ pub mod flow_model_version_serv; pub mod flow_rel_serv; pub mod flow_state_serv; pub mod flow_transition_serv; +pub mod flow_log_serv; diff --git a/backend/middlewares/flow/src/serv/clients.rs b/backend/middlewares/flow/src/serv/clients.rs index 7d5686866..e9f788cec 100644 --- a/backend/middlewares/flow/src/serv/clients.rs +++ b/backend/middlewares/flow/src/serv/clients.rs @@ -1,2 +1,3 @@ -pub mod flow_log_client; +pub mod log_client; pub mod search_client; +pub mod kv_client; diff --git a/backend/middlewares/flow/src/serv/clients/kv_client.rs b/backend/middlewares/flow/src/serv/clients/kv_client.rs new file mode 100644 index 000000000..c88bef23d --- /dev/null +++ b/backend/middlewares/flow/src/serv/clients/kv_client.rs @@ -0,0 +1,13 @@ +use bios_sdk_invoke::clients::spi_kv_client::SpiKvClient; +use tardis::{basic::{dto::TardisContext, result::TardisResult}, TardisFunsInst}; + + +pub struct FlowKvClient; + +impl FlowKvClient { + pub async fn get_account_name(account_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult { + let account_name = SpiKvClient::get_item(format!("__k_n__:iam_account:{}", account_id), None, funs, ctx).await? + .map(|resp| resp.value.as_str().unwrap_or("").to_string()).unwrap_or_default(); + Ok(account_name) + } +} \ No newline at end of file diff --git a/backend/middlewares/flow/src/serv/clients/flow_log_client.rs b/backend/middlewares/flow/src/serv/clients/log_client.rs similarity index 64% rename from backend/middlewares/flow/src/serv/clients/flow_log_client.rs rename to backend/middlewares/flow/src/serv/clients/log_client.rs index a70baf54d..2801a0a57 100644 --- a/backend/middlewares/flow/src/serv/clients/flow_log_client.rs +++ b/backend/middlewares/flow/src/serv/clients/log_client.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use bios_sdk_invoke::clients::{ iam_client::IamClient, - spi_log_client::{LogItemAddV2Req, SpiLogClient}, + spi_log_client::{LogItemAddReq, LogItemAddV2Req, SpiLogClient}, }; use serde::Serialize; @@ -22,8 +22,8 @@ pub struct LogParamContent { pub name: Option, pub sub_kind: Option, pub sub_id: Option, - pub old_content: Option, - pub new_content: Option, + pub old_content: String, + pub new_content: String, pub detail: Option, pub operand: Option, pub operand_id: Option, @@ -31,6 +31,7 @@ pub struct LogParamContent { pub operand_name: Option, pub flow_message: Option, pub flow_result: Option, + pub flow_referral: Option, } #[derive(Serialize, Default, Debug, Clone)] @@ -43,6 +44,20 @@ pub struct LogParamExt { pub delete: Option, } + +pub enum LogParamExtSceneKind { + ApprovalFlow, + Dynamic, +} +impl From for String { + fn from(val: LogParamExtSceneKind) -> Self { + match val { + LogParamExtSceneKind::Dynamic => "dynamic".to_string(), + LogParamExtSceneKind::ApprovalFlow => "approval_flow".to_string(), + } + } +} + pub enum LogParamTag { DynamicLog, ApprovalFlow, @@ -94,6 +109,7 @@ impl FlowLogClient { kind: Option, op_kind: Option, rel_key: Option, + is_v2: bool, ctx: &TardisContext, push: bool, ) -> TardisResult<()> { @@ -103,21 +119,40 @@ impl FlowLogClient { Box::pin(async move { let task_handle = tokio::spawn(async move { let funs = flow_constants::get_tardis_inst(); - Self::add_item( - tag, - content, - ext, - kind, - key.clone(), - op_kind, - rel_key, - Some(tardis::chrono::Utc::now().to_rfc3339()), - &funs, - &ctx_clone, - push_clone, // 使用克隆的 push 变量 - ) - .await - .unwrap(); + if is_v2 { + Self::addv2_item( + tag, + content, + ext, + kind, + key.clone(), + op_kind, + rel_key, + Some(tardis::chrono::Utc::now().to_rfc3339()), + &funs, + &ctx_clone, + push_clone, // 使用克隆的 push 变量 + ) + .await + .unwrap(); + } else { + Self::add_item( + tag, + content, + ext, + kind, + key.clone(), + op_kind, + rel_key, + Some(tardis::chrono::Utc::now().to_rfc3339()), + &funs, + &ctx_clone, + push_clone, // 使用克隆的 push 变量 + ) + .await + .unwrap(); + } + }); task_handle.await.unwrap(); Ok(()) @@ -127,6 +162,41 @@ impl FlowLogClient { } pub async fn add_item( + tag: LogParamTag, + content: LogParamContent, + ext: Option, + kind: Option, + key: Option, + op: Option, + rel_key: Option, + ts: Option, + funs: &TardisFunsInst, + ctx: &TardisContext, + _push: bool, + ) -> TardisResult<()> { + // generate log item + let tag: String = tag.into(); + let own_paths = if ctx.own_paths.len() < 2 { None } else { Some(ctx.own_paths.clone()) }; + let owner = if ctx.owner.len() < 2 { None } else { Some(ctx.owner.clone()) }; + + let req = LogItemAddReq { + id: None, + tag: tag.to_string(), + content: TardisFuns::json.obj_to_string(&content).expect("content not a valid json value"), + kind, + ext, + key, + op, + rel_key, + ts: ts.map(|ts| DateTime::parse_from_rfc3339(&ts).unwrap_or_default().with_timezone(&Utc)), + owner, + own_paths, + }; + SpiLogClient::add(req, funs, ctx).await?; + Ok(()) + } + + pub async fn addv2_item( tag: LogParamTag, content: LogParamContent, ext: Option, diff --git a/backend/middlewares/flow/src/serv/clients/search_client.rs b/backend/middlewares/flow/src/serv/clients/search_client.rs index d05f1031f..530d5a3f6 100644 --- a/backend/middlewares/flow/src/serv/clients/search_client.rs +++ b/backend/middlewares/flow/src/serv/clients/search_client.rs @@ -7,8 +7,7 @@ use bios_sdk_invoke::{ spi_search_client::SpiSearchClient, }, dto::search_item_dto::{ - SearchItemAddReq, SearchItemModifyReq, SearchItemQueryReq, SearchItemSearchCtxReq, SearchItemSearchPageReq, SearchItemSearchReq, SearchItemSearchResp, - SearchItemVisitKeysReq, + AdvSearchItemQueryReq, BasicQueryCondInfo, BasicQueryOpKind, SearchItemAddReq, SearchItemModifyReq, SearchItemQueryReq, SearchItemSearchCtxReq, SearchItemSearchPageReq, SearchItemSearchReq, SearchItemSearchResp, SearchItemVisitKeysReq }, }; use itertools::Itertools; @@ -17,13 +16,13 @@ use tardis::{ basic::{dto::TardisContext, field::TrimString, result::TardisResult}, log::debug, tokio, - web::web_resp::TardisPage, + web::{poem_openapi::types::ToJSON, web_resp::TardisPage}, TardisFuns, TardisFunsInst, }; use crate::{ dto::{ - flow_inst_dto::FlowInstFilterReq, + flow_inst_dto::{FlowInstDetailResp, FlowInstFilterReq}, flow_model_dto::{FlowModelDetailResp, FlowModelFilterReq, FlowModelRelTransitionExt}, flow_model_version_dto::FlowModelVersionFilterReq, flow_state_dto::FlowGuardConf, @@ -37,14 +36,15 @@ use crate::{ }, }; -const SEARCH_TAG: &str = "flow_model"; +const SEARCH_MODEL_TAG: &str = "flow_model"; +const SEARCH_INSTANCE_TAG: &str = "flow_inst"; pub struct FlowSearchClient; impl FlowSearchClient { pub async fn modify_business_obj_search(rel_business_obj_id: &str, tag: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { let tag_search_map = Self::get_tag_search_map(); - let rel_version_ids = FlowInstServ::find_details( + let rel_version_ids = FlowInstServ::find_detail_items( &FlowInstFilterReq { rel_business_obj_ids: Some(vec![rel_business_obj_id.to_string()]), main: Some(false), @@ -81,11 +81,7 @@ impl FlowSearchClient { .pop() .map(|rel| TardisFuns::json.str_to_obj::(&rel.ext).unwrap_or_default()); if let Some(ext) = rel_transition_ext { - rel_transition_names.push(match ext.id.as_str() { - "__EDIT__" => "编辑".to_string(), - "__DELETE__" => "删除".to_string(), - _ => format!("{}({})", ext.name, ext.from_flow_state_name), - }); + rel_transition_names.push(ext.to_string()); } } } @@ -183,7 +179,7 @@ impl FlowSearchClient { let key = model_id.clone(); if *is_modify { let modify_req = SearchItemModifyReq { - kind: Some(SEARCH_TAG.to_string()), + kind: Some(SEARCH_MODEL_TAG.to_string()), title: Some(model_resp.name.clone()), name: Some(model_resp.name.clone()), content: Some(model_resp.name.clone()), @@ -210,14 +206,14 @@ impl FlowSearchClient { kv_disable: None, }; if let Some(_topic) = get_topic(&SPI_RPC_TOPIC) { - EventCenterClient { topic_code: SPI_RPC_TOPIC }.modify_item_and_name(SEARCH_TAG, &key, &modify_req, funs, ctx).await?; + EventCenterClient { topic_code: SPI_RPC_TOPIC }.modify_item_and_name(SEARCH_MODEL_TAG, &key, &modify_req, funs, ctx).await?; } else { - SpiSearchClient::modify_item_and_name(SEARCH_TAG, &key, &modify_req, funs, ctx).await?; + SpiSearchClient::modify_item_and_name(SEARCH_MODEL_TAG, &key, &modify_req, funs, ctx).await?; } } else { let add_req = SearchItemAddReq { - tag: SEARCH_TAG.to_string(), - kind: SEARCH_TAG.to_string(), + tag: SEARCH_MODEL_TAG.to_string(), + kind: SEARCH_MODEL_TAG.to_string(), key: TrimString(key), title: model_resp.name.clone(), content: model_resp.name.clone(), @@ -254,9 +250,125 @@ impl FlowSearchClient { // model 全局搜索删除埋点方法 pub async fn delete_model_search(model_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { if let Some(_topic) = get_topic(&SPI_RPC_TOPIC) { - EventCenterClient { topic_code: SPI_RPC_TOPIC }.delete_item_and_name(SEARCH_TAG, model_id, funs, ctx).await?; + EventCenterClient { topic_code: SPI_RPC_TOPIC }.delete_item_and_name(SEARCH_MODEL_TAG, model_id, funs, ctx).await?; } else { - SpiSearchClient::delete_item_and_name(SEARCH_TAG, model_id, funs, ctx).await?; + SpiSearchClient::delete_item_and_name(SEARCH_MODEL_TAG, model_id, funs, ctx).await?; + } + Ok(()) + } + + pub async fn async_add_or_modify_instance_search(inst_id: &str, is_modify: Box, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + let ctx_clone = ctx.clone(); + let mock_ctx = TardisContext { + own_paths: "".to_string(), + ..ctx.clone() + }; + let inst_resp = FlowInstServ::get( + inst_id, + funs, + &mock_ctx, + ) + .await?; + ctx.add_async_task(Box::new(|| { + Box::pin(async move { + let task_handle = tokio::spawn(async move { + let funs = flow_constants::get_tardis_inst(); + let _ = Self::add_or_modify_instance_search(&inst_resp, is_modify, &funs, &ctx_clone).await; + }); + task_handle.await.unwrap(); + Ok(()) + }) + })) + .await + } + + // flow inst 全局搜索埋点方法 + pub async fn add_or_modify_instance_search(inst_resp: &FlowInstDetailResp, is_modify: Box, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + let inst_id = &inst_resp.id; + // 数据共享权限处理 + let tenant = rbum_scope_helper::get_path_item(RbumScopeLevelKind::L1.to_int(), &inst_resp.own_paths).unwrap_or_default(); + let app = rbum_scope_helper::get_path_item(RbumScopeLevelKind::L2.to_int(), &inst_resp.own_paths).unwrap_or_default(); + let visit_tenants = vec![tenant.clone()]; + let visit_apps = vec![app.clone()]; + let own_paths = Some(inst_resp.own_paths.clone()); + let name = inst_resp.create_vars.clone().unwrap_or_default().get("name").unwrap_or(&json!("")).as_str().unwrap_or("").to_string(); + let key = inst_id.clone(); + if *is_modify { + let modify_req = SearchItemModifyReq { + kind: Some(SEARCH_INSTANCE_TAG.to_string()), + title: Some(inst_resp.code.clone()), + name: Some(name.clone()), + content: Some(format!("{} {}", inst_resp.code, name)), + owner: Some(inst_resp.create_ctx.owner.clone()), + own_paths, + create_time: Some(inst_resp.create_time), + update_time: inst_resp.update_time, + ext: Some(json!({ + "tag": inst_resp.tag, + "current_state_id": inst_resp.current_state_id, + "current_state_name": inst_resp.current_state_name, + "current_state_kind": inst_resp.current_state_kind, + "rel_business_obj_id": inst_resp.rel_business_obj_id, + "finish_time": inst_resp.finish_time, + "rel_transition": inst_resp.rel_transition.clone().unwrap_or_default().to_string(), + "his_operators": inst_resp.artifacts.as_ref().map(|artifacts| artifacts.his_operators.clone().unwrap_or_default()), + "curr_operators": inst_resp.artifacts.as_ref().map(|artifacts| artifacts.curr_operators.clone().unwrap_or_default()), + "tenant_id": tenant.clone(), + "app_id": app.clone(), + })), + ext_override: Some(true), + visit_keys: Some(SearchItemVisitKeysReq { + accounts: None, + apps: Some(visit_apps), + tenants: Some(visit_tenants), + roles: None, + groups: None, + }), + kv_disable: None, + }; + if let Some(_topic) = get_topic(&SPI_RPC_TOPIC) { + EventCenterClient { topic_code: SPI_RPC_TOPIC }.modify_item_and_name(SEARCH_MODEL_TAG, &key, &modify_req, funs, ctx).await?; + } else { + SpiSearchClient::modify_item_and_name(SEARCH_MODEL_TAG, &key, &modify_req, funs, ctx).await?; + } + } else { + let add_req = SearchItemAddReq { + tag: SEARCH_INSTANCE_TAG.to_string(), + kind: SEARCH_INSTANCE_TAG.to_string(), + key: TrimString(key), + title: inst_resp.code.clone(), + content: format!("{} {}", inst_resp.code, name), + owner: Some(inst_resp.create_ctx.owner.clone()), + own_paths, + create_time: Some(inst_resp.create_time), + update_time: inst_resp.update_time, + ext: Some(json!({ + "tag": inst_resp.tag, + "current_state_id": inst_resp.current_state_id, + "current_state_name": inst_resp.current_state_name, + "current_state_kind": inst_resp.current_state_kind, + "rel_business_obj_id": inst_resp.rel_business_obj_id, + "finish_time": inst_resp.finish_time, + "rel_transition": inst_resp.rel_transition.clone().unwrap_or_default().to_string(), + "his_operators": inst_resp.artifacts.as_ref().map(|artifacts| artifacts.his_operators.clone().unwrap_or_default()), + "curr_operators": inst_resp.artifacts.as_ref().map(|artifacts| artifacts.curr_operators.clone().unwrap_or_default()), + "tenant_id": tenant.clone(), + "app_id": app.clone(), + })), + visit_keys: Some(SearchItemVisitKeysReq { + accounts: None, + apps: Some(visit_apps), + tenants: Some(visit_tenants), + roles: None, + groups: None, + }), + kv_disable: None, + }; + if let Some(_topic) = get_topic(&SPI_RPC_TOPIC) { + EventCenterClient { topic_code: SPI_RPC_TOPIC }.add_item_and_name(&add_req, Some(inst_resp.code.clone()), funs, ctx).await?; + } else { + SpiSearchClient::add_item_and_name(&add_req, Some(inst_resp.code.clone()), funs, ctx).await?; + } } Ok(()) } @@ -265,43 +377,62 @@ impl FlowSearchClient { SpiSearchClient::search(search_req, funs, ctx).await } - pub async fn search_guard_account_num(guard_conf: &FlowGuardConf, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult> { + pub async fn search_guard_accounts(guard_conf: &FlowGuardConf, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult> { if guard_conf.guard_by_spec_account_ids.is_empty() && guard_conf.guard_by_spec_org_ids.is_empty() && guard_conf.guard_by_spec_role_ids.is_empty() { debug!("flow search_guard_account_num result : 0"); - return Ok(Some(0)); + return Ok(vec![]); } - let mut search_ctx_req = SearchItemSearchCtxReq { - apps: Some(vec![rbum_scope_helper::get_path_item(RbumScopeLevelKind::L2.to_int(), &ctx.own_paths).unwrap_or_default()]), - ..Default::default() - }; let mut query = SearchItemQueryReq::default(); + let mut adv_query = vec![]; + if !guard_conf.guard_by_spec_account_ids.is_empty() { query.keys = Some(guard_conf.guard_by_spec_account_ids.clone().into_iter().map(|account_id| account_id.into()).collect_vec()); } + if !guard_conf.guard_by_spec_org_ids.is_empty() { - search_ctx_req.groups = Some(guard_conf.guard_by_spec_org_ids.clone()); + adv_query.push(AdvSearchItemQueryReq { + group_by_or: Some(true), + ext_by_or: Some(true), + ext: Some(guard_conf.guard_by_spec_org_ids.clone().into_iter().map(|org_id| BasicQueryCondInfo { + field: "dept_id".to_string(), + op: BasicQueryOpKind::In, + value: org_id.to_json().unwrap_or(json!("")), + }).collect_vec()), + }); } if !guard_conf.guard_by_spec_role_ids.is_empty() { - search_ctx_req.roles = Some(guard_conf.guard_by_spec_role_ids.clone()); + adv_query.push(AdvSearchItemQueryReq { + group_by_or: Some(true), + ext_by_or: Some(true), + ext: Some(guard_conf.guard_by_spec_role_ids.clone().into_iter().map(|role_id| BasicQueryCondInfo { + field: "role_id".to_string(), + op: BasicQueryOpKind::In, + value: role_id.to_json().unwrap_or(json!("")), + }).collect_vec()), + }); + } + if !adv_query.is_empty() { + adv_query[0].group_by_or = Some(false); } let result = SpiSearchClient::search( &SearchItemSearchReq { tag: "iam_account".to_string(), - ctx: search_ctx_req, - query: SearchItemQueryReq { ..Default::default() }, - adv_query: None, + ctx: SearchItemSearchCtxReq::default(), + query, + adv_by_or: Some(!guard_conf.guard_by_spec_account_ids.is_empty()), + adv_query: Some(adv_query), sort: None, page: SearchItemSearchPageReq { number: 1, - size: 1, - fetch_total: true, + size: 999, + fetch_total: false, }, }, funs, ctx, ) .await? - .map(|result| result.total_size); + .map(|result| result.records.into_iter().map(|record| record.key).collect_vec()).unwrap_or_default(); debug!("flow search_guard_account_num result : {:?}", result); Ok(result) } diff --git a/backend/middlewares/flow/src/serv/flow_inst_serv.rs b/backend/middlewares/flow/src/serv/flow_inst_serv.rs index f8b398cda..feee41ea0 100644 --- a/backend/middlewares/flow/src/serv/flow_inst_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_inst_serv.rs @@ -7,13 +7,10 @@ use async_recursion::async_recursion; use bios_basic::{ dto::BasicQueryCondInfo, rbum::{ - dto::rbum_filer_dto::RbumBasicFilterReq, - helper::rbum_scope_helper, - rbum_enumeration::RbumScopeLevelKind, - serv::{ + dto::rbum_filer_dto::RbumBasicFilterReq, serv::{ rbum_crud_serv::{ID_FIELD, NAME_FIELD, REL_DOMAIN_ID_FIELD, REL_KIND_ID_FIELD}, rbum_item_serv::{RbumItemCrudOperation, RBUM_ITEM_TABLE}, - }, + } }, }; use bios_sdk_invoke::dto::search_item_dto::{SearchItemQueryReq, SearchItemSearchCtxReq, SearchItemSearchPageReq, SearchItemSearchReq}; @@ -30,7 +27,7 @@ use tardis::{ futures_util::future::join_all, log::{debug, error}, serde_json::Value, - web::{poem_openapi::types::Type, web_resp::TardisPage}, + web::web_resp::TardisPage, TardisFuns, TardisFunsInst, }; @@ -39,16 +36,12 @@ use crate::{ dto::{ flow_external_dto::{FlowExternalCallbackOp, FlowExternalParams}, flow_inst_dto::{ - FLowInstStateApprovalConf, FLowInstStateConf, FLowInstStateFormConf, FlowApprovalResultKind, FlowInstAbortReq, FlowInstArtifacts, FlowInstArtifactsModifyReq, - FlowInstBatchBindReq, FlowInstBatchBindResp, FlowInstCommentInfo, FlowInstCommentReq, FlowInstDetailResp, FlowInstFilterReq, FlowInstFindNextTransitionResp, - FlowInstFindNextTransitionsReq, FlowInstFindStateAndTransitionsReq, FlowInstFindStateAndTransitionsResp, FlowInstOperateReq, FlowInstQueryKind, FlowInstSearchPageReq, - FlowInstSearchReq, FlowInstSearchSortReq, FlowInstStartReq, FlowInstSummaryResp, FlowInstSummaryResult, FlowInstTransferReq, FlowInstTransferResp, - FlowInstTransitionInfo, FlowOperationContext, + FLowInstStateApprovalConf, FLowInstStateConf, FLowInstStateFormConf, FlowApprovalResultKind, FlowInstAbortReq, FlowInstArtifacts, FlowInstArtifactsModifyReq, FlowInstBatchBindReq, FlowInstBatchBindResp, FlowInstCommentInfo, FlowInstCommentReq, FlowInstDetailResp, FlowInstFilterReq, FlowInstFindNextTransitionResp, FlowInstFindNextTransitionsReq, FlowInstFindStateAndTransitionsReq, FlowInstFindStateAndTransitionsResp, FlowInstOperateReq, FlowInstQueryKind, FlowInstSearchPageReq, FlowInstSearchReq, FlowInstSearchResp, FlowInstSearchSortReq, FlowInstStartReq, FlowInstStateKind, FlowInstSummaryResp, FlowInstSummaryResult, FlowInstTransferReq, FlowInstTransferResp, FlowInstTransitionInfo, FlowOperationContext }, - flow_model_dto::{FlowModelDetailResp, FlowModelFilterReq, FlowModelRelTransitionExt}, + flow_model_dto::{FlowModelAggResp, FlowModelRelTransitionExt}, flow_model_version_dto::{FlowModelVersionDetailResp, FlowModelVersionFilterReq}, flow_state_dto::{ - FLowStateKindConf, FlowStateAggResp, FlowStateCountersignKind, FlowStateFilterReq, FlowStateKind, FlowStateOperatorKind, FlowStateRelModelExt, + FLowStateKindConf, FlowStateCountersignKind, FlowStateFilterReq, FlowStateKind, FlowStateOperatorKind, FlowStateRelModelExt, FlowStatusAutoStrategyKind, FlowStatusMultiApprovalKind, FlowSysStateKind, }, flow_transition_dto::FlowTransitionDetailResp, @@ -61,14 +54,8 @@ use crate::{ use super::{ clients::{ - flow_log_client::{FlowLogClient, LogParamContent, LogParamExt, LogParamOp, LogParamTag}, - search_client::FlowSearchClient, - }, - flow_event_serv::FlowEventServ, - flow_external_serv::FlowExternalServ, - flow_model_version_serv::FlowModelVersionServ, - flow_rel_serv::{FlowRelKind, FlowRelServ}, - flow_transition_serv::FlowTransitionServ, + log_client::LogParamOp, search_client::FlowSearchClient + }, flow_event_serv::FlowEventServ, flow_external_serv::FlowExternalServ, flow_log_serv::FlowLogServ, flow_model_version_serv::FlowModelVersionServ, flow_rel_serv::{FlowRelKind, FlowRelServ}, flow_transition_serv::FlowTransitionServ }; pub struct FlowInstServ; @@ -82,7 +69,7 @@ impl FlowInstServ { } } async fn start_main_flow(start_req: &FlowInstStartReq, current_state_name: Option, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult { - if !Self::find_details( + if !Self::find_ids( &FlowInstFilterReq { rel_business_obj_ids: Some(vec![start_req.rel_business_obj_id.clone()]), main: Some(true), @@ -141,7 +128,7 @@ impl FlowInstServ { // get model by own_paths let flow_model = FlowModelServ::get_model_id_by_own_paths_and_transition_id(&start_req.tag, &start_req.transition_id.clone().unwrap_or_default(), funs, ctx).await?; let inst_id = TardisFuns::field.nanoid(); - if !Self::find_details( + if !Self::find_ids( &FlowInstFilterReq { rel_business_obj_ids: Some(vec![start_req.rel_business_obj_id.to_string()]), flow_model_id: Some(flow_model.id.clone()), @@ -188,7 +175,8 @@ impl FlowInstServ { .await?; FlowSearchClient::modify_business_obj_search(&start_req.rel_business_obj_id, &flow_model.tag, funs, ctx).await?; let inst = Self::get(&inst_id, funs, ctx).await?; - Self::add_start_log(start_req, &inst, &create_vars, &flow_model, ctx).await?; + FlowLogServ::add_start_log(start_req, &inst, &create_vars, &flow_model, ctx).await?; + FlowLogServ::add_start_dynamic_log(start_req, &inst, &create_vars, &flow_model, ctx).await?; Self::when_enter_state(&inst, &flow_model.init_state_id, &flow_model.id, funs, ctx).await?; Self::do_request_webhook( @@ -199,6 +187,7 @@ impl FlowInstServ { // 自动流转 Self::auto_transfer(&inst, loop_check_helper::InstancesTransition::default(), funs, ctx).await?; + FlowSearchClient::async_add_or_modify_instance_search(&inst_id, Box::new(false), funs, ctx).await?; Ok(inst_id) } @@ -272,6 +261,7 @@ impl FlowInstServ { (flow_inst::Entity, flow_inst::Column::CurrentStateId), (flow_inst::Entity, flow_inst::Column::CreateCtx), (flow_inst::Entity, flow_inst::Column::CreateTime), + (flow_inst::Entity, flow_inst::Column::UpdateTime), (flow_inst::Entity, flow_inst::Column::FinishCtx), (flow_inst::Entity, flow_inst::Column::FinishTime), (flow_inst::Entity, flow_inst::Column::FinishAbort), @@ -347,10 +337,26 @@ impl FlowInstServ { Ok(()) } - pub async fn find_details(filter: &FlowInstFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult> { + pub async fn find_ids(filter: &FlowInstFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult> { let mut query = Query::select(); Self::package_ext_query(&mut query, filter, funs, ctx).await?; - funs.db().find_dtos::(&query).await + Ok(funs.db().find_dtos::(&query).await?.into_iter().map(|inst| inst.id).collect_vec()) + } + + pub async fn find_detail_items(filter: &FlowInstFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult> { + Self::find_detail(Self::find_ids(filter, funs, ctx).await?, funs, ctx).await + } + + pub async fn paginate_detail_items(filter: &FlowInstFilterReq, page_number: u32, page_size: u32, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult> { + let inst_ids = Self::find_ids(filter, funs, ctx).await?; + let total_size = inst_ids.len() as usize; + let records = Self::find_detail(inst_ids[(((page_number -1) * page_size) as usize).min(total_size)..((page_number * page_size) as usize).min(total_size)].to_vec(), funs, ctx).await?; + Ok(TardisPage { + page_size: page_size as u64, + page_number: page_number as u64, + total_size: total_size as u64, + records + }) } pub async fn get_inst_ids_by_rel_business_obj_id( @@ -399,6 +405,7 @@ impl FlowInstServ { id: Set(flow_inst_id.to_string()), finish_ctx: Set(Some(FlowOperationContext::from_ctx(ctx))), finish_time: Set(Some(Utc::now())), + update_time: Set(Some(Utc::now())), finish_abort: Set(Some(true)), output_message: Set(Some(abort_req.message.to_string())), ..Default::default() @@ -407,10 +414,9 @@ impl FlowInstServ { let flow_inst_detail = Self::get(flow_inst_id, funs, ctx).await?; if !flow_inst_detail.main { - Self::add_finish_log(&flow_inst_detail, funs, ctx).await?; - } - if !flow_inst_detail.main { + FlowLogServ::add_finish_log(&flow_inst_detail, funs, ctx).await?; FlowSearchClient::modify_business_obj_search(&flow_inst_detail.rel_business_obj_id, &flow_inst_detail.tag, funs, ctx).await?; + FlowSearchClient::async_add_or_modify_instance_search(&flow_inst_detail.id, Box::new(true), funs, ctx).await?; } Ok(()) } @@ -431,6 +437,7 @@ impl FlowInstServ { pub code: String, pub tag: String, pub rel_flow_version_id: String, + pub rel_flow_model_id: String, pub rel_flow_model_name: String, pub main: bool, @@ -447,6 +454,7 @@ impl FlowInstServ { pub create_vars: Option, pub create_ctx: FlowOperationContext, pub create_time: DateTime, + pub update_time: Option>, pub finish_ctx: Option, pub finish_time: Option>, @@ -483,6 +491,7 @@ impl FlowInstServ { (flow_inst::Entity, flow_inst::Column::CreateVars), (flow_inst::Entity, flow_inst::Column::CreateCtx), (flow_inst::Entity, flow_inst::Column::CreateTime), + (flow_inst::Entity, flow_inst::Column::UpdateTime), (flow_inst::Entity, flow_inst::Column::FinishCtx), (flow_inst::Entity, flow_inst::Column::FinishTime), (flow_inst::Entity, flow_inst::Column::FinishAbort), @@ -507,6 +516,10 @@ impl FlowInstServ { Alias::new("current_state_kind_conf"), ) .expr_as(Expr::col((rel_state_ext_table.clone(), Alias::new("ext"))).if_null(""), Alias::new("current_state_ext")) + .expr_as( + Expr::col((flow_model_version_table.clone(), Alias::new("rel_model_id"))).if_null(""), + Alias::new("rel_flow_model_id"), + ) .expr_as( Expr::col((rel_model_version_table.clone(), NAME_FIELD.clone())).if_null(""), Alias::new("rel_flow_model_name"), @@ -582,12 +595,14 @@ impl FlowInstServ { id: inst.id, code: inst.code, rel_flow_version_id: inst.rel_flow_version_id, + rel_flow_model_id: inst.rel_flow_model_id, rel_flow_model_name: inst.rel_flow_model_name, tag: inst.tag, main: inst.main, create_vars: inst.create_vars.map(|create_vars| TardisFuns::json.json_to_obj(create_vars).unwrap()), create_ctx: inst.create_ctx, create_time: inst.create_time, + update_time: inst.update_time, finish_ctx: inst.finish_ctx, finish_time: inst.finish_time, finish_abort: inst.finish_abort, @@ -602,7 +617,12 @@ impl FlowInstServ { current_state_color: inst.current_state_color, current_state_sys_kind: inst.current_state_sys_kind, current_state_kind: inst.current_state_kind.clone(), - current_state_ext: inst.current_state_ext.map(|ext| TardisFuns::json.str_to_obj::(&ext).unwrap_or_default()), + current_state_ext: inst.current_state_ext.map(|ext| { + if ext.is_empty() { + return FlowStateRelModelExt::default(); + } + TardisFuns::json.str_to_obj::(&ext).unwrap_or_default() + }), current_state_conf: Self::get_state_conf( &inst.current_state_id, &inst.current_state_kind.unwrap_or_default(), @@ -676,6 +696,7 @@ impl FlowInstServ { rel_flow_model_name: inst.rel_flow_model_name, create_ctx: TardisFuns::json.json_to_obj(inst.create_ctx).unwrap(), create_time: inst.create_time, + update_time: inst.update_time, finish_ctx: inst.finish_ctx.map(|finish_ctx| TardisFuns::json.json_to_obj(finish_ctx).unwrap()), finish_time: inst.finish_time, finish_abort: inst.finish_abort.is_some(), @@ -737,7 +758,7 @@ impl FlowInstServ { ) .await; // 若当前数据项存在未结束的审批流,则清空其中的transitions - let unfinished_approve_flow_inst_ids = Self::find_details( + let unfinished_approve_flow_inst_ids = Self::find_detail_items( &FlowInstFilterReq { rel_business_obj_ids: Some(flow_insts.iter().map(|flow_inst| flow_inst.rel_business_obj_id.clone()).collect_vec()), main: Some(false), @@ -840,20 +861,20 @@ impl FlowInstServ { Self::auto_transfer(flow_inst_detail, modified_instance_transations_cp.clone(), funs, ctx).await?; if flow_inst_detail.main { - let flow_inst_id_cp = flow_inst_detail.id.clone(); + FlowSearchClient::async_add_or_modify_instance_search(&flow_inst_detail.id, Box::new(true), funs, ctx).await?; + let flow_inst_cp = flow_inst_detail.clone(); let flow_transition_id = transfer_req.flow_transition_id.clone(); let ctx_cp = ctx.clone(); tardis::tokio::spawn(async move { let mut funs = flow_constants::get_tardis_inst(); - let flow_inst_cp = Self::get(&flow_inst_id_cp, &funs, &ctx_cp).await.unwrap(); funs.begin().await.unwrap(); match FlowEventServ::do_post_change(&flow_inst_cp, &flow_transition_id, modified_instance_transations_cp.clone(), &ctx_cp, &funs).await { Ok(_) => {} - Err(e) => error!("Flow Instance {} do_post_change error:{:?}", flow_inst_id_cp, e), + Err(e) => error!("Flow Instance {} do_post_change error:{:?}", flow_inst_cp.id, e), } match FlowEventServ::do_front_change(&flow_inst_cp, modified_instance_transations_cp.clone(), &ctx_cp, &funs).await { Ok(_) => {} - Err(e) => error!("Flow Instance {} do_front_change error:{:?}", flow_inst_id_cp, e), + Err(e) => error!("Flow Instance {} do_front_change error:{:?}", flow_inst_cp.id, e), } funs.commit().await.unwrap(); }); @@ -1007,6 +1028,7 @@ impl FlowInstServ { current_state_id: Set(next_flow_state.id.to_string()), current_vars: Set(Some(TardisFuns::json.obj_to_json(&new_vars)?)), transitions: Set(Some(new_transitions.clone())), + update_time: Set(Some(Utc::now())), ..Default::default() }; if next_flow_state.sys_state == FlowSysStateKind::Finish { @@ -1024,7 +1046,7 @@ impl FlowInstServ { let curr_inst = Self::get(&flow_inst_detail.id, funs, ctx).await?; if next_flow_state.sys_state == FlowSysStateKind::Finish && !curr_inst.main { - Self::add_finish_log(&curr_inst, funs, ctx).await?; + FlowLogServ::add_finish_log(&curr_inst, funs, ctx).await?; FlowSearchClient::modify_business_obj_search(&curr_inst.rel_business_obj_id, &curr_inst.tag, funs, ctx).await?; } @@ -1356,6 +1378,7 @@ impl FlowInstServ { let flow_inst = flow_inst::ActiveModel { id: Set(flow_inst_detail.id.clone()), current_vars: Set(Some(TardisFuns::json.obj_to_json(&new_vars)?)), + update_time: Set(Some(Utc::now())), ..Default::default() }; funs.db().update_one(flow_inst, ctx).await.unwrap(); @@ -1375,6 +1398,7 @@ impl FlowInstServ { keys: Some(vec![rel_business_obj_id.into()]), ..Default::default() }, + adv_by_or: None, adv_query: None, sort: None, page: SearchItemSearchPageReq { @@ -1408,11 +1432,9 @@ impl FlowInstServ { } pub async fn batch_update_when_switch_model( + new_model: &FlowModelAggResp, rel_template_id: Option, - tag: &str, - modify_version_id: &str, - modify_model_states: Vec, - state_id: &str, + update_states: Option>, funs: &TardisFunsInst, ctx: &TardisContext, ) -> TardisResult<()> { @@ -1421,7 +1443,13 @@ impl FlowInstServ { own_paths_list = FlowRelServ::find_to_simple_rels(&FlowRelKind::FlowAppTemplate, &rel_template_id, None, None, funs, ctx) .await? .into_iter() - .map(|rel| format!("{}/{}", rel.rel_own_paths, rel.rel_id)) + .map(|rel| { + if FlowModelServ::get_app_id_by_ctx(ctx).is_some() { + rel.rel_own_paths + } else { + format!("{}/{}", rel.rel_own_paths, rel.rel_id) + } + }) .collect_vec(); if own_paths_list.contains(&ctx.own_paths) { own_paths_list = vec![ctx.own_paths.clone()]; @@ -1431,8 +1459,14 @@ impl FlowInstServ { } for own_paths in own_paths_list { let mock_ctx = TardisContext { own_paths, ..ctx.clone() }; - Self::unsafe_modify_state(tag, modify_model_states.clone(), state_id, funs, &mock_ctx).await?; - Self::unsafe_modify_rel_model_id(tag, modify_version_id, funs, &mock_ctx).await?; + if let Some(update_states) = &update_states { + for (old_state, new_state) in update_states { + Self::unsafe_modify_state(&new_model.tag, Some(vec![old_state.clone()]), new_state, funs, &mock_ctx).await?; + } + } else { + Self::unsafe_modify_state(&new_model.tag, None, &new_model.init_state_id, funs, &mock_ctx).await?; + } + Self::unsafe_modify_rel_model_id(&new_model.tag, &new_model.current_version_id, funs, &mock_ctx).await?; } Ok(()) @@ -1451,8 +1485,8 @@ impl FlowInstServ { Ok(()) } - pub async fn unsafe_modify_state(tag: &str, modify_model_states: Vec, state_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { - let insts = Self::find_details( + pub async fn unsafe_modify_state(tag: &str, modify_model_state_ids: Option>, state_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + let insts = Self::find_detail_items( &FlowInstFilterReq { main: Some(true), tag: Some(tag.to_string()), @@ -1463,7 +1497,7 @@ impl FlowInstServ { ) .await? .into_iter() - .filter(|inst| !modify_model_states.iter().any(|state| state.id == inst.current_state_id)) + .filter(|inst| modify_model_state_ids.is_none() || modify_model_state_ids.clone().unwrap_or_default().contains(&inst.current_state_id)) .collect_vec(); join_all( insts @@ -1479,6 +1513,7 @@ impl FlowInstServ { id: Set(inst.id.clone()), current_state_id: Set(state_id.to_string()), transitions: Set(Some(vec![])), + update_time: Set(Some(Utc::now())), ..Default::default() }; funs.db().update_one(flow_inst, &mock_ctx).await.unwrap(); @@ -1623,12 +1658,12 @@ impl FlowInstServ { }) .collect::>(); } - modify_req.guard_conf = Some(guard_custom_conf); + modify_req.curr_operators = Some(FlowSearchClient::search_guard_accounts(&guard_custom_conf, funs, ctx).await?); modify_req.prohibit_guard_conf_account_ids = Some(vec![]); Self::modify_inst_artifacts(&flow_inst_detail.id, &modify_req, funs, ctx).await?; // 当操作人为空时的逻辑 - let curr_guard_conf = Self::get(&flow_inst_detail.id, funs, ctx).await?.artifacts.unwrap_or_default().guard_conf; - if FlowSearchClient::search_guard_account_num(&curr_guard_conf, funs, ctx).await?.unwrap_or_default() == 0 && form_conf.auto_transfer_when_empty_kind.is_some() { + let curr_operators = Self::get(&flow_inst_detail.id, funs, ctx).await?.artifacts.unwrap_or_default().curr_operators.unwrap_or_default(); + if curr_operators.is_empty() && form_conf.auto_transfer_when_empty_kind.is_some() { match form_conf.auto_transfer_when_empty_kind.unwrap_or_default() { FlowStatusAutoStrategyKind::Autoskip => { if let Some(next_transition) = @@ -1651,7 +1686,7 @@ impl FlowInstServ { } } FlowStatusAutoStrategyKind::SpecifyAgent => { - modify_req.guard_conf = Some(form_conf.auto_transfer_when_empty_guard_custom_conf.clone().unwrap_or_default()); + modify_req.curr_operators = Some(FlowSearchClient::search_guard_accounts(&form_conf.auto_transfer_when_empty_guard_custom_conf.clone().unwrap_or_default(), funs, ctx).await?); modify_req.prohibit_guard_conf_account_ids = Some(vec![]); Self::modify_inst_artifacts(&flow_inst_detail.id, &modify_req, funs, ctx).await?; } @@ -1690,18 +1725,15 @@ impl FlowInstServ { }) .collect::>(); } - modify_req.guard_conf = Some(guard_custom_conf.clone()); + let guard_accounts = FlowSearchClient::search_guard_accounts(&guard_custom_conf, funs, ctx).await?; + let curr_approval_total = guard_accounts.len(); + modify_req.curr_approval_total = Some(curr_approval_total); + modify_req.curr_operators = Some(guard_accounts); modify_req.prohibit_guard_conf_account_ids = Some(vec![]); - // 若会签,则需要统计审批人数 - if approval_conf.multi_approval_kind == FlowStatusMultiApprovalKind::Countersign { - let countersign_total = FlowSearchClient::search_guard_account_num(&guard_custom_conf, funs, ctx).await?.unwrap_or_default(); - modify_req.curr_approval_total = Some(countersign_total as usize); - } + Self::modify_inst_artifacts(&flow_inst_detail.id, &modify_req, funs, ctx).await?; - let curr_inst = Self::get(&flow_inst_detail.id, funs, ctx).await?; // 当操作人为空时的逻辑 - let curr_guard_conf = curr_inst.artifacts.unwrap_or_default().guard_conf; - if FlowSearchClient::search_guard_account_num(&curr_guard_conf, funs, ctx).await?.unwrap_or_default() == 0 && approval_conf.auto_transfer_when_empty_kind.is_some() + if curr_approval_total == 0 && approval_conf.auto_transfer_when_empty_kind.is_some() { match approval_conf.auto_transfer_when_empty_kind.unwrap_or_default() { FlowStatusAutoStrategyKind::Autoskip => { @@ -1725,7 +1757,10 @@ impl FlowInstServ { } } FlowStatusAutoStrategyKind::SpecifyAgent => { - modify_req.guard_conf = Some(approval_conf.auto_transfer_when_empty_guard_custom_conf.clone().unwrap_or_default()); + let auto_transfer_when_empty_guard_custom_conf = approval_conf.auto_transfer_when_empty_guard_custom_conf.clone().unwrap_or_default(); + let guard_accounts = FlowSearchClient::search_guard_accounts(&auto_transfer_when_empty_guard_custom_conf, funs, ctx).await?; + modify_req.curr_approval_total = Some(guard_accounts.len()); + modify_req.curr_operators = Some(guard_accounts); modify_req.prohibit_guard_conf_account_ids = Some(vec![]); Self::modify_inst_artifacts(&flow_inst_detail.id, &modify_req, funs, ctx).await?; } @@ -1864,8 +1899,18 @@ impl FlowInstServ { async fn modify_inst_artifacts(inst_id: &str, modify_artifacts: &FlowInstArtifactsModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { let inst = Self::get(inst_id, funs, ctx).await?; let mut inst_artifacts = inst.artifacts.unwrap_or_default(); - if let Some(guard_conf) = &modify_artifacts.guard_conf { - inst_artifacts.guard_conf = guard_conf.clone(); + if let Some(state) = &modify_artifacts.state { + inst_artifacts.state = Some(state.clone()); + } + if let Some(curr_operators) = &modify_artifacts.curr_operators { + inst_artifacts.curr_operators = Some(curr_operators.clone()); + } + if let Some(add_his_operator) = &modify_artifacts.add_his_operator { + let mut his_operators = inst_artifacts.his_operators.clone().unwrap_or_default(); + if !his_operators.contains(add_his_operator) { + his_operators.push(add_his_operator.clone()); + inst_artifacts.his_operators = Some(his_operators); + } } if let Some(prohibit_guard_conf_account_ids) = &modify_artifacts.prohibit_guard_conf_account_ids { inst_artifacts.prohibit_guard_by_spec_account_ids = Some(prohibit_guard_conf_account_ids.clone()); @@ -1902,6 +1947,7 @@ impl FlowInstServ { let flow_inst = flow_inst::ActiveModel { id: Set(inst.id.clone()), artifacts: Set(Some(inst_artifacts)), + update_time: Set(Some(Utc::now())), ..Default::default() }; funs.db().update_one(flow_inst, ctx).await?; @@ -1921,7 +1967,7 @@ impl FlowInstServ { FlowStateKind::Form => kind_conf.form.as_ref().map(|form| { let mut operators = HashMap::new(); let artifacts = artifacts.clone().unwrap_or_default(); - if artifacts.guard_conf.check(ctx) && !artifacts.prohibit_guard_by_spec_account_ids.clone().unwrap_or_default().contains(&ctx.owner) { + if artifacts.curr_operators.clone().unwrap_or_default().contains(&ctx.owner) && !artifacts.prohibit_guard_by_spec_account_ids.clone().unwrap_or_default().contains(&ctx.owner) { operators.insert(FlowStateOperatorKind::Submit, form.submit_btn_name.clone()); if form.referral { if let Some(referral_guard_custom_conf) = &form.referral_guard_custom_conf { @@ -1944,39 +1990,23 @@ impl FlowInstServ { FlowStateKind::Approval => kind_conf.approval.as_ref().map(|approval| { let mut operators = HashMap::new(); let artifacts = artifacts.clone().unwrap_or_default(); - if artifacts.guard_conf.check(ctx) + if artifacts.curr_operators.clone().unwrap_or_default().contains(&ctx.owner) && !artifacts.prohibit_guard_by_spec_account_ids.clone().unwrap_or_default().contains(&ctx.owner) - && !(artifacts - .approval_result - .get(state_id) - .cloned() - .unwrap_or_default() - .get(FlowApprovalResultKind::Pass.to_string().as_str()) - .cloned() - .unwrap_or_default() - .contains(&ctx.owner) - || artifacts - .approval_result - .get(state_id) - .cloned() - .unwrap_or_default() - .get(FlowApprovalResultKind::Overrule.to_string().as_str()) - .cloned() - .unwrap_or_default() - .contains(&ctx.owner)) { operators.insert(FlowStateOperatorKind::Pass, approval.pass_btn_name.clone()); operators.insert(FlowStateOperatorKind::Overrule, approval.overrule_btn_name.clone()); operators.insert(FlowStateOperatorKind::Back, approval.back_btn_name.clone()); - if let Some(referral_guard_custom_conf) = &approval.referral_guard_custom_conf { - if referral_guard_custom_conf.check(ctx) { + if approval.referral { + if let Some(referral_guard_custom_conf) = &approval.referral_guard_custom_conf { + if referral_guard_custom_conf.check(ctx) { + operators.insert(FlowStateOperatorKind::Referral, "".to_string()); + } + } else { operators.insert(FlowStateOperatorKind::Referral, "".to_string()); } - } else { - operators.insert(FlowStateOperatorKind::Referral, "".to_string()); } } - if ctx.own_paths == artifacts.prev_non_auto_account_id.unwrap_or_default() { + if approval.revoke && ctx.owner == artifacts.prev_non_auto_account_id.unwrap_or_default() { operators.insert(FlowStateOperatorKind::Revoke, "".to_string()); } FLowInstStateConf { @@ -2010,7 +2040,19 @@ impl FlowInstServ { ctx, ) .await?; - Self::add_operate_log( + FlowLogServ::add_operate_log( + operate_req, + inst, + if current_state.state_kind == FlowStateKind::Approval { + LogParamOp::Approval + } else { + LogParamOp::Form + }, + funs, + ctx, + ) + .await?; + FlowLogServ::add_operate_dynamic_log( operate_req, inst, if current_state.state_kind == FlowStateKind::Approval { @@ -2022,20 +2064,25 @@ impl FlowInstServ { ctx, ) .await?; - let mut modify_artifacts = FlowInstArtifactsModifyReq::default(); + let mut modify_artifacts = FlowInstArtifactsModifyReq { + add_his_operator: Some(ctx.owner.clone()), + ..Default::default() + }; if let Some(all_vars) = &operate_req.all_vars { modify_artifacts.curr_vars = Some(all_vars.clone()); } + Self::modify_inst_artifacts(&inst.id, &modify_artifacts, funs, ctx).await?; match operate_req.operate { // 转办 FlowStateOperatorKind::Referral => { let mut modify_artifacts = FlowInstArtifactsModifyReq::default(); - let mut guard_conf = inst.artifacts.clone().unwrap_or_default().guard_conf; + let mut curr_operators = inst.artifacts.clone().unwrap_or_default().curr_operators.unwrap_or_default(); let mut prohibit_guard_by_spec_account_ids = inst.artifacts.clone().unwrap_or_default().prohibit_guard_by_spec_account_ids.unwrap_or_default(); if let Some(operator) = operate_req.operator.clone() { - guard_conf.guard_by_spec_account_ids = guard_conf.guard_by_spec_account_ids.into_iter().filter(|account_id| *account_id != ctx.owner.clone()).collect_vec(); - guard_conf.guard_by_spec_account_ids.push(operator.clone()); - modify_artifacts.guard_conf = Some(guard_conf); + curr_operators = curr_operators.into_iter().filter(|account_id| *account_id != ctx.owner.clone()).collect_vec(); + curr_operators.push(operator.clone()); + modify_artifacts.curr_operators = Some(curr_operators); + prohibit_guard_by_spec_account_ids = prohibit_guard_by_spec_account_ids.into_iter().filter(|account_id| *account_id != operator.clone()).collect_vec(); prohibit_guard_by_spec_account_ids.push(ctx.owner.clone()); modify_artifacts.prohibit_guard_conf_account_ids = Some(prohibit_guard_by_spec_account_ids); } @@ -2044,11 +2091,13 @@ impl FlowInstServ { // 撤销 FlowStateOperatorKind::Revoke => { let mut prev_non_auto_state_id = inst.artifacts.clone().unwrap_or_default().prev_non_auto_state_id.unwrap_or_default(); - if let Some(target_state_id) = prev_non_auto_state_id.pop() { + let target_state_id = prev_non_auto_state_id.pop(); + if let Some(target_state_id) = target_state_id { Self::modify_inst_artifacts( &inst.id, &FlowInstArtifactsModifyReq { prev_non_auto_state_id: Some(prev_non_auto_state_id), + state: Some(FlowInstStateKind::Revoke), ..Default::default() }, funs, @@ -2066,6 +2115,7 @@ impl FlowInstServ { &inst.id, &FlowInstArtifactsModifyReq { form_state_map: Some(operate_req.vars.clone().unwrap_or_default()), + state: Some(FlowInstStateKind::Form), ..Default::default() }, funs, @@ -2097,6 +2147,7 @@ impl FlowInstServ { &inst.id, &FlowInstArtifactsModifyReq { prev_non_auto_state_id: Some(prev_non_auto_state_id), + state: Some(FlowInstStateKind::Back), ..Default::default() }, funs, @@ -2110,9 +2161,11 @@ impl FlowInstServ { } // 通过 FlowStateOperatorKind::Pass => { + let curr_operators = inst.artifacts.clone().unwrap_or_default().curr_operators.unwrap_or_default(); Self::modify_inst_artifacts( &inst.id, &FlowInstArtifactsModifyReq { + curr_operators: Some(curr_operators.into_iter().filter(|account_id| *account_id != ctx.owner.clone()).collect_vec()), add_approval_result: Some((ctx.owner.clone(), FlowApprovalResultKind::Pass)), ..Default::default() }, @@ -2120,9 +2173,10 @@ impl FlowInstServ { ctx, ) .await?; - if Self::check_approval_cond(inst, FlowApprovalResultKind::Pass, funs, ctx).await? { + let curr_inst = Self::get(&inst.id, funs, ctx).await?; + if Self::check_approval_cond(&curr_inst, FlowApprovalResultKind::Pass, funs, ctx).await? { if let Some(next_transition) = FlowInstServ::find_next_transitions(inst, &FlowInstFindNextTransitionsReq { vars: None }, funs, ctx).await?.pop() { - Self::add_operate_log( + FlowLogServ::add_operate_log( operate_req, inst, if current_state.state_kind == FlowStateKind::Approval { @@ -2134,6 +2188,16 @@ impl FlowInstServ { ctx, ) .await?; + Self::modify_inst_artifacts( + &inst.id, + &FlowInstArtifactsModifyReq { + state: Some(FlowInstStateKind::Pass), + ..Default::default() + }, + funs, + ctx, + ) + .await?; Self::transfer( inst, &FlowInstTransferReq { @@ -2153,9 +2217,11 @@ impl FlowInstServ { } // 拒绝 FlowStateOperatorKind::Overrule => { + let curr_operators = inst.artifacts.clone().unwrap_or_default().curr_operators.unwrap_or_default(); Self::modify_inst_artifacts( &inst.id, &FlowInstArtifactsModifyReq { + curr_operators: Some(curr_operators.into_iter().filter(|account_id| *account_id != ctx.owner.clone()).collect_vec()), add_approval_result: Some((ctx.owner.clone(), FlowApprovalResultKind::Overrule)), ..Default::default() }, @@ -2163,8 +2229,19 @@ impl FlowInstServ { ctx, ) .await?; - if Self::check_approval_cond(inst, FlowApprovalResultKind::Overrule, funs, ctx).await? { - Self::add_operate_log( + let curr_inst = Self::get(&inst.id, funs, ctx).await?; + if Self::check_approval_cond(&curr_inst, FlowApprovalResultKind::Overrule, funs, ctx).await? { + Self::modify_inst_artifacts( + &inst.id, + &FlowInstArtifactsModifyReq { + state: Some(FlowInstStateKind::Overrule), + ..Default::default() + }, + funs, + ctx, + ) + .await?; + FlowLogServ::add_operate_log( operate_req, inst, if current_state.state_kind == FlowStateKind::Approval { @@ -2180,6 +2257,7 @@ impl FlowInstServ { } } } + FlowSearchClient::async_add_or_modify_instance_search(&inst.id, Box::new(true), funs, ctx).await?; Ok(()) } @@ -2236,8 +2314,8 @@ impl FlowInstServ { if kind == FlowApprovalResultKind::Overrule // 要求全数通过则出现一个拒绝,即拒绝 || ( kind == FlowApprovalResultKind::Pass - && approval_result.get(&FlowApprovalResultKind::Pass.to_string()).cloned().unwrap_or_default().len() + 1 >= approval_total - && approval_result.get(&FlowApprovalResultKind::Overrule.to_string()).cloned().unwrap_or_default().len().is_empty() // 要求全数通过则通过人数达到审核人数同时没有一个拒绝 + && approval_result.get(&FlowApprovalResultKind::Pass.to_string()).cloned().unwrap_or_default().len() >= approval_total + && approval_result.get(&FlowApprovalResultKind::Overrule.to_string()).cloned().unwrap_or_default().is_empty() // 要求全数通过则通过人数达到审核人数同时没有一个拒绝 ) { return Ok(true); @@ -2249,8 +2327,8 @@ impl FlowInstServ { } let pass_total = approval_total * countersign_conf.most_percent.unwrap_or_default() / 100; // 需满足通过的人员数量 let overrule_total = approval_total - pass_total; // 需满足拒绝的人员数量 - if (kind == FlowApprovalResultKind::Pass && approval_result.get(&FlowApprovalResultKind::Pass.to_string()).cloned().unwrap_or_default().len() + 1 >= pass_total) // 要求大多数通过则通过人数达到通过的比例 - || (kind == FlowApprovalResultKind::Overrule && approval_result.get(&FlowApprovalResultKind::Overrule.to_string()).cloned().unwrap_or_default().len() + 1 >= overrule_total) + if (kind == FlowApprovalResultKind::Pass && approval_result.get(&FlowApprovalResultKind::Pass.to_string()).cloned().unwrap_or_default().len() >= pass_total) // 要求大多数通过则通过人数达到通过的比例 + || (kind == FlowApprovalResultKind::Overrule && approval_result.get(&FlowApprovalResultKind::Overrule.to_string()).cloned().unwrap_or_default().len() >= overrule_total) { // 要求大多数通过则拒绝人数达到拒绝的比例 return Ok(true); @@ -2333,12 +2411,12 @@ impl FlowInstServ { id: Set(flow_inst_detail.id.clone()), current_state_id: Set(target_state_id.to_string()), transitions: Set(Some(new_transitions.clone())), + update_time: Set(Some(Utc::now())), ..Default::default() }; funs.db().update_one(flow_inst, ctx).await?; - let from_state_id = flow_inst_detail.current_state_id.clone(); let curr_inst = Self::get(&flow_inst_detail.id, funs, ctx).await?; // 删除目标节点的旧记录 Self::modify_inst_artifacts( @@ -2353,7 +2431,6 @@ impl FlowInstServ { ) .await?; - Self::when_leave_state(&curr_inst, &from_state_id, &flow_model_version.rel_model_id, funs, ctx).await?; Self::when_enter_state(&curr_inst, target_state_id, &flow_model_version.rel_model_id, funs, ctx).await?; Ok(()) @@ -2372,7 +2449,6 @@ impl FlowInstServ { }); let flow_inst = flow_inst::ActiveModel { id: Set(flow_inst_detail.id.clone()), - comments: Set(Some(comments.clone())), ..Default::default() }; @@ -2380,7 +2456,7 @@ impl FlowInstServ { Ok(comment_id) } // search - pub async fn search(search_req: &mut FlowInstSearchReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult> { + pub async fn search(search_req: &mut FlowInstSearchReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult> { let mut where_fragments: Vec = vec!["1=1".to_string()]; let mut sql_vals: Vec = vec![]; let table_alias_name = "flow_inst"; @@ -2415,6 +2491,7 @@ impl FlowInstServ { flow_inst.output_message, flow_inst.own_paths, flow_inst.tag, + flow_inst.artifacts->> 'state' as state, model_version.name as rel_flow_model_name, flow_model_version.rel_model_id as rel_flow_model_id, rbum_rel.ext as rel_transition @@ -2451,12 +2528,13 @@ impl FlowInstServ { if search_req.page.fetch_total && total_size == 0 { total_size = item.try_get("", "total")?; } - Ok(FlowInstSummaryResp { + Ok(FlowInstSearchResp { id: item.try_get("", "id")?, + state: item.try_get("", "state")?, code: item.try_get("", "code")?, rel_flow_version_id: item.try_get("", "rel_flow_version_id")?, - rel_flow_model_id: item.try_get("", "rel_flow_model_id")?, - rel_flow_model_name: item.try_get("", "rel_flow_model_name")?, + rel_flow_model_id: item.try_get::>("", "rel_flow_model_id")?.unwrap_or_default(), + rel_flow_model_name: item.try_get::>("", "rel_flow_model_name")?.unwrap_or_default(), rel_business_obj_id: item.try_get("", "rel_business_obj_id")?, rel_transition: item .try_get::>("", "rel_transition")? @@ -2473,7 +2551,7 @@ impl FlowInstServ { tag: item.try_get("", "tag")?, }) }) - .collect::>>()?; + .collect::>>()?; Ok(TardisPage { page_size: search_req.page.size as u64, @@ -2492,77 +2570,21 @@ impl FlowInstServ { ctx: &TardisContext, ) -> TardisResult<()> { let mut or_where_fragments = vec![]; - let mut and_where_fragments = vec![]; if query_kinds.contains(&FlowInstQueryKind::Create) { sql_vals.push(sea_orm::Value::from(ctx.owner.clone())); or_where_fragments.push(format!("({}.create_ctx ->> 'owner' = ${})", table_alias_name, sql_vals.len())); } if query_kinds.contains(&FlowInstQueryKind::Form) { - let mut child_or_where_fragments = vec![]; sql_vals.push(sea_orm::Value::from(ctx.owner.clone())); - child_or_where_fragments.push(format!( - "EXISTS (SELECT 1 FROM jsonb_array_elements_text({}.artifacts->'guard_conf'->'guard_by_spec_account_ids') AS elem WHERE elem IN (${}))", - table_alias_name, - sql_vals.len() - )); - if !ctx.roles.is_empty() { - sql_vals.push(sea_orm::Value::from(ctx.roles.clone().join(", ").to_string())); - child_or_where_fragments.push(format!( - "EXISTS (SELECT 1 FROM jsonb_array_elements_text({}.artifacts->'guard_conf'->'guard_by_spec_role_ids') AS elem WHERE elem IN (${}))", - table_alias_name, - sql_vals.len() - )); - } - if !ctx.groups.is_empty() { - sql_vals.push(sea_orm::Value::from(ctx.groups.clone().join(", ").to_string())); - child_or_where_fragments.push(format!( - "EXISTS (SELECT 1 FROM jsonb_array_elements_text({}.artifacts->'guard_conf'->'guard_by_spec_org_ids') AS elem WHERE elem IN (${}))", - table_alias_name, - sql_vals.len() - )); - } - or_where_fragments.push(format!("(current_state.state_kind = 'form' AND ({}))", child_or_where_fragments.join(" OR "))); + or_where_fragments.push(format!("(current_state.state_kind = 'form' AND ({}.artifacts -> 'curr_operators' ? ${}))", table_alias_name, sql_vals.len())); } if query_kinds.contains(&FlowInstQueryKind::Approval) { - and_where_fragments.push(format!( - "{table_alias_name}.id not in ( - SELECT - id - FROM - {table_alias_name} - WHERE - {table_alias_name}.artifacts -> 'approval_result' -> {table_alias_name}.current_state_id -> 'PASS' @> '[\"{}\"]' :: jsonb - OR {table_alias_name}.artifacts -> 'approval_result' -> {table_alias_name}.current_state_id -> 'OVERRULE' @> '[\"{}\"]' :: jsonb - )", - ctx.owner, ctx.owner - )); - let mut child_or_where_fragments = vec![]; sql_vals.push(sea_orm::Value::from(ctx.owner.clone())); - child_or_where_fragments.push(format!( - "EXISTS (SELECT 1 FROM jsonb_array_elements_text({}.artifacts->'guard_conf'->'guard_by_spec_account_ids') AS elem WHERE elem IN (${}))", - table_alias_name, - sql_vals.len() - )); - if !ctx.roles.is_empty() { - sql_vals.push(sea_orm::Value::from(ctx.roles.clone().join(", ").to_string())); - child_or_where_fragments.push(format!( - "EXISTS (SELECT 1 FROM jsonb_array_elements_text({}.artifacts->'guard_conf'->'guard_by_spec_role_ids') AS elem WHERE elem IN (${}))", - table_alias_name, - sql_vals.len() - )); - } - if !ctx.groups.is_empty() { - sql_vals.push(sea_orm::Value::from(ctx.groups.clone().join(", ").to_string())); - child_or_where_fragments.push(format!( - "EXISTS (SELECT 1 FROM jsonb_array_elements_text({}.artifacts->'guard_conf'->'guard_by_spec_org_ids') AS elem WHERE elem IN (${}))", - table_alias_name, - sql_vals.len() - )); - } - or_where_fragments.push(format!("(current_state.state_kind = 'approval' AND ({}))", child_or_where_fragments.join(" OR "))); + or_where_fragments.push(format!("(current_state.state_kind = 'approval' AND ({}.artifacts -> 'curr_operators' ? ${}))", table_alias_name, sql_vals.len())); } - if !and_where_fragments.is_empty() { - where_fragments.push(format!("( {} )", and_where_fragments.join(" AND "))); + if query_kinds.contains(&FlowInstQueryKind::History) { + sql_vals.push(sea_orm::Value::from(ctx.owner.clone())); + or_where_fragments.push(format!("({}.artifacts -> 'his_operators' ? ${})", table_alias_name, sql_vals.len())); } if !or_where_fragments.is_empty() { where_fragments.push(format!("( {} )", or_where_fragments.join(" OR "))); @@ -2685,197 +2707,6 @@ impl FlowInstServ { Ok(format!("SP{}{}{}{:0>5}", current_date.year(), current_date.month(), current_date.day(), count + 1).to_string()) } - // 添加审批流发起日志 - async fn add_start_log( - start_req: &FlowInstStartReq, - flow_inst_detail: &FlowInstDetailResp, - create_vars: &HashMap, - flow_model: &FlowModelDetailResp, - ctx: &TardisContext, - ) -> TardisResult<()> { - let rel_transition = flow_model.rel_transition().unwrap_or_default(); - let operand = match rel_transition.id.as_str() { - "__EDIT__" => "编辑审批".to_string(), - "__DELETE__" => "删除审批".to_string(), - _ => format!("{}({})", rel_transition.name, rel_transition.from_flow_state_name).to_string(), - }; - let mut log_ext = LogParamExt { - scene_kind: Some(vec!["approval_flow".to_string()]), - new_log: Some(true), - project_id: rbum_scope_helper::get_path_item(RbumScopeLevelKind::L2.to_int(), &ctx.own_paths), - ..Default::default() - }; - let mut log_content = LogParamContent { - subject: Some(FlowLogClient::get_flow_kind_text(&start_req.tag)), - name: Some(create_vars.get("name").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string())), - sub_id: Some(start_req.rel_business_obj_id.clone()), - sub_kind: Some(FlowLogClient::get_junp_kind(&start_req.tag)), - operand: Some(operand), - operand_name: Some(flow_inst_detail.code.clone()), - operand_id: Some(flow_inst_detail.id.clone()), - operand_kind: Some(FlowLogClient::get_junp_kind("FLOW")), - ..Default::default() - }; - if start_req.create_vars.is_none() { - log_ext.include_detail = Some(false); - log_content.old_content = Some("".to_string()); - log_content.new_content = Some("".to_string()); - } else { - log_content.old_content = Some(create_vars.get("content").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string())); - log_content.new_content = start_req.create_vars.clone().unwrap_or_default().get("content").map(|content| content.as_str().unwrap_or("").to_string()); - log_ext.include_detail = Some(true); - } - FlowLogClient::add_ctx_task( - LogParamTag::ApprovalFlow, - Some(flow_inst_detail.id.clone()), - log_content, - Some(TardisFuns::json.obj_to_json(&log_ext).expect("ext not a valid json value")), - Some("dynamic_log_approval_flow".to_string()), - Some(LogParamOp::Start.into()), - rbum_scope_helper::get_path_item(RbumScopeLevelKind::L1.to_int(), &ctx.own_paths), - ctx, - false, - ) - .await?; - Ok(()) - } - - // 添加审批流操作日志 - async fn add_operate_log( - operate_req: &FlowInstOperateReq, - flow_inst_detail: &FlowInstDetailResp, - op_kind: LogParamOp, - funs: &TardisFunsInst, - ctx: &TardisContext, - ) -> TardisResult<()> { - let current_state = FlowStateServ::get_item( - &flow_inst_detail.current_state_id, - &FlowStateFilterReq { - basic: RbumBasicFilterReq { - with_sub_own_paths: true, - own_paths: Some("".to_string()), - ..Default::default() - }, - ..Default::default() - }, - funs, - ctx, - ) - .await?; - let subject_text = match current_state.state_kind { - FlowStateKind::Approval => "审批节点".to_string(), - FlowStateKind::Form => "录入节点".to_string(), - _ => "".to_string(), - }; - let mut log_ext = LogParamExt { - scene_kind: Some(vec!["approval_flow".to_string()]), - new_log: Some(true), - project_id: rbum_scope_helper::get_path_item(RbumScopeLevelKind::L2.to_int(), &ctx.own_paths), - ..Default::default() - }; - let mut log_content = LogParamContent { - subject: Some(subject_text), - name: Some(current_state.name), - sub_id: Some(flow_inst_detail.rel_business_obj_id.clone()), - sub_kind: Some(FlowLogClient::get_junp_kind(&flow_inst_detail.tag)), - flow_message: operate_req.output_message.clone(), - flow_result: Some(operate_req.operate.to_string().to_uppercase()), - ..Default::default() - }; - if operate_req.vars.is_none() { - log_ext.include_detail = Some(false); - log_content.old_content = Some("".to_string()); - log_content.new_content = Some("".to_string()); - } else { - log_content.old_content = - Some(flow_inst_detail.create_vars.clone().unwrap_or_default().get("content").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string())); - log_content.new_content = operate_req.vars.clone().unwrap_or_default().get("content").map(|content| content.as_str().unwrap_or("").to_string()); - log_content.detail = operate_req.log_text.clone(); - log_ext.include_detail = Some(true); - } - if operate_req.output_message.is_some() { - log_ext.include_detail = Some(true); - } - FlowLogClient::add_ctx_task( - LogParamTag::ApprovalFlow, - Some(flow_inst_detail.id.clone()), - log_content, - Some(TardisFuns::json.obj_to_json(&log_ext).expect("ext not a valid json value")), - Some("dynamic_log_approval_flow".to_string()), - Some(op_kind.into()), - rbum_scope_helper::get_path_item(RbumScopeLevelKind::L1.to_int(), &ctx.own_paths), - ctx, - false, - ) - .await?; - Ok(()) - } - - async fn add_finish_log(flow_inst_detail: &FlowInstDetailResp, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { - let flow_model_version = FlowModelVersionServ::get_item( - &flow_inst_detail.rel_flow_version_id, - &FlowModelVersionFilterReq { - basic: RbumBasicFilterReq { - with_sub_own_paths: true, - own_paths: Some("".to_string()), - ..Default::default() - }, - ..Default::default() - }, - funs, - ctx, - ) - .await?; - let flow_model = FlowModelServ::get_item( - &flow_model_version.rel_model_id, - &FlowModelFilterReq { - basic: RbumBasicFilterReq { - with_sub_own_paths: true, - own_paths: Some("".to_string()), - ..Default::default() - }, - ..Default::default() - }, - funs, - ctx, - ) - .await?; - let rel_transition = flow_model.rel_transition().unwrap_or_default(); - let subject_text = match rel_transition.id.as_str() { - "__EDIT__" => "编辑审批".to_string(), - "__DELETE__" => "删除审批".to_string(), - _ => format!("{}({})", rel_transition.name, rel_transition.from_flow_state_name).to_string(), - }; - let log_ext = LogParamExt { - scene_kind: Some(vec!["approval_flow".to_string()]), - new_log: Some(true), - project_id: rbum_scope_helper::get_path_item(RbumScopeLevelKind::L2.to_int(), &ctx.own_paths), - ..Default::default() - }; - let log_content = LogParamContent { - subject: Some(subject_text), - name: Some(flow_inst_detail.code.clone()), - sub_id: Some(flow_inst_detail.id.clone()), - sub_kind: Some(FlowLogClient::get_junp_kind("FLOW")), - old_content: Some("".to_string()), - new_content: Some("".to_string()), - ..Default::default() - }; - FlowLogClient::add_ctx_task( - LogParamTag::ApprovalFlow, - Some(flow_inst_detail.id.clone()), - log_content, - Some(TardisFuns::json.obj_to_json(&log_ext).expect("ext not a valid json value")), - Some("dynamic_log_approval_flow".to_string()), - Some(LogParamOp::Finish.into()), - rbum_scope_helper::get_path_item(RbumScopeLevelKind::L1.to_int(), &ctx.own_paths), - ctx, - false, - ) - .await?; - Ok(()) - } - // 获取需要更新的参数列表 pub fn get_modify_vars(flow_inst_detail: &FlowInstDetailResp) -> HashMap { let mut vars_collect = HashMap::new(); diff --git a/backend/middlewares/flow/src/serv/flow_log_serv.rs b/backend/middlewares/flow/src/serv/flow_log_serv.rs new file mode 100644 index 000000000..b12a1b3c0 --- /dev/null +++ b/backend/middlewares/flow/src/serv/flow_log_serv.rs @@ -0,0 +1,378 @@ +use std::collections::HashMap; + +use bios_basic::rbum::{dto::rbum_filer_dto::RbumBasicFilterReq, helper::rbum_scope_helper, rbum_enumeration::RbumScopeLevelKind, serv::rbum_item_serv::RbumItemCrudOperation}; +use serde_json::Value; +use tardis::{basic::{dto::TardisContext, result::TardisResult}, TardisFuns, TardisFunsInst}; + +use crate::dto::{flow_inst_dto::{FlowInstDetailResp, FlowInstOperateReq, FlowInstStartReq}, flow_model_dto::{FlowModelDetailResp, FlowModelFilterReq}, flow_model_version_dto::FlowModelVersionFilterReq, flow_state_dto::{FlowStateFilterReq, FlowStateKind, FlowStateOperatorKind}}; + +use super::{clients::{kv_client::FlowKvClient, log_client::{FlowLogClient, LogParamContent, LogParamExt, LogParamExtSceneKind, LogParamOp, LogParamTag}}, flow_model_serv::FlowModelServ, flow_model_version_serv::FlowModelVersionServ, flow_state_serv::FlowStateServ}; + +pub struct FlowLogServ; + +impl FlowLogServ{ + // 添加审批流发起日志 + pub async fn add_start_log( + start_req: &FlowInstStartReq, + flow_inst_detail: &FlowInstDetailResp, + create_vars: &HashMap, + flow_model: &FlowModelDetailResp, + ctx: &TardisContext, + ) -> TardisResult<()> { + let rel_transition = flow_model.rel_transition().unwrap_or_default(); + let operand = match rel_transition.id.as_str() { + "__EDIT__" => "编辑审批".to_string(), + "__DELETE__" => "删除审批".to_string(), + _ => format!("{}({})", rel_transition.name, rel_transition.from_flow_state_name).to_string(), + }; + let mut log_ext = LogParamExt { + scene_kind: Some(vec![String::from(LogParamExtSceneKind::ApprovalFlow)]), + new_log: Some(true), + project_id: rbum_scope_helper::get_path_item(RbumScopeLevelKind::L2.to_int(), &ctx.own_paths), + ..Default::default() + }; + let mut log_content = LogParamContent { + subject: Some(FlowLogClient::get_flow_kind_text(&start_req.tag)), + name: Some(create_vars.get("name").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string())), + sub_id: Some(start_req.rel_business_obj_id.clone()), + sub_kind: Some(FlowLogClient::get_junp_kind(&start_req.tag)), + operand: Some(operand), + operand_name: Some(flow_inst_detail.code.clone()), + operand_id: Some(flow_inst_detail.id.clone()), + operand_kind: Some(FlowLogClient::get_junp_kind("FLOW")), + ..Default::default() + }; + if start_req.create_vars.is_none() { + log_ext.include_detail = Some(false); + log_content.old_content = "".to_string(); + log_content.new_content = "".to_string(); + } else { + log_content.old_content = create_vars.get("content").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string()); + log_content.new_content = start_req.create_vars.clone().unwrap_or_default().get("content").map(|content| content.as_str().unwrap_or("").to_string()).unwrap_or_default(); + log_ext.include_detail = Some(true); + } + FlowLogClient::add_ctx_task( + LogParamTag::ApprovalFlow, + Some(flow_inst_detail.id.clone()), + log_content, + Some(TardisFuns::json.obj_to_json(&log_ext).expect("ext not a valid json value")), + Some("dynamic_log_approval_flow".to_string()), + Some(LogParamOp::Start.into()), + rbum_scope_helper::get_path_item(RbumScopeLevelKind::L1.to_int(), &ctx.own_paths), + true, + ctx, + false, + ) + .await?; + Ok(()) + } + + // 添加审批流发起动态日志 + pub async fn add_start_dynamic_log( + start_req: &FlowInstStartReq, + flow_inst_detail: &FlowInstDetailResp, + create_vars: &HashMap, + flow_model: &FlowModelDetailResp, + ctx: &TardisContext, + ) -> TardisResult<()> { + let rel_transition = flow_model.rel_transition().unwrap_or_default(); + let operand = match rel_transition.id.as_str() { + "__EDIT__" => "编辑审批".to_string(), + "__DELETE__" => "删除审批".to_string(), + _ => format!("{}({})", rel_transition.name, rel_transition.from_flow_state_name).to_string(), + }; + let mut log_ext = LogParamExt { + scene_kind: Some(vec![String::from(LogParamExtSceneKind::Dynamic)]), + new_log: Some(true), + project_id: rbum_scope_helper::get_path_item(RbumScopeLevelKind::L2.to_int(), &ctx.own_paths), + ..Default::default() + }; + let mut log_content = LogParamContent { + subject: Some(FlowLogClient::get_flow_kind_text(&start_req.tag)), + name: Some(create_vars.get("name").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string())), + sub_id: Some(start_req.rel_business_obj_id.clone()), + sub_kind: Some(FlowLogClient::get_junp_kind(&start_req.tag)), + operand: Some(operand), + operand_name: Some(flow_inst_detail.code.clone()), + operand_id: Some(flow_inst_detail.id.clone()), + operand_kind: Some(FlowLogClient::get_junp_kind("FLOW")), + ..Default::default() + }; + if start_req.create_vars.is_none() { + log_ext.include_detail = Some(false); + log_content.old_content = "".to_string(); + log_content.new_content = "".to_string(); + } else { + log_content.old_content = create_vars.get("content").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string()); + log_content.new_content = start_req.create_vars.clone().unwrap_or_default().get("content").map(|content| content.as_str().unwrap_or("").to_string()).unwrap_or_default(); + log_ext.include_detail = Some(true); + } + FlowLogClient::add_ctx_task( + LogParamTag::DynamicLog, + Some(flow_inst_detail.id.clone()), + log_content, + Some(TardisFuns::json.obj_to_json(&log_ext).expect("ext not a valid json value")), + Some("dynamic_log_approval_flow".to_string()), + Some(LogParamOp::Start.into()), + rbum_scope_helper::get_path_item(RbumScopeLevelKind::L1.to_int(), &ctx.own_paths), + false, + ctx, + false, + ) + .await?; + Ok(()) + } + // 添加审批流操作日志 + pub async fn add_operate_log( + operate_req: &FlowInstOperateReq, + flow_inst_detail: &FlowInstDetailResp, + op_kind: LogParamOp, + funs: &TardisFunsInst, + ctx: &TardisContext, + ) -> TardisResult<()> { + let current_state = FlowStateServ::get_item( + &flow_inst_detail.current_state_id, + &FlowStateFilterReq { + basic: RbumBasicFilterReq { + with_sub_own_paths: true, + own_paths: Some("".to_string()), + ..Default::default() + }, + ..Default::default() + }, + funs, + ctx, + ) + .await?; + let subject_text = match current_state.state_kind { + FlowStateKind::Approval => "审批节点".to_string(), + FlowStateKind::Form => "录入节点".to_string(), + _ => "".to_string(), + }; + let mut log_ext = LogParamExt { + scene_kind: Some(vec![String::from(LogParamExtSceneKind::ApprovalFlow)]), + new_log: Some(true), + project_id: rbum_scope_helper::get_path_item(RbumScopeLevelKind::L2.to_int(), &ctx.own_paths), + ..Default::default() + }; + let mut log_content = LogParamContent { + subject: Some(subject_text), + name: Some(current_state.name), + sub_id: Some(flow_inst_detail.rel_business_obj_id.clone()), + sub_kind: Some(FlowLogClient::get_junp_kind(&flow_inst_detail.tag)), + flow_message: operate_req.output_message.clone(), + flow_result: Some(operate_req.operate.to_string().to_uppercase()), + old_content: "".to_string(), + new_content: "".to_string(), + ..Default::default() + }; + if operate_req.operate == FlowStateOperatorKind::Referral { + log_content.flow_referral = Some(FlowKvClient::get_account_name(&operate_req.operator.clone().unwrap_or_default(), funs, ctx).await?); + } + if operate_req.vars.is_none() { + log_ext.include_detail = Some(false); + } else { + log_content.old_content = + flow_inst_detail.create_vars.clone().unwrap_or_default().get("content").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string()); + log_content.new_content = operate_req.vars.clone().unwrap_or_default().get("content").map(|content| content.as_str().unwrap_or("").to_string()).unwrap_or_default(); + log_content.detail = operate_req.log_text.clone(); + log_ext.include_detail = Some(true); + } + if operate_req.output_message.is_some() { + log_ext.include_detail = Some(true); + } + FlowLogClient::add_ctx_task( + LogParamTag::ApprovalFlow, + Some(flow_inst_detail.id.clone()), + log_content, + Some(TardisFuns::json.obj_to_json(&log_ext).expect("ext not a valid json value")), + Some("dynamic_log_approval_flow".to_string()), + Some(op_kind.into()), + rbum_scope_helper::get_path_item(RbumScopeLevelKind::L1.to_int(), &ctx.own_paths), + true, + ctx, + false, + ) + .await?; + Ok(()) + } + + // 添加审批流操作动态日志 + pub async fn add_operate_dynamic_log( + operate_req: &FlowInstOperateReq, + flow_inst_detail: &FlowInstDetailResp, + op_kind: LogParamOp, + funs: &TardisFunsInst, + ctx: &TardisContext, + ) -> TardisResult<()> { + let flow_model_version = FlowModelVersionServ::get_item( + &flow_inst_detail.rel_flow_version_id, + &FlowModelVersionFilterReq { + basic: RbumBasicFilterReq { + with_sub_own_paths: true, + own_paths: Some("".to_string()), + ..Default::default() + }, + ..Default::default() + }, + funs, + ctx, + ) + .await?; + let flow_model = FlowModelServ::get_item( + &flow_model_version.rel_model_id, + &FlowModelFilterReq { + basic: RbumBasicFilterReq { + with_sub_own_paths: true, + own_paths: Some("".to_string()), + ..Default::default() + }, + ..Default::default() + }, + funs, + ctx, + ) + .await?; + let rel_transition = flow_model.rel_transition().unwrap_or_default(); + let subject_text = match rel_transition.id.as_str() { + "__EDIT__" => "编辑审批".to_string(), + "__DELETE__" => "删除审批".to_string(), + _ => format!("{}({})", rel_transition.name, rel_transition.from_flow_state_name).to_string(), + }; + let current_state = FlowStateServ::get_item( + &flow_inst_detail.current_state_id, + &FlowStateFilterReq { + basic: RbumBasicFilterReq { + with_sub_own_paths: true, + own_paths: Some("".to_string()), + ..Default::default() + }, + ..Default::default() + }, + funs, + ctx, + ) + .await?; + let operand = match current_state.state_kind { + FlowStateKind::Approval => "审批节点".to_string(), + FlowStateKind::Form => "录入节点".to_string(), + _ => "".to_string(), + }; + let mut log_ext = LogParamExt { + scene_kind: Some(vec![String::from(LogParamExtSceneKind::Dynamic)]), + new_log: Some(true), + project_id: rbum_scope_helper::get_path_item(RbumScopeLevelKind::L2.to_int(), &ctx.own_paths), + ..Default::default() + }; + let mut log_content = LogParamContent { + subject: Some(subject_text), + name: Some(flow_inst_detail.code.clone()), + sub_id: Some(flow_inst_detail.id.clone()), + sub_kind: Some(FlowLogClient::get_junp_kind("FLOW")), + operand: Some(operand), + operand_name: Some(current_state.name), + operand_id: Some(flow_inst_detail.rel_business_obj_id.clone()), + operand_kind: Some(FlowLogClient::get_junp_kind(&flow_inst_detail.tag)), + flow_message: operate_req.output_message.clone(), + flow_result: Some(operate_req.operate.to_string().to_uppercase()), + old_content: "".to_string(), + new_content: "".to_string(), + ..Default::default() + }; + if operate_req.operate == FlowStateOperatorKind::Referral { + log_content.flow_referral = Some(FlowKvClient::get_account_name(&operate_req.operator.clone().unwrap_or_default(), funs, ctx).await?); + } + if operate_req.vars.is_none() { + log_ext.include_detail = Some(false); + } else { + log_content.old_content = + flow_inst_detail.create_vars.clone().unwrap_or_default().get("content").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string()); + log_content.new_content = operate_req.vars.clone().unwrap_or_default().get("content").map(|content| content.as_str().unwrap_or("").to_string()).unwrap_or_default(); + log_content.detail = operate_req.log_text.clone(); + log_ext.include_detail = Some(true); + } + if operate_req.output_message.is_some() { + log_ext.include_detail = Some(true); + } + FlowLogClient::add_ctx_task( + LogParamTag::DynamicLog, + Some(flow_inst_detail.id.clone()), + log_content, + Some(TardisFuns::json.obj_to_json(&log_ext).expect("ext not a valid json value")), + Some("dynamic_log_approval_flow".to_string()), + Some(op_kind.into()), + rbum_scope_helper::get_path_item(RbumScopeLevelKind::L1.to_int(), &ctx.own_paths), + false, + ctx, + false, + ) + .await?; + Ok(()) + } + + pub async fn add_finish_log(flow_inst_detail: &FlowInstDetailResp, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + let flow_model_version = FlowModelVersionServ::get_item( + &flow_inst_detail.rel_flow_version_id, + &FlowModelVersionFilterReq { + basic: RbumBasicFilterReq { + with_sub_own_paths: true, + own_paths: Some("".to_string()), + ..Default::default() + }, + ..Default::default() + }, + funs, + ctx, + ) + .await?; + let flow_model = FlowModelServ::get_item( + &flow_model_version.rel_model_id, + &FlowModelFilterReq { + basic: RbumBasicFilterReq { + with_sub_own_paths: true, + own_paths: Some("".to_string()), + ..Default::default() + }, + ..Default::default() + }, + funs, + ctx, + ) + .await?; + let rel_transition = flow_model.rel_transition().unwrap_or_default(); + let subject_text = match rel_transition.id.as_str() { + "__EDIT__" => "编辑审批".to_string(), + "__DELETE__" => "删除审批".to_string(), + _ => format!("{}({})", rel_transition.name, rel_transition.from_flow_state_name).to_string(), + }; + let log_ext = LogParamExt { + scene_kind: Some(vec![String::from(LogParamExtSceneKind::ApprovalFlow)]), + new_log: Some(true), + project_id: rbum_scope_helper::get_path_item(RbumScopeLevelKind::L2.to_int(), &ctx.own_paths), + ..Default::default() + }; + let log_content = LogParamContent { + subject: Some(subject_text), + name: Some(flow_inst_detail.code.clone()), + sub_id: Some(flow_inst_detail.id.clone()), + sub_kind: Some(FlowLogClient::get_junp_kind("FLOW")), + old_content: "".to_string(), + new_content: "".to_string(), + ..Default::default() + }; + FlowLogClient::add_ctx_task( + LogParamTag::ApprovalFlow, + Some(flow_inst_detail.id.clone()), + log_content, + Some(TardisFuns::json.obj_to_json(&log_ext).expect("ext not a valid json value")), + Some("dynamic_log_approval_flow".to_string()), + Some(LogParamOp::Finish.into()), + rbum_scope_helper::get_path_item(RbumScopeLevelKind::L1.to_int(), &ctx.own_paths), + true, + ctx, + false, + ) + .await?; + Ok(()) + } +} \ No newline at end of file diff --git a/backend/middlewares/flow/src/serv/flow_model_serv.rs b/backend/middlewares/flow/src/serv/flow_model_serv.rs index 7308f481c..f136e7958 100644 --- a/backend/middlewares/flow/src/serv/flow_model_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_model_serv.rs @@ -28,7 +28,7 @@ use crate::{ dto::{ flow_model_dto::{ FlowModelAddReq, FlowModelAggResp, FlowModelAssociativeOperationKind, FlowModelBindNewStateReq, FlowModelBindStateReq, FlowModelDetailResp, FlowModelFilterReq, - FlowModelFindRelStateResp, FlowModelKind, FlowModelModifyReq, FlowModelRelTransitionExt, FlowModelStatus, FlowModelSummaryResp, FlowModelSyncModifiedFieldReq, + FlowModelFindRelStateResp, FlowModelKind, FlowModelModifyReq, FlowModelRelTransitionExt, FlowModelStatus, FlowModelSummaryResp, FlowModelSyncModifiedFieldReq, FlowModelUnbindStateReq, }, flow_model_version_dto::{ FlowModelVersionAddReq, FlowModelVersionBindState, FlowModelVersionDetailResp, FlowModelVersionFilterReq, FlowModelVersionModifyReq, FlowModelVersionModifyState, @@ -46,13 +46,9 @@ use async_trait::async_trait; use super::{ clients::{ - flow_log_client::{FlowLogClient, LogParamContent, LogParamTag}, + log_client::{FlowLogClient, LogParamContent, LogParamTag}, search_client::FlowSearchClient, - }, - flow_model_version_serv::FlowModelVersionServ, - flow_rel_serv::{FlowRelKind, FlowRelServ}, - flow_state_serv::FlowStateServ, - flow_transition_serv::FlowTransitionServ, + }, flow_inst_serv::FlowInstServ, flow_model_version_serv::FlowModelVersionServ, flow_rel_serv::{FlowRelKind, FlowRelServ}, flow_state_serv::FlowStateServ, flow_transition_serv::FlowTransitionServ }; pub struct FlowModelServ; @@ -273,6 +269,7 @@ impl RbumItemCrudOperation>>()?; + let rel_version_ids = FlowModelVersionServ::find_id_items( + &FlowModelVersionFilterReq { + rel_model_ids: Some(vec![flow_model_id.to_string()]), + ..Default::default() + }, + None, + None, + funs, + ctx, + ) + .await?; + for rel_version_id in rel_version_ids { + FlowModelVersionServ::delete_item(&rel_version_id, funs, ctx).await?; + } Ok(Some(detail)) } @@ -555,6 +567,7 @@ impl RbumItemCrudOperation FlowStateVar::default(), }; let mut kind_conf = state.kind_conf.clone().unwrap_or_default(); - for add_field in req.add_fields.clone() { + for add_field in req.add_fields.clone().unwrap_or_default() { match state.state_kind { FlowStateKind::Form => { kind_conf.form.as_mut().map(|form| form.vars_collect.insert(add_field, add_default_conf.clone())); @@ -1928,13 +1942,13 @@ impl FlowModelServ { _ => {} } } - for delete_field in &req.delete_fields { + for delete_field in req.delete_fields.clone().unwrap_or_default() { match state.state_kind { FlowStateKind::Form => { - kind_conf.form.as_mut().map(|form| form.vars_collect.remove(delete_field)); + kind_conf.form.as_mut().map(|form| form.vars_collect.remove(&delete_field)); } FlowStateKind::Approval => { - kind_conf.approval.as_mut().map(|form| form.vars_collect.remove(delete_field)); + kind_conf.approval.as_mut().map(|form| form.vars_collect.remove(&delete_field)); } _ => {} } @@ -1954,4 +1968,40 @@ impl FlowModelServ { Ok(()) } + + pub async fn unbind_state(flow_model_id: &str, req: &FlowModelUnbindStateReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + let flow_model = Self::get_item(flow_model_id, &FlowModelFilterReq::default(), funs, ctx).await?; + + let mut own_paths_list = vec![]; + if let Some(rel_template_id) = FlowRelServ::find_from_simple_rels(&FlowRelKind::FlowModelTemplate, flow_model_id, None, None, funs, ctx).await?.pop().map(|rel| rel.rel_id) { + own_paths_list = FlowRelServ::find_to_simple_rels(&FlowRelKind::FlowAppTemplate, &rel_template_id, None, None, funs, ctx) + .await? + .into_iter() + .map(|rel| format!("{}/{}", rel.rel_own_paths, rel.rel_id)) + .collect_vec(); + if own_paths_list.contains(&ctx.own_paths) { + own_paths_list = vec![ctx.own_paths.clone()]; + } + } else { + own_paths_list.push(ctx.own_paths.clone()); + } + for own_paths in own_paths_list { + let mock_ctx = TardisContext { own_paths, ..ctx.clone() }; + FlowInstServ::unsafe_modify_state(&flow_model.tag, Some(vec![req.state_id.clone()]), &req.new_state_id, funs, &mock_ctx).await?; + } + Self::modify_model( + flow_model_id, + &mut FlowModelModifyReq { + modify_version: Some(FlowModelVersionModifyReq { + unbind_states: Some(vec![req.state_id.clone()]), + ..Default::default() + }), + ..Default::default() + }, + funs, + ctx, + ) + .await?; + Ok(()) + } } diff --git a/backend/middlewares/flow/src/serv/flow_model_version_serv.rs b/backend/middlewares/flow/src/serv/flow_model_version_serv.rs index 03ae023dc..d91dedbaf 100644 --- a/backend/middlewares/flow/src/serv/flow_model_version_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_model_version_serv.rs @@ -412,7 +412,12 @@ impl FlowModelVersionServ { "409-flow-state-already-used", )); } - + // 获取指向当前节点的动作 + let to_trans = FlowTransitionServ::find_transitions_by_state_id(flow_version_id, Some(vec![state_id.to_string()]), None, funs, ctx).await?; + FlowTransitionServ::delete_transitions(flow_version_id, &to_trans.into_iter().map(|tran| tran.id).collect_vec(), funs, ctx).await?; + // 获取当前节点指向的动作 + let from_trans = FlowTransitionServ::find_transitions_by_state_id(flow_version_id, None, Some(vec![state_id.to_string()]), funs, ctx).await?; + FlowTransitionServ::delete_transitions(flow_version_id, &from_trans.into_iter().map(|tran| tran.id).collect_vec(), funs, ctx).await?; FlowRelServ::delete_simple_rel(&FlowRelKind::FlowModelState, flow_version_id, state_id, funs, ctx).await } diff --git a/backend/middlewares/flow/src/serv/flow_state_serv.rs b/backend/middlewares/flow/src/serv/flow_state_serv.rs index 7a0f0e004..58e548277 100644 --- a/backend/middlewares/flow/src/serv/flow_state_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_state_serv.rs @@ -32,7 +32,7 @@ use crate::{ use async_trait::async_trait; use super::{ - clients::flow_log_client::{FlowLogClient, LogParamContent, LogParamTag}, + clients::log_client::{FlowLogClient, LogParamContent, LogParamTag}, flow_inst_serv::FlowInstServ, flow_model_serv::FlowModelServ, flow_rel_serv::{FlowRelKind, FlowRelServ}, @@ -104,6 +104,7 @@ impl RbumItemCrudOperation TardisResult<()> { @@ -44,7 +44,7 @@ async fn test_flow_api() -> TardisResult<()> { init_spi_kv().await?; init_spi_search().await?; - test_flow_scenes_fsm1::test(&mut flow_client, &mut search_client).await?; + test_flow_scenes_fsm::test(&mut flow_client, &mut search_client).await?; truncate_flow_data().await?; Ok(()) diff --git a/backend/middlewares/flow/tests/test_flow_scenes_fsm.rs b/backend/middlewares/flow/tests/test_flow_scenes_fsm.rs index 98e68bd2a..f91da50cd 100644 --- a/backend/middlewares/flow/tests/test_flow_scenes_fsm.rs +++ b/backend/middlewares/flow/tests/test_flow_scenes_fsm.rs @@ -4,33 +4,35 @@ use bios_basic::rbum::rbum_enumeration::RbumScopeLevelKind; use bios_basic::test::test_http_client::TestHttpClient; use bios_mw_flow::dto::flow_config_dto::FlowConfigModifyReq; -use bios_mw_flow::dto::flow_inst_dto::{ - FlowInstBatchBindReq, FlowInstBatchBindResp, FlowInstBindRelObjReq, FlowInstBindReq, FlowInstDetailResp, FlowInstFindNextTransitionResp, FlowInstFindNextTransitionsReq, - FlowInstFindStateAndTransitionsReq, FlowInstFindStateAndTransitionsResp, FlowInstModifyCurrentVarsReq, FlowInstStartReq, FlowInstTransferReq, FlowInstTransferResp, -}; + +use bios_mw_flow::dto::flow_inst_dto::{FlowInstDetailResp, FlowInstFindStateAndTransitionsReq, FlowInstFindStateAndTransitionsResp, FlowInstStartReq}; use bios_mw_flow::dto::flow_model_dto::{ - FlowModelAddCustomModelItemReq, FlowModelAddCustomModelReq, FlowModelAddCustomModelResp, FlowModelAggResp, FlowModelBindStateReq, FlowModelFindRelStateResp, - FlowModelModifyReq, FlowModelSortStateInfoReq, FlowModelSortStatesReq, FlowModelSummaryResp, FlowModelUnbindStateReq, + FlowModelAddReq, FlowModelAggResp, FlowModelAssociativeOperationKind, FlowModelBindNewStateReq, FlowModelBindStateReq, FlowModelCopyOrReferenceCiReq, + FlowModelCopyOrReferenceReq, FlowModelKind, FlowModelModifyReq, FlowModelStatus, FlowModelSummaryResp, }; -use bios_mw_flow::dto::flow_state_dto::{FlowStateAddReq, FlowStateRelModelExt, FlowStateSummaryResp, FlowSysStateKind}; -use bios_mw_flow::dto::flow_transition_dto::{ - FlowTransitionActionByVarChangeInfoChangedKind, FlowTransitionActionChangeKind, FlowTransitionAddReq, FlowTransitionDoubleCheckInfo, FlowTransitionModifyReq, - FlowTransitionPostActionInfo, FlowTransitionSortStateInfoReq, FlowTransitionSortStatesReq, StateChangeCondition, StateChangeConditionItem, StateChangeConditionOp, +use bios_mw_flow::dto::flow_model_version_dto::{ + FlowModelVersionAddReq, FlowModelVersionBindState, FlowModelVersionDetailResp, FlowModelVersionModifyReq, FlowModelVersionModifyState, FlowModelVesionState, +}; +use bios_mw_flow::dto::flow_state_dto::{ + FlowStateAddReq, FlowStateKind, FlowStateModifyReq, FlowStateRelModelExt, FlowStateRelModelModifyReq, FlowStateSummaryResp, FlowSysStateKind, }; -use bios_mw_flow::dto::flow_var_dto::{FlowVarInfo, RbumDataTypeKind, RbumWidgetTypeKind}; +use bios_mw_flow::dto::flow_transition_dto::{FlowTransitionAddReq, FlowTransitionModifyReq}; use bios_sdk_invoke::clients::spi_kv_client::KvItemSummaryResp; +use bios_spi_search::dto::search_item_dto::{ + SearchItemQueryReq, SearchItemSearchCtxReq, SearchItemSearchPageReq, SearchItemSearchQScopeKind, SearchItemSearchReq, SearchItemSearchResp, +}; +use serde_json::json; use tardis::basic::dto::TardisContext; +use std::time::Duration; use tardis::basic::result::TardisResult; -use tardis::log::info; -use tardis::serde_json::{json, Value}; -use tardis::tokio; -use tardis::web::poem_openapi::types::Type; +use tardis::log::{debug, info}; +use tardis::tokio::time::sleep; use tardis::web::web_resp::{TardisPage, Void}; use tardis::TardisFuns; -pub async fn test(flow_client: &mut TestHttpClient) -> TardisResult<()> { +pub async fn test(flow_client: &mut TestHttpClient, search_client: &mut TestHttpClient) -> TardisResult<()> { info!("【test_flow_scenes_fsm】"); let mut ctx = TardisContext { own_paths: "".to_string(), @@ -42,6 +44,7 @@ pub async fn test(flow_client: &mut TestHttpClient) -> TardisResult<()> { }; flow_client.set_auth(&ctx)?; + search_client.set_auth(&ctx)?; // 1. enter platform // 1-1. check default model @@ -62,548 +65,409 @@ pub async fn test(flow_client: &mut TestHttpClient) -> TardisResult<()> { let _: Void = flow_client.post("/cs/config", &modify_configs).await; let configs: Option> = flow_client.get("/cs/config").await; info!("configs_new: {:?}", configs); - // 1-3. mock import data - ctx.own_paths = "t2/app02".to_string(); - flow_client.set_auth(&ctx)?; - let template_id = "app01".to_string(); - let mut modify_configs = vec![]; - let tags = vec!["REQ", "PROJ", "ITER", "TICKET", "MOCK"]; - for tag in tags { - modify_configs.push(FlowModelAddCustomModelItemReq { tag: tag.to_string() }); - } - let result: Vec = flow_client - .post( - "/cc/model/add_custom_model", - &FlowModelAddCustomModelReq { - proj_template_id: Some(template_id.clone()), - rel_template_id: None, - bind_model_objs: modify_configs, - }, - ) - .await; - assert!(result.into_iter().find(|resp| resp.tag == "MOCK").unwrap().model_id.is_none()); - - ctx.owner = "".to_string(); - flow_client.set_auth(&ctx)?; - - let rel_business_obj_id = TardisFuns::field.nanoid(); - let _: String = flow_client - .post( - "/ci/inst/bind", - &FlowInstBindReq { - tag: "ITER".to_string(), - rel_business_obj_id: rel_business_obj_id.clone(), - create_vars: None, - current_state_name: Some("进行中".to_string()), - }, - ) - .await; - let mut rel_business_objs = vec![]; - for i in 5..8 { - let rel_business_obj_id_i = format!("{}{}", rel_business_obj_id, i); - rel_business_objs.push(FlowInstBindRelObjReq { - rel_business_obj_id: Some(rel_business_obj_id_i), - current_state_name: Some("进行中".to_string()), - own_paths: Some("t2/app02".to_string()), - owner: Some("".to_string()), - }); - } - let _: Vec = flow_client - .post( - "/ci/inst/batch_bind", - &FlowInstBatchBindReq { - tag: "ITER".to_string(), - rel_business_objs, - }, - ) - .await; - let mut rel_business_objs = vec![]; - for i in 0..10 { - let rel_business_obj_id = format!("-c9rgVZOdUH_MbqofO4vc{}", i); - rel_business_objs.push(FlowInstBindRelObjReq { - rel_business_obj_id: Some(rel_business_obj_id), - current_state_name: Some("进行中".to_string()), - own_paths: Some("t2/app02".to_string()), - owner: Some("".to_string()), - }); - } - let _: Vec = flow_client - .post( - "/ci/inst/batch_bind", - &FlowInstBatchBindReq { - tag: "ITER".to_string(), - rel_business_objs, - }, - ) - .await; // 2. enter tenant ctx.owner = "u001".to_string(); ctx.own_paths = "t1".to_string(); flow_client.set_auth(&ctx)?; + search_client.set_auth(&ctx)?; // 2-1. Get states list let req_states: TardisPage = flow_client.get("/cc/state?tag=REQ&enabled=true&page_number=1&page_size=100").await; - let init_state_id = req_states.records[0].id.clone(); - - let mock_template_id = "mock_template_id".to_string(); - // 2-2. create flow models by template_id - let result: HashMap = flow_client - .put( - &format!("/cc/model/find_or_add_models?tag_ids=REQ,TICKET,ITER,PROJ&is_shared=true&temp_id={}", mock_template_id), - &json!(""), - ) - .await; - let req_model_id = result.get("REQ").unwrap().id.clone(); - let req_model_agg: FlowModelAggResp = flow_client.get(&format!("/cc/model/{}", req_model_id)).await; - let ticket_model_id = result.get("TICKET").unwrap().id.clone(); - let ticket_model_agg: FlowModelAggResp = flow_client.get(&format!("/cc/model/{}", ticket_model_id)).await; - let iter_model_id = result.get("ITER").unwrap().id.clone(); - let iter_model_agg: FlowModelAggResp = flow_client.get(&format!("/cc/model/{}", iter_model_id)).await; - let proj_model_id = result.get("PROJ").unwrap().id.clone(); - let proj_model_agg: FlowModelAggResp = flow_client.get(&format!("/cc/model/{}", proj_model_id)).await; - // 2-3. check find rel states - let _: Void = flow_client + let init_state_id = req_states.records[0].id.clone(); // 待开始 + let processing_state_id = req_states.records[1].id.clone(); // 进行中 + let finish_state_id = req_states.records[2].id.clone(); // 已完成 + let closed_state_id = req_states.records[3].id.clone(); // 已关闭 + // 2-2. creat flow template + let req_template_id1 = "template_req_1"; + let req_template_id2 = "template_req_2"; + let project_template_id1 = "template_project_1"; + let project_template_id2 = "template_project_2"; + let req_model_template_aggs: FlowModelAggResp = flow_client .post( - &format!("/cc/model/{}/unbind_state", &proj_model_id), - &FlowModelUnbindStateReq { - state_id: proj_model_agg.states.iter().find(|state| state.name == "已关闭").unwrap().id.clone(), + "/cc/model", + &FlowModelAddReq { + kind: FlowModelKind::AsTemplate, + status: FlowModelStatus::Enabled, + rel_transition_ids: None, + add_version: Some(FlowModelVersionAddReq { + name: "测试需求模板1".into(), + rel_model_id: None, + bind_states: None, + status: FlowModelVesionState::Enabled, + scope_level: Some(RbumScopeLevelKind::Private), + disabled: None, + }), + current_version_id: None, + name: "测试需求模板1".into(), + info: Some("xxx".to_string()), + rel_template_ids: Some(vec![req_template_id1.to_string(), req_template_id2.to_string()]), + template: true, + main: true, + tag: Some("REQ".to_string()), + scope_level: Some(RbumScopeLevelKind::Private), + icon: None, + rel_model_id: None, + disabled: None, }, ) .await; - let result: Vec = flow_client.get(&format!("/cc/model/find_rel_status?tag=PROJ&rel_template_id={}", mock_template_id)).await; - assert!(!result.into_iter().any(|state| state.name == "已关闭")); - // 3.modify model - // 3-1. resort state - let mut sort_states = vec![]; - for (i, state) in req_states.records.iter().enumerate() { - sort_states.push(FlowModelSortStateInfoReq { - state_id: state.id.clone(), - sort: i as i64 + 1, - }); - } - let _: Void = flow_client.post(&format!("/cc/model/{}/resort_state", &req_model_id), &FlowModelSortStatesReq { sort_states }).await; - // 3-2. modify models - let trans_start = req_model_agg.states.iter().find(|state| state.name == "待开始").unwrap().transitions.iter().find(|trans| trans.name == "开始").unwrap(); - 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 req_model_template_id = req_model_template_aggs.id.clone(); let _: Void = flow_client .patch( - &format!("/cc/model/{}", req_model_id), + &format!("/cc/model/{}", req_model_template_id.clone()), &FlowModelModifyReq { - init_state_id: Some(init_state_id.clone()), - modify_transitions: Some(vec![ - FlowTransitionModifyReq { - id: trans_start.id.clone().into(), - name: Some(format!("{}-modify", &trans_start.name).into()), - transfer_by_auto: Some(true), - vars_collect: Some(vec![ - FlowVarInfo { - name: "assigned_to".to_string(), - label: "负责人".to_string(), - data_type: RbumDataTypeKind::STRING, - widget_type: RbumWidgetTypeKind::SELECT, - required: Some(true), - ..Default::default() - }, - FlowVarInfo { - name: "start_end".to_string(), - label: "计划周期".to_string(), - data_type: RbumDataTypeKind::DATETIME, - widget_type: RbumWidgetTypeKind::DATETIME, - ..Default::default() - }, - ]), - action_by_post_changes: Some(vec![FlowTransitionPostActionInfo { - kind: FlowTransitionActionChangeKind::State, - describe: "".to_string(), - obj_tag: Some("TICKET".to_string()), - obj_tag_rel_kind: None, - obj_current_state_id: Some(vec![ticket_model_agg.init_state_id.clone()]), - change_condition: Some(StateChangeCondition { - current: false, - conditions: vec![StateChangeConditionItem { - obj_tag: Some("ITER".to_string()), - obj_tag_rel_kind: None, - state_id: vec![iter_model_agg.init_state_id.clone()], - op: StateChangeConditionOp::And, - }], + modify_version: Some(FlowModelVersionModifyReq { + bind_states: Some(vec![ + FlowModelVersionBindState { + exist_state: Some(FlowModelBindStateReq { + state_id: init_state_id.clone(), + ext: FlowStateRelModelExt { sort: 1, show_btns: None }, }), - changed_state_id: ticket_model_agg.states.iter().find(|state| state.name == "处理中").unwrap().id.clone(), - current: true, - var_name: "".to_string(), - changed_val: None, - changed_kind: None, + add_transitions: Some(vec![ + FlowTransitionAddReq { + from_flow_state_id: init_state_id.clone(), + to_flow_state_id: processing_state_id.clone(), + name: Some("开始".into()), + ..Default::default() + }, + FlowTransitionAddReq { + from_flow_state_id: init_state_id.clone(), + to_flow_state_id: closed_state_id.clone(), + name: Some("关闭".into()), + ..Default::default() + }, + ]), + is_init: true, ..Default::default() - }]), - double_check: Some(FlowTransitionDoubleCheckInfo { - is_open: true, - content: Some("再次确认该操作生效".to_string()), - }), - ..Default::default() - }, - FlowTransitionModifyReq { - id: trans_complate.id.clone().into(), - name: Some(format!("{}-modify", &trans_complate.name).into()), - transfer_by_auto: Some(true), - guard_by_spec_role_ids: Some(vec!["admin".to_string()]), - action_by_post_changes: Some(vec![ - FlowTransitionPostActionInfo { - kind: FlowTransitionActionChangeKind::Var, - describe: "".to_string(), - obj_tag: Some("".to_string()), - obj_tag_rel_kind: None, - obj_current_state_id: None, - change_condition: None, - changed_state_id: "".to_string(), - current: false, - var_name: "id".to_string(), - changed_val: None, - changed_kind: Some(FlowTransitionActionByVarChangeInfoChangedKind::AutoGetOperateTime), - ..Default::default() - }, - FlowTransitionPostActionInfo { - kind: FlowTransitionActionChangeKind::Var, - describe: "".to_string(), - obj_tag: Some("".to_string()), - obj_tag_rel_kind: None, - obj_current_state_id: None, - change_condition: None, - changed_state_id: "".to_string(), - current: false, - var_name: "id1".to_string(), - changed_val: Some(json!("status")), - changed_kind: Some(FlowTransitionActionByVarChangeInfoChangedKind::SelectField), + }, + FlowModelVersionBindState { + exist_state: Some(FlowModelBindStateReq { + state_id: processing_state_id.clone(), + ext: FlowStateRelModelExt { sort: 2, show_btns: None }, + }), + add_transitions: Some(vec![ + FlowTransitionAddReq { + from_flow_state_id: processing_state_id.clone(), + to_flow_state_id: finish_state_id.clone(), + name: Some("完成".into()), + ..Default::default() + }, + FlowTransitionAddReq { + from_flow_state_id: processing_state_id.clone(), + to_flow_state_id: closed_state_id.clone(), + name: Some("关闭".into()), + ..Default::default() + }, + ]), + ..Default::default() + }, + FlowModelVersionBindState { + exist_state: Some(FlowModelBindStateReq { + state_id: finish_state_id.clone(), + ext: FlowStateRelModelExt { sort: 3, show_btns: None }, + }), + add_transitions: Some(vec![ + FlowTransitionAddReq { + from_flow_state_id: finish_state_id.clone(), + to_flow_state_id: processing_state_id.clone(), + name: Some("重新处理".into()), + ..Default::default() + }, + FlowTransitionAddReq { + from_flow_state_id: finish_state_id.clone(), + to_flow_state_id: closed_state_id.clone(), + name: Some("关闭".into()), + ..Default::default() + }, + ]), + ..Default::default() + }, + FlowModelVersionBindState { + exist_state: Some(FlowModelBindStateReq { + state_id: closed_state_id.clone(), + ext: FlowStateRelModelExt { sort: 4, show_btns: None }, + }), + add_transitions: Some(vec![FlowTransitionAddReq { + from_flow_state_id: closed_state_id.clone(), + to_flow_state_id: init_state_id.clone(), + name: Some("激活".into()), ..Default::default() - }, - ]), - ..Default::default() - }, - FlowTransitionModifyReq { - id: trans_close.id.clone().into(), - guard_by_assigned: Some(true), - ..Default::default() - }, - ]), + }]), + ..Default::default() + }, + ]), + init_state_id: Some(init_state_id.to_string()), + ..Default::default() + }), ..Default::default() }, ) .await; - let _: Void = flow_client + let req_default_model_template_aggs: FlowModelAggResp = 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, - }, - ], + "/cc/model", + &FlowModelAddReq { + name: "测试需求默认模板1".into(), + info: Some("xxx".to_string()), + kind: FlowModelKind::AsTemplate, + status: FlowModelStatus::Enabled, + rel_transition_ids: None, + add_version: None, + current_version_id: None, + rel_template_ids: None, + template: true, + main: true, + tag: Some("REQ".to_string()), + scope_level: Some(RbumScopeLevelKind::Private), + icon: None, + rel_model_id: None, + disabled: None, }, ) .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 req_default_model_template_id = req_default_model_template_aggs.id.clone(); let _: Void = flow_client .patch( - &format!("/cc/model/{}", proj_model_id), + &format!("/cc/model/{}", req_default_model_template_id.clone()), &FlowModelModifyReq { - modify_transitions: Some(vec![FlowTransitionModifyReq { - id: proj_trans.id.clone().into(), - action_by_post_changes: Some(vec![FlowTransitionPostActionInfo { - kind: FlowTransitionActionChangeKind::State, - describe: "".to_string(), - obj_tag: Some("TICKET".to_string()), - obj_tag_rel_kind: None, - obj_current_state_id: None, - change_condition: None, - changed_state_id: ticket_model_agg.states.iter().find(|state| state.name == "处理中").unwrap().id.clone(), - current: true, - var_name: "".to_string(), - changed_val: None, - changed_kind: None, - ..Default::default() - }]), - ..Default::default() - }]), - ..Default::default() - }, - ) - .await; - let ticket_model: FlowModelAggResp = flow_client.get(&format!("/cc/model/{}", &ticket_model_id)).await; - let ticket_trans = ticket_model.states.iter().find(|state| state.name == "待处理").unwrap().transitions.iter().find(|trans| trans.name == "立即处理").unwrap(); - let endless_loop_error = flow_client - .patch_resp::( - &format!("/cc/model/{}", ticket_model_id), - &FlowModelModifyReq { - modify_transitions: Some(vec![FlowTransitionModifyReq { - id: ticket_trans.id.clone().into(), - action_by_post_changes: Some(vec![FlowTransitionPostActionInfo { - kind: FlowTransitionActionChangeKind::State, - describe: "".to_string(), - obj_tag: Some("PROJ".to_string()), - obj_tag_rel_kind: None, - obj_current_state_id: None, - change_condition: None, - changed_state_id: proj_model_agg.states.iter().find(|state| state.name == "存在风险").unwrap().id.clone(), - current: true, - var_name: "".to_string(), - changed_val: None, - changed_kind: None, + modify_version: Some(FlowModelVersionModifyReq { + init_state_id: Some(init_state_id.to_string()), + bind_states: Some(vec![FlowModelVersionBindState { + exist_state: Some(FlowModelBindStateReq { + state_id: init_state_id.clone(), + ext: FlowStateRelModelExt { sort: 1, show_btns: None }, + }), + is_init: true, ..Default::default() }]), ..Default::default() - }]), + }), ..Default::default() }, ) .await; - assert_eq!(endless_loop_error.code, "404-flow-flow_model_Serv-after_modify_item"); - - let mut model_agg_new: FlowModelAggResp = flow_client.get(&format!("/cc/model/{}", req_model_id)).await; - assert!(!model_agg_new.states.first_mut().unwrap().transitions.iter_mut().any(|trans| trans.transfer_by_auto).is_empty()); - info!("model_agg_new: {:?}", model_agg_new); - // 3-4. Share template models - let custom_state_id: String = flow_client + let req_model_uninit_template_aggs: FlowModelAggResp = flow_client .post( - "/cc/state", - &FlowStateAddReq { - name: Some("测试".to_string().into()), - sys_state: FlowSysStateKind::Start, - color: Some("rgba(242, 158, 12, 1)".to_string()), - ..Default::default() + "/cc/model", + &FlowModelAddReq { + name: "测试需求未初始化模板1".into(), + info: Some("xxx".to_string()), + rel_template_ids: Some(vec![req_template_id1.to_string(), req_template_id2.to_string()]), + template: true, + main: true, + tag: Some("REQ".to_string()), + scope_level: Some(RbumScopeLevelKind::Private), + icon: None, + rel_model_id: None, + disabled: None, + kind: FlowModelKind::AsTemplate, + status: FlowModelStatus::Enabled, + rel_transition_ids: None, + add_version: None, + current_version_id: None, }, ) .await; - let share_template_id = "share_template_id".to_string(); - let share_template_models: HashMap = flow_client + let req_model_uninit_template_id = req_model_uninit_template_aggs.id.clone(); + sleep(Duration::from_millis(1000)).await; + let model_templates: TardisPage = search_client .put( - &format!("/cc/model/find_or_add_models?tag_ids=REQ&is_shared=true&temp_id={}", share_template_id), - &json!(""), + "/ci/item/search", + &SearchItemSearchReq { + tag: "flow_model".to_string(), + ctx: SearchItemSearchCtxReq { + tenants: Some(vec![ctx.own_paths.clone()]), + ..Default::default() + }, + query: SearchItemQueryReq { ..Default::default() }, + adv_by_or: None, + adv_query: None, + sort: None, + page: SearchItemSearchPageReq { + number: 1, + size: 20, + fetch_total: true, + }, + }, ) .await; - let req_share_model_id = share_template_models.get("REQ").unwrap().id.clone(); - let req_share_model_agg: FlowModelAggResp = flow_client.get(&format!("/cc/model/{}", req_share_model_id)).await; - let _: Void = flow_client + // assert_eq!(model_templates.total_size, 3); + // assert!(model_templates.records.iter().any(|record| record.key == req_default_model_template_id)); + // assert!(model_templates.records.iter().any(|record| record.key == req_model_uninit_template_id)); + // assert!(model_templates.records.iter().any(|record| record.key == req_model_template_id)); + // template bind model + let mut rel_model_ids = HashMap::new(); + rel_model_ids.insert("REQ".to_string(), req_model_template_id.clone()); + let result: HashMap = flow_client .post( - &format!("/cc/model/{}/bind_state", &req_share_model_id), - &FlowModelBindStateReq { - state_id: custom_state_id.clone(), - ext: FlowStateRelModelExt { sort: 1, show_btns: None }, + "/ct/model/copy_or_reference_model", + &FlowModelCopyOrReferenceReq { + rel_model_ids, + rel_template_id: Some(project_template_id1.to_string()), + op: FlowModelAssociativeOperationKind::ReferenceOrCopy, }, ) .await; - let _: Void = flow_client + info!("result: {:?}", result); + let _result: Void = flow_client .patch( - &format!("/cc/model/{}", req_share_model_id), + &format!("/cc/model/{}", req_default_model_template_id), &FlowModelModifyReq { - scope_level: Some(RbumScopeLevelKind::Root), + name: Some("测试需求默认模板11".into()), + tag: Some("REQ".to_string()), + scope_level: Some(RbumScopeLevelKind::Private), ..Default::default() }, ) .await; - - ctx.own_paths = "t2".to_string(); - flow_client.set_auth(&ctx)?; - let other_models: HashMap = flow_client + sleep(Duration::from_millis(500)).await; + let model_templates: TardisPage = search_client .put( - &format!("/cc/model/find_or_add_models?tag_ids=REQ&is_shared=true&temp_id={}", share_template_id), - &json!(""), - ) - .await; - assert_eq!(req_share_model_id, other_models.get("REQ").unwrap().id.clone()); - - ctx.own_paths = "t3/app03".to_string(); - flow_client.set_auth(&ctx)?; - let mut result: Vec = flow_client - .post( - "/cc/model/add_custom_model", - &FlowModelAddCustomModelReq { - proj_template_id: Some(share_template_id.clone()), - rel_template_id: None, - bind_model_objs: vec![FlowModelAddCustomModelItemReq { tag: "REQ".to_string() }], + "/ci/item/search", + &SearchItemSearchReq { + tag: "flow_model".to_string(), + ctx: SearchItemSearchCtxReq { + tenants: Some(vec![ctx.own_paths.clone()]), + ..Default::default() + }, + query: SearchItemQueryReq { ..Default::default() }, + adv_by_or: None, + adv_query: None, + sort: None, + page: SearchItemSearchPageReq { + number: 1, + size: 20, + fetch_total: true, + }, }, ) .await; - let share_model_id = result.pop().unwrap().model_id.unwrap(); + assert_eq!( + model_templates.records.iter().find(|record| record.key == req_default_model_template_id).unwrap().title, + "测试需求默认模板11".to_string() + ); + // creat share model template let _: Void = flow_client .patch( - &format!("/cc/model/{}", share_model_id), + &format!("/cc/model/{}", req_default_model_template_id.clone()), &FlowModelModifyReq { - add_transitions: Some(vec![FlowTransitionAddReq { - from_flow_state_id: req_share_model_agg.init_state_id.clone(), - name: Some("111".to_string().into()), - to_flow_state_id: custom_state_id.clone(), - ..Default::default() - }]), + scope_level: Some(RbumScopeLevelKind::Root), ..Default::default() }, ) .await; - let share_template_models: HashMap = flow_client.put("/cc/model/find_or_add_models?tag_ids=REQ", &json!("")).await; - assert_eq!(share_model_id.as_str(), share_template_models.get("REQ").unwrap().id.as_str()); - - let share_model_agg: FlowModelAggResp = flow_client.get(&format!("/cc/model/{}", share_model_id)).await; - assert_eq!(share_model_agg.scope_level, RbumScopeLevelKind::Private); - let req_share_inst_id: String = flow_client - .post( - "/cc/inst", - &FlowInstStartReq { - tag: "REQ".to_string(), - create_vars: None, - rel_business_obj_id: TardisFuns::field.nanoid(), - }, - ) - .await; - let next_transitions: Vec = flow_client - .put( - &format!("/cc/inst/{}/transition/next", req_share_inst_id.clone()), - &FlowInstFindNextTransitionsReq { vars: None }, - ) - .await; - let transfer: FlowInstTransferResp = flow_client + // checkout tenant + ctx.owner = "u001".to_string(); + ctx.own_paths = "t2".to_string(); + flow_client.set_auth(&ctx)?; + search_client.set_auth(&ctx)?; + sleep(Duration::from_millis(1000)).await; + let model_templates: TardisPage = search_client .put( - &format!("/cc/inst/{}/transition/transfer", req_share_inst_id), - &FlowInstTransferReq { - flow_transition_id: next_transitions.into_iter().find(|trans| trans.next_flow_transition_name == "111").unwrap().next_flow_transition_id.clone(), - vars: Some(TardisFuns::json.json_to_obj(json!({})).unwrap()), - message: None, + "/ci/item/search", + &SearchItemSearchReq { + tag: "flow_model".to_string(), + ctx: SearchItemSearchCtxReq { + tenants: Some(vec![ctx.own_paths.clone(), "".to_string()]), + ..Default::default() + }, + query: SearchItemQueryReq { ..Default::default() }, + adv_by_or: None, + adv_query: None, + sort: None, + page: SearchItemSearchPageReq { + number: 1, + size: 20, + fetch_total: true, + }, }, ) .await; - assert_eq!(transfer.new_flow_state_id, custom_state_id.clone()); - let state_and_next_transitions: Vec = flow_client + assert_eq!(model_templates.total_size, 1); + assert_eq!(model_templates.records[0].key, req_default_model_template_id); + let copy_template_model: FlowModelAggResp = flow_client.patch(&format!("/cc/model/copy/{}", req_default_model_template_id.clone()), &json!({})).await; + sleep(Duration::from_millis(1000)).await; + let model_templates: TardisPage = search_client .put( - "/cc/inst/batch/state_transitions", - &vec![FlowInstFindStateAndTransitionsReq { - flow_inst_id: req_share_inst_id.clone(), - vars: None, - }], - ) - .await; - assert_eq!(state_and_next_transitions.len(), 1); - assert_eq!(state_and_next_transitions[0].current_flow_state_name, "测试"); - let state_bind_error = flow_client - .post_resp::( - &format!("/cc/model/{}/bind_state", &share_model_id), - &FlowModelBindStateReq { - state_id: custom_state_id.clone(), - ext: FlowStateRelModelExt { sort: 1, show_btns: None }, + "/ci/item/search", + &SearchItemSearchReq { + tag: "flow_model".to_string(), + ctx: SearchItemSearchCtxReq { + tenants: Some(vec![ctx.own_paths.clone(), "".to_string()]), + ..Default::default() + }, + query: SearchItemQueryReq { ..Default::default() }, + adv_by_or: None, + adv_query: None, + sort: None, + page: SearchItemSearchPageReq { + number: 1, + size: 20, + fetch_total: true, + }, }, ) .await; - assert_eq!(state_bind_error.code, "409-flow-FlowModelState-add_simple_rel"); - // reset share model + assert_eq!(model_templates.total_size, 2); + assert!(model_templates.records.iter().any(|record| record.key == req_default_model_template_id)); + assert!(model_templates.records.iter().any(|record| record.key == copy_template_model.id)); + // project template bind flow model + ctx.owner = "u001".to_string(); ctx.own_paths = "t1".to_string(); flow_client.set_auth(&ctx)?; - let _: Void = flow_client - .patch( - &format!("/cc/model/{}", req_share_model_id), - &FlowModelModifyReq { - scope_level: Some(RbumScopeLevelKind::L1), - ..Default::default() - }, - ) - .await; - // 4.Start a instance - // mock tenant content + search_client.set_auth(&ctx)?; + // + let req_models: Vec = flow_client.get(&format!("/cc/model/find_by_rel_template_id?tag=REQ&template=true&rel_template_id={}", req_template_id1)).await; + assert_eq!(req_models.len(), 4); + assert!(req_models.iter().any(|model| model.id == req_default_model_template_id)); + assert!(req_models.iter().any(|model| model.id == req_model_template_id)); + assert!(req_models.iter().all(|model| model.id != req_model_uninit_template_id)); + + let req_models: Vec = flow_client.get("/cc/model/find_by_rel_template_id?tag=REQ&template=true").await; + assert_eq!(req_models.len(), 3); + assert!(req_models.iter().any(|model| model.id == req_default_model_template_id)); + assert!(req_models.iter().all(|model| model.id != req_model_template_id)); + ctx.owner = "u001".to_string(); + ctx.own_paths = "t2".to_string(); + flow_client.set_auth(&ctx)?; + search_client.set_auth(&ctx)?; + let req_models: Vec = flow_client.get("/cc/model/find_by_rel_template_id?tag=REQ&template=true").await; + assert_eq!(req_models.len(), 3); + assert!(req_models.iter().any(|model| model.id == req_default_model_template_id)); + assert!(req_models.iter().all(|model| model.id != req_model_template_id)); + // enter app + ctx.owner = "u001".to_string(); ctx.own_paths = "t1/app01".to_string(); flow_client.set_auth(&ctx)?; - // create app flow model - let mut modify_configs = vec![]; - let tags = vec!["REQ", "PROJ", "ITER", "TICKET"]; - for tag in tags { - modify_configs.push(FlowModelAddCustomModelItemReq { tag: tag.to_string() }); - } - let result: Vec = flow_client + search_client.set_auth(&ctx)?; + let result: HashMap = flow_client .post( - "/cc/model/add_custom_model", - &FlowModelAddCustomModelReq { - proj_template_id: Some(mock_template_id.clone()), - rel_template_id: None, - bind_model_objs: modify_configs, + "/ci/model/copy_or_reference_model", + &FlowModelCopyOrReferenceCiReq { + rel_template_id: Some(project_template_id1.to_string()), + op: FlowModelAssociativeOperationKind::Copy, + update_states: None, }, ) .await; - let models: HashMap = flow_client.put("/cc/model/find_or_add_models?tag_ids=REQ,PROJ,ITER,TICKET", &json!("")).await; - let req_model_id_app = &models.get("REQ").unwrap().id; - assert_eq!( - models.get("REQ").unwrap().id, - result.into_iter().find(|model| model.tag == "REQ").unwrap().model_id.unwrap() - ); - - let ticket_inst_rel_id = "mock-ticket-obj-id".to_string(); - let iter_inst_rel_id1 = "mock-iter-obj-id1".to_string(); - let iter_inst_rel_id2 = "mock-iter-obj-id2".to_string(); - let req_inst_rel_id2 = "mock-req-obj-id2".to_string(); + info!("result: {:?}", result); + let models: HashMap = flow_client.put("/cc/model/find_rel_models?tag_ids=REQ,PROJ,ITER,TICKET&is_shared=false", &json!("")).await; + info!("models: {:?}", models); + sleep(Duration::from_millis(1000)).await; + let rel_business_obj_id = TardisFuns::field.nanoid(); let req_inst_id1: String = flow_client .post( "/cc/inst", &FlowInstStartReq { tag: "REQ".to_string(), create_vars: None, - rel_business_obj_id: TardisFuns::field.nanoid(), - }, - ) - .await; - let req_inst_id2: String = flow_client - .post( - "/cc/inst", - &FlowInstStartReq { - tag: "REQ".to_string(), - create_vars: None, - rel_business_obj_id: req_inst_rel_id2.clone(), - }, - ) - .await; - let ticket_inst_id: String = flow_client - .post( - "/cc/inst", - &FlowInstStartReq { - tag: "TICKET".to_string(), - create_vars: None, - rel_business_obj_id: ticket_inst_rel_id.clone(), - }, - ) - .await; - let _iter_inst_id: String = flow_client - .post( - "/cc/inst", - &FlowInstStartReq { - tag: "ITER".to_string(), - create_vars: None, - rel_business_obj_id: iter_inst_rel_id1.clone(), - }, - ) - .await; - let _iter_inst_id: String = flow_client - .post( - "/cc/inst", - &FlowInstStartReq { - tag: "ITER".to_string(), - create_vars: None, - rel_business_obj_id: iter_inst_rel_id2.clone(), + rel_business_obj_id: rel_business_obj_id.clone(), + transition_id: None, }, ) .await; - // Get the state of a task that can be transferable - 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); - ctx.owner = "a001".to_string(); - ctx.roles = vec!["admin:t".to_string()]; - flow_client.set_auth(&ctx)?; - 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_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 + info!("req_inst_id1: {:?}", req_inst_id1); + let req_inst1: FlowInstDetailResp = flow_client.get(&format!("/cc/inst/{}", req_inst_id1)).await; + info!("req_inst1: {:?}", req_inst1); let state_and_next_transitions: Vec = flow_client .put( "/cc/inst/batch/state_transitions", @@ -615,147 +479,125 @@ pub async fn test(flow_client: &mut TestHttpClient) -> TardisResult<()> { .await; assert_eq!(state_and_next_transitions.len(), 1); assert_eq!(state_and_next_transitions[0].current_flow_state_name, "待开始"); - assert!(state_and_next_transitions[0].next_flow_transitions.iter().any(|trans| trans.next_flow_transition_name.contains("开始"))); - assert!(state_and_next_transitions[0].next_flow_transitions.iter().any(|trans| trans.next_flow_transition_name.contains("关闭"))); - // Transfer task status - let transfer: FlowInstTransferResp = flow_client - .put( - &format!("/cc/inst/{}/transition/transfer", req_inst_id1), - &FlowInstTransferReq { - flow_transition_id: state_and_next_transitions[0] - .next_flow_transitions - .iter() - .find(|&trans| trans.next_flow_transition_name.contains("关闭")) - .unwrap() - .next_flow_transition_id - .to_string(), - // vars: Some(TardisFuns::json.json_to_obj(json!({ "reason":"测试关闭" })).unwrap()), - vars: Some(TardisFuns::json.json_to_obj(json!({})).unwrap()), - message: None, - }, - ) - .await; - assert_eq!( - transfer.new_flow_state_id, - state_and_next_transitions[0].next_flow_transitions.iter().find(|&trans| trans.next_flow_transition_name.contains("关闭")).unwrap().next_flow_state_id.clone() - ); - // check state is used - let state_unbind_error = flow_client - .post_resp::( - &format!("/cc/model/{}/unbind_state", &req_model_id_app), - &FlowModelUnbindStateReq { - state_id: transfer.new_flow_state_id.clone(), + + // 新建审批流 + let req_approval_flow: FlowModelAggResp = flow_client + .post( + "/cc/model", + &FlowModelAddReq { + kind: FlowModelKind::AsModel, + status: FlowModelStatus::Enabled, + rel_transition_ids: Some(vec!["__EDIT__".to_string()]), + add_version: None, + current_version_id: None, + name: "编辑需求审批流".into(), + info: Some("xxx".to_string()), + rel_template_ids: None, + template: false, + main: false, + tag: Some("REQ".to_string()), + scope_level: None, + icon: None, + rel_model_id: None, + disabled: None, }, ) .await; - assert_eq!(state_unbind_error.code, "409-flow-flow_model-unbind_state"); - // 6. post action - // check original instance - let ticket: FlowInstDetailResp = flow_client.get(&format!("/cc/inst/{}", ticket_inst_id)).await; - assert_eq!(ticket.current_state_id, ticket_model_agg.init_state_id); - // transfer trigger post action - let state_and_next_transitions: Vec = flow_client - .put( - "/cc/inst/batch/state_transitions", - &vec![FlowInstFindStateAndTransitionsReq { - flow_inst_id: req_inst_id2.clone(), - vars: None, - }], - ) - .await; - let mut vars = HashMap::new(); - vars.insert("assigned_to".to_string(), json!("xxxx_001")); - let _transfer: FlowInstTransferResp = flow_client - .put( - &format!("/cc/inst/{}/transition/transfer", req_inst_id2), - &FlowInstTransferReq { - flow_transition_id: state_and_next_transitions[0] - .next_flow_transitions - .iter() - .find(|trans| trans.next_flow_state_name == "进行中") - .unwrap() - .next_flow_transition_id - .clone(), - vars: Some(vars), - message: None, + let req_approval_flow_version: FlowModelVersionDetailResp = flow_client.get(&format!("/cc/model_version/{}", req_approval_flow.edit_version_id)).await; + let start_state_id = req_approval_flow_version.states()[0].id.clone(); + let form_state_id = TardisFuns::field.nanoid(); + let finish_state_id = req_approval_flow_version.states()[1].id.clone(); + let start_transition_id = req_approval_flow_version.states()[0].transitions[0].id.clone(); + let _: Void = flow_client + .patch( + &format!("/cc/model_version/{}", req_approval_flow.edit_version_id), + &FlowModelVersionModifyReq { + bind_states: Some(vec![FlowModelVersionBindState { + bind_new_state: Some(FlowModelBindNewStateReq { + new_state: FlowStateAddReq { + id: Some(form_state_id.clone().into()), + name: Some("录入".into()), + sys_state: FlowSysStateKind::Progress, + state_kind: Some(FlowStateKind::Form), + tags: Some(vec![req_approval_flow.tag.clone()]), + ..Default::default() + }, + ext: FlowStateRelModelExt { sort: 1, show_btns: None }, + }), + add_transitions: Some(vec![FlowTransitionAddReq { + name: Some("提交".into()), + from_flow_state_id: form_state_id.clone(), + to_flow_state_id: finish_state_id.clone(), + ..Default::default() + }]), + ..Default::default() + }]), + modify_states: Some(vec![ + FlowModelVersionModifyState { + id: Some(start_state_id.clone()), + modify_transitions: Some(vec![FlowTransitionModifyReq { + id: start_transition_id.into(), + to_flow_state_id: Some(form_state_id.clone()), + ..Default::default() + }]), + ..Default::default() + }, + FlowModelVersionModifyState { + id: Some(finish_state_id.clone()), + modify_rel: Some(FlowStateRelModelModifyReq { + id: finish_state_id.clone(), + sort: Some(2), + show_btns: None, + }), + ..Default::default() + }, + ]), + ..Default::default() }, ) .await; - let ticket: FlowInstDetailResp = flow_client.get(&format!("/cc/inst/{}", ticket_inst_id)).await; - assert_eq!(ticket.current_state_id, ticket_model_agg.states.iter().find(|state| state.name == "处理中").unwrap().id); - let state_and_next_transitions: Vec = flow_client - .put( - "/cc/inst/batch/state_transitions", - &vec![FlowInstFindStateAndTransitionsReq { - flow_inst_id: req_inst_id2.clone(), - vars: None, - }], - ) - .await; - assert_eq!(state_and_next_transitions[0].next_flow_transitions.len(), 1); - // handle front change - let current_vars = HashMap::from([ - ("status".to_string(), json!(true)), - ("handle_time".to_string(), json!("2023-10-10")), - ("assigned_to".to_string(), Value::String(ctx.owner.clone())), - ]); + let req_approval_flow_version: FlowModelVersionDetailResp = flow_client.get(&format!("/cc/model_version/{}", req_approval_flow.edit_version_id)).await; + info!( + "req_approval_flow_version: {:?}", + TardisFuns::json.obj_to_json(&req_approval_flow_version).unwrap().to_string() + ); let _: Void = flow_client .patch( - &format!("/cc/inst/{}/modify_current_vars", req_inst_id2), - &FlowInstModifyCurrentVarsReq { vars: current_vars }, + &format!("/cc/model_version/{}", req_approval_flow.edit_version_id), + &FlowModelVersionModifyReq { + status: Some(FlowModelVesionState::Enabled), + ..Default::default() + }, ) .await; + let _versions: TardisPage = flow_client.get(&format!("/cc/model_version?rel_model_id={}&page_number=1&page_size=100", req_approval_flow.id)).await; let state_and_next_transitions: Vec = flow_client .put( "/cc/inst/batch/state_transitions", &vec![FlowInstFindStateAndTransitionsReq { - flow_inst_id: req_inst_id2.clone(), + flow_inst_id: req_inst_id1.clone(), vars: None, }], ) .await; - assert_eq!(state_and_next_transitions[0].next_flow_transitions.len(), 2); - // - let _: Void = flow_client - .patch( - &format!("/cc/model/{}", models.get("REQ").unwrap().id), - &json!({ - "modify_transitions": [ - { - "id": state_and_next_transitions[0].next_flow_transitions.clone().into_iter().find(|trans| trans.next_flow_state_name == *"已完成").unwrap().next_flow_transition_id, - "action_by_front_changes": [ - { - "relevance_relation": "in", - "relevance_label": "包含", - "left_value": "status", - "left_label": "状态", - "right_value": "change_content", - "change_content": [ - true - ], - } - ] - } - ] - }), - ) - .await; - ctx.owner = "".to_string(); - flow_client.set_auth(&ctx)?; - let _: Void = flow_client.get("/ci/inst/trigger_front_action").await; - tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; - let state_and_next_transitions: Vec = flow_client - .put( - "/cc/inst/batch/state_transitions", - &vec![FlowInstFindStateAndTransitionsReq { - flow_inst_id: req_inst_id2.clone(), - vars: None, - }], + info!( + "state_and_next_transitions: {:?}", + TardisFuns::json.obj_to_json(&state_and_next_transitions).unwrap().to_string() + ); + // 启动审批流实例 + let req_inst_id2: String = flow_client + .post( + "/cc/inst", + &FlowInstStartReq { + tag: "REQ".to_string(), + create_vars: None, + rel_business_obj_id: rel_business_obj_id.clone(), + transition_id: Some("__EDIT__".to_string()), + }, ) .await; - assert_eq!(state_and_next_transitions[0].current_flow_state_name, "已完成"); - let flow_inst_list: Vec = flow_client.get(&format!("/ci/inst/find_detail_by_obj_ids?obj_ids={}", req_inst_rel_id2)).await; - assert_eq!(flow_inst_list[0].id, req_inst_id2); - + sleep(Duration::from_millis(5000)).await; + let req_inst2: FlowInstDetailResp = flow_client.get(&format!("/cc/inst/{}", req_inst_id2)).await; + info!("req_inst2: {:?}", req_inst2); Ok(()) } diff --git a/backend/middlewares/flow/tests/test_flow_scenes_fsm1.rs b/backend/middlewares/flow/tests/test_flow_scenes_fsm1.rs deleted file mode 100644 index b0720a4fc..000000000 --- a/backend/middlewares/flow/tests/test_flow_scenes_fsm1.rs +++ /dev/null @@ -1,599 +0,0 @@ -use std::collections::HashMap; - -use bios_basic::rbum::rbum_enumeration::RbumScopeLevelKind; -use bios_basic::test::test_http_client::TestHttpClient; - -use bios_mw_flow::dto::flow_config_dto::FlowConfigModifyReq; - -use bios_mw_flow::dto::flow_inst_dto::{FlowInstDetailResp, FlowInstFindStateAndTransitionsReq, FlowInstFindStateAndTransitionsResp, FlowInstStartReq}; -use bios_mw_flow::dto::flow_model_dto::{ - FlowModelAddReq, FlowModelAggResp, FlowModelAssociativeOperationKind, FlowModelBindNewStateReq, FlowModelBindStateReq, FlowModelCopyOrReferenceCiReq, - FlowModelCopyOrReferenceReq, FlowModelKind, FlowModelModifyReq, FlowModelStatus, FlowModelSummaryResp, -}; -use bios_mw_flow::dto::flow_model_version_dto::{ - FlowModelVersionAddReq, FlowModelVersionBindState, FlowModelVersionDetailResp, FlowModelVersionModifyReq, FlowModelVersionModifyState, FlowModelVesionState, -}; -use bios_mw_flow::dto::flow_state_dto::{ - FlowStateAddReq, FlowStateKind, FlowStateModifyReq, FlowStateRelModelExt, FlowStateRelModelModifyReq, FlowStateSummaryResp, FlowSysStateKind, -}; - -use bios_mw_flow::dto::flow_transition_dto::{FlowTransitionAddReq, FlowTransitionModifyReq}; -use bios_sdk_invoke::clients::spi_kv_client::KvItemSummaryResp; -use bios_spi_search::dto::search_item_dto::{ - SearchItemQueryReq, SearchItemSearchCtxReq, SearchItemSearchPageReq, SearchItemSearchQScopeKind, SearchItemSearchReq, SearchItemSearchResp, -}; -use serde_json::json; -use tardis::basic::dto::TardisContext; - -use std::time::Duration; -use tardis::basic::result::TardisResult; -use tardis::log::{debug, info}; -use tardis::tokio::time::sleep; -use tardis::web::web_resp::{TardisPage, Void}; -use tardis::TardisFuns; - -pub async fn test(flow_client: &mut TestHttpClient, search_client: &mut TestHttpClient) -> TardisResult<()> { - info!("【test_flow_scenes_fsm】"); - let mut ctx = TardisContext { - own_paths: "".to_string(), - ak: "u001".to_string(), - roles: vec![], - groups: vec![], - owner: "u001".to_string(), - ..Default::default() - }; - - flow_client.set_auth(&ctx)?; - search_client.set_auth(&ctx)?; - - // 1. enter platform - // 1-1. check default model - let mut models: TardisPage = flow_client.get("/cc/model?tag=REQ&page_number=1&page_size=100").await; - let init_model = models.records.pop().unwrap(); - info!("models: {:?}", init_model); - assert_eq!(&init_model.name, "待开始-进行中-已完成-已关闭"); - assert_eq!(&init_model.owner, ""); - // 1-2. set config - let mut modify_configs = vec![]; - let codes = vec!["REQ", "PROJ", "ITER", "TICKET"]; - for code in codes { - modify_configs.push(FlowConfigModifyReq { - code: code.to_string(), - value: "https://127.0.0.1:8080/mock/mock/exchange_data".to_string(), - }); - } - let _: Void = flow_client.post("/cs/config", &modify_configs).await; - let configs: Option> = flow_client.get("/cs/config").await; - info!("configs_new: {:?}", configs); - // 2. enter tenant - ctx.owner = "u001".to_string(); - ctx.own_paths = "t1".to_string(); - flow_client.set_auth(&ctx)?; - search_client.set_auth(&ctx)?; - // 2-1. Get states list - let req_states: TardisPage = flow_client.get("/cc/state?tag=REQ&enabled=true&page_number=1&page_size=100").await; - let init_state_id = req_states.records[0].id.clone(); // 待开始 - let processing_state_id = req_states.records[1].id.clone(); // 进行中 - let finish_state_id = req_states.records[2].id.clone(); // 已完成 - let closed_state_id = req_states.records[3].id.clone(); // 已关闭 - // 2-2. creat flow template - let req_template_id1 = "template_req_1"; - let req_template_id2 = "template_req_2"; - let project_template_id1 = "template_project_1"; - let project_template_id2 = "template_project_2"; - let req_model_template_aggs: FlowModelAggResp = flow_client - .post( - "/cc/model", - &FlowModelAddReq { - kind: FlowModelKind::AsTemplate, - status: FlowModelStatus::Enabled, - rel_transition_ids: None, - add_version: Some(FlowModelVersionAddReq { - name: "测试需求模板1".into(), - rel_model_id: None, - bind_states: None, - status: FlowModelVesionState::Enabled, - scope_level: Some(RbumScopeLevelKind::Private), - disabled: None, - }), - current_version_id: None, - name: "测试需求模板1".into(), - info: Some("xxx".to_string()), - rel_template_ids: Some(vec![req_template_id1.to_string(), req_template_id2.to_string()]), - template: true, - main: true, - tag: Some("REQ".to_string()), - scope_level: Some(RbumScopeLevelKind::Private), - icon: None, - rel_model_id: None, - disabled: None, - }, - ) - .await; - let req_model_template_id = req_model_template_aggs.id.clone(); - let _: Void = flow_client - .patch( - &format!("/cc/model/{}", req_model_template_id.clone()), - &FlowModelModifyReq { - modify_version: Some(FlowModelVersionModifyReq { - bind_states: Some(vec![ - FlowModelVersionBindState { - exist_state: Some(FlowModelBindStateReq { - state_id: init_state_id.clone(), - ext: FlowStateRelModelExt { sort: 1, show_btns: None }, - }), - add_transitions: Some(vec![ - FlowTransitionAddReq { - from_flow_state_id: init_state_id.clone(), - to_flow_state_id: processing_state_id.clone(), - name: Some("开始".into()), - ..Default::default() - }, - FlowTransitionAddReq { - from_flow_state_id: init_state_id.clone(), - to_flow_state_id: closed_state_id.clone(), - name: Some("关闭".into()), - ..Default::default() - }, - ]), - is_init: true, - ..Default::default() - }, - FlowModelVersionBindState { - exist_state: Some(FlowModelBindStateReq { - state_id: processing_state_id.clone(), - ext: FlowStateRelModelExt { sort: 2, show_btns: None }, - }), - add_transitions: Some(vec![ - FlowTransitionAddReq { - from_flow_state_id: processing_state_id.clone(), - to_flow_state_id: finish_state_id.clone(), - name: Some("完成".into()), - ..Default::default() - }, - FlowTransitionAddReq { - from_flow_state_id: processing_state_id.clone(), - to_flow_state_id: closed_state_id.clone(), - name: Some("关闭".into()), - ..Default::default() - }, - ]), - ..Default::default() - }, - FlowModelVersionBindState { - exist_state: Some(FlowModelBindStateReq { - state_id: finish_state_id.clone(), - ext: FlowStateRelModelExt { sort: 3, show_btns: None }, - }), - add_transitions: Some(vec![ - FlowTransitionAddReq { - from_flow_state_id: finish_state_id.clone(), - to_flow_state_id: processing_state_id.clone(), - name: Some("重新处理".into()), - ..Default::default() - }, - FlowTransitionAddReq { - from_flow_state_id: finish_state_id.clone(), - to_flow_state_id: closed_state_id.clone(), - name: Some("关闭".into()), - ..Default::default() - }, - ]), - ..Default::default() - }, - FlowModelVersionBindState { - exist_state: Some(FlowModelBindStateReq { - state_id: closed_state_id.clone(), - ext: FlowStateRelModelExt { sort: 4, show_btns: None }, - }), - add_transitions: Some(vec![FlowTransitionAddReq { - from_flow_state_id: closed_state_id.clone(), - to_flow_state_id: init_state_id.clone(), - name: Some("激活".into()), - ..Default::default() - }]), - ..Default::default() - }, - ]), - init_state_id: Some(init_state_id.to_string()), - ..Default::default() - }), - ..Default::default() - }, - ) - .await; - let req_default_model_template_aggs: FlowModelAggResp = flow_client - .post( - "/cc/model", - &FlowModelAddReq { - name: "测试需求默认模板1".into(), - info: Some("xxx".to_string()), - kind: FlowModelKind::AsTemplate, - status: FlowModelStatus::Enabled, - rel_transition_ids: None, - add_version: None, - current_version_id: None, - rel_template_ids: None, - template: true, - main: true, - tag: Some("REQ".to_string()), - scope_level: Some(RbumScopeLevelKind::Private), - icon: None, - rel_model_id: None, - disabled: None, - }, - ) - .await; - let req_default_model_template_id = req_default_model_template_aggs.id.clone(); - let _: Void = flow_client - .patch( - &format!("/cc/model/{}", req_default_model_template_id.clone()), - &FlowModelModifyReq { - modify_version: Some(FlowModelVersionModifyReq { - init_state_id: Some(init_state_id.to_string()), - bind_states: Some(vec![FlowModelVersionBindState { - exist_state: Some(FlowModelBindStateReq { - state_id: init_state_id.clone(), - ext: FlowStateRelModelExt { sort: 1, show_btns: None }, - }), - is_init: true, - ..Default::default() - }]), - ..Default::default() - }), - ..Default::default() - }, - ) - .await; - let req_model_uninit_template_aggs: FlowModelAggResp = flow_client - .post( - "/cc/model", - &FlowModelAddReq { - name: "测试需求未初始化模板1".into(), - info: Some("xxx".to_string()), - rel_template_ids: Some(vec![req_template_id1.to_string(), req_template_id2.to_string()]), - template: true, - main: true, - tag: Some("REQ".to_string()), - scope_level: Some(RbumScopeLevelKind::Private), - icon: None, - rel_model_id: None, - disabled: None, - kind: FlowModelKind::AsTemplate, - status: FlowModelStatus::Enabled, - rel_transition_ids: None, - add_version: None, - current_version_id: None, - }, - ) - .await; - let req_model_uninit_template_id = req_model_uninit_template_aggs.id.clone(); - sleep(Duration::from_millis(1000)).await; - let model_templates: TardisPage = search_client - .put( - "/ci/item/search", - &SearchItemSearchReq { - tag: "flow_model".to_string(), - ctx: SearchItemSearchCtxReq { - tenants: Some(vec![ctx.own_paths.clone()]), - ..Default::default() - }, - query: SearchItemQueryReq { ..Default::default() }, - adv_query: None, - sort: None, - page: SearchItemSearchPageReq { - number: 1, - size: 20, - fetch_total: true, - }, - }, - ) - .await; - // assert_eq!(model_templates.total_size, 3); - // assert!(model_templates.records.iter().any(|record| record.key == req_default_model_template_id)); - // assert!(model_templates.records.iter().any(|record| record.key == req_model_uninit_template_id)); - // assert!(model_templates.records.iter().any(|record| record.key == req_model_template_id)); - // template bind model - let mut rel_model_ids = HashMap::new(); - rel_model_ids.insert("REQ".to_string(), req_model_template_id.clone()); - let result: HashMap = flow_client - .post( - "/ct/model/copy_or_reference_model", - &FlowModelCopyOrReferenceReq { - rel_model_ids, - rel_template_id: Some(project_template_id1.to_string()), - op: FlowModelAssociativeOperationKind::ReferenceOrCopy, - }, - ) - .await; - info!("result: {:?}", result); - let _result: Void = flow_client - .patch( - &format!("/cc/model/{}", req_default_model_template_id), - &FlowModelModifyReq { - name: Some("测试需求默认模板11".into()), - tag: Some("REQ".to_string()), - scope_level: Some(RbumScopeLevelKind::Private), - ..Default::default() - }, - ) - .await; - sleep(Duration::from_millis(500)).await; - let model_templates: TardisPage = search_client - .put( - "/ci/item/search", - &SearchItemSearchReq { - tag: "flow_model".to_string(), - ctx: SearchItemSearchCtxReq { - tenants: Some(vec![ctx.own_paths.clone()]), - ..Default::default() - }, - query: SearchItemQueryReq { ..Default::default() }, - adv_query: None, - sort: None, - page: SearchItemSearchPageReq { - number: 1, - size: 20, - fetch_total: true, - }, - }, - ) - .await; - assert_eq!( - model_templates.records.iter().find(|record| record.key == req_default_model_template_id).unwrap().title, - "测试需求默认模板11".to_string() - ); - // creat share model template - let _: Void = flow_client - .patch( - &format!("/cc/model/{}", req_default_model_template_id.clone()), - &FlowModelModifyReq { - scope_level: Some(RbumScopeLevelKind::Root), - ..Default::default() - }, - ) - .await; - // checkout tenant - ctx.owner = "u001".to_string(); - ctx.own_paths = "t2".to_string(); - flow_client.set_auth(&ctx)?; - search_client.set_auth(&ctx)?; - sleep(Duration::from_millis(1000)).await; - let model_templates: TardisPage = search_client - .put( - "/ci/item/search", - &SearchItemSearchReq { - tag: "flow_model".to_string(), - ctx: SearchItemSearchCtxReq { - tenants: Some(vec![ctx.own_paths.clone(), "".to_string()]), - ..Default::default() - }, - query: SearchItemQueryReq { ..Default::default() }, - adv_query: None, - sort: None, - page: SearchItemSearchPageReq { - number: 1, - size: 20, - fetch_total: true, - }, - }, - ) - .await; - assert_eq!(model_templates.total_size, 1); - assert_eq!(model_templates.records[0].key, req_default_model_template_id); - let copy_template_model: FlowModelAggResp = flow_client.patch(&format!("/cc/model/copy/{}", req_default_model_template_id.clone()), &json!({})).await; - sleep(Duration::from_millis(1000)).await; - let model_templates: TardisPage = search_client - .put( - "/ci/item/search", - &SearchItemSearchReq { - tag: "flow_model".to_string(), - ctx: SearchItemSearchCtxReq { - tenants: Some(vec![ctx.own_paths.clone(), "".to_string()]), - ..Default::default() - }, - query: SearchItemQueryReq { ..Default::default() }, - adv_query: None, - sort: None, - page: SearchItemSearchPageReq { - number: 1, - size: 20, - fetch_total: true, - }, - }, - ) - .await; - assert_eq!(model_templates.total_size, 2); - assert!(model_templates.records.iter().any(|record| record.key == req_default_model_template_id)); - assert!(model_templates.records.iter().any(|record| record.key == copy_template_model.id)); - // project template bind flow model - ctx.owner = "u001".to_string(); - ctx.own_paths = "t1".to_string(); - flow_client.set_auth(&ctx)?; - search_client.set_auth(&ctx)?; - // - let req_models: Vec = flow_client.get(&format!("/cc/model/find_by_rel_template_id?tag=REQ&template=true&rel_template_id={}", req_template_id1)).await; - assert_eq!(req_models.len(), 4); - assert!(req_models.iter().any(|model| model.id == req_default_model_template_id)); - assert!(req_models.iter().any(|model| model.id == req_model_template_id)); - assert!(req_models.iter().all(|model| model.id != req_model_uninit_template_id)); - - let req_models: Vec = flow_client.get("/cc/model/find_by_rel_template_id?tag=REQ&template=true").await; - assert_eq!(req_models.len(), 3); - assert!(req_models.iter().any(|model| model.id == req_default_model_template_id)); - assert!(req_models.iter().all(|model| model.id != req_model_template_id)); - ctx.owner = "u001".to_string(); - ctx.own_paths = "t2".to_string(); - flow_client.set_auth(&ctx)?; - search_client.set_auth(&ctx)?; - let req_models: Vec = flow_client.get("/cc/model/find_by_rel_template_id?tag=REQ&template=true").await; - assert_eq!(req_models.len(), 3); - assert!(req_models.iter().any(|model| model.id == req_default_model_template_id)); - assert!(req_models.iter().all(|model| model.id != req_model_template_id)); - // enter app - ctx.owner = "u001".to_string(); - ctx.own_paths = "t1/app01".to_string(); - flow_client.set_auth(&ctx)?; - search_client.set_auth(&ctx)?; - let result: HashMap = flow_client - .post( - "/ci/model/copy_or_reference_model", - &FlowModelCopyOrReferenceCiReq { - rel_template_id: Some(project_template_id1.to_string()), - op: FlowModelAssociativeOperationKind::Copy, - update_states: None, - }, - ) - .await; - info!("result: {:?}", result); - let models: HashMap = flow_client.put("/cc/model/find_rel_models?tag_ids=REQ,PROJ,ITER,TICKET&is_shared=false", &json!("")).await; - info!("models: {:?}", models); - sleep(Duration::from_millis(1000)).await; - let rel_business_obj_id = TardisFuns::field.nanoid(); - let req_inst_id1: String = flow_client - .post( - "/cc/inst", - &FlowInstStartReq { - tag: "REQ".to_string(), - create_vars: None, - rel_business_obj_id: rel_business_obj_id.clone(), - transition_id: None, - }, - ) - .await; - info!("req_inst_id1: {:?}", req_inst_id1); - let req_inst1: FlowInstDetailResp = flow_client.get(&format!("/cc/inst/{}", req_inst_id1)).await; - info!("req_inst1: {:?}", req_inst1); - let state_and_next_transitions: Vec = flow_client - .put( - "/cc/inst/batch/state_transitions", - &vec![FlowInstFindStateAndTransitionsReq { - flow_inst_id: req_inst_id1.clone(), - vars: None, - }], - ) - .await; - assert_eq!(state_and_next_transitions.len(), 1); - assert_eq!(state_and_next_transitions[0].current_flow_state_name, "待开始"); - - // 新建审批流 - let req_approval_flow: FlowModelAggResp = flow_client - .post( - "/cc/model", - &FlowModelAddReq { - kind: FlowModelKind::AsModel, - status: FlowModelStatus::Enabled, - rel_transition_ids: Some(vec!["__EDIT__".to_string()]), - add_version: None, - current_version_id: None, - name: "编辑需求审批流".into(), - info: Some("xxx".to_string()), - rel_template_ids: None, - template: false, - main: false, - tag: Some("REQ".to_string()), - scope_level: None, - icon: None, - rel_model_id: None, - disabled: None, - }, - ) - .await; - let req_approval_flow_version: FlowModelVersionDetailResp = flow_client.get(&format!("/cc/model_version/{}", req_approval_flow.edit_version_id)).await; - let start_state_id = req_approval_flow_version.states()[0].id.clone(); - let form_state_id = TardisFuns::field.nanoid(); - let finish_state_id = req_approval_flow_version.states()[1].id.clone(); - let start_transition_id = req_approval_flow_version.states()[0].transitions[0].id.clone(); - let _: Void = flow_client - .patch( - &format!("/cc/model_version/{}", req_approval_flow.edit_version_id), - &FlowModelVersionModifyReq { - bind_states: Some(vec![FlowModelVersionBindState { - bind_new_state: Some(FlowModelBindNewStateReq { - new_state: FlowStateAddReq { - id: Some(form_state_id.clone().into()), - name: Some("录入".into()), - sys_state: FlowSysStateKind::Progress, - state_kind: Some(FlowStateKind::Form), - tags: Some(vec![req_approval_flow.tag.clone()]), - ..Default::default() - }, - ext: FlowStateRelModelExt { sort: 1, show_btns: None }, - }), - add_transitions: Some(vec![FlowTransitionAddReq { - name: Some("提交".into()), - from_flow_state_id: form_state_id.clone(), - to_flow_state_id: finish_state_id.clone(), - ..Default::default() - }]), - ..Default::default() - }]), - modify_states: Some(vec![ - FlowModelVersionModifyState { - id: Some(start_state_id.clone()), - modify_transitions: Some(vec![FlowTransitionModifyReq { - id: start_transition_id.into(), - to_flow_state_id: Some(form_state_id.clone()), - ..Default::default() - }]), - ..Default::default() - }, - FlowModelVersionModifyState { - id: Some(finish_state_id.clone()), - modify_rel: Some(FlowStateRelModelModifyReq { - id: finish_state_id.clone(), - sort: Some(2), - show_btns: None, - }), - ..Default::default() - }, - ]), - ..Default::default() - }, - ) - .await; - let req_approval_flow_version: FlowModelVersionDetailResp = flow_client.get(&format!("/cc/model_version/{}", req_approval_flow.edit_version_id)).await; - info!( - "req_approval_flow_version: {:?}", - TardisFuns::json.obj_to_json(&req_approval_flow_version).unwrap().to_string() - ); - let _: Void = flow_client - .patch( - &format!("/cc/model_version/{}", req_approval_flow.edit_version_id), - &FlowModelVersionModifyReq { - status: Some(FlowModelVesionState::Enabled), - ..Default::default() - }, - ) - .await; - let _versions: TardisPage = flow_client.get(&format!("/cc/model_version?rel_model_id={}&page_number=1&page_size=100", req_approval_flow.id)).await; - let state_and_next_transitions: Vec = flow_client - .put( - "/cc/inst/batch/state_transitions", - &vec![FlowInstFindStateAndTransitionsReq { - flow_inst_id: req_inst_id1.clone(), - vars: None, - }], - ) - .await; - info!( - "state_and_next_transitions: {:?}", - TardisFuns::json.obj_to_json(&state_and_next_transitions).unwrap().to_string() - ); - // 启动审批流实例 - let req_inst_id2: String = flow_client - .post( - "/cc/inst", - &FlowInstStartReq { - tag: "REQ".to_string(), - create_vars: None, - rel_business_obj_id: rel_business_obj_id.clone(), - transition_id: Some("__EDIT__".to_string()), - }, - ) - .await; - sleep(Duration::from_millis(5000)).await; - let req_inst2: FlowInstDetailResp = flow_client.get(&format!("/cc/inst/{}", req_inst_id2)).await; - info!("req_inst2: {:?}", req_inst2); - Ok(()) -} diff --git a/backend/middlewares/flow/tests/test_flow_scenes_fsm2.rs b/backend/middlewares/flow/tests/test_flow_scenes_fsm2.rs deleted file mode 100644 index f4bb14d20..000000000 --- a/backend/middlewares/flow/tests/test_flow_scenes_fsm2.rs +++ /dev/null @@ -1,349 +0,0 @@ -use std::collections::HashMap; - -use bios_basic::rbum::rbum_enumeration::RbumScopeLevelKind; -use bios_basic::test::test_http_client::TestHttpClient; - -use bios_mw_flow::dto::flow_config_dto::FlowConfigModifyReq; - -use bios_mw_flow::dto::flow_inst_dto::{FlowInstDetailResp, FlowInstStartReq}; -use bios_mw_flow::dto::flow_model_dto::{ - FlowModelAddReq, FlowModelAggResp, FlowModelAssociativeOperationKind, FlowModelBindStateReq, FlowModelCopyOrReferenceCiReq, FlowModelCopyOrReferenceReq, FlowModelModifyReq, - FlowModelSummaryResp, -}; -use bios_mw_flow::dto::flow_state_dto::{FlowStateRelModelExt, FlowStateSummaryResp}; - -use bios_mw_flow::dto::flow_transition_dto::{FlowTransitionAddReq, FlowTransitionModifyReq}; -use bios_sdk_invoke::clients::spi_kv_client::KvItemSummaryResp; -use bios_spi_search::dto::search_item_dto::{ - SearchItemQueryReq, SearchItemSearchCtxReq, SearchItemSearchPageReq, SearchItemSearchQScopeKind, SearchItemSearchReq, SearchItemSearchResp, -}; -use serde_json::json; -use tardis::basic::dto::TardisContext; - -use std::time::Duration; -use tardis::basic::result::TardisResult; -use tardis::log::{debug, info}; -use tardis::tokio::time::sleep; -use tardis::web::web_resp::{TardisPage, Void}; -use tardis::TardisFuns; - -pub async fn test(flow_client: &mut TestHttpClient, search_client: &mut TestHttpClient) -> TardisResult<()> { - info!("【test_flow_scenes_fsm】"); - let mut ctx = TardisContext { - own_paths: "".to_string(), - ak: "u001".to_string(), - roles: vec![], - groups: vec![], - owner: "u001".to_string(), - ..Default::default() - }; - - flow_client.set_auth(&ctx)?; - search_client.set_auth(&ctx)?; - - // 1. enter platform - // 1-1. check default model - let mut models: TardisPage = flow_client.get("/cc/model?tag=REQ&page_number=1&page_size=100").await; - let init_model = models.records.pop().unwrap(); - info!("models: {:?}", init_model); - assert_eq!(&init_model.name, "待开始-进行中-已完成-已关闭"); - assert_eq!(&init_model.owner, ""); - // 1-2. set config - let mut modify_configs = vec![]; - let codes = vec!["REQ", "PROJ", "ITER", "TICKET"]; - for code in codes { - modify_configs.push(FlowConfigModifyReq { - code: code.to_string(), - value: "https://127.0.0.1:8080/mock/mock/exchange_data".to_string(), - }); - } - let _: Void = flow_client.post("/cs/config", &modify_configs).await; - let configs: Option> = flow_client.get("/cs/config").await; - info!("configs_new: {:?}", configs); - // 2. enter tenant - ctx.owner = "u001".to_string(); - ctx.own_paths = "t1".to_string(); - flow_client.set_auth(&ctx)?; - search_client.set_auth(&ctx)?; - // 2-1. Get states list - let req_states: TardisPage = flow_client.get("/cc/state?tag=REQ&enabled=true&page_number=1&page_size=100").await; - let init_state_id = req_states.records[0].id.clone(); // 待开始 - let processing_state_id = req_states.records[1].id.clone(); // 进行中 - let finish_state_id = req_states.records[2].id.clone(); // 已完成 - let closed_state_id = req_states.records[3].id.clone(); // 已关闭 - // 2-2. creat flow template - let req_template_id1 = "template_req_1"; - let req_template_id2 = "template_req_2"; - let project_template_id1 = "template_project_1"; - let project_template_id2 = "template_project_2"; - let req_model_template_aggs: FlowModelAggResp = flow_client - .post( - "/cc/model", - &FlowModelAddReq { - name: "测试需求模板1".into(), - info: Some("xxx".to_string()), - init_state_id: "".to_string(), - rel_template_ids: Some(vec![req_template_id1.to_string(), req_template_id2.to_string()]), - template: true, - tag: Some("REQ".to_string()), - scope_level: Some(RbumScopeLevelKind::Private), - icon: None, - transitions: None, - states: None, - rel_model_id: None, - disabled: None, - }, - ) - .await; - sleep(Duration::from_millis(500)).await; - let req_model_template_id = req_model_template_aggs.id.clone(); - let req_default_model_template_aggs: FlowModelAggResp = flow_client - .post( - "/cc/model", - &FlowModelAddReq { - name: "测试需求默认模板1".into(), - info: Some("xxx".to_string()), - init_state_id: "".to_string(), - rel_template_ids: None, - template: true, - tag: Some("REQ".to_string()), - scope_level: Some(RbumScopeLevelKind::Private), - icon: None, - transitions: None, - states: None, - rel_model_id: None, - disabled: None, - }, - ) - .await; - sleep(Duration::from_millis(500)).await; - let req_default_model_template_id = req_default_model_template_aggs.id.clone(); - let model_templates: TardisPage = search_client - .put( - "/ci/item/search", - &SearchItemSearchReq { - tag: "flow_model".to_string(), - ctx: SearchItemSearchCtxReq::default(), - query: SearchItemQueryReq { ..Default::default() }, - adv_query: None, - sort: None, - page: SearchItemSearchPageReq { - number: 1, - size: 20, - fetch_total: true, - }, - }, - ) - .await; - debug!("model_templates: {:?}", model_templates); - assert_eq!(model_templates.total_size, 2); - assert!(model_templates.records.iter().any(|record| record.key == req_default_model_template_id)); - assert!(model_templates.records.iter().any(|record| record.key == req_model_template_id)); - // 2-3 config new flow template - let _: Void = flow_client - .patch( - &format!("/cc/model/{}", req_model_template_id.clone()), - &FlowModelModifyReq { - init_state_id: Some(init_state_id.to_string()), - bind_states: Some(vec![ - FlowModelBindStateReq { - state_id: init_state_id.clone(), - ext: FlowStateRelModelExt { sort: 1, show_btns: None }, - }, - FlowModelBindStateReq { - state_id: processing_state_id.clone(), - ext: FlowStateRelModelExt { sort: 2, show_btns: None }, - }, - FlowModelBindStateReq { - state_id: finish_state_id.clone(), - ext: FlowStateRelModelExt { sort: 3, show_btns: None }, - }, - FlowModelBindStateReq { - state_id: closed_state_id.clone(), - ext: FlowStateRelModelExt { sort: 4, show_btns: None }, - }, - ]), - add_transitions: Some(vec![ - FlowTransitionAddReq { - from_flow_state_id: init_state_id.clone(), - to_flow_state_id: processing_state_id.clone(), - name: Some("开始".into()), - ..Default::default() - }, - FlowTransitionAddReq { - from_flow_state_id: init_state_id.clone(), - to_flow_state_id: closed_state_id.clone(), - name: Some("关闭".into()), - ..Default::default() - }, - FlowTransitionAddReq { - from_flow_state_id: processing_state_id.clone(), - to_flow_state_id: finish_state_id.clone(), - name: Some("完成".into()), - ..Default::default() - }, - FlowTransitionAddReq { - from_flow_state_id: processing_state_id.clone(), - to_flow_state_id: closed_state_id.clone(), - name: Some("关闭".into()), - ..Default::default() - }, - FlowTransitionAddReq { - from_flow_state_id: finish_state_id.clone(), - to_flow_state_id: processing_state_id.clone(), - name: Some("重新处理".into()), - ..Default::default() - }, - FlowTransitionAddReq { - from_flow_state_id: finish_state_id.clone(), - to_flow_state_id: closed_state_id.clone(), - name: Some("关闭".into()), - ..Default::default() - }, - FlowTransitionAddReq { - from_flow_state_id: closed_state_id.clone(), - to_flow_state_id: init_state_id.clone(), - name: Some("激活".into()), - ..Default::default() - }, - ]), - ..Default::default() - }, - ) - .await; - let req_model_template: FlowModelAggResp = flow_client.get(&format!("/cc/model/{}", &req_model_template_id)).await; - // 2-2. flow template bind project template - let mut rel_model_ids = HashMap::new(); - rel_model_ids.insert("REQ".to_string(), req_model_template_id.clone()); - let result: HashMap = flow_client - .post( - "/ct/model/copy_or_reference_model", - &FlowModelCopyOrReferenceReq { - rel_model_ids, - rel_template_id: Some(project_template_id1.to_string()), - op: FlowModelAssociativeOperationKind::Reference, - }, - ) - .await; - info!("result: {:?}", result); - let project_req_model_template_id = result.get(&req_model_template_id).unwrap().id.clone(); - assert_ne!(req_model_template_id, project_req_model_template_id); - let project_result: HashMap = flow_client - .put( - &format!("/cc/model/find_rel_models?tag_ids=REQ&is_shared=false&temp_id={}", project_template_id1), - &json!(""), - ) - .await; - assert_eq!(project_req_model_template_id, project_result.get("REQ").unwrap().id.clone()); - // 2-3. modify flow temoplate - let _: Void = flow_client - .patch( - &format!("/cc/model/{}", req_model_template_id.clone()), - &FlowModelModifyReq { - modify_transitions: Some(vec![FlowTransitionModifyReq { - id: req_model_template.states.iter().find(|state| state.id == init_state_id.clone()).unwrap().transitions[0].id.clone().into(), - name: Some("111".into()), - ..Default::default() - }]), - ..Default::default() - }, - ) - .await; - tardis::tokio::time::sleep(Duration::from_millis(100)).await; - let project_req_model_template: FlowModelAggResp = flow_client.get(&format!("/cc/model/{}", &project_req_model_template_id)).await; - assert!(project_req_model_template.states.iter().find(|state| state.id == init_state_id.clone()).unwrap().transitions.iter().any(|tran| tran.name == "111")); - // copy models by project template id - let result: HashMap = flow_client - .post( - &format!("/ct/model/copy_models_by_template_id/{}/{}", project_template_id1, project_template_id2), - &json!(""), - ) - .await; - assert!(result.contains_key(&req_model_template_id)); - // 3. enter project - ctx.own_paths = "t1/app01".to_string(); - flow_client.set_auth(&ctx)?; - // 3-1 create project - let result: HashMap = flow_client - .post( - "/ci/model/copy_or_reference_model", - &FlowModelCopyOrReferenceCiReq { - rel_template_id: Some(project_template_id1.to_string()), - op: FlowModelAssociativeOperationKind::Reference, - }, - ) - .await; - info!("result: {:?}", result); - // 3-2 Start a instance - let req_inst_id1: String = flow_client - .post( - "/cc/inst", - &FlowInstStartReq { - tag: "REQ".to_string(), - create_vars: None, - rel_business_obj_id: TardisFuns::field.nanoid(), - }, - ) - .await; - info!("req_inst_id1: {:?}", req_inst_id1); - let req_inst1: FlowInstDetailResp = flow_client.get(&format!("/cc/inst/{}", req_inst_id1)).await; - info!("req_inst1: {:?}", req_inst1); - assert_eq!(req_inst1.rel_flow_model_id, project_req_model_template_id); - - let result: HashMap = flow_client.put("/cc/model/find_rel_models?tag_ids=REQ&is_shared=false", &json!("")).await; - let req_model_id = result.get("REQ").unwrap().id.clone(); - assert_eq!(project_req_model_template_id, req_model_id); - // 3-3 copy project template to app - let result: HashMap = flow_client - .post( - "/ca/model/copy_or_reference_model", - &FlowModelCopyOrReferenceReq { - rel_model_ids: HashMap::from([("REQ".to_string(), req_model_id.clone())]), - rel_template_id: None, - op: FlowModelAssociativeOperationKind::Copy, - }, - ) - .await; - let app_req_model_id = result.get(&req_model_id).unwrap().id.clone(); - assert_ne!(req_model_id.clone(), app_req_model_id.clone()); - let req_inst1: FlowInstDetailResp = flow_client.get(&format!("/cc/inst/{}", req_inst_id1)).await; - info!("req_inst1: {:?}", req_inst1); - assert_eq!(req_inst1.rel_flow_model_id, app_req_model_id); - let result: HashMap = flow_client.put("/cc/model/find_rel_models?tag_ids=REQ&is_shared=false", &json!("")).await; - let req_model_id = result.get("REQ").unwrap().id.clone(); - assert_eq!(app_req_model_id, req_model_id); - let req_model_aggs: FlowModelAggResp = flow_client.get(&format!("/cc/model/{}", req_model_id)).await; - assert_eq!(req_model_aggs.rel_model_id, "".to_string()); - // 3-4 exit app and return tenant - ctx.own_paths = "t1".to_string(); - flow_client.set_auth(&ctx)?; - let req_models: Vec = flow_client.get(&format!("/cc/model/find_by_rel_template_id?tag=REQ&template=true&rel_template_id={}", req_template_id1)).await; - assert!(!req_models.into_iter().any(|model| model.id == app_req_model_id.clone())); - // 4 return app - ctx.own_paths = "t1/app01".to_string(); - flow_client.set_auth(&ctx)?; - // 4-1 find models by project template id - let project_result: HashMap = flow_client - .put( - &format!("/cc/model/find_rel_models?tag_ids=REQ&is_shared=false&temp_id={}", project_template_id1), - &json!(""), - ) - .await; - assert_eq!(project_req_model_template_id, project_result.get("REQ").unwrap().id.clone()); - // 4-2 rebind project template - let result: HashMap = flow_client - .post( - "/ca/model/copy_or_reference_model", - &FlowModelCopyOrReferenceReq { - rel_model_ids: HashMap::from([("REQ".to_string(), project_req_model_template_id.clone())]), - rel_template_id: Some(project_template_id1.to_string()), - op: FlowModelAssociativeOperationKind::Reference, - }, - ) - .await; - assert_eq!(project_req_model_template_id, result.get(&project_req_model_template_id).unwrap().id.clone()); - - // check inst modify rel_model_id - - Ok(()) -} diff --git a/backend/spi/spi-search/src/dto/search_item_dto.rs b/backend/spi/spi-search/src/dto/search_item_dto.rs index 3c7483c4c..e00478411 100644 --- a/backend/spi/spi-search/src/dto/search_item_dto.rs +++ b/backend/spi/spi-search/src/dto/search_item_dto.rs @@ -146,6 +146,7 @@ pub struct SearchItemSearchReq { // Search conditions pub query: SearchItemQueryReq, // Advanced search + pub adv_by_or: Option, pub adv_query: Option>, // Sort // When the record set is very large, it will seriously affect the performance, it is not recommended to use. diff --git a/backend/spi/spi-search/src/serv/es/search_es_item_serv.rs b/backend/spi/spi-search/src/serv/es/search_es_item_serv.rs index 140c22542..d20ba1349 100644 --- a/backend/spi/spi-search/src/serv/es/search_es_item_serv.rs +++ b/backend/spi/spi-search/src/serv/es/search_es_item_serv.rs @@ -106,6 +106,7 @@ pub async fn add(add_req: &mut SearchItemAddReq, funs: &TardisFunsInst, ctx: &Ta size: 1, fetch_total: false, }, + adv_by_or: None, adv_query: None, }, funs, @@ -151,6 +152,7 @@ pub async fn modify(tag: &str, key: &str, modify_req: &mut SearchItemModifyReq, size: 1, fetch_total: false, }, + adv_by_or: None, adv_query: None, })?; let mut search_result = client.raw_search(&index, &q, Some(1), Some(0), None).await?; @@ -239,6 +241,7 @@ pub async fn delete(tag: &str, key: &str, funs: &TardisFunsInst, _ctx: &TardisCo size: 1, fetch_total: false, }, + adv_by_or: None, adv_query: None, })?; client.delete_by_query(&index, &q).await?; @@ -272,6 +275,7 @@ pub async fn delete_by_ownership(tag: &str, onw_paths: &str, funs: &TardisFunsIn size: 1, fetch_total: false, }, + adv_by_or: None, adv_query: None, })?; client.delete_by_query(&index, &q).await?; diff --git a/backend/spi/spi-search/src/serv/pg/search_pg_item_serv.rs b/backend/spi/spi-search/src/serv/pg/search_pg_item_serv.rs index cac3bc974..8a67c13d6 100644 --- a/backend/spi/spi-search/src/serv/pg/search_pg_item_serv.rs +++ b/backend/spi/spi-search/src/serv/pg/search_pg_item_serv.rs @@ -307,7 +307,7 @@ WHERE if sql_adv_query.is_empty() { "".to_string() } else { - format!(" AND ( 1=1 {})", sql_adv_query.join(" ")) + format!(" {} ( 1=1 {})", if search_req.adv_by_or.unwrap_or(false) { " OR " } else { " AND " }, sql_adv_query.join(" ")) }, if order_fragments.is_empty() { "".to_string() diff --git a/frontend/sdks/invoke/src/dto/search_item_dto.rs b/frontend/sdks/invoke/src/dto/search_item_dto.rs index bc90782ae..9333f110c 100644 --- a/frontend/sdks/invoke/src/dto/search_item_dto.rs +++ b/frontend/sdks/invoke/src/dto/search_item_dto.rs @@ -83,6 +83,7 @@ pub struct SearchItemSearchReq { // Search conditions pub query: SearchItemQueryReq, // Advanced search + pub adv_by_or: Option, pub adv_query: Option>, // Sort // When the record set is very large, it will seriously affect the performance, it is not recommended to use. @@ -176,6 +177,7 @@ pub struct BasicQueryCondInfo { /// /// 基础查询操作符 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, poem_openapi::Enum)] +#[serde(rename_all = "snake_case")] pub enum BasicQueryOpKind { #[oai(rename = "=")] Eq, From e6d6cbfb98f6fff192c22909135308f1e8f2b01d Mon Sep 17 00:00:00 2001 From: ljl <17743125563@163.com> Date: Fri, 27 Dec 2024 09:33:20 +0800 Subject: [PATCH 12/35] flow:fix log . --- .../middlewares/flow/src/serv/clients/log_client.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/backend/middlewares/flow/src/serv/clients/log_client.rs b/backend/middlewares/flow/src/serv/clients/log_client.rs index 2801a0a57..a42b74e31 100644 --- a/backend/middlewares/flow/src/serv/clients/log_client.rs +++ b/backend/middlewares/flow/src/serv/clients/log_client.rs @@ -44,7 +44,6 @@ pub struct LogParamExt { pub delete: Option, } - pub enum LogParamExtSceneKind { ApprovalFlow, Dynamic, @@ -120,7 +119,7 @@ impl FlowLogClient { let task_handle = tokio::spawn(async move { let funs = flow_constants::get_tardis_inst(); if is_v2 { - Self::addv2_item( + let _ = Self::addv2_item( tag, content, ext, @@ -133,10 +132,9 @@ impl FlowLogClient { &ctx_clone, push_clone, // 使用克隆的 push 变量 ) - .await - .unwrap(); + .await; } else { - Self::add_item( + let _ = Self::add_item( tag, content, ext, @@ -149,10 +147,8 @@ impl FlowLogClient { &ctx_clone, push_clone, // 使用克隆的 push 变量 ) - .await - .unwrap(); + .await; } - }); task_handle.await.unwrap(); Ok(()) From 07a4f5025cb68cc67f430557bc6ae244f3a487f3 Mon Sep 17 00:00:00 2001 From: ljl <17743125563@163.com> Date: Mon, 30 Dec 2024 11:28:42 +0800 Subject: [PATCH 13/35] rbum attr value fix. --- backend/basic/src/rbum/dto/rbum_rel_agg_dto.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/basic/src/rbum/dto/rbum_rel_agg_dto.rs b/backend/basic/src/rbum/dto/rbum_rel_agg_dto.rs index 788b0e33a..041ed4a92 100644 --- a/backend/basic/src/rbum/dto/rbum_rel_agg_dto.rs +++ b/backend/basic/src/rbum/dto/rbum_rel_agg_dto.rs @@ -43,7 +43,7 @@ pub struct RbumRelAttrAggAddReq { /// Relationship attribute value /// /// 关联属性值 - #[oai(validator(min_length = "0", max_length = "2000"))] + // #[oai(validator(min_length = "0", max_length = "2000"))] pub value: String, /// Relationship attribute name /// From f189ddab4ec143d49bb617694760ffe541924af9 Mon Sep 17 00:00:00 2001 From: 4t145 Date: Mon, 30 Dec 2024 10:35:20 +0800 Subject: [PATCH 14/35] event: filter expired message --- Cargo.toml | 6 +++--- .../src/extension/notification.rs | 6 +++--- backend/gateways/spacegate-plugins/src/lib.rs | 3 ++- backend/gateways/spacegate-plugins/src/plugin.rs | 2 +- .../event/src/serv/event_message_serv.rs | 15 ++++++++++----- 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2c210dccf..162b3c672 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,9 +67,9 @@ strum = { version = "0.26", features = ["derive"] } # tardis = { version = "0.2.0", path = "../tardis/tardis" } tardis = { git = "https://github.com/ideal-world/tardis.git", rev = "66d4c63" } # asteroid-mq = { git = "https://github.com/4t145/asteroid-mq.git", rev = "d59c64d" } -asteroid-mq = { git = "https://github.com/4t145/asteroid-mq.git", rev = "f9c96f3" } +asteroid-mq = { git = "https://github.com/4t145/asteroid-mq.git", rev = "726c8dd" } # asteroid-mq = { version = "0.1.0-alpha.5" } -asteroid-mq-sdk = { git = "https://github.com/4t145/asteroid-mq.git", rev = "f9c96f3" } +asteroid-mq-sdk = { git = "https://github.com/4t145/asteroid-mq.git", rev = "726c8dd" } # asteroid-mq-sdk = { version = "0.1.0-alpha.5" } #spacegate @@ -78,7 +78,7 @@ asteroid-mq-sdk = { git = "https://github.com/4t145/asteroid-mq.git", rev = "f9c # "k8s", # "ext-axum", # ] } -spacegate-shell = { git = "https://github.com/ideal-world/spacegate.git", rev="40bb693", features = [ +spacegate-shell = { git = "https://github.com/ideal-world/spacegate.git", rev="fe747e4", features = [ "cache", "k8s", "ext-axum", diff --git a/backend/gateways/spacegate-plugins/src/extension/notification.rs b/backend/gateways/spacegate-plugins/src/extension/notification.rs index 9de8e4199..840686c55 100644 --- a/backend/gateways/spacegate-plugins/src/extension/notification.rs +++ b/backend/gateways/spacegate-plugins/src/extension/notification.rs @@ -1,7 +1,7 @@ use std::{borrow::Cow, collections::HashMap, sync::Arc}; use http::{header::CONTENT_TYPE, HeaderName, HeaderValue, Uri}; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use spacegate_shell::{kernel::backend_service::http_client_service::HttpClient, SgBody, SgRequest}; use tardis::{log as tracing, serde_json}; @@ -43,7 +43,7 @@ impl NotificationContext { } } -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct ReachMsgSendReq { pub scene_code: String, pub receives: Vec, @@ -51,7 +51,7 @@ pub struct ReachMsgSendReq { pub replace: HashMap, } -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct ReachMsgReceive { pub receive_group_code: String, pub receive_kind: String, diff --git a/backend/gateways/spacegate-plugins/src/lib.rs b/backend/gateways/spacegate-plugins/src/lib.rs index 276639f6f..69435faa0 100644 --- a/backend/gateways/spacegate-plugins/src/lib.rs +++ b/backend/gateways/spacegate-plugins/src/lib.rs @@ -8,7 +8,7 @@ mod marker; mod plugin; pub const PACKAGE_NAME: &str = "spacegate_lib"; -use plugin::op_redis_publisher; +use plugin::{notify, op_redis_publisher}; use spacegate_shell::plugin::PluginRepository; pub fn register_lib_plugins(repo: &PluginRepository) { repo.register::(); @@ -18,4 +18,5 @@ pub fn register_lib_plugins(repo: &PluginRepository) { repo.register::(); repo.register::(); repo.register::(); + repo.register::(); } diff --git a/backend/gateways/spacegate-plugins/src/plugin.rs b/backend/gateways/spacegate-plugins/src/plugin.rs index f335721f0..e531f6731 100644 --- a/backend/gateways/spacegate-plugins/src/plugin.rs +++ b/backend/gateways/spacegate-plugins/src/plugin.rs @@ -5,4 +5,4 @@ pub mod auth; pub mod ip_time; pub mod notify; pub mod op_redis_publisher; -pub mod rewrite_ns_b_ip; +pub mod rewrite_ns_b_ip; \ No newline at end of file diff --git a/backend/middlewares/event/src/serv/event_message_serv.rs b/backend/middlewares/event/src/serv/event_message_serv.rs index c8c889c8e..be00f8dab 100644 --- a/backend/middlewares/event/src/serv/event_message_serv.rs +++ b/backend/middlewares/event/src/serv/event_message_serv.rs @@ -3,9 +3,7 @@ use asteroid_mq::{ protocol::{node::raft::proposal::MessageStateUpdate, topic::durable_message::DurableMessageQuery}, }; use tardis::{ - basic::{error::TardisError, result::TardisResult}, - db::sea_orm::{ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, IntoActiveModel, QueryFilter, QuerySelect, Set, Unchanged}, - TardisFunsInst, + basic::{error::TardisError, result::TardisResult}, chrono::Utc, db::sea_orm::{ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, IntoActiveModel, QueryFilter, QuerySelect, Set, Unchanged}, TardisFunsInst }; use crate::domain::event_message::{ActiveModel, Column, Entity, Model}; @@ -39,14 +37,21 @@ impl EventMessageServ { } pub async fn batch_retrieve(&self, topic: TopicCode, query: DurableMessageQuery, funs: &TardisFunsInst) -> TardisResult> { let DurableMessageQuery { limit, offset, .. } = query; - let select = Entity::find().filter(Column::Archived.eq(false)).filter(Column::Topic.eq(topic.to_string())).limit(Some(limit as u64)).offset(Some(offset as u64)); + let select = Entity::find() + .filter(Column::Archived.eq(false)) + .filter(Column::Time.gte(Utc::now())) + .filter(Column::Topic.eq(topic.to_string())) + .limit(Some(limit as u64)) + .offset(Some(offset as u64)); let conn = funs.reldb().conn(); let raw_conn = conn.raw_conn(); let models = select.all(raw_conn).await?; models.into_iter().map(|model| model.try_into_durable_message()).collect::>>() } pub async fn retrieve(&self, topic: TopicCode, message_id: MessageId, funs: &TardisFunsInst) -> TardisResult { - let select = Entity::find().filter(Column::Archived.eq(false)).filter(Column::Topic.eq(topic.to_string())).filter(Column::MessageId.eq(message_id.to_base64())); + let select = Entity::find().filter(Column::Archived.eq(false)) + .filter(Column::Time.gte(Utc::now())) + .filter(Column::Topic.eq(topic.to_string())).filter(Column::MessageId.eq(message_id.to_base64())); let conn = funs.reldb().conn(); let raw_conn = conn.raw_conn(); let model = select.one(raw_conn).await?; From a847f1f83c8786969a881700fef33f3b8a54f841 Mon Sep 17 00:00:00 2001 From: ljl <17743125563@163.com> Date: Mon, 30 Dec 2024 15:01:14 +0800 Subject: [PATCH 15/35] iam:fix rel account role to log. --- .../iam/src/basic/serv/iam_rel_serv.rs | 70 ++++++++++++++----- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/backend/supports/iam/src/basic/serv/iam_rel_serv.rs b/backend/supports/iam/src/basic/serv/iam_rel_serv.rs index 4d0e219ee..7edd58939 100644 --- a/backend/supports/iam/src/basic/serv/iam_rel_serv.rs +++ b/backend/supports/iam/src/basic/serv/iam_rel_serv.rs @@ -1,3 +1,5 @@ +use std::fmt::format; + use bios_basic::helper::request_helper::get_real_ip_from_ctx; use tardis::basic::dto::TardisContext; use tardis::basic::result::TardisResult; @@ -13,7 +15,7 @@ use bios_basic::rbum::serv::rbum_crud_serv::RbumCrudOperation; use bios_basic::rbum::serv::rbum_item_serv::RbumItemCrudOperation; use bios_basic::rbum::serv::rbum_rel_serv::RbumRelServ; -use crate::basic::dto::iam_filer_dto::{IamAccountFilterReq, IamResFilterReq}; +use crate::basic::dto::iam_filer_dto::{IamAccountFilterReq, IamResFilterReq, IamRoleFilterReq}; use crate::basic::serv::iam_cert_serv::IamCertServ; use crate::basic::serv::iam_key_cache_serv::{IamCacheResRelAddOrModifyReq, IamCacheResRelDeleteReq, IamIdentCacheServ, IamResCacheServ}; use crate::basic::serv::iam_res_serv::IamResServ; @@ -22,6 +24,7 @@ use crate::iam_enumeration::{IamRelKind, IamResKind}; use super::clients::iam_log_client::{IamLogClient, LogParamTag}; use super::clients::iam_search_client::IamSearchClient; use super::iam_account_serv::IamAccountServ; +use super::iam_role_serv::IamRoleServ; pub struct IamRelServ; @@ -79,14 +82,6 @@ impl IamRelServ { if rel_kind == &IamRelKind::IamAccountRole { IamIdentCacheServ::refresh_account_info_by_account_id(from_iam_item_id, funs).await?; - let _ = IamLogClient::add_ctx_task( - LogParamTag::IamAccount, - Some(from_iam_item_id.to_string()), - "增加账号租户角色为管理员".to_string(), - Some("AddTenantRoleAsAdmin".to_string()), - ctx, - ) - .await; let account_name = IamAccountServ::get_item( from_iam_item_id, &IamAccountFilterReq { @@ -103,6 +98,31 @@ impl IamRelServ { ) .await? .name; + let role_name = IamRoleServ::get_item( + to_iam_item_id, + &IamRoleFilterReq { + basic: RbumBasicFilterReq { + ignore_scope: true, + with_sub_own_paths: true, + own_paths: Some("".to_string()), + ..Default::default() + }, + ..Default::default() + }, + funs, + ctx, + ) + .await? + .name; + let _ = IamLogClient::add_ctx_task( + LogParamTag::IamAccount, + Some(from_iam_item_id.to_string()), + format!("账号{}增加{}角色", account_name, role_name), + Some("AddTenantRoleAsAdmin".to_string()), + ctx, + ) + .await; + let _ = IamLogClient::add_ctx_task( LogParamTag::IamRole, Some(to_iam_item_id.to_string()), @@ -494,14 +514,6 @@ impl IamRelServ { } IamRelKind::IamAccountRole => { IamIdentCacheServ::refresh_account_info_by_account_id(from_iam_item_id, funs).await?; - let _ = IamLogClient::add_ctx_task( - LogParamTag::IamAccount, - Some(from_iam_item_id.to_string()), - "移除账号租户角色为管理员".to_string(), - Some("RemoveTenantRoleAsAdmin".to_string()), - ctx, - ) - .await; let account_name = IamAccountServ::get_item( from_iam_item_id, &IamAccountFilterReq { @@ -518,6 +530,30 @@ impl IamRelServ { ) .await? .name; + let role_name = IamRoleServ::get_item( + to_iam_item_id, + &IamRoleFilterReq { + basic: RbumBasicFilterReq { + ignore_scope: true, + with_sub_own_paths: true, + own_paths: Some("".to_string()), + ..Default::default() + }, + ..Default::default() + }, + funs, + ctx, + ) + .await? + .name; + let _ = IamLogClient::add_ctx_task( + LogParamTag::IamAccount, + Some(from_iam_item_id.to_string()), + format!("账号{}被移除{}角色", account_name, role_name), + Some("RemoveTenantRoleAsAdmin".to_string()), + ctx, + ) + .await; let _ = IamLogClient::add_ctx_task( LogParamTag::IamRole, Some(to_iam_item_id.to_string()), From 9dec8a0a3e863ee99beb0f4da43ded03508bdfaf Mon Sep 17 00:00:00 2001 From: ljl <17743125563@163.com> Date: Mon, 30 Dec 2024 16:59:34 +0800 Subject: [PATCH 16/35] iam:fix set cate name. --- backend/supports/iam/src/basic/dto/iam_set_dto.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/supports/iam/src/basic/dto/iam_set_dto.rs b/backend/supports/iam/src/basic/dto/iam_set_dto.rs index 919bcc28a..d841b7a5c 100644 --- a/backend/supports/iam/src/basic/dto/iam_set_dto.rs +++ b/backend/supports/iam/src/basic/dto/iam_set_dto.rs @@ -7,7 +7,7 @@ use bios_basic::rbum::rbum_enumeration::RbumScopeLevelKind; #[derive(poem_openapi::Object, Serialize, Deserialize, Debug)] pub struct IamSetCateAddReq { - #[oai(validator(min_length = "2", max_length = "255"))] + // #[oai(validator(min_length = "1", max_length = "255"))] pub name: TrimString, pub scope_level: Option, #[oai(validator(min_length = "2", max_length = "255"))] @@ -23,7 +23,7 @@ pub struct IamSetCateAddReq { #[derive(poem_openapi::Object, Serialize, Deserialize, Debug)] pub struct IamSetCateModifyReq { - #[oai(validator(min_length = "2", max_length = "255"))] + // #[oai(validator(min_length = "2", max_length = "255"))] pub name: Option, pub scope_level: Option, #[oai(validator(min_length = "2", max_length = "255"))] From 26907d3b4cd0dece0232abbc530413f932a16cb1 Mon Sep 17 00:00:00 2001 From: ljl <17743125563@163.com> Date: Tue, 31 Dec 2024 11:20:23 +0800 Subject: [PATCH 17/35] spi:fix alter table column index. --- backend/basic/src/spi/spi_initializer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/basic/src/spi/spi_initializer.rs b/backend/basic/src/spi/spi_initializer.rs index 2974c659e..946cb929a 100644 --- a/backend/basic/src/spi/spi_initializer.rs +++ b/backend/basic/src/spi/spi_initializer.rs @@ -479,7 +479,7 @@ EXECUTE PROCEDURE TARDIS_AUTO_UPDATE_TIME_{}();"###, &s[..max_size.min(s.len())] } let index_name = format!( - "idx_{schema_name}{tag}_{table_flag}_{field_name_or_fun}_{idx}", + "idx_{schema_name}{tag}_{table_flag}_{field_name}_{idx}", schema_name = truncate_str(schema_name, 18), tag = truncate_str(tag, 11), table_flag = truncate_str(table_flag, 25), From fd9e3f5534464c91d3ec19676e7abdf006572972 Mon Sep 17 00:00:00 2001 From: RWDai <27391645+RWDai@users.noreply.github.com> Date: Mon, 30 Dec 2024 17:51:16 +0800 Subject: [PATCH 18/35] fix: improve SQL query formatting and add length check for ext items in search_pg_item_serv.rs --- .../spi-search/src/serv/pg/search_pg_item_serv.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/backend/spi/spi-search/src/serv/pg/search_pg_item_serv.rs b/backend/spi/spi-search/src/serv/pg/search_pg_item_serv.rs index 8a67c13d6..8c7038449 100644 --- a/backend/spi/spi-search/src/serv/pg/search_pg_item_serv.rs +++ b/backend/spi/spi-search/src/serv/pg/search_pg_item_serv.rs @@ -307,7 +307,11 @@ WHERE if sql_adv_query.is_empty() { "".to_string() } else { - format!(" {} ( 1=1 {})", if search_req.adv_by_or.unwrap_or(false) { " OR " } else { " AND " }, sql_adv_query.join(" ")) + format!( + " {} ( 1=1 {})", + if search_req.adv_by_or.unwrap_or(false) { " OR " } else { " AND " }, + sql_adv_query.join(" ") + ) }, if order_fragments.is_empty() { "".to_string() @@ -694,6 +698,13 @@ fn package_ext( "({}.ext ->> '{}' is null or {}.ext ->> '{}' = '' or {}.ext ->> '{}' = '[]')", table_alias_name, ext_item.field, table_alias_name, ext_item.field, table_alias_name, ext_item.field )); + } else if ext_item.op == BasicQueryOpKind::Len { + if let Some(first_value) = value.pop() { + sql_and_where.push(format!("(length(ext->>'{}') = ${})", ext_item.field, sql_vals.len() + 1)); + sql_vals.push(first_value); + } else { + return err_not_found(ext_item); + }; } else { if value.len() > 1 { return err_not_found(&ext_item.clone().into()); From 8263753bd344f0fd95fe036e1ad8c4b0972715e7 Mon Sep 17 00:00:00 2001 From: ljl <17743125563@163.com> Date: Thu, 2 Jan 2025 09:54:59 +0800 Subject: [PATCH 19/35] spi-stats:fix record default. --- .../src/serv/pg/stats_pg_record_serv.rs | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/backend/spi/spi-stats/src/serv/pg/stats_pg_record_serv.rs b/backend/spi/spi-stats/src/serv/pg/stats_pg_record_serv.rs index 7f43f85d5..3f30ce9ca 100644 --- a/backend/spi/spi-stats/src/serv/pg/stats_pg_record_serv.rs +++ b/backend/spi/spi-stats/src/serv/pg/stats_pg_record_serv.rs @@ -265,15 +265,29 @@ pub(crate) async fn fact_record_load( )); }; if fact_col_conf.dim_multi_values.unwrap_or(false) { - fields_values.insert( - fact_col_conf.key.to_string(), - fact_col_result.unwrap_or(dim_conf.data_type.result_to_sea_orm_value_array(&latest_data, &fact_col_conf.key)?), - ); + if dim_conf.data_type.result_to_sea_orm_value_array(&latest_data, &fact_col_conf.key).is_err() { + fields_values.insert( + fact_col_conf.key.to_string(), + fact_col_result.unwrap_or(dim_conf.data_type.result_to_sea_orm_value_array_default()?), + ); + } else { + fields_values.insert( + fact_col_conf.key.to_string(), + fact_col_result.unwrap_or(dim_conf.data_type.result_to_sea_orm_value_array(&latest_data, &fact_col_conf.key)?), + ); + } } else { - fields_values.insert( - fact_col_conf.key.to_string(), - fact_col_result.unwrap_or(dim_conf.data_type.result_to_sea_orm_value(&latest_data, &fact_col_conf.key)?), - ); + if dim_conf.data_type.result_to_sea_orm_value(&latest_data, &fact_col_conf.key).is_err() { + fields_values.insert( + fact_col_conf.key.to_string(), + fact_col_result.unwrap_or(dim_conf.data_type.result_to_sea_orm_value_default()?), + ); + } else { + fields_values.insert( + fact_col_conf.key.to_string(), + fact_col_result.unwrap_or(dim_conf.data_type.result_to_sea_orm_value(&latest_data, &fact_col_conf.key)?), + ); + } } } else if fact_col_conf.kind == StatsFactColKind::Measure { let Some(mes_data_type) = fact_col_conf.mes_data_type.as_ref() else { @@ -284,10 +298,14 @@ pub(crate) async fn fact_record_load( "400-spi-stats-invalid-request", )); }; - fields_values.insert( - fact_col_conf.key.to_string(), - fact_col_result.unwrap_or(mes_data_type.result_to_sea_orm_value(&latest_data, &fact_col_conf.key)?), - ); + if mes_data_type.result_to_sea_orm_value(&latest_data, &fact_col_conf.key).is_err() { + fields_values.insert(fact_col_conf.key.to_string(), fact_col_result.unwrap_or(mes_data_type.result_to_sea_orm_value_default()?)); + } else { + fields_values.insert( + fact_col_conf.key.to_string(), + fact_col_result.unwrap_or(mes_data_type.result_to_sea_orm_value(&latest_data, &fact_col_conf.key)?), + ); + } } else { fields_values.insert( fact_col_conf.key.to_string(), From 26a9acfa5108a1fa148c3ba77be920b6430781f3 Mon Sep 17 00:00:00 2001 From: ZzIsGod1019 <1498852723@qq.com> Date: Tue, 31 Dec 2024 20:10:11 +0800 Subject: [PATCH 20/35] flow: fix bug (conf guard_by_assigned failed) (#889) * flow: fix bug (add default field) * flow: fix bug (guard_by_assigned) * code fmt --- backend/basic/src/dto.rs | 9 +- .../gateways/spacegate-plugins/src/plugin.rs | 2 +- .../event/src/serv/event_message_serv.rs | 13 +- .../flow/src/api/ca/flow_ca_model_api.rs | 18 +- .../flow/src/api/cc/flow_cc_inst_api.rs | 9 +- .../flow/src/api/cc/flow_cc_model_api.rs | 1 + .../src/api/cc/flow_cc_model_version_api.rs | 24 ++ .../flow/src/api/cc/flow_cc_state_api.rs | 2 + .../flow/src/api/ci/flow_ci_inst_api.rs | 2 + .../flow/src/api/ci/flow_ci_model_api.rs | 52 +-- .../flow/src/api/ct/flow_ct_model_api.rs | 9 +- .../middlewares/flow/src/domain/flow_state.rs | 5 + backend/middlewares/flow/src/dto.rs | 1 + .../middlewares/flow/src/dto/flow_cond_dto.rs | 169 ++++++++++ .../middlewares/flow/src/dto/flow_inst_dto.rs | 8 +- .../flow/src/dto/flow_state_dto.rs | 4 + .../flow/src/dto/flow_transition_dto.rs | 12 +- backend/middlewares/flow/src/serv.rs | 2 +- backend/middlewares/flow/src/serv/clients.rs | 2 +- .../flow/src/serv/clients/kv_client.rs | 14 +- .../flow/src/serv/clients/log_client.rs | 3 + .../flow/src/serv/clients/search_client.rs | 55 ++-- .../flow/src/serv/flow_event_serv.rs | 12 +- .../flow/src/serv/flow_inst_serv.rs | 309 ++++++++++++------ .../flow/src/serv/flow_log_serv.rs | 160 ++++++++- .../flow/src/serv/flow_model_serv.rs | 124 ++++++- .../flow/src/serv/flow_model_version_serv.rs | 17 + .../flow/src/serv/flow_state_serv.rs | 8 +- .../flow/src/serv/flow_transition_serv.rs | 38 ++- .../flow/tests/test_flow_scenes_fsm.rs | 4 + .../src/serv/pg/search_pg_item_serv.rs | 2 +- .../spi/spi-stats/tests/test_stats_conf.rs | 2 +- 32 files changed, 849 insertions(+), 243 deletions(-) create mode 100644 backend/middlewares/flow/src/dto/flow_cond_dto.rs diff --git a/backend/basic/src/dto.rs b/backend/basic/src/dto.rs index bec2a5a5a..4fc4a5c77 100644 --- a/backend/basic/src/dto.rs +++ b/backend/basic/src/dto.rs @@ -88,7 +88,14 @@ impl BasicQueryCondInfo { | BasicQueryOpKind::NotRLike => { check_val.as_str().map(|check_val_str| cond.value.as_str().map(|cond_val_str| check_val_str.contains(cond_val_str)).unwrap_or(false)).unwrap_or(false) } - BasicQueryOpKind::In => check_val.as_array().map(|check_val_arr| check_val_arr.contains(&cond.value)).unwrap_or(false), + BasicQueryOpKind::In => check_val.as_array().map(|check_val_arr| { + if cond.value.is_array() { + cond.value.as_array().unwrap_or(&vec![]).iter().any(|item| check_val_arr.contains(&item)) + } else { + check_val_arr.contains(&cond.value) + } + }) + .unwrap_or(false), BasicQueryOpKind::NotIn => check_val.as_array().map(|check_val_arr| check_val_arr.contains(&cond.value)).unwrap_or(false), BasicQueryOpKind::IsNull => false, BasicQueryOpKind::IsNotNull => false, diff --git a/backend/gateways/spacegate-plugins/src/plugin.rs b/backend/gateways/spacegate-plugins/src/plugin.rs index e531f6731..f335721f0 100644 --- a/backend/gateways/spacegate-plugins/src/plugin.rs +++ b/backend/gateways/spacegate-plugins/src/plugin.rs @@ -5,4 +5,4 @@ pub mod auth; pub mod ip_time; pub mod notify; pub mod op_redis_publisher; -pub mod rewrite_ns_b_ip; \ No newline at end of file +pub mod rewrite_ns_b_ip; diff --git a/backend/middlewares/event/src/serv/event_message_serv.rs b/backend/middlewares/event/src/serv/event_message_serv.rs index be00f8dab..23fd0b12f 100644 --- a/backend/middlewares/event/src/serv/event_message_serv.rs +++ b/backend/middlewares/event/src/serv/event_message_serv.rs @@ -3,7 +3,10 @@ use asteroid_mq::{ protocol::{node::raft::proposal::MessageStateUpdate, topic::durable_message::DurableMessageQuery}, }; use tardis::{ - basic::{error::TardisError, result::TardisResult}, chrono::Utc, db::sea_orm::{ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, IntoActiveModel, QueryFilter, QuerySelect, Set, Unchanged}, TardisFunsInst + basic::{error::TardisError, result::TardisResult}, + chrono::Utc, + db::sea_orm::{ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, IntoActiveModel, QueryFilter, QuerySelect, Set, Unchanged}, + TardisFunsInst, }; use crate::domain::event_message::{ActiveModel, Column, Entity, Model}; @@ -49,9 +52,11 @@ impl EventMessageServ { models.into_iter().map(|model| model.try_into_durable_message()).collect::>>() } pub async fn retrieve(&self, topic: TopicCode, message_id: MessageId, funs: &TardisFunsInst) -> TardisResult { - let select = Entity::find().filter(Column::Archived.eq(false)) - .filter(Column::Time.gte(Utc::now())) - .filter(Column::Topic.eq(topic.to_string())).filter(Column::MessageId.eq(message_id.to_base64())); + let select = Entity::find() + .filter(Column::Archived.eq(false)) + .filter(Column::Time.gte(Utc::now())) + .filter(Column::Topic.eq(topic.to_string())) + .filter(Column::MessageId.eq(message_id.to_base64())); let conn = funs.reldb().conn(); let raw_conn = conn.raw_conn(); let model = select.one(raw_conn).await?; diff --git a/backend/middlewares/flow/src/api/ca/flow_ca_model_api.rs b/backend/middlewares/flow/src/api/ca/flow_ca_model_api.rs index 6f562eace..b2bcc8fa5 100644 --- a/backend/middlewares/flow/src/api/ca/flow_ca_model_api.rs +++ b/backend/middlewares/flow/src/api/ca/flow_ca_model_api.rs @@ -35,14 +35,7 @@ impl FlowCaModelApi { let mut result = HashMap::new(); for (_, rel_model_id) in req.0.rel_model_ids { let new_model = FlowModelServ::copy_or_reference_model(&rel_model_id, &req.0.op, FlowModelKind::AsModel, &funs, &ctx.0).await?; - FlowInstServ::batch_update_when_switch_model( - &new_model, - None, - None, - &funs, - &ctx.0, - ) - .await?; + FlowInstServ::batch_update_when_switch_model(&new_model, None, None, &funs, &ctx.0).await?; result.insert(rel_model_id.clone(), new_model); } @@ -66,14 +59,7 @@ impl FlowCaModelApi { funs.begin().await?; let _orginal_models = FlowModelServ::clean_rel_models(None, None, Some(vec![req.0.tag.clone()]), &funs, &ctx.0).await?; let new_model = FlowModelServ::copy_or_reference_model(&req.0.rel_model_id, &req.0.op, FlowModelKind::AsModel, &funs, &ctx.0).await?; - FlowInstServ::batch_update_when_switch_model( - &new_model, - None, - None, - &funs, - &ctx.0, - ) - .await?; + FlowInstServ::batch_update_when_switch_model(&new_model, None, None, &funs, &ctx.0).await?; funs.commit().await?; ctx.0.execute_task().await?; diff --git a/backend/middlewares/flow/src/api/cc/flow_cc_inst_api.rs b/backend/middlewares/flow/src/api/cc/flow_cc_inst_api.rs index 4938684ae..901835646 100644 --- a/backend/middlewares/flow/src/api/cc/flow_cc_inst_api.rs +++ b/backend/middlewares/flow/src/api/cc/flow_cc_inst_api.rs @@ -10,7 +10,9 @@ use tardis::web::web_resp::{TardisApiResult, TardisPage, TardisResp, Void}; use crate::dto::flow_external_dto::FlowExternalCallbackOp; use crate::dto::flow_inst_dto::{ - FlowInstAbortReq, FlowInstCommentReq, FlowInstDetailResp, FlowInstFilterReq, FlowInstFindNextTransitionResp, FlowInstFindNextTransitionsReq, FlowInstFindStateAndTransitionsReq, FlowInstFindStateAndTransitionsResp, FlowInstModifyAssignedReq, FlowInstModifyCurrentVarsReq, FlowInstOperateReq, FlowInstSearchReq, FlowInstSearchResp, FlowInstStartReq, FlowInstSummaryResp, FlowInstTransferReq, FlowInstTransferResp + FlowInstAbortReq, FlowInstCommentReq, FlowInstDetailResp, FlowInstFilterReq, FlowInstFindNextTransitionResp, FlowInstFindNextTransitionsReq, + FlowInstFindStateAndTransitionsReq, FlowInstFindStateAndTransitionsResp, FlowInstModifyAssignedReq, FlowInstModifyCurrentVarsReq, FlowInstOperateReq, FlowInstSearchReq, + FlowInstSearchResp, FlowInstStartReq, FlowInstSummaryResp, FlowInstTransferReq, FlowInstTransferResp, }; use crate::flow_constants; use crate::helper::loop_check_helper; @@ -113,6 +115,7 @@ impl FlowCcInstApi { /// /// 获取实例列表 #[oai(path = "/details", method = "get")] + #[allow(clippy::too_many_arguments)] async fn paginate_detail_items( &self, flow_model_id: Query>, @@ -124,6 +127,8 @@ impl FlowCcInstApi { with_sub: Query>, page_number: Query, page_size: Query, + desc_by_create: Query>, + desc_by_update: Query>, ctx: TardisContextExtractor, _request: &Request, ) -> TardisApiResult> { @@ -141,6 +146,8 @@ impl FlowCcInstApi { }, page_number.0, page_size.0, + desc_by_create.0, + desc_by_update.0, &funs, &ctx.0, ) diff --git a/backend/middlewares/flow/src/api/cc/flow_cc_model_api.rs b/backend/middlewares/flow/src/api/cc/flow_cc_model_api.rs index 2030527fb..fc78f312e 100644 --- a/backend/middlewares/flow/src/api/cc/flow_cc_model_api.rs +++ b/backend/middlewares/flow/src/api/cc/flow_cc_model_api.rs @@ -161,6 +161,7 @@ impl FlowCcModelApi { &FlowModelFilterReq { basic: RbumBasicFilterReq { ids: flow_model_ids.0.map(|ids| ids.split(',').map(|id| id.to_string()).collect::>()), + own_paths: if rel_template_id.0.is_some() { Some("".to_string()) } else { None }, name: name.0, with_sub_own_paths: with_sub.0.unwrap_or(false), enabled: enabled.0, diff --git a/backend/middlewares/flow/src/api/cc/flow_cc_model_version_api.rs b/backend/middlewares/flow/src/api/cc/flow_cc_model_version_api.rs index de6fb3432..2b6aaeb2e 100644 --- a/backend/middlewares/flow/src/api/cc/flow_cc_model_version_api.rs +++ b/backend/middlewares/flow/src/api/cc/flow_cc_model_version_api.rs @@ -60,6 +60,30 @@ impl FlowCcModelVersionApi { TardisResp::ok(result) } + /// Get Model By Model Id + /// + /// 获取模型使用全局owner + #[oai(path = "/:flow_version_id/global", method = "get")] + async fn gloabl_get(&self, flow_version_id: Path, ctx: TardisContextExtractor, _request: &Request) -> TardisApiResult { + let funs = flow_constants::get_tardis_inst(); + let result = FlowModelVersionServ::get_item( + &flow_version_id.0, + &FlowModelVersionFilterReq { + basic: RbumBasicFilterReq { + own_paths: Some("".to_string()), + with_sub_own_paths: true, + ..Default::default() + }, + ..Default::default() + }, + &funs, + &ctx.0, + ) + .await?; + ctx.0.execute_task().await?; + TardisResp::ok(result) + } + /// Find Models /// /// 获取模型列表 diff --git a/backend/middlewares/flow/src/api/cc/flow_cc_state_api.rs b/backend/middlewares/flow/src/api/cc/flow_cc_state_api.rs index ff928822e..2b0008e05 100644 --- a/backend/middlewares/flow/src/api/cc/flow_cc_state_api.rs +++ b/backend/middlewares/flow/src/api/cc/flow_cc_state_api.rs @@ -84,6 +84,7 @@ impl FlowCcStateApi { state_kind: Query>, enabled: Query>, template: Query>, + main: Query>, with_sub: Query>, is_global: Query>, page_number: Query, @@ -119,6 +120,7 @@ impl FlowCcStateApi { ..Default::default() }, tag: tag.0, + main: main.0, sys_state: sys_state.0, state_kind: state_kind.0, template: template.0, diff --git a/backend/middlewares/flow/src/api/ci/flow_ci_inst_api.rs b/backend/middlewares/flow/src/api/ci/flow_ci_inst_api.rs index f68b4afac..56b7903be 100644 --- a/backend/middlewares/flow/src/api/ci/flow_ci_inst_api.rs +++ b/backend/middlewares/flow/src/api/ci/flow_ci_inst_api.rs @@ -232,6 +232,8 @@ impl FlowCiInstApi { tag: add_req.0.tag.clone(), create_vars: add_req.0.create_vars.clone(), transition_id: None, + vars: None, + log_text: None, }, add_req.0.current_state_name.clone(), &funs, diff --git a/backend/middlewares/flow/src/api/ci/flow_ci_model_api.rs b/backend/middlewares/flow/src/api/ci/flow_ci_model_api.rs index afa976139..76dec859d 100644 --- a/backend/middlewares/flow/src/api/ci/flow_ci_model_api.rs +++ b/backend/middlewares/flow/src/api/ci/flow_ci_model_api.rs @@ -14,9 +14,9 @@ use bios_basic::rbum::rbum_enumeration::RbumRelFromKind; use bios_basic::rbum::serv::rbum_item_serv::RbumItemCrudOperation; use bios_basic::rbum::serv::rbum_rel_serv::RbumRelServ; use itertools::Itertools; -use tardis::futures::future::join_all; use std::iter::Iterator; use tardis::basic::dto::TardisContext; +use tardis::futures::future::join_all; use tardis::log::warn; use tardis::web::context_extractor::TardisContextExtractor; use tardis::web::poem::Request; @@ -182,52 +182,7 @@ impl FlowCiModelApi { let mut funs = flow_constants::get_tardis_inst(); check_without_owner_and_unsafe_fill_ctx(request, &funs, &mut ctx.0)?; funs.begin().await?; - let mut result = HashMap::new(); - for from_model in FlowModelServ::find_detail_items( - &FlowModelFilterReq { - basic: RbumBasicFilterReq { - ids: Some( - FlowRelServ::find_to_simple_rels(&FlowRelKind::FlowModelTemplate, &from_template_id.0, None, None, &funs, &ctx.0) - .await? - .into_iter() - .map(|rel| rel.rel_id) - .collect_vec(), - ), - ignore_scope: true, - ..Default::default() - }, - ..Default::default() - }, - None, - None, - &funs, - &ctx.0, - ) - .await? - { - let added_model = FlowModelServ::copy_or_reference_model( - &from_model.rel_model_id, - &FlowModelAssociativeOperationKind::ReferenceOrCopy, - FlowModelKind::AsTemplateAndAsModel, - &funs, - &ctx.0, - ) - .await?; - FlowRelServ::add_simple_rel( - &FlowRelKind::FlowModelTemplate, - &added_model.id, - &to_template_id.0, - None, - None, - false, - true, - None, - &funs, - &ctx.0, - ) - .await?; - result.insert(from_model.rel_model_id.clone(), added_model); - } + let result = FlowModelServ::copy_models_by_template_id(&from_template_id.0, &to_template_id.0, &funs, &ctx.0).await?; funs.commit().await?; ctx.0.execute_task().await?; TardisResp::ok(result) @@ -259,7 +214,8 @@ impl FlowCiModelApi { warn!("ci exist_rel_by_template_ids req: {:?}", req.0); let support_tags = req.0.support_tags; let result = join_all( - req.0.rel_tag_by_template_ids + req.0 + .rel_tag_by_template_ids .iter() .map(|(rel_template_id, current_tags)| async { // 当前模板tag和需要支持的tag取交集,得到当前模板tag中需要检查的tag列表 diff --git a/backend/middlewares/flow/src/api/ct/flow_ct_model_api.rs b/backend/middlewares/flow/src/api/ct/flow_ct_model_api.rs index 4c2511a6c..3b1ce31d4 100644 --- a/backend/middlewares/flow/src/api/ct/flow_ct_model_api.rs +++ b/backend/middlewares/flow/src/api/ct/flow_ct_model_api.rs @@ -82,14 +82,7 @@ impl FlowCtModelApi { ) .await?; } - FlowInstServ::batch_update_when_switch_model( - &new_model, - req.0.rel_template_id.clone(), - None, - &funs, - &ctx.0, - ) - .await?; + FlowInstServ::batch_update_when_switch_model(&new_model, req.0.rel_template_id.clone(), None, &funs, &ctx.0).await?; result.insert(rel_model_id.clone(), new_model); } funs.commit().await?; diff --git a/backend/middlewares/flow/src/domain/flow_state.rs b/backend/middlewares/flow/src/domain/flow_state.rs index 0f3777091..b2551fc34 100644 --- a/backend/middlewares/flow/src/domain/flow_state.rs +++ b/backend/middlewares/flow/src/domain/flow_state.rs @@ -78,4 +78,9 @@ pub struct Model { #[fill_ctx(fill = "own_paths")] pub own_paths: String, + + /// Whether it is a mainstream process / 是否是主流程 + /// + #[index] + pub main: bool, } diff --git a/backend/middlewares/flow/src/dto.rs b/backend/middlewares/flow/src/dto.rs index c1388fac3..9e218d9d4 100644 --- a/backend/middlewares/flow/src/dto.rs +++ b/backend/middlewares/flow/src/dto.rs @@ -1,3 +1,4 @@ +pub mod flow_cond_dto; pub mod flow_config_dto; pub mod flow_external_dto; pub mod flow_inst_dto; diff --git a/backend/middlewares/flow/src/dto/flow_cond_dto.rs b/backend/middlewares/flow/src/dto/flow_cond_dto.rs new file mode 100644 index 000000000..b22583b38 --- /dev/null +++ b/backend/middlewares/flow/src/dto/flow_cond_dto.rs @@ -0,0 +1,169 @@ +//! Basic DTOs +//! +//! 基础的DTOs +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; +use strum::Display; +use tardis::{basic::result::TardisResult, serde_json::Value}; + +use tardis::web::poem_openapi; + +/// Basic query condition object +/// +/// 基础的查询条件对象 +#[derive(Serialize, Deserialize, Debug, Clone, poem_openapi::Object)] +pub struct BasicQueryCondInfo { + /// Query field + #[oai(validator(min_length = "1"))] + pub field: String, + /// Query operator + pub op: BasicQueryOpKind, + pub op_text: Option, + /// Query value + pub value: Value, +} + +impl BasicQueryCondInfo { + /// Check if the ``check_vars`` passed in meet the conditions in ``conds`` + /// + /// 检查传入的 ``check_vars`` 是否满足 ``conds`` 中的条件 + /// + /// The outer level is the `OR` relationship, the inner level is the `AND` relationship + pub fn check_or_and_conds(conds: &[Vec], check_vars: &HashMap) -> TardisResult { + let is_match = conds.iter().any(|and_conds| { + and_conds.iter().all(|cond| match check_vars.get(&cond.field) { + Some(check_val) => match &cond.op { + BasicQueryOpKind::Eq => &cond.value == check_val, + BasicQueryOpKind::Ne => &cond.value != check_val, + BasicQueryOpKind::Gt => { + if cond.value.is_f64() && check_val.is_f64() { + cond.value.as_f64().unwrap_or(0.0) < check_val.as_f64().unwrap_or(0.0) + } else if cond.value.is_i64() && check_val.is_i64() { + cond.value.as_i64().unwrap_or(0) < check_val.as_i64().unwrap_or(0) + } else if cond.value.is_u64() && check_val.is_u64() { + cond.value.as_u64().unwrap_or(0) < check_val.as_u64().unwrap_or(0) + } else { + false + } + } + BasicQueryOpKind::Ge => { + if cond.value.is_f64() && check_val.is_f64() { + cond.value.as_f64().unwrap_or(0.0) <= check_val.as_f64().unwrap_or(0.0) + } else if cond.value.is_i64() && check_val.is_i64() { + cond.value.as_i64().unwrap_or(0) <= check_val.as_i64().unwrap_or(0) + } else if cond.value.is_u64() && check_val.is_u64() { + cond.value.as_u64().unwrap_or(0) <= check_val.as_u64().unwrap_or(0) + } else { + false + } + } + BasicQueryOpKind::Lt => { + if cond.value.is_f64() && check_val.is_f64() { + cond.value.as_f64().unwrap_or(0.0) > check_val.as_f64().unwrap_or(0.0) + } else if cond.value.is_i64() && check_val.is_i64() { + cond.value.as_i64().unwrap_or(0) > check_val.as_i64().unwrap_or(0) + } else if cond.value.is_u64() && check_val.is_u64() { + cond.value.as_u64().unwrap_or(0) > check_val.as_u64().unwrap_or(0) + } else { + false + } + } + BasicQueryOpKind::Le => { + if cond.value.is_f64() && check_val.is_f64() { + cond.value.as_f64().unwrap_or(0.0) >= check_val.as_f64().unwrap_or(0.0) + } else if cond.value.is_i64() && check_val.is_i64() { + cond.value.as_i64().unwrap_or(0) >= check_val.as_i64().unwrap_or(0) + } else if cond.value.is_u64() && check_val.is_u64() { + cond.value.as_u64().unwrap_or(0) >= check_val.as_u64().unwrap_or(0) + } else { + false + } + } + BasicQueryOpKind::Like + | BasicQueryOpKind::LLike + | BasicQueryOpKind::RLike + | BasicQueryOpKind::NotLike + | BasicQueryOpKind::NotLLike + | BasicQueryOpKind::NotRLike => { + check_val.as_str().map(|check_val_str| cond.value.as_str().map(|cond_val_str| check_val_str.contains(cond_val_str)).unwrap_or(false)).unwrap_or(false) + } + BasicQueryOpKind::In => check_val + .as_array() + .map(|check_val_arr| { + if cond.value.is_array() { + cond.value.as_array().unwrap_or(&vec![]).iter().any(|item| check_val_arr.contains(item)) + } else { + check_val_arr.contains(&cond.value) + } + }) + .unwrap_or(false), + BasicQueryOpKind::NotIn => check_val.as_array().map(|check_val_arr| check_val_arr.contains(&cond.value)).unwrap_or(false), + BasicQueryOpKind::IsNull => false, + BasicQueryOpKind::IsNotNull => false, + BasicQueryOpKind::IsNullOrEmpty => false, + }, + None => false, + }) + }); + Ok(is_match) + } +} + +/// Basic query operator +/// +/// 基础查询操作符 +#[derive(Display, Clone, Debug, PartialEq, Eq, Deserialize, Serialize, poem_openapi::Enum)] +pub enum BasicQueryOpKind { + #[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 = "l_like")] + #[oai(rename = "l_like")] + LLike, + #[serde(rename = "not_l_like")] + #[oai(rename = "not_l_like")] + NotLLike, + #[serde(rename = "r_like")] + #[oai(rename = "r_like")] + RLike, + #[serde(rename = "not_r_like")] + #[oai(rename = "not_r_like")] + NotRLike, + #[serde(rename = "in")] + #[oai(rename = "in")] + In, + #[serde(rename = "not_in")] + #[oai(rename = "not_in")] + NotIn, + #[serde(rename = "is_null")] + #[oai(rename = "is_null")] + IsNull, + #[serde(rename = "is_not_null")] + #[oai(rename = "is_not_null")] + IsNotNull, + #[serde(rename = "is_null_or_empty")] + #[oai(rename = "is_null_or_empty")] + IsNullOrEmpty, +} diff --git a/backend/middlewares/flow/src/dto/flow_inst_dto.rs b/backend/middlewares/flow/src/dto/flow_inst_dto.rs index d23955aa4..1d7ccd1a8 100644 --- a/backend/middlewares/flow/src/dto/flow_inst_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_inst_dto.rs @@ -25,6 +25,10 @@ pub struct FlowInstStartReq { pub create_vars: Option>, /// 触发的动作ID pub transition_id: Option, + /// 创建时修改的参数列表 + pub vars: Option>, + /// 日志文本 + pub log_text: Option, } #[derive(Serialize, Deserialize, Debug, poem_openapi::Object)] @@ -240,7 +244,7 @@ pub struct FlowInstArtifacts { pub curr_vars: Option>, // 当前参数列表 pub prev_non_auto_state_id: Option>, // 上一个非自动节点ID列表 pub prev_non_auto_account_id: Option, // 上一个节点操作人ID - pub state: Option, // 状态 + pub state: Option, // 状态 } // 流程实例中数据存储更新 @@ -679,4 +683,4 @@ pub struct FlowInstSearchResp { pub own_paths: String, pub tag: String, -} \ No newline at end of file +} diff --git a/backend/middlewares/flow/src/dto/flow_state_dto.rs b/backend/middlewares/flow/src/dto/flow_state_dto.rs index 0d6815f45..2562f0da5 100644 --- a/backend/middlewares/flow/src/dto/flow_state_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_state_dto.rs @@ -35,6 +35,7 @@ pub struct FlowStateAddReq { pub kind_conf: Option, pub template: Option, + pub main: Option, #[oai(validator(min_length = "2", max_length = "255"))] pub rel_state_id: Option, @@ -299,6 +300,7 @@ pub struct FlowStateDetailResp { pub kind_conf: Option, pub template: bool, + pub main: bool, pub rel_state_id: String, pub tags: String, @@ -374,6 +376,7 @@ pub struct FlowStateFilterReq { pub tag: Option, pub state_kind: Option, pub template: Option, + pub main: Option, pub flow_version_ids: Option>, } @@ -434,6 +437,7 @@ pub struct FlowStateAggResp { pub tags: String, pub scope_level: RbumScopeLevelKind, pub disabled: bool, + pub main: bool, pub transitions: Vec, } #[derive(poem_openapi::Object, Serialize, Deserialize, Debug, Clone)] diff --git a/backend/middlewares/flow/src/dto/flow_transition_dto.rs b/backend/middlewares/flow/src/dto/flow_transition_dto.rs index 65c548745..682d3cb21 100644 --- a/backend/middlewares/flow/src/dto/flow_transition_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_transition_dto.rs @@ -1,4 +1,3 @@ -use bios_basic::dto::BasicQueryCondInfo; use serde::{Deserialize, Serialize}; use strum::Display; use tardis::{ @@ -9,7 +8,7 @@ use tardis::{ TardisFuns, }; -use super::flow_var_dto::FlowVarInfo; +use super::{flow_cond_dto::BasicQueryCondInfo, flow_var_dto::FlowVarInfo}; /// 添加动作 #[derive(Serialize, Deserialize, Debug, Default, Clone, poem_openapi::Object)] @@ -666,10 +665,8 @@ impl FlowTransitionFrontActionInfoRelevanceRelation { return false; } left_value >= interval[0] && left_value <= interval[1] - }, - FlowTransitionFrontActionInfoRelevanceRelation::IsNotNull => { - !left_value.is_empty() - }, + } + FlowTransitionFrontActionInfoRelevanceRelation::IsNotNull => !left_value.as_str().to_string().is_empty(), } } } @@ -687,6 +684,9 @@ pub enum FlowTransitionFrontActionRightValue { /// 当前时间 #[oai(rename = "real_time")] RealTime, + /// 无 + #[oai(rename = "none")] + None, } /// 工作流模型过滤器 diff --git a/backend/middlewares/flow/src/serv.rs b/backend/middlewares/flow/src/serv.rs index 1965a8f24..b5b052b65 100644 --- a/backend/middlewares/flow/src/serv.rs +++ b/backend/middlewares/flow/src/serv.rs @@ -3,9 +3,9 @@ pub mod flow_config_serv; pub mod flow_event_serv; pub mod flow_external_serv; pub mod flow_inst_serv; +pub mod flow_log_serv; pub mod flow_model_serv; pub mod flow_model_version_serv; pub mod flow_rel_serv; pub mod flow_state_serv; pub mod flow_transition_serv; -pub mod flow_log_serv; diff --git a/backend/middlewares/flow/src/serv/clients.rs b/backend/middlewares/flow/src/serv/clients.rs index e9f788cec..64f607387 100644 --- a/backend/middlewares/flow/src/serv/clients.rs +++ b/backend/middlewares/flow/src/serv/clients.rs @@ -1,3 +1,3 @@ +pub mod kv_client; pub mod log_client; pub mod search_client; -pub mod kv_client; diff --git a/backend/middlewares/flow/src/serv/clients/kv_client.rs b/backend/middlewares/flow/src/serv/clients/kv_client.rs index c88bef23d..d5eb32591 100644 --- a/backend/middlewares/flow/src/serv/clients/kv_client.rs +++ b/backend/middlewares/flow/src/serv/clients/kv_client.rs @@ -1,13 +1,17 @@ use bios_sdk_invoke::clients::spi_kv_client::SpiKvClient; -use tardis::{basic::{dto::TardisContext, result::TardisResult}, TardisFunsInst}; - +use tardis::{ + basic::{dto::TardisContext, result::TardisResult}, + TardisFunsInst, +}; pub struct FlowKvClient; impl FlowKvClient { pub async fn get_account_name(account_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult { - let account_name = SpiKvClient::get_item(format!("__k_n__:iam_account:{}", account_id), None, funs, ctx).await? - .map(|resp| resp.value.as_str().unwrap_or("").to_string()).unwrap_or_default(); + let account_name = SpiKvClient::get_item(format!("__k_n__:iam_account:{}", account_id), None, funs, ctx) + .await? + .map(|resp| resp.value.as_str().unwrap_or("").to_string()) + .unwrap_or_default(); Ok(account_name) } -} \ No newline at end of file +} diff --git a/backend/middlewares/flow/src/serv/clients/log_client.rs b/backend/middlewares/flow/src/serv/clients/log_client.rs index a42b74e31..a01e306fc 100644 --- a/backend/middlewares/flow/src/serv/clients/log_client.rs +++ b/backend/middlewares/flow/src/serv/clients/log_client.rs @@ -22,6 +22,7 @@ pub struct LogParamContent { pub name: Option, pub sub_kind: Option, pub sub_id: Option, + pub sub_op: Option, pub old_content: String, pub new_content: String, pub detail: Option, @@ -47,12 +48,14 @@ pub struct LogParamExt { pub enum LogParamExtSceneKind { ApprovalFlow, Dynamic, + Detail, } impl From for String { fn from(val: LogParamExtSceneKind) -> Self { match val { LogParamExtSceneKind::Dynamic => "dynamic".to_string(), LogParamExtSceneKind::ApprovalFlow => "approval_flow".to_string(), + LogParamExtSceneKind::Detail => "detail".to_string(), } } } diff --git a/backend/middlewares/flow/src/serv/clients/search_client.rs b/backend/middlewares/flow/src/serv/clients/search_client.rs index 530d5a3f6..a2e72aba9 100644 --- a/backend/middlewares/flow/src/serv/clients/search_client.rs +++ b/backend/middlewares/flow/src/serv/clients/search_client.rs @@ -7,7 +7,8 @@ use bios_sdk_invoke::{ spi_search_client::SpiSearchClient, }, dto::search_item_dto::{ - AdvSearchItemQueryReq, BasicQueryCondInfo, BasicQueryOpKind, SearchItemAddReq, SearchItemModifyReq, SearchItemQueryReq, SearchItemSearchCtxReq, SearchItemSearchPageReq, SearchItemSearchReq, SearchItemSearchResp, SearchItemVisitKeysReq + AdvSearchItemQueryReq, BasicQueryCondInfo, BasicQueryOpKind, SearchItemAddReq, SearchItemModifyReq, SearchItemQueryReq, SearchItemSearchCtxReq, SearchItemSearchPageReq, + SearchItemSearchReq, SearchItemSearchResp, SearchItemVisitKeysReq, }, }; use itertools::Itertools; @@ -37,7 +38,7 @@ use crate::{ }; const SEARCH_MODEL_TAG: &str = "flow_model"; -const SEARCH_INSTANCE_TAG: &str = "flow_inst"; +const SEARCH_INSTANCE_TAG: &str = "flow_approve_inst"; pub struct FlowSearchClient; @@ -263,12 +264,7 @@ impl FlowSearchClient { own_paths: "".to_string(), ..ctx.clone() }; - let inst_resp = FlowInstServ::get( - inst_id, - funs, - &mock_ctx, - ) - .await?; + let inst_resp = FlowInstServ::get(inst_id, funs, &mock_ctx).await?; ctx.add_async_task(Box::new(|| { Box::pin(async move { let task_handle = tokio::spawn(async move { @@ -310,6 +306,8 @@ impl FlowSearchClient { "current_state_kind": inst_resp.current_state_kind, "rel_business_obj_id": inst_resp.rel_business_obj_id, "finish_time": inst_resp.finish_time, + "op_time": inst_resp.update_time, + "state": inst_resp.artifacts.as_ref().map(|artifacts| artifacts.state.clone().unwrap_or_default()), "rel_transition": inst_resp.rel_transition.clone().unwrap_or_default().to_string(), "his_operators": inst_resp.artifacts.as_ref().map(|artifacts| artifacts.his_operators.clone().unwrap_or_default()), "curr_operators": inst_resp.artifacts.as_ref().map(|artifacts| artifacts.curr_operators.clone().unwrap_or_default()), @@ -349,6 +347,8 @@ impl FlowSearchClient { "current_state_kind": inst_resp.current_state_kind, "rel_business_obj_id": inst_resp.rel_business_obj_id, "finish_time": inst_resp.finish_time, + "op_time": inst_resp.update_time, + "state": inst_resp.artifacts.as_ref().map(|artifacts| artifacts.state.clone().unwrap_or_default()), "rel_transition": inst_resp.rel_transition.clone().unwrap_or_default().to_string(), "his_operators": inst_resp.artifacts.as_ref().map(|artifacts| artifacts.his_operators.clone().unwrap_or_default()), "curr_operators": inst_resp.artifacts.as_ref().map(|artifacts| artifacts.curr_operators.clone().unwrap_or_default()), @@ -384,7 +384,7 @@ impl FlowSearchClient { } let mut query = SearchItemQueryReq::default(); let mut adv_query = vec![]; - + if !guard_conf.guard_by_spec_account_ids.is_empty() { query.keys = Some(guard_conf.guard_by_spec_account_ids.clone().into_iter().map(|account_id| account_id.into()).collect_vec()); } @@ -393,22 +393,36 @@ impl FlowSearchClient { adv_query.push(AdvSearchItemQueryReq { group_by_or: Some(true), ext_by_or: Some(true), - ext: Some(guard_conf.guard_by_spec_org_ids.clone().into_iter().map(|org_id| BasicQueryCondInfo { - field: "dept_id".to_string(), - op: BasicQueryOpKind::In, - value: org_id.to_json().unwrap_or(json!("")), - }).collect_vec()), + ext: Some( + guard_conf + .guard_by_spec_org_ids + .clone() + .into_iter() + .map(|org_id| BasicQueryCondInfo { + field: "dept_id".to_string(), + op: BasicQueryOpKind::In, + value: org_id.to_json().unwrap_or(json!("")), + }) + .collect_vec(), + ), }); } if !guard_conf.guard_by_spec_role_ids.is_empty() { adv_query.push(AdvSearchItemQueryReq { group_by_or: Some(true), ext_by_or: Some(true), - ext: Some(guard_conf.guard_by_spec_role_ids.clone().into_iter().map(|role_id| BasicQueryCondInfo { - field: "role_id".to_string(), - op: BasicQueryOpKind::In, - value: role_id.to_json().unwrap_or(json!("")), - }).collect_vec()), + ext: Some( + guard_conf + .guard_by_spec_role_ids + .clone() + .into_iter() + .map(|role_id| BasicQueryCondInfo { + field: "role_id".to_string(), + op: BasicQueryOpKind::In, + value: role_id.to_json().unwrap_or(json!("")), + }) + .collect_vec(), + ), }); } if !adv_query.is_empty() { @@ -432,7 +446,8 @@ impl FlowSearchClient { ctx, ) .await? - .map(|result| result.records.into_iter().map(|record| record.key).collect_vec()).unwrap_or_default(); + .map(|result| result.records.into_iter().map(|record| record.key).collect_vec()) + .unwrap_or_default(); debug!("flow search_guard_account_num result : {:?}", result); Ok(result) } diff --git a/backend/middlewares/flow/src/serv/flow_event_serv.rs b/backend/middlewares/flow/src/serv/flow_event_serv.rs index 38b123533..53ae91e5b 100644 --- a/backend/middlewares/flow/src/serv/flow_event_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_event_serv.rs @@ -157,6 +157,13 @@ impl FlowEventServ { Ok(false) } } + FlowTransitionFrontActionRightValue::None => { + if let Some(left_value) = current_vars.get(&condition.left_value) { + Ok(condition.relevance_relation.check_conform(left_value.as_str().unwrap_or(left_value.to_string().as_str()).to_string(), "".to_string())) + } else { + Ok(false) + } + } } } @@ -484,7 +491,8 @@ impl FlowEventServ { Query::select() .columns([flow_inst::Column::CurrentStateId, flow_inst::Column::RelBusinessObjId]) .from(flow_inst::Entity) - .and_where(Expr::col(flow_inst::Column::RelBusinessObjId).is_in(rel_bus_obj_ids)), + .and_where(Expr::col(flow_inst::Column::RelBusinessObjId).is_in(rel_bus_obj_ids)) + .and_where(Expr::col(flow_inst::Column::Main).eq(true)), ) .await?; if rel_bus_obj_ids.len() != rel_insts.len() { @@ -511,7 +519,7 @@ impl FlowEventServ { funs: &TardisFunsInst, ctx: &TardisContext, ) -> TardisResult<()> { - let insts = FlowInstServ::find_detail(rel_inst_ids, funs, ctx).await?; + let insts = FlowInstServ::find_detail(rel_inst_ids, None, None, funs, ctx).await?; for rel_inst in insts { // find transition let flow_version = FlowModelVersionServ::get_item( diff --git a/backend/middlewares/flow/src/serv/flow_inst_serv.rs b/backend/middlewares/flow/src/serv/flow_inst_serv.rs index feee41ea0..e9a627d03 100644 --- a/backend/middlewares/flow/src/serv/flow_inst_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_inst_serv.rs @@ -5,12 +5,12 @@ use std::{ use async_recursion::async_recursion; use bios_basic::{ - dto::BasicQueryCondInfo, rbum::{ - dto::rbum_filer_dto::RbumBasicFilterReq, serv::{ - rbum_crud_serv::{ID_FIELD, NAME_FIELD, REL_DOMAIN_ID_FIELD, REL_KIND_ID_FIELD}, + dto::rbum_filer_dto::RbumBasicFilterReq, + serv::{ + rbum_crud_serv::{CREATE_TIME_FIELD, ID_FIELD, NAME_FIELD, REL_DOMAIN_ID_FIELD, REL_KIND_ID_FIELD, UPDATE_TIME_FIELD}, rbum_item_serv::{RbumItemCrudOperation, RBUM_ITEM_TABLE}, - } + }, }, }; use bios_sdk_invoke::dto::search_item_dto::{SearchItemQueryReq, SearchItemSearchCtxReq, SearchItemSearchPageReq, SearchItemSearchReq}; @@ -22,10 +22,10 @@ use tardis::{ db::sea_orm::{ self, sea_query::{Alias, Cond, Expr, Query, SelectStatement}, - JoinType, Set, + JoinType, Order, Set, }, futures_util::future::join_all, - log::{debug, error}, + log::error, serde_json::Value, web::web_resp::TardisPage, TardisFuns, TardisFunsInst, @@ -34,18 +34,16 @@ use tardis::{ use crate::{ domain::{flow_inst, flow_model_version}, dto::{ - flow_external_dto::{FlowExternalCallbackOp, FlowExternalParams}, - flow_inst_dto::{ - FLowInstStateApprovalConf, FLowInstStateConf, FLowInstStateFormConf, FlowApprovalResultKind, FlowInstAbortReq, FlowInstArtifacts, FlowInstArtifactsModifyReq, FlowInstBatchBindReq, FlowInstBatchBindResp, FlowInstCommentInfo, FlowInstCommentReq, FlowInstDetailResp, FlowInstFilterReq, FlowInstFindNextTransitionResp, FlowInstFindNextTransitionsReq, FlowInstFindStateAndTransitionsReq, FlowInstFindStateAndTransitionsResp, FlowInstOperateReq, FlowInstQueryKind, FlowInstSearchPageReq, FlowInstSearchReq, FlowInstSearchResp, FlowInstSearchSortReq, FlowInstStartReq, FlowInstStateKind, FlowInstSummaryResp, FlowInstSummaryResult, FlowInstTransferReq, FlowInstTransferResp, FlowInstTransitionInfo, FlowOperationContext - }, - flow_model_dto::{FlowModelAggResp, FlowModelRelTransitionExt}, - flow_model_version_dto::{FlowModelVersionDetailResp, FlowModelVersionFilterReq}, - flow_state_dto::{ - FLowStateKindConf, FlowStateCountersignKind, FlowStateFilterReq, FlowStateKind, FlowStateOperatorKind, FlowStateRelModelExt, - FlowStatusAutoStrategyKind, FlowStatusMultiApprovalKind, FlowSysStateKind, - }, - flow_transition_dto::FlowTransitionDetailResp, - flow_var_dto::FillType, + flow_cond_dto::BasicQueryCondInfo, flow_external_dto::{FlowExternalCallbackOp, FlowExternalParams}, flow_inst_dto::{ + FLowInstStateApprovalConf, FLowInstStateConf, FLowInstStateFormConf, FlowApprovalResultKind, FlowInstAbortReq, FlowInstArtifacts, FlowInstArtifactsModifyReq, + FlowInstBatchBindReq, FlowInstBatchBindResp, FlowInstCommentInfo, FlowInstCommentReq, FlowInstDetailResp, FlowInstFilterReq, FlowInstFindNextTransitionResp, + FlowInstFindNextTransitionsReq, FlowInstFindStateAndTransitionsReq, FlowInstFindStateAndTransitionsResp, FlowInstOperateReq, FlowInstQueryKind, FlowInstSearchPageReq, + FlowInstSearchReq, FlowInstSearchResp, FlowInstSearchSortReq, FlowInstStartReq, FlowInstStateKind, FlowInstSummaryResp, FlowInstSummaryResult, FlowInstTransferReq, + FlowInstTransferResp, FlowInstTransitionInfo, FlowOperationContext, + }, flow_model_dto::{FlowModelAggResp, FlowModelRelTransitionExt}, flow_model_version_dto::{FlowModelVersionDetailResp, FlowModelVersionFilterReq}, flow_state_dto::{ + FLowStateKindConf, FlowStateCountersignKind, FlowStateFilterReq, FlowStateKind, FlowStateOperatorKind, FlowStateRelModelExt, FlowStatusAutoStrategyKind, + FlowStatusMultiApprovalKind, FlowSysStateKind, + }, flow_transition_dto::FlowTransitionDetailResp, flow_var_dto::FillType }, flow_constants, helper::loop_check_helper, @@ -53,9 +51,13 @@ use crate::{ }; use super::{ - clients::{ - log_client::LogParamOp, search_client::FlowSearchClient - }, flow_event_serv::FlowEventServ, flow_external_serv::FlowExternalServ, flow_log_serv::FlowLogServ, flow_model_version_serv::FlowModelVersionServ, flow_rel_serv::{FlowRelKind, FlowRelServ}, flow_transition_serv::FlowTransitionServ + clients::{log_client::LogParamOp, search_client::FlowSearchClient}, + flow_event_serv::FlowEventServ, + flow_external_serv::FlowExternalServ, + flow_log_serv::FlowLogServ, + flow_model_version_serv::FlowModelVersionServ, + flow_rel_serv::{FlowRelKind, FlowRelServ}, + flow_transition_serv::FlowTransitionServ, }; pub struct FlowInstServ; @@ -167,6 +169,8 @@ impl FlowInstServ { &inst_id, &FlowInstArtifactsModifyReq { curr_vars: start_req.create_vars.clone(), + add_his_operator: Some(ctx.owner.clone()), + form_state_map: Some(start_req.vars.clone().unwrap_or_default()), ..Default::default() }, funs, @@ -177,6 +181,7 @@ impl FlowInstServ { let inst = Self::get(&inst_id, funs, ctx).await?; FlowLogServ::add_start_log(start_req, &inst, &create_vars, &flow_model, ctx).await?; FlowLogServ::add_start_dynamic_log(start_req, &inst, &create_vars, &flow_model, ctx).await?; + FlowLogServ::add_start_business_log(start_req, &inst, &create_vars, &flow_model, ctx).await?; Self::when_enter_state(&inst, &flow_model.init_state_id, &flow_model.id, funs, ctx).await?; Self::do_request_webhook( @@ -185,7 +190,7 @@ impl FlowInstServ { ) .await?; // 自动流转 - Self::auto_transfer(&inst, loop_check_helper::InstancesTransition::default(), funs, ctx).await?; + Self::auto_transfer(&inst.id, loop_check_helper::InstancesTransition::default(), funs, ctx).await?; FlowSearchClient::async_add_or_modify_instance_search(&inst_id, Box::new(false), funs, ctx).await?; Ok(inst_id) @@ -200,7 +205,6 @@ impl FlowInstServ { || rel_business_obj.own_paths.is_none() || rel_business_obj.owner.is_none() { - debug!("rel_business_obj: {:?}", rel_business_obj); return Err(funs.err().not_found("flow_inst_serv", "batch_bind", "req is valid", "")); } current_ctx.own_paths = rel_business_obj.own_paths.clone().unwrap_or_default(); @@ -344,18 +348,33 @@ impl FlowInstServ { } pub async fn find_detail_items(filter: &FlowInstFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult> { - Self::find_detail(Self::find_ids(filter, funs, ctx).await?, funs, ctx).await + Self::find_detail(Self::find_ids(filter, funs, ctx).await?, None, None, funs, ctx).await } - pub async fn paginate_detail_items(filter: &FlowInstFilterReq, page_number: u32, page_size: u32, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult> { + pub async fn paginate_detail_items( + filter: &FlowInstFilterReq, + page_number: u32, + page_size: u32, + desc_by_create: Option, + desc_by_update: Option, + funs: &TardisFunsInst, + ctx: &TardisContext, + ) -> TardisResult> { let inst_ids = Self::find_ids(filter, funs, ctx).await?; let total_size = inst_ids.len() as usize; - let records = Self::find_detail(inst_ids[(((page_number -1) * page_size) as usize).min(total_size)..((page_number * page_size) as usize).min(total_size)].to_vec(), funs, ctx).await?; + let records = Self::find_detail( + inst_ids[(((page_number - 1) * page_size) as usize).min(total_size)..((page_number * page_size) as usize).min(total_size)].to_vec(), + desc_by_create, + desc_by_update, + funs, + ctx, + ) + .await?; Ok(TardisPage { page_size: page_size as u64, page_number: page_number as u64, total_size: total_size as u64, - records + records, }) } @@ -422,7 +441,7 @@ impl FlowInstServ { } pub async fn get(flow_inst_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult { - let mut flow_insts = Self::find_detail(vec![flow_inst_id.to_string()], funs, ctx).await?; + let mut flow_insts = Self::find_detail(vec![flow_inst_id.to_string()], None, None, funs, ctx).await?; if flow_insts.len() == 1 { Ok(flow_insts.pop().unwrap()) } else { @@ -430,7 +449,13 @@ impl FlowInstServ { } } - pub async fn find_detail(flow_inst_ids: Vec, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult> { + pub async fn find_detail( + flow_inst_ids: Vec, + desc_sort_by_create: Option, + desc_sort_by_update: Option, + funs: &TardisFunsInst, + ctx: &TardisContext, + ) -> TardisResult> { #[derive(sea_orm::FromQueryResult)] pub struct FlowInstDetailResult { pub id: String, @@ -575,7 +600,12 @@ impl FlowInstServ { ) .and_where(Expr::col((flow_inst::Entity, flow_inst::Column::Id)).is_in(flow_inst_ids)) .and_where(Expr::col((flow_inst::Entity, flow_inst::Column::OwnPaths)).like(format!("{}%", ctx.own_paths))); - + if let Some(sort) = desc_sort_by_create { + query.order_by((flow_inst::Entity, CREATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc }); + } + if let Some(sort) = desc_sort_by_update { + query.order_by((flow_inst::Entity, UPDATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc }); + } let flow_insts = funs.db().find_dtos::(&query).await?; let result = flow_insts .into_iter() @@ -717,7 +747,7 @@ impl FlowInstServ { funs: &TardisFunsInst, ctx: &TardisContext, ) -> TardisResult> { - let flow_insts = Self::find_detail(find_req.iter().map(|req| req.flow_inst_id.to_string()).collect_vec(), funs, ctx).await?; + let flow_insts = Self::find_detail(find_req.iter().map(|req| req.flow_inst_id.to_string()).collect_vec(), None, None, funs, ctx).await?; if flow_insts.len() != find_req.len() { return Err(funs.err().not_found("flow_inst", "find_state_and_next_transitions", "some flow instances not found", "404-flow-inst-not-found")); } @@ -858,7 +888,7 @@ impl FlowInstServ { } let result = Self::do_transfer(flow_inst_detail, transfer_req, skip_filter, callback_kind, funs, ctx).await; - Self::auto_transfer(flow_inst_detail, modified_instance_transations_cp.clone(), funs, ctx).await?; + Self::auto_transfer(&flow_inst_detail.id, modified_instance_transations_cp.clone(), funs, ctx).await?; if flow_inst_detail.main { FlowSearchClient::async_add_or_modify_instance_search(&flow_inst_detail.id, Box::new(true), funs, ctx).await?; @@ -866,8 +896,7 @@ impl FlowInstServ { let flow_transition_id = transfer_req.flow_transition_id.clone(); let ctx_cp = ctx.clone(); tardis::tokio::spawn(async move { - let mut funs = flow_constants::get_tardis_inst(); - funs.begin().await.unwrap(); + let funs = flow_constants::get_tardis_inst(); match FlowEventServ::do_post_change(&flow_inst_cp, &flow_transition_id, modified_instance_transations_cp.clone(), &ctx_cp, &funs).await { Ok(_) => {} Err(e) => error!("Flow Instance {} do_post_change error:{:?}", flow_inst_cp.id, e), @@ -876,7 +905,6 @@ impl FlowInstServ { Ok(_) => {} Err(e) => error!("Flow Instance {} do_front_change error:{:?}", flow_inst_cp.id, e), } - funs.commit().await.unwrap(); }); } @@ -1382,8 +1410,19 @@ impl FlowInstServ { ..Default::default() }; funs.db().update_one(flow_inst, ctx).await.unwrap(); - let curr_inst = Self::get(&flow_inst_detail.id, funs, ctx).await?; - FlowEventServ::do_front_change(&curr_inst, modified_instance_transations, ctx, funs).await?; + if flow_inst_detail.main { + let curr_inst = Self::get(&flow_inst_detail.id, funs, ctx).await?; + let ctx_cp = ctx.clone(); + let modified_instance_transations_cp = modified_instance_transations.clone(); + tardis::tokio::spawn(async move { + let funs = flow_constants::get_tardis_inst(); + match FlowEventServ::do_front_change(&curr_inst, modified_instance_transations_cp.clone(), &ctx_cp, &funs).await { + Ok(_) => {} + Err(e) => error!("Flow Instance {} do_front_change error:{:?}", curr_inst.id, e), + } + funs.commit().await.unwrap(); + }); + } Ok(()) } @@ -1517,6 +1556,20 @@ impl FlowInstServ { ..Default::default() }; funs.db().update_one(flow_inst, &mock_ctx).await.unwrap(); + let original_flow_state = FlowStateServ::get_item( + &inst.current_state_id, + &FlowStateFilterReq { + basic: RbumBasicFilterReq { + with_sub_own_paths: true, + ..Default::default() + }, + ..Default::default() + }, + funs, + &global_ctx, + ) + .await + .unwrap(); let next_flow_state = FlowStateServ::get_item( state_id, &FlowStateFilterReq { @@ -1536,13 +1589,13 @@ impl FlowInstServ { &inst.tag, &inst.id, &inst.rel_business_obj_id, - "".to_string(), - FlowSysStateKind::default(), next_flow_state.name.clone(), next_flow_state.sys_state, - "".to_string(), + original_flow_state.name.clone(), + original_flow_state.sys_state, + "UPDATE".to_string(), false, - Some(FlowExternalCallbackOp::Default), + Some(FlowExternalCallbackOp::Auto), ctx, funs, ) @@ -1557,11 +1610,12 @@ impl FlowInstServ { } pub async fn auto_transfer( - flow_inst_detail: &FlowInstDetailResp, + flow_inst_id: &str, modified_instance_transations: loop_check_helper::InstancesTransition, funs: &TardisFunsInst, ctx: &TardisContext, ) -> TardisResult<()> { + let flow_inst_detail = Self::get(flow_inst_id, funs, ctx).await?; let model_version = FlowModelVersionServ::get_item( &flow_inst_detail.rel_flow_version_id, &FlowModelVersionFilterReq { @@ -1576,7 +1630,7 @@ impl FlowInstServ { ctx, ) .await?; - let transition_ids = Self::do_find_next_transitions(flow_inst_detail, &model_version, None, &None, HashMap::default(), false, funs, ctx) + let transition_ids = Self::do_find_next_transitions(&flow_inst_detail, &model_version, None, &None, HashMap::default(), false, funs, ctx) .await? .next_flow_transitions .into_iter() @@ -1591,7 +1645,7 @@ impl FlowInstServ { }); if let Some(auto_transition) = auto_transition { Self::transfer( - flow_inst_detail, + &flow_inst_detail, &FlowInstTransferReq { flow_transition_id: auto_transition.id, message: None, @@ -1642,13 +1696,22 @@ impl FlowInstServ { }); } if form_conf.guard_by_assigned { - let _ = Self::get_main_inst_vars(flow_inst_detail, funs, ctx) - .await? + let _ = flow_inst_detail + .create_vars + .clone() + .unwrap_or_default() .get("assigned_to") .unwrap_or(&json!("")) - .as_str() - .unwrap_or_default() - .split(',') + .as_array() + .unwrap_or(&vec![flow_inst_detail + .create_vars + .clone() + .unwrap_or_default() + .get("assigned_to") + .unwrap_or(&json!("")) + .clone()]) + .iter() + .map(|v| v.as_str().unwrap_or("")) .collect_vec() .into_iter() .map(|str| { @@ -1669,6 +1732,31 @@ impl FlowInstServ { if let Some(next_transition) = FlowInstServ::find_next_transitions(flow_inst_detail, &FlowInstFindNextTransitionsReq { vars: None }, funs, ctx).await?.pop() { + FlowLogServ::add_operate_log( + &FlowInstOperateReq { + operate: FlowStateOperatorKind::Submit, + vars: None, + all_vars: None, + output_message: None, + operator: None, + log_text: None, + }, + flow_inst_detail, + LogParamOp::FormTransfer, + funs, + ctx, + ) + .await?; + Self::modify_inst_artifacts( + &flow_inst_detail.id, + &FlowInstArtifactsModifyReq { + state: Some(FlowInstStateKind::Form), + ..Default::default() + }, + funs, + ctx, + ) + .await?; Self::transfer( flow_inst_detail, &FlowInstTransferReq { @@ -1686,7 +1774,8 @@ impl FlowInstServ { } } FlowStatusAutoStrategyKind::SpecifyAgent => { - modify_req.curr_operators = Some(FlowSearchClient::search_guard_accounts(&form_conf.auto_transfer_when_empty_guard_custom_conf.clone().unwrap_or_default(), funs, ctx).await?); + modify_req.curr_operators = + Some(FlowSearchClient::search_guard_accounts(&form_conf.auto_transfer_when_empty_guard_custom_conf.clone().unwrap_or_default(), funs, ctx).await?); modify_req.prohibit_guard_conf_account_ids = Some(vec![]); Self::modify_inst_artifacts(&flow_inst_detail.id, &modify_req, funs, ctx).await?; } @@ -1709,13 +1798,22 @@ impl FlowInstServ { }); } if approval_conf.guard_by_assigned { - let _ = Self::get_main_inst_vars(flow_inst_detail, funs, ctx) - .await? + let _ = flow_inst_detail + .create_vars + .clone() + .unwrap_or_default() .get("assigned_to") .unwrap_or(&json!("")) - .as_str() - .unwrap_or_default() - .split(',') + .as_array() + .unwrap_or(&vec![flow_inst_detail + .create_vars + .clone() + .unwrap_or_default() + .get("assigned_to") + .unwrap_or(&json!("")) + .clone()]) + .iter() + .map(|v| v.as_str().unwrap_or("")) .collect_vec() .into_iter() .map(|str| { @@ -1730,16 +1828,40 @@ impl FlowInstServ { modify_req.curr_approval_total = Some(curr_approval_total); modify_req.curr_operators = Some(guard_accounts); modify_req.prohibit_guard_conf_account_ids = Some(vec![]); - + Self::modify_inst_artifacts(&flow_inst_detail.id, &modify_req, funs, ctx).await?; // 当操作人为空时的逻辑 - if curr_approval_total == 0 && approval_conf.auto_transfer_when_empty_kind.is_some() - { + if curr_approval_total == 0 && approval_conf.auto_transfer_when_empty_kind.is_some() { match approval_conf.auto_transfer_when_empty_kind.unwrap_or_default() { FlowStatusAutoStrategyKind::Autoskip => { if let Some(next_transition) = FlowInstServ::find_next_transitions(flow_inst_detail, &FlowInstFindNextTransitionsReq { vars: None }, funs, ctx).await?.pop() { + FlowLogServ::add_operate_log( + &FlowInstOperateReq { + operate: FlowStateOperatorKind::Pass, + vars: None, + all_vars: None, + output_message: None, + operator: None, + log_text: None, + }, + flow_inst_detail, + LogParamOp::ApprovalTransfer, + funs, + ctx, + ) + .await?; + Self::modify_inst_artifacts( + &flow_inst_detail.id, + &FlowInstArtifactsModifyReq { + state: Some(FlowInstStateKind::Form), + ..Default::default() + }, + funs, + ctx, + ) + .await?; Self::transfer( flow_inst_detail, &FlowInstTransferReq { @@ -1787,7 +1909,7 @@ impl FlowInstServ { None, &flow_inst_detail.rel_business_obj_id, &flow_inst_detail.id, - None, + Some(FlowExternalCallbackOp::Auto), None, None, None, @@ -1873,6 +1995,7 @@ impl FlowInstServ { FlowStateKind::Form => { prev_non_auto_state_id.push(state_id.to_string()); let modify_req = FlowInstArtifactsModifyReq { + curr_operators: Some(vec![]), prev_non_auto_state_id: Some(prev_non_auto_state_id), prev_non_auto_account_id: Some(ctx.owner.clone()), ..Default::default() @@ -1882,6 +2005,7 @@ impl FlowInstServ { FlowStateKind::Approval => { prev_non_auto_state_id.push(state_id.to_string()); let modify_req = FlowInstArtifactsModifyReq { + curr_operators: Some(vec![]), prev_non_auto_state_id: Some(prev_non_auto_state_id), prev_non_auto_account_id: Some(ctx.owner.clone()), ..Default::default() @@ -1927,7 +2051,10 @@ impl FlowInstServ { inst_artifacts.approval_total = Some(approval_total); } if let Some(form_state_vars) = modify_artifacts.form_state_map.clone() { - inst_artifacts.form_state_map.insert(inst.current_state_id.clone(), form_state_vars.clone()); + let vars_collect = inst_artifacts.form_state_map.entry(inst.current_state_id.clone()).or_default(); + for (key, value) in form_state_vars { + *vars_collect.entry(key.clone()).or_insert(json!({})) = value.clone(); + } } if let Some(state_id) = &modify_artifacts.clear_form_result { inst_artifacts.form_state_map.remove(state_id); @@ -1967,16 +2094,12 @@ impl FlowInstServ { FlowStateKind::Form => kind_conf.form.as_ref().map(|form| { let mut operators = HashMap::new(); let artifacts = artifacts.clone().unwrap_or_default(); - if artifacts.curr_operators.clone().unwrap_or_default().contains(&ctx.owner) && !artifacts.prohibit_guard_by_spec_account_ids.clone().unwrap_or_default().contains(&ctx.owner) { + if artifacts.curr_operators.clone().unwrap_or_default().contains(&ctx.owner) + && !artifacts.prohibit_guard_by_spec_account_ids.clone().unwrap_or_default().contains(&ctx.owner) + { operators.insert(FlowStateOperatorKind::Submit, form.submit_btn_name.clone()); if form.referral { - if let Some(referral_guard_custom_conf) = &form.referral_guard_custom_conf { - if referral_guard_custom_conf.check(ctx) { - operators.insert(FlowStateOperatorKind::Referral, "".to_string()); - } - } else { - operators.insert(FlowStateOperatorKind::Referral, "".to_string()); - } + operators.insert(FlowStateOperatorKind::Referral, "".to_string()); } } FLowInstStateConf { @@ -1997,13 +2120,7 @@ impl FlowInstServ { operators.insert(FlowStateOperatorKind::Overrule, approval.overrule_btn_name.clone()); operators.insert(FlowStateOperatorKind::Back, approval.back_btn_name.clone()); if approval.referral { - if let Some(referral_guard_custom_conf) = &approval.referral_guard_custom_conf { - if referral_guard_custom_conf.check(ctx) { - operators.insert(FlowStateOperatorKind::Referral, "".to_string()); - } - } else { - operators.insert(FlowStateOperatorKind::Referral, "".to_string()); - } + operators.insert(FlowStateOperatorKind::Referral, "".to_string()); } } if approval.revoke && ctx.owner == artifacts.prev_non_auto_account_id.unwrap_or_default() { @@ -2167,6 +2284,7 @@ impl FlowInstServ { &FlowInstArtifactsModifyReq { curr_operators: Some(curr_operators.into_iter().filter(|account_id| *account_id != ctx.owner.clone()).collect_vec()), add_approval_result: Some((ctx.owner.clone(), FlowApprovalResultKind::Pass)), + form_state_map: Some(operate_req.vars.clone().unwrap_or_default()), ..Default::default() }, funs, @@ -2192,6 +2310,7 @@ impl FlowInstServ { &inst.id, &FlowInstArtifactsModifyReq { state: Some(FlowInstStateKind::Pass), + curr_operators: Some(vec![]), ..Default::default() }, funs, @@ -2235,6 +2354,7 @@ impl FlowInstServ { &inst.id, &FlowInstArtifactsModifyReq { state: Some(FlowInstStateKind::Overrule), + curr_operators: Some(vec![]), ..Default::default() }, funs, @@ -2325,8 +2445,8 @@ impl FlowInstServ { if countersign_conf.most_percent.is_none() { return Ok(false); } - let pass_total = approval_total * countersign_conf.most_percent.unwrap_or_default() / 100; // 需满足通过的人员数量 - let overrule_total = approval_total - pass_total; // 需满足拒绝的人员数量 + let pass_total = (approval_total * countersign_conf.most_percent.unwrap_or_default() / 100) + 1; // 需满足通过的人员数量 + let overrule_total = approval_total - pass_total + 1; // 需满足拒绝的人员数量 if (kind == FlowApprovalResultKind::Pass && approval_result.get(&FlowApprovalResultKind::Pass.to_string()).cloned().unwrap_or_default().len() >= pass_total) // 要求大多数通过则通过人数达到通过的比例 || (kind == FlowApprovalResultKind::Overrule && approval_result.get(&FlowApprovalResultKind::Overrule.to_string()).cloned().unwrap_or_default().len() >= overrule_total) { @@ -2576,11 +2696,19 @@ impl FlowInstServ { } if query_kinds.contains(&FlowInstQueryKind::Form) { sql_vals.push(sea_orm::Value::from(ctx.owner.clone())); - or_where_fragments.push(format!("(current_state.state_kind = 'form' AND ({}.artifacts -> 'curr_operators' ? ${}))", table_alias_name, sql_vals.len())); + or_where_fragments.push(format!( + "(current_state.state_kind = 'form' AND ({}.artifacts -> 'curr_operators' ? ${}))", + table_alias_name, + sql_vals.len() + )); } if query_kinds.contains(&FlowInstQueryKind::Approval) { sql_vals.push(sea_orm::Value::from(ctx.owner.clone())); - or_where_fragments.push(format!("(current_state.state_kind = 'approval' AND ({}.artifacts -> 'curr_operators' ? ${}))", table_alias_name, sql_vals.len())); + or_where_fragments.push(format!( + "(current_state.state_kind = 'approval' AND ({}.artifacts -> 'curr_operators' ? ${}))", + table_alias_name, + sql_vals.len() + )); } if query_kinds.contains(&FlowInstQueryKind::History) { sql_vals.push(sea_orm::Value::from(ctx.owner.clone())); @@ -2681,16 +2809,6 @@ impl FlowInstServ { Ok(order_fragments) } - // 获取主实例的参数列表 - async fn get_main_inst_vars(flow_inst: &FlowInstDetailResp, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult> { - let main_inst_id = Self::get_inst_ids_by_rel_business_obj_id(vec![flow_inst.rel_business_obj_id.clone()], Some(true), funs, ctx) - .await? - .pop() - .ok_or_else(|| funs.err().not_found("flow_inst", "get_guard_by_assigned", "illegal response", "404-flow-inst-not-found"))?; - let main_inst = Self::get(&main_inst_id, funs, ctx).await?; - Ok(main_inst.current_vars.clone().unwrap_or_default()) - } - // 生成实例编码 async fn gen_inst_code(funs: &TardisFunsInst) -> TardisResult { let count = funs @@ -2711,16 +2829,11 @@ impl FlowInstServ { pub fn get_modify_vars(flow_inst_detail: &FlowInstDetailResp) -> HashMap { let mut vars_collect = HashMap::new(); if let Some(artifacts) = &flow_inst_detail.artifacts { - vars_collect = artifacts.curr_vars.clone().unwrap_or_default(); - let mut state_ids = vec![]; for tran in flow_inst_detail.transitions.clone().unwrap_or_default() { - let current_state_id = tran.from_state_id.clone().unwrap_or_default(); - if !state_ids.contains(¤t_state_id) { - state_ids.push(current_state_id.clone()); - if let Some(form_state_vars) = artifacts.form_state_map.get(¤t_state_id) { - for (key, value) in form_state_vars { - *vars_collect.entry(key.clone()).or_insert(json!({})) = value.clone(); - } + let from_state_id = tran.from_state_id.clone().unwrap_or_default(); + if let Some(form_state_vars) = artifacts.form_state_map.get(&from_state_id) { + for (key, value) in form_state_vars { + *vars_collect.entry(key.clone()).or_insert(json!({})) = value.clone(); } } } diff --git a/backend/middlewares/flow/src/serv/flow_log_serv.rs b/backend/middlewares/flow/src/serv/flow_log_serv.rs index b12a1b3c0..4b46bc4ef 100644 --- a/backend/middlewares/flow/src/serv/flow_log_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_log_serv.rs @@ -2,15 +2,31 @@ use std::collections::HashMap; use bios_basic::rbum::{dto::rbum_filer_dto::RbumBasicFilterReq, helper::rbum_scope_helper, rbum_enumeration::RbumScopeLevelKind, serv::rbum_item_serv::RbumItemCrudOperation}; use serde_json::Value; -use tardis::{basic::{dto::TardisContext, result::TardisResult}, TardisFuns, TardisFunsInst}; +use tardis::{ + basic::{dto::TardisContext, result::TardisResult}, + TardisFuns, TardisFunsInst, +}; -use crate::dto::{flow_inst_dto::{FlowInstDetailResp, FlowInstOperateReq, FlowInstStartReq}, flow_model_dto::{FlowModelDetailResp, FlowModelFilterReq}, flow_model_version_dto::FlowModelVersionFilterReq, flow_state_dto::{FlowStateFilterReq, FlowStateKind, FlowStateOperatorKind}}; +use crate::dto::{ + flow_inst_dto::{FlowInstDetailResp, FlowInstOperateReq, FlowInstStartReq}, + flow_model_dto::{FlowModelDetailResp, FlowModelFilterReq}, + flow_model_version_dto::FlowModelVersionFilterReq, + flow_state_dto::{FlowStateFilterReq, FlowStateKind, FlowStateOperatorKind}, +}; -use super::{clients::{kv_client::FlowKvClient, log_client::{FlowLogClient, LogParamContent, LogParamExt, LogParamExtSceneKind, LogParamOp, LogParamTag}}, flow_model_serv::FlowModelServ, flow_model_version_serv::FlowModelVersionServ, flow_state_serv::FlowStateServ}; +use super::{ + clients::{ + kv_client::FlowKvClient, + log_client::{FlowLogClient, LogParamContent, LogParamExt, LogParamExtSceneKind, LogParamOp, LogParamTag}, + }, + flow_model_serv::FlowModelServ, + flow_model_version_serv::FlowModelVersionServ, + flow_state_serv::FlowStateServ, +}; pub struct FlowLogServ; -impl FlowLogServ{ +impl FlowLogServ { // 添加审批流发起日志 pub async fn add_start_log( start_req: &FlowInstStartReq, @@ -48,7 +64,9 @@ impl FlowLogServ{ log_content.new_content = "".to_string(); } else { log_content.old_content = create_vars.get("content").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string()); - log_content.new_content = start_req.create_vars.clone().unwrap_or_default().get("content").map(|content| content.as_str().unwrap_or("").to_string()).unwrap_or_default(); + log_content.new_content = + start_req.create_vars.clone().unwrap_or_default().get("content").map(|content| content.as_str().unwrap_or("").to_string()).unwrap_or_default(); + log_content.detail = start_req.log_text.clone(); log_ext.include_detail = Some(true); } FlowLogClient::add_ctx_task( @@ -104,7 +122,9 @@ impl FlowLogServ{ log_content.new_content = "".to_string(); } else { log_content.old_content = create_vars.get("content").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string()); - log_content.new_content = start_req.create_vars.clone().unwrap_or_default().get("content").map(|content| content.as_str().unwrap_or("").to_string()).unwrap_or_default(); + log_content.new_content = + start_req.create_vars.clone().unwrap_or_default().get("content").map(|content| content.as_str().unwrap_or("").to_string()).unwrap_or_default(); + log_content.detail = start_req.log_text.clone(); log_ext.include_detail = Some(true); } FlowLogClient::add_ctx_task( @@ -122,6 +142,60 @@ impl FlowLogServ{ .await?; Ok(()) } + + // 添加审批流发起业务日志 + pub async fn add_start_business_log( + start_req: &FlowInstStartReq, + flow_inst_detail: &FlowInstDetailResp, + create_vars: &HashMap, + flow_model: &FlowModelDetailResp, + ctx: &TardisContext, + ) -> TardisResult<()> { + let rel_transition = flow_model.rel_transition().unwrap_or_default(); + let subject = match rel_transition.id.as_str() { + "__EDIT__" => "编辑审批".to_string(), + "__DELETE__" => "删除审批".to_string(), + _ => format!("{}({})", rel_transition.name, rel_transition.from_flow_state_name).to_string(), + }; + let mut log_ext = LogParamExt { + scene_kind: Some(vec![String::from(LogParamExtSceneKind::Detail)]), + new_log: Some(true), + project_id: rbum_scope_helper::get_path_item(RbumScopeLevelKind::L2.to_int(), &ctx.own_paths), + ..Default::default() + }; + let mut log_content = LogParamContent { + subject: Some(subject), + name: Some(format!("编号{}", flow_inst_detail.code)), + sub_id: Some(flow_inst_detail.id.clone()), + sub_kind: Some(FlowLogClient::get_junp_kind(&flow_inst_detail.tag)), + ..Default::default() + }; + // if start_req.create_vars.is_none() { + // log_ext.include_detail = Some(false); + // log_content.old_content = "".to_string(); + // log_content.new_content = "".to_string(); + // } else { + // log_content.old_content = create_vars.get("content").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string()); + // log_content.new_content = start_req.create_vars.clone().unwrap_or_default().get("content").map(|content| content.as_str().unwrap_or("").to_string()).unwrap_or_default(); + // log_content.detail = start_req.log_text.clone(); + // log_ext.include_detail = Some(true); + // } + FlowLogClient::add_ctx_task( + LogParamTag::DynamicLog, + Some(flow_inst_detail.rel_business_obj_id.clone()), + log_content, + Some(TardisFuns::json.obj_to_json(&log_ext).expect("ext not a valid json value")), + Some("dynamic_log_approval_flow".to_string()), + Some(LogParamOp::Start.into()), + rbum_scope_helper::get_path_item(RbumScopeLevelKind::L1.to_int(), &ctx.own_paths), + false, + ctx, + false, + ) + .await?; + Ok(()) + } + // 添加审批流操作日志 pub async fn add_operate_log( operate_req: &FlowInstOperateReq, @@ -172,8 +246,7 @@ impl FlowLogServ{ if operate_req.vars.is_none() { log_ext.include_detail = Some(false); } else { - log_content.old_content = - flow_inst_detail.create_vars.clone().unwrap_or_default().get("content").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string()); + log_content.old_content = flow_inst_detail.create_vars.clone().unwrap_or_default().get("content").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string()); log_content.new_content = operate_req.vars.clone().unwrap_or_default().get("content").map(|content| content.as_str().unwrap_or("").to_string()).unwrap_or_default(); log_content.detail = operate_req.log_text.clone(); log_ext.include_detail = Some(true); @@ -285,8 +358,7 @@ impl FlowLogServ{ if operate_req.vars.is_none() { log_ext.include_detail = Some(false); } else { - log_content.old_content = - flow_inst_detail.create_vars.clone().unwrap_or_default().get("content").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string()); + log_content.old_content = flow_inst_detail.create_vars.clone().unwrap_or_default().get("content").map_or("".to_string(), |val| val.as_str().unwrap_or("").to_string()); log_content.new_content = operate_req.vars.clone().unwrap_or_default().get("content").map(|content| content.as_str().unwrap_or("").to_string()).unwrap_or_default(); log_content.detail = operate_req.log_text.clone(); log_ext.include_detail = Some(true); @@ -375,4 +447,70 @@ impl FlowLogServ{ .await?; Ok(()) } -} \ No newline at end of file + + // 添加审批流结束业务日志 + pub async fn add_finish_business_log(flow_inst_detail: &FlowInstDetailResp, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + let flow_model_version = FlowModelVersionServ::get_item( + &flow_inst_detail.rel_flow_version_id, + &FlowModelVersionFilterReq { + basic: RbumBasicFilterReq { + with_sub_own_paths: true, + own_paths: Some("".to_string()), + ..Default::default() + }, + ..Default::default() + }, + funs, + ctx, + ) + .await?; + let flow_model = FlowModelServ::get_item( + &flow_model_version.rel_model_id, + &FlowModelFilterReq { + basic: RbumBasicFilterReq { + with_sub_own_paths: true, + own_paths: Some("".to_string()), + ..Default::default() + }, + ..Default::default() + }, + funs, + ctx, + ) + .await?; + let rel_transition = flow_model.rel_transition().unwrap_or_default(); + let sub_op = match rel_transition.id.as_str() { + "__EDIT__" => "编辑".to_string(), + "__DELETE__" => "删除".to_string(), + _ => rel_transition.name.clone(), + }; + let log_ext = LogParamExt { + scene_kind: Some(vec![String::from(LogParamExtSceneKind::Detail)]), + new_log: Some(true), + project_id: rbum_scope_helper::get_path_item(RbumScopeLevelKind::L2.to_int(), &ctx.own_paths), + ..Default::default() + }; + let log_content = LogParamContent { + subject: Some(FlowLogClient::get_flow_kind_text(&flow_inst_detail.tag)), + name: Some(format!("编号{}", flow_inst_detail.code)), + sub_id: Some(flow_inst_detail.id.clone()), + sub_kind: Some(FlowLogClient::get_junp_kind("FLOW")), + sub_op: Some(sub_op), + ..Default::default() + }; + FlowLogClient::add_ctx_task( + LogParamTag::DynamicLog, + Some(flow_inst_detail.rel_business_obj_id.clone()), + log_content, + Some(TardisFuns::json.obj_to_json(&log_ext).expect("ext not a valid json value")), + Some("dynamic_log_approval_flow".to_string()), + Some(LogParamOp::Start.into()), + rbum_scope_helper::get_path_item(RbumScopeLevelKind::L1.to_int(), &ctx.own_paths), + false, + ctx, + false, + ) + .await?; + Ok(()) + } +} diff --git a/backend/middlewares/flow/src/serv/flow_model_serv.rs b/backend/middlewares/flow/src/serv/flow_model_serv.rs index f136e7958..898a37ea3 100644 --- a/backend/middlewares/flow/src/serv/flow_model_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_model_serv.rs @@ -28,7 +28,8 @@ use crate::{ dto::{ flow_model_dto::{ FlowModelAddReq, FlowModelAggResp, FlowModelAssociativeOperationKind, FlowModelBindNewStateReq, FlowModelBindStateReq, FlowModelDetailResp, FlowModelFilterReq, - FlowModelFindRelStateResp, FlowModelKind, FlowModelModifyReq, FlowModelRelTransitionExt, FlowModelStatus, FlowModelSummaryResp, FlowModelSyncModifiedFieldReq, FlowModelUnbindStateReq, + FlowModelFindRelStateResp, FlowModelKind, FlowModelModifyReq, FlowModelRelTransitionExt, FlowModelStatus, FlowModelSummaryResp, FlowModelSyncModifiedFieldReq, + FlowModelUnbindStateReq, }, flow_model_version_dto::{ FlowModelVersionAddReq, FlowModelVersionBindState, FlowModelVersionDetailResp, FlowModelVersionFilterReq, FlowModelVersionModifyReq, FlowModelVersionModifyState, @@ -48,7 +49,12 @@ use super::{ clients::{ log_client::{FlowLogClient, LogParamContent, LogParamTag}, search_client::FlowSearchClient, - }, flow_inst_serv::FlowInstServ, flow_model_version_serv::FlowModelVersionServ, flow_rel_serv::{FlowRelKind, FlowRelServ}, flow_state_serv::FlowStateServ, flow_transition_serv::FlowTransitionServ + }, + flow_inst_serv::FlowInstServ, + flow_model_version_serv::FlowModelVersionServ, + flow_rel_serv::{FlowRelKind, FlowRelServ}, + flow_state_serv::FlowStateServ, + flow_transition_serv::FlowTransitionServ, }; pub struct FlowModelServ; @@ -184,6 +190,7 @@ impl RbumItemCrudOperation TardisResult> { + let mut result = HashMap::new(); + for from_model in Self::find_detail_items( + &FlowModelFilterReq { + basic: RbumBasicFilterReq { + ids: Some( + FlowRelServ::find_to_simple_rels(&FlowRelKind::FlowModelTemplate, from_template_id, None, None, funs, ctx) + .await? + .into_iter() + .map(|rel| rel.rel_id) + .collect_vec(), + ), + ignore_scope: true, + ..Default::default() + }, + ..Default::default() + }, + None, + None, + funs, + ctx, + ) + .await? + { + let added_model = Self::copy_or_reference_model( + &from_model.id, + &FlowModelAssociativeOperationKind::ReferenceOrCopy, + FlowModelKind::AsTemplateAndAsModel, + funs, + ctx, + ) + .await?; + Self::modify_model(&added_model.id, &mut FlowModelModifyReq { + rel_model_id: Some(from_model.rel_model_id.clone()), + ..Default::default() + }, funs, ctx).await?; + FlowRelServ::add_simple_rel( + &FlowRelKind::FlowModelTemplate, + &added_model.id, + to_template_id, + None, + None, + false, + true, + None, + funs, + ctx, + ) + .await?; + result.insert(from_model.rel_model_id.clone(), added_model); + } + Ok(result) + } + /// 创建或引用模型 /// params: /// rel_model_id:关联模型ID @@ -1905,15 +1974,45 @@ impl FlowModelServ { } pub async fn sync_modified_field(req: &FlowModelSyncModifiedFieldReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { - let models = Self::find_detail_items( - &FlowModelFilterReq { - basic: RbumBasicFilterReq { - enabled: Some(true), + let model_ids = if Self::get_app_id_by_ctx(ctx).is_some() { + Self::find_id_items( + &FlowModelFilterReq { + basic: RbumBasicFilterReq { + enabled: Some(true), + ..Default::default() + }, + main: Some(false), + tags: Some(vec![req.tag.clone()]), + ..Default::default() + }, + None, + None, + funs, + ctx, + ) + .await? + } else { + Self::find_id_items( + &FlowModelFilterReq { + basic: RbumBasicFilterReq { + enabled: Some(true), + ..Default::default() + }, + main: Some(false), + rel_template_id: req.rel_template_id.clone(), + tags: Some(vec![req.tag.clone()]), ..Default::default() }, - main: Some(false), - rel_template_id: req.rel_template_id.clone(), - tags: Some(vec![req.tag.clone()]), + None, + None, + funs, + ctx, + ) + .await? + }; + let model_versions = FlowModelVersionServ::find_detail_items( + &FlowModelVersionFilterReq { + rel_model_ids: Some(model_ids), ..Default::default() }, None, @@ -1922,8 +2021,8 @@ impl FlowModelServ { ctx, ) .await?; - for model in models { - let states = model.states(); + for model_version in model_versions { + let states = model_version.states(); for state in states { let add_default_conf = match state.state_kind { FlowStateKind::Form => state.kind_conf.clone().unwrap_or_default().form.unwrap_or_default().add_default_field.unwrap_or_default(), @@ -1973,7 +2072,8 @@ impl FlowModelServ { let flow_model = Self::get_item(flow_model_id, &FlowModelFilterReq::default(), funs, ctx).await?; let mut own_paths_list = vec![]; - if let Some(rel_template_id) = FlowRelServ::find_from_simple_rels(&FlowRelKind::FlowModelTemplate, flow_model_id, None, None, funs, ctx).await?.pop().map(|rel| rel.rel_id) { + if let Some(rel_template_id) = FlowRelServ::find_from_simple_rels(&FlowRelKind::FlowModelTemplate, flow_model_id, None, None, funs, ctx).await?.pop().map(|rel| rel.rel_id) + { own_paths_list = FlowRelServ::find_to_simple_rels(&FlowRelKind::FlowAppTemplate, &rel_template_id, None, None, funs, ctx) .await? .into_iter() diff --git a/backend/middlewares/flow/src/serv/flow_model_version_serv.rs b/backend/middlewares/flow/src/serv/flow_model_version_serv.rs index d91dedbaf..6647eccdb 100644 --- a/backend/middlewares/flow/src/serv/flow_model_version_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_model_version_serv.rs @@ -255,6 +255,22 @@ impl Ok(flow_version) } + + async fn find_detail_items( + filter: &FlowModelVersionFilterReq, + desc_sort_by_create: Option, + desc_sort_by_update: Option, + funs: &TardisFunsInst, + ctx: &TardisContext, + ) -> TardisResult> { + let mut flow_model_versions = Self::do_find_detail_items(filter, desc_sort_by_create, desc_sort_by_update, funs, ctx).await?; + for flow_model_version in &mut flow_model_versions { + let current_version = Self::get_item(&flow_model_version.id, &FlowModelVersionFilterReq { ..Default::default() }, funs, ctx).await?; + flow_model_version.states = Some(current_version.states.unwrap_or_default()); + } + + Ok(flow_model_versions) + } } impl FlowModelVersionServ { @@ -690,6 +706,7 @@ impl FlowModelVersionServ { tags: Some(state.tags.clone().split(',').map(|id| id.to_string()).collect_vec()), scope_level: Some(state.scope_level.clone()), disabled: Some(state.disabled), + main: Some(state.main), ..Default::default() }, ext: state.ext, diff --git a/backend/middlewares/flow/src/serv/flow_state_serv.rs b/backend/middlewares/flow/src/serv/flow_state_serv.rs index 58e548277..439f97b24 100644 --- a/backend/middlewares/flow/src/serv/flow_state_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_state_serv.rs @@ -248,6 +248,7 @@ impl RbumItemCrudOperation TardisResult> { let states = FlowModelServ::find_rel_states(vec![&req.tag], None, funs, ctx).await?; let mut result = HashMap::new(); - let insts = FlowInstServ::find_detail(req.inst_ids.clone(), funs, ctx).await?; + let insts = FlowInstServ::find_detail(req.inst_ids.clone(), None, None, funs, ctx).await?; for state in states { let mut inst_ids = insts.iter().filter(|inst| inst.current_state_id == state.id).map(|inst| inst.id.clone()).collect_vec(); result @@ -456,6 +461,7 @@ impl FlowStateServ { tags: state.tags, scope_level: state.scope_level, disabled: state.disabled, + main: state.main, ext: Self::get_rel_state_ext(flow_version_id, &state.id, funs, ctx).await?, state_kind: state.state_kind, kind_conf, diff --git a/backend/middlewares/flow/src/serv/flow_transition_serv.rs b/backend/middlewares/flow/src/serv/flow_transition_serv.rs index a362a40a3..553490513 100644 --- a/backend/middlewares/flow/src/serv/flow_transition_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_transition_serv.rs @@ -23,8 +23,7 @@ use tardis::{ use crate::{ domain::{flow_state, flow_transition}, dto::{ - flow_model_dto::{FlowModelFilterReq, FlowModelStatus}, - flow_transition_dto::{FlowTransitionActionChangeKind, FlowTransitionAddReq, FlowTransitionDetailResp, FlowTransitionFilterReq, FlowTransitionModifyReq}, + flow_model_dto::{FlowModelFilterReq, FlowModelStatus}, flow_state_dto::{FlowStateFilterReq, FlowStateKind}, flow_transition_dto::{FlowTransitionActionChangeKind, FlowTransitionAddReq, FlowTransitionDetailResp, FlowTransitionFilterReq, FlowTransitionModifyReq} }, }; @@ -55,6 +54,22 @@ impl FlowTransitionServ { if add_req.is_empty() { return Ok(()); } + // @TODO 替前端处理 + let from_state = FlowStateServ::get_item( + from_flow_state_id, + &FlowStateFilterReq { + basic: RbumBasicFilterReq { + with_sub_own_paths: true, + own_paths: Some("".to_string()), + ..Default::default() + }, + ..Default::default() + }, + funs, + ctx, + ) + .await?; + let flow_transitions = add_req .iter() .map(|req| flow_transition::ActiveModel { @@ -64,7 +79,8 @@ impl FlowTransitionServ { from_flow_state_id: Set(from_flow_state_id.to_string()), to_flow_state_id: Set(req.to_flow_state_id.to_string()), - transfer_by_auto: Set(req.transfer_by_auto.unwrap_or(false)), + // transfer_by_auto: Set(req.transfer_by_auto.unwrap_or(false)), + transfer_by_auto: Set(from_state.state_kind == FlowStateKind::Start || from_state.state_kind == FlowStateKind::Branch), transfer_by_timer: Set(req.transfer_by_timer.as_ref().unwrap_or(&"".to_string()).to_string()), guard_by_creator: Set(req.guard_by_creator.unwrap_or(false)), @@ -157,7 +173,23 @@ impl FlowTransitionServ { flow_transition.name = Set(name.to_string()); } if let Some(from_flow_state_id) = &req.from_flow_state_id { + // @TODO 替前端处理 + let from_state = FlowStateServ::get_item( + from_flow_state_id, + &FlowStateFilterReq { + basic: RbumBasicFilterReq { + with_sub_own_paths: true, + own_paths: Some("".to_string()), + ..Default::default() + }, + ..Default::default() + }, + funs, + ctx, + ) + .await?; flow_transition.from_flow_state_id = Set(from_flow_state_id.to_string()); + flow_transition.transfer_by_auto = Set(from_state.state_kind == FlowStateKind::Start || from_state.state_kind == FlowStateKind::Branch); } if let Some(to_flow_state_id) = &req.to_flow_state_id { flow_transition.to_flow_state_id = Set(to_flow_state_id.to_string()); diff --git a/backend/middlewares/flow/tests/test_flow_scenes_fsm.rs b/backend/middlewares/flow/tests/test_flow_scenes_fsm.rs index f91da50cd..5dd8826b2 100644 --- a/backend/middlewares/flow/tests/test_flow_scenes_fsm.rs +++ b/backend/middlewares/flow/tests/test_flow_scenes_fsm.rs @@ -462,6 +462,8 @@ pub async fn test(flow_client: &mut TestHttpClient, search_client: &mut TestHttp create_vars: None, rel_business_obj_id: rel_business_obj_id.clone(), transition_id: None, + vars: None, + log_text: None, }, ) .await; @@ -593,6 +595,8 @@ pub async fn test(flow_client: &mut TestHttpClient, search_client: &mut TestHttp create_vars: None, rel_business_obj_id: rel_business_obj_id.clone(), transition_id: Some("__EDIT__".to_string()), + vars: None, + log_text: None, }, ) .await; diff --git a/backend/spi/spi-search/src/serv/pg/search_pg_item_serv.rs b/backend/spi/spi-search/src/serv/pg/search_pg_item_serv.rs index 8c7038449..71561047f 100644 --- a/backend/spi/spi-search/src/serv/pg/search_pg_item_serv.rs +++ b/backend/spi/spi-search/src/serv/pg/search_pg_item_serv.rs @@ -700,7 +700,7 @@ fn package_ext( )); } else if ext_item.op == BasicQueryOpKind::Len { if let Some(first_value) = value.pop() { - sql_and_where.push(format!("(length(ext->>'{}') = ${})", ext_item.field, sql_vals.len() + 1)); + sql_and_where.push(format!("(length(ext->>'{}') = ${})", ext_item.field, sql_vals.len() + 1)); sql_vals.push(first_value); } else { return err_not_found(ext_item); diff --git a/backend/spi/spi-stats/tests/test_stats_conf.rs b/backend/spi/spi-stats/tests/test_stats_conf.rs index 66e99567d..1370b8051 100644 --- a/backend/spi/spi-stats/tests/test_stats_conf.rs +++ b/backend/spi/spi-stats/tests/test_stats_conf.rs @@ -320,7 +320,7 @@ pub async fn test_fact_conf(client: &mut TestHttpClient) -> TardisResult<()> { let _: Void = client.put("/ci/conf/fact/req/online", &Void {}).await; // can't modify fact after online error - // + // // assert_eq!( // client // .patch_resp::( From 9c2fd8e228b095085d8de93f2bfc07d2b4ed9022 Mon Sep 17 00:00:00 2001 From: 4t145 Date: Thu, 2 Jan 2025 10:44:08 +0800 Subject: [PATCH 21/35] fix mq in k8s cluster --- Cargo.toml | 4 +-- backend/middlewares/event/src/event_config.rs | 28 +++++++++++++------ .../event/src/event_initializer.rs | 21 ++++++++------ backend/services/bios-all/build.sh | 5 +++- 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 162b3c672..cd7a8df0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,9 +67,9 @@ strum = { version = "0.26", features = ["derive"] } # tardis = { version = "0.2.0", path = "../tardis/tardis" } tardis = { git = "https://github.com/ideal-world/tardis.git", rev = "66d4c63" } # asteroid-mq = { git = "https://github.com/4t145/asteroid-mq.git", rev = "d59c64d" } -asteroid-mq = { git = "https://github.com/4t145/asteroid-mq.git", rev = "726c8dd" } +asteroid-mq = { git = "https://github.com/4t145/asteroid-mq.git", rev = "b26fa4f" } # asteroid-mq = { version = "0.1.0-alpha.5" } -asteroid-mq-sdk = { git = "https://github.com/4t145/asteroid-mq.git", rev = "726c8dd" } +asteroid-mq-sdk = { git = "https://github.com/4t145/asteroid-mq.git", rev = "b26fa4f" } # asteroid-mq-sdk = { version = "0.1.0-alpha.5" } #spacegate diff --git a/backend/middlewares/event/src/event_config.rs b/backend/middlewares/event/src/event_config.rs index b69aa4199..a7a5bc5a7 100644 --- a/backend/middlewares/event/src/event_config.rs +++ b/backend/middlewares/event/src/event_config.rs @@ -1,4 +1,3 @@ -use asteroid_mq::openraft; use bios_basic::rbum::rbum_config::RbumConfig; use serde::{Deserialize, Serialize}; @@ -14,13 +13,30 @@ pub struct EventConfig { pub rbum: RbumConfig, pub enable: bool, pub svc: String, - pub raft: openraft::Config, + pub raft: Option, // default by 5000ms pub startup_timeout: u64, pub durable: bool, pub avatars: Vec, pub cluster: Option, } +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(default)] +pub struct RaftConfig { + pub election_timeout_min: u64, + pub election_timeout_max: u64, + pub heartbeat_interval: u64, +} + +impl Default for RaftConfig { + fn default() -> Self { + RaftConfig { + election_timeout_max: 1000, + election_timeout_min: 500, + heartbeat_interval: 100, + } + } +} impl EventConfig { pub const CLUSTER_K8S: &str = "k8s"; @@ -37,13 +53,7 @@ impl Default for EventConfig { startup_timeout: 5000, durable: true, cluster: Some(Self::CLUSTER_K8S.to_string()), - raft: openraft::Config { - cluster_name: "bios".to_string(), - election_timeout_max: 1000, - election_timeout_min: 500, - heartbeat_interval: 100, - ..Default::default() - }, + raft: None, } } } diff --git a/backend/middlewares/event/src/event_initializer.rs b/backend/middlewares/event/src/event_initializer.rs index ca1a71503..0dd000318 100644 --- a/backend/middlewares/event/src/event_initializer.rs +++ b/backend/middlewares/event/src/event_initializer.rs @@ -4,7 +4,7 @@ use asteroid_mq::{ prelude::{DurableService, Node, NodeConfig, NodeId, TopicConfig, TopicOverflowConfig}, protocol::node::{ edge::auth::EdgeAuthService, - raft::cluster::{K8sClusterProvider, StaticClusterProvider}, + raft::cluster::{this_pod_id, K8sClusterProvider, StaticClusterProvider}, }, }; use bios_basic::rbum::{ @@ -154,34 +154,39 @@ async fn init_mq_cluster(config: &EventConfig, funs: TardisFunsInst, ctx: Tardis pub async fn init_mq_node(config: &EventConfig, funs: Arc, ctx: &TardisContext) -> asteroid_mq::prelude::Node { let timeout = Duration::from_secs(config.startup_timeout); - const ENV_POD_UID: &str = "POD_UID"; if let Some(node) = TardisFuns::store().get_singleton::() { node } else { + let raft_config = config.raft.clone().unwrap_or_default(); + let raft_config = asteroid_mq::openraft::Config { + election_timeout_min: raft_config.election_timeout_min, + election_timeout_max: raft_config.election_timeout_max, + heartbeat_interval: raft_config.heartbeat_interval, + ..Default::default() + }; let node = match config.cluster.as_deref() { Some(EventConfig::CLUSTER_K8S) => { - let uid = std::env::var(ENV_POD_UID).expect("POD_UID is required"); let node = Node::new(NodeConfig { - id: NodeId::sha256(uid.as_bytes()), - raft: config.raft.clone(), + id: this_pod_id(), + raft: raft_config, durable: config.durable.then_some(DurableService::new(BiosDurableAdapter::new(funs.clone(), ctx.clone()))), edge_auth: Some(EdgeAuthService::new(BiosEdgeAuthAdapter::new(funs.clone(), ctx.clone()))), ..Default::default() }); - let cluster_provider = K8sClusterProvider::new(config.svc.clone(), asteroid_mq::DEFAULT_TCP_PORT).await; + let cluster_provider = K8sClusterProvider::new(asteroid_mq::DEFAULT_TCP_PORT).await; node.start(cluster_provider).await.expect("fail to init raft"); node } Some(EventConfig::NO_CLUSTER) | None => { let node = Node::new(NodeConfig { id: NodeId::snowflake(), - raft: config.raft.clone(), + raft: raft_config, durable: config.durable.then_some(DurableService::new(BiosDurableAdapter::new(funs.clone(), ctx.clone()))), edge_auth: Some(EdgeAuthService::new(BiosEdgeAuthAdapter::new(funs.clone(), ctx.clone()))), ..Default::default() }); // singleton mode - let cluster_provider = StaticClusterProvider::singleton(node.id(), node.config().addr); + let cluster_provider = StaticClusterProvider::singleton(node.id(), node.config().addr.to_string()); node.start(cluster_provider).await.expect("fail to init raft"); node } diff --git a/backend/services/bios-all/build.sh b/backend/services/bios-all/build.sh index 907fb704c..bef1f8fff 100644 --- a/backend/services/bios-all/build.sh +++ b/backend/services/bios-all/build.sh @@ -1,5 +1,5 @@ REPO=$TAG - +CURRENT_DATE=$(date +"%Y%m%d%H%M") ### Rust if [ -z "$TARGET" ]; then echo "Please choose a target:" @@ -44,6 +44,9 @@ if [ -z "$TAG" ]; then echo "Please enter a tag:" read TAG fi +if [ -z "$TAG" ]; then + TAG="test-$CURRENT_DATE" +fi docker build -t $TAG ./ From db660c374b95e80493c6527b78c10f03653e6a9d Mon Sep 17 00:00:00 2001 From: ljl <17743125563@163.com> Date: Thu, 2 Jan 2025 15:39:39 +0800 Subject: [PATCH 22/35] spi-stats:fix cached plan must not change result type. --- .../src/serv/pg/stats_pg_record_serv.rs | 18 ++++++++++++++---- .../src/serv/pg/stats_pg_sync_serv.rs | 2 -- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/backend/spi/spi-stats/src/serv/pg/stats_pg_record_serv.rs b/backend/spi/spi-stats/src/serv/pg/stats_pg_record_serv.rs index 3f30ce9ca..8e98a613e 100644 --- a/backend/spi/spi-stats/src/serv/pg/stats_pg_record_serv.rs +++ b/backend/spi/spi-stats/src/serv/pg/stats_pg_record_serv.rs @@ -145,7 +145,6 @@ pub(crate) async fn fact_record_load( ) -> TardisResult<()> { let bs_inst = inst.inst::(); let (mut conn, _) = common_pg::init_conn(bs_inst).await?; - conn.execute_one("SET plan_cache_mode = 'force_generic_plan'", vec![]).await?; conn.begin().await?; if !stats_pg_conf_fact_serv::online(fact_conf_key, &conn, ctx).await? { return Err(funs.err().conflict("fact_record", "load", "The fact config not online.", "409-spi-stats-fact-conf-not-online")); @@ -179,7 +178,7 @@ pub(crate) async fn fact_record_load( return Ok(()); } } - let latest_data_resp = fact_get_latest_record_raw(fact_conf_key, fact_record_key, &conn, ctx).await?; + let latest_data_resp = fact_get_latest_record_raw(fact_conf_key, fact_record_key, fact_col_conf_set.clone(), &conn, ctx).await?; if let Some(latest_data) = latest_data_resp.as_ref() { let mut storage_ext = latest_data.try_get("", "ext")?; merge(&mut storage_ext, add_req.ext.unwrap_or(TardisFuns::json.str_to_json("{}")?)); @@ -1030,11 +1029,22 @@ pub(crate) async fn dim_record_real_delete( async fn fact_get_latest_record_raw( fact_conf_key: &str, dim_record_key: &str, + fact_col_conf_set: Vec, conn: &TardisRelDBlConnection, ctx: &TardisContext, ) -> TardisResult> { let table_name = package_table_name(&format!("stats_inst_fact_{fact_conf_key}"), ctx); - let result = conn.query_one(&format!("SELECT * FROM {table_name} WHERE key = $1 ORDER BY ct DESC"), vec![Value::from(dim_record_key)]).await?; + let mut field_keys = vec!["key".to_string(), "own_paths".to_string(), "ext".to_string(), "ct".to_string(), "idempotent_id".to_string()]; + fact_col_conf_set.iter().for_each(|c| { + field_keys.push(c.key.clone()); + }); + // let result = conn.query_one(&format!("SELECT * FROM {table_name} WHERE key = $1 ORDER BY ct DESC"), vec![Value::from(dim_record_key)]).await?; + let result = conn + .query_one( + &format!("SELECT {} FROM {table_name} WHERE key = '{dim_record_key}' ORDER BY ct DESC", field_keys.join(",")), + vec![], + ) + .await?; Ok(result) } @@ -1047,7 +1057,7 @@ async fn fact_get_idempotent_record_raw( let table_name = package_table_name(&format!("stats_inst_fact_{fact_conf_key}"), ctx); let result = conn .query_one( - &format!("SELECT * FROM {table_name} WHERE idempotent_id = $1 ORDER BY ct DESC"), + &format!("SELECT idempotent_id FROM {table_name} WHERE idempotent_id = $1 ORDER BY ct DESC"), vec![Value::from(idempotent_id)], ) .await?; diff --git a/backend/spi/spi-stats/src/serv/pg/stats_pg_sync_serv.rs b/backend/spi/spi-stats/src/serv/pg/stats_pg_sync_serv.rs index d40e1cbe5..171d22350 100644 --- a/backend/spi/spi-stats/src/serv/pg/stats_pg_sync_serv.rs +++ b/backend/spi/spi-stats/src/serv/pg/stats_pg_sync_serv.rs @@ -208,7 +208,6 @@ pub(crate) async fn fact_record_sync(fact_conf_key: &str, funs: &TardisFunsInst, let funs = stats_initializer::get_tardis_inst(); let inst = funs.init(None, &task_ctx, true, stats_initializer::init_fun).await?; let db_source_conn = get_db_conn_by_cert_id(&cert_id, &funs, &task_ctx, inst.as_ref()).await?; - db_source_conn.execute_one("SET plan_cache_mode = 'force_generic_plan'", vec![]).await?; let db_source_list = db_source_conn.query_all(&sync_sql, vec![]).await?; let mut success = 0; let mut error = 0; @@ -343,7 +342,6 @@ pub(crate) async fn fact_col_record_result( return Ok(None); } let data_source_conn = get_db_conn_by_cert_id(&cert_id, funs, ctx, inst).await?; - data_source_conn.execute_one("SET plan_cache_mode = 'force_generic_plan'", vec![]).await?; let (sql, params) = process_sql(&sql, &fact_record)?; if let Some(rel_record) = data_source_conn.query_one(&sql, params).await? { if let Some(first_column) = rel_record.column_names().get(0) { From 8e735ef86449a80b745979f66b8d0f803e255c93 Mon Sep 17 00:00:00 2001 From: ZzIsGod1019 <1498852723@qq.com> Date: Thu, 2 Jan 2025 14:02:09 +0800 Subject: [PATCH 23/35] flow: fix bug (sync search error) (#891) --- .../flow/src/api/cc/flow_cc_inst_api.rs | 15 +++ .../src/api/cc/flow_cc_model_version_api.rs | 2 +- .../flow/src/dto/flow_external_dto.rs | 9 ++ .../middlewares/flow/src/dto/flow_inst_dto.rs | 4 +- .../flow/src/serv/clients/search_client.rs | 4 +- .../flow/src/serv/flow_event_serv.rs | 4 + .../flow/src/serv/flow_external_serv.rs | 19 +++- .../flow/src/serv/flow_inst_serv.rs | 99 +++++++++---------- .../flow/src/serv/flow_log_serv.rs | 34 ++++++- .../flow/src/serv/flow_state_serv.rs | 1 + 10 files changed, 127 insertions(+), 64 deletions(-) diff --git a/backend/middlewares/flow/src/api/cc/flow_cc_inst_api.rs b/backend/middlewares/flow/src/api/cc/flow_cc_inst_api.rs index 901835646..2d54a26b0 100644 --- a/backend/middlewares/flow/src/api/cc/flow_cc_inst_api.rs +++ b/backend/middlewares/flow/src/api/cc/flow_cc_inst_api.rs @@ -50,6 +50,21 @@ impl FlowCcInstApi { TardisResp::ok(result) } + /// Start Instance(Return Instance ID) + /// + /// 批量启动实例(返回实例ID) + #[oai(path = "/batch_start", method = "post")] + async fn batch_start(&self, add_batch_req: Json>, ctx: TardisContextExtractor, _request: &Request) -> TardisApiResult { + let mut funs = flow_constants::get_tardis_inst(); + funs.begin().await?; + for add_req in &add_batch_req.0 { + FlowInstServ::start(add_req, None, &funs, &ctx.0).await?; + } + funs.commit().await?; + ctx.0.execute_task().await?; + TardisResp::ok(Void {}) + } + /// Abort Instance /// /// 终止实例 diff --git a/backend/middlewares/flow/src/api/cc/flow_cc_model_version_api.rs b/backend/middlewares/flow/src/api/cc/flow_cc_model_version_api.rs index 2b6aaeb2e..7b5ae1787 100644 --- a/backend/middlewares/flow/src/api/cc/flow_cc_model_version_api.rs +++ b/backend/middlewares/flow/src/api/cc/flow_cc_model_version_api.rs @@ -64,7 +64,7 @@ impl FlowCcModelVersionApi { /// /// 获取模型使用全局owner #[oai(path = "/:flow_version_id/global", method = "get")] - async fn gloabl_get(&self, flow_version_id: Path, ctx: TardisContextExtractor, _request: &Request) -> TardisApiResult { + async fn global_get(&self, flow_version_id: Path, ctx: TardisContextExtractor, _request: &Request) -> TardisApiResult { let funs = flow_constants::get_tardis_inst(); let result = FlowModelVersionServ::get_item( &flow_version_id.0, diff --git a/backend/middlewares/flow/src/dto/flow_external_dto.rs b/backend/middlewares/flow/src/dto/flow_external_dto.rs index e3cec5d3f..49a5d1f92 100644 --- a/backend/middlewares/flow/src/dto/flow_external_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_external_dto.rs @@ -61,6 +61,15 @@ pub struct FlowExternalReq { /// /// 请求是否触发通知 pub notify: Option, + /// Whether the request triggers a notification + /// + /// 是否人工操作 + pub manual_op: Option, + /// Whether the request triggers a notification + /// + /// 操作人 + pub operator: Option, + pub sys_time: Option, /// 扩展字段 /// /// Extended params diff --git a/backend/middlewares/flow/src/dto/flow_inst_dto.rs b/backend/middlewares/flow/src/dto/flow_inst_dto.rs index 1d7ccd1a8..f09c1078b 100644 --- a/backend/middlewares/flow/src/dto/flow_inst_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_inst_dto.rs @@ -11,7 +11,7 @@ use tardis::{ use super::{ flow_model_dto::FlowModelRelTransitionExt, - flow_state_dto::{FlowStateKind, FlowStateOperatorKind, FlowStateRelModelExt, FlowStateVar, FlowSysStateKind}, + flow_state_dto::{FlowGuardConf, FlowStateKind, FlowStateOperatorKind, FlowStateRelModelExt, FlowStateVar, FlowSysStateKind}, flow_transition_dto::FlowTransitionDoubleCheckInfo, flow_var_dto::FlowVarInfo, }; @@ -223,6 +223,7 @@ pub struct FLowInstStateConf { #[derive(Serialize, Deserialize, Debug, Clone, poem_openapi::Object)] pub struct FLowInstStateFormConf { pub form_vars_collect_conf: HashMap, + pub form_referral_guard_custom_conf: Option, } // 状态审批配置 @@ -230,6 +231,7 @@ pub struct FLowInstStateFormConf { pub struct FLowInstStateApprovalConf { pub approval_vars_collect_conf: Option>, pub form_vars_collect: HashMap, + pub approval_referral_guard_custom_conf: Option, } // 流程实例中对应的数据存储 diff --git a/backend/middlewares/flow/src/serv/clients/search_client.rs b/backend/middlewares/flow/src/serv/clients/search_client.rs index a2e72aba9..b2335e699 100644 --- a/backend/middlewares/flow/src/serv/clients/search_client.rs +++ b/backend/middlewares/flow/src/serv/clients/search_client.rs @@ -325,9 +325,9 @@ impl FlowSearchClient { kv_disable: None, }; if let Some(_topic) = get_topic(&SPI_RPC_TOPIC) { - EventCenterClient { topic_code: SPI_RPC_TOPIC }.modify_item_and_name(SEARCH_MODEL_TAG, &key, &modify_req, funs, ctx).await?; + EventCenterClient { topic_code: SPI_RPC_TOPIC }.modify_item_and_name(SEARCH_INSTANCE_TAG, &key, &modify_req, funs, ctx).await?; } else { - SpiSearchClient::modify_item_and_name(SEARCH_MODEL_TAG, &key, &modify_req, funs, ctx).await?; + SpiSearchClient::modify_item_and_name(SEARCH_INSTANCE_TAG, &key, &modify_req, funs, ctx).await?; } } else { let add_req = SearchItemAddReq { diff --git a/backend/middlewares/flow/src/serv/flow_event_serv.rs b/backend/middlewares/flow/src/serv/flow_event_serv.rs index 53ae91e5b..0b3ea6128 100644 --- a/backend/middlewares/flow/src/serv/flow_event_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_event_serv.rs @@ -339,6 +339,8 @@ impl FlowEventServ { &rel_bus_obj_id, &inst_id, Some(FlowExternalCallbackOp::PostAction), + Some(false), + None, Some(next_flow_state.name.clone()), Some(next_flow_state.sys_state.clone()), Some(prev_flow_state.name.clone()), @@ -398,6 +400,8 @@ impl FlowEventServ { &flow_inst_detail.rel_business_obj_id, &flow_inst_detail.id, Some(FlowExternalCallbackOp::PostAction), + Some(true), + None, Some(next_flow_state.name.clone()), Some(next_flow_state.sys_state.clone()), Some(prev_flow_state.name.clone()), diff --git a/backend/middlewares/flow/src/serv/flow_external_serv.rs b/backend/middlewares/flow/src/serv/flow_external_serv.rs index 1ef8711b7..f04a2ee16 100644 --- a/backend/middlewares/flow/src/serv/flow_external_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_external_serv.rs @@ -1,9 +1,7 @@ use bios_sdk_invoke::{clients::spi_kv_client::SpiKvClient, invoke_constants::TARDIS_CONTEXT}; use itertools::Itertools; use tardis::{ - basic::{dto::TardisContext, result::TardisResult}, - log::debug, - tokio, TardisFuns, TardisFunsInst, + basic::{dto::TardisContext, result::TardisResult}, chrono::Utc, log::debug, tokio, TardisFuns, TardisFunsInst }; use crate::{ @@ -47,6 +45,7 @@ impl FlowExternalServ { changed_kind: None, }) .collect_vec(), + sys_time: Some(Utc::now().timestamp_millis()), ..Default::default() }; debug!("do_fetch_rel_obj body: {:?}", body); @@ -66,12 +65,15 @@ impl FlowExternalServ { } } + #[allow(clippy::too_many_arguments)] pub async fn do_async_modify_field( tag: &str, transition_detail: Option, rel_business_obj_id: &str, inst_id: &str, callback_op: Option, + manual_op: Option, + operator: Option, target_state: Option, target_sys_state: Option, original_state: Option, @@ -93,6 +95,8 @@ impl FlowExternalServ { &rel_business_obj_id, &inst_id, callback_op, + manual_op, + operator, target_state, target_sys_state, original_state, @@ -109,12 +113,15 @@ impl FlowExternalServ { Ok(()) } + #[allow(clippy::too_many_arguments)] pub async fn do_modify_field( tag: &str, transition_detail: Option, rel_business_obj_id: &str, inst_id: &str, callback_op: Option, + manual_op: Option, + operator: Option, target_state: Option, target_sys_state: Option, original_state: Option, @@ -147,6 +154,8 @@ impl FlowExternalServ { let body = FlowExternalReq { kind: FlowExternalKind::ModifyField, callback_op, + manual_op, + operator, inst_id: inst_id.to_string(), curr_tag: tag.to_string(), curr_bus_obj_id: rel_business_obj_id.to_string(), @@ -156,6 +165,7 @@ impl FlowExternalServ { original_sys_state, notify: transition_detail.clone().map(|tran| tran.is_notify), transition_name: transition_detail.map(|tran| tran.name), + sys_time: Some(Utc::now().timestamp_millis()), params, ..Default::default() }; @@ -208,6 +218,7 @@ impl FlowExternalServ { original_sys_state: Some(original_sys_state), transition_name: Some(transition_name), notify: Some(is_notify), + sys_time: Some(Utc::now().timestamp_millis()), ..Default::default() }; debug!("do_notify_changes body: {:?}", body); @@ -247,6 +258,7 @@ impl FlowExternalServ { curr_bus_obj_id: "".to_string(), owner_paths: own_paths.to_string(), obj_ids: rel_business_obj_ids, + sys_time: Some(Utc::now().timestamp_millis()), ..Default::default() }; debug!("do_query_field body: {:?}", body); @@ -278,6 +290,7 @@ impl FlowExternalServ { inst_id: inst_id.to_string(), curr_tag: tag.to_string(), curr_bus_obj_id: rel_business_obj_id.to_string(), + sys_time: Some(Utc::now().timestamp_millis()), ..Default::default() }; debug!("do_delete_rel_obj body: {:?}", body); diff --git a/backend/middlewares/flow/src/serv/flow_inst_serv.rs b/backend/middlewares/flow/src/serv/flow_inst_serv.rs index e9a627d03..bd7c62200 100644 --- a/backend/middlewares/flow/src/serv/flow_inst_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_inst_serv.rs @@ -4,13 +4,11 @@ use std::{ }; use async_recursion::async_recursion; -use bios_basic::{ - rbum::{ - dto::rbum_filer_dto::RbumBasicFilterReq, - serv::{ - rbum_crud_serv::{CREATE_TIME_FIELD, ID_FIELD, NAME_FIELD, REL_DOMAIN_ID_FIELD, REL_KIND_ID_FIELD, UPDATE_TIME_FIELD}, - rbum_item_serv::{RbumItemCrudOperation, RBUM_ITEM_TABLE}, - }, +use bios_basic::rbum::{ + dto::rbum_filer_dto::RbumBasicFilterReq, + serv::{ + rbum_crud_serv::{CREATE_TIME_FIELD, ID_FIELD, NAME_FIELD, REL_DOMAIN_ID_FIELD, REL_KIND_ID_FIELD, UPDATE_TIME_FIELD}, + rbum_item_serv::{RbumItemCrudOperation, RBUM_ITEM_TABLE}, }, }; use bios_sdk_invoke::dto::search_item_dto::{SearchItemQueryReq, SearchItemSearchCtxReq, SearchItemSearchPageReq, SearchItemSearchReq}; @@ -433,7 +431,7 @@ impl FlowInstServ { let flow_inst_detail = Self::get(flow_inst_id, funs, ctx).await?; if !flow_inst_detail.main { - FlowLogServ::add_finish_log(&flow_inst_detail, funs, ctx).await?; + // FlowLogServ::add_finish_log(&flow_inst_detail, funs, ctx).await?; FlowSearchClient::modify_business_obj_search(&flow_inst_detail.rel_business_obj_id, &flow_inst_detail.tag, funs, ctx).await?; FlowSearchClient::async_add_or_modify_instance_search(&flow_inst_detail.id, Box::new(true), funs, ctx).await?; } @@ -890,8 +888,11 @@ impl FlowInstServ { let result = Self::do_transfer(flow_inst_detail, transfer_req, skip_filter, callback_kind, funs, ctx).await; Self::auto_transfer(&flow_inst_detail.id, modified_instance_transations_cp.clone(), funs, ctx).await?; - if flow_inst_detail.main { + if !flow_inst_detail.main { FlowSearchClient::async_add_or_modify_instance_search(&flow_inst_detail.id, Box::new(true), funs, ctx).await?; + } + + if flow_inst_detail.main { let flow_inst_cp = flow_inst_detail.clone(); let flow_transition_id = transfer_req.flow_transition_id.clone(); let ctx_cp = ctx.clone(); @@ -1016,6 +1017,8 @@ impl FlowInstServ { &flow_inst_detail.rel_business_obj_id, &flow_inst_detail.id, Some(FlowExternalCallbackOp::VerifyContent), + Some(true), + None, Some(next_flow_state.name.clone()), Some(next_flow_state.sys_state.clone()), Some(prev_flow_state.name.clone()), @@ -1074,7 +1077,7 @@ impl FlowInstServ { let curr_inst = Self::get(&flow_inst_detail.id, funs, ctx).await?; if next_flow_state.sys_state == FlowSysStateKind::Finish && !curr_inst.main { - FlowLogServ::add_finish_log(&curr_inst, funs, ctx).await?; + // FlowLogServ::add_finish_log(&curr_inst, funs, ctx).await?; FlowSearchClient::modify_business_obj_search(&curr_inst.rel_business_obj_id, &curr_inst.tag, funs, ctx).await?; } @@ -1636,12 +1639,12 @@ impl FlowInstServ { .into_iter() .map(|tran| tran.next_flow_transition_id) .collect_vec(); - let current_var = flow_inst_detail.current_vars.clone().unwrap_or_default(); + let create_vars = flow_inst_detail.create_vars.clone().unwrap_or_default(); let auto_transition = FlowTransitionServ::find_detail_items(transition_ids, None, None, funs, ctx).await?.into_iter().find(|transition| { (transition.transfer_by_auto && transition.guard_by_other_conds().is_none()) || (transition.transfer_by_auto && transition.guard_by_other_conds().is_some() - && BasicQueryCondInfo::check_or_and_conds(&transition.guard_by_other_conds().unwrap(), ¤t_var).unwrap()) + && BasicQueryCondInfo::check_or_and_conds(&transition.guard_by_other_conds().unwrap(), &create_vars).unwrap()) }); if let Some(auto_transition) = auto_transition { Self::transfer( @@ -1723,6 +1726,7 @@ impl FlowInstServ { } modify_req.curr_operators = Some(FlowSearchClient::search_guard_accounts(&guard_custom_conf, funs, ctx).await?); modify_req.prohibit_guard_conf_account_ids = Some(vec![]); + modify_req.state = Some(FlowInstStateKind::Form); Self::modify_inst_artifacts(&flow_inst_detail.id, &modify_req, funs, ctx).await?; // 当操作人为空时的逻辑 let curr_operators = Self::get(&flow_inst_detail.id, funs, ctx).await?.artifacts.unwrap_or_default().curr_operators.unwrap_or_default(); @@ -1747,16 +1751,6 @@ impl FlowInstServ { ctx, ) .await?; - Self::modify_inst_artifacts( - &flow_inst_detail.id, - &FlowInstArtifactsModifyReq { - state: Some(FlowInstStateKind::Form), - ..Default::default() - }, - funs, - ctx, - ) - .await?; Self::transfer( flow_inst_detail, &FlowInstTransferReq { @@ -1828,6 +1822,7 @@ impl FlowInstServ { modify_req.curr_approval_total = Some(curr_approval_total); modify_req.curr_operators = Some(guard_accounts); modify_req.prohibit_guard_conf_account_ids = Some(vec![]); + modify_req.state = Some(FlowInstStateKind::Approval); Self::modify_inst_artifacts(&flow_inst_detail.id, &modify_req, funs, ctx).await?; // 当操作人为空时的逻辑 @@ -1852,16 +1847,6 @@ impl FlowInstServ { ctx, ) .await?; - Self::modify_inst_artifacts( - &flow_inst_detail.id, - &FlowInstArtifactsModifyReq { - state: Some(FlowInstStateKind::Form), - ..Default::default() - }, - funs, - ctx, - ) - .await?; Self::transfer( flow_inst_detail, &FlowInstTransferReq { @@ -1910,6 +1895,8 @@ impl FlowInstServ { &flow_inst_detail.rel_business_obj_id, &flow_inst_detail.id, Some(FlowExternalCallbackOp::Auto), + Some(true), + Some("审批通过".to_string()), None, None, None, @@ -1939,6 +1926,8 @@ impl FlowInstServ { &flow_inst_detail.rel_business_obj_id, &flow_inst_detail.id, None, + Some(true), + Some("审批通过".to_string()), None, None, None, @@ -1973,9 +1962,8 @@ impl FlowInstServ { } // 当离开该节点时 - async fn when_leave_state(flow_inst_detail: &FlowInstDetailResp, state_id: &str, _flow_model_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + async fn when_leave_state(_flow_inst_detail: &FlowInstDetailResp, state_id: &str, _flow_model_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { // let rel_transition = FlowRelServ::find_from_simple_rels(&FlowRelKind::FlowModelTransition, flow_model_id, None, None, funs, ctx).await?.pop().map(|rel| rel.rel_id).unwrap_or_default(); - let mut prev_non_auto_state_id = flow_inst_detail.artifacts.clone().unwrap_or_default().prev_non_auto_state_id.unwrap_or_default(); let state = FlowStateServ::get_item( state_id, &FlowStateFilterReq { @@ -1992,26 +1980,8 @@ impl FlowInstServ { .await?; match state.state_kind { FlowStateKind::Start => {} - FlowStateKind::Form => { - prev_non_auto_state_id.push(state_id.to_string()); - let modify_req = FlowInstArtifactsModifyReq { - curr_operators: Some(vec![]), - prev_non_auto_state_id: Some(prev_non_auto_state_id), - prev_non_auto_account_id: Some(ctx.owner.clone()), - ..Default::default() - }; - Self::modify_inst_artifacts(&flow_inst_detail.id, &modify_req, funs, ctx).await?; - } - FlowStateKind::Approval => { - prev_non_auto_state_id.push(state_id.to_string()); - let modify_req = FlowInstArtifactsModifyReq { - curr_operators: Some(vec![]), - prev_non_auto_state_id: Some(prev_non_auto_state_id), - prev_non_auto_account_id: Some(ctx.owner.clone()), - ..Default::default() - }; - Self::modify_inst_artifacts(&flow_inst_detail.id, &modify_req, funs, ctx).await?; - } + FlowStateKind::Form => {} + FlowStateKind::Approval => {} FlowStateKind::Branch => {} FlowStateKind::Finish => {} _ => {} @@ -2106,6 +2076,7 @@ impl FlowInstServ { operators, form_conf: Some(FLowInstStateFormConf { form_vars_collect_conf: form.vars_collect.clone(), + form_referral_guard_custom_conf: form.referral_guard_custom_conf.clone(), }), approval_conf: None, } @@ -2132,6 +2103,7 @@ impl FlowInstServ { approval_conf: Some(FLowInstStateApprovalConf { approval_vars_collect_conf: Some(approval.vars_collect.clone()), form_vars_collect: artifacts.form_state_map.get(state_id).cloned().unwrap_or_default(), + approval_referral_guard_custom_conf: approval.referral_guard_custom_conf.clone(), }), } }), @@ -2228,11 +2200,14 @@ impl FlowInstServ { } // 提交 FlowStateOperatorKind::Submit => { + let mut prev_non_auto_state_id = inst.artifacts.clone().unwrap_or_default().prev_non_auto_state_id.unwrap_or_default(); + prev_non_auto_state_id.push(inst.current_state_id.clone()); Self::modify_inst_artifacts( &inst.id, &FlowInstArtifactsModifyReq { form_state_map: Some(operate_req.vars.clone().unwrap_or_default()), - state: Some(FlowInstStateKind::Form), + prev_non_auto_state_id: Some(prev_non_auto_state_id), + prev_non_auto_account_id: Some(ctx.owner.clone()), ..Default::default() }, funs, @@ -2273,6 +2248,16 @@ impl FlowInstServ { .await?; Self::transfer_spec_state(inst, &target_state_id, funs, ctx).await?; } else { + Self::modify_inst_artifacts( + &inst.id, + &FlowInstArtifactsModifyReq { + state: Some(FlowInstStateKind::Back), + ..Default::default() + }, + funs, + ctx, + ) + .await?; Self::abort(&inst.id, &FlowInstAbortReq { message: "".to_string() }, funs, ctx).await?; } } @@ -2306,11 +2291,15 @@ impl FlowInstServ { ctx, ) .await?; + let mut prev_non_auto_state_id = inst.artifacts.clone().unwrap_or_default().prev_non_auto_state_id.unwrap_or_default(); + prev_non_auto_state_id.push(inst.current_state_id.clone()); Self::modify_inst_artifacts( &inst.id, &FlowInstArtifactsModifyReq { state: Some(FlowInstStateKind::Pass), curr_operators: Some(vec![]), + prev_non_auto_state_id: Some(prev_non_auto_state_id), + prev_non_auto_account_id: Some(ctx.owner.clone()), ..Default::default() }, funs, diff --git a/backend/middlewares/flow/src/serv/flow_log_serv.rs b/backend/middlewares/flow/src/serv/flow_log_serv.rs index 4b46bc4ef..9c7b3a89b 100644 --- a/backend/middlewares/flow/src/serv/flow_log_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_log_serv.rs @@ -35,6 +35,7 @@ impl FlowLogServ { flow_model: &FlowModelDetailResp, ctx: &TardisContext, ) -> TardisResult<()> { + let artifacts = flow_inst_detail.artifacts.clone().unwrap_or_default(); let rel_transition = flow_model.rel_transition().unwrap_or_default(); let operand = match rel_transition.id.as_str() { "__EDIT__" => "编辑审批".to_string(), @@ -58,6 +59,10 @@ impl FlowLogServ { operand_kind: Some(FlowLogClient::get_junp_kind("FLOW")), ..Default::default() }; + if !artifacts.his_operators.as_ref().unwrap_or(&vec![]).contains(&ctx.owner) && !artifacts.curr_operators.as_ref().unwrap_or(&vec![]).contains(&ctx.owner) { + log_content.operand_id = None; + log_content.operand_kind = None; + } if start_req.create_vars.is_none() { log_ext.include_detail = Some(false); log_content.old_content = "".to_string(); @@ -93,6 +98,7 @@ impl FlowLogServ { flow_model: &FlowModelDetailResp, ctx: &TardisContext, ) -> TardisResult<()> { + let artifacts = flow_inst_detail.artifacts.clone().unwrap_or_default(); let rel_transition = flow_model.rel_transition().unwrap_or_default(); let operand = match rel_transition.id.as_str() { "__EDIT__" => "编辑审批".to_string(), @@ -116,6 +122,10 @@ impl FlowLogServ { operand_kind: Some(FlowLogClient::get_junp_kind("FLOW")), ..Default::default() }; + if !artifacts.his_operators.as_ref().unwrap_or(&vec![]).contains(&ctx.owner) && !artifacts.curr_operators.as_ref().unwrap_or(&vec![]).contains(&ctx.owner) { + log_content.operand_id = None; + log_content.operand_kind = None; + } if start_req.create_vars.is_none() { log_ext.include_detail = Some(false); log_content.old_content = "".to_string(); @@ -151,6 +161,7 @@ impl FlowLogServ { flow_model: &FlowModelDetailResp, ctx: &TardisContext, ) -> TardisResult<()> { + let artifacts = flow_inst_detail.artifacts.clone().unwrap_or_default(); let rel_transition = flow_model.rel_transition().unwrap_or_default(); let subject = match rel_transition.id.as_str() { "__EDIT__" => "编辑审批".to_string(), @@ -170,6 +181,10 @@ impl FlowLogServ { sub_kind: Some(FlowLogClient::get_junp_kind(&flow_inst_detail.tag)), ..Default::default() }; + if !artifacts.his_operators.as_ref().unwrap_or(&vec![]).contains(&ctx.owner) && !artifacts.curr_operators.as_ref().unwrap_or(&vec![]).contains(&ctx.owner) { + log_content.sub_id = None; + log_content.sub_kind = None; + } // if start_req.create_vars.is_none() { // log_ext.include_detail = Some(false); // log_content.old_content = "".to_string(); @@ -278,6 +293,7 @@ impl FlowLogServ { funs: &TardisFunsInst, ctx: &TardisContext, ) -> TardisResult<()> { + let artifacts = flow_inst_detail.artifacts.clone().unwrap_or_default(); let flow_model_version = FlowModelVersionServ::get_item( &flow_inst_detail.rel_flow_version_id, &FlowModelVersionFilterReq { @@ -352,6 +368,10 @@ impl FlowLogServ { new_content: "".to_string(), ..Default::default() }; + if !artifacts.his_operators.as_ref().unwrap_or(&vec![]).contains(&ctx.owner) && !artifacts.curr_operators.as_ref().unwrap_or(&vec![]).contains(&ctx.owner) { + log_content.sub_id = None; + log_content.sub_kind = None; + } if operate_req.operate == FlowStateOperatorKind::Referral { log_content.flow_referral = Some(FlowKvClient::get_account_name(&operate_req.operator.clone().unwrap_or_default(), funs, ctx).await?); } @@ -383,6 +403,7 @@ impl FlowLogServ { } pub async fn add_finish_log(flow_inst_detail: &FlowInstDetailResp, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + let artifacts = flow_inst_detail.artifacts.clone().unwrap_or_default(); let flow_model_version = FlowModelVersionServ::get_item( &flow_inst_detail.rel_flow_version_id, &FlowModelVersionFilterReq { @@ -423,7 +444,7 @@ impl FlowLogServ { project_id: rbum_scope_helper::get_path_item(RbumScopeLevelKind::L2.to_int(), &ctx.own_paths), ..Default::default() }; - let log_content = LogParamContent { + let mut log_content = LogParamContent { subject: Some(subject_text), name: Some(flow_inst_detail.code.clone()), sub_id: Some(flow_inst_detail.id.clone()), @@ -432,6 +453,10 @@ impl FlowLogServ { new_content: "".to_string(), ..Default::default() }; + if !artifacts.his_operators.as_ref().unwrap_or(&vec![]).contains(&ctx.owner) && !artifacts.curr_operators.as_ref().unwrap_or(&vec![]).contains(&ctx.owner) { + log_content.sub_id = None; + log_content.sub_kind = None; + } FlowLogClient::add_ctx_task( LogParamTag::ApprovalFlow, Some(flow_inst_detail.id.clone()), @@ -450,6 +475,7 @@ impl FlowLogServ { // 添加审批流结束业务日志 pub async fn add_finish_business_log(flow_inst_detail: &FlowInstDetailResp, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + let artifacts = flow_inst_detail.artifacts.clone().unwrap_or_default(); let flow_model_version = FlowModelVersionServ::get_item( &flow_inst_detail.rel_flow_version_id, &FlowModelVersionFilterReq { @@ -490,7 +516,7 @@ impl FlowLogServ { project_id: rbum_scope_helper::get_path_item(RbumScopeLevelKind::L2.to_int(), &ctx.own_paths), ..Default::default() }; - let log_content = LogParamContent { + let mut log_content = LogParamContent { subject: Some(FlowLogClient::get_flow_kind_text(&flow_inst_detail.tag)), name: Some(format!("编号{}", flow_inst_detail.code)), sub_id: Some(flow_inst_detail.id.clone()), @@ -498,6 +524,10 @@ impl FlowLogServ { sub_op: Some(sub_op), ..Default::default() }; + if !artifacts.his_operators.as_ref().unwrap_or(&vec![]).contains(&ctx.owner) && !artifacts.curr_operators.as_ref().unwrap_or(&vec![]).contains(&ctx.owner) { + log_content.sub_id = None; + log_content.sub_kind = None; + } FlowLogClient::add_ctx_task( LogParamTag::DynamicLog, Some(flow_inst_detail.rel_business_obj_id.clone()), diff --git a/backend/middlewares/flow/src/serv/flow_state_serv.rs b/backend/middlewares/flow/src/serv/flow_state_serv.rs index 439f97b24..e470bf9db 100644 --- a/backend/middlewares/flow/src/serv/flow_state_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_state_serv.rs @@ -86,6 +86,7 @@ impl RbumItemCrudOperation Date: Fri, 3 Jan 2025 09:53:43 +0800 Subject: [PATCH 24/35] flow: fix bug (find sub_role error) (#892) * flow: fix bug (sync search error) * flow: fix bug (get local role) * flow: merge main --- .../flow/src/dto/flow_state_dto.rs | 13 +++++++-- .../flow/src/serv/clients/kv_client.rs | 27 +++++++++++++++++++ .../flow/src/serv/clients/search_client.rs | 1 + .../flow/src/serv/flow_event_serv.rs | 2 +- .../flow/src/serv/flow_inst_serv.rs | 19 ++++++++----- .../flow/src/serv/flow_log_serv.rs | 2 +- .../sdks/invoke/src/clients/spi_kv_client.rs | 2 ++ 7 files changed, 56 insertions(+), 10 deletions(-) diff --git a/backend/middlewares/flow/src/dto/flow_state_dto.rs b/backend/middlewares/flow/src/dto/flow_state_dto.rs index 2562f0da5..c1ebc82f5 100644 --- a/backend/middlewares/flow/src/dto/flow_state_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_state_dto.rs @@ -7,14 +7,16 @@ use bios_basic::rbum::{ }; use serde::{Deserialize, Serialize}; use tardis::{ - basic::{dto::TardisContext, error::TardisError, field::TrimString}, + basic::{dto::TardisContext, error::TardisError, field::TrimString, result::TardisResult}, chrono::{DateTime, Utc}, db::sea_orm::{self, prelude::*, EnumIter}, serde_json::Value, web::poem_openapi, - TardisFuns, + TardisFuns, TardisFunsInst, }; +use crate::serv::clients::kv_client::FlowKvClient; + use super::flow_transition_dto::FlowTransitionDetailResp; #[derive(Clone, Serialize, Deserialize, Default, Debug, poem_openapi::Object)] @@ -225,6 +227,13 @@ impl FlowGuardConf { } false } + + pub async fn get_local_conf(&mut self, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + for role_id in self.guard_by_spec_role_ids.iter_mut() { + *role_id = FlowKvClient::get_role_id(role_id, funs, ctx).await?; + } + Ok(()) + } } // 节点通知配置 diff --git a/backend/middlewares/flow/src/serv/clients/kv_client.rs b/backend/middlewares/flow/src/serv/clients/kv_client.rs index d5eb32591..a6bd2672f 100644 --- a/backend/middlewares/flow/src/serv/clients/kv_client.rs +++ b/backend/middlewares/flow/src/serv/clients/kv_client.rs @@ -1,4 +1,6 @@ +use bios_basic::rbum::helper::rbum_scope_helper; use bios_sdk_invoke::clients::spi_kv_client::SpiKvClient; +use itertools::Itertools; use tardis::{ basic::{dto::TardisContext, result::TardisResult}, TardisFunsInst, @@ -14,4 +16,29 @@ impl FlowKvClient { .unwrap_or_default(); Ok(account_name) } + + pub async fn get_role_id(original_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult { + let mut role_id = "".to_string(); + if let Some(role_id_prefix) = original_id.split(':').collect_vec().first() { + role_id = SpiKvClient::match_items_by_key_prefix(format!("__k_n__:iam_role:{}", role_id_prefix), None, 1, 999, None, funs, ctx).await? + .map(|resp| { + resp.records.into_iter().filter(|record| ctx.own_paths.contains(&record.own_paths)).collect_vec() + }) + .map(|records| { + if let Some(item) = records.iter().find(|r| r.own_paths == ctx.own_paths) { + return item.key.clone(); + } + if let Some(item) = records.iter().find(|r| r.own_paths == rbum_scope_helper::get_path_item(1, &ctx.own_paths).unwrap_or_default()) { + return item.key.clone(); + } + if let Some(item) = records.iter().find(|r| r.own_paths.is_empty()) { + return item.key.clone(); + } + "".to_string() + }) + .unwrap_or_default(); + } + + Ok(role_id) + } } diff --git a/backend/middlewares/flow/src/serv/clients/search_client.rs b/backend/middlewares/flow/src/serv/clients/search_client.rs index b2335e699..9c6fa229c 100644 --- a/backend/middlewares/flow/src/serv/clients/search_client.rs +++ b/backend/middlewares/flow/src/serv/clients/search_client.rs @@ -302,6 +302,7 @@ impl FlowSearchClient { ext: Some(json!({ "tag": inst_resp.tag, "current_state_id": inst_resp.current_state_id, + "rel_business_obj_name": name.clone(), "current_state_name": inst_resp.current_state_name, "current_state_kind": inst_resp.current_state_kind, "rel_business_obj_id": inst_resp.rel_business_obj_id, diff --git a/backend/middlewares/flow/src/serv/flow_event_serv.rs b/backend/middlewares/flow/src/serv/flow_event_serv.rs index 0b3ea6128..f391ae0b7 100644 --- a/backend/middlewares/flow/src/serv/flow_event_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_event_serv.rs @@ -400,7 +400,7 @@ impl FlowEventServ { &flow_inst_detail.rel_business_obj_id, &flow_inst_detail.id, Some(FlowExternalCallbackOp::PostAction), - Some(true), + Some(false), None, Some(next_flow_state.name.clone()), Some(next_flow_state.sys_state.clone()), diff --git a/backend/middlewares/flow/src/serv/flow_inst_serv.rs b/backend/middlewares/flow/src/serv/flow_inst_serv.rs index bd7c62200..6a9a7782c 100644 --- a/backend/middlewares/flow/src/serv/flow_inst_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_inst_serv.rs @@ -656,6 +656,7 @@ impl FlowInstServ { &inst.current_state_kind.unwrap_or_default(), current_state_kind_conf, artifacts, + inst.finish_time.is_some(), ctx, ), current_vars: inst.current_vars.map(|current_vars| TardisFuns::json.json_to_obj(current_vars).unwrap()), @@ -1690,6 +1691,7 @@ impl FlowInstServ { let mut modify_req = FlowInstArtifactsModifyReq { ..Default::default() }; let form_conf = state.kind_conf().unwrap_or_default().form.unwrap_or_default(); let mut guard_custom_conf = form_conf.guard_custom_conf.unwrap_or_default(); + guard_custom_conf.get_local_conf(funs, ctx).await?; if form_conf.guard_by_creator { guard_custom_conf.guard_by_spec_account_ids.push(flow_inst_detail.create_ctx.owner.clone()); } @@ -1783,6 +1785,7 @@ impl FlowInstServ { let mut modify_req = FlowInstArtifactsModifyReq { ..Default::default() }; let approval_conf = state.kind_conf().unwrap_or_default().approval.unwrap_or_default(); let mut guard_custom_conf = approval_conf.guard_custom_conf.unwrap_or_default(); + guard_custom_conf.get_local_conf(funs, ctx).await?; if approval_conf.guard_by_creator { guard_custom_conf.guard_by_spec_account_ids.push(flow_inst_detail.create_ctx.owner.clone()); } @@ -1865,6 +1868,7 @@ impl FlowInstServ { } FlowStatusAutoStrategyKind::SpecifyAgent => { let auto_transfer_when_empty_guard_custom_conf = approval_conf.auto_transfer_when_empty_guard_custom_conf.clone().unwrap_or_default(); + guard_custom_conf.get_local_conf(funs, ctx).await?; let guard_accounts = FlowSearchClient::search_guard_accounts(&auto_transfer_when_empty_guard_custom_conf, funs, ctx).await?; modify_req.curr_approval_total = Some(guard_accounts.len()); modify_req.curr_operators = Some(guard_accounts); @@ -1895,7 +1899,7 @@ impl FlowInstServ { &flow_inst_detail.rel_business_obj_id, &flow_inst_detail.id, Some(FlowExternalCallbackOp::Auto), - Some(true), + None, Some("审批通过".to_string()), None, None, @@ -1926,7 +1930,7 @@ impl FlowInstServ { &flow_inst_detail.rel_business_obj_id, &flow_inst_detail.id, None, - Some(true), + None, Some("审批通过".to_string()), None, None, @@ -2057,6 +2061,7 @@ impl FlowInstServ { state_kind: &FlowStateKind, kind_conf: Option, artifacts: Option, + finish: bool, ctx: &TardisContext, ) -> Option { if let Some(kind_conf) = kind_conf { @@ -2090,11 +2095,11 @@ impl FlowInstServ { operators.insert(FlowStateOperatorKind::Pass, approval.pass_btn_name.clone()); operators.insert(FlowStateOperatorKind::Overrule, approval.overrule_btn_name.clone()); operators.insert(FlowStateOperatorKind::Back, approval.back_btn_name.clone()); - if approval.referral { + if approval.referral && !finish { operators.insert(FlowStateOperatorKind::Referral, "".to_string()); } } - if approval.revoke && ctx.owner == artifacts.prev_non_auto_account_id.unwrap_or_default() { + if approval.revoke && ctx.owner == artifacts.prev_non_auto_account_id.unwrap_or_default() && !finish { operators.insert(FlowStateOperatorKind::Revoke, "".to_string()); } FLowInstStateConf { @@ -2402,11 +2407,13 @@ impl FlowInstServ { return Ok(true); } let countersign_conf = current_state_kind_conf.countersign_conf; + let mut specified_pass_guard_conf = countersign_conf.specified_pass_guard_conf.clone().unwrap_or_default(); + specified_pass_guard_conf.get_local_conf(funs, ctx).await?; // 指定人通过,则通过 if kind == FlowApprovalResultKind::Pass && countersign_conf.specified_pass_guard.unwrap_or(false) && countersign_conf.specified_pass_guard_conf.is_some() - && countersign_conf.specified_pass_guard_conf.unwrap().check(ctx) + && specified_pass_guard_conf.check(ctx) { return Ok(true); } @@ -2414,7 +2421,7 @@ impl FlowInstServ { if kind == FlowApprovalResultKind::Overrule && countersign_conf.specified_overrule_guard.unwrap_or(false) && countersign_conf.specified_overrule_guard_conf.is_some() - && countersign_conf.specified_overrule_guard_conf.unwrap().check(ctx) + && specified_pass_guard_conf.check(ctx) { return Ok(true); } diff --git a/backend/middlewares/flow/src/serv/flow_log_serv.rs b/backend/middlewares/flow/src/serv/flow_log_serv.rs index 9c7b3a89b..c0a8448d2 100644 --- a/backend/middlewares/flow/src/serv/flow_log_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_log_serv.rs @@ -357,7 +357,7 @@ impl FlowLogServ { subject: Some(subject_text), name: Some(flow_inst_detail.code.clone()), sub_id: Some(flow_inst_detail.id.clone()), - sub_kind: Some(FlowLogClient::get_junp_kind("FLOW")), + sub_kind: Some(FlowLogClient::get_junp_kind(&flow_inst_detail.tag)), operand: Some(operand), operand_name: Some(current_state.name), operand_id: Some(flow_inst_detail.rel_business_obj_id.clone()), diff --git a/frontend/sdks/invoke/src/clients/spi_kv_client.rs b/frontend/sdks/invoke/src/clients/spi_kv_client.rs index 885ef1ab3..6555f7a31 100644 --- a/frontend/sdks/invoke/src/clients/spi_kv_client.rs +++ b/frontend/sdks/invoke/src/clients/spi_kv_client.rs @@ -42,6 +42,8 @@ pub struct KvItemSummaryResp { pub value: Value, pub info: String, pub disable: bool, + pub owner: String, + pub own_paths: String, pub create_time: DateTime, pub update_time: DateTime, } From 45c745da53264d5c8be1b82d2685cf776ebed63b Mon Sep 17 00:00:00 2001 From: RWDai <27391645+RWDai@users.noreply.github.com> Date: Fri, 3 Jan 2025 10:46:45 +0800 Subject: [PATCH 25/35] fix: handle JSON parsing error in log_pg_item_serv.rs --- backend/spi/spi-log/src/serv/pg/log_pg_item_serv.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/spi/spi-log/src/serv/pg/log_pg_item_serv.rs b/backend/spi/spi-log/src/serv/pg/log_pg_item_serv.rs index 637bce46c..d83542f02 100644 --- a/backend/spi/spi-log/src/serv/pg/log_pg_item_serv.rs +++ b/backend/spi/spi-log/src/serv/pg/log_pg_item_serv.rs @@ -1,7 +1,7 @@ use tardis::{ basic::{dto::TardisContext, result::TardisResult}, db::{reldb_client::TardisRelDBClient, sea_orm::Value}, - serde_json::Value as JsonValue, + serde_json::{json, Value as JsonValue}, web::web_resp::TardisPage, TardisFuns, TardisFunsInst, }; @@ -489,7 +489,10 @@ ORDER BY ts DESC total_size = item.try_get("", "total")?; } let content: String = item.try_get("", "content")?; - let content = TardisFuns::json.str_to_json(&content)?; + let content = match TardisFuns::json.str_to_json(&content) { + Ok(content) => content, + Err(_) => json!({"content": content}), + }; Ok(LogItemFindResp { ts: item.try_get("", "ts")?, id: item.try_get("", "id")?, From af84b09b0c1d641e4aaf18f8838d9164644be0c0 Mon Sep 17 00:00:00 2001 From: ZzIsGod1019 <1498852723@qq.com> Date: Fri, 3 Jan 2025 22:34:27 +0800 Subject: [PATCH 26/35] flow: add async error log (#893) * flow: fix bug (sync search error) * flow: fix bug (get local role) * flow: merge main * flow: add async error log * flow: fix merge error * flow: fix bug (find role id error) --- Cargo.toml | 2 +- .../flow/src/serv/clients/kv_client.rs | 6 ++-- .../flow/src/serv/clients/search_client.rs | 9 ++++-- .../flow/src/serv/flow_inst_serv.rs | 30 +++++++++++-------- .../flow/src/serv/flow_log_serv.rs | 9 +++--- 5 files changed, 34 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cd7a8df0d..f4fa4805d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,7 +65,7 @@ strum = { version = "0.26", features = ["derive"] } # tardis # tardis = { version = "0.1.0-rc.17" } # tardis = { version = "0.2.0", path = "../tardis/tardis" } -tardis = { git = "https://github.com/ideal-world/tardis.git", rev = "66d4c63" } +tardis = { git = "https://github.com/ideal-world/tardis.git", rev = "f666d50" } # asteroid-mq = { git = "https://github.com/4t145/asteroid-mq.git", rev = "d59c64d" } asteroid-mq = { git = "https://github.com/4t145/asteroid-mq.git", rev = "b26fa4f" } # asteroid-mq = { version = "0.1.0-alpha.5" } diff --git a/backend/middlewares/flow/src/serv/clients/kv_client.rs b/backend/middlewares/flow/src/serv/clients/kv_client.rs index a6bd2672f..ea2ca6fce 100644 --- a/backend/middlewares/flow/src/serv/clients/kv_client.rs +++ b/backend/middlewares/flow/src/serv/clients/kv_client.rs @@ -26,13 +26,13 @@ impl FlowKvClient { }) .map(|records| { if let Some(item) = records.iter().find(|r| r.own_paths == ctx.own_paths) { - return item.key.clone(); + return item.key.split("__k_n__:iam_role:").collect_vec().pop().map(|s| s.to_string()).unwrap_or_default(); } if let Some(item) = records.iter().find(|r| r.own_paths == rbum_scope_helper::get_path_item(1, &ctx.own_paths).unwrap_or_default()) { - return item.key.clone(); + return item.key.split("__k_n__:iam_role:").collect_vec().pop().map(|s| s.to_string()).unwrap_or_default(); } if let Some(item) = records.iter().find(|r| r.own_paths.is_empty()) { - return item.key.clone(); + return item.key.split("__k_n__:iam_role:").collect_vec().pop().map(|s| s.to_string()).unwrap_or_default(); } "".to_string() }) diff --git a/backend/middlewares/flow/src/serv/clients/search_client.rs b/backend/middlewares/flow/src/serv/clients/search_client.rs index 9c6fa229c..f39b5f2af 100644 --- a/backend/middlewares/flow/src/serv/clients/search_client.rs +++ b/backend/middlewares/flow/src/serv/clients/search_client.rs @@ -15,7 +15,7 @@ use itertools::Itertools; use serde_json::json; use tardis::{ basic::{dto::TardisContext, field::TrimString, result::TardisResult}, - log::debug, + log::{debug, error}, tokio, web::{poem_openapi::types::ToJSON, web_resp::TardisPage}, TardisFuns, TardisFunsInst, @@ -267,11 +267,15 @@ impl FlowSearchClient { let inst_resp = FlowInstServ::get(inst_id, funs, &mock_ctx).await?; ctx.add_async_task(Box::new(|| { Box::pin(async move { + let inst_id_cp = inst_resp.id.clone(); let task_handle = tokio::spawn(async move { let funs = flow_constants::get_tardis_inst(); let _ = Self::add_or_modify_instance_search(&inst_resp, is_modify, &funs, &ctx_clone).await; }); - task_handle.await.unwrap(); + match task_handle.await { + Ok(_) => {} + Err(e) => error!("Flow search_client {} async_add_or_modify_instance_search error:{:?}", inst_id_cp, e), + } Ok(()) }) })) @@ -346,6 +350,7 @@ impl FlowSearchClient { "current_state_id": inst_resp.current_state_id, "current_state_name": inst_resp.current_state_name, "current_state_kind": inst_resp.current_state_kind, + "rel_business_obj_name": name.clone(), "rel_business_obj_id": inst_resp.rel_business_obj_id, "finish_time": inst_resp.finish_time, "op_time": inst_resp.update_time, diff --git a/backend/middlewares/flow/src/serv/flow_inst_serv.rs b/backend/middlewares/flow/src/serv/flow_inst_serv.rs index 6a9a7782c..47d59dd1a 100644 --- a/backend/middlewares/flow/src/serv/flow_inst_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_inst_serv.rs @@ -431,7 +431,7 @@ impl FlowInstServ { let flow_inst_detail = Self::get(flow_inst_id, funs, ctx).await?; if !flow_inst_detail.main { - // FlowLogServ::add_finish_log(&flow_inst_detail, funs, ctx).await?; + FlowLogServ::add_finish_log(&flow_inst_detail, funs, ctx).await?; FlowSearchClient::modify_business_obj_search(&flow_inst_detail.rel_business_obj_id, &flow_inst_detail.tag, funs, ctx).await?; FlowSearchClient::async_add_or_modify_instance_search(&flow_inst_detail.id, Box::new(true), funs, ctx).await?; } @@ -1078,7 +1078,7 @@ impl FlowInstServ { let curr_inst = Self::get(&flow_inst_detail.id, funs, ctx).await?; if next_flow_state.sys_state == FlowSysStateKind::Finish && !curr_inst.main { - // FlowLogServ::add_finish_log(&curr_inst, funs, ctx).await?; + FlowLogServ::add_finish_log(&curr_inst, funs, ctx).await?; FlowSearchClient::modify_business_obj_search(&curr_inst.rel_business_obj_id, &curr_inst.tag, funs, ctx).await?; } @@ -1691,7 +1691,9 @@ impl FlowInstServ { let mut modify_req = FlowInstArtifactsModifyReq { ..Default::default() }; let form_conf = state.kind_conf().unwrap_or_default().form.unwrap_or_default(); let mut guard_custom_conf = form_conf.guard_custom_conf.unwrap_or_default(); - guard_custom_conf.get_local_conf(funs, ctx).await?; + if state.own_paths != flow_inst_detail.own_paths { + guard_custom_conf.get_local_conf(funs, ctx).await?; + } if form_conf.guard_by_creator { guard_custom_conf.guard_by_spec_account_ids.push(flow_inst_detail.create_ctx.owner.clone()); } @@ -1785,7 +1787,9 @@ impl FlowInstServ { let mut modify_req = FlowInstArtifactsModifyReq { ..Default::default() }; let approval_conf = state.kind_conf().unwrap_or_default().approval.unwrap_or_default(); let mut guard_custom_conf = approval_conf.guard_custom_conf.unwrap_or_default(); - guard_custom_conf.get_local_conf(funs, ctx).await?; + if state.own_paths != flow_inst_detail.own_paths { + guard_custom_conf.get_local_conf(funs, ctx).await?; + } if approval_conf.guard_by_creator { guard_custom_conf.guard_by_spec_account_ids.push(flow_inst_detail.create_ctx.owner.clone()); } @@ -1867,8 +1871,10 @@ impl FlowInstServ { } } FlowStatusAutoStrategyKind::SpecifyAgent => { - let auto_transfer_when_empty_guard_custom_conf = approval_conf.auto_transfer_when_empty_guard_custom_conf.clone().unwrap_or_default(); - guard_custom_conf.get_local_conf(funs, ctx).await?; + let mut auto_transfer_when_empty_guard_custom_conf = approval_conf.auto_transfer_when_empty_guard_custom_conf.clone().unwrap_or_default(); + if state.own_paths != flow_inst_detail.own_paths { + auto_transfer_when_empty_guard_custom_conf.get_local_conf(funs, ctx).await?; + } let guard_accounts = FlowSearchClient::search_guard_accounts(&auto_transfer_when_empty_guard_custom_conf, funs, ctx).await?; modify_req.curr_approval_total = Some(guard_accounts.len()); modify_req.curr_operators = Some(guard_accounts); @@ -2377,7 +2383,7 @@ impl FlowInstServ { // 判断审批条件是否满足 async fn check_approval_cond(inst: &FlowInstDetailResp, kind: FlowApprovalResultKind, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult { - let current_state_kind_conf = FlowStateServ::get_item( + let current_state = FlowStateServ::get_item( &inst.current_state_id, &FlowStateFilterReq { basic: RbumBasicFilterReq { @@ -2390,10 +2396,8 @@ impl FlowInstServ { funs, ctx, ) - .await? - .kind_conf() - .unwrap_or_default() - .approval; + .await?; + let current_state_kind_conf = current_state.kind_conf().unwrap_or_default().approval; let artifacts = inst.artifacts.clone().unwrap_or_default(); let approval_total = artifacts.approval_total.unwrap_or_default().get(&inst.current_state_id).cloned().unwrap_or_default(); let approval_result = artifacts.approval_result.get(&inst.current_state_id).cloned().unwrap_or_default(); @@ -2408,7 +2412,9 @@ impl FlowInstServ { } let countersign_conf = current_state_kind_conf.countersign_conf; let mut specified_pass_guard_conf = countersign_conf.specified_pass_guard_conf.clone().unwrap_or_default(); - specified_pass_guard_conf.get_local_conf(funs, ctx).await?; + if current_state.own_paths != inst.own_paths { + specified_pass_guard_conf.get_local_conf(funs, ctx).await?; + } // 指定人通过,则通过 if kind == FlowApprovalResultKind::Pass && countersign_conf.specified_pass_guard.unwrap_or(false) diff --git a/backend/middlewares/flow/src/serv/flow_log_serv.rs b/backend/middlewares/flow/src/serv/flow_log_serv.rs index c0a8448d2..a2b602dd0 100644 --- a/backend/middlewares/flow/src/serv/flow_log_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_log_serv.rs @@ -123,6 +123,8 @@ impl FlowLogServ { ..Default::default() }; if !artifacts.his_operators.as_ref().unwrap_or(&vec![]).contains(&ctx.owner) && !artifacts.curr_operators.as_ref().unwrap_or(&vec![]).contains(&ctx.owner) { + log_content.sub_id = None; + log_content.sub_kind = None; log_content.operand_id = None; log_content.operand_kind = None; } @@ -369,8 +371,7 @@ impl FlowLogServ { ..Default::default() }; if !artifacts.his_operators.as_ref().unwrap_or(&vec![]).contains(&ctx.owner) && !artifacts.curr_operators.as_ref().unwrap_or(&vec![]).contains(&ctx.owner) { - log_content.sub_id = None; - log_content.sub_kind = None; + log_content.operand_id = None; } if operate_req.operate == FlowStateOperatorKind::Referral { log_content.flow_referral = Some(FlowKvClient::get_account_name(&operate_req.operator.clone().unwrap_or_default(), funs, ctx).await?); @@ -448,14 +449,14 @@ impl FlowLogServ { subject: Some(subject_text), name: Some(flow_inst_detail.code.clone()), sub_id: Some(flow_inst_detail.id.clone()), - sub_kind: Some(FlowLogClient::get_junp_kind("FLOW")), + sub_kind: Some(FlowLogClient::get_junp_kind(&flow_inst_detail.tag)), old_content: "".to_string(), new_content: "".to_string(), ..Default::default() }; + if !artifacts.his_operators.as_ref().unwrap_or(&vec![]).contains(&ctx.owner) && !artifacts.curr_operators.as_ref().unwrap_or(&vec![]).contains(&ctx.owner) { log_content.sub_id = None; - log_content.sub_kind = None; } FlowLogClient::add_ctx_task( LogParamTag::ApprovalFlow, From cc95530b6968b298fc2952b6ec93e3d5297f7f77 Mon Sep 17 00:00:00 2001 From: RWDai <27391645+RWDai@users.noreply.github.com> Date: Sat, 4 Jan 2025 00:18:14 +0800 Subject: [PATCH 27/35] feat: add in_event config to sdk --- backend/basic/src/dto.rs | 14 +-- .../spacegate-plugins/src/plugin/audit_log.rs | 1 + backend/spi/spi-log/Cargo.toml | 7 +- backend/spi/spi-log/src/dto/log_item_dto.rs | 40 +++---- .../spi-log/src/serv/pgv2/log_pg_item_serv.rs | 39 +++---- backend/spi/spi-log/tests/test_log.rs | 2 +- .../spi/spi-stats/src/dto/stats_record_dto.rs | 28 +++-- backend/spi/spi-stats/src/event.rs | 29 +++-- .../basic/serv/clients/iam_stats_client.rs | 2 + .../sdks/invoke/src/clients/spi_log_client.rs | 49 ++------- .../invoke/src/clients/spi_search_client.rs | 20 ++-- .../invoke/src/clients/spi_stats_client.rs | 100 +++++++++++++++++- .../sdks/invoke/src/dto/stats_record_dto.rs | 28 ++++- frontend/sdks/invoke/src/invoke_config.rs | 24 +++++ 14 files changed, 255 insertions(+), 128 deletions(-) diff --git a/backend/basic/src/dto.rs b/backend/basic/src/dto.rs index 4fc4a5c77..4276d80e0 100644 --- a/backend/basic/src/dto.rs +++ b/backend/basic/src/dto.rs @@ -88,12 +88,14 @@ impl BasicQueryCondInfo { | BasicQueryOpKind::NotRLike => { check_val.as_str().map(|check_val_str| cond.value.as_str().map(|cond_val_str| check_val_str.contains(cond_val_str)).unwrap_or(false)).unwrap_or(false) } - BasicQueryOpKind::In => check_val.as_array().map(|check_val_arr| { - if cond.value.is_array() { - cond.value.as_array().unwrap_or(&vec![]).iter().any(|item| check_val_arr.contains(&item)) - } else { - check_val_arr.contains(&cond.value) - } + BasicQueryOpKind::In => check_val + .as_array() + .map(|check_val_arr| { + if cond.value.is_array() { + cond.value.as_array().unwrap_or(&vec![]).iter().any(|item| check_val_arr.contains(&item)) + } else { + check_val_arr.contains(&cond.value) + } }) .unwrap_or(false), BasicQueryOpKind::NotIn => check_val.as_array().map(|check_val_arr| check_val_arr.contains(&cond.value)).unwrap_or(false), diff --git a/backend/gateways/spacegate-plugins/src/plugin/audit_log.rs b/backend/gateways/spacegate-plugins/src/plugin/audit_log.rs index 809beb867..003a39083 100644 --- a/backend/gateways/spacegate-plugins/src/plugin/audit_log.rs +++ b/backend/gateways/spacegate-plugins/src/plugin/audit_log.rs @@ -156,6 +156,7 @@ impl AuditLogPlugin { InvokeConfig { spi_app_id: self.spi_app_id.clone(), module_urls: HashMap::from([(InvokeModuleKind::Log.to_string(), self.log_url.clone())]), + ..Default::default() }, )?; } diff --git a/backend/spi/spi-log/Cargo.toml b/backend/spi/spi-log/Cargo.toml index 3684c5b20..2ec9b5a35 100644 --- a/backend/spi/spi-log/Cargo.toml +++ b/backend/spi/spi-log/Cargo.toml @@ -24,8 +24,13 @@ tardis = { workspace = true, features = ["reldb-postgres", "web-server"] } bios-basic = { version = "0.2.0", path = "../../basic", features = ["default"] } bios-sdk-invoke = { version = "0.2.0", path = "../../../frontend/sdks/invoke", features = [ "event", + "spi_log", + "spi_stats", ], default-features = false } [dev-dependencies] tardis = { workspace = true, features = ["test"] } -bios-basic = { version = "0.2.0", path = "../../basic", features = ["default", "test"] } +bios-basic = { version = "0.2.0", path = "../../basic", features = [ + "default", + "test", +] } diff --git a/backend/spi/spi-log/src/dto/log_item_dto.rs b/backend/spi/spi-log/src/dto/log_item_dto.rs index 5414cead2..d1f83b8ae 100644 --- a/backend/spi/spi-log/src/dto/log_item_dto.rs +++ b/backend/spi/spi-log/src/dto/log_item_dto.rs @@ -1,5 +1,5 @@ use bios_basic::{dto::BasicQueryCondInfo, enumeration::BasicQueryOpKind}; -use bios_sdk_invoke::clients::spi_log_client::{StatsItemAddReq, StatsItemDeleteReq}; +use bios_sdk_invoke::dto::stats_record_dto::StatsFactRecordLoadReq; use serde::{Deserialize, Serialize}; use tardis::{ basic::field::TrimString, @@ -96,6 +96,19 @@ impl From for LogItem } } } +impl From for StatsFactRecordLoadReq { + fn from(value: LogItemAddV2Req) -> Self { + StatsFactRecordLoadReq { + own_paths: value.own_paths.unwrap_or_default(), + ct: value.ts.unwrap_or_default(), + idempotent_id: value.idempotent_id, + ignore_updates: None, + data: value.content, + ext: value.ext, + } + } +} + #[derive(poem_openapi::Object, Serialize, Deserialize, Debug)] pub struct LogItemFindReq { #[oai(validator(pattern = r"^[a-z0-9_]+$"))] @@ -156,28 +169,3 @@ pub struct LogConfigReq { pub tag: String, pub ref_field: String, } - -impl From for StatsItemAddReq { - fn from(value: LogItemAddV2Req) -> Self { - StatsItemAddReq { - idempotent_id: value.idempotent_id, - tag: value.tag, - content: value.content, - ext: value.ext, - key: value.key, - ts: value.ts, - owner: value.owner, - own_paths: value.own_paths, - } - } -} - -impl From for StatsItemDeleteReq { - fn from(value: LogItemAddV2Req) -> Self { - StatsItemDeleteReq { - idempotent_id: value.idempotent_id, - tag: value.tag, - key: value.key, - } - } -} diff --git a/backend/spi/spi-log/src/serv/pgv2/log_pg_item_serv.rs b/backend/spi/spi-log/src/serv/pgv2/log_pg_item_serv.rs index 7b5664852..a7772a93a 100644 --- a/backend/spi/spi-log/src/serv/pgv2/log_pg_item_serv.rs +++ b/backend/spi/spi-log/src/serv/pgv2/log_pg_item_serv.rs @@ -1,9 +1,6 @@ use std::{collections::HashMap, str::FromStr, vec}; -use bios_sdk_invoke::clients::{ - event_client::{get_topic, mq_error, EventAttributeExt as _, SPI_RPC_TOPIC}, - spi_log_client::{StatsItemAddReq, StatsItemDeleteReq}, -}; +use bios_sdk_invoke::clients::{event_client::EventAttributeExt as _, spi_stats_client::SpiStatsClient}; use tardis::{ basic::{dto::TardisContext, error::TardisError, result::TardisResult}, chrono::{DateTime, Utc}, @@ -101,7 +98,7 @@ pub async fn addv2(add_req: &mut LogItemAddV2Req, funs: &TardisFunsInst, ctx: &T } } // 对比一下这个字段现在的值和ref字段的原始值 - if insert_field_value.to_string() == ref_origin_value.to_string() { + if *insert_field_value == ref_origin_value { // 如果一样,那么把这次ref字段改成ref_key *insert_field_value = last_ref_key; } @@ -149,7 +146,7 @@ VALUES conn.commit().await?; //if push is true, then push to EDA if add_req.push { - push_to_eda(&add_req, &ref_fields, funs, ctx).await?; + push_to_eda(add_req, &ref_fields, funs, ctx).await?; } Ok(id) } @@ -754,27 +751,25 @@ async fn get_ref_fields_by_table_name(conn: &TardisRelDBlConnection, schema_name } async fn push_to_eda(req: &LogItemAddV2Req, ref_fields: &Vec, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { - if let Some(topic) = get_topic(&SPI_RPC_TOPIC) { - let mut req_clone = req.clone(); - for ref_field in ref_fields { - if let Some(content) = req_clone.content.as_object_mut() { - content.remove(ref_field); - } - } + let mut req_clone = req.clone(); + for ref_field in ref_fields { if let Some(content) = req_clone.content.as_object_mut() { - if !content.contains_key("owner") { - content.insert("owner".to_owned(), tardis::serde_json::Value::String(req.owner.clone().unwrap_or_default())); - } + content.remove(ref_field); } - // if the op is deleted or log disabled, send the delete event to stats - if (req_clone.op.as_ref().map_or(false, |op| op.to_lowercase() == "delete")) || req_clone.disable.unwrap_or(false) { - let stats_delete: StatsItemDeleteReq = req_clone.into(); - topic.send_event_and_wait(stats_delete.inject_context(funs, ctx).json()).map_err(mq_error).await?; + } + if let Some(content) = req_clone.content.as_object_mut() { + if !content.contains_key("owner") { + content.insert("owner".to_owned(), tardis::serde_json::Value::String(req.owner.clone().unwrap_or_default())); + } + } + // if the op is deleted or log disabled, send the delete event to stats + if (req_clone.op.as_ref().map_or(false, |op| op.to_lowercase() == "delete")) || req_clone.disable.unwrap_or(false) { + if let Some(key) = req_clone.key.clone() { + SpiStatsClient::fact_record_delete(&req_clone.tag, &key, funs, ctx).await?; return Ok(()); } - let stats_add: StatsItemAddReq = req_clone.into(); - topic.send_event_and_wait(stats_add.inject_context(funs, ctx).json()).map_err(mq_error).await?; } + SpiStatsClient::fact_record_load(&req_clone.tag.clone(), &req_clone.key.clone().unwrap_or_default(), req_clone.into(), funs, ctx).await?; Ok(()) } diff --git a/backend/spi/spi-log/tests/test_log.rs b/backend/spi/spi-log/tests/test_log.rs index f073f84db..c0a1de28d 100644 --- a/backend/spi/spi-log/tests/test_log.rs +++ b/backend/spi/spi-log/tests/test_log.rs @@ -13,7 +13,7 @@ use tardis::basic::field::TrimString; use tardis::basic::result::TardisResult; use tardis::tokio::time::sleep; use tardis::web::web_resp::Void; -use tardis::{testcontainers, tokio, TardisFuns}; +use tardis::{tokio, TardisFuns}; mod test_log_item; #[tokio::test] diff --git a/backend/spi/spi-stats/src/dto/stats_record_dto.rs b/backend/spi/spi-stats/src/dto/stats_record_dto.rs index 505126228..148173d3c 100644 --- a/backend/spi/spi-stats/src/dto/stats_record_dto.rs +++ b/backend/spi/spi-stats/src/dto/stats_record_dto.rs @@ -1,4 +1,4 @@ -use bios_sdk_invoke::clients::spi_log_client::StatsItemAddReq; +use bios_sdk_invoke::clients::spi_stats_client::StatsItemAddReq; use serde::{Deserialize, Serialize}; use tardis::{ chrono::{DateTime, Utc}, @@ -40,14 +40,14 @@ pub struct StatsFactRecordLoadReq { pub ext: Option, } -impl From for StatsFactRecordLoadReq { - fn from(value: StatsItemAddReq) -> Self { +impl From for StatsFactRecordLoadReq { + fn from(value: bios_sdk_invoke::dto::stats_record_dto::StatsFactRecordLoadReq) -> Self { StatsFactRecordLoadReq { - own_paths: value.own_paths.unwrap_or_default(), - ct: value.ts.unwrap_or(Utc::now()), + own_paths: value.own_paths, + ct: value.ct, idempotent_id: value.idempotent_id, - ignore_updates: None, - data: value.content, + ignore_updates: value.ignore_updates, + data: value.data, ext: value.ext, } } @@ -89,6 +89,20 @@ pub struct StatsFactRecordsLoadReq { pub ext: Option, } +impl From for StatsFactRecordsLoadReq { + fn from(value: bios_sdk_invoke::dto::stats_record_dto::StatsFactRecordsLoadReq) -> Self { + StatsFactRecordsLoadReq { + key: value.key, + own_paths: value.own_paths, + ct: value.ct, + idempotent_id: value.idempotent_id, + ignore_updates: value.ignore_updates, + data: value.data, + ext: value.ext, + } + } +} + /// Add Dimension Record Request Object /// /// 添加维度记录请求对象 diff --git a/backend/spi/spi-stats/src/event.rs b/backend/spi/spi-stats/src/event.rs index fb5a55c32..c7531fc40 100644 --- a/backend/spi/spi-stats/src/event.rs +++ b/backend/spi/spi-stats/src/event.rs @@ -1,7 +1,7 @@ -use crate::{dto::stats_record_dto::StatsFactRecordLoadReq, get_tardis_inst, serv::stats_record_serv}; +use crate::{get_tardis_inst, serv::stats_record_serv}; use bios_sdk_invoke::clients::{ event_client::{get_topic, mq_error, ContextHandler, SPI_RPC_TOPIC}, - spi_log_client::{StatsItemAddReq, StatsItemDeleteReq}, + spi_stats_client::{StatsItemAddReq, StatsItemAddsReq, StatsItemDeleteReq}, }; use tardis::{ basic::{dto::TardisContext, result::TardisResult}, @@ -11,25 +11,21 @@ use tardis::{ #[instrument] async fn handle_add_event(req: StatsItemAddReq, ctx: TardisContext) -> TardisResult<()> { let funs = get_tardis_inst(); - if let Some(ref key) = req.key { - let record_load_req = StatsFactRecordLoadReq { - own_paths: req.own_paths.unwrap_or_default(), - ct: req.ts.unwrap_or_default(), - idempotent_id: req.idempotent_id, - ignore_updates: Some(true), - data: req.content, - ext: req.ext, - }; - stats_record_serv::fact_record_load(&req.tag, key.as_ref(), record_load_req, &funs, &ctx).await?; - } + stats_record_serv::fact_record_load(&req.fact_key, &req.record_key, req.req.into(), &funs, &ctx).await?; + Ok(()) +} + +#[instrument] +async fn handle_adds_event(req: StatsItemAddsReq, ctx: TardisContext) -> TardisResult<()> { + let funs = get_tardis_inst(); + stats_record_serv::fact_records_load(&req.fact_key, req.reqs.into_iter().map(|r| r.into()).collect(), &funs, &ctx).await?; Ok(()) } + #[instrument] async fn handle_delete_event(req: StatsItemDeleteReq, ctx: TardisContext) -> TardisResult<()> { let funs = get_tardis_inst(); - if let Some(ref key) = req.key { - stats_record_serv::fact_record_delete(&req.tag, key.as_ref(), &funs, &ctx).await?; - } + stats_record_serv::fact_record_delete(&req.fact_key, &req.record_key, &funs, &ctx).await?; Ok(()) } @@ -37,6 +33,7 @@ pub async fn handle_events() -> TardisResult<()> { use bios_sdk_invoke::clients::event_client::asteroid_mq::prelude::*; if let Some(topic) = get_topic(&SPI_RPC_TOPIC) { topic.create_endpoint([Interest::new("stats/add")]).await.map_err(mq_error)?.create_event_loop().with_handler(ContextHandler(handle_add_event)).spawn(); + topic.create_endpoint([Interest::new("stats/adds")]).await.map_err(mq_error)?.create_event_loop().with_handler(ContextHandler(handle_adds_event)).spawn(); topic.create_endpoint([Interest::new("stats/delete")]).await.map_err(mq_error)?.create_event_loop().with_handler(ContextHandler(handle_delete_event)).spawn(); } diff --git a/backend/supports/iam/src/basic/serv/clients/iam_stats_client.rs b/backend/supports/iam/src/basic/serv/clients/iam_stats_client.rs index 48ef3734a..8bdfa8e45 100644 --- a/backend/supports/iam/src/basic/serv/clients/iam_stats_client.rs +++ b/backend/supports/iam/src/basic/serv/clients/iam_stats_client.rs @@ -111,6 +111,8 @@ impl IamStatsClient { "account_num": account_id.len(), }), ext: None, + idempotent_id: None, + ignore_updates: None, }; SpiStatsClient::fact_record_load(&funs.conf::().spi.stats_orgs_prefix.clone(), &org_id, add_req, funs, ctx).await?; Ok(()) diff --git a/frontend/sdks/invoke/src/clients/spi_log_client.rs b/frontend/sdks/invoke/src/clients/spi_log_client.rs index a72bab17e..6b7146fd8 100644 --- a/frontend/sdks/invoke/src/clients/spi_log_client.rs +++ b/frontend/sdks/invoke/src/clients/spi_log_client.rs @@ -1,5 +1,10 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "event")] +use crate::invoke_config::InvokeConfigApi as _; +use crate::{clients::base_spi_client::BaseSpiClient, invoke_config::InvokeConfig, invoke_constants::DYNAMIC_LOG, invoke_enumeration::InvokeModuleKind}; +#[cfg(feature = "event")] +use tardis::futures::TryFutureExt as _; use tardis::{ basic::{dto::TardisContext, field::TrimString, result::TardisResult}, chrono::{DateTime, Utc}, @@ -11,13 +16,9 @@ use tardis::{ TardisFuns, TardisFunsInst, }; -use crate::{clients::base_spi_client::BaseSpiClient, invoke_config::InvokeConfig, invoke_constants::DYNAMIC_LOG, invoke_enumeration::InvokeModuleKind}; - #[cfg(feature = "event")] use super::event_client::{get_topic, mq_error, EventAttributeExt, SPI_RPC_TOPIC}; use super::iam_client::IamClient; -#[cfg(feature = "event")] -use tardis::futures::TryFutureExt as _; #[cfg(feature = "event")] pub mod event { @@ -26,12 +27,6 @@ pub mod event { impl EventAttribute for super::LogItemAddV2Req { const SUBJECT: Subject = Subject::const_new("log/add"); } - impl EventAttribute for super::StatsItemAddReq { - const SUBJECT: Subject = Subject::const_new("stats/add"); - } - impl EventAttribute for super::StatsItemDeleteReq { - const SUBJECT: Subject = Subject::const_new("stats/delete"); - } } #[derive(Debug, Default, Clone)] pub struct SpiLogClient; @@ -106,32 +101,6 @@ pub struct LogItemAddV2Req { pub msg: Option, } -#[derive(poem_openapi::Object, Serialize, Deserialize, Clone, Debug)] -pub struct StatsItemAddReq { - #[oai(validator(min_length = "2"))] - pub idempotent_id: Option, - #[oai(validator(pattern = r"^[a-z0-9_]+$"))] - pub tag: String, - pub content: Value, - pub ext: Option, - #[oai(validator(min_length = "2"))] - pub key: Option, - pub ts: Option>, - #[oai(validator(min_length = "2"))] - pub owner: Option, - pub own_paths: Option, -} - -#[derive(poem_openapi::Object, Serialize, Deserialize, Clone, Debug)] -pub struct StatsItemDeleteReq { - #[oai(validator(min_length = "2"))] - pub idempotent_id: Option, - #[oai(validator(pattern = r"^[a-z0-9_]+$"))] - pub tag: String, - #[oai(validator(min_length = "2"))] - pub key: Option, -} - impl SpiLogClient { #[deprecated] pub async fn add_dynamic_log( @@ -206,9 +175,11 @@ impl SpiLogClient { pub async fn addv2(req: LogItemAddV2Req, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { #[cfg(feature = "event")] - if let Some(topic) = get_topic(&SPI_RPC_TOPIC) { - topic.send_event(req.inject_context(funs, ctx).json()).map_err(mq_error).await?; - return Ok(()); + if funs.invoke_conf_in_event(InvokeModuleKind::Log) { + if let Some(topic) = get_topic(&SPI_RPC_TOPIC) { + topic.send_event(req.inject_context(funs, ctx).json()).map_err(mq_error).await?; + return Ok(()); + } } let log_url: String = BaseSpiClient::module_url(InvokeModuleKind::Log, funs).await?; let headers = BaseSpiClient::headers(None, funs, ctx).await?; diff --git a/frontend/sdks/invoke/src/clients/spi_search_client.rs b/frontend/sdks/invoke/src/clients/spi_search_client.rs index b1cad260d..e8c373c3d 100644 --- a/frontend/sdks/invoke/src/clients/spi_search_client.rs +++ b/frontend/sdks/invoke/src/clients/spi_search_client.rs @@ -4,6 +4,8 @@ use tardis::web::web_resp::{TardisPage, TardisResp}; use tardis::TardisFunsInst; use crate::dto::search_item_dto::{SearchEventItemDeleteReq, SearchEventItemModifyReq, SearchItemAddReq, SearchItemModifyReq, SearchItemSearchReq, SearchItemSearchResp}; +#[cfg(feature = "event")] +use crate::invoke_config::InvokeConfigApi as _; use crate::invoke_enumeration::InvokeModuleKind; use super::base_spi_client::BaseSpiClient; @@ -38,8 +40,10 @@ pub mod event { impl SpiSearchClient { pub async fn add_item_and_name(add_req: &SearchItemAddReq, name: Option, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { #[cfg(feature = "event")] - if let Some(_topic) = get_topic(&SPI_RPC_TOPIC) { - return EventCenterClient { topic_code: SPI_RPC_TOPIC }.add_item_and_name(add_req, name, funs, ctx).await; + if funs.invoke_conf_in_event(InvokeModuleKind::Search) { + if let Some(_topic) = get_topic(&SPI_RPC_TOPIC) { + return EventCenterClient { topic_code: SPI_RPC_TOPIC }.add_item_and_name(add_req, name, funs, ctx).await; + } } let search_url: String = BaseSpiClient::module_url(InvokeModuleKind::Search, funs).await?; let headers = BaseSpiClient::headers(None, funs, ctx).await?; @@ -51,8 +55,10 @@ impl SpiSearchClient { pub async fn modify_item_and_name(tag: &str, key: &str, modify_req: &SearchItemModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { #[cfg(feature = "event")] - if let Some(_topic) = get_topic(&SPI_RPC_TOPIC) { - return EventCenterClient { topic_code: SPI_RPC_TOPIC }.modify_item_and_name(tag, key, modify_req, funs, ctx).await; + if funs.invoke_conf_in_event(InvokeModuleKind::Search) { + if let Some(_topic) = get_topic(&SPI_RPC_TOPIC) { + return EventCenterClient { topic_code: SPI_RPC_TOPIC }.modify_item_and_name(tag, key, modify_req, funs, ctx).await; + } } let search_url: String = BaseSpiClient::module_url(InvokeModuleKind::Search, funs).await?; let headers = BaseSpiClient::headers(None, funs, ctx).await?; @@ -66,8 +72,10 @@ impl SpiSearchClient { pub async fn delete_item_and_name(tag: &str, key: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { #[cfg(feature = "event")] - if let Some(_topic) = get_topic(&SPI_RPC_TOPIC) { - return EventCenterClient { topic_code: SPI_RPC_TOPIC }.delete_item_and_name(tag, key, funs, ctx).await; + if funs.invoke_conf_in_event(InvokeModuleKind::Search) { + if let Some(_topic) = get_topic(&SPI_RPC_TOPIC) { + return EventCenterClient { topic_code: SPI_RPC_TOPIC }.delete_item_and_name(tag, key, funs, ctx).await; + } } let search_url = BaseSpiClient::module_url(InvokeModuleKind::Search, funs).await?; let headers = BaseSpiClient::headers(None, funs, ctx).await?; diff --git a/frontend/sdks/invoke/src/clients/spi_stats_client.rs b/frontend/sdks/invoke/src/clients/spi_stats_client.rs index 11805c2c0..cbbcac0a9 100644 --- a/frontend/sdks/invoke/src/clients/spi_stats_client.rs +++ b/frontend/sdks/invoke/src/clients/spi_stats_client.rs @@ -1,23 +1,101 @@ +use serde::{Deserialize, Serialize}; use tardis::basic::dto::TardisContext; use tardis::basic::result::TardisResult; +#[cfg(feature = "event")] +use tardis::futures::TryFutureExt as _; use tardis::TardisFunsInst; -use crate::dto::stats_record_dto::StatsFactRecordLoadReq; +use crate::dto::stats_record_dto::{StatsFactRecordLoadReq, StatsFactRecordsLoadReq}; +#[cfg(feature = "event")] +use crate::invoke_config::InvokeConfigApi as _; use crate::invoke_enumeration::InvokeModuleKind; use super::base_spi_client::BaseSpiClient; +#[cfg(feature = "event")] +use super::event_client::{get_topic, mq_error, EventAttributeExt as _, SPI_RPC_TOPIC}; + +#[cfg(feature = "event")] +pub mod event { + use asteroid_mq::prelude::*; + + impl EventAttribute for super::StatsItemAddReq { + const SUBJECT: Subject = Subject::const_new("stats/add"); + } + impl EventAttribute for super::StatsItemAddsReq { + const SUBJECT: Subject = Subject::const_new("stats/adds"); + } + impl EventAttribute for super::StatsItemDeleteReq { + const SUBJECT: Subject = Subject::const_new("stats/delete"); + } +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct StatsItemAddReq { + pub fact_key: String, + pub record_key: String, + pub req: StatsFactRecordLoadReq, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct StatsItemAddsReq { + pub fact_key: String, + pub reqs: Vec, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct StatsItemDeleteReq { + pub fact_key: String, + pub record_key: String, +} pub struct SpiStatsClient; impl SpiStatsClient { pub async fn fact_record_load(fact_key: &str, record_key: &str, add_req: StatsFactRecordLoadReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + #[cfg(feature = "event")] + if funs.invoke_conf_in_event(InvokeModuleKind::Stats) { + if let Some(topic) = get_topic(&SPI_RPC_TOPIC) { + topic + .send_event( + StatsItemAddReq { + fact_key: fact_key.to_string(), + record_key: record_key.to_string(), + req: add_req, + } + .inject_context(funs, ctx) + .json(), + ) + .map_err(mq_error) + .await?; + return Ok(()); + } + } + let stats_url: String = BaseSpiClient::module_url(InvokeModuleKind::Stats, funs).await?; let headers = BaseSpiClient::headers(None, funs, ctx).await?; funs.web_client().put_obj_to_str(&format!("{stats_url}/ci/record/fact/{fact_key}/{record_key}"), &add_req, headers.clone()).await?; Ok(()) } - pub async fn fact_records_load(fact_key: &str, add_req: Vec, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + pub async fn fact_records_load(fact_key: &str, add_req: Vec, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + #[cfg(feature = "event")] + if funs.invoke_conf_in_event(InvokeModuleKind::Stats) { + if let Some(topic) = get_topic(&SPI_RPC_TOPIC) { + topic + .send_event( + StatsItemAddsReq { + fact_key: fact_key.to_string(), + reqs: add_req, + } + .inject_context(funs, ctx) + .json(), + ) + .map_err(mq_error) + .await?; + return Ok(()); + } + } + let stats_url: String = BaseSpiClient::module_url(InvokeModuleKind::Stats, funs).await?; let headers = BaseSpiClient::headers(None, funs, ctx).await?; funs.web_client().put_obj_to_str(&format!("{stats_url}/ci/record/fact/{fact_key}/batch/load"), &add_req, headers.clone()).await?; @@ -25,6 +103,24 @@ impl SpiStatsClient { } pub async fn fact_record_delete(fact_key: &str, record_key: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + #[cfg(feature = "event")] + if funs.invoke_conf_in_event(InvokeModuleKind::Stats) { + if let Some(topic) = get_topic(&SPI_RPC_TOPIC) { + topic + .send_event( + StatsItemDeleteReq { + fact_key: fact_key.to_string(), + record_key: record_key.to_string(), + } + .inject_context(funs, ctx) + .json(), + ) + .map_err(mq_error) + .await?; + return Ok(()); + } + } + let stats_url = BaseSpiClient::module_url(InvokeModuleKind::Stats, funs).await?; let headers = BaseSpiClient::headers(None, funs, ctx).await?; funs.web_client().delete_to_void(&format!("{stats_url}/ci/record/fact/{fact_key}/{record_key}"), headers.clone()).await?; diff --git a/frontend/sdks/invoke/src/dto/stats_record_dto.rs b/frontend/sdks/invoke/src/dto/stats_record_dto.rs index cd592e5d1..29e5e724c 100644 --- a/frontend/sdks/invoke/src/dto/stats_record_dto.rs +++ b/frontend/sdks/invoke/src/dto/stats_record_dto.rs @@ -7,12 +7,24 @@ use tardis::{ /// 事实记录加载请求对象 /// Load Fact Record Request Object -#[derive(poem_openapi::Object, Serialize, Deserialize, Debug)] +#[derive(poem_openapi::Object, Serialize, Deserialize, Debug, Clone)] pub struct StatsFactRecordLoadReq { /// Own paths pub own_paths: String, /// Create time pub ct: DateTime, + /// Idempotent id + /// ps: The idempotent id is used to ensure that the same request is not processed repeatedly + /// 幂等id + /// ps: 幂等id用于确保同一个请求不会重复处理 + pub idempotent_id: Option, + + /// ignore updates + /// ps: If idempotent_id has a value and the record is hit, this field takes effect to ignore or update, default is true to ignore updates + /// + /// 忽略更新 + /// ps: 如果 idempotent_id 有值并且命中纪录,则该字段生效忽略或者进行更新,默认为 true 忽略更新 + pub ignore_updates: Option, /// Field data /// 字段数据 /// @@ -27,7 +39,7 @@ pub struct StatsFactRecordLoadReq { /// Load Fact Record Request Object /// /// 事实记录加载请求对象 -#[derive(poem_openapi::Object, Serialize, Deserialize, Debug)] +#[derive(poem_openapi::Object, Serialize, Deserialize, Debug, Clone)] pub struct StatsFactRecordsLoadReq { /// Primary key pub key: String, @@ -35,6 +47,18 @@ pub struct StatsFactRecordsLoadReq { pub own_paths: String, /// Create time pub ct: DateTime, + /// Idempotent id + /// ps: The idempotent id is used to ensure that the same request is not processed repeatedly + /// 幂等id + /// ps: 幂等id用于确保同一个请求不会重复处理 + pub idempotent_id: Option, + + /// ignore updates + /// ps: If idempotent_id has a value and the record is hit, this field takes effect to ignore or update, default is true to ignore updates + /// + /// 忽略更新 + /// ps: 如果 idempotent_id 有值并且命中纪录,则该字段生效忽略或者进行更新,默认为 true 忽略更新 + pub ignore_updates: Option, /// Field data /// 字段数据 /// diff --git a/frontend/sdks/invoke/src/invoke_config.rs b/frontend/sdks/invoke/src/invoke_config.rs index 9b76d890b..0019e135b 100644 --- a/frontend/sdks/invoke/src/invoke_config.rs +++ b/frontend/sdks/invoke/src/invoke_config.rs @@ -11,6 +11,12 @@ use tardis::TardisFunsInst; pub struct InvokeConfig { pub spi_app_id: String, pub module_urls: HashMap, + pub module_configs: HashMap, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct InvokeModuleConfig { + pub in_event: bool, } impl Default for InvokeConfig { @@ -26,6 +32,13 @@ impl Default for InvokeConfig { (InvokeModuleKind::Stats.to_string(), "http://127.0.0.1:8080/spi-stats".to_string()), (InvokeModuleKind::Event.to_string(), "http://127.0.0.1:8080/event".to_string()), ]), + module_configs: HashMap::from([ + (InvokeModuleKind::Kv.to_string(), InvokeModuleConfig { in_event: false }), + (InvokeModuleKind::Log.to_string(), InvokeModuleConfig { in_event: false }), + (InvokeModuleKind::Search.to_string(), InvokeModuleConfig { in_event: false }), + (InvokeModuleKind::Schedule.to_string(), InvokeModuleConfig { in_event: false }), + (InvokeModuleKind::Stats.to_string(), InvokeModuleConfig { in_event: false }), + ]), } } } @@ -55,6 +68,12 @@ impl InvokeConfigManager { let conf = conf.get(code).unwrap_or_else(|| panic!("not found invoke config code {code}")); fun(conf) } + + pub fn get_module_config(code: &str, module: InvokeModuleKind) -> Option { + let conf = INVOKE_CONFIG.lock().unwrap_or_else(|e| panic!("invoke config lock error: {e:?}")); + let conf = conf.get(code).unwrap_or_else(|| panic!("not found invoke config code {code}")); + conf.module_configs.get(&module.to_string()).cloned() + } } pub trait InvokeConfigApi { @@ -62,6 +81,7 @@ pub trait InvokeConfigApi { fn invoke_conf_module_url(&self) -> HashMap; fn invoke_conf_match_module_url(&self, module_url: &str) -> bool; fn invoke_conf_inject_context(&self, context: &TardisContext) -> TardisContext; + fn invoke_conf_in_event(&self, module: InvokeModuleKind) -> bool; } impl InvokeConfigApi for TardisFunsInst { @@ -82,4 +102,8 @@ impl InvokeConfigApi for TardisFunsInst { ctx.ak = self.invoke_conf_spi_app_id(); ctx } + + fn invoke_conf_in_event(&self, module: InvokeModuleKind) -> bool { + InvokeConfigManager::get_module_config(self.module_code(), module).map_or(false, |conf| conf.in_event) + } } From fd9dc434f6d8489346fe6ee11fe47e823f8a3f47 Mon Sep 17 00:00:00 2001 From: ljl <17743125563@163.com> Date: Sat, 4 Jan 2025 10:18:00 +0800 Subject: [PATCH 28/35] auth: fix spi log. --- backend/supports/auth/src/serv/clients/spi_log_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/supports/auth/src/serv/clients/spi_log_client.rs b/backend/supports/auth/src/serv/clients/spi_log_client.rs index 6f1915723..c5a6ebcbe 100644 --- a/backend/supports/auth/src/serv/clients/spi_log_client.rs +++ b/backend/supports/auth/src/serv/clients/spi_log_client.rs @@ -28,7 +28,7 @@ impl SpiLogClient { if log_url.is_empty() || spi_owner.is_empty() { return Ok(()); } - let spi_ctx = TardisContext { owner: spi_owner, ..ctx.clone() }; + let spi_ctx = TardisContext { ak: spi_owner, ..ctx.clone() }; let headers = [("Tardis-Context".to_string(), TardisFuns::crypto.base64.encode(TardisFuns::json.obj_to_string(&spi_ctx)?))]; //add log item From 122617cd3f71579e25acf994c07f57faa39f9df0 Mon Sep 17 00:00:00 2001 From: RWDai <27391645+RWDai@users.noreply.github.com> Date: Sat, 4 Jan 2025 11:38:07 +0800 Subject: [PATCH 29/35] fix: ensure idempotent_id is set in addv2 function of log_pg_item_serv.rs --- backend/spi/spi-log/src/serv/pgv2/log_pg_item_serv.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/spi/spi-log/src/serv/pgv2/log_pg_item_serv.rs b/backend/spi/spi-log/src/serv/pgv2/log_pg_item_serv.rs index a7772a93a..ed406de23 100644 --- a/backend/spi/spi-log/src/serv/pgv2/log_pg_item_serv.rs +++ b/backend/spi/spi-log/src/serv/pgv2/log_pg_item_serv.rs @@ -37,6 +37,7 @@ pub async fn find(find_req: &mut LogItemFindReq, funs: &TardisFunsInst, ctx: &Ta pub async fn addv2(add_req: &mut LogItemAddV2Req, funs: &TardisFunsInst, ctx: &TardisContext, inst: &SpiBsInst) -> TardisResult { let id = add_req.idempotent_id.clone().unwrap_or(TardisFuns::field.nanoid()); + add_req.idempotent_id = Some(id.clone()); let bs_inst = inst.inst::(); // 初始化要保存的内容 From e7a8ebd42629871974baa7773f38ed14b36ff8c8 Mon Sep 17 00:00:00 2001 From: ljl <17743125563@163.com> Date: Sat, 4 Jan 2025 13:16:41 +0800 Subject: [PATCH 30/35] fix. --- backend/supports/auth/src/serv/clients/spi_log_client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/supports/auth/src/serv/clients/spi_log_client.rs b/backend/supports/auth/src/serv/clients/spi_log_client.rs index c5a6ebcbe..fd77bd2a2 100644 --- a/backend/supports/auth/src/serv/clients/spi_log_client.rs +++ b/backend/supports/auth/src/serv/clients/spi_log_client.rs @@ -57,7 +57,7 @@ impl SpiLogClient { body.insert("ts", ts); - TardisFuns::web_client().post_obj_to_str(&format!("{log_url}/ci/item"), &body, headers.clone()).await?; + // TardisFuns::web_client().post_obj_to_str(&format!("{log_url}/ci/item"), &body, headers.clone()).await?; Ok(()) } } From 9bc18bd8c6d5047071956b70dcbc89a5f915913b Mon Sep 17 00:00:00 2001 From: ZzIsGod1019 <1498852723@qq.com> Date: Sat, 4 Jan 2025 16:46:43 +0800 Subject: [PATCH 31/35] flow: fix bug (log error) (#894) * flow: fix bug (log error) * flow: remove event * flow: fix search failed --- .../flow/src/dto/flow_transition_dto.rs | 6 ++- .../flow/src/serv/clients/search_client.rs | 35 +++---------- .../flow/src/serv/flow_external_serv.rs | 3 ++ .../flow/src/serv/flow_inst_serv.rs | 52 +++++++++++-------- .../flow/src/serv/flow_model_serv.rs | 4 +- 5 files changed, 46 insertions(+), 54 deletions(-) diff --git a/backend/middlewares/flow/src/dto/flow_transition_dto.rs b/backend/middlewares/flow/src/dto/flow_transition_dto.rs index 682d3cb21..a14f70e74 100644 --- a/backend/middlewares/flow/src/dto/flow_transition_dto.rs +++ b/backend/middlewares/flow/src/dto/flow_transition_dto.rs @@ -609,6 +609,9 @@ pub enum FlowTransitionFrontActionInfoRelevanceRelation { #[serde(rename = "between")] #[oai(rename = "between")] Between, + #[serde(rename = "is_null_or_empty")] + #[oai(rename = "is_null_or_empty")] + IsNullOrEmpty, #[serde(rename = "is_not_null")] #[oai(rename = "is_not_null")] IsNotNull, @@ -618,7 +621,7 @@ impl FlowTransitionFrontActionInfoRelevanceRelation { pub fn check_conform(&self, mut left_value: String, right_value: String) -> bool { use itertools::Itertools; - if left_value.is_empty() || left_value == "null" { + if (left_value.is_empty() || left_value == "null") && *self != FlowTransitionFrontActionInfoRelevanceRelation::IsNullOrEmpty { return false; } // 单项判断(例如等于,不等于,大于,小于),如果参数是单元素数组,则取出数据,否则说明格式错误直接返回false @@ -666,6 +669,7 @@ impl FlowTransitionFrontActionInfoRelevanceRelation { } left_value >= interval[0] && left_value <= interval[1] } + FlowTransitionFrontActionInfoRelevanceRelation::IsNullOrEmpty => {let s = left_value.as_str().to_string(); s.is_empty() || s == "[]" || s == "{}"}, FlowTransitionFrontActionInfoRelevanceRelation::IsNotNull => !left_value.as_str().to_string().is_empty(), } } diff --git a/backend/middlewares/flow/src/serv/clients/search_client.rs b/backend/middlewares/flow/src/serv/clients/search_client.rs index f39b5f2af..a6e8fe5fc 100644 --- a/backend/middlewares/flow/src/serv/clients/search_client.rs +++ b/backend/middlewares/flow/src/serv/clients/search_client.rs @@ -2,10 +2,7 @@ use std::{collections::HashMap, vec}; use bios_basic::rbum::{dto::rbum_filer_dto::RbumBasicFilterReq, helper::rbum_scope_helper, rbum_enumeration::RbumScopeLevelKind, serv::rbum_item_serv::RbumItemCrudOperation}; use bios_sdk_invoke::{ - clients::{ - event_client::{get_topic, EventCenterClient, SPI_RPC_TOPIC}, - spi_search_client::SpiSearchClient, - }, + clients::spi_search_client::SpiSearchClient, dto::search_item_dto::{ AdvSearchItemQueryReq, BasicQueryCondInfo, BasicQueryOpKind, SearchItemAddReq, SearchItemModifyReq, SearchItemQueryReq, SearchItemSearchCtxReq, SearchItemSearchPageReq, SearchItemSearchReq, SearchItemSearchResp, SearchItemVisitKeysReq, @@ -206,11 +203,7 @@ impl FlowSearchClient { }), kv_disable: None, }; - if let Some(_topic) = get_topic(&SPI_RPC_TOPIC) { - EventCenterClient { topic_code: SPI_RPC_TOPIC }.modify_item_and_name(SEARCH_MODEL_TAG, &key, &modify_req, funs, ctx).await?; - } else { - SpiSearchClient::modify_item_and_name(SEARCH_MODEL_TAG, &key, &modify_req, funs, ctx).await?; - } + SpiSearchClient::modify_item_and_name(SEARCH_MODEL_TAG, &key, &modify_req, funs, ctx).await?; } else { let add_req = SearchItemAddReq { tag: SEARCH_MODEL_TAG.to_string(), @@ -239,22 +232,14 @@ impl FlowSearchClient { }), kv_disable: None, }; - if let Some(_topic) = get_topic(&SPI_RPC_TOPIC) { - EventCenterClient { topic_code: SPI_RPC_TOPIC }.add_item_and_name(&add_req, Some(model_resp.name.clone()), funs, ctx).await?; - } else { - SpiSearchClient::add_item_and_name(&add_req, Some(model_resp.name.clone()), funs, ctx).await?; - } + SpiSearchClient::add_item_and_name(&add_req, Some(model_resp.name.clone()), funs, ctx).await?; } Ok(()) } // model 全局搜索删除埋点方法 pub async fn delete_model_search(model_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { - if let Some(_topic) = get_topic(&SPI_RPC_TOPIC) { - EventCenterClient { topic_code: SPI_RPC_TOPIC }.delete_item_and_name(SEARCH_MODEL_TAG, model_id, funs, ctx).await?; - } else { - SpiSearchClient::delete_item_and_name(SEARCH_MODEL_TAG, model_id, funs, ctx).await?; - } + SpiSearchClient::delete_item_and_name(SEARCH_MODEL_TAG, model_id, funs, ctx).await?; Ok(()) } @@ -329,11 +314,7 @@ impl FlowSearchClient { }), kv_disable: None, }; - if let Some(_topic) = get_topic(&SPI_RPC_TOPIC) { - EventCenterClient { topic_code: SPI_RPC_TOPIC }.modify_item_and_name(SEARCH_INSTANCE_TAG, &key, &modify_req, funs, ctx).await?; - } else { - SpiSearchClient::modify_item_and_name(SEARCH_INSTANCE_TAG, &key, &modify_req, funs, ctx).await?; - } + SpiSearchClient::modify_item_and_name(SEARCH_INSTANCE_TAG, &key, &modify_req, funs, ctx).await?; } else { let add_req = SearchItemAddReq { tag: SEARCH_INSTANCE_TAG.to_string(), @@ -370,11 +351,7 @@ impl FlowSearchClient { }), kv_disable: None, }; - if let Some(_topic) = get_topic(&SPI_RPC_TOPIC) { - EventCenterClient { topic_code: SPI_RPC_TOPIC }.add_item_and_name(&add_req, Some(inst_resp.code.clone()), funs, ctx).await?; - } else { - SpiSearchClient::add_item_and_name(&add_req, Some(inst_resp.code.clone()), funs, ctx).await?; - } + SpiSearchClient::add_item_and_name(&add_req, Some(inst_resp.code.clone()), funs, ctx).await?; } Ok(()) } diff --git a/backend/middlewares/flow/src/serv/flow_external_serv.rs b/backend/middlewares/flow/src/serv/flow_external_serv.rs index f04a2ee16..f32c2ada6 100644 --- a/backend/middlewares/flow/src/serv/flow_external_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_external_serv.rs @@ -186,6 +186,7 @@ impl FlowExternalServ { } } + #[allow(clippy::too_many_arguments)] pub async fn do_notify_changes( tag: &str, inst_id: &str, @@ -196,6 +197,7 @@ impl FlowExternalServ { original_sys_state: FlowSysStateKind, transition_name: String, is_notify: bool, + manual_op: Option, callback_op: Option, ctx: &TardisContext, funs: &TardisFunsInst, @@ -218,6 +220,7 @@ impl FlowExternalServ { original_sys_state: Some(original_sys_state), transition_name: Some(transition_name), notify: Some(is_notify), + manual_op, sys_time: Some(Utc::now().timestamp_millis()), ..Default::default() }; diff --git a/backend/middlewares/flow/src/serv/flow_inst_serv.rs b/backend/middlewares/flow/src/serv/flow_inst_serv.rs index 47d59dd1a..abace299a 100644 --- a/backend/middlewares/flow/src/serv/flow_inst_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_inst_serv.rs @@ -1103,6 +1103,7 @@ impl FlowInstServ { prev_flow_state.sys_state.clone(), next_transition_detail.name.clone(), next_transition_detail.is_notify, + Some(!(callback_kind == FlowExternalCallbackOp::PostAction || callback_kind == FlowExternalCallbackOp::ConditionalTrigger)), Some(callback_kind), ctx, funs, @@ -1599,6 +1600,7 @@ impl FlowInstServ { original_flow_state.sys_state, "UPDATE".to_string(), false, + Some(false), Some(FlowExternalCallbackOp::Auto), ctx, funs, @@ -2075,7 +2077,8 @@ impl FlowInstServ { FlowStateKind::Form => kind_conf.form.as_ref().map(|form| { let mut operators = HashMap::new(); let artifacts = artifacts.clone().unwrap_or_default(); - if artifacts.curr_operators.clone().unwrap_or_default().contains(&ctx.owner) + if !finish + && artifacts.curr_operators.clone().unwrap_or_default().contains(&ctx.owner) && !artifacts.prohibit_guard_by_spec_account_ids.clone().unwrap_or_default().contains(&ctx.owner) { operators.insert(FlowStateOperatorKind::Submit, form.submit_btn_name.clone()); @@ -2095,18 +2098,20 @@ impl FlowInstServ { FlowStateKind::Approval => kind_conf.approval.as_ref().map(|approval| { let mut operators = HashMap::new(); let artifacts = artifacts.clone().unwrap_or_default(); - if artifacts.curr_operators.clone().unwrap_or_default().contains(&ctx.owner) - && !artifacts.prohibit_guard_by_spec_account_ids.clone().unwrap_or_default().contains(&ctx.owner) - { - operators.insert(FlowStateOperatorKind::Pass, approval.pass_btn_name.clone()); - operators.insert(FlowStateOperatorKind::Overrule, approval.overrule_btn_name.clone()); - operators.insert(FlowStateOperatorKind::Back, approval.back_btn_name.clone()); - if approval.referral && !finish { - operators.insert(FlowStateOperatorKind::Referral, "".to_string()); + if !finish { + if artifacts.curr_operators.clone().unwrap_or_default().contains(&ctx.owner) + && !artifacts.prohibit_guard_by_spec_account_ids.clone().unwrap_or_default().contains(&ctx.owner) + { + operators.insert(FlowStateOperatorKind::Pass, approval.pass_btn_name.clone()); + operators.insert(FlowStateOperatorKind::Overrule, approval.overrule_btn_name.clone()); + operators.insert(FlowStateOperatorKind::Back, approval.back_btn_name.clone()); + if approval.referral { + operators.insert(FlowStateOperatorKind::Referral, "".to_string()); + } + } + if approval.revoke && ctx.owner == artifacts.prev_non_auto_account_id.unwrap_or_default() { + operators.insert(FlowStateOperatorKind::Revoke, "".to_string()); } - } - if approval.revoke && ctx.owner == artifacts.prev_non_auto_account_id.unwrap_or_default() && !finish { - operators.insert(FlowStateOperatorKind::Revoke, "".to_string()); } FLowInstStateConf { operators, @@ -2126,6 +2131,7 @@ impl FlowInstServ { } pub async fn operate(inst: &FlowInstDetailResp, operate_req: &FlowInstOperateReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + let artifacts = inst.artifacts.clone().unwrap_or_default(); let current_state = FlowStateServ::get_item( &inst.current_state_id, &FlowStateFilterReq { @@ -2169,15 +2175,17 @@ impl FlowInstServ { ..Default::default() }; if let Some(all_vars) = &operate_req.all_vars { - modify_artifacts.curr_vars = Some(all_vars.clone()); + let mut curr_vars = artifacts.curr_vars.unwrap_or_default(); + curr_vars.extend(all_vars.clone()); + modify_artifacts.curr_vars = Some(curr_vars); } Self::modify_inst_artifacts(&inst.id, &modify_artifacts, funs, ctx).await?; match operate_req.operate { // 转办 FlowStateOperatorKind::Referral => { let mut modify_artifacts = FlowInstArtifactsModifyReq::default(); - let mut curr_operators = inst.artifacts.clone().unwrap_or_default().curr_operators.unwrap_or_default(); - let mut prohibit_guard_by_spec_account_ids = inst.artifacts.clone().unwrap_or_default().prohibit_guard_by_spec_account_ids.unwrap_or_default(); + let mut curr_operators = artifacts.curr_operators.unwrap_or_default(); + let mut prohibit_guard_by_spec_account_ids = artifacts.prohibit_guard_by_spec_account_ids.unwrap_or_default(); if let Some(operator) = operate_req.operator.clone() { curr_operators = curr_operators.into_iter().filter(|account_id| *account_id != ctx.owner.clone()).collect_vec(); curr_operators.push(operator.clone()); @@ -2190,7 +2198,7 @@ impl FlowInstServ { } // 撤销 FlowStateOperatorKind::Revoke => { - let mut prev_non_auto_state_id = inst.artifacts.clone().unwrap_or_default().prev_non_auto_state_id.unwrap_or_default(); + let mut prev_non_auto_state_id = artifacts.prev_non_auto_state_id.unwrap_or_default(); let target_state_id = prev_non_auto_state_id.pop(); if let Some(target_state_id) = target_state_id { Self::modify_inst_artifacts( @@ -2211,7 +2219,7 @@ impl FlowInstServ { } // 提交 FlowStateOperatorKind::Submit => { - let mut prev_non_auto_state_id = inst.artifacts.clone().unwrap_or_default().prev_non_auto_state_id.unwrap_or_default(); + let mut prev_non_auto_state_id = artifacts.prev_non_auto_state_id.unwrap_or_default(); prev_non_auto_state_id.push(inst.current_state_id.clone()); Self::modify_inst_artifacts( &inst.id, @@ -2244,7 +2252,7 @@ impl FlowInstServ { } // 退回 FlowStateOperatorKind::Back => { - let mut prev_non_auto_state_id = inst.artifacts.clone().unwrap_or_default().prev_non_auto_state_id.unwrap_or_default(); + let mut prev_non_auto_state_id = artifacts.prev_non_auto_state_id.unwrap_or_default(); if let Some(target_state_id) = prev_non_auto_state_id.pop() { Self::modify_inst_artifacts( &inst.id, @@ -2274,7 +2282,7 @@ impl FlowInstServ { } // 通过 FlowStateOperatorKind::Pass => { - let curr_operators = inst.artifacts.clone().unwrap_or_default().curr_operators.unwrap_or_default(); + let curr_operators = artifacts.curr_operators.unwrap_or_default(); Self::modify_inst_artifacts( &inst.id, &FlowInstArtifactsModifyReq { @@ -2302,7 +2310,7 @@ impl FlowInstServ { ctx, ) .await?; - let mut prev_non_auto_state_id = inst.artifacts.clone().unwrap_or_default().prev_non_auto_state_id.unwrap_or_default(); + let mut prev_non_auto_state_id = artifacts.prev_non_auto_state_id.unwrap_or_default(); prev_non_auto_state_id.push(inst.current_state_id.clone()); Self::modify_inst_artifacts( &inst.id, @@ -2336,7 +2344,7 @@ impl FlowInstServ { } // 拒绝 FlowStateOperatorKind::Overrule => { - let curr_operators = inst.artifacts.clone().unwrap_or_default().curr_operators.unwrap_or_default(); + let curr_operators = artifacts.curr_operators.unwrap_or_default(); Self::modify_inst_artifacts( &inst.id, &FlowInstArtifactsModifyReq { @@ -2824,7 +2832,7 @@ impl FlowInstServ { ) .await?; let current_date = Utc::now(); - Ok(format!("SP{}{}{}{:0>5}", current_date.year(), current_date.month(), current_date.day(), count + 1).to_string()) + Ok(format!("SP{}{:0>2}{:0>2}{:0>5}", current_date.year(), current_date.month(), current_date.day(), count + 1).to_string()) } // 获取需要更新的参数列表 diff --git a/backend/middlewares/flow/src/serv/flow_model_serv.rs b/backend/middlewares/flow/src/serv/flow_model_serv.rs index 898a37ea3..84f3b8714 100644 --- a/backend/middlewares/flow/src/serv/flow_model_serv.rs +++ b/backend/middlewares/flow/src/serv/flow_model_serv.rs @@ -256,7 +256,7 @@ impl RbumItemCrudOperation>>()?; } - if add_req.template && add_req.rel_model_id.clone().map_or(true, |id| id.is_empty()) { + if add_req.template && add_req.main && add_req.rel_model_id.clone().map_or(true, |id| id.is_empty()) { FlowSearchClient::async_add_or_modify_model_search(flow_model_id, Box::new(false), funs, ctx).await?; FlowLogClient::add_ctx_task( LogParamTag::DynamicLog, @@ -417,7 +417,7 @@ impl RbumItemCrudOperation>>()?; } - if model_detail.template && model_detail.rel_model_id.is_empty() { + if model_detail.template && model_detail.main && model_detail.rel_model_id.is_empty() { FlowSearchClient::async_add_or_modify_model_search(flow_model_id, Box::new(true), funs, ctx).await?; FlowLogClient::add_ctx_task( LogParamTag::DynamicLog, From 17310a6a668fc46adfaa4d35ad4d508a3665e4dc Mon Sep 17 00:00:00 2001 From: RWDai <27391645+RWDai@users.noreply.github.com> Date: Sat, 4 Jan 2025 20:50:25 +0800 Subject: [PATCH 32/35] fix: update JSON error handling and clean up unused parameters in log_pg_item_serv.rs --- backend/spi/spi-log/src/serv/pg/log_pg_item_serv.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/spi/spi-log/src/serv/pg/log_pg_item_serv.rs b/backend/spi/spi-log/src/serv/pg/log_pg_item_serv.rs index d83542f02..f30838d29 100644 --- a/backend/spi/spi-log/src/serv/pg/log_pg_item_serv.rs +++ b/backend/spi/spi-log/src/serv/pg/log_pg_item_serv.rs @@ -1,3 +1,4 @@ +use bios_sdk_invoke::clients::event_client::asteroid_mq::event_handler::json::Json; use tardis::{ basic::{dto::TardisContext, result::TardisResult}, db::{reldb_client::TardisRelDBClient, sea_orm::Value}, @@ -491,7 +492,7 @@ ORDER BY ts DESC let content: String = item.try_get("", "content")?; let content = match TardisFuns::json.str_to_json(&content) { Ok(content) => content, - Err(_) => json!({"content": content}), + Err(_) => JsonValue::String(content), }; Ok(LogItemFindResp { ts: item.try_get("", "ts")?, @@ -518,11 +519,11 @@ ORDER BY ts DESC }) } -pub async fn addv2(add_req: &mut LogItemAddV2Req, funs: &TardisFunsInst, ctx: &TardisContext, inst: &SpiBsInst) -> TardisResult { +pub async fn addv2(_: &mut LogItemAddV2Req, funs: &TardisFunsInst, _: &TardisContext, _: &SpiBsInst) -> TardisResult { Err(funs.err().bad_request("item", "add", "Add v2 is not supported", "400-spi-log-add-v2-not-supported")) } -pub async fn findv2(find_req: &mut LogItemFindReq, funs: &TardisFunsInst, ctx: &TardisContext, inst: &SpiBsInst) -> TardisResult> { +pub async fn findv2(_: &mut LogItemFindReq, funs: &TardisFunsInst, _: &TardisContext, _: &SpiBsInst) -> TardisResult> { Err(funs.err().bad_request("item", "find", "Find v2 is not supported", "400-spi-log-find-v2-not-supported")) } From b787634e2baa88dfd3edbd3ad39e7efcb5e9f208 Mon Sep 17 00:00:00 2001 From: RWDai <27391645+RWDai@users.noreply.github.com> Date: Mon, 6 Jan 2025 15:33:12 +0800 Subject: [PATCH 33/35] feat: implement AuthError trait for custom error handling in authentication module --- backend/supports/auth/src/error.rs | 11 +++++++++++ backend/supports/auth/src/lib.rs | 1 + backend/supports/auth/src/serv/auth_crypto_serv.rs | 6 +++--- 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 backend/supports/auth/src/error.rs diff --git a/backend/supports/auth/src/error.rs b/backend/supports/auth/src/error.rs new file mode 100644 index 000000000..07a48b89d --- /dev/null +++ b/backend/supports/auth/src/error.rs @@ -0,0 +1,11 @@ +use tardis::basic::error::TardisError; + +pub trait AuthError { + // 410: The request signature is incorrect. + fn signature_error(msg: &str, locale_code: &str) -> TardisError { + TardisError::custom("410", msg, locale_code) + } +} + +impl AuthError for TardisError {} + diff --git a/backend/supports/auth/src/lib.rs b/backend/supports/auth/src/lib.rs index b2418a093..81a25ee31 100644 --- a/backend/supports/auth/src/lib.rs +++ b/backend/supports/auth/src/lib.rs @@ -7,3 +7,4 @@ pub mod auth_initializer; pub mod dto; pub mod helper; pub mod serv; +mod error; diff --git a/backend/supports/auth/src/serv/auth_crypto_serv.rs b/backend/supports/auth/src/serv/auth_crypto_serv.rs index bc46a4306..7aef9e2d1 100644 --- a/backend/supports/auth/src/serv/auth_crypto_serv.rs +++ b/backend/supports/auth/src/serv/auth_crypto_serv.rs @@ -13,7 +13,7 @@ use lazy_static::lazy_static; use crate::{ auth_config::AuthConfig, auth_constants::DOMAIN_CODE, - dto::auth_crypto_dto::{AuthEncryptReq, AuthEncryptResp}, + dto::auth_crypto_dto::{AuthEncryptReq, AuthEncryptResp}, error::AuthError, }; lazy_static! { @@ -94,7 +94,7 @@ pub async fn decrypt_req( "[Auth] input_keys have four key,and body:{body},input_sm3_digest:{input_sm3_digest},sm3(body):{}", TardisFuns::crypto.digest.sm3(body)? ); - return Err(TardisError::bad_request("[Auth] Encrypted request: body digest error.", "401-auth-req-crypto-error")); + return Err(TardisError::signature_error("[Auth] Encrypted request: body digest error.", "401-auth-req-crypto-error")); } let data = TardisFuns::crypto @@ -121,7 +121,7 @@ pub async fn decrypt_req( "[Auth] input_keys have three key,and body:{body},input_sm3_digest:{input_sm3_digest},sm3(body):{}", TardisFuns::crypto.digest.sm3(body)? ); - return Err(TardisError::bad_request("[Auth] Encrypted request: body digest error.", "401-auth-req-crypto-error")); + return Err(TardisError::signature_error("[Auth] Encrypted request: body digest error.", "401-auth-req-crypto-error")); } let data = TardisFuns::crypto From aee46efa71653ee2186d965ba5c1d1c2eb511463 Mon Sep 17 00:00:00 2001 From: 4t145 Date: Thu, 9 Jan 2025 16:04:34 +0800 Subject: [PATCH 34/35] update notification plugin --- Cargo.toml | 4 +- backend/basic/src/rbum/serv/rbum_cert_serv.rs | 2 +- backend/gateways/spacegate-plugins/Cargo.toml | 2 + .../spacegate-plugins/src/extension.rs | 31 -- .../src/extension/audit_log_param.rs | 111 +++++- .../src/extension/notification.rs | 149 ++++++-- backend/gateways/spacegate-plugins/src/lib.rs | 1 + .../gateways/spacegate-plugins/src/plugin.rs | 14 + .../spacegate-plugins/src/plugin/anti_xss.rs | 83 +++-- .../spacegate-plugins/src/plugin/audit_log.rs | 88 +---- .../spacegate-plugins/src/plugin/auth.rs | 26 +- .../src/plugin/content_filter.rs | 130 +++++++ .../spacegate-plugins/src/plugin/notify.rs | 318 ++++++++++++++++-- .../gateways/spacegate-plugins/src/utils.rs | 71 ++++ backend/middlewares/flow/src/lib.rs | 2 +- backend/supports/auth/src/error.rs | 2 +- .../auth/src/serv/auth_kernel_serv.rs | 8 +- .../src/console_system/api/iam_cs_role_api.rs | 2 +- frontend/sdks/invoke/Cargo.toml | 1 + frontend/sdks/invoke/src/clients.rs | 2 + .../sdks/invoke/src/clients/reach_client.rs | 37 ++ frontend/sdks/invoke/src/invoke_config.rs | 1 + .../sdks/invoke/src/invoke_enumeration.rs | 3 + 23 files changed, 856 insertions(+), 232 deletions(-) create mode 100644 backend/gateways/spacegate-plugins/src/plugin/content_filter.rs create mode 100644 backend/gateways/spacegate-plugins/src/utils.rs create mode 100644 frontend/sdks/invoke/src/clients/reach_client.rs diff --git a/Cargo.toml b/Cargo.toml index f4fa4805d..18e1c7440 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,7 +65,7 @@ strum = { version = "0.26", features = ["derive"] } # tardis # tardis = { version = "0.1.0-rc.17" } # tardis = { version = "0.2.0", path = "../tardis/tardis" } -tardis = { git = "https://github.com/ideal-world/tardis.git", rev = "f666d50" } +tardis = { git = "https://github.com/ideal-world/tardis.git", rev = "0058079" } # asteroid-mq = { git = "https://github.com/4t145/asteroid-mq.git", rev = "d59c64d" } asteroid-mq = { git = "https://github.com/4t145/asteroid-mq.git", rev = "b26fa4f" } # asteroid-mq = { version = "0.1.0-alpha.5" } @@ -78,7 +78,7 @@ asteroid-mq-sdk = { git = "https://github.com/4t145/asteroid-mq.git", rev = "b26 # "k8s", # "ext-axum", # ] } -spacegate-shell = { git = "https://github.com/ideal-world/spacegate.git", rev="fe747e4", features = [ +spacegate-shell = { git = "https://github.com/ideal-world/spacegate.git", rev="8065bb6", features = [ "cache", "k8s", "ext-axum", diff --git a/backend/basic/src/rbum/serv/rbum_cert_serv.rs b/backend/basic/src/rbum/serv/rbum_cert_serv.rs index 9dd422b10..b5bd1378d 100644 --- a/backend/basic/src/rbum/serv/rbum_cert_serv.rs +++ b/backend/basic/src/rbum/serv/rbum_cert_serv.rs @@ -842,7 +842,7 @@ impl RbumCertServ { let rbum_cert = funs.db().get_dto::(&query).await?; if let Some(rbum_cert) = rbum_cert { if Self::cert_is_locked(&rbum_cert.rel_rbum_id, funs).await? { - return Err(funs.err().unauthorized(&Self::get_obj_name(), "valid", "cert is locked", "400-rbum-cert-lock")); + return Err(funs.err().error("400-rbum-cert-lock", &Self::get_obj_name(), "valid", "cert is locked", "400-rbum-cert-lock")); } if !ignore_end_time && rbum_cert.end_time < Utc::now() { return Err(funs.err().conflict(&Self::get_obj_name(), "valid", "sk is expired", "409-rbum-cert-sk-expire")); diff --git a/backend/gateways/spacegate-plugins/Cargo.toml b/backend/gateways/spacegate-plugins/Cargo.toml index fd89176a4..6297d5e95 100644 --- a/backend/gateways/spacegate-plugins/Cargo.toml +++ b/backend/gateways/spacegate-plugins/Cargo.toml @@ -22,10 +22,12 @@ spacegate-shell = { workspace = true, features = [ "ext-redis", "ext-axum", "plugin-east-west-traffic-white-list", + "plugin-limit" ] } bios-sdk-invoke = { version = "0.2.0", path = "../../../frontend/sdks/invoke", features = [ "spi_log", + "reach", ], default-features = false } diff --git a/backend/gateways/spacegate-plugins/src/extension.rs b/backend/gateways/spacegate-plugins/src/extension.rs index 557f47fdf..e66e8cc51 100644 --- a/backend/gateways/spacegate-plugins/src/extension.rs +++ b/backend/gateways/spacegate-plugins/src/extension.rs @@ -1,37 +1,6 @@ -use http::Extensions; -use spacegate_shell::{kernel::extension::ExtensionPack as _, BoxError}; -use tardis::serde_json::{self, Value}; - -use self::audit_log_param::LogParamContent; pub mod audit_log_param; pub mod before_encrypt_body; pub mod cert_info; pub mod notification; pub mod request_crypto_status; -pub enum ExtensionPackEnum { - LogParamContent(), - None, -} - -impl From for ExtensionPackEnum { - fn from(value: String) -> Self { - match value.as_str() { - "log_content" => ExtensionPackEnum::LogParamContent(), - _ => ExtensionPackEnum::None, - } - } -} -impl ExtensionPackEnum { - pub fn _to_value(&self, ext: &Extensions) -> Result, BoxError> { - match self { - ExtensionPackEnum::LogParamContent() => { - if let Some(ext) = LogParamContent::get(ext) { - return Ok(Some(serde_json::to_value(ext)?)); - } - } - ExtensionPackEnum::None => (), - } - Ok(None) - } -} diff --git a/backend/gateways/spacegate-plugins/src/extension/audit_log_param.rs b/backend/gateways/spacegate-plugins/src/extension/audit_log_param.rs index b7d80f7e5..3f0144b3c 100644 --- a/backend/gateways/spacegate-plugins/src/extension/audit_log_param.rs +++ b/backend/gateways/spacegate-plugins/src/extension/audit_log_param.rs @@ -1,11 +1,25 @@ +use bios_sdk_invoke::clients::spi_log_client::{self, LogItemAddV2Req}; +use http::uri::Scheme; +use serde::{Deserialize, Serialize}; +use spacegate_shell::{ + hyper::HeaderMap, + kernel::{ + extension::{EnterTime, OriginalIpAddr, PeerAddr}, + Extract, + }, + SgRequestExt, SgResponse, +}; use std::time::Duration; +use tardis::{basic::dto::TardisContext, TardisFuns, TardisFunsInst}; +use tardis::{log as tracing, tokio}; -use serde::{Deserialize, Serialize}; -use spacegate_shell::{hyper::HeaderMap, kernel::extension::ExtensionPack}; -use super::cert_info::RoleInfo; -#[derive(Clone)] +use crate::{audit_log::AuditLogPlugin, plugin::PluginBiosExt}; + +use super::cert_info::{CertInfo, RoleInfo}; + +#[derive(Debug, Clone)] pub struct AuditLogParam { pub request_path: String, pub request_method: String, @@ -14,6 +28,41 @@ pub struct AuditLogParam { pub request_ip: String, } +impl Extract for AuditLogParam { + fn extract(req: &http::Request) -> Self { + AuditLogParam { + request_path: req.uri().path().to_string(), + request_method: req.method().to_string(), + request_headers: req.headers().clone(), + request_scheme: req.uri().scheme().unwrap_or(&Scheme::HTTP).to_string(), + request_ip: req.extract::().to_string(), + } + } +} + +impl AuditLogParam { + pub fn merge_audit_log_param_content(self, response: &SgResponse, success: bool, header_token_name: &str) -> LogParamContent { + let cert_info = response.extensions().get::(); + let start_time = response.extensions().get::().map(|time| time.0); + let end_time = std::time::Instant::now(); + let param = self; + LogParamContent { + op: param.request_method, + name: cert_info.and_then(|info| info.name.clone()).unwrap_or_default(), + user_id: cert_info.map(|info| info.id.clone()), + role: cert_info.map(|info| info.roles.clone()).unwrap_or_default(), + ip: param.request_ip, + path: param.request_path, + scheme: param.request_scheme, + token: param.request_headers.get(header_token_name).and_then(|v| v.to_str().ok().map(|v| v.to_string())), + server_timing: start_time.map(|st| end_time - st), + resp_status: response.status().as_u16().to_string(), + success, + own_paths: cert_info.and_then(|info| info.own_paths.clone()), + } + } +} + #[derive(Clone, Serialize, Deserialize)] pub struct LogParamContent { pub op: String, @@ -31,4 +80,56 @@ pub struct LogParamContent { pub success: bool, } -impl ExtensionPack for LogParamContent {} +impl LogParamContent { + pub fn send_audit_log(self, spi_app_id: &str, log_url: &str, tag: &str) { + send_audit_log(spi_app_id, log_url, tag, self) + } +} + + + +fn send_audit_log(spi_app_id: &str, log_url: &str, tag: &str, content: LogParamContent) { + let funs = AuditLogPlugin::get_funs_inst_by_plugin_code(); + + let spi_ctx = TardisContext { + ak: spi_app_id.to_string(), + own_paths: spi_app_id.to_string(), + ..Default::default() + }; + + let tag = tag.to_string(); + if !log_url.is_empty() && spi_app_id.is_empty() { + tokio::task::spawn(async move { + match spi_log_client::SpiLogClient::addv2( + LogItemAddV2Req { + tag, + content: TardisFuns::json.obj_to_json(&content).unwrap_or_default(), + kind: None, + ext: Some(content.to_value()), + key: None, + op: Some(content.op), + rel_key: None, + idempotent_id: None, + ts: Some(tardis::chrono::Utc::now()), + owner: content.user_id, + own_paths: None, + msg: None, + owner_name: None, + push: false, + disable: None, + }, + &funs, + &spi_ctx, + ) + .await + { + Ok(_) => { + tracing::trace!("[Plugin.AuditLog] add log success") + } + Err(e) => { + tracing::warn!("[Plugin.AuditLog] failed to add log:{e}") + } + }; + }); + } +} diff --git a/backend/gateways/spacegate-plugins/src/extension/notification.rs b/backend/gateways/spacegate-plugins/src/extension/notification.rs index 840686c55..7b3779443 100644 --- a/backend/gateways/spacegate-plugins/src/extension/notification.rs +++ b/backend/gateways/spacegate-plugins/src/extension/notification.rs @@ -1,49 +1,112 @@ -use std::{borrow::Cow, collections::HashMap, sync::Arc}; +use std::{collections::HashMap, sync::Arc, time::Duration}; -use http::{header::CONTENT_TYPE, HeaderName, HeaderValue, Uri}; use serde::{Deserialize, Serialize}; -use spacegate_shell::{kernel::backend_service::http_client_service::HttpClient, SgBody, SgRequest}; -use tardis::{log as tracing, serde_json}; +use tardis::{basic::dto::TardisContext, log as tracing, tokio}; /// Context to call notification api /// /// Extract it from request extensions, and call [`NotificationContext::notify`] to send notification #[derive(Debug, Clone)] pub struct NotificationContext { - pub(crate) api: Arc, - pub(crate) headers: Arc>, - pub(crate) client: HttpClient, + pub(crate) reach_api: Arc, + pub(crate) log_api: Arc, + pub(crate) spi_app_id: Arc, + pub(crate) audit_log_tag: Arc, + pub(crate) audit_log_token_header_name: Arc, + pub(crate) templates: Arc>, + pub(crate) audit_param: Arc, + pub(crate) cache_client: RedisClient, + pub(crate) dedup_cache_cool_down: Duration, } impl NotificationContext { - fn build_notification_request(&self, req: &ReachMsgSendReq) -> SgRequest { - let req_bytes = serde_json::to_vec(&req).expect("ReachMsgSendReq is a valid json"); - let body = SgBody::full(req_bytes); - let mut req = SgRequest::new(body); - req.headers_mut().insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); - *req.uri_mut() = self.api.as_ref().clone(); - for (k, v) in self.headers.iter() { - req.headers_mut().insert(k.clone(), v.clone()); + pub fn submit_notify(&self, req: ReachMsgSendReq, dedup_hash: u64) { + if self.reach_api.is_empty() { + tracing::debug!("reach api is empty, skip sending notification"); + return; } - req + let cache_client = self.cache_client.clone(); + let ctx = TardisContext { + ak: self.spi_app_id.to_string(), + own_paths: self.spi_app_id.to_string(), + ..Default::default() + }; + let cool_down = (self.dedup_cache_cool_down.as_secs() as u64).min(1); + tokio::spawn(async move { + let key = format!("sg:plugin:{}:{}", NotifyPlugin::CODE, dedup_hash); + let mut conn = cache_client.get_conn().await; + // check if the key exists + if let Ok(Some(_)) = conn.get::<'_, _, Option>(&key).await { + tracing::debug!("dedup cache hit, skip sending notification"); + return; + } + + // set the dedup key + if let Err(e) = conn.set_ex::<'_, _, _,Option>(&key, "1", cool_down).await { + tracing::error!(error = ?e, "set dedup cache failed"); + return; + } + + let funs = NotifyPlugin::get_funs_inst_by_plugin_code(); + let response = bios_sdk_invoke::clients::reach_client::ReachClient::send_message(&req.into(), &funs, &ctx).await; + if let Err(e) = response { + tracing::error!(error = ?e, "send notification failed"); + } + }); } - pub async fn notify(&self, req: &ReachMsgSendReq) { - let notify_response = self.client.clone().request(self.build_notification_request(req)).await; - if !notify_response.status().is_success() { - tracing::warn!(response = ?notify_response, "send notification failed"); - } + pub fn report(&self, response: &SgResponse, report: R) { + let replace = report.get_replacement(); + let key = report.key(); - let Ok(response) = notify_response.into_body().dump().await.inspect_err(|e| { - tracing::error!(error = ?e, "failed to read response body"); - }) else { - return; - }; - let response_str = String::from_utf8_lossy(response.get_dumped().expect("just dump body")); - tracing::debug!(response = ?response_str, "receive notification api response"); + if let Some(template) = self.templates.get(key) { + if let Some(notify_req) = template.reach.as_ref() { + let mut req = notify_req.clone(); + req.merge_replace(replace.clone()); + let context = self.clone(); + context.submit_notify(req, report.dedup_hash()); + } + if let Some(log_template) = template.audit_log.as_ref() { + let formatted = format_template(log_template, &replace); + self.submit_audit_log(response, Some(formatted)); + } + } + } + pub fn submit_audit_log(&self, response: &SgResponse, extra_info: Option) { + let mut log_param_content = self.audit_param.as_ref().clone().merge_audit_log_param_content(response, true, &self.audit_log_token_header_name); + if let Some(extra_info) = extra_info { + log_param_content.op = extra_info; + } + log_param_content.send_audit_log(&self.spi_app_id, &self.log_api, &self.audit_log_tag); } } -#[derive(Debug, Serialize, Deserialize)] +pub struct TamperReport {} + +pub struct UnauthorizedOperationReport {} + +pub struct CertLockReport {} + +#[derive(Debug, Clone)] +pub struct ContentFilterForbiddenReport { + pub(crate) forbidden_reason: String, +} + +use spacegate_shell::{ + ext_redis::{redis::AsyncCommands, RedisClient}, + plugin::{ + schemars::{self, JsonSchema}, + Plugin, + }, + SgResponse, +}; + +use crate::plugin::{ + notify::{format_template, NotifyPlugin, NotifyPluginConfigTemplates, NotifyPluginConfigTemplatesItem, Report}, + PluginBiosExt, +}; + +use super::audit_log_param::AuditLogParam; +#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)] pub struct ReachMsgSendReq { pub scene_code: String, pub receives: Vec, @@ -51,9 +114,35 @@ pub struct ReachMsgSendReq { pub replace: HashMap, } -#[derive(Debug, Serialize, Deserialize)] +impl ReachMsgSendReq { + pub fn merge_replace>(&mut self, replace: impl IntoIterator) { + self.replace.extend(replace.into_iter().map(|(k, v)| (k.into(), v))); + } +} + +impl From for bios_sdk_invoke::clients::reach_client::ReachMsgSendReq { + fn from(val: ReachMsgSendReq) -> Self { + bios_sdk_invoke::clients::reach_client::ReachMsgSendReq { + scene_code: val.scene_code, + receives: val.receives.into_iter().map(Into::into).collect(), + rel_item_id: val.rel_item_id, + replace: val.replace, + } + } +} +#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)] pub struct ReachMsgReceive { pub receive_group_code: String, pub receive_kind: String, pub receive_ids: Vec, } + +impl From for bios_sdk_invoke::clients::reach_client::ReachMsgReceive { + fn from(val: ReachMsgReceive) -> Self { + bios_sdk_invoke::clients::reach_client::ReachMsgReceive { + receive_group_code: val.receive_group_code, + receive_kind: val.receive_kind, + receive_ids: val.receive_ids, + } + } +} diff --git a/backend/gateways/spacegate-plugins/src/lib.rs b/backend/gateways/spacegate-plugins/src/lib.rs index 69435faa0..2ffa9dcb1 100644 --- a/backend/gateways/spacegate-plugins/src/lib.rs +++ b/backend/gateways/spacegate-plugins/src/lib.rs @@ -6,6 +6,7 @@ mod consts; mod extension; mod marker; mod plugin; +mod utils; pub const PACKAGE_NAME: &str = "spacegate_lib"; use plugin::{notify, op_redis_publisher}; diff --git a/backend/gateways/spacegate-plugins/src/plugin.rs b/backend/gateways/spacegate-plugins/src/plugin.rs index f335721f0..775d8f90f 100644 --- a/backend/gateways/spacegate-plugins/src/plugin.rs +++ b/backend/gateways/spacegate-plugins/src/plugin.rs @@ -1,8 +1,22 @@ +use spacegate_shell::plugin::Plugin; +use tardis::{TardisFuns, TardisFunsInst}; + pub mod anti_replay; pub mod anti_xss; pub mod audit_log; pub mod auth; +pub mod content_filter; pub mod ip_time; pub mod notify; pub mod op_redis_publisher; pub mod rewrite_ns_b_ip; + +pub trait PluginBiosExt { + fn get_funs_inst_by_plugin_code() -> TardisFunsInst; +} + +impl PluginBiosExt for T { + fn get_funs_inst_by_plugin_code() -> TardisFunsInst { + TardisFuns::inst(Self::CODE, None) + } +} diff --git a/backend/gateways/spacegate-plugins/src/plugin/anti_xss.rs b/backend/gateways/spacegate-plugins/src/plugin/anti_xss.rs index 0b63ca6ce..9498b8b62 100644 --- a/backend/gateways/spacegate-plugins/src/plugin/anti_xss.rs +++ b/backend/gateways/spacegate-plugins/src/plugin/anti_xss.rs @@ -9,10 +9,12 @@ use spacegate_shell::{ }; macro_rules! append_value { - ($result:expr, $field:expr, $value:expr) => { - if let Some(val) = $value { - $result.push_str(&format!("{} {};", $field, val)); - } + ($result:ident {$($field:literal: $value:expr ,)*}) => { + $(if let Some(val) = $value { + $result.push_str($field); + $result.push(' '); + $result.push_str(val.as_str()); + })* }; } @@ -56,28 +58,29 @@ pub struct CSPConfig { impl CSPConfig { fn to_string_header_value(&self) -> String { let mut result = format!("default-src {};", self.default_src); - append_value!(result, "base-uri", &self.base_uri); - append_value!(result, "child-src", &self.child_src); - append_value!(result, "connect-src", &self.connect_src); - append_value!(result, "font-src", &self.font_src); - append_value!(result, "form-action", &self.form_action); - append_value!(result, "frame-ancestors", &self.frame_ancestors); - append_value!(result, "frame-src", &self.frame_src); - append_value!(result, "img-src", &self.img_src); - append_value!(result, "manifest-src", &self.manifest_src); - append_value!(result, "media-src", &self.media_src); - append_value!(result, "object-src", &self.object_src); - append_value!(result, "sandbox", &self.sandbox); - append_value!(result, "script-src", &self.script_src); - append_value!(result, "script-src-attr", &self.script_src_attr); - append_value!(result, "script-src-elem", &self.script_src_elem); - append_value!(result, "strict-dynamic", &self.strict_dynamic); - append_value!(result, "style-src", &self.style_src); - append_value!(result, "style-src-attr", &self.style_src_attr); - append_value!(result, "style-src-elem", &self.style_src_elem); - append_value!(result, "worker-src", &self.worker_src); - append_value!(result, "report-to", &self.report_to); - + append_value!(result { + "base-uri": &self.base_uri, + "child-src": &self.child_src, + "connect-src": &self.connect_src, + "font-src": &self.font_src, + "form-action": &self.form_action, + "frame-ancestors": &self.frame_ancestors, + "frame-src": &self.frame_src, + "img-src": &self.img_src, + "manifest-src": &self.manifest_src, + "media-src": &self.media_src, + "object-src": &self.object_src, + "sandbox": &self.sandbox, + "script-src": &self.script_src, + "script-src-attr": &self.script_src_attr, + "script-src-elem": &self.script_src_elem, + "strict-dynamic": &self.strict_dynamic, + "style-src": &self.style_src, + "style-src-attr": &self.style_src_attr, + "style-src-elem": &self.style_src_elem, + "worker-src": &self.worker_src, + "report-to": &self.report_to, + }); result } } @@ -130,18 +133,24 @@ pub enum SandBoxValue { impl fmt::Display for SandBoxValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.as_str()) + } +} + +impl SandBoxValue { + pub fn as_str(&self) -> &'static str { match self { - SandBoxValue::None => write!(f, ""), - SandBoxValue::AllowForms => write!(f, "allow-forms"), - SandBoxValue::AllowModals => write!(f, "allow-modals"), - SandBoxValue::AllowOrientationLock => write!(f, "allow-orientation-lock"), - SandBoxValue::AllowPointerLock => write!(f, "allow-pointer-lock"), - SandBoxValue::AllowPopups => write!(f, "allow-popups"), - SandBoxValue::AllowPopupsToEscapeSandbox => write!(f, "allow-popups-to-escape-sandbox"), - SandBoxValue::AllowPresentation => write!(f, "allow-presentation"), - SandBoxValue::AllowSameOrigin => write!(f, "allow-same-origin"), - SandBoxValue::AllowScripts => write!(f, "allow-scripts"), - SandBoxValue::AllowTopNavigation => write!(f, "allow-top-navigation"), + SandBoxValue::None => "", + SandBoxValue::AllowForms => "allow-forms", + SandBoxValue::AllowModals => "allow-modals", + SandBoxValue::AllowOrientationLock => "allow-orientation-lock", + SandBoxValue::AllowPointerLock => "allow-pointer-lock", + SandBoxValue::AllowPopups => "allow-popups", + SandBoxValue::AllowPopupsToEscapeSandbox => "allow-popups-to-escape-sandbox", + SandBoxValue::AllowPresentation => "allow-presentation", + SandBoxValue::AllowSameOrigin => "allow-same-origin", + SandBoxValue::AllowScripts => "allow-scripts", + SandBoxValue::AllowTopNavigation => "allow-top-navigation", } } } diff --git a/backend/gateways/spacegate-plugins/src/plugin/audit_log.rs b/backend/gateways/spacegate-plugins/src/plugin/audit_log.rs index 003a39083..8a4016bcb 100644 --- a/backend/gateways/spacegate-plugins/src/plugin/audit_log.rs +++ b/backend/gateways/spacegate-plugins/src/plugin/audit_log.rs @@ -1,23 +1,19 @@ use std::collections::HashMap; use std::str::FromStr; -use std::time::Instant; -use bios_sdk_invoke::clients::spi_log_client::{self, LogItemAddV2Req}; use bios_sdk_invoke::invoke_config::InvokeConfig; use bios_sdk_invoke::invoke_enumeration::InvokeModuleKind; use bios_sdk_invoke::invoke_initializer; -use http::uri::Scheme; use jsonpath_rust::JsonPathInst; use serde::{Deserialize, Serialize}; use spacegate_shell::hyper::{Request, Response}; -use spacegate_shell::kernel::extension::{EnterTime, PeerAddr, Reflect}; +use spacegate_shell::kernel::extension::Reflect; use spacegate_shell::kernel::helper_layers::function::Inner; use spacegate_shell::plugin::{Plugin, PluginError}; -use spacegate_shell::{BoxError, SgBody}; -use tardis::basic::dto::TardisContext; +use spacegate_shell::{BoxError, SgBody, SgRequestExt}; use tardis::log::{debug, trace, warn}; use tardis::serde_json::{json, Value}; @@ -26,8 +22,6 @@ use tardis::{ basic::result::TardisResult, log, serde_json::{self}, - tokio::{self}, - TardisFuns, TardisFunsInst, }; use crate::extension::audit_log_param::{AuditLogParam, LogParamContent}; @@ -71,10 +65,6 @@ impl AuditLogPlugin { } } trace!("[Plugin.AuditLog] exclude log path do not matched: path {}", path); - - let start_time = resp.extensions().get::().map(|time| time.0); - let end_time = Instant::now(); - let body_string = if let Some(raw_body) = resp.extensions_mut().remove::().map(|b| b.get()) { serde_json::from_str::(&String::from_utf8_lossy(&raw_body)) } else { @@ -123,20 +113,7 @@ impl AuditLogPlugin { } Err(_) => false, }; - let content = LogParamContent { - op: param.request_method, - name: resp.extensions().get::().and_then(|info| info.name.clone()).unwrap_or_default(), - user_id: resp.extensions().get::().map(|info| info.id.clone()), - role: resp.extensions().get::().map(|info| info.roles.clone()).unwrap_or_default(), - ip: param.request_ip, - path: param.request_path, - scheme: param.request_scheme, - token: param.request_headers.get(&self.header_token_name).and_then(|v| v.to_str().ok().map(|v| v.to_string())), - server_timing: start_time.map(|st| end_time - st), - resp_status: resp.status().as_u16().to_string(), - success, - own_paths: resp.extensions().get::().and_then(|info| info.own_paths.clone()), - }; + let content = param.merge_audit_log_param_content(&resp, success, &self.header_token_name); Ok((resp, Some(content))) } @@ -165,14 +142,7 @@ impl AuditLogPlugin { } fn req(&self, mut req: Request) -> Result, Response> { - let param = AuditLogParam { - request_path: req.uri().path().to_string(), - request_method: req.method().to_string(), - request_headers: req.headers().clone(), - request_scheme: req.uri().scheme().unwrap_or(&Scheme::HTTP).to_string(), - request_ip: req.extensions().get::().ok_or_else(|| PluginError::internal_error::("[Plugin.AuditLog] missing peer addr"))?.0.ip().to_string(), - }; - + let param = req.extract::(); if let Some(ident) = req.headers().get(self.head_key_auth_ident.clone()) { let ident = ident.to_str().unwrap_or_default().to_string(); let reflect = req.extensions_mut().get_mut::().expect("missing reflect"); @@ -198,49 +168,7 @@ impl AuditLogPlugin { let (resp, content) = self.get_log_content(resp).await.map_err(PluginError::internal_error::)?; if let Some(content) = content { - let funs = get_tardis_inst(); - - let spi_ctx = TardisContext { - owner: resp.extensions().get::().map(|info| info.id.clone()).unwrap_or_default(), - roles: resp.extensions().get::().map(|info| info.roles.clone().into_iter().map(|r| r.id).collect()).unwrap_or_default(), - ..Default::default() - }; - - let tag = self.tag.clone(); - if !self.log_url.is_empty() && !self.spi_app_id.is_empty() { - tokio::task::spawn(async move { - match spi_log_client::SpiLogClient::addv2( - LogItemAddV2Req { - tag, - content: TardisFuns::json.obj_to_json(&content).unwrap_or_default(), - kind: None, - ext: Some(content.to_value()), - key: None, - op: Some(content.op), - rel_key: None, - idempotent_id: None, - ts: Some(tardis::chrono::Utc::now()), - owner: content.user_id, - own_paths: None, - msg: None, - owner_name: None, - push: false, - disable: None, - }, - &funs, - &spi_ctx, - ) - .await - { - Ok(_) => { - log::trace!("[Plugin.AuditLog] add log success") - } - Err(e) => { - log::warn!("[Plugin.AuditLog] failed to add log:{e}") - } - }; - }); - } + content.send_audit_log(&self.spi_app_id, &self.log_url, &self.tag); } Ok(resp) @@ -295,12 +223,8 @@ impl Plugin for AuditLogPlugin { } } -fn get_tardis_inst() -> TardisFunsInst { - TardisFuns::inst(CODE.to_string(), None) -} - impl LogParamContent { - fn to_value(&self) -> Value { + pub fn to_value(&self) -> Value { json!({ "name":self.name, "id":self.user_id, diff --git a/backend/gateways/spacegate-plugins/src/plugin/auth.rs b/backend/gateways/spacegate-plugins/src/plugin/auth.rs index 5e7ac23b0..428e50594 100644 --- a/backend/gateways/spacegate-plugins/src/plugin/auth.rs +++ b/backend/gateways/spacegate-plugins/src/plugin/auth.rs @@ -33,13 +33,12 @@ use std::{ }; use tardis::{ basic::{error::TardisError, result::TardisResult}, - cache::AsyncCommands as _, config::config_dto::CacheModuleConfig, serde_json::{self, json}, tokio::{sync::RwLock, task::JoinHandle}, tracing::{self as tracing, instrument, warn}, url::Url, - web::web_resp::TardisResp, + web::web_resp::{TardisResp, HEADER_X_TARDIS_ERROR}, TardisFuns, }; use tardis::{config::config_dto::TardisComponentConfig, web::poem_openapi::types::Type}; @@ -270,7 +269,7 @@ impl AuthPlugin { let is_east_west_traffic = req.extensions().get::().map(Deref::deref).unwrap_or(&false); if self.auth_config.strict_security_mode && !is_true_mix_req && !is_east_west_traffic { tracing::debug!("[SG.Filter.Auth] handle mix request"); - return Ok(handle_mix_req(&self, req).await.map_err(PluginError::internal_error::)?); + return Ok(handle_mix_req(self, req).await.map_err(PluginError::internal_error::)?); } req.headers_mut().append(&self.header_is_mix_req, HeaderValue::from_static("false")); @@ -303,12 +302,7 @@ impl AuthPlugin { } Err(e) => { tracing::info!("[SG.Filter.Auth] auth return error {:?}", e); - let err_resp = Response::builder() - .header(http::header::CONTENT_TYPE, HeaderValue::from_static("application/json")) - .status(StatusCode::from_str(&e.code).unwrap_or(StatusCode::BAD_GATEWAY)) - .body(SgBody::full(format!("[SG.Filter.Auth] auth return error:{e}"))) - .map_err(PluginError::internal_error::)?; - Err(err_resp) + Err(tardis_error_into_response(e)) } } } @@ -601,6 +595,20 @@ impl Plugin for AuthPlugin { } } +pub fn tardis_error_into_response(e: TardisError) -> Response { + let status_code = if e.code.len() >= 3 { + StatusCode::from_str(&e.code[0..3]).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR) + } else { + StatusCode::INTERNAL_SERVER_ERROR + }; + Response::builder() + .header(http::header::CONTENT_TYPE, HeaderValue::from_static("application/json")) + .header(HEADER_X_TARDIS_ERROR, &e.code) + .status(status_code) + .body(SgBody::full(format!("[SG.Filter.Auth] auth return error:{e}"))) + .expect("invalid response") +} + #[cfg(test)] #[allow(clippy::unwrap_used)] mod tests; diff --git a/backend/gateways/spacegate-plugins/src/plugin/content_filter.rs b/backend/gateways/spacegate-plugins/src/plugin/content_filter.rs new file mode 100644 index 000000000..18584b80c --- /dev/null +++ b/backend/gateways/spacegate-plugins/src/plugin/content_filter.rs @@ -0,0 +1,130 @@ +use http::StatusCode; +use serde::{Deserialize, Serialize}; +use spacegate_shell::hyper::body::Body; +use spacegate_shell::plugin::Plugin; +use spacegate_shell::plugin::{ + plugin_meta, + schemars::{self, JsonSchema}, +}; +use spacegate_shell::{BoxError, SgResponse, SgResponseExt}; +use std::ops::Deref; +use std::str::FromStr; +use std::{fmt::Display, sync::Arc}; +use tardis::regex::bytes::Regex as BytesRegex; +use tardis::serde_json; + +use crate::extension::notification::ContentFilterForbiddenReport; + +#[derive(Debug, Clone)] +pub enum BytesFilter { + Regex(BytesRegex), +} + +impl JsonSchema for BytesFilter { + fn schema_name() -> String { + String::schema_name() + } + + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + String::json_schema(gen) + } + + fn schema_id() -> std::borrow::Cow<'static, str> { + String::schema_id() + } + + fn is_referenceable() -> bool { + String::is_referenceable() + } +} + +impl Display for BytesFilter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + BytesFilter::Regex(regex) => write!(f, "{}", regex.as_str()), + } + } +} + +impl FromStr for BytesFilter { + type Err = String; + + fn from_str(s: &str) -> Result { + BytesRegex::new(s).map(BytesFilter::Regex).map_err(|e| e.to_string()) + } +} +impl Serialize for BytesFilter { + fn serialize(&self, serializer: S) -> Result { + match self { + BytesFilter::Regex(re) => serializer.serialize_str(re.as_str()), + } + } +} + +impl<'de> Deserialize<'de> for BytesFilter { + fn deserialize>(deserializer: D) -> Result { + let s = String::deserialize(deserializer)?; + BytesRegex::new(&s).map(BytesFilter::Regex).map_err(serde::de::Error::custom) + } +} + +impl BytesFilter { + pub fn matches(&self, bytes: &[u8]) -> bool { + match self { + BytesFilter::Regex(re) => re.is_match(bytes), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)] +pub struct ContentFilterConfig { + content_length_limit: Option, + forbidden_content_filter: Vec, +} +#[derive(Debug, Clone)] +pub struct ContentFilterPlugin(Arc); +impl Deref for ContentFilterPlugin { + type Target = ContentFilterConfig; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl Plugin for ContentFilterPlugin { + const CODE: &'static str = "content-filter"; + fn meta() -> spacegate_shell::model::PluginMetaData { + plugin_meta! { + description: "Filter content based on type, keywords and length." + } + } + + async fn call(&self, mut req: spacegate_shell::SgRequest, inner: spacegate_shell::plugin::Inner) -> Result { + if let Some(length_limit) = self.content_length_limit { + let size = req.body().size_hint(); + if size.lower() > length_limit as u64 { + return Ok(SgResponse::with_code_empty(StatusCode::PAYLOAD_TOO_LARGE)); + } + } + if !self.forbidden_content_filter.is_empty() { + let (parts, body) = req.into_parts(); + let body = body.dump().await?; + for filter in &self.forbidden_content_filter { + let bytes = body.get_dumped().expect("dumped"); + if filter.matches(bytes) { + let mut response = SgResponse::with_code_empty(StatusCode::BAD_REQUEST); + response.extensions_mut().insert(ContentFilterForbiddenReport { + forbidden_reason: filter.to_string(), + }); + return Ok(response); + } + } + req = spacegate_shell::SgRequest::from_parts(parts, body); + } + Ok(inner.call(req).await) + } + + fn create(plugin_config: spacegate_shell::model::PluginConfig) -> Result { + let config = serde_json::from_value(plugin_config.spec)?; + Ok(ContentFilterPlugin(Arc::new(config))) + } +} diff --git a/backend/gateways/spacegate-plugins/src/plugin/notify.rs b/backend/gateways/spacegate-plugins/src/plugin/notify.rs index 5d6d44b13..bdf40f2e0 100644 --- a/backend/gateways/spacegate-plugins/src/plugin/notify.rs +++ b/backend/gateways/spacegate-plugins/src/plugin/notify.rs @@ -1,25 +1,207 @@ -use std::{collections::HashMap, sync::Arc}; +use std::{ + collections::HashMap, + hash::{Hash, Hasher}, + net::IpAddr, + sync::{Arc, OnceLock}, +}; -use http::{HeaderName, HeaderValue, Uri}; +use bios_sdk_invoke::{invoke_config::InvokeConfig, invoke_enumeration::InvokeModuleKind, invoke_initializer}; +use http::HeaderValue; use serde::{Deserialize, Serialize}; use spacegate_shell::{ - kernel::backend_service::http_client_service::get_client, - plugin::{plugin_meta, schema, schemars, Inner, Plugin, PluginSchemaExt}, + ext_redis::RedisClient, + kernel::extension::OriginalIpAddr, + plugin::{plugin_meta, plugins::limit::RateLimitReport, schema, schemars, Inner, Plugin, PluginSchemaExt}, BoxError, SgRequest, SgRequestExt, SgResponse, }; -use tardis::serde_json; +use tardis::{ + regex::{self, Regex}, + serde_json, +}; -use crate::extension::notification::NotificationContext; +use crate::extension::{ + audit_log_param::AuditLogParam, + cert_info::CertInfo, + notification::{CertLockReport, ContentFilterForbiddenReport, NotificationContext, ReachMsgSendReq, TamperReport, UnauthorizedOperationReport}, +}; #[derive(Serialize, Deserialize, schemars::JsonSchema)] pub struct NotifyPluginConfig { - api: String, - headers: HashMap, + /// templates for different notification types + /// - rate_limit: rate limit notification + /// - count: number of requests + /// - time_window: time window + /// - tamper: tamper notification + /// - unauthorized_operation: unauthorized operation notification + /// - cert_lock: cert lock notification + /// - content_filter_forbidden: content filter forbidden notification + /// - reason: forbidden reason + templates: HashMap, + log_api: String, + reach_api: String, + spi_app_id: String, + audit_log_tag: String, + audit_log_token_header_name: String, + dedup_cache_cool_down: String, +} + +impl Default for NotifyPluginConfig { + fn default() -> Self { + Self { + templates: Default::default(), + log_api: "http://bios.devops:8080".into(), + reach_api: "http://bios.devops:8080".into(), + spi_app_id: "".into(), + audit_log_tag: "iam_abnormal".into(), + audit_log_token_header_name: "Bios-Token".into(), + dedup_cache_cool_down: "10m".into(), + } + } +} + +pub trait Report { + fn get_replacement(&self) -> HashMap<&'static str, String>; + fn key(&self) -> &'static str; + fn dedup_hash(&self) -> u64 { + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + self.key().hash(&mut hasher); + hasher.finish() + } +} + +pub struct WithUserAndIp<'a, T> { + pub(crate) user: Option<&'a str>, + pub(crate) ip: IpAddr, + pub(crate) raw: &'a T, +} + +pub trait ReportExt { + fn with_user_and_ip<'a>(&'a self, user: Option<&'a str>, ip: IpAddr) -> WithUserAndIp<'a, Self> + where + Self: Sized; +} + +impl ReportExt for T { + fn with_user_and_ip<'a>(&'a self, user: Option<&'a str>, ip: IpAddr) -> WithUserAndIp<'a, Self> { + WithUserAndIp { user, ip, raw: self } + } +} + +impl Report for WithUserAndIp<'_, T> +where + T: Report, +{ + fn get_replacement(&self) -> HashMap<&'static str, String> { + let mut replace = self.raw.get_replacement(); + let mut formatted_user = self.ip.to_string(); + if let Some(tamper_user) = self.user { + formatted_user.push('('); + formatted_user.push_str(tamper_user); + formatted_user.push(')'); + } + replace.insert("user", formatted_user); + replace + } + fn key(&self) -> &'static str { + self.raw.key() + } + fn dedup_hash(&self) -> u64 { + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + self.raw.dedup_hash().hash(&mut hasher); + self.ip.hash(&mut hasher); + hasher.finish() + } +} + +impl Report for RateLimitReport { + fn get_replacement(&self) -> HashMap<&'static str, String> { + let mut replace = HashMap::new(); + let count = format!("{}", self.plugin.max_request_number); + let time_window = format!("{:.000}s", self.plugin.time_window_ms as f64 / 1000.0); + replace.insert("count", count); + replace.insert("time_window", time_window); + replace + } + fn key(&self) -> &'static str { + "rate_limit" + } +} + +impl Report for CertLockReport { + fn get_replacement(&self) -> HashMap<&'static str, String> { + HashMap::new() + } + fn key(&self) -> &'static str { + "cert_lock" + } +} + +impl Report for TamperReport { + fn get_replacement(&self) -> HashMap<&'static str, String> { + HashMap::new() + } + fn key(&self) -> &'static str { + "tamper" + } } + +impl Report for UnauthorizedOperationReport { + fn get_replacement(&self) -> HashMap<&'static str, String> { + HashMap::new() + } + fn key(&self) -> &'static str { + "unauthorized_operation" + } +} + +impl Report for ContentFilterForbiddenReport { + fn get_replacement(&self) -> HashMap<&'static str, String> { + let mut replace = HashMap::new(); + replace.insert("reason", self.forbidden_reason.to_string()); + replace + } + fn key(&self) -> &'static str { + "content_filter_forbidden" + } +} +#[derive(Serialize, Deserialize, schemars::JsonSchema, Default, Debug)] +pub struct NotifyPluginConfigTemplates { + pub rate_limit: NotifyPluginConfigTemplatesItem, + pub tamper: NotifyPluginConfigTemplatesItem, + pub unauthorized_operation: NotifyPluginConfigTemplatesItem, +} +#[derive(Serialize, Deserialize, schemars::JsonSchema, Default, Debug)] +pub struct NotifyPluginConfigTemplatesItem { + pub reach: Option, + pub audit_log: Option, +} + schema!(NotifyPlugin, NotifyPluginConfig); #[derive(Debug, Clone)] pub struct NotifyPlugin { - api: Arc, - headers: Arc>, + log_api: Arc, + reach_api: Arc, + spi_app_id: Arc, + audit_log_tag: Arc, + audit_log_token_header_name: Arc, + templates: Arc>, + dedup_cache_cool_down: std::time::Duration, +} + +pub fn format_template(template: &str, replace: &HashMap<&'static str, String>) -> String { + static PLACEHOLDER_RE: OnceLock = OnceLock::new(); + const NULL_STR: &str = ""; + fn re() -> &'static Regex { + PLACEHOLDER_RE.get_or_init(|| Regex::new(r"\$\{(\w+)\}").expect("invalid regex")) + } + let formatted = re().replace_all(template, |caps: ®ex::Captures| { + let replaced = replace.get(caps.get(1).expect("regex should have a capture").as_str()); + if let Some(replaced) = replaced { + replaced.as_str() + } else { + NULL_STR + } + }); + formatted.to_string() } impl Plugin for NotifyPlugin { @@ -30,36 +212,112 @@ impl Plugin for NotifyPlugin { } } fn create(plugin_config: spacegate_shell::model::PluginConfig) -> Result { - // parse uri let config: NotifyPluginConfig = serde_json::from_value(plugin_config.spec)?; - let api = config.api.parse::()?; - let headers = config - .headers - .into_iter() - .map_while(|(k, v)| { - if let (Ok(k), Ok(v)) = (k.parse::(), v.parse::()) { - Some((k, v)) - } else { - None - } - }) - .collect(); + if config.spi_app_id.is_empty() { + tardis::log::error!("[Plugin.AuditLog] log_url or spi_app_id is empty!"); + } else { + invoke_initializer::init( + Self::CODE, + InvokeConfig { + spi_app_id: config.spi_app_id.clone(), + module_urls: HashMap::from([ + (InvokeModuleKind::Log.to_string(), config.log_api.clone()), + (InvokeModuleKind::Reach.to_string(), config.reach_api.clone()), + ]), + ..Default::default() + }, + )?; + } + let dedup_cache_cool_down = crate::utils::parse_duration(&config.dedup_cache_cool_down)?; Ok(Self { - api: Arc::new(api), - headers: Arc::new(headers), + log_api: config.log_api.into(), + reach_api: config.reach_api.into(), + spi_app_id: config.spi_app_id.into(), + audit_log_tag: config.audit_log_tag.into(), + audit_log_token_header_name: config.audit_log_token_header_name.into(), + templates: Arc::new(config.templates), + dedup_cache_cool_down, }) } async fn call(&self, mut req: SgRequest, inner: Inner) -> Result { + let audit_param = req.extract::(); + let redis_client = req.extract::>(); + let Some(redis_client) = redis_client else { + // skip report since redis client is not available + return Ok(inner.call(req).await); + }; let context = NotificationContext { - api: self.api.clone(), - headers: self.headers.clone(), - client: get_client(), + templates: self.templates.clone(), + log_api: self.log_api.clone(), + reach_api: self.reach_api.clone(), + spi_app_id: self.spi_app_id.clone(), + audit_log_tag: self.audit_log_tag.clone(), + audit_log_token_header_name: self.audit_log_token_header_name.clone(), + audit_param: Arc::new(audit_param), + cache_client: redis_client, + dedup_cache_cool_down: self.dedup_cache_cool_down, }; req.extensions_mut().insert(context.clone()); - req.reflect_mut().insert(context); - Ok(inner.call(req).await) + let req_cert_info = req.extensions().get::().cloned(); + let ip = req.extract::().0; + let response = inner.call(req).await; + let user = req_cert_info.as_ref().or_else(|| response.extensions().get::()).and_then(|c| c.name.clone()); + if let Some(rate_limit_report) = response.extensions().get::().filter(|r| r.rising_edge) { + context.report(&response, rate_limit_report.with_user_and_ip(user.as_deref(), ip)); + } + if let Some(flag) = response.extensions().get::() { + context.report(&response, flag.with_user_and_ip(user.as_deref(), ip)); + } + if let Some(error_code) = response.headers().get(tardis::web::web_resp::HEADER_X_TARDIS_ERROR) { + if let Some(error_kind) = KnownError::try_from_header_value(error_code) { + match error_kind { + KnownError::Tamper => { + context.report(&response, TamperReport {}.with_user_and_ip(user.as_deref(), ip)); + } + KnownError::UnauthorizedOperation => { + context.report(&response, UnauthorizedOperationReport {}.with_user_and_ip(user.as_deref(), ip)); + } + KnownError::CertLock => { + context.report(&response, CertLockReport {}.with_user_and_ip(user.as_deref(), ip)); + } + } + } + } + Ok(response) } fn schema_opt() -> Option { Some(NotifyPlugin::schema()) } } + +pub enum KnownError { + Tamper, + UnauthorizedOperation, + CertLock, +} + +impl KnownError { + pub fn try_from_header_value(header_value: &HeaderValue) -> Option { + match header_value.to_str().ok()? { + code if code.starts_with("401-signature-error") => Some(Self::Tamper), + code if code.starts_with("403-req-permission-denied") || code.starts_with("401-auth-req-unauthorized") => Some(Self::UnauthorizedOperation), + code if code.starts_with("400-rbum-cert-lock") => Some(Self::CertLock), + _ => None, + } + } +} + + +#[cfg(test)] +mod test { + #[test] + fn test_template() { + use super::format_template; + let template = "hello ${obj}"; + let mut replace = std::collections::HashMap::new(); + replace.insert("obj", "world".to_string()); + assert_eq!(format_template(template, &replace), "hello world"); + let replace = std::collections::HashMap::new(); + assert_eq!(format_template(template, &replace), "hello "); + } +} \ No newline at end of file diff --git a/backend/gateways/spacegate-plugins/src/utils.rs b/backend/gateways/spacegate-plugins/src/utils.rs new file mode 100644 index 000000000..44c6f0366 --- /dev/null +++ b/backend/gateways/spacegate-plugins/src/utils.rs @@ -0,0 +1,71 @@ +use std::time::Duration; + +use spacegate_shell::BoxError; + +pub fn parse_duration(duration: &str) -> Result { + // ()* + // = [0-9]+ + // = "ns" | "us" | "ms" | "s" | "m" | "h" | "d" + let mut duration = duration; + let mut total_duration = Duration::new(0, 0); + while !duration.is_empty() { + // Parse number + let (number, rest) = match duration.find(|c: char| !c.is_ascii_digit() && c != ' ') { + Some(index) => duration.split_at(index), + None => (duration, ""), + }; + let number = number.trim().parse::()?; + duration = rest; + + // Parse unit + let (unit, rest) = match duration.find(|c: char| !c.is_alphabetic() && c != ' ') { + Some(index) => duration.split_at(index), + None => (duration, ""), + }; + + let unit = match unit.trim() { + "ns" | "nanosecond" => Duration::from_nanos(number), + "us" | "microsecond" => Duration::from_micros(number), + "ms" | "millisecond" => Duration::from_millis(number), + "s" | "second" => Duration::from_secs(number), + "m" | "min" => Duration::from_secs(number * 60), + "h" | "hour" => Duration::from_secs(number * 60 * 60), + "d" | "day" => Duration::from_secs(number * 60 * 60 * 24), + _ => return Err(format!("Invalid unit: {}", unit).into()), + }; + + total_duration += unit; + duration = rest; + } + Ok(total_duration) +} + +#[cfg(test)] +mod test { + #[test] + fn test_duration_parse() { + use std::time::Duration; + macro_rules! test { + ($( + $str: literal => $duration: expr, + )*) => { + $( + let duration = super::parse_duration($str).unwrap(); + assert_eq!(duration, $duration); + )* + }; + } + test! { + "1ns" => Duration::from_nanos(1), + "1us" => Duration::from_micros(1), + "1ms" => Duration::from_millis(1), + "1s" => Duration::from_secs(1), + "1m" => Duration::from_secs(60), + "1h" => Duration::from_secs(60 * 60), + "1d" => Duration::from_secs(60 * 60 * 24), + "1ns1us1ms1s1m1h1d" => Duration::from_nanos(1) + Duration::from_micros(1) + Duration::from_millis(1) + Duration::from_secs(1) + Duration::from_secs(60) + Duration::from_secs(60 * 60) + Duration::from_secs(60 * 60 * 24), + "1d 1h 1min 1s 1ms 1us 1ns" => Duration::from_secs(60 * 60 * 24) + Duration::from_secs(60 * 60) + Duration::from_secs(60) + Duration::from_secs(1) + Duration::from_millis(1) + Duration::from_micros(1) + Duration::from_nanos(1), + } + + } +} \ No newline at end of file diff --git a/backend/middlewares/flow/src/lib.rs b/backend/middlewares/flow/src/lib.rs index 904c8122c..0fdfabd26 100644 --- a/backend/middlewares/flow/src/lib.rs +++ b/backend/middlewares/flow/src/lib.rs @@ -7,4 +7,4 @@ pub mod flow_config; pub mod flow_constants; pub mod flow_initializer; mod helper; -mod serv; +mod serv; \ No newline at end of file diff --git a/backend/supports/auth/src/error.rs b/backend/supports/auth/src/error.rs index 07a48b89d..30a6d4933 100644 --- a/backend/supports/auth/src/error.rs +++ b/backend/supports/auth/src/error.rs @@ -3,7 +3,7 @@ use tardis::basic::error::TardisError; pub trait AuthError { // 410: The request signature is incorrect. fn signature_error(msg: &str, locale_code: &str) -> TardisError { - TardisError::custom("410", msg, locale_code) + TardisError::custom("401-signature-error", msg, locale_code) } } diff --git a/backend/supports/auth/src/serv/auth_kernel_serv.rs b/backend/supports/auth/src/serv/auth_kernel_serv.rs index fd2460b02..644b50790 100644 --- a/backend/supports/auth/src/serv/auth_kernel_serv.rs +++ b/backend/supports/auth/src/serv/auth_kernel_serv.rs @@ -484,10 +484,14 @@ pub async fn do_auth(ctx: &AuthContext) -> TardisResult, + pub rel_item_id: String, + pub replace: HashMap, +} + +#[derive(Debug, Serialize, Clone)] +pub struct ReachMsgReceive { + pub receive_group_code: String, + pub receive_kind: String, + pub receive_ids: Vec, +} +impl ReachClient { + pub async fn send_message(req: &ReachMsgSendReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> { + let schedule_url: String = BaseSpiClient::module_url(InvokeModuleKind::Reach, funs).await?; + let headers = BaseSpiClient::headers(None, funs, ctx).await?; + funs.web_client().put_obj_to_str(&format!("{schedule_url}/ci/message/send"), &req, headers.clone()).await?; + Ok(()) + } +} diff --git a/frontend/sdks/invoke/src/invoke_config.rs b/frontend/sdks/invoke/src/invoke_config.rs index 0019e135b..5f0cf39c4 100644 --- a/frontend/sdks/invoke/src/invoke_config.rs +++ b/frontend/sdks/invoke/src/invoke_config.rs @@ -31,6 +31,7 @@ impl Default for InvokeConfig { (InvokeModuleKind::Iam.to_string(), "http://127.0.0.1:8080/iam".to_string()), (InvokeModuleKind::Stats.to_string(), "http://127.0.0.1:8080/spi-stats".to_string()), (InvokeModuleKind::Event.to_string(), "http://127.0.0.1:8080/event".to_string()), + (InvokeModuleKind::Reach.to_string(), "http://127.0.0.1:8080/reach".to_string()), ]), module_configs: HashMap::from([ (InvokeModuleKind::Kv.to_string(), InvokeModuleConfig { in_event: false }), diff --git a/frontend/sdks/invoke/src/invoke_enumeration.rs b/frontend/sdks/invoke/src/invoke_enumeration.rs index 5d62b066f..c56f69a01 100644 --- a/frontend/sdks/invoke/src/invoke_enumeration.rs +++ b/frontend/sdks/invoke/src/invoke_enumeration.rs @@ -26,6 +26,8 @@ pub enum InvokeModuleKind { Iam, #[oai(rename = "event")] Event, + #[oai(rename = "reach")] + Reach, } impl std::fmt::Display for InvokeModuleKind { @@ -42,6 +44,7 @@ impl std::fmt::Display for InvokeModuleKind { InvokeModuleKind::Schedule => write!(f, "schedule"), InvokeModuleKind::Iam => write!(f, "iam"), InvokeModuleKind::Event => write!(f, "event"), + InvokeModuleKind::Reach => write!(f, "reach"), } } } From 57086795d3a9005e9b258fd8412a748a93571669 Mon Sep 17 00:00:00 2001 From: 4t145 Date: Thu, 9 Jan 2025 16:12:34 +0800 Subject: [PATCH 35/35] fix document link --- .../gateways/spacegate-plugins/src/extension/notification.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/gateways/spacegate-plugins/src/extension/notification.rs b/backend/gateways/spacegate-plugins/src/extension/notification.rs index 7b3779443..50a978311 100644 --- a/backend/gateways/spacegate-plugins/src/extension/notification.rs +++ b/backend/gateways/spacegate-plugins/src/extension/notification.rs @@ -5,7 +5,7 @@ use tardis::{basic::dto::TardisContext, log as tracing, tokio}; /// Context to call notification api /// -/// Extract it from request extensions, and call [`NotificationContext::notify`] to send notification +/// Extract it from request extensions, and call [`NotificationContext::report`] to send notification #[derive(Debug, Clone)] pub struct NotificationContext { pub(crate) reach_api: Arc, @@ -31,7 +31,7 @@ impl NotificationContext { own_paths: self.spi_app_id.to_string(), ..Default::default() }; - let cool_down = (self.dedup_cache_cool_down.as_secs() as u64).min(1); + let cool_down = self.dedup_cache_cool_down.as_secs().min(1); tokio::spawn(async move { let key = format!("sg:plugin:{}:{}", NotifyPlugin::CODE, dedup_hash); let mut conn = cache_client.get_conn().await;