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 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 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 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 result_main.push(r);
448 }
449 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 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 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 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 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}