bios_iam/basic/serv/
iam_set_serv.rs

1use std::collections::{HashMap, HashSet};
2
3use bios_basic::rbum::dto::rbum_filer_dto::{RbumBasicFilterReq, RbumRelFilterReq, RbumSetCateFilterReq, RbumSetFilterReq, RbumSetItemFilterReq, RbumSetTreeFilterReq};
4
5use bios_basic::rbum::dto::rbum_set_cate_dto::{RbumSetCateAddReq, RbumSetCateModifyReq, RbumSetCateSummaryResp};
6use bios_basic::rbum::dto::rbum_set_dto::{RbumSetAddReq, RbumSetPathResp, RbumSetTreeNodeResp, RbumSetTreeResp};
7use bios_basic::rbum::dto::rbum_set_item_dto::{RbumSetItemAddReq, RbumSetItemDetailResp, RbumSetItemModifyReq};
8use bios_basic::rbum::helper::rbum_scope_helper;
9use bios_basic::rbum::rbum_config::RbumConfigApi;
10use bios_basic::rbum::rbum_enumeration::{RbumRelFromKind, RbumScopeLevelKind, RbumSetCateLevelQueryKind};
11use bios_basic::rbum::serv::rbum_crud_serv::RbumCrudOperation;
12use bios_basic::rbum::serv::rbum_item_serv::RbumItemCrudOperation;
13use bios_basic::rbum::serv::rbum_rel_serv::RbumRelServ;
14use bios_basic::rbum::serv::rbum_set_serv::{RbumSetCateServ, RbumSetItemServ, RbumSetServ};
15use tardis::basic::dto::TardisContext;
16use tardis::basic::field::TrimString;
17use tardis::basic::result::TardisResult;
18
19use tardis::serde_json::json;
20use tardis::web::web_resp::TardisPage;
21use tardis::{TardisFuns, TardisFunsInst};
22
23use crate::basic::dto::iam_filer_dto::IamAccountFilterReq;
24use crate::basic::dto::iam_set_dto::{IamSetCateAddReq, IamSetCateModifyReq, IamSetItemAddReq};
25use crate::iam_config::{IamBasicConfigApi, IamConfig};
26use crate::iam_constants::{RBUM_SCOPE_LEVEL_APP, RBUM_SCOPE_LEVEL_TENANT};
27use crate::iam_enumeration::{IamRelKind, IamSetCateKind, IamSetKind};
28
29use super::clients::iam_kv_client::IamKvClient;
30use super::clients::iam_log_client::{IamLogClient, LogParamTag};
31use super::clients::iam_search_client::IamSearchClient;
32use super::clients::iam_stats_client::IamStatsClient;
33use super::iam_account_serv::IamAccountServ;
34use super::iam_rel_serv::IamRelServ;
35
36const SET_AND_ITEM_SPLIT_FLAG: &str = ":";
37
38pub struct IamSetServ;
39
40impl IamSetServ {
41    pub async fn init_set(set_kind: IamSetKind, scope_level: RbumScopeLevelKind, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<(String, Option<(String, String)>)> {
42        let code = Self::get_default_code(&set_kind, &ctx.own_paths);
43        let set_id = RbumSetServ::add_rbum(
44            &mut RbumSetAddReq {
45                code: TrimString(code.clone()),
46                kind: TrimString(set_kind.to_string()),
47                name: TrimString(code),
48                note: None,
49                icon: None,
50                sort: None,
51                ext: None,
52                scope_level: Some(scope_level.clone()),
53                disabled: None,
54            },
55            funs,
56            ctx,
57        )
58        .await?;
59        let cates = if set_kind == IamSetKind::Res {
60            let cate_menu_id = RbumSetCateServ::add_rbum(
61                &mut RbumSetCateAddReq {
62                    name: TrimString("Menus".to_string()),
63                    bus_code: TrimString("__menus__".to_string()),
64                    icon: None,
65                    sort: None,
66                    ext: Some(IamSetCateKind::Root.to_string()),
67                    rbum_parent_cate_id: None,
68                    rel_rbum_set_id: set_id.clone(),
69                    scope_level: Some(scope_level.clone()),
70                },
71                funs,
72                ctx,
73            )
74            .await?;
75            let cate_api_id = RbumSetCateServ::add_rbum(
76                &mut RbumSetCateAddReq {
77                    name: TrimString("Apis".to_string()),
78                    bus_code: TrimString("__apis__".to_string()),
79                    icon: None,
80                    sort: None,
81                    ext: None,
82                    rbum_parent_cate_id: None,
83                    rel_rbum_set_id: set_id.clone(),
84                    scope_level: Some(scope_level.clone()),
85                },
86                funs,
87                ctx,
88            )
89            .await?;
90            Some((cate_menu_id, cate_api_id))
91        } else {
92            None
93        };
94        Ok((set_id, cates))
95    }
96
97    pub async fn get_default_set_id_by_ctx(kind: &IamSetKind, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
98        Self::get_set_id_by_code(&Self::get_default_code(kind, &ctx.own_paths), true, funs, ctx).await
99    }
100
101    pub async fn get_set_id_by_code(code: &str, with_sub: bool, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
102        RbumSetServ::get_rbum_set_id_by_code(code, with_sub, funs, ctx)
103            .await?
104            .ok_or_else(|| funs.err().not_found("iam_set", "get_id", &format!("not found set by code {code}"), "404-rbum-set-code-not-exist"))
105    }
106
107    pub async fn try_get_rel_ctx_by_set_id(set_id: Option<String>, funs: &TardisFunsInst, mut ctx: TardisContext) -> TardisResult<TardisContext> {
108        if let Some(set_id) = set_id {
109            let code = Self::get_code_ctx_by_set_id(&set_id, funs, ctx.clone())
110                .await?
111                .ok_or_else(|| funs.err().not_found("iam_set", "get_rel_ctx", &format!("not found set by set_id {set_id}"), "404-rbum-set-id-not-exist"))?;
112            let splits = code.split(':').collect::<Vec<_>>();
113            if let Some(own_paths) = splits.first() {
114                ctx.own_paths = own_paths.to_string();
115            }
116            Ok(ctx)
117        } else {
118            Ok(ctx)
119        }
120    }
121
122    pub async fn get_code_ctx_by_set_id(set_id: &str, funs: &TardisFunsInst, ctx: TardisContext) -> TardisResult<Option<String>> {
123        let mock_ctx = TardisContext { own_paths: "".to_string(), ..ctx };
124        if let Some(rbum_set) = RbumSetServ::find_one_rbum(
125            &RbumSetFilterReq {
126                basic: RbumBasicFilterReq {
127                    ids: Some(vec![set_id.to_string()]),
128                    with_sub_own_paths: true,
129                    ..Default::default()
130                },
131                ..Default::default()
132            },
133            funs,
134            &mock_ctx,
135        )
136        .await?
137        {
138            Ok(Some(rbum_set.code))
139        } else {
140            Ok(None)
141        }
142    }
143
144    pub fn get_default_org_code_by_system() -> String {
145        Self::get_default_code(&IamSetKind::Org, "")
146    }
147
148    pub fn get_default_org_code_by_tenant(funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
149        if let Some(own_paths) = rbum_scope_helper::get_path_item(RBUM_SCOPE_LEVEL_TENANT.to_int(), &ctx.own_paths) {
150            Ok(Self::get_default_code(&IamSetKind::Org, &own_paths))
151        } else {
152            Err(funs.err().not_found("iam_set", "get_org_code", "not found tenant own_paths", "404-rbum-set-code-not-exist"))
153        }
154    }
155
156    pub fn get_default_org_code_by_app(funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
157        if let Some(own_paths) = rbum_scope_helper::get_path_item(RBUM_SCOPE_LEVEL_APP.to_int(), &ctx.own_paths) {
158            Ok(Self::get_default_code(&IamSetKind::Org, &own_paths))
159        } else {
160            Err(funs.err().not_found("iam_set", "get_org_code", "not found app own_paths", "404-rbum-set-code-not-exist"))
161        }
162    }
163
164    pub fn get_default_code(kind: &IamSetKind, own_paths: &str) -> String {
165        format!("{}:{}", own_paths, kind.to_string().to_lowercase())
166    }
167
168    pub async fn add_set_cate(set_id: &str, add_req: &IamSetCateAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
169        let result = RbumSetCateServ::add_rbum(
170            &mut RbumSetCateAddReq {
171                name: add_req.name.clone(),
172                bus_code: add_req.bus_code.as_ref().unwrap_or(&TrimString("".to_string())).clone(),
173                icon: add_req.icon.clone(),
174                sort: add_req.sort,
175                ext: add_req.ext.clone(),
176                rbum_parent_cate_id: add_req.rbum_parent_cate_id.clone(),
177                rel_rbum_set_id: set_id.to_string(),
178                scope_level: add_req.scope_level.clone(),
179            },
180            funs,
181            ctx,
182        )
183        .await;
184
185        let item = RbumSetServ::get_rbum(set_id, &RbumSetFilterReq::default(), funs, ctx).await?;
186        let mut kind = item.kind;
187
188        if kind == IamSetKind::Apps.to_string() && result.is_ok() {
189            IamKvClient::add_or_modify_key_name(&funs.conf::<IamConfig>().spi.kv_apps_prefix.clone(), &result.clone()?, &add_req.name, None, funs, ctx).await?;
190        } else if kind == IamSetKind::Org.to_string() && result.is_ok() {
191            IamStatsClient::async_org_fact_record_load(result.clone().unwrap(), funs, ctx).await?;
192        }
193        if result.is_ok() {
194            kind.make_ascii_lowercase();
195            let (op_describe, tag, op_kind) = match kind.as_str() {
196                "org" => ("添加部门".to_string(), Some(LogParamTag::IamOrg), Some("Add".to_string())),
197                "res" => ("添加目录".to_string(), Some(LogParamTag::IamRes), Some("Add".to_string())),
198                _ => (String::new(), None, None),
199            };
200
201            if let Some(tag) = tag {
202                let _ = IamLogClient::add_ctx_task(tag, Some(result.clone()?), op_describe, op_kind, ctx).await;
203            }
204        }
205
206        result
207    }
208
209    pub async fn modify_set_cate(set_cate_id: &str, modify_req: &IamSetCateModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
210        let result = RbumSetCateServ::modify_rbum(
211            set_cate_id,
212            &mut RbumSetCateModifyReq {
213                bus_code: modify_req.bus_code.clone(),
214                name: modify_req.name.clone(),
215                icon: modify_req.icon.clone(),
216                sort: modify_req.sort,
217                ext: modify_req.ext.clone(),
218                rbum_parent_cate_id: None,
219                scope_level: modify_req.scope_level.clone(),
220            },
221            funs,
222            ctx,
223        )
224        .await;
225        if result.is_ok() {
226            let set_cate_item = RbumSetCateServ::get_rbum(
227                set_cate_id,
228                &RbumSetCateFilterReq {
229                    basic: RbumBasicFilterReq {
230                        with_sub_own_paths: true,
231                        ..Default::default()
232                    },
233                    ..Default::default()
234                },
235                funs,
236                ctx,
237            )
238            .await?;
239            let item = RbumSetServ::get_rbum(
240                &set_cate_item.rel_rbum_set_id,
241                &RbumSetFilterReq {
242                    basic: RbumBasicFilterReq {
243                        with_sub_own_paths: true,
244                        ..Default::default()
245                    },
246                    ..Default::default()
247                },
248                funs,
249                ctx,
250            )
251            .await?;
252            let mut kind = item.kind;
253            if kind == IamSetKind::Apps.to_string() {
254                IamKvClient::add_or_modify_key_name(
255                    &funs.conf::<IamConfig>().spi.kv_apps_prefix.clone(),
256                    set_cate_id,
257                    &set_cate_item.name.clone(),
258                    None,
259                    funs,
260                    ctx,
261                )
262                .await?;
263            } else if kind == IamSetKind::Org.to_string() && result.is_ok() {
264                IamStatsClient::async_org_fact_record_load(set_cate_id.to_owned(), funs, ctx).await?;
265            }
266            kind.make_ascii_lowercase();
267            match kind.as_str() {
268                "org" => {
269                    if let Some(name) = &modify_req.name {
270                        let _ = IamLogClient::add_ctx_task(
271                            LogParamTag::IamOrg,
272                            Some(set_cate_id.to_string()),
273                            format!("重命名部门为{}", name),
274                            Some("Rename".to_string()),
275                            ctx,
276                        )
277                        .await;
278                    }
279                }
280                "res" => {
281                    let _ = IamLogClient::add_ctx_task(
282                        LogParamTag::IamRes,
283                        Some(set_cate_id.to_string()),
284                        "编辑目录".to_string(),
285                        Some("ModifyContent".to_string()),
286                        ctx,
287                    )
288                    .await;
289                }
290                _ => {}
291            }
292        }
293
294        result
295    }
296
297    pub async fn move_set_cate(set_cate_id: &str, parent_set_cate_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
298        RbumSetCateServ::move_set_cate(set_cate_id, parent_set_cate_id, funs, ctx).await
299    }
300
301    pub async fn delete_set_cate(set_cate_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<u64> {
302        let set_cate_item = RbumSetCateServ::get_rbum(
303            set_cate_id,
304            &RbumSetCateFilterReq {
305                basic: RbumBasicFilterReq {
306                    with_sub_own_paths: true,
307                    ..Default::default()
308                },
309                ..Default::default()
310            },
311            funs,
312            ctx,
313        )
314        .await?;
315        let item = RbumSetServ::get_rbum(
316            &set_cate_item.rel_rbum_set_id,
317            &RbumSetFilterReq {
318                basic: RbumBasicFilterReq {
319                    with_sub_own_paths: true,
320                    ..Default::default()
321                },
322                ..Default::default()
323            },
324            funs,
325            ctx,
326        )
327        .await?;
328
329        let result = RbumSetCateServ::delete_rbum(set_cate_id, funs, ctx).await;
330
331        if result.is_ok() {
332            let mut kind = item.kind;
333            if kind == IamSetKind::Org.to_string() {
334                IamStatsClient::async_org_fact_record_remove(set_cate_id.to_owned(), funs, ctx).await?;
335            }
336            kind.make_ascii_lowercase();
337            let (op_describe, tag, op_kind) = match kind.as_str() {
338                "org" => ("删除部门".to_string(), Some(LogParamTag::IamOrg), Some("Delete".to_string())),
339                "res" => ("删除目录".to_string(), Some(LogParamTag::IamRes), Some("Delete".to_string())),
340                _ => (String::new(), None, None),
341            };
342            if let Some(tag) = tag {
343                let _ = IamLogClient::add_ctx_task(tag, Some(set_cate_id.to_string()), op_describe, op_kind, ctx).await;
344            }
345        }
346
347        result
348    }
349    pub async fn find_set_cate(
350        filter: &RbumSetCateFilterReq,
351        desc_sort_by_create: Option<bool>,
352        desc_sort_by_update: Option<bool>,
353        funs: &TardisFunsInst,
354        ctx: &TardisContext,
355    ) -> TardisResult<Vec<RbumSetCateSummaryResp>> {
356        RbumSetCateServ::find_rbums(filter, desc_sort_by_create, desc_sort_by_update, funs, ctx).await
357    }
358
359    pub async fn get_tree(set_id: &str, filter: &mut RbumSetTreeFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<RbumSetTreeResp> {
360        filter.rel_rbum_item_domain_ids = Some(vec![funs.iam_basic_domain_iam_id()]);
361        let resp = RbumSetServ::get_tree(set_id, filter, funs, ctx).await?;
362        Self::find_rel_set_cate(resp, filter, funs, ctx).await
363    }
364
365    // find set_cate 对应的set_id,返回set_id下面set_cate
366    // 返回的节点里面,如果有通过关联关系而来的cate(不属于此set_id的),会在ext中标识它真正的set_id
367    async fn find_rel_set_cate(resp: RbumSetTreeResp, filter: &mut RbumSetTreeFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<RbumSetTreeResp> {
368        let mut result_main: Vec<RbumSetTreeNodeResp> = vec![];
369        let mut resp_items = HashMap::new();
370        let mut resp_item_domains = HashMap::new();
371        let mut resp_item_kinds = HashMap::new();
372        let mut resp_item_number_agg = HashMap::new();
373
374        //from set_cate to find tenant_id (set_id)
375        for mut r in resp.main.clone() {
376            if let Some(set_rel) = RbumRelServ::find_one_rbum(
377                &RbumRelFilterReq {
378                    basic: RbumBasicFilterReq {
379                        own_paths: Some(ctx.own_paths.clone()),
380                        with_sub_own_paths: true,
381                        ..Default::default()
382                    },
383                    tag: Some(IamRelKind::IamOrgRel.to_string()),
384                    from_rbum_kind: Some(RbumRelFromKind::SetCate),
385                    from_rbum_id: Some(r.id.to_string()),
386                    ..Default::default()
387                },
388                funs,
389                ctx,
390            )
391            .await?
392            {
393                let new_resp = RbumSetTreeNodeResp {
394                    rel: Some(set_rel.to_rbum_item_id.clone()),
395                    ..r.clone()
396                };
397                r = new_resp;
398
399                let mock_ctx = TardisContext {
400                    own_paths: set_rel.to_own_paths.clone(),
401                    ..ctx.clone()
402                };
403                let mut set_filter = filter.clone();
404                if set_filter.sys_codes.is_some() {
405                    set_filter.sys_codes = Some(vec!["".to_string()]);
406                }
407                if set_filter.sys_codes.is_some() && set_filter.sys_code_query_depth == Some(1) && set_filter.sys_code_query_kind == Some(RbumSetCateLevelQueryKind::CurrentAndSub)
408                {
409                    //只获取一层,那么就不需要查询关联的
410                    result_main.push(r);
411                    continue;
412                }
413                let rel_set_id = IamSetServ::get_default_set_id_by_ctx(&IamSetKind::Org, funs, &mock_ctx).await?;
414                let mut tenant_resp = RbumSetServ::get_tree(&rel_set_id, &set_filter, funs, &mock_ctx).await?;
415                let mut resp_tenant_node: Vec<RbumSetTreeNodeResp> = tenant_resp
416                    .main
417                    .clone()
418                    .iter()
419                    .filter(|r_main| r_main.pid.is_none())
420                    .map(|r_main| RbumSetTreeNodeResp {
421                        pid: Some(r.id.clone()),
422                        ..r_main.clone()
423                    })
424                    .collect();
425                tenant_resp.main.retain(|r_main| r_main.pid.is_some());
426                resp_tenant_node.extend(tenant_resp.main.clone());
427                result_main.extend(
428                    resp_tenant_node
429                        .iter()
430                        .map(|rm| {
431                            let mut r = rm.clone();
432                            r.ext = json!({"set_id":rel_set_id.clone(),"disable_import":true}).to_string();
433                            r
434                        })
435                        .collect::<Vec<RbumSetTreeNodeResp>>(),
436                );
437                if set_filter.fetch_cate_item {
438                    if let Some(ext_resp) = tenant_resp.ext {
439                        resp_items.extend(ext_resp.items);
440                        resp_item_domains.extend(ext_resp.item_domains);
441                        resp_item_kinds.extend(ext_resp.item_kinds);
442                        resp_item_number_agg.extend(ext_resp.item_number_agg);
443                    }
444                }
445            }
446            //把原来的resp.main完全拷贝到result_main中
447            result_main.push(r);
448        }
449        // 向上查询 标识父级也不能显示绑定
450        for rm in result_main.clone() {
451            if rm.rel.is_some() {
452                let mut pid = rm.pid;
453                loop {
454                    if pid.is_none() {
455                        break;
456                    }
457                    if let Some(p_node) = result_main.iter_mut().find(|r| pid.is_some() && r.id == pid.clone().unwrap_or_default()) {
458                        p_node.ext = json!({"disable_import":true}).to_string();
459                        pid.clone_from(&p_node.pid);
460                    } else {
461                        break;
462                    }
463                }
464            }
465        }
466        let mut result = RbumSetTreeResp { main: result_main, ext: None };
467        if filter.fetch_cate_item {
468            if let Some(mut ext_resp) = resp.ext.clone() {
469                ext_resp.items.extend(resp_items);
470                ext_resp.item_domains.extend(resp_item_domains);
471                ext_resp.item_kinds.extend(resp_item_kinds);
472                ext_resp.item_number_agg.extend(resp_item_number_agg);
473                result.ext = Some(ext_resp);
474            }
475        }
476        Ok(result)
477    }
478
479    pub async fn get_tree_with_auth_by_account(set_id: &str, account_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<RbumSetTreeResp> {
480        let tree_with_account = Self::get_tree(
481            set_id,
482            &mut RbumSetTreeFilterReq {
483                fetch_cate_item: true,
484                hide_item_with_disabled: true,
485                rel_rbum_item_ids: Some(vec![account_id.to_string()]),
486                rel_rbum_item_kind_ids: Some(vec![funs.iam_basic_kind_account_id()]),
487                ..Default::default()
488            },
489            funs,
490            ctx,
491        )
492        .await?;
493        let mut account_rel_sys_codes = vec![];
494        if let Some(tree_ext) = tree_with_account.ext.as_ref() {
495            account_rel_sys_codes = tree_with_account.main.into_iter().filter(|cate| !tree_ext.items[&cate.id].is_empty()).map(|cate| cate.sys_code).collect::<Vec<String>>();
496        }
497        if account_rel_sys_codes.is_empty() {
498            return Ok(RbumSetTreeResp { main: vec![], ext: None });
499        }
500
501        Self::get_tree(
502            set_id,
503            &mut RbumSetTreeFilterReq {
504                fetch_cate_item: true,
505                sys_codes: Some(account_rel_sys_codes),
506                sys_code_query_kind: Some(RbumSetCateLevelQueryKind::CurrentAndSub),
507                ..Default::default()
508            },
509            funs,
510            ctx,
511        )
512        .await
513    }
514
515    pub async fn get_menu_tree_by_roles(set_id: &str, role_ids: &Vec<String>, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<RbumSetTreeResp> {
516        let set_cate_sys_code_node_len = funs.rbum_conf_set_cate_sys_code_node_len();
517        let menu_sys_code = String::from_utf8(vec![b'0'; set_cate_sys_code_node_len])?;
518        let mut res_ids = HashSet::new();
519        let mut global_ctx = ctx.clone();
520        global_ctx.own_paths = "".to_string();
521        // TODO default empty res
522        res_ids.insert("".to_string());
523        for role_id in role_ids {
524            let rel_res_ids = IamRelServ::find_to_id_rels(&IamRelKind::IamResRole, role_id, None, None, funs, &global_ctx).await?;
525            res_ids.extend(rel_res_ids.into_iter());
526        }
527        let mut filter = RbumSetTreeFilterReq {
528            fetch_cate_item: true,
529            hide_cate_with_empty_item: true,
530            hide_item_with_disabled: true,
531            sys_codes: Some(vec![menu_sys_code]),
532            sys_code_query_kind: Some(RbumSetCateLevelQueryKind::CurrentAndSub),
533            ..Default::default()
534        };
535        if !res_ids.is_empty() {
536            filter.rel_rbum_item_ids = Some(res_ids.into_iter().collect());
537        }
538        RbumSetServ::get_tree(set_id, &filter, funs, ctx).await
539    }
540
541    pub async fn get_menu_tree(set_id: &str, exts: Option<String>, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<RbumSetTreeResp> {
542        let cate_exts = exts.map(|exts| exts.split(',').map(|r| r.to_string()).collect());
543        let set_cate_sys_code_node_len = funs.rbum_conf_set_cate_sys_code_node_len();
544        let menu_sys_code = String::from_utf8(vec![b'0'; set_cate_sys_code_node_len])?;
545        Self::get_tree_with_sys_codes(set_id, Some(vec![menu_sys_code]), cate_exts, funs, ctx).await
546    }
547
548    pub async fn get_api_tree(set_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<RbumSetTreeResp> {
549        let set_cate_sys_code_node_len = funs.rbum_conf_set_cate_sys_code_node_len();
550        if let Some(api_sys_code) = TardisFuns::field.incr_by_base36(&String::from_utf8(vec![b'0'; set_cate_sys_code_node_len])?) {
551            Self::get_tree_with_sys_codes(set_id, Some(vec![api_sys_code]), None, funs, ctx).await
552        } else {
553            Self::get_tree_with_sys_codes(set_id, None, None, funs, ctx).await
554        }
555    }
556
557    pub async fn get_cate_id_with_sys_code(set_id: &str, filter_sys_code: Option<Vec<String>>, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
558        let rbum_cate = RbumSetCateServ::find_one_rbum(
559            &RbumSetCateFilterReq {
560                rel_rbum_set_id: Some(set_id.to_string()),
561                sys_codes: filter_sys_code,
562                sys_code_query_kind: Some(RbumSetCateLevelQueryKind::Current),
563                ..Default::default()
564            },
565            funs,
566            ctx,
567        )
568        .await?;
569        Ok(rbum_cate.as_ref().map(|r| r.id.clone()).unwrap_or_default())
570    }
571
572    async fn get_tree_with_sys_codes(
573        set_id: &str,
574        filter_sys_codes: Option<Vec<String>>,
575        cate_exts: Option<Vec<String>>,
576        funs: &TardisFunsInst,
577        ctx: &TardisContext,
578    ) -> TardisResult<RbumSetTreeResp> {
579        RbumSetServ::get_tree(
580            set_id,
581            &RbumSetTreeFilterReq {
582                fetch_cate_item: true,
583                sys_codes: filter_sys_codes,
584                sys_code_query_kind: Some(RbumSetCateLevelQueryKind::CurrentAndSub),
585                cate_exts,
586                ..Default::default()
587            },
588            funs,
589            ctx,
590        )
591        .await
592    }
593
594    pub async fn add_set_item(add_req: &IamSetItemAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
595        let result: Result<String, tardis::basic::error::TardisError> = RbumSetItemServ::add_rbum(
596            &mut RbumSetItemAddReq {
597                sort: add_req.sort,
598                rel_rbum_set_id: add_req.set_id.clone(),
599                rel_rbum_set_cate_id: add_req.set_cate_id.clone(),
600                rel_rbum_item_id: add_req.rel_rbum_item_id.clone(),
601            },
602            funs,
603            ctx,
604        )
605        .await;
606
607        let set_cate_id = add_req.set_cate_id.clone();
608        if let Ok(account) = IamAccountServ::get_item(add_req.rel_rbum_item_id.clone().as_str(), &IamAccountFilterReq::default(), funs, ctx).await {
609            let _ = IamLogClient::add_ctx_task(
610                LogParamTag::IamOrg,
611                Some(set_cate_id.clone()),
612                format!("添加部门人员{}", account.name.clone()),
613                Some("AddAccount".to_string()),
614                ctx,
615            )
616            .await;
617            let _ = IamSearchClient::async_add_or_modify_account_search(&add_req.rel_rbum_item_id, Box::new(true), "", funs, ctx).await;
618            IamStatsClient::async_org_fact_record_load(set_cate_id.clone(), funs, ctx).await?;
619        }
620
621        result
622    }
623
624    pub async fn modify_set_item(set_item_id: &str, modify_req: &mut RbumSetItemModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
625        RbumSetItemServ::modify_rbum(set_item_id, modify_req, funs, ctx).await
626    }
627
628    pub async fn delete_set_item(set_item_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<u64> {
629        let item: RbumSetItemDetailResp = RbumSetItemServ::get_rbum(
630            set_item_id,
631            &RbumSetItemFilterReq {
632                basic: Default::default(),
633                rel_rbum_item_disabled: Some(false),
634                rel_rbum_item_can_not_exist: Some(true),
635                ..Default::default()
636            },
637            funs,
638            ctx,
639        )
640        .await?;
641
642        let result = RbumSetItemServ::delete_rbum(set_item_id, funs, ctx).await;
643
644        if result.is_ok() && item.rel_rbum_item_kind_id == funs.iam_basic_kind_account_id() {
645            if let Some(cate_id) = item.rel_rbum_set_cate_id.clone() {
646                IamStatsClient::async_org_fact_record_load(cate_id, funs, ctx).await?;
647            }
648            if let Ok(account) = IamAccountServ::get_item(item.rel_rbum_item_id.clone().as_str(), &IamAccountFilterReq::default(), funs, ctx).await {
649                let _ = IamLogClient::add_ctx_task(
650                    LogParamTag::IamOrg,
651                    Some(item.rel_rbum_set_cate_id.unwrap_or_default().clone()),
652                    format!("移除部门人员{}", account.name.clone()),
653                    Some("RemoveAccount".to_string()),
654                    ctx,
655                )
656                .await;
657                let _ = IamSearchClient::async_add_or_modify_account_search(&item.rel_rbum_item_id, Box::new(true), "", funs, ctx).await;
658            }
659        }
660
661        result
662    }
663
664    pub async fn find_set_cate_name(filter_req: &RbumSetCateFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Vec<String>> {
665        RbumSetCateServ::find_detail_rbums(filter_req, None, None, funs, ctx).await.map(|r| r.into_iter().map(|r| format!("{},{}", r.id, r.name)).collect())
666    }
667
668    pub async fn paginate_set_items(
669        set_id: Option<String>,
670        set_cate_id: Option<String>,
671        item_id: Option<String>,
672        scope_level: Option<RbumScopeLevelKind>,
673        with_sub: bool,
674        table_rbum_set_cate_is_left: Option<bool>,
675        page_number: u32,
676        page_size: u32,
677        funs: &TardisFunsInst,
678        ctx: &TardisContext,
679    ) -> TardisResult<TardisPage<RbumSetItemDetailResp>> {
680        RbumSetItemServ::paginate_detail_rbums(
681            &RbumSetItemFilterReq {
682                basic: RbumBasicFilterReq {
683                    with_sub_own_paths: with_sub,
684                    ..Default::default()
685                },
686                rel_rbum_item_can_not_exist: table_rbum_set_cate_is_left,
687                rel_rbum_item_disabled: Some(false),
688                rel_rbum_set_id: set_id.clone(),
689                rel_rbum_set_cate_ids: set_cate_id.map(|r| vec![r]),
690                rel_rbum_item_ids: item_id.map(|i| vec![i]),
691                rel_rbum_item_scope_level: scope_level,
692                ..Default::default()
693            },
694            page_number,
695            page_size,
696            None,
697            None,
698            funs,
699            ctx,
700        )
701        .await
702    }
703
704    pub async fn find_set_items(
705        set_id: Option<String>,
706        set_cate_id: Option<String>,
707        item_id: Option<String>,
708        scope_level: Option<RbumScopeLevelKind>,
709        with_sub: bool,
710        table_rbum_set_cate_is_left: Option<bool>,
711        funs: &TardisFunsInst,
712        ctx: &TardisContext,
713    ) -> TardisResult<Vec<RbumSetItemDetailResp>> {
714        RbumSetItemServ::find_detail_rbums(
715            &RbumSetItemFilterReq {
716                basic: RbumBasicFilterReq {
717                    with_sub_own_paths: with_sub,
718                    ..Default::default()
719                },
720                rel_rbum_item_can_not_exist: table_rbum_set_cate_is_left,
721                rel_rbum_item_disabled: Some(false),
722                rel_rbum_set_id: set_id.clone(),
723                rel_rbum_set_cate_ids: set_cate_id.map(|r| vec![r]),
724                rel_rbum_item_ids: item_id.map(|i| vec![i]),
725                rel_rbum_item_scope_level: scope_level,
726                ..Default::default()
727            },
728            None,
729            None,
730            funs,
731            ctx,
732        )
733        .await
734    }
735    /// 和find_set_items的区别是,对set_cate_id为None时候的处理不同
736    pub async fn find_set_items_with_none_set_cate_id(
737        set_id: Option<String>,
738        set_cate_id: Option<String>,
739        item_id: Option<String>,
740        with_sub: bool,
741        funs: &TardisFunsInst,
742        ctx: &TardisContext,
743    ) -> TardisResult<Vec<RbumSetItemDetailResp>> {
744        if set_cate_id.is_none() {
745            RbumSetItemServ::find_detail_rbums(
746                &RbumSetItemFilterReq {
747                    basic: RbumBasicFilterReq {
748                        with_sub_own_paths: with_sub,
749                        ..Default::default()
750                    },
751                    rel_rbum_item_disabled: Some(false),
752                    rel_rbum_set_id: set_id.clone(),
753                    rel_rbum_set_item_cate_code: Some("".to_string()),
754                    rel_rbum_item_can_not_exist: Some(true),
755                    rel_rbum_item_ids: item_id.map(|i| vec![i]),
756                    ..Default::default()
757                },
758                None,
759                None,
760                funs,
761                ctx,
762            )
763            .await
764        } else {
765            Self::find_set_items(set_id, set_cate_id, item_id, None, with_sub, None, funs, ctx).await
766        }
767    }
768
769    pub async fn find_set_paths(set_item_id: &str, set_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Vec<Vec<RbumSetPathResp>>> {
770        RbumSetItemServ::find_set_paths(set_item_id, set_id, funs, ctx).await
771    }
772
773    pub async fn find_flat_set_items(set_id: &str, item_id: &str, with_sub: bool, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<HashMap<String, String>> {
774        let items = Self::find_set_items(Some(set_id.to_string()), None, Some(item_id.to_string()), None, with_sub, None, funs, ctx).await?;
775        let items = items
776            .into_iter()
777            .map(|item| {
778                (
779                    format!("{}{}{}", item.rel_rbum_set_id, SET_AND_ITEM_SPLIT_FLAG, item.rel_rbum_set_cate_sys_code.unwrap_or_default()),
780                    item.rel_rbum_set_cate_name.unwrap_or_default(),
781                )
782            })
783            .collect();
784        Ok(items)
785    }
786
787    pub async fn check_scope(app_id: &str, account_id: &str, set_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<bool> {
788        RbumSetItemServ::check_a_is_parent_or_sibling_of_b(account_id, app_id, set_id, funs, ctx).await
789    }
790
791    pub async fn cut_tree_to_new_set<'a>(
792        from_tree: &'a RbumSetTreeResp,
793        target_set_id: &'a str,
794        old_pid: Option<String>,
795        target_pid: Option<String>,
796        funs: &'a TardisFunsInst,
797        from_ctx: &'a TardisContext,
798        target_ctx: &'a TardisContext,
799    ) -> TardisResult<()> {
800        Self::copy_tree_to_new_set(from_tree, target_set_id, old_pid.clone(), target_pid, funs, target_ctx).await?;
801        Self::delete_tree(from_tree, old_pid, funs, from_ctx).await
802    }
803
804    pub async fn delete_tree<'a>(delete_tree: &'a RbumSetTreeResp, pid: Option<String>, funs: &'a TardisFunsInst, ctx: &'a TardisContext) -> TardisResult<()> {
805        let mut stack = vec![];
806        stack.push(pid.clone());
807        let mut cate_vec = delete_tree.main.to_owned();
808        let mut cate_item_vec = if let Some(ext) = &delete_tree.ext { ext.items.to_owned() } else { HashMap::new() };
809        while !stack.is_empty() {
810            let mut loop_cate_vec = cate_vec.clone();
811            let loop_pid = stack.pop().unwrap_or_default();
812            loop_cate_vec.retain(|cate| cate.pid == loop_pid);
813            //have sub tree?
814            let have_next_node = !loop_cate_vec.is_empty();
815            if have_next_node && loop_pid.is_some() {
816                stack.push(loop_pid.clone());
817            }
818            for r in loop_cate_vec {
819                if let Some(set_items) = cate_item_vec.get(&r.id) {
820                    for set_item in set_items {
821                        Self::delete_set_item(&set_item.id, funs, ctx).await?;
822                    }
823                    cate_item_vec.insert(r.id.clone(), vec![]);
824                }
825
826                stack.push(Some(r.id.clone()));
827            }
828            if !have_next_node && loop_pid.is_some() && loop_pid != pid {
829                Self::delete_set_cate(&loop_pid.clone().unwrap_or_default(), funs, ctx).await?;
830                cate_vec.retain(|c| c.id != loop_pid.clone().unwrap_or_default());
831            }
832        }
833
834        Ok(())
835    }
836
837    pub async fn copy_tree_to_new_set<'a>(
838        tree: &'a RbumSetTreeResp,
839        target_set_id: &'a str,
840        old_pid: Option<String>,
841        target_pid: Option<String>,
842        funs: &'a TardisFunsInst,
843        target_ctx: &'a TardisContext,
844    ) -> TardisResult<()> {
845        let mut old_stack = vec![];
846        let mut target_stack = vec![];
847        old_stack.push(old_pid.clone());
848        target_stack.push(target_pid);
849
850        let cate_vec = tree.main.to_owned();
851        let cate_item_vec = if let Some(ext) = &tree.ext { ext.items.to_owned() } else { HashMap::new() };
852
853        while !old_stack.is_empty() {
854            let mut loop_cate_vec = cate_vec.clone();
855            let loop_pid = old_stack.pop().unwrap_or_default();
856            let loop_target_pid = target_stack.pop().unwrap_or_default();
857            loop_cate_vec.retain(|cate| cate.pid == loop_pid);
858            for r in loop_cate_vec {
859                let new_cate_id = Self::add_set_cate(
860                    target_set_id,
861                    &IamSetCateAddReq {
862                        name: TrimString(r.name.clone()),
863                        scope_level: Some(r.scope_level.clone()),
864                        bus_code: None,
865                        icon: None,
866                        sort: None,
867                        ext: None,
868                        rbum_parent_cate_id: loop_target_pid.clone(),
869                    },
870                    funs,
871                    target_ctx,
872                )
873                .await?;
874                old_stack.push(Some(r.id.clone()));
875                target_stack.push(Some(new_cate_id.clone()));
876                if let Some(set_items) = cate_item_vec.get(&r.id) {
877                    let mut sort = 1;
878                    for set_item in set_items {
879                        //只有全局账号可以跨租户
880                        if set_item.rel_rbum_item_scope_level != RbumScopeLevelKind::Root {
881                            continue;
882                        }
883                        Self::add_set_item(
884                            &IamSetItemAddReq {
885                                set_id: target_set_id.to_string(),
886                                set_cate_id: new_cate_id.clone(),
887                                sort,
888                                rel_rbum_item_id: set_item.rel_rbum_item_id.clone(),
889                            },
890                            funs,
891                            target_ctx,
892                        )
893                        .await?;
894                        sort += 1;
895                    }
896                }
897            }
898        }
899        Ok(())
900    }
901}