bios_iam/basic/serv/
iam_res_serv.rs

1use std::collections::{HashMap, HashSet};
2
3use async_trait::async_trait;
4use bios_basic::rbum::rbum_config::RbumConfigApi;
5use bios_basic::rbum::rbum_enumeration::RbumSetCateLevelQueryKind;
6use bios_basic::rbum::serv::rbum_crud_serv::RbumCrudOperation;
7use bios_basic::rbum::serv::rbum_set_serv::{RbumSetCateServ, RbumSetItemServ};
8use itertools::Itertools;
9use ldap3::log::warn;
10use tardis::basic::dto::TardisContext;
11use tardis::basic::field::TrimString;
12use tardis::basic::result::TardisResult;
13use tardis::db::sea_orm::sea_query::{Expr, SelectStatement};
14use tardis::db::sea_orm::*;
15use tardis::futures::future::BoxFuture;
16use tardis::futures::FutureExt;
17use tardis::futures_util::future::join_all;
18use tardis::web::web_resp::TardisPage;
19use tardis::TardisFunsInst;
20
21use bios_basic::rbum::dto::rbum_filer_dto::{RbumBasicFilterReq, RbumSetItemFilterReq};
22use bios_basic::rbum::dto::rbum_item_dto::{RbumItemKernelAddReq, RbumItemKernelModifyReq};
23use bios_basic::rbum::dto::rbum_rel_dto::RbumRelBoneResp;
24use bios_basic::rbum::dto::rbum_set_cate_dto::RbumSetCateAddReq;
25use bios_basic::rbum::serv::rbum_item_serv::RbumItemCrudOperation;
26
27use crate::basic::domain::iam_res;
28use crate::basic::dto::iam_filer_dto::IamResFilterReq;
29use crate::basic::dto::iam_res_dto::{IamResAddReq, IamResAggAddReq, IamResDetailResp, IamResModifyReq, IamResSummaryResp, JsonMenu, MenuItem};
30use crate::basic::dto::iam_set_dto::{IamSetItemAddReq, IamSetItemAggAddReq};
31use crate::basic::serv::iam_key_cache_serv::IamResCacheServ;
32use crate::basic::serv::iam_rel_serv::IamRelServ;
33use crate::basic::serv::iam_set_serv::IamSetServ;
34use crate::iam_config::IamBasicInfoManager;
35use crate::iam_constants;
36use crate::iam_enumeration::{IamRelKind, IamResKind, IamSetCateKind};
37
38use super::clients::iam_log_client::{IamLogClient, LogParamTag};
39use super::iam_account_serv::IamAccountServ;
40use super::iam_cert_serv::IamCertServ;
41use super::iam_key_cache_serv::IamCacheResRelAddOrModifyReq;
42use super::iam_role_serv::IamRoleServ;
43
44pub struct IamResServ;
45
46pub struct IamMenuServ;
47
48#[async_trait]
49impl RbumItemCrudOperation<iam_res::ActiveModel, IamResAddReq, IamResModifyReq, IamResSummaryResp, IamResDetailResp, IamResFilterReq> for IamResServ {
50    fn get_ext_table_name() -> &'static str {
51        iam_res::Entity.table_name()
52    }
53
54    fn get_rbum_kind_id() -> Option<String> {
55        Some(IamBasicInfoManager::get_config(|conf| conf.kind_res_id.clone()))
56    }
57
58    fn get_rbum_domain_id() -> Option<String> {
59        Some(IamBasicInfoManager::get_config(|conf| conf.domain_iam_id.clone()))
60    }
61
62    async fn package_item_add(add_req: &IamResAddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<RbumItemKernelAddReq> {
63        Ok(RbumItemKernelAddReq {
64            code: Some(add_req.code.clone()),
65            name: add_req.name.clone(),
66            disabled: add_req.disabled,
67            scope_level: add_req.scope_level.clone(),
68            ..Default::default()
69        })
70    }
71
72    async fn package_ext_add(id: &str, add_req: &IamResAddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<iam_res::ActiveModel> {
73        Ok(iam_res::ActiveModel {
74            id: Set(id.to_string()),
75            kind: Set(add_req.kind.to_int()),
76            icon: Set(add_req.icon.as_ref().unwrap_or(&"".to_string()).to_string()),
77            sort: Set(add_req.sort.unwrap_or(0)),
78            method: Set(add_req.method.as_ref().unwrap_or(&TrimString("*".to_string())).to_string()),
79            hide: Set(add_req.hide.unwrap_or(false)),
80            action: Set(add_req.action.as_ref().unwrap_or(&"".to_string()).to_string()),
81            crypto_req: Set(add_req.crypto_req.unwrap_or(false)),
82            crypto_resp: Set(add_req.crypto_resp.unwrap_or(false)),
83            double_auth: Set(add_req.double_auth.unwrap_or(false)),
84            double_auth_msg: Set(add_req.double_auth_msg.as_ref().unwrap_or(&"".to_string()).to_string()),
85            ext: Set(add_req.ext.as_ref().unwrap_or(&"".to_string()).to_string()),
86            need_login: Set(add_req.need_login.unwrap_or(false)),
87            ..Default::default()
88        })
89    }
90
91    async fn before_add_item(add_req: &mut IamResAddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<()> {
92        add_req.encoding();
93        Ok(())
94    }
95
96    async fn after_add_item(id: &str, _: &mut IamResAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
97        let res = Self::peek_item(
98            id,
99            &IamResFilterReq {
100                basic: RbumBasicFilterReq {
101                    with_sub_own_paths: true,
102                    ..Default::default()
103                },
104                ..Default::default()
105            },
106            funs,
107            ctx,
108        )
109        .await?;
110        if res.kind == IamResKind::Api {
111            IamResCacheServ::add_res(&res.code, &res.method, res.crypto_req, res.crypto_resp, res.double_auth, res.need_login, funs).await?;
112        }
113        let (op_describe, op_kind) = match res.kind {
114            IamResKind::Menu => ("添加目录页面".to_string(), "AddContentPageaspersonal".to_string()),
115            IamResKind::Api => ("添加API".to_string(), "AddApi".to_string()),
116            IamResKind::Ele => ("添加目录页面按钮".to_string(), "AddContentPageButton".to_string()),
117            IamResKind::Product => ("添加产品".to_string(), "AddProduct".to_string()),
118            IamResKind::Spec => ("添加产品规格".to_string(), "AddSpecification".to_string()),
119        };
120        if !op_describe.is_empty() {
121            let _ = IamLogClient::add_ctx_task(LogParamTag::IamRes, Some(id.to_string()), op_describe, Some(op_kind), ctx).await;
122        }
123
124        Ok(())
125    }
126
127    async fn before_modify_item(id: &str, modify_req: &mut IamResModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
128        if modify_req.code.is_some() || modify_req.method.is_some() {
129            let item = Self::get_item(id, &IamResFilterReq::default(), funs, ctx).await?;
130            modify_req.encoding(
131                item.kind.clone(),
132                if let Some(method) = &modify_req.method {
133                    method.to_string()
134                } else {
135                    item.method.clone()
136                },
137            );
138        }
139        Ok(())
140    }
141
142    async fn package_item_modify(_: &str, modify_req: &IamResModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<Option<RbumItemKernelModifyReq>> {
143        if modify_req.name.is_none() && modify_req.scope_level.is_none() && modify_req.disabled.is_none() && modify_req.code.is_none() {
144            return Ok(None);
145        }
146        Ok(Some(RbumItemKernelModifyReq {
147            code: modify_req.code.clone(),
148            name: modify_req.name.clone(),
149            scope_level: modify_req.scope_level.clone(),
150            disabled: modify_req.disabled,
151        }))
152    }
153
154    async fn package_ext_modify(id: &str, modify_req: &IamResModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<Option<iam_res::ActiveModel>> {
155        if modify_req.icon.is_none()
156            && modify_req.sort.is_none()
157            && modify_req.hide.is_none()
158            && modify_req.action.is_none()
159            && modify_req.method.is_none()
160            && modify_req.crypto_req.is_none()
161            && modify_req.crypto_resp.is_none()
162            && modify_req.double_auth.is_none()
163        {
164            return Ok(None);
165        }
166        let mut iam_res = iam_res::ActiveModel {
167            id: Set(id.to_string()),
168            ..Default::default()
169        };
170        if let Some(icon) = &modify_req.icon {
171            iam_res.icon = Set(icon.to_string());
172        }
173        if let Some(sort) = modify_req.sort {
174            iam_res.sort = Set(sort);
175        }
176        if let Some(hide) = modify_req.hide {
177            iam_res.hide = Set(hide);
178        }
179        if let Some(action) = &modify_req.action {
180            iam_res.action = Set(action.to_string());
181        }
182        if let Some(method) = &modify_req.method {
183            iam_res.method = Set(method.to_string());
184        }
185        if let Some(crypto_req) = modify_req.crypto_req {
186            iam_res.crypto_req = Set(crypto_req);
187        }
188        if let Some(crypto_resp) = modify_req.crypto_resp {
189            iam_res.crypto_resp = Set(crypto_resp);
190        }
191        if let Some(double_auth) = modify_req.double_auth {
192            iam_res.double_auth = Set(double_auth);
193        }
194        if let Some(double_auth_msg) = &modify_req.double_auth_msg {
195            iam_res.double_auth_msg = Set(double_auth_msg.to_string());
196        }
197        if let Some(need_login) = modify_req.need_login {
198            iam_res.need_login = Set(need_login);
199        }
200        if let Some(ext) = &modify_req.ext {
201            iam_res.ext = Set(ext.to_string());
202        }
203        Ok(Some(iam_res))
204    }
205
206    async fn after_modify_item(id: &str, modify_req: &mut IamResModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
207        let res = Self::peek_item(
208            id,
209            &IamResFilterReq {
210                basic: RbumBasicFilterReq {
211                    with_sub_own_paths: true,
212                    ..Default::default()
213                },
214                ..Default::default()
215            },
216            funs,
217            ctx,
218        )
219        .await?;
220        if modify_req.crypto_req.is_some() || modify_req.crypto_resp.is_some() || modify_req.double_auth.is_some() || modify_req.method.is_some() {
221            IamResCacheServ::add_or_modify_res_rel(
222                &res.code,
223                &res.method,
224                &IamCacheResRelAddOrModifyReq {
225                    st: None,
226                    et: None,
227                    accounts: vec![],
228                    roles: vec![],
229                    groups: vec![],
230                    apps: vec![],
231                    tenants: vec![],
232                    aks: vec![],
233                    need_crypto_req: modify_req.crypto_req,
234                    need_crypto_resp: modify_req.crypto_resp,
235                    need_double_auth: modify_req.double_auth,
236                    need_login: modify_req.need_login,
237                },
238                funs,
239            )
240            .await?;
241        }
242        if let Some(disabled) = modify_req.disabled {
243            if res.kind == IamResKind::Api {
244                if disabled {
245                    IamResCacheServ::delete_res(&res.code, &res.method, funs).await?;
246                } else {
247                    IamResCacheServ::add_res(&res.code, &res.method, res.crypto_req, res.crypto_resp, res.double_auth, res.need_login, funs).await?;
248                }
249            }
250        }
251        if let Some(bind_api_res) = &modify_req.bind_api_res {
252            let old_api_res = IamResServ::find_to_simple_rel_roles(&IamRelKind::IamResApi, id, None, None, funs, ctx).await?.into_iter().map(|rel| rel.rel_id).collect_vec();
253            if old_api_res != *bind_api_res {
254                for del_to_item_id in old_api_res {
255                    IamRelServ::delete_simple_rel(&IamRelKind::IamResApi, &del_to_item_id, id, funs, ctx).await?;
256                }
257                for add_to_item_id in bind_api_res {
258                    IamRelServ::add_simple_rel(&IamRelKind::IamResApi, add_to_item_id, id, None, None, false, false, funs, ctx).await?;
259                }
260            }
261        }
262        let (op_describe, op_kind) = match res.kind {
263            IamResKind::Menu => ("编辑目录页面".to_string(), "ModifyContentPage".to_string()),
264            IamResKind::Api => ("编辑API".to_string(), "ModifyApi".to_string()),
265            IamResKind::Ele => ("编辑操作".to_string(), "ModifyEle".to_string()),
266            IamResKind::Product => ("编辑产品".to_string(), "ModifyProduct".to_string()),
267            IamResKind::Spec => ("编辑产品规格".to_string(), "ModifySpecification".to_string()),
268        };
269        if !op_describe.is_empty() {
270            let _ = IamLogClient::add_ctx_task(LogParamTag::IamRes, Some(id.to_string()), op_describe, Some(op_kind), ctx).await;
271        }
272
273        Ok(())
274    }
275
276    async fn before_delete_item(id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<IamResDetailResp>> {
277        Ok(Some(
278            Self::get_item(
279                id,
280                &IamResFilterReq {
281                    basic: RbumBasicFilterReq {
282                        with_sub_own_paths: true,
283                        ..Default::default()
284                    },
285                    ..Default::default()
286                },
287                funs,
288                ctx,
289            )
290            .await?,
291        ))
292    }
293
294    async fn after_delete_item(_: &str, deleted_item: &Option<IamResDetailResp>, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
295        if let Some(deleted_item) = deleted_item {
296            if deleted_item.kind == IamResKind::Api {
297                IamResCacheServ::delete_res(&deleted_item.code, &deleted_item.method, funs).await?;
298            }
299            let (op_describe, op_kind) = match deleted_item.kind {
300                IamResKind::Menu => ("删除目录页面".to_string(), "DeleteContentPageAsPersonal".to_string()),
301                IamResKind::Api => ("删除API".to_string(), "DeleteApi".to_string()),
302                IamResKind::Ele => ("移除目录页面按钮".to_string(), "RemoveContentPageButton".to_string()),
303                IamResKind::Product => ("移除产品".to_string(), "RemoveProduct".to_string()),
304                IamResKind::Spec => ("移除产品规格".to_string(), "RemoveSpecification".to_string()),
305            };
306            if !op_describe.is_empty() {
307                let _ = IamLogClient::add_ctx_task(LogParamTag::IamRes, Some(deleted_item.id.to_string()), op_describe, Some(op_kind), ctx).await;
308            }
309
310            Ok(())
311        } else {
312            Err(funs.err().not_found(&Self::get_obj_name(), "delete", "not found resource", "404-iam-res-not-exist"))
313        }
314    }
315
316    async fn package_ext_query(query: &mut SelectStatement, _: bool, filter: &IamResFilterReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<()> {
317        query.column((iam_res::Entity, iam_res::Column::Kind));
318        query.column((iam_res::Entity, iam_res::Column::Icon));
319        query.column((iam_res::Entity, iam_res::Column::Sort));
320        query.column((iam_res::Entity, iam_res::Column::Method));
321        query.column((iam_res::Entity, iam_res::Column::Hide));
322        query.column((iam_res::Entity, iam_res::Column::Action));
323        query.column((iam_res::Entity, iam_res::Column::CryptoReq));
324        query.column((iam_res::Entity, iam_res::Column::CryptoResp));
325        query.column((iam_res::Entity, iam_res::Column::DoubleAuth));
326        query.column((iam_res::Entity, iam_res::Column::DoubleAuthMsg));
327        query.column((iam_res::Entity, iam_res::Column::NeedLogin));
328        query.column((iam_res::Entity, iam_res::Column::Ext));
329        if let Some(kind) = &filter.kind {
330            query.and_where(Expr::col(iam_res::Column::Kind).eq(kind.to_int()));
331        }
332        Ok(())
333    }
334
335    async fn peek_item(id: &str, filter: &IamResFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<IamResSummaryResp> {
336        let res = Self::do_peek_item(id, filter, funs, ctx).await?;
337        Ok(res.decoding())
338    }
339
340    async fn get_item(id: &str, filter: &IamResFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<IamResDetailResp> {
341        let res = Self::do_get_item(id, filter, funs, ctx).await?;
342        Ok(res.decoding())
343    }
344
345    async fn paginate_items(
346        filter: &IamResFilterReq,
347        page_number: u32,
348        page_size: u32,
349        desc_sort_by_create: Option<bool>,
350        desc_sort_by_update: Option<bool>,
351        funs: &TardisFunsInst,
352        ctx: &TardisContext,
353    ) -> TardisResult<TardisPage<IamResSummaryResp>> {
354        let mut res = Self::do_paginate_items(filter, page_number, page_size, desc_sort_by_create, desc_sort_by_update, funs, ctx).await?;
355        res.records = res.records.into_iter().map(|r| r.decoding()).collect();
356        Ok(res)
357    }
358
359    async fn paginate_detail_items(
360        filter: &IamResFilterReq,
361        page_number: u32,
362        page_size: u32,
363        desc_sort_by_create: Option<bool>,
364        desc_sort_by_update: Option<bool>,
365        funs: &TardisFunsInst,
366        ctx: &TardisContext,
367    ) -> TardisResult<TardisPage<IamResDetailResp>> {
368        let mut res = Self::do_paginate_detail_items(filter, page_number, page_size, desc_sort_by_create, desc_sort_by_update, funs, ctx).await?;
369        res.records = res.records.into_iter().map(|r| r.decoding()).collect();
370        Ok(res)
371    }
372
373    async fn find_one_item(filter: &IamResFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<IamResSummaryResp>> {
374        let res = Self::do_find_one_item(filter, funs, ctx).await?;
375        if let Some(r) = res {
376            Ok(Some(r.decoding()))
377        } else {
378            Ok(None)
379        }
380    }
381
382    async fn find_items(
383        filter: &IamResFilterReq,
384        desc_sort_by_create: Option<bool>,
385        desc_sort_by_update: Option<bool>,
386        funs: &TardisFunsInst,
387        ctx: &TardisContext,
388    ) -> TardisResult<Vec<IamResSummaryResp>> {
389        let res = Self::do_find_items(filter, desc_sort_by_create, desc_sort_by_update, funs, ctx).await?;
390        Ok(res.into_iter().map(|r| r.decoding()).collect())
391    }
392
393    async fn find_one_detail_item(filter: &IamResFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<IamResDetailResp>> {
394        let res = Self::do_find_one_detail_item(filter, funs, ctx).await?;
395        if let Some(r) = res {
396            Ok(Some(r.decoding()))
397        } else {
398            Ok(None)
399        }
400    }
401
402    async fn find_detail_items(
403        filter: &IamResFilterReq,
404        desc_sort_by_create: Option<bool>,
405        desc_sort_by_update: Option<bool>,
406        funs: &TardisFunsInst,
407        ctx: &TardisContext,
408    ) -> TardisResult<Vec<IamResDetailResp>> {
409        let res = Self::do_find_detail_items(filter, desc_sort_by_create, desc_sort_by_update, funs, ctx).await?;
410        Ok(res.into_iter().map(|r| r.decoding()).collect())
411    }
412}
413
414impl IamResServ {
415    pub async fn find_from_id_rel_roles(
416        rel_kind: &IamRelKind,
417        with_sub: bool,
418        res_id: &str,
419        desc_by_create: Option<bool>,
420        desc_by_update: Option<bool>,
421        funs: &TardisFunsInst,
422        ctx: &TardisContext,
423    ) -> TardisResult<Vec<String>> {
424        IamRelServ::find_from_id_rels(rel_kind, with_sub, res_id, desc_by_create, desc_by_update, funs, ctx).await
425    }
426
427    pub async fn find_to_id_rel_roles(
428        rel_kind: &IamRelKind,
429        res_id: &str,
430        desc_by_create: Option<bool>,
431        desc_by_update: Option<bool>,
432        funs: &TardisFunsInst,
433        ctx: &TardisContext,
434    ) -> TardisResult<Vec<String>> {
435        IamRelServ::find_to_id_rels(rel_kind, res_id, desc_by_create, desc_by_update, funs, ctx).await
436    }
437
438    pub async fn find_from_simple_rel_roles(
439        rel_kind: &IamRelKind,
440        with_sub: bool,
441        res_id: &str,
442        desc_by_create: Option<bool>,
443        desc_by_update: Option<bool>,
444        funs: &TardisFunsInst,
445        ctx: &TardisContext,
446    ) -> TardisResult<Vec<RbumRelBoneResp>> {
447        IamRelServ::find_from_simple_rels(rel_kind, with_sub, res_id, desc_by_create, desc_by_update, funs, ctx).await
448    }
449
450    pub async fn find_to_simple_rel_roles(
451        rel_kind: &IamRelKind,
452        res_id: &str,
453        desc_by_create: Option<bool>,
454        desc_by_update: Option<bool>,
455        funs: &TardisFunsInst,
456        ctx: &TardisContext,
457    ) -> TardisResult<Vec<RbumRelBoneResp>> {
458        IamRelServ::find_to_simple_rels(rel_kind, res_id, desc_by_create, desc_by_update, funs, ctx).await
459    }
460
461    pub async fn find_rel_res(
462        rel_kind: &IamRelKind,
463        res_id: &str,
464        desc_by_create: Option<bool>,
465        desc_by_update: Option<bool>,
466        funs: &TardisFunsInst,
467        ctx: &TardisContext,
468    ) -> TardisResult<Vec<IamResDetailResp>> {
469        Ok(join_all(
470            Self::find_to_simple_rel_roles(rel_kind, res_id, desc_by_create, desc_by_update, funs, ctx)
471                .await?
472                .into_iter()
473                .map(|rel| async move { Self::get_item(&rel.rel_id, &IamResFilterReq::default(), funs, ctx).await.unwrap() })
474                .collect_vec(),
475        )
476        .await)
477    }
478
479    pub async fn find_to_multi_rel_roles(
480        rel_kind: &IamRelKind,
481        res_id: Vec<&str>,
482        funs: &TardisFunsInst,
483        ctx: &TardisContext,
484    ) -> TardisResult<HashMap<String, Vec<IamResDetailResp>>> {
485        let mut result = HashMap::new();
486        for id in res_id {
487            let res_list = join_all(
488                Self::find_to_simple_rel_roles(rel_kind, id, None, None, funs, ctx)
489                    .await?
490                    .into_iter()
491                    .map(|rel| async move { Self::get_item(&rel.rel_id, &IamResFilterReq::default(), funs, ctx).await.unwrap() })
492                    .collect_vec(),
493            )
494            .await;
495            result.insert(id.to_string(), res_list);
496        }
497        Ok(result)
498    }
499
500    pub async fn paginate_from_simple_rel_roles(
501        rel_kind: &IamRelKind,
502        res_id: &str,
503        with_sub: bool,
504        page_number: u32,
505        page_size: u32,
506        desc_by_create: Option<bool>,
507        desc_by_update: Option<bool>,
508        funs: &TardisFunsInst,
509        ctx: &TardisContext,
510    ) -> TardisResult<TardisPage<RbumRelBoneResp>> {
511        IamRelServ::paginate_from_simple_rels(rel_kind, with_sub, res_id, page_number, page_size, desc_by_create, desc_by_update, funs, ctx).await
512    }
513
514    pub async fn paginate_to_simple_rel_roles(
515        rel_kind: &IamRelKind,
516        res_id: &str,
517        page_number: u32,
518        page_size: u32,
519        desc_by_create: Option<bool>,
520        desc_by_update: Option<bool>,
521        funs: &TardisFunsInst,
522        ctx: &TardisContext,
523    ) -> TardisResult<TardisPage<RbumRelBoneResp>> {
524        IamRelServ::paginate_to_simple_rels(rel_kind, res_id, page_number, page_size, desc_by_create, desc_by_update, funs, ctx).await
525    }
526
527    pub async fn add_res_agg(add_req: &mut IamResAggAddReq, set_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
528        if add_req.res.kind == IamResKind::Menu {
529            let set_cate_sys_code_node_len = funs.rbum_conf_set_cate_sys_code_node_len();
530            // TODO: check menu cate
531            let menu_ids = &Self::find_id_items(
532                &IamResFilterReq {
533                    basic: RbumBasicFilterReq {
534                        with_sub_own_paths: true,
535                        ..Default::default()
536                    },
537                    kind: Some(IamResKind::Menu),
538                    ..Default::default()
539                },
540                None,
541                None,
542                funs,
543                ctx,
544            )
545            .await?;
546            let count = RbumSetItemServ::count_rbums(
547                &RbumSetItemFilterReq {
548                    basic: RbumBasicFilterReq {
549                        with_sub_own_paths: true,
550                        ..Default::default()
551                    },
552                    sys_code_query_kind: Some(RbumSetCateLevelQueryKind::Sub),
553                    rel_rbum_set_cate_sys_codes: Some(vec![String::from_utf8(vec![b'0'; set_cate_sys_code_node_len])?]),
554                    rel_rbum_item_ids: Some(menu_ids.iter().map(|id| id.to_string()).collect()),
555                    rel_rbum_set_id: Some(set_id.to_string()),
556                    rel_rbum_set_cate_ids: Some(vec![add_req.set.set_cate_id.to_string()]),
557                    ..Default::default()
558                },
559                funs,
560                ctx,
561            )
562            .await?;
563            if count > 0 {
564                return Err(funs.err().bad_request(&Self::get_obj_name(), "add", "conflict error", "409-iam-cate-menu-conflict"));
565            }
566        }
567        let res_id = Self::add_item(&mut add_req.res, funs, ctx).await?;
568        IamSetServ::add_set_item(
569            &IamSetItemAddReq {
570                set_id: set_id.to_string(),
571                set_cate_id: add_req.set.set_cate_id.to_string(),
572                sort: 0,
573                rel_rbum_item_id: res_id.clone(),
574            },
575            funs,
576            ctx,
577        )
578        .await?;
579        if let Some(bind_api_res) = &add_req.res.bind_api_res {
580            for api_id in bind_api_res {
581                IamRelServ::add_simple_rel(&IamRelKind::IamResApi, api_id, &res_id, None, None, false, false, funs, ctx).await?;
582            }
583        }
584        Ok(res_id)
585    }
586
587    pub async fn get_res_by_app_code(
588        app_ids: Vec<String>,
589        res_codes: Option<Vec<String>>,
590        funs: &TardisFunsInst,
591        ctx: &TardisContext,
592    ) -> TardisResult<HashMap<String, Vec<IamResSummaryResp>>> {
593        let raw_roles = IamAccountServ::find_simple_rel_roles(&ctx.owner, true, Some(true), None, funs, ctx).await?;
594        let mut roles: Vec<RbumRelBoneResp> = vec![];
595        let mut result = HashMap::new();
596        for role in raw_roles {
597            if !IamRoleServ::is_disabled(&role.rel_id, funs).await? {
598                roles.push(role)
599            }
600        }
601        let global_ctx = IamCertServ::use_sys_ctx_unsafe(ctx.clone())?;
602        for app_id in app_ids {
603            let mut res_ids = HashSet::new();
604            let app_ctx = IamCertServ::try_use_app_ctx(ctx.clone(), Some(app_id.clone()))?;
605            let app_role_ids =
606                roles.iter().filter(|r| r.rel_own_paths == app_ctx.own_paths || r.rel_own_paths == ctx.own_paths).map(|r| r.rel_id.to_string()).collect::<Vec<String>>();
607            // TODO default empty res
608            res_ids.insert("".to_string());
609            for role_id in app_role_ids {
610                let rel_res_ids = IamRelServ::find_to_id_rels(&IamRelKind::IamResRole, &role_id, None, None, funs, &global_ctx).await?;
611                res_ids.extend(rel_res_ids.into_iter());
612                if role_id.contains(':') {
613                    let extend_role_id = role_id.split(':').collect::<Vec<&str>>()[0];
614                    let rel_res_ids = IamRelServ::find_to_id_rels(&IamRelKind::IamResRole, extend_role_id, None, None, funs, &global_ctx).await?;
615                    res_ids.extend(rel_res_ids.into_iter());
616                }
617            }
618            let res_codes = if let Some(res_codes) = &res_codes {
619                let codes = res_codes.clone();
620                Some(res_codes.iter().map(|code| format!("{}/{}/{}", IamResKind::Ele.to_int(), "*", code)).chain(codes).collect::<Vec<String>>())
621            } else {
622                None
623            };
624            let res = Self::find_items(
625                &IamResFilterReq {
626                    basic: RbumBasicFilterReq {
627                        with_sub_own_paths: true,
628                        ids: Some(res_ids.into_iter().collect()),
629                        codes: res_codes.clone(),
630                        ..Default::default()
631                    },
632                    ..Default::default()
633                },
634                None,
635                None,
636                funs,
637                ctx,
638            )
639            .await?;
640            result.insert(app_id, res);
641        }
642        Ok(result)
643    }
644
645    pub async fn delete_res(id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<u64> {
646        let item_detail = Self::get_item(
647            id,
648            &IamResFilterReq {
649                basic: RbumBasicFilterReq {
650                    with_sub_own_paths: true,
651                    ..Default::default()
652                },
653                ..Default::default()
654            },
655            funs,
656            ctx,
657        )
658        .await?;
659        if item_detail.kind == IamResKind::Ele || item_detail.kind == IamResKind::Menu {
660            let delete_api_res = IamResServ::find_to_simple_rel_roles(&IamRelKind::IamResApi, id, None, None, funs, ctx).await?.into_iter().map(|rel| rel.rel_id).collect_vec();
661            for delete_api_id in delete_api_res {
662                IamRelServ::delete_simple_rel(&IamRelKind::IamResApi, &delete_api_id, id, funs, ctx).await?;
663            }
664        }
665        Self::delete_item_with_all_rels(id, funs, ctx).await
666    }
667}
668
669impl IamMenuServ {
670    pub fn parse_menu<'a>(set_id: &'a str, parent_cate_id: &'a str, json_menu: JsonMenu, funs: &'a TardisFunsInst, ctx: &'a TardisContext) -> BoxFuture<'a, TardisResult<String>> {
671        async move {
672            let new_cate_id = Self::add_cate_menu(
673                set_id,
674                parent_cate_id,
675                &json_menu.name,
676                &json_menu.bus_code,
677                &IamSetCateKind::parse(&json_menu.ext)?,
678                funs,
679                ctx,
680            )
681            .await?;
682            if let Some(items) = json_menu.items {
683                for item in items {
684                    Self::parse_item(set_id, &new_cate_id, item, funs, ctx).await?;
685                }
686            };
687            if let Some(children_menus) = json_menu.children {
688                for children_menu in children_menus {
689                    Self::parse_menu(set_id, &new_cate_id, children_menu, funs, ctx).await?;
690                }
691            };
692            Ok(new_cate_id)
693        }
694        .boxed()
695    }
696
697    async fn add_cate_menu<'a>(
698        set_id: &str,
699        parent_cate_menu_id: &str,
700        name: &str,
701        bus_code: &str,
702        ext: &IamSetCateKind,
703        funs: &TardisFunsInst,
704        ctx: &TardisContext,
705    ) -> TardisResult<String> {
706        RbumSetCateServ::add_rbum(
707            &mut RbumSetCateAddReq {
708                name: TrimString(name.to_string()),
709                bus_code: TrimString(bus_code.to_string()),
710                icon: None,
711                sort: None,
712                ext: Some(ext.to_string()),
713                rbum_parent_cate_id: Some(parent_cate_menu_id.to_string()),
714                rel_rbum_set_id: set_id.to_string(),
715                scope_level: Some(iam_constants::RBUM_SCOPE_LEVEL_GLOBAL),
716            },
717            funs,
718            ctx,
719        )
720        .await
721    }
722
723    async fn parse_item(set_id: &str, cate_menu_id: &str, item: MenuItem, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
724        let id = match &item.kind as &str {
725            "Menu" => Self::add_menu_res(set_id, cate_menu_id, &item.name, &item.code, funs, ctx).await?,
726            "Ele" => Self::add_ele_res(set_id, cate_menu_id, &item.name, &item.code, funs, ctx).await?,
727            _ => {
728                warn!("item({},{}) have unsupported kind {} !", &item.name, &item.code, &item.kind);
729                "".to_string()
730            }
731        };
732        Ok(id)
733    }
734    async fn add_menu_res<'a>(set_id: &str, cate_menu_id: &str, name: &str, code: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
735        IamResServ::add_res_agg(
736            &mut IamResAggAddReq {
737                res: IamResAddReq {
738                    code: TrimString(code.to_string()),
739                    name: TrimString(name.to_string()),
740                    kind: IamResKind::Menu,
741                    icon: None,
742                    sort: None,
743                    method: None,
744                    hide: None,
745                    action: None,
746                    scope_level: Some(iam_constants::RBUM_SCOPE_LEVEL_GLOBAL),
747                    disabled: None,
748                    crypto_req: None,
749                    crypto_resp: None,
750                    double_auth: None,
751                    double_auth_msg: None,
752                    need_login: None,
753                    bind_api_res: None,
754                    ext: None,
755                },
756                set: IamSetItemAggAddReq {
757                    set_cate_id: cate_menu_id.to_string(),
758                },
759            },
760            set_id,
761            funs,
762            ctx,
763        )
764        .await
765    }
766
767    async fn add_ele_res<'a>(set_id: &str, cate_menu_id: &str, name: &str, code: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
768        IamResServ::add_res_agg(
769            &mut IamResAggAddReq {
770                res: IamResAddReq {
771                    code: TrimString(code.to_string()),
772                    name: TrimString(name.to_string()),
773                    kind: IamResKind::Ele,
774                    icon: None,
775                    sort: None,
776                    method: None,
777                    hide: None,
778                    action: None,
779                    scope_level: Some(iam_constants::RBUM_SCOPE_LEVEL_GLOBAL),
780                    disabled: None,
781                    crypto_req: None,
782                    crypto_resp: None,
783                    double_auth: None,
784                    double_auth_msg: None,
785                    need_login: None,
786                    bind_api_res: None,
787                    ext: None,
788                },
789                set: IamSetItemAggAddReq {
790                    set_cate_id: cate_menu_id.to_string(),
791                },
792            },
793            set_id,
794            funs,
795            ctx,
796        )
797        .await
798    }
799}