bios_basic/rbum/serv/
rbum_crud_serv.rs

1use std::collections::HashMap;
2
3use async_trait::async_trait;
4use itertools::Itertools;
5use lazy_static::lazy_static;
6use serde::{Deserialize, Serialize};
7use tardis::basic::dto::TardisContext;
8use tardis::basic::result::TardisResult;
9use tardis::db::reldb_client::{IdResp, TardisActiveModel};
10use tardis::db::sea_orm::sea_query::{Alias, Cond, Expr, Func, IntoValueTuple, JoinType, Order, Query, SelectStatement, Value, ValueTuple};
11use tardis::db::sea_orm::{self, Condition, EntityTrait, FromQueryResult, QueryFilter};
12use tardis::regex::Regex;
13
14use tardis::web::poem_openapi;
15use tardis::web::poem_openapi::types::{ParseFromJSON, ToJSON};
16use tardis::web::web_resp::TardisPage;
17use tardis::TardisFunsInst;
18
19use crate::rbum::domain::rbum_item;
20use crate::rbum::dto::rbum_filer_dto::RbumBasicFilterReq;
21use crate::rbum::helper::{rbum_event_helper, rbum_scope_helper};
22#[cfg(feature = "with-mq")]
23use crate::rbum::rbum_config::RbumConfigApi;
24
25pub const ID_FIELD_NAME: &str = "id";
26
27lazy_static! {
28    pub static ref OWNER_TABLE: Alias = Alias::new("t_owner");
29    pub static ref ID_FIELD: Alias = Alias::new(ID_FIELD_NAME);
30    pub static ref OWNER_FIELD: Alias = Alias::new("owner");
31    pub static ref OWN_PATHS_FIELD: Alias = Alias::new("own_paths");
32    pub static ref CREATE_TIME_FIELD: Alias = Alias::new("create_time");
33    pub static ref UPDATE_TIME_FIELD: Alias = Alias::new("update_time");
34    pub static ref CODE_FIELD: Alias = Alias::new("code");
35    pub static ref NAME_FIELD: Alias = Alias::new("name");
36    pub static ref SORT_FIELD: Alias = Alias::new("sort");
37    pub static ref SCOPE_LEVEL_FIELD: Alias = Alias::new("scope_level");
38    pub static ref REL_KIND_ID_FIELD: Alias = Alias::new("rel_rbum_kind_id");
39    pub static ref REL_DOMAIN_ID_FIELD: Alias = Alias::new("rel_rbum_domain_id");
40    pub static ref DISABLED_FIELD: Alias = Alias::new("disabled");
41    pub static ref R_URL_PART_CODE: Regex = Regex::new(r"^[a-z0-9-.]+$").expect("Regular parsing error");
42}
43
44/// Resource CURD operation
45///
46/// 资源CURD操作
47#[async_trait]
48pub trait RbumCrudOperation<E, AddReq, ModifyReq, SummaryResp, DetailResp, FilterReq>
49where
50    E: TardisActiveModel + Sync + Send,
51    AddReq: Sync + Send,
52    ModifyReq: Sync + Send,
53    SummaryResp: FromQueryResult + ParseFromJSON + ToJSON + Serialize + Send + Sync,
54    DetailResp: FromQueryResult + ParseFromJSON + ToJSON + Serialize + Send + Sync,
55    FilterReq: Sync + Send,
56{
57    /// Get the name of the table
58    ///
59    /// 获取表的名称
60    fn get_table_name() -> &'static str;
61
62    /// Get the name of the object
63    ///
64    /// 获取对象的名称
65    ///
66    /// Mostly used for printing log identifiers.
67    ///
68    /// 多用于打印日志的标识。
69    fn get_obj_name() -> String {
70        Self::get_obj_name_from(Self::get_table_name())
71    }
72
73    /// Get the object name from the custom table name
74    ///
75    /// 从自定义表名中获取对象名称
76    fn get_obj_name_from(table_name: &str) -> String {
77        table_name.replace("rbum_", "")
78    }
79
80    // ----------------------------- Ownership -------------------------------
81
82    /// Check the ownership of the specified resource id
83    ///
84    /// 检查指定资源id的所有权
85    ///
86    /// When the resource ownership path is equal to the current context ownership path or its descendant,
87    /// return ``Ok``, otherwise return ``Err``.
88    ///
89    /// 当资源所有权路径等于当前上下文所有权路径或是其子孙级时返回 ``Ok`` ,反之返回 ``Err`` 。
90    async fn check_ownership(id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
91        Self::check_ownership_with_table_name(id, Self::get_table_name(), funs, ctx).await
92    }
93
94    /// Check the ownership of the specified resource id and table name
95    ///
96    /// 检查指定资源id和表名的所有权
97    ///
98    /// When the resource ownership path is equal to the current context ownership path or its descendant,
99    /// return ``Ok``, otherwise return ``Err``.
100    ///
101    /// 当资源所有权路径等于当前上下文所有权路径或是其子孙级时返回 ``Ok`` ,反之返回 ``Err`` 。
102    async fn check_ownership_with_table_name(id: &str, table_name: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
103        if funs.db().count(&Self::package_ownership_query_with_table_name(id, table_name, ctx)).await? == 0 {
104            return Err(funs.err().not_found(
105                &Self::get_obj_name_from(table_name),
106                "check",
107                &format!("ownership {}.{} is illegal by {}", Self::get_obj_name_from(table_name), id, ctx.owner),
108                "404-rbum-*-ownership-illegal",
109            ));
110        }
111        Ok(())
112    }
113
114    /// Package the ownership query statement of the specified resource id
115    ///
116    /// 组装指定资源id的所有权的查询语句
117    ///
118    /// When the resource ownership path is equal to the current context ownership path or its descendant,
119    /// return a record, otherwise return an empty record.
120    ///
121    /// 当资源所有权路径等于当前上下文所有权路径或是其子孙级时查询到一条记录 ,反之查询为空。
122    fn package_ownership_query(id: &str, ctx: &TardisContext) -> SelectStatement {
123        Self::package_ownership_query_with_table_name(id, Self::get_table_name(), ctx)
124    }
125
126    /// Package the ownership query statement of the specified resource id and table name
127    ///
128    /// 组装指定资源id和表名的所有权的查询语句
129    ///
130    /// When the resource ownership path is equal to the current context ownership path or its descendant,
131    /// return a record, otherwise return an empty record.
132    fn package_ownership_query_with_table_name(id: &str, table_name: &str, ctx: &TardisContext) -> SelectStatement {
133        let mut query = Query::select();
134        query
135            .column(ID_FIELD.clone())
136            .from(Alias::new(table_name))
137            .and_where(Expr::col(ID_FIELD.clone()).eq(id))
138            .and_where(Expr::col(OWN_PATHS_FIELD.clone()).like(format!("{}%", ctx.own_paths).as_str()));
139        query
140    }
141
142    // ----------------------------- Scope -------------------------------
143
144    /// Check the scope of the specified resource id and table name
145    ///
146    /// 检查指定资源id和表名的作用域
147    async fn check_scope(id: &str, table_name: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
148        if funs
149            .db()
150            .count(
151                Query::select()
152                    .column((Alias::new(table_name), ID_FIELD.clone()))
153                    .from(Alias::new(table_name))
154                    .and_where(Expr::col((Alias::new(table_name), ID_FIELD.clone())).eq(id))
155                    .with_scope(table_name, &ctx.own_paths, false),
156            )
157            .await?
158            == 0
159        {
160            return Err(funs.err().not_found(
161                &Self::get_obj_name_from(table_name),
162                "check",
163                &format!("scope {}.{} is illegal by {}", Self::get_obj_name_from(table_name), id, ctx.owner),
164                "404-rbum-*-scope-illegal",
165            ));
166        }
167        Ok(())
168    }
169
170    /// Check the scope of the specified field set and table name
171    ///
172    /// 检查指定字段集合和表名的作用域
173    ///
174    /// When the number of records queried is equal to the expected number of records, return ``Ok``, otherwise return ``Err``.
175    ///
176    /// 当查询的记录数等于期望的记录数时返回 ``Ok`` ,反之返回 ``Err`` 。
177    ///
178    /// # Parameters
179    /// - `values` - The field set to be checked. key = field name, value = The set of values ​​used for ``in`` queries
180    /// - `expect_number` - expected number of eligible records
181    /// - `table_name` - The table name to be checked
182    async fn check_scopes(values: HashMap<String, &Vec<String>>, expect_number: u64, table_name: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
183        let mut query = Query::select();
184        let msg = values.iter().map(|(k, v)| format!("{k}={v:?}")).join(",");
185        query.column((Alias::new(table_name), ID_FIELD.clone())).from(Alias::new(table_name)).with_scope(table_name, &ctx.own_paths, false);
186        for (k, v) in values {
187            query.and_where(Expr::col((Alias::new(table_name), Alias::new(&k))).is_in(v.clone()));
188        }
189        if funs.db().count(&query).await? != expect_number {
190            return Err(funs.err().not_found(
191                &Self::get_obj_name_from(table_name),
192                "check",
193                &format!("scopes {}.{} is illegal by {}", Self::get_obj_name_from(table_name), msg, ctx.owner),
194                "404-rbum-*-scope-illegal",
195            ));
196        }
197        Ok(())
198    }
199
200    // ----------------------------- Exist -------------------------------
201
202    /// Check whether there is an association before deleting the resource with the specified id and table name.
203    ///
204    ///
205    /// 删除指定id和表名的资源前检查是否存在关联
206    async fn check_exist_before_delete(id: &str, rel_table_name: &str, rel_field_name: &str, funs: &TardisFunsInst) -> TardisResult<()> {
207        if funs
208            .db()
209            .count(
210                Query::select()
211                    .column((Alias::new(rel_table_name), ID_FIELD.clone()))
212                    .from(Alias::new(rel_table_name))
213                    .and_where(Expr::col((Alias::new(rel_table_name), Alias::new(rel_field_name))).eq(id)),
214            )
215            .await?
216            > 0
217        {
218            return Err(funs.err().conflict(
219                &Self::get_obj_name(),
220                "delete",
221                &format!(
222                    "can not delete {}.{} when there are associated by {}.{}",
223                    Self::get_obj_name(),
224                    id,
225                    Self::get_obj_name_from(rel_table_name),
226                    rel_field_name
227                ),
228                "409-rbum-*-delete-conflict",
229            ));
230        }
231        Ok(())
232    }
233
234    /// Check whether there is an association before deleting the resource with the specified condition and table name.
235    ///
236    /// 删除指定条件和表名的资源前检查是否存在关联
237    async fn check_exist_with_cond_before_delete(rel_table_name: &str, condition: Condition, funs: &TardisFunsInst) -> TardisResult<()> {
238        if funs.db().count(Query::select().column((Alias::new(rel_table_name), ID_FIELD.clone())).from(Alias::new(rel_table_name)).cond_where(condition)).await? > 0 {
239            return Err(funs.err().conflict(
240                &Self::get_obj_name(),
241                "delete",
242                &format!(
243                    "can not delete {} when there are associated by {}",
244                    Self::get_obj_name(),
245                    Self::get_obj_name_from(rel_table_name)
246                ),
247                "409-rbum-*-delete-conflict",
248            ));
249        }
250        Ok(())
251    }
252
253    // ----------------------------- Add -------------------------------
254
255    /// Pre-processing of the add request
256    ///
257    /// 添加请求的前置处理
258    async fn before_add_rbum(_: &mut AddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<()> {
259        Ok(())
260    }
261
262    /// Package add request
263    ///
264    /// 组装资源的添加请求
265    ///
266    /// # Examples:
267    ///
268    /// ```
269    /// async fn package_add(add_req: &IamConfigAddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<iam_config::ActiveModel> {
270    ///     Ok(iam_config::ActiveModel {
271    ///         id: Set(TardisFuns::field.nanoid()),
272    ///         code: Set(add_req.code.to_string()),
273    ///         name: Set(add_req.name.as_ref().unwrap_or(&"".to_string()).to_string()),
274    ///         note: Set(add_req.note.as_ref().unwrap_or(&"".to_string()).to_string()),
275    ///         value1: Set(add_req.value1.as_ref().unwrap_or(&"".to_string()).to_string()),
276    ///         value2: Set(add_req.value2.as_ref().unwrap_or(&"".to_string()).to_string()),
277    ///         ext: Set(add_req.ext.as_ref().unwrap_or(&"".to_string()).to_string()),
278    ///         rel_item_id: Set(add_req.rel_item_id.to_string()),
279    ///         disabled: Set(add_req.disabled.unwrap_or(false)),
280    ///         data_type: Set(add_req.data_type.to_string()),
281    ///         ..Default::default()
282    ///     })
283    /// }
284    /// ```
285    async fn package_add(add_req: &AddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<E>;
286
287    /// Post-processing of the add request
288    ///
289    /// 添加请求的后置处理
290    async fn after_add_rbum(_: &str, _: &AddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<()> {
291        Ok(())
292    }
293
294    /// Add resource
295    ///
296    /// 添加资源
297    async fn add_rbum(add_req: &mut AddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
298        Self::before_add_rbum(add_req, funs, ctx).await?;
299        let domain = Self::package_add(add_req, funs, ctx).await?;
300        let insert_result = funs.db().insert_one(domain, ctx).await?;
301        let id_value = insert_result.last_insert_id.into_value_tuple();
302        let id = match id_value {
303            ValueTuple::One(v) => {
304                if let Value::String(s) = v {
305                    s.map(|id| id.to_string())
306                } else {
307                    None
308                }
309            }
310            _ => None,
311        };
312        if let Some(id) = id {
313            Self::after_add_rbum(&id, add_req, funs, ctx).await?;
314            rbum_event_helper::add_notify_event(Self::get_table_name(), "c", id.as_str(), ctx).await?;
315            // rbum_event_helper::try_notify(Self::get_table_name(), "c", &id, funs, ctx).await?;
316            Ok(id.to_string())
317        } else {
318            return Err(funs.err().internal_error(
319                &Self::get_obj_name(),
320                "add",
321                "id data type is invalid, currently only the string is supported",
322                "500-rbum-crud-id-type",
323            ));
324        }
325    }
326
327    // ----------------------------- Modify -------------------------------
328
329    ///  Pre-processing of the modify request
330    ///
331    /// 修改请求的前置处理
332    async fn before_modify_rbum(id: &str, _: &mut ModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
333        Self::check_ownership(id, funs, ctx).await
334    }
335
336    /// Package modify request
337    ///
338    /// 组装资源的修改请求
339    ///
340    /// # Examples:
341    ///
342    /// ```
343    /// async fn package_modify(id: &str, modify_req: &IamConfigModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<iam_config::ActiveModel> {
344    ///     let mut iam_config = iam_config::ActiveModel {
345    ///         id: Set(id.to_string()),
346    ///         ..Default::default()
347    ///     };
348    ///     if let Some(name) = &modify_req.name {
349    ///         iam_config.name = Set(name.to_string());
350    ///     }
351    ///     if let Some(data_type) = &modify_req.data_type {
352    ///         iam_config.data_type = Set(data_type.to_string());
353    ///     }
354    ///     if let Some(note) = &modify_req.note {
355    ///         iam_config.note = Set(note.to_string());
356    ///     }
357    ///     if let Some(value1) = &modify_req.value1 {
358    ///         iam_config.value1 = Set(value1.to_string());
359    ///     }
360    ///     if let Some(value2) = &modify_req.value2 {
361    ///         iam_config.value2 = Set(value2.to_string());
362    ///     }
363    ///     if let Some(ext) = &modify_req.ext {
364    ///         iam_config.ext = Set(ext.to_string());
365    ///     }
366    ///     if let Some(disabled) = &modify_req.disabled {
367    ///         iam_config.disabled = Set(*disabled);
368    ///     }
369    ///     Ok(iam_config)
370    /// }
371    /// ```
372    async fn package_modify(id: &str, modify_req: &ModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<E>;
373
374    /// Post-processing of the modify request
375    ///
376    /// 修改请求的后置处理
377    async fn after_modify_rbum(_: &str, _: &mut ModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<()> {
378        Ok(())
379    }
380
381    /// Modify resource
382    ///
383    /// 修改资源
384    async fn modify_rbum(id: &str, modify_req: &mut ModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
385        Self::before_modify_rbum(id, modify_req, funs, ctx).await?;
386        let domain = Self::package_modify(id, modify_req, funs, ctx).await?;
387        funs.db().update_one(domain, ctx).await?;
388        Self::after_modify_rbum(id, modify_req, funs, ctx).await?;
389        rbum_event_helper::add_notify_event(Self::get_table_name(), "u", id, ctx).await?;
390        Ok(())
391    }
392
393    // ----------------------------- Delete -------------------------------
394
395    /// Pre-processing of the delete request
396    ///
397    /// 删除请求的前置处理
398    async fn before_delete_rbum(id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<DetailResp>> {
399        Self::check_ownership(id, funs, ctx).await?;
400        Ok(None)
401    }
402
403    /// Post-processing of the delete request
404    ///
405    /// 删除请求的后置处理
406    async fn after_delete_rbum(_: &str, _: &Option<DetailResp>, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<()> {
407        Ok(())
408    }
409
410    /// Delete resource
411    ///
412    /// 删除资源
413    async fn delete_rbum(id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<u64> {
414        let deleted_rbum = Self::before_delete_rbum(id, funs, ctx).await?;
415        let res_select_req = <E::Entity as EntityTrait>::find().filter(Expr::col(ID_FIELD.clone()).eq(id));
416        #[cfg(feature = "with-mq")]
417        {
418            let delete_records = funs.db().soft_delete_custom(res_select_req, "id").await?;
419            let mq_topic_entity_deleted = &funs.rbum_conf_mq_topic_entity_deleted();
420            let mq_header = std::collections::HashMap::from([(funs.rbum_conf_mq_header_name_operator(), ctx.owner.clone())]);
421            for delete_record in &delete_records {
422                funs.mq().publish(mq_topic_entity_deleted, tardis::TardisFuns::json.obj_to_string(delete_record)?, &mq_header).await?;
423            }
424            Self::after_delete_rbum(id, &deleted_rbum, funs, ctx).await?;
425            rbum_event_helper::add_notify_event(Self::get_table_name(), "d", id, ctx).await?;
426            Ok(delete_records.len() as u64)
427        }
428        #[cfg(not(feature = "with-mq"))]
429        {
430            let delete_records = funs.db().soft_delete(res_select_req, &ctx.owner).await?;
431            Self::after_delete_rbum(id, &deleted_rbum, funs, ctx).await?;
432            rbum_event_helper::add_notify_event(Self::get_table_name(), "d", id, ctx).await?;
433            Ok(delete_records)
434        }
435    }
436
437    // ----------------------------- Query -------------------------------
438
439    /// Package query request
440    ///
441    /// 组装资源的查询请求
442    ///
443    /// # Examples:
444    ///
445    /// ```
446    /// async fn package_query(is_detail: bool, filter: &IamConfigFilterReq, _: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<SelectStatement> {
447    ///     let mut query = Query::select();
448    ///     query
449    ///         .columns(vec![
450    ///             (iam_config::Entity, iam_config::Column::Id),
451    ///             (iam_config::Entity, iam_config::Column::Code),
452    ///             (iam_config::Entity, iam_config::Column::Name),
453    ///             (iam_config::Entity, iam_config::Column::Note),
454    ///             (iam_config::Entity, iam_config::Column::Value1),
455    ///             (iam_config::Entity, iam_config::Column::Value2),
456    ///             (iam_config::Entity, iam_config::Column::Ext),
457    ///             (iam_config::Entity, iam_config::Column::Disabled),
458    ///             (iam_config::Entity, iam_config::Column::DataType),
459    ///             (iam_config::Entity, iam_config::Column::RelItemId),
460    ///             (iam_config::Entity, iam_config::Column::OwnPaths),
461    ///             (iam_config::Entity, iam_config::Column::Owner),
462    ///             (iam_config::Entity, iam_config::Column::CreateTime),
463    ///             (iam_config::Entity, iam_config::Column::UpdateTime),
464    ///         ])
465    ///         .from(iam_config::Entity);
466    ///     if let Some(code) = &filter.code {
467    ///         query.and_where(Expr::col(iam_config::Column::Code).eq(code));
468    ///     }
469    ///     if let Some(rel_item_id) = &filter.rel_item_id {
470    ///         query.and_where(Expr::col(iam_config::Column::RelItemId).eq(rel_item_id));
471    ///     }
472    ///     if let Some(disabled) = &filter.disabled {
473    ///         query.and_where(Expr::col(iam_config::Column::Disabled).eq(*disabled));
474    ///     }
475    ///     query.with_filter(Self::get_table_name(), &filter.basic, is_detail, false, ctx);
476    ///     Ok(query)
477    /// }
478    /// ```
479    async fn package_query(is_detail: bool, filter: &FilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<SelectStatement>;
480
481    /// Query and get a resource summary
482    ///
483    /// 查询并获取一条资源概要信息
484    async fn peek_rbum(id: &str, filter: &FilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<SummaryResp> {
485        Self::do_peek_rbum(id, filter, funs, ctx).await
486    }
487
488    /// Query and get a resource summary
489    ///
490    /// 查询并获取一条资源概要信息
491    ///
492    /// NOTE: Internal method, not recommended to override.
493    ///
494    /// NOTE: 内部方法,不建议重写。
495    async fn do_peek_rbum(id: &str, filter: &FilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<SummaryResp> {
496        let mut query = Self::package_query(false, filter, funs, ctx).await?;
497        query.and_where(Expr::col((Alias::new(Self::get_table_name()), ID_FIELD.clone())).eq(id));
498        let query = funs.db().get_dto(&query).await?;
499        match query {
500            Some(resp) => Ok(resp),
501            None => Err(funs.err().not_found(
502                &Self::get_obj_name(),
503                "peek",
504                &format!("not found {}.{} by {}", Self::get_obj_name(), id, ctx.owner),
505                "404-rbum-*-obj-not-exist",
506            )),
507        }
508    }
509
510    /// Query and get a resource detail
511    ///
512    /// 查询并获取一条资源详细信息
513    async fn get_rbum(id: &str, filter: &FilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<DetailResp> {
514        Self::do_get_rbum(id, filter, funs, ctx).await
515    }
516
517    /// Query and get a resource detail
518    ///
519    /// 查询并获取一条资源详细信息
520    ///
521    /// NOTE: Internal method, not recommended to override.
522    ///
523    /// NOTE: 内部方法,不建议重写。
524    async fn do_get_rbum(id: &str, filter: &FilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<DetailResp> {
525        let mut query = Self::package_query(true, filter, funs, ctx).await?;
526        query.and_where(Expr::col((Alias::new(Self::get_table_name()), ID_FIELD.clone())).eq(id));
527        let query = funs.db().get_dto(&query).await?;
528        match query {
529            Some(resp) => Ok(resp),
530            None => Err(funs.err().not_found(
531                &Self::get_obj_name(),
532                "get",
533                &format!("not found {}.{} by {}", Self::get_obj_name(), id, ctx.owner),
534                "404-rbum-*-obj-not-exist",
535            )),
536        }
537    }
538
539    /// Query and page to get the resource id set
540    ///
541    /// 查询并分页获取资源id集合
542    async fn paginate_id_rbums(
543        filter: &FilterReq,
544        page_number: u32,
545        page_size: u32,
546        desc_sort_by_create: Option<bool>,
547        desc_sort_by_update: Option<bool>,
548        funs: &TardisFunsInst,
549        ctx: &TardisContext,
550    ) -> TardisResult<TardisPage<String>> {
551        Self::do_paginate_id_rbums(filter, page_number, page_size, desc_sort_by_create, desc_sort_by_update, funs, ctx).await
552    }
553
554    /// Query and page to get the resource id set
555    ///
556    /// 查询并分页获取资源id集合
557    ///
558    /// NOTE: Internal method, not recommended to override.
559    ///
560    /// NOTE: 内部方法,不建议重写。
561    async fn do_paginate_id_rbums(
562        filter: &FilterReq,
563        page_number: u32,
564        page_size: u32,
565        desc_sort_by_create: Option<bool>,
566        desc_sort_by_update: Option<bool>,
567        funs: &TardisFunsInst,
568        ctx: &TardisContext,
569    ) -> TardisResult<TardisPage<String>> {
570        let mut query = Self::package_query(false, filter, funs, ctx).await?;
571        query.clear_selects();
572        query.column((Alias::new(Self::get_table_name()), ID_FIELD.clone()));
573        if let Some(sort) = desc_sort_by_create {
574            query.order_by((Alias::new(Self::get_table_name()), CREATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
575        }
576        if let Some(sort) = desc_sort_by_update {
577            query.order_by((Alias::new(Self::get_table_name()), UPDATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
578        }
579        let (records, total_size) = funs.db().paginate_dtos::<IdResp>(&query, page_number as u64, page_size as u64).await?;
580        Ok(TardisPage {
581            page_size: page_size as u64,
582            page_number: page_number as u64,
583            total_size,
584            records: records.into_iter().map(|resp| resp.id).collect(),
585        })
586    }
587
588    /// Query and page to get the resource id and name set
589    ///
590    /// 查询并分页获取资源id和名称集合
591    async fn paginate_id_name_rbums(
592        filter: &FilterReq,
593        page_number: u32,
594        page_size: u32,
595        desc_sort_by_create: Option<bool>,
596        desc_sort_by_update: Option<bool>,
597        funs: &TardisFunsInst,
598        ctx: &TardisContext,
599    ) -> TardisResult<TardisPage<IdNameResp>> {
600        Self::do_paginate_id_name_rbums(filter, page_number, page_size, desc_sort_by_create, desc_sort_by_update, funs, ctx).await
601    }
602
603    /// Query and page to get the resource id and name set
604    ///
605    /// 查询并分页获取资源id和名称集合
606    ///
607    /// NOTE: Internal method, not recommended to override.
608    ///
609    /// NOTE: 内部方法,不建议重写。
610    async fn do_paginate_id_name_rbums(
611        filter: &FilterReq,
612        page_number: u32,
613        page_size: u32,
614        desc_sort_by_create: Option<bool>,
615        desc_sort_by_update: Option<bool>,
616        funs: &TardisFunsInst,
617        ctx: &TardisContext,
618    ) -> TardisResult<TardisPage<IdNameResp>> {
619        let mut query = Self::package_query(false, filter, funs, ctx).await?;
620        query.clear_selects();
621        query.columns([
622            (Alias::new(Self::get_table_name()), ID_FIELD.clone()),
623            (Alias::new(Self::get_table_name()), NAME_FIELD.clone()),
624        ]);
625        if let Some(sort) = desc_sort_by_create {
626            query.order_by((Alias::new(Self::get_table_name()), CREATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
627        }
628        if let Some(sort) = desc_sort_by_update {
629            query.order_by((Alias::new(Self::get_table_name()), UPDATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
630        }
631        let (records, total_size) = funs.db().paginate_dtos::<IdNameResp>(&query, page_number as u64, page_size as u64).await?;
632        Ok(TardisPage {
633            page_size: page_size as u64,
634            page_number: page_number as u64,
635            total_size,
636            records,
637        })
638    }
639
640    /// Query and page to get the resource summary set
641    ///
642    /// 查询并分页获取资源概要信息集合
643    async fn paginate_rbums(
644        filter: &FilterReq,
645        page_number: u32,
646        page_size: u32,
647        desc_sort_by_create: Option<bool>,
648        desc_sort_by_update: Option<bool>,
649        funs: &TardisFunsInst,
650        ctx: &TardisContext,
651    ) -> TardisResult<TardisPage<SummaryResp>> {
652        Self::do_paginate_rbums(filter, page_number, page_size, desc_sort_by_create, desc_sort_by_update, funs, ctx).await
653    }
654
655    /// Query and page to get the resource summary set
656    ///
657    /// 查询并分页获取资源概要信息集合
658    ///
659    /// NOTE: Internal method, not recommended to override.
660    ///
661    /// NOTE: 内部方法,不建议重写。
662    async fn do_paginate_rbums(
663        filter: &FilterReq,
664        page_number: u32,
665        page_size: u32,
666        desc_sort_by_create: Option<bool>,
667        desc_sort_by_update: Option<bool>,
668        funs: &TardisFunsInst,
669        ctx: &TardisContext,
670    ) -> TardisResult<TardisPage<SummaryResp>> {
671        let mut query = Self::package_query(false, filter, funs, ctx).await?;
672        if let Some(sort) = desc_sort_by_create {
673            query.order_by((Alias::new(Self::get_table_name()), CREATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
674        }
675        if let Some(sort) = desc_sort_by_update {
676            query.order_by((Alias::new(Self::get_table_name()), UPDATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
677        }
678        let (records, total_size) = funs.db().paginate_dtos(&query, page_number as u64, page_size as u64).await?;
679        Ok(TardisPage {
680            page_size: page_size as u64,
681            page_number: page_number as u64,
682            total_size,
683            records,
684        })
685    }
686
687    /// Query and page to get the resource detail set
688    ///
689    /// 查询并分页获取资源详细信息集合
690    async fn paginate_detail_rbums(
691        filter: &FilterReq,
692        page_number: u32,
693        page_size: u32,
694        desc_sort_by_create: Option<bool>,
695        desc_sort_by_update: Option<bool>,
696        funs: &TardisFunsInst,
697        ctx: &TardisContext,
698    ) -> TardisResult<TardisPage<DetailResp>> {
699        Self::do_paginate_detail_rbums(filter, page_number, page_size, desc_sort_by_create, desc_sort_by_update, funs, ctx).await
700    }
701
702    /// Query and page to get the resource detail set
703    ///
704    /// 查询并分页获取资源详细信息集合
705    ///
706    /// NOTE: Internal method, not recommended to override.
707    ///
708    /// NOTE: 内部方法,不建议重写。
709    async fn do_paginate_detail_rbums(
710        filter: &FilterReq,
711        page_number: u32,
712        page_size: u32,
713        desc_sort_by_create: Option<bool>,
714        desc_sort_by_update: Option<bool>,
715        funs: &TardisFunsInst,
716        ctx: &TardisContext,
717    ) -> TardisResult<TardisPage<DetailResp>> {
718        let mut query = Self::package_query(true, filter, funs, ctx).await?;
719        if let Some(sort) = desc_sort_by_create {
720            query.order_by((Alias::new(Self::get_table_name()), CREATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
721        }
722        if let Some(sort) = desc_sort_by_update {
723            query.order_by((Alias::new(Self::get_table_name()), UPDATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
724        }
725        let (records, total_size) = funs.db().paginate_dtos(&query, page_number as u64, page_size as u64).await?;
726        Ok(TardisPage {
727            page_size: page_size as u64,
728            page_number: page_number as u64,
729            total_size,
730            records,
731        })
732    }
733
734    /// Query and get a resource summary
735    ///
736    /// 查询并获取一条资源概要信息
737    async fn find_one_rbum(filter: &FilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<SummaryResp>> {
738        Self::do_find_one_rbum(filter, funs, ctx).await
739    }
740
741    /// Query and get a resource summary
742    ///
743    /// 查询并获取一条资源概要信息
744    ///
745    /// NOTE: Internal method, not recommended to override.
746    ///
747    /// NOTE: 内部方法,不建议重写。
748    async fn do_find_one_rbum(filter: &FilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<SummaryResp>> {
749        let result = Self::find_rbums(filter, None, None, funs, ctx).await?;
750        if result.len() > 1 {
751            Err(funs.err().conflict(&Self::get_obj_name(), "find_one", "found multiple records", "409-rbum-*-obj-multi-exist"))
752        } else {
753            Ok(result.into_iter().next())
754        }
755    }
756
757    /// Query and get the resource id set
758    ///
759    /// 查询并获取资源id集合
760    async fn find_id_rbums(
761        filter: &FilterReq,
762        desc_sort_by_create: Option<bool>,
763        desc_sort_by_update: Option<bool>,
764        funs: &TardisFunsInst,
765        ctx: &TardisContext,
766    ) -> TardisResult<Vec<String>> {
767        Self::do_find_id_rbums(filter, desc_sort_by_create, desc_sort_by_update, funs, ctx).await
768    }
769
770    /// Query and get the resource id set
771    ///
772    /// 查询并获取资源id集合
773    ///
774    /// NOTE: Internal method, not recommended to override.
775    ///
776    /// NOTE: 内部方法,不建议重写。
777    async fn do_find_id_rbums(
778        filter: &FilterReq,
779        desc_sort_by_create: Option<bool>,
780        desc_sort_by_update: Option<bool>,
781        funs: &TardisFunsInst,
782        ctx: &TardisContext,
783    ) -> TardisResult<Vec<String>> {
784        let mut query = Self::package_query(false, filter, funs, ctx).await?;
785        query.clear_selects();
786        query.column((Alias::new(Self::get_table_name()), ID_FIELD.clone()));
787        if let Some(sort) = desc_sort_by_create {
788            query.order_by((Alias::new(Self::get_table_name()), CREATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
789        }
790        if let Some(sort) = desc_sort_by_update {
791            query.order_by((Alias::new(Self::get_table_name()), UPDATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
792        }
793        Ok(funs.db().find_dtos::<IdResp>(&query).await?.into_iter().map(|resp| resp.id).collect())
794    }
795
796    /// Query and get the resource id and name set
797    ///
798    /// 查询并获取资源id和名称集合
799    async fn find_id_name_rbums(
800        filter: &FilterReq,
801        desc_sort_by_create: Option<bool>,
802        desc_sort_by_update: Option<bool>,
803        funs: &TardisFunsInst,
804        ctx: &TardisContext,
805    ) -> TardisResult<HashMap<String, String>> {
806        Self::do_find_id_name_rbums(filter, desc_sort_by_create, desc_sort_by_update, funs, ctx).await
807    }
808
809    /// Query and get the resource id and name set
810    ///
811    /// 查询并获取资源id和名称集合
812    ///
813    /// NOTE: Internal method, not recommended to override.
814    ///
815    /// NOTE: 内部方法,不建议重写。
816    async fn do_find_id_name_rbums(
817        filter: &FilterReq,
818        desc_sort_by_create: Option<bool>,
819        desc_sort_by_update: Option<bool>,
820        funs: &TardisFunsInst,
821        ctx: &TardisContext,
822    ) -> TardisResult<HashMap<String, String>> {
823        let mut query = Self::package_query(false, filter, funs, ctx).await?;
824        query.clear_selects();
825        query.columns([
826            (Alias::new(Self::get_table_name()), ID_FIELD.clone()),
827            (Alias::new(Self::get_table_name()), NAME_FIELD.clone()),
828        ]);
829        if let Some(sort) = desc_sort_by_create {
830            query.order_by((Alias::new(Self::get_table_name()), CREATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
831        }
832        if let Some(sort) = desc_sort_by_update {
833            query.order_by((Alias::new(Self::get_table_name()), UPDATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
834        }
835        Ok(funs.db().find_dtos::<IdNameResp>(&query).await?.into_iter().map(|resp| (resp.id, resp.name)).collect())
836    }
837
838    /// Query and get the resource summary set
839    ///
840    /// 查询并获取资源概要信息集合
841    async fn find_rbums(
842        filter: &FilterReq,
843        desc_sort_by_create: Option<bool>,
844        desc_sort_by_update: Option<bool>,
845        funs: &TardisFunsInst,
846        ctx: &TardisContext,
847    ) -> TardisResult<Vec<SummaryResp>> {
848        Self::do_find_rbums(filter, desc_sort_by_create, desc_sort_by_update, funs, ctx).await
849    }
850
851    /// Query and get the resource summary set
852    ///
853    /// 查询并获取资源概要信息集合
854    ///
855    /// NOTE: Internal method, not recommended to override.
856    ///
857    /// NOTE: 内部方法,不建议重写。
858    async fn do_find_rbums(
859        filter: &FilterReq,
860        desc_sort_by_create: Option<bool>,
861        desc_sort_by_update: Option<bool>,
862        funs: &TardisFunsInst,
863        ctx: &TardisContext,
864    ) -> TardisResult<Vec<SummaryResp>> {
865        let mut query = Self::package_query(false, filter, funs, ctx).await?;
866        if let Some(sort) = desc_sort_by_create {
867            query.order_by((Alias::new(Self::get_table_name()), CREATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
868        }
869        if let Some(sort) = desc_sort_by_update {
870            query.order_by((Alias::new(Self::get_table_name()), UPDATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
871        }
872        Ok(funs.db().find_dtos(&query).await?)
873    }
874
875    /// Query and get a resource detail
876    ///
877    /// 查询并获取一条资源详细信息
878    async fn find_one_detail_rbum(filter: &FilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<DetailResp>> {
879        Self::do_find_one_detail_rbum(filter, funs, ctx).await
880    }
881
882    /// Query and get a resource detail
883    ///
884    /// 查询并获取一条资源详细信息
885    ///
886    /// NOTE: Internal method, not recommended to override.
887    ///
888    /// NOTE: 内部方法,不建议重写。
889    async fn do_find_one_detail_rbum(filter: &FilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<DetailResp>> {
890        let result = Self::find_detail_rbums(filter, None, None, funs, ctx).await?;
891        if result.len() > 1 {
892            Err(funs.err().conflict(&Self::get_obj_name(), "find_one_detail", "found multiple records", "409-rbum-*-obj-multi-exist"))
893        } else {
894            Ok(result.into_iter().next())
895        }
896    }
897
898    /// Query and get the resource detail set
899    ///
900    /// 查询并获取资源详细信息集合
901    async fn find_detail_rbums(
902        filter: &FilterReq,
903        desc_sort_by_create: Option<bool>,
904        desc_sort_by_update: Option<bool>,
905        funs: &TardisFunsInst,
906        ctx: &TardisContext,
907    ) -> TardisResult<Vec<DetailResp>> {
908        Self::do_find_detail_rbums(filter, desc_sort_by_create, desc_sort_by_update, funs, ctx).await
909    }
910
911    /// Query and get the resource detail set
912    ///
913    /// 查询并获取资源详细信息集合
914    ///
915    /// NOTE: Internal method, not recommended to override.
916    ///
917    /// NOTE: 内部方法,不建议重写。
918    async fn do_find_detail_rbums(
919        filter: &FilterReq,
920        desc_sort_by_create: Option<bool>,
921        desc_sort_by_update: Option<bool>,
922        funs: &TardisFunsInst,
923        ctx: &TardisContext,
924    ) -> TardisResult<Vec<DetailResp>> {
925        let mut query = Self::package_query(true, filter, funs, ctx).await?;
926        if let Some(sort) = desc_sort_by_create {
927            query.order_by((Alias::new(Self::get_table_name()), CREATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
928        }
929        if let Some(sort) = desc_sort_by_update {
930            query.order_by((Alias::new(Self::get_table_name()), UPDATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
931        }
932        Ok(funs.db().find_dtos(&query).await?)
933    }
934
935    /// Query and count the number of resources
936    ///
937    /// 查询并统计资源数量
938    async fn count_rbums(filter: &FilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<u64> {
939        Self::do_count_rbums(filter, funs, ctx).await
940    }
941
942    /// Query and count the number of resources
943    ///
944    /// 查询并统计资源数量
945    ///
946    /// NOTE: Internal method, not recommended to override.
947    ///
948    /// NOTE: 内部方法,不建议重写。
949    async fn do_count_rbums(filter: &FilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<u64> {
950        let query = Self::package_query(false, filter, funs, ctx).await?;
951        funs.db().count(&query).await
952    }
953
954    /// Query and check whether the resource exists
955    ///
956    /// 查询并检查资源是否存在
957    async fn exist_rbum(filter: &FilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<bool> {
958        let query = Self::count_rbums(filter, funs, ctx).await?;
959        Ok(query > 0)
960    }
961}
962
963/// Package query request
964pub trait RbumCrudQueryPackage {
965    /// Add common filter request conditions
966    ///
967    /// 添加通用过滤请求条件
968    fn with_filter(&mut self, table_name: &str, filter: &RbumBasicFilterReq, ignore_owner: bool, has_scope: bool, ctx: &TardisContext) -> &mut Self;
969    /// Add scope filter conditions
970    ///
971    /// 添加作用域过滤条件
972    fn with_scope(&mut self, table_name: &str, filter_own_paths: &str, with_sub_own_paths: bool) -> &mut Self;
973}
974
975impl RbumCrudQueryPackage for SelectStatement {
976    fn with_filter(&mut self, table_name: &str, filter: &RbumBasicFilterReq, with_owner: bool, has_scope: bool, ctx: &TardisContext) -> &mut Self {
977        if filter.rel_ctx_owner {
978            self.and_where(Expr::col((Alias::new(table_name), OWNER_FIELD.clone())).eq(ctx.owner.as_str()));
979        }
980        if let Some(ids) = &filter.ids {
981            self.and_where(Expr::col((Alias::new(table_name), ID_FIELD.clone())).is_in(ids.clone()));
982        }
983
984        if let Some(scope_level) = &filter.scope_level {
985            self.and_where(Expr::col((Alias::new(table_name), SCOPE_LEVEL_FIELD.clone())).eq(scope_level.to_int()));
986        }
987        if let Some(enabled) = filter.enabled {
988            self.and_where(Expr::col((Alias::new(table_name), DISABLED_FIELD.clone())).eq(!enabled));
989        }
990
991        if let Some(name) = &filter.name {
992            self.and_where(Expr::col((Alias::new(table_name), NAME_FIELD.clone())).like(format!("%{name}%").as_str()));
993        }
994        if let Some(names) = &filter.names {
995            self.and_where(Expr::col((Alias::new(table_name), NAME_FIELD.clone())).is_in(names.clone()));
996        }
997        if let Some(code) = &filter.code {
998            self.and_where(Expr::col((Alias::new(table_name), CODE_FIELD.clone())).like(format!("{code}%").as_str()));
999        }
1000        if let Some(codes) = &filter.codes {
1001            self.and_where(Expr::col((Alias::new(table_name), CODE_FIELD.clone())).is_in(codes.clone()));
1002        }
1003
1004        if let Some(rbum_kind_id) = &filter.rbum_kind_id {
1005            self.and_where(Expr::col((Alias::new(table_name), REL_KIND_ID_FIELD.clone())).eq(rbum_kind_id.to_string()));
1006        }
1007        if let Some(rbum_domain_id) = &filter.rbum_domain_id {
1008            self.and_where(Expr::col((Alias::new(table_name), REL_DOMAIN_ID_FIELD.clone())).eq(rbum_domain_id.to_string()));
1009        }
1010        if with_owner {
1011            self.expr_as(Expr::col((OWNER_TABLE.clone(), NAME_FIELD.clone())), Alias::new("owner_name")).join_as(
1012                JoinType::LeftJoin,
1013                rbum_item::Entity,
1014                OWNER_TABLE.clone(),
1015                Expr::col((OWNER_TABLE.clone(), ID_FIELD.clone())).equals((Alias::new(table_name), OWNER_FIELD.clone())),
1016            );
1017        }
1018        let filter_own_paths = if let Some(own_paths) = &filter.own_paths { own_paths.as_str() } else { &ctx.own_paths };
1019        if has_scope && !filter.ignore_scope {
1020            self.with_scope(table_name, filter_own_paths, filter.with_sub_own_paths);
1021        } else if filter.with_sub_own_paths {
1022            self.and_where(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())).like(format!("{filter_own_paths}%").as_str()));
1023        } else {
1024            self.and_where(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())).eq(filter_own_paths));
1025        }
1026        self
1027    }
1028
1029    fn with_scope(&mut self, table_name: &str, filter_own_paths: &str, with_sub_own_paths: bool) -> &mut Self {
1030        let mut cond = Cond::any().add(Expr::col((Alias::new(table_name), SCOPE_LEVEL_FIELD.clone())).eq(0));
1031
1032        let own_cond = if with_sub_own_paths {
1033            Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())).like(format!("{filter_own_paths}%"))
1034        } else {
1035            Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())).eq(filter_own_paths)
1036        };
1037        cond = cond.add(own_cond);
1038
1039        if let Some(p1) = rbum_scope_helper::get_pre_paths(1, filter_own_paths) {
1040            cond = cond.add(
1041                Cond::all().add(Expr::col((Alias::new(table_name), SCOPE_LEVEL_FIELD.clone())).eq(1)).add(
1042                    Cond::any()
1043                        .add(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())).eq(""))
1044                        .add(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())).like(format!("{p1}%"))),
1045                ),
1046            );
1047            if let Some(p2) = rbum_scope_helper::get_pre_paths(2, filter_own_paths) {
1048                let node_len = (p2.len() - p1.len() - 1) as u8;
1049                cond = cond.add(
1050                    Cond::all().add(Expr::col((Alias::new(table_name), SCOPE_LEVEL_FIELD.clone())).eq(2)).add(
1051                        Cond::any()
1052                            .add(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())).eq(""))
1053                            .add(
1054                                Cond::all()
1055                                    .add(Expr::expr(Func::char_length(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())))).eq(node_len))
1056                                    .add(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())).like(format!("{p1}%"))),
1057                            )
1058                            .add(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())).like(format!("{p2}%"))),
1059                    ),
1060                );
1061                if let Some(p3) = rbum_scope_helper::get_pre_paths(3, filter_own_paths) {
1062                    cond = cond.add(
1063                        Cond::all().add(Expr::col((Alias::new(table_name), SCOPE_LEVEL_FIELD.clone())).eq(3)).add(
1064                            Cond::any()
1065                                .add(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())).eq(""))
1066                                .add(
1067                                    Cond::all()
1068                                        .add(Expr::expr(Func::char_length(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())))).eq(node_len))
1069                                        .add(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())).like(format!("{p1}%"))),
1070                                )
1071                                .add(
1072                                    Cond::all()
1073                                        .add(Expr::expr(Func::char_length(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())))).eq(node_len * 2 + 1))
1074                                        .add(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())).like(format!("{p2}%"))),
1075                                )
1076                                .add(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())).like(format!("{p3}%"))),
1077                        ),
1078                    );
1079                } else if with_sub_own_paths {
1080                    // System admin (own_paths = "") created Tenant admin (scope_level = 1 & own_paths = "") and App admin (scope_level = 2 & own_paths = "").
1081                    //
1082                    // A tenant admin needs to query the roles under that tenant and app, the corresponding condition should be (with_sub_own_paths = true):
1083                    //
1084                    // ```sql
1085                    // scope_level = 0
1086                    // OR own_paths LIKE '<tenant_id>%'
1087                    // OR (scope_level = 1 AND (own_paths = '' OR own_paths LIKE '<tenant_id>%'))
1088                    // OR (scope_level = 2 AND (own_paths = '' OR own_paths LIKE '<tenant_id>%'))
1089                    // ```
1090                    cond = cond.add(
1091                        Cond::all().add(Expr::col((Alias::new(table_name), SCOPE_LEVEL_FIELD.clone())).eq(3)).add(
1092                            Cond::any()
1093                                .add(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())).eq(""))
1094                                .add(
1095                                    Cond::all()
1096                                        .add(Expr::expr(Func::char_length(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())))).eq(node_len))
1097                                        .add(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())).like(format!("{p1}%"))),
1098                                )
1099                                .add(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())).like(format!("{p2}%"))),
1100                        ),
1101                    );
1102                }
1103            } else if with_sub_own_paths {
1104                cond = cond.add(
1105                    Cond::all().add(Expr::col((Alias::new(table_name), SCOPE_LEVEL_FIELD.clone())).eq(2)).add(
1106                        Cond::any()
1107                            .add(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())).eq(""))
1108                            .add(Expr::col((Alias::new(table_name), OWN_PATHS_FIELD.clone())).like(format!("{p1}%"))),
1109                    ),
1110                );
1111            }
1112        };
1113
1114        self.cond_where(Cond::all().add(cond));
1115        self
1116    }
1117}
1118
1119#[derive(Debug, sea_orm::FromQueryResult)]
1120pub struct NameResp {
1121    pub name: String,
1122}
1123
1124#[derive(Debug, Serialize, Deserialize, sea_orm::FromQueryResult, poem_openapi::Object)]
1125pub struct IdNameResp {
1126    pub id: String,
1127    pub name: String,
1128}