bios_basic/rbum/serv/
rbum_set_serv.rs

1use std::collections::{HashMap, HashSet};
2use std::time::Duration;
3
4use crate::rbum::domain::{rbum_cert, rbum_item, rbum_rel, rbum_set, rbum_set_cate, rbum_set_item};
5use crate::rbum::dto::rbum_filer_dto::{RbumBasicFilterReq, RbumKindFilterReq, RbumSetCateFilterReq, RbumSetFilterReq, RbumSetItemFilterReq, RbumSetTreeFilterReq};
6use crate::rbum::dto::rbum_set_cate_dto::{RbumSetCateAddReq, RbumSetCateDetailResp, RbumSetCateModifyReq, RbumSetCateSummaryResp};
7use crate::rbum::dto::rbum_set_dto::{
8    RbumSetAddReq, RbumSetDetailResp, RbumSetModifyReq, RbumSetPathResp, RbumSetSummaryResp, RbumSetTreeExtResp, RbumSetTreeNodeResp, RbumSetTreeResp,
9};
10use crate::rbum::dto::rbum_set_item_dto::{RbumSetItemAddReq, RbumSetItemDetailResp, RbumSetItemModifyReq, RbumSetItemRelInfoResp, RbumSetItemSummaryResp};
11use crate::rbum::rbum_config::RbumConfigApi;
12use crate::rbum::rbum_enumeration::{RbumCertRelKind, RbumRelFromKind, RbumScopeLevelKind, RbumSetCateLevelQueryKind};
13use crate::rbum::serv::rbum_cert_serv::RbumCertServ;
14use crate::rbum::serv::rbum_crud_serv::{RbumCrudOperation, RbumCrudQueryPackage};
15use crate::rbum::serv::rbum_domain_serv::RbumDomainServ;
16use crate::rbum::serv::rbum_item_serv::RbumItemServ;
17use crate::rbum::serv::rbum_kind_serv::RbumKindServ;
18use crate::rbum::serv::rbum_rel_serv::RbumRelServ;
19use async_recursion::async_recursion;
20use async_trait::async_trait;
21use itertools::Itertools;
22use tardis::basic::dto::TardisContext;
23use tardis::basic::result::TardisResult;
24use tardis::db::sea_orm::sea_query::*;
25use tardis::db::sea_orm::*;
26use tardis::db::sea_orm::{self, IdenStatic};
27use tardis::tokio::time::sleep;
28use tardis::{TardisFuns, TardisFunsInst};
29
30pub struct RbumSetServ;
31
32pub struct RbumSetCateServ;
33
34pub struct RbumSetItemServ;
35
36#[async_trait]
37impl RbumCrudOperation<rbum_set::ActiveModel, RbumSetAddReq, RbumSetModifyReq, RbumSetSummaryResp, RbumSetDetailResp, RbumSetFilterReq> for RbumSetServ {
38    fn get_table_name() -> &'static str {
39        rbum_set::Entity.table_name()
40    }
41
42    async fn package_add(add_req: &RbumSetAddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<rbum_set::ActiveModel> {
43        Ok(rbum_set::ActiveModel {
44            id: Set(TardisFuns::field.nanoid()),
45            code: Set(add_req.code.to_string()),
46            kind: Set(add_req.kind.to_string()),
47            name: Set(add_req.name.to_string()),
48            note: Set(add_req.note.as_ref().unwrap_or(&"".to_string()).to_string()),
49            icon: Set(add_req.icon.as_ref().unwrap_or(&"".to_string()).to_string()),
50            sort: Set(add_req.sort.unwrap_or(0)),
51            ext: Set(add_req.ext.as_ref().unwrap_or(&"".to_string()).to_string()),
52            scope_level: Set(add_req.scope_level.as_ref().unwrap_or(&RbumScopeLevelKind::Private).to_int()),
53            disabled: Set(add_req.disabled.unwrap_or(false)),
54            ..Default::default()
55        })
56    }
57
58    async fn package_modify(id: &str, modify_req: &RbumSetModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<rbum_set::ActiveModel> {
59        let mut rbum_set = rbum_set::ActiveModel {
60            id: Set(id.to_string()),
61            ..Default::default()
62        };
63        if let Some(name) = &modify_req.name {
64            rbum_set.name = Set(name.to_string());
65        }
66        if let Some(note) = &modify_req.note {
67            rbum_set.note = Set(note.to_string());
68        }
69        if let Some(icon) = &modify_req.icon {
70            rbum_set.icon = Set(icon.to_string());
71        }
72        if let Some(sort) = modify_req.sort {
73            rbum_set.sort = Set(sort);
74        }
75        if let Some(ext) = &modify_req.ext {
76            rbum_set.ext = Set(ext.to_string());
77        }
78        if let Some(scope_level) = &modify_req.scope_level {
79            rbum_set.scope_level = Set(scope_level.to_int());
80        }
81        if let Some(disabled) = modify_req.disabled {
82            rbum_set.disabled = Set(disabled);
83        }
84        Ok(rbum_set)
85    }
86
87    async fn before_delete_rbum(id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<RbumSetDetailResp>> {
88        Self::check_ownership(id, funs, ctx).await?;
89        // Cannot be deleted when there are categories (nodes), associated resource items, associated relationships, and associated resource credentials.
90        //
91        // 存在分类(节点)、关联资源项、关联关系、关联资源凭证时不能删除。
92        Self::check_exist_before_delete(id, RbumSetCateServ::get_table_name(), rbum_set_cate::Column::RelRbumSetId.as_str(), funs).await?;
93        Self::check_exist_before_delete(id, RbumSetItemServ::get_table_name(), rbum_set_item::Column::RelRbumSetId.as_str(), funs).await?;
94        Self::check_exist_with_cond_before_delete(
95            RbumRelServ::get_table_name(),
96            any![
97                all![
98                    Expr::col(rbum_rel::Column::FromRbumKind).eq(RbumRelFromKind::Set.to_int()),
99                    Expr::col(rbum_rel::Column::FromRbumId).eq(id)
100                ],
101                Expr::col(rbum_rel::Column::ToRbumItemId).eq(id)
102            ],
103            funs,
104        )
105        .await?;
106        Self::check_exist_with_cond_before_delete(
107            RbumCertServ::get_table_name(),
108            all![
109                Expr::col(rbum_cert::Column::RelRbumKind).eq(RbumCertRelKind::Set.to_int()),
110                Expr::col(rbum_cert::Column::RelRbumId).eq(id)
111            ],
112            funs,
113        )
114        .await?;
115        // 删除缓存
116        let result = Self::peek_rbum(
117            id,
118            &RbumSetFilterReq {
119                basic: RbumBasicFilterReq {
120                    with_sub_own_paths: true,
121                    ..Default::default()
122                },
123                ..Default::default()
124            },
125            funs,
126            ctx,
127        )
128        .await?;
129        let key = &format!("{}{}", funs.rbum_conf_cache_key_set_code_(), result.code);
130        funs.cache().del(key).await?;
131        Ok(None)
132    }
133
134    async fn package_query(is_detail: bool, filter: &RbumSetFilterReq, _: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<SelectStatement> {
135        let mut query = Query::select();
136        query
137            .columns(vec![
138                (rbum_set::Entity, rbum_set::Column::Id),
139                (rbum_set::Entity, rbum_set::Column::Code),
140                (rbum_set::Entity, rbum_set::Column::Kind),
141                (rbum_set::Entity, rbum_set::Column::Name),
142                (rbum_set::Entity, rbum_set::Column::Note),
143                (rbum_set::Entity, rbum_set::Column::Icon),
144                (rbum_set::Entity, rbum_set::Column::Sort),
145                (rbum_set::Entity, rbum_set::Column::Ext),
146                (rbum_set::Entity, rbum_set::Column::Disabled),
147                (rbum_set::Entity, rbum_set::Column::OwnPaths),
148                (rbum_set::Entity, rbum_set::Column::Owner),
149                (rbum_set::Entity, rbum_set::Column::CreateTime),
150                (rbum_set::Entity, rbum_set::Column::UpdateTime),
151                (rbum_set::Entity, rbum_set::Column::ScopeLevel),
152            ])
153            .from(rbum_set::Entity);
154        if let Some(kind) = &filter.kind {
155            query.and_where(Expr::col((rbum_set::Entity, rbum_set::Column::Kind)).eq(kind.to_string()));
156        }
157        if let Some(rbum_item_rel_filter_req) = &filter.rel {
158            if rbum_item_rel_filter_req.rel_by_from {
159                query.inner_join(
160                    rbum_rel::Entity,
161                    Expr::col((rbum_rel::Entity, rbum_rel::Column::FromRbumId)).equals((rbum_set::Entity, rbum_set::Column::Id)),
162                );
163                if let Some(rel_item_id) = &rbum_item_rel_filter_req.rel_item_id {
164                    query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::ToRbumItemId)).eq(rel_item_id.to_string()));
165                }
166            } else {
167                query.inner_join(
168                    rbum_rel::Entity,
169                    Expr::col((rbum_rel::Entity, rbum_rel::Column::ToRbumItemId)).equals((rbum_set::Entity, rbum_set::Column::Id)),
170                );
171                if let Some(rel_item_id) = &rbum_item_rel_filter_req.rel_item_id {
172                    query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::FromRbumId)).eq(rel_item_id.to_string()));
173                }
174            }
175            if let Some(tag) = &rbum_item_rel_filter_req.tag {
176                query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::Tag)).eq(tag.to_string()));
177            }
178            if let Some(from_rbum_kind) = &rbum_item_rel_filter_req.from_rbum_kind {
179                query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::FromRbumKind)).eq(from_rbum_kind.to_int()));
180            }
181        }
182        query.with_filter(Self::get_table_name(), &filter.basic, is_detail, true, ctx);
183        Ok(query)
184    }
185}
186
187impl RbumSetServ {
188    /// Fetch resource tree
189    ///
190    /// 获取资源树
191    pub async fn get_tree(rbum_set_id: &str, filter: &RbumSetTreeFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<RbumSetTreeResp> {
192        Self::check_scope(rbum_set_id, RbumSetServ::get_table_name(), funs, ctx).await?;
193        // check filter.filter_cate_sys_codes scope
194        if let Some(sys_codes) = &filter.sys_codes {
195            let sys_code_vec: Vec<String> = sys_codes.iter().filter(|sys_code| !sys_code.is_empty()).map(|sys_code| sys_code.to_string()).collect();
196            if !sys_code_vec.is_empty() {
197                let tmp_set_ids = vec![rbum_set_id.to_string()];
198                let values = HashMap::from([("rel_rbum_set_id".to_string(), &tmp_set_ids), ("sys_code".to_string(), &sys_code_vec)]);
199                Self::check_scopes(values, sys_code_vec.len() as u64, RbumSetCateServ::get_table_name(), funs, ctx).await?;
200            }
201        }
202        let set_cate_sys_code_node_len = funs.rbum_conf_set_cate_sys_code_node_len();
203        let mut rbum_set_cates = RbumSetCateServ::find_rbums(
204            &RbumSetCateFilterReq {
205                basic: RbumBasicFilterReq {
206                    with_sub_own_paths: true,
207                    ..Default::default()
208                },
209                rel_rbum_set_id: Some(rbum_set_id.to_string()),
210                sys_codes: filter.sys_codes.clone(),
211                sys_code_query_kind: filter.sys_code_query_kind.clone(),
212                sys_code_query_depth: filter.sys_code_query_depth,
213                cate_exts: filter.cate_exts.clone(),
214                ..Default::default()
215            },
216            None,
217            None,
218            funs,
219            ctx,
220        )
221        .await?;
222        rbum_set_cates.sort_by(|a, b| a.sys_code.cmp(&b.sys_code));
223        rbum_set_cates.sort_by(|a, b| a.sort.cmp(&b.sort));
224        let mut tree_main = rbum_set_cates
225            .iter()
226            .map(|r| RbumSetTreeNodeResp {
227                id: r.id.to_string(),
228                sys_code: r.sys_code.to_string(),
229                bus_code: r.bus_code.to_string(),
230                name: r.name.to_string(),
231                icon: r.icon.to_string(),
232                sort: r.sort,
233                ext: r.ext.to_string(),
234                own_paths: r.own_paths.to_string(),
235                owner: r.owner.to_string(),
236                scope_level: r.scope_level.clone(),
237                pid: rbum_set_cates.iter().find(|i| i.sys_code == r.sys_code[..r.sys_code.len() - set_cate_sys_code_node_len]).map(|i| i.id.to_string()),
238                rel: None,
239                create_time: r.create_time,
240                update_time: r.update_time,
241            })
242            .collect();
243        if !filter.fetch_cate_item {
244            return Ok(RbumSetTreeResp { main: tree_main, ext: None });
245        }
246        let rel_rbum_item_disabled = if filter.hide_item_with_disabled { Some(false) } else { None };
247        let rbum_set_items = RbumSetItemServ::find_detail_rbums(
248            &RbumSetItemFilterReq {
249                basic: RbumBasicFilterReq {
250                    // Set cate item is only used for connection,
251                    // it will do scope checking on both ends of the connection when it is created,
252                    // so we can release the own paths restriction here to avoid query errors.
253                    // E.g.
254                    // A tenant creates a set cate with scope_level=0 and associates a item,
255                    // if the permission is not released, all app will not query the data.
256                    own_paths: Some("".to_string()),
257                    with_sub_own_paths: true,
258                    ..Default::default()
259                },
260                rel_rbum_set_id: Some(rbum_set_id.to_string()),
261                rel_rbum_item_can_not_exist: Some(true),
262                sys_code_query_kind: filter.sys_code_query_kind.clone(),
263                sys_code_query_depth: filter.sys_code_query_depth,
264                rel_rbum_set_cate_sys_codes: filter.sys_codes.clone(),
265                rel_rbum_item_ids: filter.rel_rbum_item_ids.clone(),
266                rel_rbum_item_kind_ids: filter.rel_rbum_item_kind_ids.clone(),
267                rel_rbum_item_domain_ids: filter.rel_rbum_item_domain_ids.clone(),
268                rel_rbum_item_disabled,
269                ..Default::default()
270            },
271            None,
272            None,
273            funs,
274            ctx,
275        )
276        .await?;
277        if filter.hide_cate_with_empty_item {
278            // Filter out nodes without associated resource items
279            //
280            // 过滤掉没有关联资源项的节点
281            let cate_ids_has_item =
282                tree_main.iter().filter(|cate| cate.pid.is_none()).flat_map(|cate| Self::filter_exist_items(&tree_main, &cate.id, &rbum_set_items)).collect::<Vec<String>>();
283            tree_main.retain(|cate| cate_ids_has_item.contains(&cate.id));
284        }
285        let mut items = tree_main
286            .iter()
287            .map(|cate| {
288                (
289                    cate.id.clone(),
290                    rbum_set_items
291                        .iter()
292                        .filter(|i| i.rel_rbum_set_cate_id.clone().unwrap_or_default() == cate.id)
293                        .map(|i| RbumSetItemRelInfoResp {
294                            id: i.id.to_string(),
295                            sort: i.sort,
296                            rel_rbum_item_id: i.rel_rbum_item_id.to_string(),
297                            rel_rbum_item_code: i.rel_rbum_item_code.to_string(),
298                            rel_rbum_item_name: i.rel_rbum_item_name.to_string(),
299                            rel_rbum_item_kind_id: i.rel_rbum_item_kind_id.to_string(),
300                            rel_rbum_item_domain_id: i.rel_rbum_item_domain_id.to_string(),
301                            rel_rbum_item_owner: i.rel_rbum_item_owner.to_string(),
302                            rel_rbum_item_create_time: i.rel_rbum_item_create_time,
303                            rel_rbum_item_update_time: i.rel_rbum_item_update_time,
304                            rel_rbum_item_disabled: i.rel_rbum_item_disabled,
305                            rel_rbum_item_scope_level: i.rel_rbum_item_scope_level.clone(),
306                            own_paths: i.own_paths.to_string(),
307                            owner: i.owner.to_string(),
308                        })
309                        .collect(),
310                )
311            })
312            .collect::<HashMap<String, Vec<RbumSetItemRelInfoResp>>>();
313        items.insert(
314            "".to_string(),
315            rbum_set_items
316                .iter()
317                .filter(|i| i.rel_rbum_set_cate_id.is_none())
318                .map(|i| RbumSetItemRelInfoResp {
319                    id: i.id.to_string(),
320                    sort: i.sort,
321                    rel_rbum_item_id: i.rel_rbum_item_id.to_string(),
322                    rel_rbum_item_code: i.rel_rbum_item_code.to_string(),
323                    rel_rbum_item_name: i.rel_rbum_item_name.to_string(),
324                    rel_rbum_item_kind_id: i.rel_rbum_item_kind_id.to_string(),
325                    rel_rbum_item_domain_id: i.rel_rbum_item_domain_id.to_string(),
326                    rel_rbum_item_owner: i.rel_rbum_item_owner.to_string(),
327                    rel_rbum_item_create_time: i.rel_rbum_item_create_time,
328                    rel_rbum_item_update_time: i.rel_rbum_item_update_time,
329                    rel_rbum_item_disabled: i.rel_rbum_item_disabled,
330                    rel_rbum_item_scope_level: i.rel_rbum_item_scope_level.clone(),
331                    own_paths: i.own_paths.to_string(),
332                    owner: i.owner.to_string(),
333                })
334                .collect(),
335        );
336        let mut item_number_agg = tree_main
337            .iter()
338            .map(|cate| {
339                (
340                    cate.id.to_string(),
341                    tree_main
342                        .iter()
343                        .filter(|c| c.sys_code.starts_with(&cate.sys_code))
344                        .flat_map(|c| items.get(&c.id).expect("ignore"))
345                        .chunk_by(|c| c.rel_rbum_item_kind_id.clone())
346                        .into_iter()
347                        .map(|(g, c)| (g, c.map(|i| i.rel_rbum_item_id.clone()).collect::<HashSet<String>>().len() as u64))
348                        .collect::<HashMap<String, u64>>(),
349                )
350            })
351            .collect::<HashMap<String, HashMap<String, u64>>>();
352        // Add an aggregate to root
353        item_number_agg.insert(
354            "".to_string(),
355            items
356                .values()
357                .flat_map(|item| item.iter())
358                .chunk_by(|c| c.rel_rbum_item_kind_id.clone())
359                .into_iter()
360                .map(|(g, c)| (g, c.map(|i| i.rel_rbum_item_id.clone()).collect::<HashSet<String>>().len() as u64))
361                .collect::<HashMap<String, u64>>(),
362        );
363        let kind_ids = Vec::from_iter(items.values().flat_map(|items| items.iter().map(|item| item.rel_rbum_item_kind_id.clone())).collect::<HashSet<String>>());
364        let item_kinds = RbumKindServ::find_rbums(
365            &RbumKindFilterReq {
366                basic: RbumBasicFilterReq {
367                    ids: Some(kind_ids),
368                    ..Default::default()
369                },
370                ..Default::default()
371            },
372            None,
373            None,
374            funs,
375            ctx,
376        )
377        .await?
378        .into_iter()
379        .map(|kind| (kind.id.clone(), kind))
380        .collect();
381
382        let domain_ids: Vec<String> = Vec::from_iter(items.values().flat_map(|items| items.iter().map(|item| item.rel_rbum_item_domain_id.clone())).collect::<HashSet<String>>());
383        let item_domains = RbumDomainServ::find_rbums(
384            &RbumBasicFilterReq {
385                ids: Some(domain_ids),
386                ..Default::default()
387            },
388            None,
389            None,
390            funs,
391            ctx,
392        )
393        .await?
394        .into_iter()
395        .map(|domain| (domain.id.clone(), domain))
396        .collect();
397
398        Ok(RbumSetTreeResp {
399            main: tree_main,
400            ext: Some(RbumSetTreeExtResp {
401                items,
402                item_number_agg,
403                item_kinds,
404                item_domains,
405            }),
406        })
407    }
408
409    /// From the first layer node, recursively find the leaf node,
410    /// if the leaf node does not have a related resource item, return empty, otherwise return all node ids from the first layer node to the leaf node.
411    ///
412    /// 从第一层节点递归查找到叶子节点,如果叶子节点没有关联资源项,则返回空,反之返回从第一层节点到叶子节点的所有节点id。
413    fn filter_exist_items(tree_main: &Vec<RbumSetTreeNodeResp>, cate_id: &str, rbum_set_items: &Vec<RbumSetItemDetailResp>) -> Vec<String> {
414        let mut sub_cates = tree_main
415            .iter()
416            .filter(|cate| cate.pid == Some(cate_id.to_string()))
417            .flat_map(|cate| Self::filter_exist_items(tree_main, &cate.id, rbum_set_items))
418            .collect::<Vec<String>>();
419        let cate = tree_main.iter().find(|cate| cate.id == cate_id).expect("ignore");
420        if sub_cates.is_empty() {
421            // leaf node
422            if !rbum_set_items.iter().any(|item| item.rel_rbum_set_cate_id.clone().unwrap_or_default() == cate.id) {
423                vec![]
424            } else {
425                vec![cate.id.to_string()]
426            }
427        } else {
428            sub_cates.insert(0, cate.id.to_string());
429            sub_cates
430        }
431    }
432
433    /// Get resource set id by resource set code
434    ///
435    /// 根据资源集编码获取对应的id
436    pub async fn get_rbum_set_id_by_code(code: &str, with_sub: bool, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<String>> {
437        let key = &format!("{}{}", funs.rbum_conf_cache_key_set_code_(), code);
438        if let Some(cached_id) = funs.cache().get(key).await? {
439            Ok(Some(cached_id))
440        } else if let Some(rbum_set) = Self::find_one_rbum(
441            &RbumSetFilterReq {
442                basic: RbumBasicFilterReq {
443                    code: Some(code.to_string()),
444                    with_sub_own_paths: with_sub,
445                    ..Default::default()
446                },
447                ..Default::default()
448            },
449            funs,
450            ctx,
451        )
452        .await?
453        {
454            funs.cache().set_ex(key, &rbum_set.id, funs.rbum_conf_cache_key_set_code_expire_sec() as u64).await?;
455            Ok(Some(rbum_set.id))
456        } else {
457            Ok(None)
458        }
459    }
460}
461
462#[async_trait]
463impl RbumCrudOperation<rbum_set_cate::ActiveModel, RbumSetCateAddReq, RbumSetCateModifyReq, RbumSetCateSummaryResp, RbumSetCateDetailResp, RbumSetCateFilterReq>
464    for RbumSetCateServ
465{
466    fn get_table_name() -> &'static str {
467        rbum_set_cate::Entity.table_name()
468    }
469
470    async fn before_add_rbum(add_req: &mut RbumSetCateAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
471        Self::check_scope(&add_req.rel_rbum_set_id, RbumSetServ::get_table_name(), funs, ctx).await?;
472        if let Some(rbum_parent_cate_id) = &add_req.rbum_parent_cate_id {
473            Self::check_scope(rbum_parent_cate_id, RbumSetCateServ::get_table_name(), funs, ctx).await?;
474        }
475        Ok(())
476    }
477
478    async fn package_add(add_req: &RbumSetCateAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<rbum_set_cate::ActiveModel> {
479        let sys_code = Self::package_sys_code(&add_req.rel_rbum_set_id, add_req.rbum_parent_cate_id.as_deref(), funs, ctx).await?;
480        Ok(rbum_set_cate::ActiveModel {
481            id: Set(TardisFuns::field.nanoid()),
482            sys_code: Set(sys_code),
483            bus_code: Set(add_req.bus_code.to_string()),
484            name: Set(add_req.name.to_string()),
485            icon: Set(add_req.icon.as_ref().unwrap_or(&"".to_string()).to_string()),
486            sort: Set(add_req.sort.unwrap_or(0)),
487            ext: Set(add_req.ext.as_ref().unwrap_or(&"".to_string()).to_string()),
488            rel_rbum_set_id: Set(add_req.rel_rbum_set_id.to_string()),
489            scope_level: Set(add_req.scope_level.as_ref().unwrap_or(&RbumScopeLevelKind::Private).to_int()),
490            ..Default::default()
491        })
492    }
493
494    async fn package_modify(id: &str, modify_req: &RbumSetCateModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<rbum_set_cate::ActiveModel> {
495        let mut rbum_set_cate = rbum_set_cate::ActiveModel {
496            id: Set(id.to_string()),
497            ..Default::default()
498        };
499        if let Some(rbum_parent_cate_id) = &modify_req.rbum_parent_cate_id {
500            if let Some(detail) = Self::find_one_detail_rbum(
501                &RbumSetCateFilterReq {
502                    basic: RbumBasicFilterReq {
503                        ids: Some(vec![rbum_parent_cate_id.to_string()]),
504                        ..Default::default()
505                    },
506                    ..Default::default()
507                },
508                funs,
509                ctx,
510            )
511            .await?
512            {
513                let sys_code = Self::package_sys_code(&detail.rel_rbum_set_id, Some(rbum_parent_cate_id.as_str()), funs, ctx).await?;
514                rbum_set_cate.sys_code = Set(sys_code.to_string());
515            }
516        }
517        if let Some(bus_code) = &modify_req.bus_code {
518            rbum_set_cate.bus_code = Set(bus_code.to_string());
519        }
520        if let Some(name) = &modify_req.name {
521            rbum_set_cate.name = Set(name.to_string());
522        }
523        if let Some(icon) = &modify_req.icon {
524            rbum_set_cate.icon = Set(icon.to_string());
525        }
526        if let Some(sort) = modify_req.sort {
527            rbum_set_cate.sort = Set(sort);
528        }
529        if let Some(ext) = &modify_req.ext {
530            rbum_set_cate.ext = Set(ext.to_string());
531        }
532        if let Some(scope_level) = &modify_req.scope_level {
533            rbum_set_cate.scope_level = Set(scope_level.to_int());
534        }
535        Ok(rbum_set_cate)
536    }
537
538    async fn before_delete_rbum(id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<RbumSetCateDetailResp>> {
539        Self::check_ownership(id, funs, ctx).await?;
540        if funs
541            .db()
542            .count(
543                Query::select()
544                    .column((rbum_set_item::Entity, rbum_set_item::Column::Id))
545                    .from(rbum_set_item::Entity)
546                    .inner_join(
547                        rbum_set_cate::Entity,
548                        Condition::all()
549                            .add(Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::SysCode)).equals((rbum_set_item::Entity, rbum_set_item::Column::RelRbumSetCateCode)))
550                            .add(Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::RelRbumSetId)).equals((rbum_set_item::Entity, rbum_set_item::Column::RelRbumSetId))),
551                    )
552                    .inner_join(
553                        rbum_item::Entity,
554                        Expr::col((rbum_item::Entity, rbum_item::Column::Id)).equals((rbum_set_item::Entity, rbum_set_item::Column::RelRbumItemId)),
555                    )
556                    .and_where(Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::Id)).eq(id))
557                    .and_where(Expr::col((rbum_item::Entity, rbum_item::Column::Disabled)).eq(false)),
558            )
559            .await?
560            > 0
561        {
562            return Err(funs.err().conflict(
563                &Self::get_obj_name(),
564                "delete",
565                &format!("can not delete {}.{} when there are associated by set_item", Self::get_obj_name(), id),
566                "409-rbum-*-delete-conflict",
567            ));
568        }
569        let set_cate = Self::peek_rbum(
570            id,
571            &RbumSetCateFilterReq {
572                basic: RbumBasicFilterReq {
573                    with_sub_own_paths: true,
574                    ..Default::default()
575                },
576                ..Default::default()
577            },
578            funs,
579            ctx,
580        )
581        .await?;
582        if funs
583            .db()
584            .count(
585                Query::select()
586                    .column(rbum_set_cate::Column::Id)
587                    .from(rbum_set_cate::Entity)
588                    .and_where(Expr::col(rbum_set_cate::Column::Id).ne(id))
589                    .and_where(Expr::col(rbum_set_cate::Column::RelRbumSetId).eq(set_cate.rel_rbum_set_id.as_str()))
590                    .and_where(Expr::col(rbum_set_cate::Column::SysCode).like(format!("{}%", set_cate.sys_code.as_str()).as_str())),
591            )
592            .await?
593            > 0
594        {
595            return Err(funs.err().conflict(
596                &Self::get_obj_name(),
597                "delete",
598                &format!("can not delete {}.{} when there are associated by sub set_cate", Self::get_obj_name(), id),
599                "409-rbum-*-delete-conflict",
600            ));
601        }
602        Ok(None)
603    }
604
605    async fn package_query(is_detail: bool, filter: &RbumSetCateFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<SelectStatement> {
606        let mut query = Query::select();
607        query
608            .columns(vec![
609                (rbum_set_cate::Entity, rbum_set_cate::Column::Id),
610                (rbum_set_cate::Entity, rbum_set_cate::Column::SysCode),
611                (rbum_set_cate::Entity, rbum_set_cate::Column::BusCode),
612                (rbum_set_cate::Entity, rbum_set_cate::Column::Name),
613                (rbum_set_cate::Entity, rbum_set_cate::Column::Icon),
614                (rbum_set_cate::Entity, rbum_set_cate::Column::Sort),
615                (rbum_set_cate::Entity, rbum_set_cate::Column::Ext),
616                (rbum_set_cate::Entity, rbum_set_cate::Column::RelRbumSetId),
617                (rbum_set_cate::Entity, rbum_set_cate::Column::OwnPaths),
618                (rbum_set_cate::Entity, rbum_set_cate::Column::Owner),
619                (rbum_set_cate::Entity, rbum_set_cate::Column::CreateTime),
620                (rbum_set_cate::Entity, rbum_set_cate::Column::UpdateTime),
621                (rbum_set_cate::Entity, rbum_set_cate::Column::ScopeLevel),
622            ])
623            .from(rbum_set_cate::Entity);
624        if let Some(rel_rbum_set_id) = &filter.rel_rbum_set_id {
625            query.and_where(Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::RelRbumSetId)).eq(rel_rbum_set_id.to_string()));
626        }
627        if let Some(sys_codes) = &filter.sys_codes {
628            let query_kind = filter.sys_code_query_kind.clone().unwrap_or(RbumSetCateLevelQueryKind::Current);
629            if sys_codes.is_empty() {
630                if query_kind == RbumSetCateLevelQueryKind::Current {
631                    // query the first level
632                    query.and_where(Expr::expr(Func::char_length(Expr::col(rbum_set_cate::Column::SysCode))).eq(funs.rbum_conf_set_cate_sys_code_node_len() as i32));
633                }
634            } else {
635                let mut cond = Cond::any();
636                match query_kind {
637                    RbumSetCateLevelQueryKind::CurrentAndSub => {
638                        if let Some(depth) = filter.sys_code_query_depth {
639                            for sys_code in sys_codes {
640                                cond = cond.add(all![
641                                    Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::SysCode)).like(format!("{sys_code}%").as_str()),
642                                    Expr::expr(Func::char_length(Expr::col(rbum_set_cate::Column::SysCode)))
643                                        .lte((sys_code.len() + funs.rbum_conf_set_cate_sys_code_node_len() * depth as usize) as i32),
644                                ]);
645                            }
646                        } else {
647                            for sys_code in sys_codes {
648                                cond = cond.add(Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::SysCode)).like(format!("{sys_code}%").as_str()));
649                            }
650                        }
651                    }
652                    RbumSetCateLevelQueryKind::Sub => {
653                        if let Some(depth) = filter.sys_code_query_depth {
654                            for sys_code in sys_codes {
655                                cond = cond.add(all![
656                                    Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::SysCode)).like(format!("{sys_code}%").as_str()),
657                                    Expr::expr(Func::char_length(Expr::col(rbum_set_cate::Column::SysCode))).gt(sys_code.len() as i32),
658                                    Expr::expr(Func::char_length(Expr::col(rbum_set_cate::Column::SysCode)))
659                                        .lte((sys_code.len() + funs.rbum_conf_set_cate_sys_code_node_len() * depth as usize) as i32),
660                                ]);
661                            }
662                        } else {
663                            for sys_code in sys_codes {
664                                cond = cond.add(all![
665                                    Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::SysCode)).like(format!("{sys_code}%").as_str()),
666                                    Expr::expr(Func::char_length(Expr::col(rbum_set_cate::Column::SysCode))).gt(sys_code.len() as i32)
667                                ]);
668                            }
669                        }
670                    }
671                    RbumSetCateLevelQueryKind::CurrentAndParent => {
672                        for sys_code in sys_codes {
673                            let mut sys_codes = Self::get_parent_sys_codes(sys_code, funs)?;
674                            sys_codes.insert(0, sys_code.to_string());
675                            cond = cond.add(Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::SysCode)).is_in(sys_codes));
676                        }
677                    }
678                    RbumSetCateLevelQueryKind::Parent => {
679                        for sys_code in sys_codes {
680                            let parent_sys_codes = Self::get_parent_sys_codes(sys_code, funs)?;
681                            cond = cond.add(Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::SysCode)).is_in(parent_sys_codes));
682                        }
683                    }
684                    RbumSetCateLevelQueryKind::Current => {
685                        for sys_code in sys_codes {
686                            cond = cond.add(Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::SysCode)).eq(sys_code.as_str()));
687                        }
688                    }
689                }
690                query.cond_where(all![cond]);
691            }
692        }
693        if let Some(cate_exts) = &filter.cate_exts {
694            query.and_where(Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::Ext)).is_in(cate_exts.clone()));
695        }
696        if let Some(rbum_item_rel_filter_req) = &filter.rel {
697            if rbum_item_rel_filter_req.rel_by_from {
698                query.inner_join(
699                    rbum_rel::Entity,
700                    Expr::col((rbum_rel::Entity, rbum_rel::Column::FromRbumId)).equals((rbum_set_cate::Entity, rbum_set_cate::Column::Id)),
701                );
702                if let Some(rel_item_id) = &rbum_item_rel_filter_req.rel_item_id {
703                    query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::ToRbumItemId)).eq(rel_item_id.to_string()));
704                }
705            } else {
706                query.inner_join(
707                    rbum_rel::Entity,
708                    Expr::col((rbum_rel::Entity, rbum_rel::Column::ToRbumItemId)).equals((rbum_set_cate::Entity, rbum_set_cate::Column::Id)),
709                );
710                if let Some(rel_item_id) = &rbum_item_rel_filter_req.rel_item_id {
711                    query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::FromRbumId)).eq(rel_item_id.to_string()));
712                }
713            }
714            if let Some(tag) = &rbum_item_rel_filter_req.tag {
715                query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::Tag)).eq(tag.to_string()));
716            }
717            if let Some(from_rbum_kind) = &rbum_item_rel_filter_req.from_rbum_kind {
718                query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::FromRbumKind)).eq(from_rbum_kind.to_int()));
719            }
720        }
721        query.with_filter(Self::get_table_name(), &filter.basic, is_detail, true, ctx);
722        query.order_by(rbum_set_cate::Column::Sort, Order::Asc);
723        Ok(query)
724    }
725}
726
727impl RbumSetCateServ {
728    /// Fetch the sys_code of the parent node
729    ///
730    /// 获取父级节点的sys_code集合
731    fn get_parent_sys_codes(sys_code: &str, funs: &TardisFunsInst) -> TardisResult<Vec<String>> {
732        let set_cate_sys_code_node_len = funs.rbum_conf_set_cate_sys_code_node_len();
733        let mut level = sys_code.len() / set_cate_sys_code_node_len - 1;
734        if level == 0 {
735            return Ok(vec![]);
736        }
737        let mut sys_code_item = Vec::with_capacity(level);
738        while level != 0 {
739            sys_code_item.push(sys_code[..set_cate_sys_code_node_len * level].to_string());
740            level -= 1;
741        }
742        Ok(sys_code_item)
743    }
744
745    /// Fetch the sys_code of the new node
746    ///
747    /// 获取新节点的sys_code
748    async fn package_sys_code(rbum_set_id: &str, rbum_set_parent_cate_id: Option<&str>, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
749        let lock_key = format!("rbum_set_cate_sys_code_{rbum_set_id}");
750        while !funs.cache().set_nx(&lock_key, "waiting").await? {
751            sleep(Duration::from_millis(100)).await;
752        }
753        funs.cache().expire(&lock_key, 10).await?;
754        let sys_code = if let Some(rbum_set_parent_cate_id) = rbum_set_parent_cate_id {
755            let rel_parent_sys_code = Self::get_sys_code(rbum_set_parent_cate_id, funs, ctx).await?;
756            Self::get_max_sys_code_by_level(rbum_set_id, Some(&rel_parent_sys_code), funs, ctx).await
757        } else {
758            Self::get_max_sys_code_by_level(rbum_set_id, None, funs, ctx).await
759        };
760        funs.cache().del(&lock_key).await?;
761        sys_code
762    }
763
764    /// Fetch the max sys_code of the current node
765    ///
766    /// 获取当前节点的最大sys_code
767    async fn get_max_sys_code_by_level(rbum_set_id: &str, parent_sys_code: Option<&str>, funs: &TardisFunsInst, _: &TardisContext) -> TardisResult<String> {
768        let set_cate_sys_code_node_len = funs.rbum_conf_set_cate_sys_code_node_len();
769        let mut query = Query::select();
770        query.columns(vec![(rbum_set_cate::Column::SysCode)]).from(rbum_set_cate::Entity).and_where(Expr::col(rbum_set_cate::Column::RelRbumSetId).eq(rbum_set_id));
771
772        if let Some(parent_sys_code) = parent_sys_code {
773            query.and_where(Expr::col(rbum_set_cate::Column::SysCode).like(format!("{parent_sys_code}%").as_str()));
774            query.and_where(Expr::expr(Func::char_length(Expr::col(rbum_set_cate::Column::SysCode))).eq((parent_sys_code.len() + set_cate_sys_code_node_len) as i32));
775        } else {
776            // fetch max code in level 1
777            query.and_where(Expr::expr(Func::char_length(Expr::col(rbum_set_cate::Column::SysCode))).eq(set_cate_sys_code_node_len as i32));
778        }
779        query.order_by(rbum_set_cate::Column::SysCode, Order::Desc);
780        let current_max_sys_code = funs.db().get_dto::<SysCodeResp>(&query).await?.map(|r| r.sys_code);
781        if let Some(current_max_sys_code) = current_max_sys_code {
782            if current_max_sys_code.len() != set_cate_sys_code_node_len {
783                // if level N (N!=1) not empty
784                let current_level_sys_code = current_max_sys_code[current_max_sys_code.len() - set_cate_sys_code_node_len..].to_string();
785                let parent_sys_code = current_max_sys_code[..current_max_sys_code.len() - set_cate_sys_code_node_len].to_string();
786                let current_level_sys_code = TardisFuns::field.incr_by_base36(&current_level_sys_code).ok_or_else(|| {
787                    funs.err().bad_request(
788                        &Self::get_obj_name(),
789                        "get_sys_code",
790                        "current number of nodes is saturated",
791                        "400-rbum-set-sys-code-saturated",
792                    )
793                })?;
794                Ok(format!("{parent_sys_code}{current_level_sys_code}"))
795            } else {
796                // if level 1 not empty
797                Ok(TardisFuns::field.incr_by_base36(&current_max_sys_code).ok_or_else(|| {
798                    funs.err().bad_request(
799                        &Self::get_obj_name(),
800                        "get_sys_code",
801                        "current number of nodes is saturated",
802                        "400-rbum-set-sys-code-saturated",
803                    )
804                })?)
805            }
806        } else if let Some(parent_sys_code) = parent_sys_code {
807            // if level N (N!=1) is empty
808            Ok(format!("{}{}", parent_sys_code, String::from_utf8(vec![b'0'; set_cate_sys_code_node_len])?))
809        } else {
810            // if level 1 is empty
811            Ok(String::from_utf8(vec![b'0'; set_cate_sys_code_node_len])?)
812        }
813    }
814
815    /// Fetch the sys_code of the specified node
816    ///
817    /// 获取指定节点的sys_code
818    async fn get_sys_code(rbum_set_cate_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
819        Self::check_scope(rbum_set_cate_id, RbumSetCateServ::get_table_name(), funs, ctx).await?;
820        let sys_code = funs
821            .db()
822            .get_dto::<SysCodeResp>(
823                Query::select().column(rbum_set_cate::Column::SysCode).from(rbum_set_cate::Entity).and_where(Expr::col(rbum_set_cate::Column::Id).eq(rbum_set_cate_id)),
824            )
825            .await?
826            .ok_or_else(|| {
827                funs.err().not_found(
828                    &Self::get_obj_name(),
829                    "get_sys_code",
830                    &format!("not found set cate {rbum_set_cate_id}"),
831                    "404-rbum-set-cate-not-exist",
832                )
833            })?
834            .sys_code;
835        Ok(sys_code)
836    }
837
838    #[async_recursion]
839    pub async fn move_set_cate(set_cate_id: &str, parent_set_cate_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
840        let set_cate_detail = Self::get_rbum(
841            set_cate_id,
842            &RbumSetCateFilterReq {
843                basic: RbumBasicFilterReq {
844                    with_sub_own_paths: true,
845                    ..Default::default()
846                },
847                ..Default::default()
848            },
849            funs,
850            ctx,
851        )
852        .await?;
853        let set_item_ids = RbumSetItemServ::find_id_rbums(
854            &RbumSetItemFilterReq {
855                sys_code_query_kind: Some(RbumSetCateLevelQueryKind::Current),
856                rel_rbum_set_cate_sys_codes: Some(vec![set_cate_detail.sys_code.clone()]),
857                ..Default::default()
858            },
859            None,
860            None,
861            funs,
862            ctx,
863        )
864        .await?;
865        let result = Self::modify_rbum(
866            set_cate_id,
867            &mut RbumSetCateModifyReq {
868                bus_code: None,
869                name: None,
870                icon: None,
871                sort: None,
872                ext: None,
873                rbum_parent_cate_id: Some(parent_set_cate_id.to_string()),
874                scope_level: None,
875            },
876            funs,
877            ctx,
878        )
879        .await;
880        for set_item_id in set_item_ids {
881            RbumSetItemServ::modify_rbum(
882                &set_item_id,
883                &mut RbumSetItemModifyReq {
884                    rel_rbum_set_cate_id: Some(set_cate_id.to_string()),
885                    sort: None,
886                },
887                funs,
888                ctx,
889            )
890            .await?;
891        }
892
893        let child_set_cates = Self::find_rbums(
894            &RbumSetCateFilterReq {
895                basic: RbumBasicFilterReq {
896                    with_sub_own_paths: true,
897                    ..Default::default()
898                },
899                rel_rbum_set_id: Some(set_cate_detail.rel_rbum_set_id.clone()),
900                sys_codes: Some(vec![set_cate_detail.sys_code.clone()]),
901                sys_code_query_kind: Some(RbumSetCateLevelQueryKind::Sub),
902                sys_code_query_depth: Some(1),
903                cate_exts: None,
904                ..Default::default()
905            },
906            None,
907            None,
908            funs,
909            ctx,
910        )
911        .await?;
912        for child_set_cate in child_set_cates {
913            Self::move_set_cate(&child_set_cate.id, &set_cate_detail.id, funs, ctx).await?;
914        }
915
916        result
917    }
918}
919
920#[async_trait]
921impl RbumCrudOperation<rbum_set_item::ActiveModel, RbumSetItemAddReq, RbumSetItemModifyReq, RbumSetItemSummaryResp, RbumSetItemDetailResp, RbumSetItemFilterReq>
922    for RbumSetItemServ
923{
924    fn get_table_name() -> &'static str {
925        rbum_set_item::Entity.table_name()
926    }
927
928    async fn before_add_rbum(add_req: &mut RbumSetItemAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
929        Self::check_scope(&add_req.rel_rbum_set_id, RbumSetServ::get_table_name(), funs, ctx).await?;
930        Self::check_scope(&add_req.rel_rbum_item_id, RbumItemServ::get_table_name(), funs, ctx).await?;
931        let rel_sys_code = if add_req.rel_rbum_set_cate_id.is_empty() {
932            "".to_string()
933        } else {
934            Self::check_scope(&add_req.rel_rbum_set_cate_id, RbumSetCateServ::get_table_name(), funs, ctx).await?;
935            RbumSetCateServ::get_sys_code(add_req.rel_rbum_set_cate_id.as_str(), funs, ctx).await?
936        };
937        if funs
938            .db()
939            .count(
940                Query::select()
941                    .column(rbum_set_item::Column::Id)
942                    .from(rbum_set_item::Entity)
943                    .and_where(Expr::col(rbum_set_item::Column::RelRbumSetId).eq(add_req.rel_rbum_set_id.as_str()))
944                    .and_where(Expr::col(rbum_set_item::Column::RelRbumItemId).eq(add_req.rel_rbum_item_id.as_str()))
945                    .and_where(Expr::col(rbum_set_item::Column::RelRbumSetCateCode).eq(rel_sys_code.as_str())),
946            )
947            .await?
948            > 0
949        {
950            return Err(funs.err().conflict(&Self::get_obj_name(), "add", "item already exists", "409-rbum-set-item-exist"));
951        }
952        Ok(())
953    }
954
955    async fn package_add(add_req: &RbumSetItemAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<rbum_set_item::ActiveModel> {
956        let rel_sys_code = if add_req.rel_rbum_set_cate_id.is_empty() {
957            "".to_string()
958        } else {
959            RbumSetCateServ::get_sys_code(add_req.rel_rbum_set_cate_id.as_str(), funs, ctx).await?
960        };
961        Ok(rbum_set_item::ActiveModel {
962            id: Set(TardisFuns::field.nanoid()),
963            rel_rbum_set_id: Set(add_req.rel_rbum_set_id.to_string()),
964            rel_rbum_set_cate_code: Set(rel_sys_code),
965            rel_rbum_item_id: Set(add_req.rel_rbum_item_id.to_string()),
966            sort: Set(add_req.sort),
967            ..Default::default()
968        })
969    }
970
971    async fn package_modify(id: &str, modify_req: &RbumSetItemModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<rbum_set_item::ActiveModel> {
972        let mut rbum_set_item = rbum_set_item::ActiveModel {
973            id: Set(id.to_string()),
974            ..Default::default()
975        };
976        if let Some(sort) = modify_req.sort {
977            rbum_set_item.sort = Set(sort);
978        }
979        if let Some(rel_rbum_set_cate_id) = &modify_req.rel_rbum_set_cate_id {
980            rbum_set_item.rel_rbum_set_cate_code = Set(RbumSetCateServ::get_sys_code(rel_rbum_set_cate_id.as_str(), funs, ctx).await?);
981        }
982
983        Ok(rbum_set_item)
984    }
985
986    async fn package_query(is_detail: bool, filter: &RbumSetItemFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<SelectStatement> {
987        let rel_item_table = Alias::new("relItem");
988        let rbum_set_cate_join_type = if let Some(true) = filter.rel_rbum_item_can_not_exist {
989            JoinType::LeftJoin
990        } else {
991            JoinType::InnerJoin
992        };
993        let mut query = Query::select();
994        query
995            .columns(vec![
996                (rbum_set_item::Entity, rbum_set_item::Column::Id),
997                (rbum_set_item::Entity, rbum_set_item::Column::Sort),
998                (rbum_set_item::Entity, rbum_set_item::Column::RelRbumSetId),
999                (rbum_set_item::Entity, rbum_set_item::Column::RelRbumItemId),
1000                (rbum_set_item::Entity, rbum_set_item::Column::OwnPaths),
1001                (rbum_set_item::Entity, rbum_set_item::Column::Owner),
1002                (rbum_set_item::Entity, rbum_set_item::Column::CreateTime),
1003                (rbum_set_item::Entity, rbum_set_item::Column::UpdateTime),
1004            ])
1005            .expr_as(Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::Id)), Alias::new("rel_rbum_set_cate_id"))
1006            .expr_as(Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::SysCode)), Alias::new("rel_rbum_set_cate_sys_code"))
1007            .expr_as(Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::Name)), Alias::new("rel_rbum_set_cate_name"))
1008            .expr_as(Expr::col((rel_item_table.clone(), rbum_item::Column::Name)), Alias::new("rel_rbum_item_name"))
1009            .from(rbum_set_item::Entity)
1010            .join(
1011                rbum_set_cate_join_type,
1012                rbum_set_cate::Entity,
1013                all![
1014                    Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::SysCode)).equals((rbum_set_item::Entity, rbum_set_item::Column::RelRbumSetCateCode)),
1015                    Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::RelRbumSetId)).equals((rbum_set_item::Entity, rbum_set_item::Column::RelRbumSetId))
1016                ],
1017            )
1018            .join_as(
1019                JoinType::InnerJoin,
1020                rbum_item::Entity,
1021                rel_item_table.clone(),
1022                Expr::col((rel_item_table.clone(), rbum_item::Column::Id)).equals((rbum_set_item::Entity, rbum_set_item::Column::RelRbumItemId)),
1023            );
1024        if is_detail {
1025            query
1026                .expr_as(Expr::col((rel_item_table.clone(), rbum_item::Column::Code)), Alias::new("rel_rbum_item_code"))
1027                .expr_as(Expr::col((rel_item_table.clone(), rbum_item::Column::RelRbumKindId)), Alias::new("rel_rbum_item_kind_id"))
1028                .expr_as(
1029                    Expr::col((rel_item_table.clone(), rbum_item::Column::RelRbumDomainId)),
1030                    Alias::new("rel_rbum_item_domain_id"),
1031                )
1032                .expr_as(Expr::col((rel_item_table.clone(), rbum_item::Column::Owner)), Alias::new("rel_rbum_item_owner"))
1033                .expr_as(Expr::col((rel_item_table.clone(), rbum_item::Column::CreateTime)), Alias::new("rel_rbum_item_create_time"))
1034                .expr_as(Expr::col((rel_item_table.clone(), rbum_item::Column::UpdateTime)), Alias::new("rel_rbum_item_update_time"))
1035                .expr_as(Expr::col((rel_item_table.clone(), rbum_item::Column::Disabled)), Alias::new("rel_rbum_item_disabled"))
1036                .expr_as(Expr::col((rel_item_table.clone(), rbum_item::Column::ScopeLevel)), Alias::new("rel_rbum_item_scope_level"));
1037        }
1038        if let Some(rel_rbum_set_id) = &filter.rel_rbum_set_id {
1039            query.and_where(Expr::col((rbum_set_item::Entity, rbum_set_item::Column::RelRbumSetId)).eq(rel_rbum_set_id.to_string()));
1040        }
1041        if let Some(rel_rbum_set_cate_ids) = &filter.rel_rbum_set_cate_ids {
1042            query.and_where(Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::Id)).is_in(rel_rbum_set_cate_ids.clone()));
1043        }
1044        if let Some(rel_rbum_item_ids) = &filter.rel_rbum_item_ids {
1045            query.and_where(Expr::col((rbum_set_item::Entity, rbum_set_item::Column::RelRbumItemId)).is_in(rel_rbum_item_ids.clone()));
1046        }
1047        if let Some(rel_rbum_item_scope_level) = &filter.rel_rbum_item_scope_level {
1048            query.and_where(Expr::col((rel_item_table.clone(), rbum_item::Column::ScopeLevel)).eq(rel_rbum_item_scope_level.to_int()));
1049        }
1050        if let Some(rel_rbum_item_disabled) = &filter.rel_rbum_item_disabled {
1051            query.and_where(Expr::col((rel_item_table.clone(), rbum_item::Column::Disabled)).eq(*rel_rbum_item_disabled));
1052        }
1053        if let Some(rel_rbum_item_domain_ids) = &filter.rel_rbum_item_domain_ids {
1054            query.and_where(Expr::col((rel_item_table.clone(), rbum_item::Column::RelRbumDomainId)).is_in(rel_rbum_item_domain_ids.clone()));
1055        }
1056        if let Some(rel_rbum_item_kind_ids) = &filter.rel_rbum_item_kind_ids {
1057            query.and_where(Expr::col((rel_item_table, rbum_item::Column::RelRbumKindId)).is_in(rel_rbum_item_kind_ids.clone()));
1058        }
1059        if let Some(sys_codes) = &filter.rel_rbum_set_cate_sys_codes {
1060            let query_kind = filter.sys_code_query_kind.clone().unwrap_or(RbumSetCateLevelQueryKind::Current);
1061            if sys_codes.is_empty() {
1062                if query_kind == RbumSetCateLevelQueryKind::Current {
1063                    // query the first level
1064                    query.and_where(Expr::expr(Func::char_length(Expr::col(rbum_set_item::Column::RelRbumSetCateCode))).eq(funs.rbum_conf_set_cate_sys_code_node_len() as i32));
1065                }
1066            } else {
1067                let mut cond = Cond::any();
1068                match query_kind {
1069                    RbumSetCateLevelQueryKind::CurrentAndSub => {
1070                        if let Some(depth) = filter.sys_code_query_depth {
1071                            for sys_code in sys_codes {
1072                                cond = cond.add(all![
1073                                    Expr::col((rbum_set_item::Entity, rbum_set_item::Column::RelRbumSetCateCode)).like(format!("{sys_code}%").as_str()),
1074                                    Expr::expr(Func::char_length(Expr::col(rbum_set_item::Column::RelRbumSetCateCode)))
1075                                        .lte((sys_code.len() + funs.rbum_conf_set_cate_sys_code_node_len() * depth as usize) as i32),
1076                                ]);
1077                            }
1078                        } else {
1079                            for sys_code in sys_codes {
1080                                cond = cond.add(Expr::col((rbum_set_item::Entity, rbum_set_item::Column::RelRbumSetCateCode)).like(format!("{sys_code}%").as_str()));
1081                            }
1082                        }
1083                    }
1084                    RbumSetCateLevelQueryKind::Sub => {
1085                        if let Some(depth) = filter.sys_code_query_depth {
1086                            for sys_code in sys_codes {
1087                                cond = cond.add(all![
1088                                    Expr::col((rbum_set_item::Entity, rbum_set_item::Column::RelRbumSetCateCode)).like(format!("{sys_code}%").as_str()),
1089                                    Expr::expr(Func::char_length(Expr::col(rbum_set_item::Column::RelRbumSetCateCode))).gt(sys_code.len() as i32),
1090                                    Expr::expr(Func::char_length(Expr::col(rbum_set_item::Column::RelRbumSetCateCode)))
1091                                        .lte((sys_code.len() + funs.rbum_conf_set_cate_sys_code_node_len() * depth as usize) as i32),
1092                                ]);
1093                            }
1094                        } else {
1095                            for sys_code in sys_codes {
1096                                cond = cond.add(all![
1097                                    Expr::col((rbum_set_item::Entity, rbum_set_item::Column::RelRbumSetCateCode)).like(format!("{sys_code}%").as_str()),
1098                                    Expr::expr(Func::char_length(Expr::col(rbum_set_item::Column::RelRbumSetCateCode))).gt(sys_code.len() as i32)
1099                                ]);
1100                            }
1101                        }
1102                    }
1103                    RbumSetCateLevelQueryKind::CurrentAndParent => {
1104                        for sys_code in sys_codes {
1105                            let mut sys_codes = RbumSetCateServ::get_parent_sys_codes(sys_code, funs)?;
1106                            sys_codes.insert(0, sys_code.to_string());
1107                            cond = cond.add(Expr::col((rbum_set_item::Entity, rbum_set_item::Column::RelRbumSetCateCode)).is_in(sys_codes));
1108                        }
1109                    }
1110                    RbumSetCateLevelQueryKind::Parent => {
1111                        for sys_code in sys_codes {
1112                            let parent_sys_codes = RbumSetCateServ::get_parent_sys_codes(sys_code, funs)?;
1113                            cond = cond.add(Expr::col((rbum_set_item::Entity, rbum_set_item::Column::RelRbumSetCateCode)).is_in(parent_sys_codes));
1114                        }
1115                    }
1116                    RbumSetCateLevelQueryKind::Current => {
1117                        for sys_code in sys_codes {
1118                            cond = cond.add(Expr::col((rbum_set_item::Entity, rbum_set_item::Column::RelRbumSetCateCode)).eq(sys_code.as_str()));
1119                        }
1120                    }
1121                }
1122                query.cond_where(all![cond]);
1123            }
1124        }
1125        if let Some(rbum_set_item_cate_code) = &filter.rel_rbum_set_item_cate_code {
1126            query.and_where(Expr::col((rbum_set_item::Entity, rbum_set_item::Column::RelRbumSetCateCode)).eq(rbum_set_item_cate_code.as_str()));
1127        }
1128        query.with_filter(Self::get_table_name(), &filter.basic, is_detail, false, ctx);
1129        Ok(query)
1130    }
1131}
1132
1133impl RbumSetItemServ {
1134    /// Fetch all the paths of the resource item in the resource set
1135    ///
1136    /// 获取资源项在某一资源集上的所有路径
1137    ///
1138    /// Return format:
1139    ///
1140    /// * The first-level array is all the paths corresponding to the resource item (a resource item can hang multiple paths)
1141    /// * The second-level data is each node (such as the path is l1/l2/l3, then the corresponding node is l1, l2, l3)
1142    ///
1143    /// * 第一层数组为资源项对应的所有路径(一个资源项可以挂多个路径)
1144    /// * 第二层数据为每个节点(比如路径为 l1/l2/l3, 那么对应的节点为 l1, l2, l3)
1145    ///
1146    /// ```json
1147    /// [
1148    ///     [
1149    ///         {
1150    ///             "id": "Node Id",
1151    ///             "name": "Node Name"
1152    ///         }
1153    ///     ]
1154    /// ]
1155    /// ```
1156    pub async fn find_set_paths(rbum_item_id: &str, rbum_set_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Vec<Vec<RbumSetPathResp>>> {
1157        let rbum_set_cate_sys_codes: Vec<String> = Self::find_rbums(
1158            &RbumSetItemFilterReq {
1159                rel_rbum_item_can_not_exist: Some(true),
1160                rel_rbum_set_id: Some(rbum_set_id.to_string()),
1161                rel_rbum_item_ids: Some(vec![rbum_item_id.to_string()]),
1162                ..Default::default()
1163            },
1164            None,
1165            None,
1166            funs,
1167            ctx,
1168        )
1169        .await?
1170        .into_iter()
1171        .map(|item| item.rel_rbum_set_cate_sys_code.unwrap_or_default())
1172        .collect();
1173        let mut result: Vec<Vec<RbumSetPathResp>> = Vec::with_capacity(rbum_set_cate_sys_codes.len());
1174        for rbum_set_cate_sys_code in rbum_set_cate_sys_codes {
1175            if rbum_set_cate_sys_code.is_empty() {
1176                // Mount on the root node and directly give an empty array
1177                result.push(vec![]);
1178                continue;
1179            }
1180            let rbum_set_paths = RbumSetCateServ::find_rbums(
1181                &RbumSetCateFilterReq {
1182                    rel_rbum_set_id: Some(rbum_set_id.to_string()),
1183                    sys_codes: Some(vec![rbum_set_cate_sys_code]),
1184                    sys_code_query_kind: Some(RbumSetCateLevelQueryKind::CurrentAndParent),
1185                    ..Default::default()
1186                },
1187                None,
1188                None,
1189                funs,
1190                ctx,
1191            )
1192            .await?
1193            .into_iter()
1194            .sorted_by(|a, b| Ord::cmp(&a.sys_code, &b.sys_code))
1195            .map(|item| RbumSetPathResp {
1196                id: item.id,
1197                name: item.name,
1198                own_paths: item.own_paths,
1199            })
1200            .collect();
1201            result.push(rbum_set_paths);
1202        }
1203        Ok(result)
1204    }
1205
1206    /// Check whether resource item a is the parent of resource item b in the specified resource set
1207    ///
1208    /// 检查在指定资源集中资源项a是否是资源项b的父级
1209    pub async fn check_a_is_parent_of_b(rbum_item_a_id: &str, rbum_item_b_id: &str, rbum_set_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<bool> {
1210        Self::check_a_and_b(rbum_item_a_id, rbum_item_b_id, true, false, rbum_set_id, funs, ctx).await
1211    }
1212
1213    /// Check whether resource item a is the sibling of resource item b in the specified resource set
1214    ///
1215    /// 检查在指定资源集中资源项a是否是资源项b的兄弟级
1216    pub async fn check_a_is_sibling_of_b(rbum_item_a_id: &str, rbum_item_b_id: &str, rbum_set_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<bool> {
1217        Self::check_a_and_b(rbum_item_a_id, rbum_item_b_id, false, true, rbum_set_id, funs, ctx).await
1218    }
1219
1220    /// Check whether resource item a is the parent or sibling of resource item b in the specified resource set
1221    ///
1222    /// 检查在指定资源集中资源项a是否是资源项b的父级或兄弟级
1223    pub async fn check_a_is_parent_or_sibling_of_b(
1224        rbum_item_a_id: &str,
1225        rbum_item_b_id: &str,
1226        rbum_set_id: &str,
1227        funs: &TardisFunsInst,
1228        ctx: &TardisContext,
1229    ) -> TardisResult<bool> {
1230        Self::check_a_and_b(rbum_item_a_id, rbum_item_b_id, true, true, rbum_set_id, funs, ctx).await
1231    }
1232
1233    async fn check_a_and_b(
1234        rbum_item_a_id: &str,
1235        rbum_item_b_id: &str,
1236        is_parent: bool,
1237        is_sibling: bool,
1238        rbum_set_id: &str,
1239        funs: &TardisFunsInst,
1240        ctx: &TardisContext,
1241    ) -> TardisResult<bool> {
1242        let set_items: Vec<RbumSetItemSummaryResp> = Self::find_rbums(
1243            &RbumSetItemFilterReq {
1244                rel_rbum_set_id: Some(rbum_set_id.to_string()),
1245                rel_rbum_item_ids: Some(vec![rbum_item_a_id.to_string(), rbum_item_b_id.to_string()]),
1246                ..Default::default()
1247            },
1248            None,
1249            None,
1250            funs,
1251            ctx,
1252        )
1253        .await?;
1254        let set_item_a_sys_codes = set_items
1255            .iter()
1256            .filter(|item| item.rel_rbum_item_id == rbum_item_a_id)
1257            .map(|item| item.rel_rbum_set_cate_sys_code.clone().unwrap_or_default())
1258            .collect::<Vec<String>>();
1259        let set_item_b_sys_codes = set_items
1260            .iter()
1261            .filter(|item| item.rel_rbum_item_id == rbum_item_b_id)
1262            .map(|item| item.rel_rbum_set_cate_sys_code.clone().unwrap_or_default())
1263            .collect::<Vec<String>>();
1264
1265        Ok(set_item_a_sys_codes.iter().any(|sys_code_a| {
1266            set_item_b_sys_codes.iter().any(|sys_code_b| {
1267                if is_parent && is_sibling {
1268                    sys_code_b.starts_with(sys_code_a)
1269                } else if is_parent {
1270                    sys_code_b.starts_with(sys_code_a) && sys_code_a != sys_code_b
1271                } else if is_sibling {
1272                    sys_code_a == sys_code_b
1273                } else {
1274                    // !is_parent && !is_sibling
1275                    sys_code_a != sys_code_b && !sys_code_b.starts_with(sys_code_a)
1276                }
1277            })
1278        }))
1279    }
1280}
1281
1282#[derive(Debug, sea_orm::FromQueryResult)]
1283struct SysCodeResp {
1284    pub sys_code: String,
1285}