bios_basic/rbum/serv/
rbum_item_serv.rs

1use std::collections::HashMap;
2
3use async_trait::async_trait;
4use serde::Serialize;
5use tardis::basic::dto::TardisContext;
6use tardis::basic::result::TardisResult;
7use tardis::db::reldb_client::{IdResp, TardisActiveModel};
8use tardis::db::sea_orm::sea_query::*;
9use tardis::db::sea_orm::*;
10use tardis::db::sea_orm::{self, IdenStatic};
11use tardis::web::poem_openapi::types::{ParseFromJSON, ToJSON};
12use tardis::web::web_resp::TardisPage;
13use tardis::{TardisFuns, TardisFunsInst};
14
15use super::rbum_crud_serv::{IdNameResp, CREATE_TIME_FIELD, ID_FIELD, UPDATE_TIME_FIELD};
16use crate::rbum::domain::{rbum_cert, rbum_cert_conf, rbum_domain, rbum_item, rbum_item_attr, rbum_kind, rbum_kind_attr, rbum_rel, rbum_set_item};
17use crate::rbum::dto::rbum_filer_dto::{
18    RbumBasicFilterReq, RbumCertConfFilterReq, RbumCertFilterReq, RbumItemAttrFilterReq, RbumItemFilterFetcher, RbumItemRelFilterReq, RbumKindAttrFilterReq, RbumKindFilterReq,
19    RbumSetItemFilterReq, RbumSetItemRelFilterReq,
20};
21use crate::rbum::dto::rbum_item_attr_dto::{RbumItemAttrAddReq, RbumItemAttrDetailResp, RbumItemAttrModifyReq, RbumItemAttrSummaryResp, RbumItemAttrsAddOrModifyReq};
22use crate::rbum::dto::rbum_item_dto::{RbumItemAddReq, RbumItemDetailResp, RbumItemKernelAddReq, RbumItemKernelModifyReq, RbumItemSummaryResp};
23use crate::rbum::dto::rbum_kind_attr_dto::RbumKindAttrSummaryResp;
24use crate::rbum::dto::rbum_rel_dto::{RbumRelAddReq, RbumRelSimpleFindReq};
25use crate::rbum::helper::rbum_event_helper;
26#[cfg(feature = "with-mq")]
27use crate::rbum::rbum_config::RbumConfigApi;
28use crate::rbum::rbum_enumeration::{RbumCertRelKind, RbumRelFromKind, RbumScopeLevelKind};
29use crate::rbum::serv::rbum_cert_serv::{RbumCertConfServ, RbumCertServ};
30#[cfg(feature = "with-mq")]
31use crate::rbum::serv::rbum_crud_serv::ID_FIELD_NAME;
32use crate::rbum::serv::rbum_crud_serv::{RbumCrudOperation, RbumCrudQueryPackage};
33use crate::rbum::serv::rbum_domain_serv::RbumDomainServ;
34use crate::rbum::serv::rbum_kind_serv::{RbumKindAttrServ, RbumKindServ};
35use crate::rbum::serv::rbum_rel_serv::RbumRelServ;
36use crate::rbum::serv::rbum_set_serv::RbumSetItemServ;
37use lazy_static::lazy_static;
38
39lazy_static! {
40    pub static ref RBUM_ITEM_TABLE: Alias = Alias::new("rbum_item");
41}
42
43pub struct RbumItemServ;
44
45pub struct RbumItemAttrServ;
46
47#[async_trait]
48impl RbumCrudOperation<rbum_item::ActiveModel, RbumItemAddReq, RbumItemKernelModifyReq, RbumItemSummaryResp, RbumItemDetailResp, RbumBasicFilterReq> for RbumItemServ {
49    fn get_table_name() -> &'static str {
50        rbum_item::Entity.table_name()
51    }
52
53    async fn before_add_rbum(add_req: &mut RbumItemAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
54        Self::check_scope(&add_req.rel_rbum_kind_id, RbumKindServ::get_table_name(), funs, ctx).await?;
55        Self::check_scope(&add_req.rel_rbum_domain_id, RbumDomainServ::get_table_name(), funs, ctx).await?;
56        Ok(())
57    }
58
59    async fn package_add(add_req: &RbumItemAddReq, funs: &TardisFunsInst, _: &TardisContext) -> TardisResult<rbum_item::ActiveModel> {
60        let id = if let Some(id) = &add_req.id { id.to_string() } else { TardisFuns::field.nanoid() };
61        let code = if let Some(code) = &add_req.code {
62            if funs
63                .db()
64                .count(
65                    Query::select()
66                        .column((rbum_item::Entity, rbum_item::Column::Id))
67                        .from(rbum_item::Entity)
68                        .inner_join(
69                            rbum_domain::Entity,
70                            Expr::col((rbum_domain::Entity, rbum_domain::Column::Id)).equals((rbum_item::Entity, rbum_item::Column::RelRbumDomainId)),
71                        )
72                        .inner_join(
73                            rbum_kind::Entity,
74                            Expr::col((rbum_kind::Entity, rbum_kind::Column::Id)).equals((rbum_item::Entity, rbum_item::Column::RelRbumKindId)),
75                        )
76                        .and_where(Expr::col((rbum_item::Entity, rbum_item::Column::Code)).eq(code.to_string())),
77                )
78                .await?
79                > 0
80            {
81                return Err(funs.err().conflict(&Self::get_obj_name(), "add", &format!("code {code} already exists"), "409-rbum-*-code-exist"));
82            }
83            code.to_string()
84        } else {
85            id.clone()
86        };
87        Ok(rbum_item::ActiveModel {
88            id: Set(id),
89            code: Set(code),
90            name: Set(add_req.name.to_string()),
91            rel_rbum_kind_id: Set(add_req.rel_rbum_kind_id.to_string()),
92            rel_rbum_domain_id: Set(add_req.rel_rbum_domain_id.to_string()),
93            scope_level: Set(add_req.scope_level.as_ref().unwrap_or(&RbumScopeLevelKind::Private).to_int()),
94            disabled: Set(add_req.disabled.unwrap_or(false)),
95            ..Default::default()
96        })
97    }
98
99    async fn package_modify(id: &str, modify_req: &RbumItemKernelModifyReq, funs: &TardisFunsInst, _: &TardisContext) -> TardisResult<rbum_item::ActiveModel> {
100        let mut rbum_item = rbum_item::ActiveModel {
101            id: Set(id.to_string()),
102            ..Default::default()
103        };
104        if let Some(code) = &modify_req.code {
105            if funs
106                .db()
107                .count(
108                    Query::select()
109                        .column((rbum_item::Entity, rbum_item::Column::Id))
110                        .from(rbum_item::Entity)
111                        .inner_join(
112                            rbum_domain::Entity,
113                            Expr::col((rbum_domain::Entity, rbum_domain::Column::Id)).equals((rbum_item::Entity, rbum_item::Column::RelRbumDomainId)),
114                        )
115                        .inner_join(
116                            rbum_kind::Entity,
117                            Expr::col((rbum_kind::Entity, rbum_kind::Column::Id)).equals((rbum_item::Entity, rbum_item::Column::RelRbumKindId)),
118                        )
119                        .and_where(Expr::col((rbum_item::Entity, rbum_item::Column::Code)).eq(code.to_string()))
120                        .and_where(Expr::col((rbum_item::Entity, rbum_item::Column::Id)).ne(id)),
121                )
122                .await?
123                > 0
124            {
125                return Err(funs.err().conflict(&Self::get_obj_name(), "modify", &format!("code {code} already exists"), "409-rbum-*-code-exist"));
126            }
127            rbum_item.code = Set(code.to_string());
128        }
129        if let Some(name) = &modify_req.name {
130            rbum_item.name = Set(name.to_string());
131        }
132        if let Some(scope_level) = &modify_req.scope_level {
133            rbum_item.scope_level = Set(scope_level.to_int());
134        }
135        if let Some(disabled) = modify_req.disabled {
136            rbum_item.disabled = Set(disabled);
137        }
138        Ok(rbum_item)
139    }
140
141    async fn before_delete_rbum(id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<RbumItemDetailResp>> {
142        Self::check_ownership(id, funs, ctx).await?;
143        Self::check_exist_before_delete(id, RbumItemAttrServ::get_table_name(), rbum_item_attr::Column::RelRbumItemId.as_str(), funs).await?;
144        Self::check_exist_with_cond_before_delete(
145            RbumRelServ::get_table_name(),
146            any![
147                all![
148                    Expr::col(rbum_rel::Column::FromRbumKind).eq(RbumRelFromKind::Item.to_int()),
149                    Expr::col(rbum_rel::Column::FromRbumId).eq(id)
150                ],
151                Expr::col(rbum_rel::Column::ToRbumItemId).eq(id)
152            ],
153            funs,
154        )
155        .await?;
156        Self::check_exist_before_delete(id, RbumSetItemServ::get_table_name(), rbum_set_item::Column::RelRbumItemId.as_str(), funs).await?;
157        Self::check_exist_before_delete(id, RbumCertConfServ::get_table_name(), rbum_cert_conf::Column::RelRbumItemId.as_str(), funs).await?;
158        Self::check_exist_with_cond_before_delete(
159            RbumCertServ::get_table_name(),
160            all![
161                Expr::col(rbum_cert::Column::RelRbumKind).eq(RbumCertRelKind::Item.to_int()),
162                Expr::col(rbum_cert::Column::RelRbumId).eq(id)
163            ],
164            funs,
165        )
166        .await?;
167        Ok(None)
168    }
169
170    async fn package_query(is_detail: bool, filter: &RbumBasicFilterReq, _: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<SelectStatement> {
171        let mut query = Query::select();
172        query
173            .columns(vec![
174                (rbum_item::Entity, rbum_item::Column::Id),
175                (rbum_item::Entity, rbum_item::Column::Code),
176                (rbum_item::Entity, rbum_item::Column::Name),
177                (rbum_item::Entity, rbum_item::Column::RelRbumKindId),
178                (rbum_item::Entity, rbum_item::Column::RelRbumDomainId),
179                (rbum_item::Entity, rbum_item::Column::OwnPaths),
180                (rbum_item::Entity, rbum_item::Column::Owner),
181                (rbum_item::Entity, rbum_item::Column::CreateTime),
182                (rbum_item::Entity, rbum_item::Column::UpdateTime),
183                (rbum_item::Entity, rbum_item::Column::ScopeLevel),
184                (rbum_item::Entity, rbum_item::Column::Disabled),
185            ])
186            .from(rbum_item::Entity);
187
188        if is_detail {
189            query
190                .expr_as(Expr::col((rbum_kind::Entity, rbum_kind::Column::Name)), Alias::new("rel_rbum_kind_name"))
191                .expr_as(Expr::col((rbum_domain::Entity, rbum_domain::Column::Name)), Alias::new("rel_rbum_domain_name"))
192                .inner_join(
193                    rbum_kind::Entity,
194                    Expr::col((rbum_kind::Entity, rbum_kind::Column::Id)).equals((rbum_item::Entity, rbum_item::Column::RelRbumKindId)),
195                )
196                .inner_join(
197                    rbum_domain::Entity,
198                    Expr::col((rbum_domain::Entity, rbum_domain::Column::Id)).equals((rbum_item::Entity, rbum_item::Column::RelRbumDomainId)),
199                );
200        }
201        query.with_filter(Self::get_table_name(), filter, is_detail, true, ctx);
202        Ok(query)
203    }
204}
205
206/// Resource item extended common operation
207///
208/// 资源项扩展公共操作
209#[async_trait]
210pub trait RbumItemCrudOperation<EXT, AddReq, ModifyReq, SummaryResp, DetailResp, ItemFilterReq>
211where
212    EXT: TardisActiveModel + Sync + Send,
213    AddReq: Sync + Send,
214    ModifyReq: Sync + Send,
215    SummaryResp: FromQueryResult + ParseFromJSON + ToJSON + Serialize + Send + Sync,
216    DetailResp: FromQueryResult + ParseFromJSON + ToJSON + Serialize + Send + Sync,
217    ItemFilterReq: Sync + Send + RbumItemFilterFetcher,
218{
219    /// Get the name of the extended table
220    ///
221    /// 获取扩展表的名称
222    fn get_ext_table_name() -> &'static str;
223
224    /// Get the name of the extended object
225    ///
226    /// 获取扩展对象的名称
227    ///
228    /// Mostly used for printing log identifiers.
229    ///
230    /// 多用于打印日志的标识。
231    fn get_obj_name() -> String {
232        Self::get_ext_table_name().to_string()
233    }
234
235    /// Get default resource kind
236    ///
237    /// 获取默认的资源类型
238    fn get_rbum_kind_id() -> Option<String>;
239
240    /// Get default resource domain
241    ///
242    /// 获取默认的资源域
243    fn get_rbum_domain_id() -> Option<String>;
244
245    // ----------------------------- Add -------------------------------
246
247    /// Pre-processing of the add request
248    ///
249    /// 添加请求的前置处理
250    async fn before_add_item(_: &mut AddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<()> {
251        Ok(())
252    }
253
254    /// Package add request of the kernel part of the resource item
255    ///
256    /// 组装资源项核心部分的添加请求
257    ///
258    /// # Examples:
259    ///
260    /// ```
261    /// async fn package_item_add(add_req: &IamAppAddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<RbumItemKernelAddReq> {
262    ///     Ok(RbumItemKernelAddReq {
263    ///         id: add_req.id.clone(),
264    ///         name: add_req.name.clone(),
265    ///         disabled: add_req.disabled,
266    ///         scope_level: add_req.scope_level.clone(),
267    ///         ..Default::default()
268    ///     })
269    /// }
270    /// ```
271    async fn package_item_add(add_req: &AddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<RbumItemKernelAddReq>;
272
273    /// Package add request of the extended part of the resource item
274    ///
275    /// 组装资源项扩展部分的添加请求
276    ///
277    /// # Examples:
278    ///
279    /// ```
280    /// async fn package_ext_add(id: &str, add_req: &IamAppAddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<iam_app::ActiveModel> {
281    ///     Ok(iam_app::ActiveModel {
282    ///         id: Set(id.to_string()),
283    ///         icon: Set(add_req.icon.as_ref().unwrap_or(&"".to_string()).to_string()),
284    ///         sort: Set(add_req.sort.unwrap_or(0)),
285    ///         contact_phone: Set(add_req.contact_phone.as_ref().unwrap_or(&"".to_string()).to_string()),
286    ///         ..Default::default()
287    ///     })
288    /// }
289    /// ```
290    async fn package_ext_add(id: &str, add_req: &AddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<EXT>;
291
292    /// Post-processing of the add request
293    ///
294    /// 添加请求的后置处理
295    async fn after_add_item(_: &str, _: &mut AddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<()> {
296        Ok(())
297    }
298
299    /// Add resource item
300    ///
301    /// 添加资源项
302    async fn add_item(add_req: &mut AddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
303        Self::before_add_item(add_req, funs, ctx).await?;
304        let add_kernel_req = Self::package_item_add(add_req, funs, ctx).await?;
305        let mut item_add_req = RbumItemAddReq {
306            id: add_kernel_req.id.clone(),
307            code: add_kernel_req.code.clone(),
308            name: add_kernel_req.name.clone(),
309            rel_rbum_kind_id: if let Some(rel_rbum_kind_id) = &add_kernel_req.rel_rbum_kind_id {
310                rel_rbum_kind_id.to_string()
311            } else {
312                Self::get_rbum_kind_id().ok_or_else(|| funs.err().bad_request(&Self::get_obj_name(), "add_item", "kind is required", "400-rbum-kind-require"))?
313            },
314            rel_rbum_domain_id: if let Some(rel_rbum_domain_id) = &add_kernel_req.rel_rbum_domain_id {
315                rel_rbum_domain_id.to_string()
316            } else {
317                Self::get_rbum_domain_id().ok_or_else(|| funs.err().bad_request(&Self::get_obj_name(), "add_item", "domain is required", "400-rbum-domain-require"))?
318            },
319            scope_level: add_kernel_req.scope_level.clone(),
320            disabled: add_kernel_req.disabled,
321        };
322        let id = RbumItemServ::add_rbum(&mut item_add_req, funs, ctx).await?;
323        let add_ext_req = Self::package_ext_add(&id, add_req, funs, ctx).await?;
324        funs.db().insert_one(add_ext_req, ctx).await?;
325        Self::after_add_item(&id, add_req, funs, ctx).await?;
326        rbum_event_helper::add_notify_event(Self::get_ext_table_name(), "c", id.as_str(), ctx).await?;
327        Ok(id)
328    }
329
330    /// Add resource item with simple relationship (the added resource item is the source party)
331    ///
332    /// 添加资源项及其简单关系(添加的资源项为来源方)
333    async fn add_item_with_simple_rel_by_from(add_req: &mut AddReq, tag: &str, to_rbum_item_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
334        let id = Self::add_item(add_req, funs, ctx).await?;
335        RbumRelServ::add_rbum(
336            &mut RbumRelAddReq {
337                tag: tag.to_string(),
338                note: None,
339                from_rbum_kind: RbumRelFromKind::Item,
340                from_rbum_id: id.to_string(),
341                to_rbum_item_id: to_rbum_item_id.to_string(),
342                to_own_paths: ctx.own_paths.to_string(),
343                to_is_outside: false,
344                ext: None,
345            },
346            funs,
347            ctx,
348        )
349        .await?;
350        Ok(id)
351    }
352
353    /// Add resource item with simple relationship (the added resource item is the target party)
354    ///
355    /// 添加资源项及其简单关系(添加的资源项为目标方)
356    async fn add_item_with_simple_rel_by_to(add_req: &mut AddReq, tag: &str, from_rbum_item_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
357        let id = Self::add_item(add_req, funs, ctx).await?;
358        RbumRelServ::add_rbum(
359            &mut RbumRelAddReq {
360                tag: tag.to_string(),
361                note: None,
362                from_rbum_kind: RbumRelFromKind::Item,
363                from_rbum_id: from_rbum_item_id.to_string(),
364                to_rbum_item_id: id.to_string(),
365                to_own_paths: ctx.own_paths.to_string(),
366                to_is_outside: false,
367                ext: None,
368            },
369            funs,
370            ctx,
371        )
372        .await?;
373        Ok(id)
374    }
375
376    // ----------------------------- Modify -------------------------------
377
378    ///  Pre-processing of the modify request
379    ///
380    /// 修改请求的前置处理
381    async fn before_modify_item(_: &str, _: &mut ModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<()> {
382        Ok(())
383    }
384
385    /// Package modify request of the kernel part of the resource item
386    ///
387    /// 组装资源项核心部分的修改请求
388    ///
389    /// # Examples:
390    ///
391    /// ```
392    /// async fn package_item_modify(_: &str, modify_req: &IamAppModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<Option<RbumItemKernelModifyReq>> {
393    ///     if modify_req.name.is_none() && modify_req.scope_level.is_none() && modify_req.disabled.is_none() {
394    ///         return Ok(None);
395    ///     }
396    ///     Ok(Some(RbumItemKernelModifyReq {
397    ///         code: None,
398    ///         name: modify_req.name.clone(),
399    ///         scope_level: modify_req.scope_level.clone(),
400    ///         disabled: modify_req.disabled,
401    ///     }))
402    /// }
403    /// ```
404    async fn package_item_modify(id: &str, modify_req: &ModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<RbumItemKernelModifyReq>>;
405
406    /// Package modify request of the extended part of the resource item
407    ///
408    /// 组装资源项扩展部分的修改请求
409    ///
410    /// # Examples:
411    ///
412    /// ```
413    /// async fn package_ext_modify(id: &str, modify_req: &IamAppModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<Option<iam_app::ActiveModel>> {
414    ///     if modify_req.icon.is_none() && modify_req.sort.is_none() && modify_req.contact_phone.is_none() {
415    ///         return Ok(None);
416    ///     }
417    ///     let mut iam_app = iam_app::ActiveModel {
418    ///         id: Set(id.to_string()),
419    ///         ..Default::default()
420    ///     };
421    ///     if let Some(icon) = &modify_req.icon {
422    ///         iam_app.icon = Set(icon.to_string());
423    ///     }
424    ///     if let Some(sort) = modify_req.sort {
425    ///         iam_app.sort = Set(sort);
426    ///     }
427    ///     if let Some(contact_phone) = &modify_req.contact_phone {
428    ///         iam_app.contact_phone = Set(contact_phone.to_string());
429    ///     }
430    ///     Ok(Some(iam_app))
431    /// }
432    /// ```
433    async fn package_ext_modify(id: &str, modify_req: &ModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<EXT>>;
434
435    /// Post-processing of the modify request
436    ///
437    /// 修改请求的后置处理
438    async fn after_modify_item(_: &str, _: &mut ModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<()> {
439        Ok(())
440    }
441
442    /// Modify resource item
443    ///
444    /// 修改资源项
445    async fn modify_item(id: &str, modify_req: &mut ModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
446        Self::before_modify_item(id, modify_req, funs, ctx).await?;
447        let modify_kernel_req = Self::package_item_modify(id, modify_req, funs, ctx).await?;
448        if let Some(mut item_modify_req) = modify_kernel_req {
449            RbumItemServ::modify_rbum(id, &mut item_modify_req, funs, ctx).await?;
450        } else {
451            RbumItemServ::check_ownership(id, funs, ctx).await?;
452        }
453        let modify_ext_req = Self::package_ext_modify(id, modify_req, funs, ctx).await?;
454        if let Some(ext_domain) = modify_ext_req {
455            funs.db().update_one(ext_domain, ctx).await?;
456        }
457        Self::after_modify_item(id, modify_req, funs, ctx).await?;
458        rbum_event_helper::add_notify_event(Self::get_ext_table_name(), "u", id, ctx).await?;
459        Ok(())
460    }
461
462    // ----------------------------- Delete -------------------------------
463
464    /// Pre-processing of the delete request
465    ///
466    /// 删除请求的前置处理
467    async fn before_delete_item(_: &str, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<Option<DetailResp>> {
468        Ok(None)
469    }
470
471    /// Post-processing of the delete request
472    ///
473    /// 删除请求的后置处理
474    async fn after_delete_item(_: &str, _: &Option<DetailResp>, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<()> {
475        Ok(())
476    }
477
478    /// Delete resource item
479    ///
480    /// 删除资源项
481    ///
482    /// TODO remove mq and send detail data to event.
483    async fn delete_item(id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<u64> {
484        let deleted_item = Self::before_delete_item(id, funs, ctx).await?;
485        let item_select_req = <EXT::Entity as EntityTrait>::find().filter(Expr::col(ID_FIELD.clone()).eq(id));
486        #[cfg(feature = "with-mq")]
487        {
488            let deleted_ext_records = funs.db().soft_delete_custom(item_select_req, ID_FIELD_NAME).await?;
489            RbumItemServ::delete_rbum(id, funs, ctx).await?;
490            let mq_topic_entity_deleted = &funs.rbum_conf_mq_topic_entity_deleted();
491            let mq_header = std::collections::HashMap::from([(funs.rbum_conf_mq_header_name_operator(), ctx.owner.clone())]);
492            for delete_record in &deleted_ext_records {
493                funs.mq().publish(mq_topic_entity_deleted, TardisFuns::json.obj_to_string(delete_record)?, &mq_header).await?;
494            }
495            Self::after_delete_item(id, &deleted_item, funs, ctx).await?;
496            rbum_event_helper::add_notify_event(Self::get_ext_table_name(), "d", id, ctx).await?;
497            Ok(deleted_ext_records.len() as u64)
498        }
499        #[cfg(not(feature = "with-mq"))]
500        {
501            let deleted_ext_records = funs.db().soft_delete(item_select_req, &ctx.owner).await?;
502            RbumItemServ::delete_rbum(id, funs, ctx).await?;
503            Self::after_delete_item(id, &deleted_item, funs, ctx).await?;
504            rbum_event_helper::add_notify_event(Self::get_ext_table_name(), "d", id, ctx).await?;
505            Ok(deleted_ext_records)
506        }
507    }
508
509    /// Delete resource item with all relationships
510    ///
511    /// 删除资源项及其所有关系
512    async fn delete_item_with_all_rels(id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<u64> {
513        // Delete rels
514        let rel_ids = RbumRelServ::find_rel_ids(
515            &RbumRelSimpleFindReq {
516                tag: None,
517                from_rbum_kind: Some(RbumRelFromKind::Item),
518                from_rbum_id: Some(id.to_string()),
519                to_rbum_item_id: None,
520                ..Default::default()
521            },
522            funs,
523            ctx,
524        )
525        .await?;
526        for rel_id in rel_ids {
527            RbumRelServ::delete_rel_with_ext(&rel_id, funs, ctx).await?;
528        }
529        let rel_ids = RbumRelServ::find_rel_ids(
530            &RbumRelSimpleFindReq {
531                tag: None,
532                from_rbum_kind: None,
533                from_rbum_id: None,
534                to_rbum_item_id: Some(id.to_string()),
535                ..Default::default()
536            },
537            funs,
538            ctx,
539        )
540        .await?;
541        for rel_id in rel_ids {
542            RbumRelServ::delete_rel_with_ext(&rel_id, funs, ctx).await?;
543        }
544
545        // Delete set items
546        let set_item_ids = RbumSetItemServ::find_id_rbums(
547            &RbumSetItemFilterReq {
548                basic: RbumBasicFilterReq {
549                    with_sub_own_paths: true,
550                    ..Default::default()
551                },
552                rel_rbum_item_ids: Some(vec![id.to_string()]),
553                ..Default::default()
554            },
555            None,
556            None,
557            funs,
558            ctx,
559        )
560        .await?;
561        for set_item_id in set_item_ids {
562            RbumSetItemServ::delete_rbum(&set_item_id, funs, ctx).await?;
563        }
564
565        // Delete Certs
566        let cert_ids = RbumCertServ::find_id_rbums(
567            &RbumCertFilterReq {
568                basic: RbumBasicFilterReq {
569                    with_sub_own_paths: true,
570                    ..Default::default()
571                },
572                rel_rbum_kind: Some(RbumCertRelKind::Item),
573                rel_rbum_id: Some(id.to_string()),
574                ..Default::default()
575            },
576            None,
577            None,
578            funs,
579            ctx,
580        )
581        .await?;
582        for cert_id in cert_ids {
583            RbumCertServ::delete_rbum(&cert_id, funs, ctx).await?;
584        }
585
586        // Delete Cert Conf
587        let cert_conf_ids = RbumCertConfServ::find_id_rbums(
588            &RbumCertConfFilterReq {
589                basic: RbumBasicFilterReq {
590                    with_sub_own_paths: true,
591                    ..Default::default()
592                },
593                rel_rbum_item_id: Some(id.to_string()),
594                ..Default::default()
595            },
596            None,
597            None,
598            funs,
599            ctx,
600        )
601        .await?;
602        for cert_conf_id in cert_conf_ids {
603            RbumCertConfServ::delete_rbum(&cert_conf_id, funs, ctx).await?;
604        }
605
606        Self::delete_item(id, funs, ctx).await
607    }
608
609    // ----------------------------- Query -------------------------------
610
611    /// Package query request of the kernel part of the resource item
612    ///
613    /// 组装资源项核心部分的查询请求
614    async fn package_item_query(is_detail: bool, filter: &ItemFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<SelectStatement> {
615        let mut query = RbumItemServ::package_query(
616            is_detail,
617            &RbumBasicFilterReq {
618                ignore_scope: filter.basic().ignore_scope,
619                rel_ctx_owner: filter.basic().rel_ctx_owner,
620                own_paths: filter.basic().own_paths.clone(),
621                with_sub_own_paths: filter.basic().with_sub_own_paths,
622                ids: filter.basic().ids.clone(),
623                scope_level: filter.basic().scope_level.clone(),
624                enabled: filter.basic().enabled,
625                name: filter.basic().name.clone(),
626                names: filter.basic().names.clone(),
627                code: filter.basic().code.clone(),
628                codes: filter.basic().codes.clone(),
629                rbum_kind_id: if filter.basic().rbum_kind_id.is_some() {
630                    filter.basic().rbum_kind_id.clone()
631                } else {
632                    Self::get_rbum_kind_id()
633                },
634                rbum_domain_id: if filter.basic().rbum_domain_id.is_some() {
635                    filter.basic().rbum_domain_id.clone()
636                } else {
637                    Self::get_rbum_domain_id()
638                },
639            },
640            funs,
641            ctx,
642        )
643        .await?;
644
645        if let Some(rbum_item_rel_filter_req) = &filter.rel() {
646            Self::package_rel(&mut query, Alias::new("rbum_rel1"), rbum_item_rel_filter_req);
647        }
648        if let Some(rbum_item_rel_filter_req) = &filter.rel2() {
649            Self::package_rel(&mut query, Alias::new("rbum_rel2"), rbum_item_rel_filter_req);
650        }
651        Ok(query)
652    }
653
654    /// Package condition of the relationship
655    ///
656    /// 组装关联关系的查询条件
657    fn package_rel(query: &mut SelectStatement, rel_table: Alias, rbum_item_rel_filter_req: &RbumItemRelFilterReq) {
658        let mut binding = Query::select();
659        let sub_query = binding.from(rbum_rel::Entity);
660        if let Some(tag) = &rbum_item_rel_filter_req.tag {
661            sub_query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::Tag)).eq(tag.to_string()));
662        }
663        if let Some(from_rbum_kind) = &rbum_item_rel_filter_req.from_rbum_kind {
664            sub_query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::FromRbumKind)).eq(from_rbum_kind.to_int()));
665        }
666        if let Some(ext_eq) = &rbum_item_rel_filter_req.ext_eq {
667            sub_query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::Ext)).eq(ext_eq.to_string()));
668        }
669        if let Some(ext_like) = &rbum_item_rel_filter_req.ext_like {
670            sub_query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::Ext)).like(format!("%{ext_like}%").as_str()));
671        }
672        if let Some(own_paths) = &rbum_item_rel_filter_req.own_paths {
673            sub_query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::OwnPaths)).eq(own_paths.to_string()));
674        }
675        if rbum_item_rel_filter_req.rel_by_from {
676            if let Some(rel_item_id) = &rbum_item_rel_filter_req.rel_item_id {
677                sub_query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::ToRbumItemId)).eq(rel_item_id.to_string()));
678            }
679            if let Some(rel_item_ids) = &rbum_item_rel_filter_req.rel_item_ids {
680                if rel_item_ids.len() == 1 {
681                    sub_query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::ToRbumItemId)).eq(rel_item_ids.first().expect("ignore").to_string()));
682                } else if !rel_item_ids.is_empty() {
683                    sub_query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::ToRbumItemId)).is_in(rel_item_ids));
684                }
685            }
686            sub_query.column((rbum_rel::Entity, rbum_rel::Column::FromRbumId));
687            sub_query.group_by_col((rbum_rel::Entity, rbum_rel::Column::FromRbumId));
688
689            if rbum_item_rel_filter_req.optional {
690                query.join_subquery(
691                    JoinType::LeftJoin,
692                    sub_query.take(),
693                    rel_table,
694                    Expr::col((rbum_rel::Entity, rbum_rel::Column::FromRbumId)).equals((rbum_item::Entity, rbum_item::Column::Id)),
695                );
696            } else {
697                query.join_subquery(
698                    JoinType::InnerJoin,
699                    sub_query.take(),
700                    rel_table.clone(),
701                    Expr::col((rel_table, rbum_rel::Column::FromRbumId)).equals((rbum_item::Entity, rbum_item::Column::Id)),
702                );
703            }
704        } else {
705            if let Some(rel_item_id) = &rbum_item_rel_filter_req.rel_item_id {
706                sub_query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::FromRbumId)).eq(rel_item_id.to_string()));
707            }
708            if let Some(rel_item_ids) = &rbum_item_rel_filter_req.rel_item_ids {
709                if rel_item_ids.len() == 1 {
710                    sub_query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::FromRbumId)).eq(rel_item_ids.first().expect("ignore").to_string()));
711                } else if !rel_item_ids.is_empty() {
712                    sub_query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::FromRbumId)).is_in(rel_item_ids));
713                }
714            }
715            sub_query.column((rbum_rel::Entity, rbum_rel::Column::ToRbumItemId));
716            sub_query.group_by_col((rbum_rel::Entity, rbum_rel::Column::ToRbumItemId));
717
718            if rbum_item_rel_filter_req.optional {
719                query.join_subquery(
720                    JoinType::LeftJoin,
721                    sub_query.take(),
722                    rel_table.clone(),
723                    Expr::col((rel_table, rbum_rel::Column::ToRbumItemId)).equals((rbum_item::Entity, rbum_item::Column::Id)),
724                );
725            } else {
726                query.join_subquery(
727                    JoinType::InnerJoin,
728                    sub_query.take(),
729                    rel_table.clone(),
730                    Expr::col((rel_table, rbum_rel::Column::ToRbumItemId)).equals((rbum_item::Entity, rbum_item::Column::Id)),
731                );
732            }
733        }
734    }
735
736    /// Package condition of the resource set
737    ///
738    /// 组装资源集的查询条件
739    fn package_set_rel(query: &mut SelectStatement, rel_table: Alias, rbum_set_rel_filter_req: &RbumSetItemRelFilterReq) {
740        let mut binding = Query::select();
741        let sub_query = binding.from(rbum_set_item::Entity);
742        if let Some(set_ids_and_cate_codes) = rbum_set_rel_filter_req.set_ids_and_cate_codes.clone() {
743            let mut cond_by_sets = Condition::any();
744            for set_id in set_ids_and_cate_codes.keys() {
745                let mut cond_by_a_set = Condition::all();
746                cond_by_a_set = cond_by_a_set.add(Expr::col((rbum_set_item::Entity, rbum_set_item::Column::RelRbumSetId)).eq(set_id));
747                if rbum_set_rel_filter_req.with_sub_set_cate_codes {
748                    if let Some(cate_codes) = set_ids_and_cate_codes.get(set_id) {
749                        let mut cond = Condition::any();
750                        for cate_code in cate_codes {
751                            cond = cond.add(Expr::col((rbum_set_item::Entity, rbum_set_item::Column::RelRbumSetCateCode)).like(format!("{}%", cate_code)));
752                        }
753                        cond_by_a_set = cond_by_a_set.add(Cond::all().add(cond));
754                    } else {
755                        // 包含所有子集
756                    }
757                } else {
758                    cond_by_a_set = cond_by_a_set.add(
759                        Expr::col((rbum_set_item::Entity, rbum_set_item::Column::RelRbumSetCateCode)).is_in(set_ids_and_cate_codes.get(set_id).unwrap_or(&Vec::<String>::new())),
760                    );
761                }
762                cond_by_sets = cond_by_sets.add(cond_by_a_set);
763            }
764            sub_query.cond_where(cond_by_sets);
765        }
766        sub_query.column((rbum_set_item::Entity, rbum_set_item::Column::RelRbumItemId));
767        sub_query.group_by_col((rbum_set_item::Entity, rbum_set_item::Column::RelRbumItemId));
768
769        query.join_subquery(
770            JoinType::InnerJoin,
771            sub_query.take(),
772            rel_table.clone(),
773            Expr::col((rel_table, rbum_set_item::Column::RelRbumItemId)).equals((rbum_item::Entity, rbum_item::Column::Id)),
774        );
775    }
776
777    /// Package query request of the extended part of the resource item
778    ///
779    /// 组装资源项扩展部分的查询请求
780    ///
781    /// # Examples:
782    ///
783    /// ```
784    /// async fn package_ext_query(query: &mut SelectStatement, _: bool, filter: &IamAppFilterReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<()> {
785    ///     query.column((iam_app::Entity, iam_app::Column::ContactPhone));
786    ///     query.column((iam_app::Entity, iam_app::Column::Icon));
787    ///     query.column((iam_app::Entity, iam_app::Column::Sort));
788    ///     if let Some(contact_phone) = &filter.contact_phone {
789    ///         query.and_where(Expr::col(iam_app::Column::ContactPhone).eq(contact_phone.as_str()));
790    /// }
791    /// Ok(())
792    /// ```
793    async fn package_ext_query(query: &mut SelectStatement, is_detail: bool, filter: &ItemFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()>;
794
795    /// Query and get a resource item summary
796    ///
797    /// 查询并获取一条资源项概要信息
798    async fn peek_item(id: &str, filter: &ItemFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<SummaryResp> {
799        Self::do_peek_item(id, filter, funs, ctx).await
800    }
801
802    /// Query and get a resource item summary
803    ///
804    /// 查询并获取一条资源项概要信息
805    ///
806    /// NOTE: Internal method, not recommended to override.
807    ///
808    /// NOTE: 内部方法,不建议重写。
809    async fn do_peek_item(id: &str, filter: &ItemFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<SummaryResp> {
810        let mut query = Self::package_item_query(false, filter, funs, ctx).await?;
811        query.inner_join(
812            Alias::new(Self::get_ext_table_name()),
813            Expr::col((Alias::new(Self::get_ext_table_name()), ID_FIELD.clone())).equals((rbum_item::Entity, rbum_item::Column::Id)),
814        );
815        Self::package_ext_query(&mut query, false, filter, funs, ctx).await?;
816        query.and_where(Expr::col((rbum_item::Entity, rbum_item::Column::Id)).eq(id));
817        let query = funs.db().get_dto(&query).await?;
818        match query {
819            Some(resp) => Ok(resp),
820            None => Err(funs.err().not_found(
821                &Self::get_obj_name(),
822                "peek",
823                &format!("not found {}.{} by {}", Self::get_obj_name(), id, ctx.owner),
824                "404-rbum-*-obj-not-exist",
825            )),
826        }
827    }
828
829    /// Query and get a resource item detail
830    ///
831    /// 查询并获取一条资源项详细信息
832    async fn get_item(id: &str, filter: &ItemFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<DetailResp> {
833        Self::do_get_item(id, filter, funs, ctx).await
834    }
835
836    /// Query and get a resource item detail
837    ///
838    /// 查询并获取一条资源项详细信息
839    ///
840    /// NOTE: Internal method, not recommended to override.
841    ///
842    /// NOTE: 内部方法,不建议重写。
843    async fn do_get_item(id: &str, filter: &ItemFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<DetailResp> {
844        let mut query = Self::package_item_query(true, filter, funs, ctx).await?;
845        query.inner_join(
846            Alias::new(Self::get_ext_table_name()),
847            Expr::col((Alias::new(Self::get_ext_table_name()), ID_FIELD.clone())).equals((rbum_item::Entity, rbum_item::Column::Id)),
848        );
849        Self::package_ext_query(&mut query, true, filter, funs, ctx).await?;
850        query.and_where(Expr::col((rbum_item::Entity, rbum_item::Column::Id)).eq(id));
851        let query = funs.db().get_dto(&query).await?;
852        match query {
853            Some(resp) => Ok(resp),
854            None => Err(funs.err().not_found(
855                &Self::get_obj_name(),
856                "get",
857                &format!("not found {}.{} by {}", Self::get_obj_name(), id, ctx.owner),
858                "404-rbum-*-obj-not-exist",
859            )),
860        }
861    }
862
863    /// Query and page to get the resource item id set
864    ///
865    /// 查询并分页获取资源项id集合
866    async fn paginate_id_items(
867        filter: &ItemFilterReq,
868        page_number: u32,
869        page_size: u32,
870        desc_sort_by_create: Option<bool>,
871        desc_sort_by_update: Option<bool>,
872        funs: &TardisFunsInst,
873        ctx: &TardisContext,
874    ) -> TardisResult<TardisPage<String>> {
875        Self::do_paginate_id_items(filter, page_number, page_size, desc_sort_by_create, desc_sort_by_update, funs, ctx).await
876    }
877
878    /// Query and page to get the resource item id set
879    ///
880    /// 查询并分页获取资源项id集合
881    ///
882    /// NOTE: Internal method, not recommended to override.
883    ///
884    /// NOTE: 内部方法,不建议重写。
885    async fn do_paginate_id_items(
886        filter: &ItemFilterReq,
887        page_number: u32,
888        page_size: u32,
889        desc_sort_by_create: Option<bool>,
890        desc_sort_by_update: Option<bool>,
891        funs: &TardisFunsInst,
892        ctx: &TardisContext,
893    ) -> TardisResult<TardisPage<String>> {
894        let mut query = Self::package_item_query(false, filter, funs, ctx).await?;
895        query.inner_join(
896            Alias::new(Self::get_ext_table_name()),
897            Expr::col((Alias::new(Self::get_ext_table_name()), ID_FIELD.clone())).equals((rbum_item::Entity, rbum_item::Column::Id)),
898        );
899        Self::package_ext_query(&mut query, false, filter, funs, ctx).await?;
900        query.clear_selects();
901        query.column((rbum_item::Entity, rbum_item::Column::Id));
902        if let Some(sort) = desc_sort_by_create {
903            query.order_by((rbum_item::Entity, CREATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
904        }
905        if let Some(sort) = desc_sort_by_update {
906            query.order_by((rbum_item::Entity, UPDATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
907        }
908        let (records, total_size) = funs.db().paginate_dtos::<IdResp>(&query, page_number as u64, page_size as u64).await?;
909        Ok(TardisPage {
910            page_size: page_size as u64,
911            page_number: page_number as u64,
912            total_size,
913            records: records.into_iter().map(|resp| resp.id).collect(),
914        })
915    }
916
917    /// Query and page to get the resource item id and name set
918    ///
919    /// 查询并分页获取资源项id和名称集合
920    async fn paginate_id_name_items(
921        filter: &ItemFilterReq,
922        page_number: u32,
923        page_size: u32,
924        desc_sort_by_create: Option<bool>,
925        desc_sort_by_update: Option<bool>,
926        funs: &TardisFunsInst,
927        ctx: &TardisContext,
928    ) -> TardisResult<TardisPage<IdNameResp>> {
929        Self::do_paginate_id_name_items(filter, page_number, page_size, desc_sort_by_create, desc_sort_by_update, funs, ctx).await
930    }
931
932    /// Query and page to get the resource item id and name set
933    ///
934    /// 查询并分页获取资源项id和名称集合
935    ///
936    /// NOTE: Internal method, not recommended to override.
937    ///
938    /// NOTE: 内部方法,不建议重写。
939    async fn do_paginate_id_name_items(
940        filter: &ItemFilterReq,
941        page_number: u32,
942        page_size: u32,
943        desc_sort_by_create: Option<bool>,
944        desc_sort_by_update: Option<bool>,
945        funs: &TardisFunsInst,
946        ctx: &TardisContext,
947    ) -> TardisResult<TardisPage<IdNameResp>> {
948        let mut query = Self::package_item_query(false, filter, funs, ctx).await?;
949        query.inner_join(
950            Alias::new(Self::get_ext_table_name()),
951            Expr::col((Alias::new(Self::get_ext_table_name()), ID_FIELD.clone())).equals((rbum_item::Entity, rbum_item::Column::Id)),
952        );
953        Self::package_ext_query(&mut query, false, filter, funs, ctx).await?;
954        query.clear_selects();
955        query.columns([(rbum_item::Entity, rbum_item::Column::Id), (rbum_item::Entity, rbum_item::Column::Name)]);
956        if let Some(sort) = desc_sort_by_create {
957            query.order_by((rbum_item::Entity, CREATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
958        }
959        if let Some(sort) = desc_sort_by_update {
960            query.order_by((rbum_item::Entity, UPDATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
961        }
962        let (records, total_size) = funs.db().paginate_dtos::<IdNameResp>(&query, page_number as u64, page_size as u64).await?;
963        Ok(TardisPage {
964            page_size: page_size as u64,
965            page_number: page_number as u64,
966            total_size,
967            records,
968        })
969    }
970
971    /// Query and page to get the resource item summary set
972    ///
973    /// 查询并分页获取资源项概要信息集合
974    async fn paginate_items(
975        filter: &ItemFilterReq,
976        page_number: u32,
977        page_size: u32,
978        desc_sort_by_create: Option<bool>,
979        desc_sort_by_update: Option<bool>,
980        funs: &TardisFunsInst,
981        ctx: &TardisContext,
982    ) -> TardisResult<TardisPage<SummaryResp>> {
983        Self::do_paginate_items(filter, page_number, page_size, desc_sort_by_create, desc_sort_by_update, funs, ctx).await
984    }
985
986    /// Query and page to get the resource item summary set
987    ///
988    /// 查询并分页获取资源项概要信息集合
989    ///
990    /// NOTE: Internal method, not recommended to override.
991    ///
992    /// NOTE: 内部方法,不建议重写。
993    async fn do_paginate_items(
994        filter: &ItemFilterReq,
995        page_number: u32,
996        page_size: u32,
997        desc_sort_by_create: Option<bool>,
998        desc_sort_by_update: Option<bool>,
999        funs: &TardisFunsInst,
1000        ctx: &TardisContext,
1001    ) -> TardisResult<TardisPage<SummaryResp>> {
1002        let mut query = Self::package_item_query(false, filter, funs, ctx).await?;
1003        query.inner_join(
1004            Alias::new(Self::get_ext_table_name()),
1005            Expr::col((Alias::new(Self::get_ext_table_name()), ID_FIELD.clone())).equals((rbum_item::Entity, rbum_item::Column::Id)),
1006        );
1007        Self::package_ext_query(&mut query, false, filter, funs, ctx).await?;
1008        if let Some(sort) = desc_sort_by_create {
1009            query.order_by((rbum_item::Entity, CREATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
1010        }
1011        if let Some(sort) = desc_sort_by_update {
1012            query.order_by((rbum_item::Entity, UPDATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
1013        }
1014        let (records, total_size) = funs.db().paginate_dtos(&query, page_number as u64, page_size as u64).await?;
1015        Ok(TardisPage {
1016            page_size: page_size as u64,
1017            page_number: page_number as u64,
1018            total_size,
1019            records,
1020        })
1021    }
1022
1023    /// Query and page to get the resource item detail set
1024    ///
1025    /// 查询并分页获取资源项详细信息集合
1026    async fn paginate_detail_items(
1027        filter: &ItemFilterReq,
1028        page_number: u32,
1029        page_size: u32,
1030        desc_sort_by_create: Option<bool>,
1031        desc_sort_by_update: Option<bool>,
1032        funs: &TardisFunsInst,
1033        ctx: &TardisContext,
1034    ) -> TardisResult<TardisPage<DetailResp>> {
1035        Self::do_paginate_detail_items(filter, page_number, page_size, desc_sort_by_create, desc_sort_by_update, funs, ctx).await
1036    }
1037
1038    /// Query and page to get the resource item detail set
1039    ///
1040    /// 查询并分页获取资源项详细信息集合
1041    ///
1042    /// NOTE: Internal method, not recommended to override.
1043    ///
1044    /// NOTE: 内部方法,不建议重写。
1045    async fn do_paginate_detail_items(
1046        filter: &ItemFilterReq,
1047        page_number: u32,
1048        page_size: u32,
1049        desc_sort_by_create: Option<bool>,
1050        desc_sort_by_update: Option<bool>,
1051        funs: &TardisFunsInst,
1052        ctx: &TardisContext,
1053    ) -> TardisResult<TardisPage<DetailResp>> {
1054        let mut query = Self::package_item_query(true, filter, funs, ctx).await?;
1055        query.inner_join(
1056            Alias::new(Self::get_ext_table_name()),
1057            Expr::col((Alias::new(Self::get_ext_table_name()), ID_FIELD.clone())).equals((rbum_item::Entity, rbum_item::Column::Id)),
1058        );
1059        Self::package_ext_query(&mut query, true, filter, funs, ctx).await?;
1060        if let Some(sort) = desc_sort_by_create {
1061            query.order_by((rbum_item::Entity, CREATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
1062        }
1063        if let Some(sort) = desc_sort_by_update {
1064            query.order_by((rbum_item::Entity, UPDATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
1065        }
1066        let (records, total_size) = funs.db().paginate_dtos(&query, page_number as u64, page_size as u64).await?;
1067        Ok(TardisPage {
1068            page_size: page_size as u64,
1069            page_number: page_number as u64,
1070            total_size,
1071            records,
1072        })
1073    }
1074
1075    /// Query and get a resource item summary
1076    ///
1077    /// 查询并获取一条资源项概要信息
1078    async fn find_one_item(filter: &ItemFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<SummaryResp>> {
1079        Self::do_find_one_item(filter, funs, ctx).await
1080    }
1081
1082    /// Query and get a resource item summary
1083    ///
1084    /// 查询并获取一条资源项概要信息
1085    ///
1086    /// NOTE: Internal method, not recommended to override.
1087    ///
1088    /// NOTE: 内部方法,不建议重写。
1089    async fn do_find_one_item(filter: &ItemFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<SummaryResp>> {
1090        let result = Self::find_items(filter, None, None, funs, ctx).await?;
1091        if result.len() > 1 {
1092            Err(funs.err().conflict(&Self::get_obj_name(), "find_one", "found multiple records", "409-rbum-*-obj-multi-exist"))
1093        } else {
1094            Ok(result.into_iter().next())
1095        }
1096    }
1097
1098    /// Query and get the resource item id set
1099    ///
1100    /// 查询并获取资源项id集合
1101    async fn find_id_items(
1102        filter: &ItemFilterReq,
1103        desc_sort_by_create: Option<bool>,
1104        desc_sort_by_update: Option<bool>,
1105        funs: &TardisFunsInst,
1106        ctx: &TardisContext,
1107    ) -> TardisResult<Vec<String>> {
1108        Self::do_find_id_items(filter, desc_sort_by_create, desc_sort_by_update, funs, ctx).await
1109    }
1110
1111    /// Query and get the resource item id set
1112    ///
1113    /// 查询并获取资源项id集合
1114    ///
1115    /// NOTE: Internal method, not recommended to override.
1116    ///
1117    /// NOTE: 内部方法,不建议重写。
1118    async fn do_find_id_items(
1119        filter: &ItemFilterReq,
1120        desc_sort_by_create: Option<bool>,
1121        desc_sort_by_update: Option<bool>,
1122        funs: &TardisFunsInst,
1123        ctx: &TardisContext,
1124    ) -> TardisResult<Vec<String>> {
1125        let mut query = Self::package_item_query(false, filter, funs, ctx).await?;
1126        query.inner_join(
1127            Alias::new(Self::get_ext_table_name()),
1128            Expr::col((Alias::new(Self::get_ext_table_name()), ID_FIELD.clone())).equals((rbum_item::Entity, rbum_item::Column::Id)),
1129        );
1130        Self::package_ext_query(&mut query, false, filter, funs, ctx).await?;
1131        query.clear_selects();
1132        query.column((rbum_item::Entity, rbum_item::Column::Id));
1133        if let Some(sort) = desc_sort_by_create {
1134            query.order_by((rbum_item::Entity, CREATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
1135        }
1136        if let Some(sort) = desc_sort_by_update {
1137            query.order_by((rbum_item::Entity, UPDATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
1138        }
1139        Ok(funs.db().find_dtos::<IdResp>(&query).await?.into_iter().map(|resp| resp.id).collect())
1140    }
1141
1142    /// Query and get the resource item id and name set
1143    ///
1144    /// 查询并获取资源项id和名称集合
1145    async fn find_id_name_items(
1146        filter: &ItemFilterReq,
1147        desc_sort_by_create: Option<bool>,
1148        desc_sort_by_update: Option<bool>,
1149        funs: &TardisFunsInst,
1150        ctx: &TardisContext,
1151    ) -> TardisResult<HashMap<String, String>> {
1152        Self::do_find_id_name_items(filter, desc_sort_by_create, desc_sort_by_update, funs, ctx).await
1153    }
1154
1155    /// Query and get the resource item id and name set
1156    ///
1157    /// 查询并获取资源项id和名称集合
1158    ///
1159    /// NOTE: Internal method, not recommended to override.
1160    ///
1161    /// NOTE: 内部方法,不建议重写。
1162    async fn do_find_id_name_items(
1163        filter: &ItemFilterReq,
1164        desc_sort_by_create: Option<bool>,
1165        desc_sort_by_update: Option<bool>,
1166        funs: &TardisFunsInst,
1167        ctx: &TardisContext,
1168    ) -> TardisResult<HashMap<String, String>> {
1169        let mut query = Self::package_item_query(false, filter, funs, ctx).await?;
1170        query.inner_join(
1171            Alias::new(Self::get_ext_table_name()),
1172            Expr::col((Alias::new(Self::get_ext_table_name()), ID_FIELD.clone())).equals((rbum_item::Entity, rbum_item::Column::Id)),
1173        );
1174        Self::package_ext_query(&mut query, false, filter, funs, ctx).await?;
1175        query.clear_selects();
1176        query.columns([(rbum_item::Entity, rbum_item::Column::Id), (rbum_item::Entity, rbum_item::Column::Name)]);
1177        if let Some(sort) = desc_sort_by_create {
1178            query.order_by((rbum_item::Entity, CREATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
1179        }
1180        if let Some(sort) = desc_sort_by_update {
1181            query.order_by((rbum_item::Entity, UPDATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
1182        }
1183        Ok(funs.db().find_dtos::<IdNameResp>(&query).await?.into_iter().map(|resp| (resp.id, resp.name)).collect())
1184    }
1185
1186    /// Query and get the resource item summary set
1187    ///
1188    /// 查询并获取资源项概要信息集合
1189    async fn find_items(
1190        filter: &ItemFilterReq,
1191        desc_sort_by_create: Option<bool>,
1192        desc_sort_by_update: Option<bool>,
1193        funs: &TardisFunsInst,
1194        ctx: &TardisContext,
1195    ) -> TardisResult<Vec<SummaryResp>> {
1196        Self::do_find_items(filter, desc_sort_by_create, desc_sort_by_update, funs, ctx).await
1197    }
1198
1199    /// Query and get the resource item summary set
1200    ///
1201    /// 查询并获取资源项概要信息集合
1202    ///
1203    /// NOTE: Internal method, not recommended to override.
1204    ///
1205    /// NOTE: 内部方法,不建议重写。
1206    async fn do_find_items(
1207        filter: &ItemFilterReq,
1208        desc_sort_by_create: Option<bool>,
1209        desc_sort_by_update: Option<bool>,
1210        funs: &TardisFunsInst,
1211        ctx: &TardisContext,
1212    ) -> TardisResult<Vec<SummaryResp>> {
1213        let mut query = Self::package_item_query(false, filter, funs, ctx).await?;
1214        query.inner_join(
1215            Alias::new(Self::get_ext_table_name()),
1216            Expr::col((Alias::new(Self::get_ext_table_name()), ID_FIELD.clone())).equals((rbum_item::Entity, rbum_item::Column::Id)),
1217        );
1218        Self::package_ext_query(&mut query, false, filter, funs, ctx).await?;
1219        if let Some(sort) = desc_sort_by_create {
1220            query.order_by((rbum_item::Entity, CREATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
1221        }
1222        if let Some(sort) = desc_sort_by_update {
1223            query.order_by((rbum_item::Entity, UPDATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
1224        }
1225        Ok(funs.db().find_dtos(&query).await?)
1226    }
1227
1228    /// Query and get a resource item detail
1229    ///
1230    /// 查询并获取一条资源项详细信息
1231    async fn find_one_detail_item(filter: &ItemFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<DetailResp>> {
1232        Self::do_find_one_detail_item(filter, funs, ctx).await
1233    }
1234
1235    /// Query and get a resource item detail
1236    ///
1237    /// 查询并获取一条资源项详细信息
1238    ///
1239    /// NOTE: Internal method, not recommended to override.
1240    ///
1241    /// NOTE: 内部方法,不建议重写。
1242    async fn do_find_one_detail_item(filter: &ItemFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<DetailResp>> {
1243        let result = Self::find_detail_items(filter, None, None, funs, ctx).await?;
1244        if result.len() > 1 {
1245            Err(funs.err().conflict(&Self::get_obj_name(), "find_one_detail", "found multiple records", "409-rbum-*-obj-multi-exist"))
1246        } else {
1247            Ok(result.into_iter().next())
1248        }
1249    }
1250
1251    /// Query and get the resource item detail set
1252    ///
1253    /// 查询并获取资源项详细信息集合
1254    async fn find_detail_items(
1255        filter: &ItemFilterReq,
1256        desc_sort_by_create: Option<bool>,
1257        desc_sort_by_update: Option<bool>,
1258        funs: &TardisFunsInst,
1259        ctx: &TardisContext,
1260    ) -> TardisResult<Vec<DetailResp>> {
1261        Self::do_find_detail_items(filter, desc_sort_by_create, desc_sort_by_update, funs, ctx).await
1262    }
1263
1264    /// Query and get the resource item detail set
1265    ///
1266    /// 查询并获取资源项详细信息集合
1267    ///
1268    /// NOTE: Internal method, not recommended to override.
1269    ///
1270    /// NOTE: 内部方法,不建议重写。
1271    async fn do_find_detail_items(
1272        filter: &ItemFilterReq,
1273        desc_sort_by_create: Option<bool>,
1274        desc_sort_by_update: Option<bool>,
1275        funs: &TardisFunsInst,
1276        ctx: &TardisContext,
1277    ) -> TardisResult<Vec<DetailResp>> {
1278        let mut query = Self::package_item_query(true, filter, funs, ctx).await?;
1279        query.inner_join(
1280            Alias::new(Self::get_ext_table_name()),
1281            Expr::col((Alias::new(Self::get_ext_table_name()), ID_FIELD.clone())).equals((rbum_item::Entity, rbum_item::Column::Id)),
1282        );
1283        Self::package_ext_query(&mut query, true, filter, funs, ctx).await?;
1284        if let Some(sort) = desc_sort_by_create {
1285            query.order_by((rbum_item::Entity, CREATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
1286        }
1287        if let Some(sort) = desc_sort_by_update {
1288            query.order_by((rbum_item::Entity, UPDATE_TIME_FIELD.clone()), if sort { Order::Desc } else { Order::Asc });
1289        }
1290        Ok(funs.db().find_dtos(&query).await?)
1291    }
1292
1293    /// Query and count the number of resource items
1294    ///
1295    /// 查询并统计资源项数量
1296    async fn count_items(filter: &ItemFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<u64> {
1297        Self::do_count_items(filter, funs, ctx).await
1298    }
1299
1300    /// Query and count the number of resource items
1301    ///
1302    /// 查询并统计资源项数量
1303    ///
1304    /// NOTE: Internal method, not recommended to override.
1305    ///
1306    /// NOTE: 内部方法,不建议重写。
1307    async fn do_count_items(filter: &ItemFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<u64> {
1308        let mut query = Self::package_item_query(false, filter, funs, ctx).await?;
1309        query.inner_join(
1310            Alias::new(Self::get_ext_table_name()),
1311            Expr::col((Alias::new(Self::get_ext_table_name()), ID_FIELD.clone())).equals((rbum_item::Entity, rbum_item::Column::Id)),
1312        );
1313        Self::package_ext_query(&mut query, false, filter, funs, ctx).await?;
1314        funs.db().count(&query).await
1315    }
1316
1317    /// Whether the resource item is disabled
1318    ///
1319    /// 判断资源项是否被禁用
1320    async fn is_disabled(id: &str, funs: &TardisFunsInst) -> TardisResult<bool> {
1321        #[derive(Debug, sea_orm::FromQueryResult)]
1322        pub struct StatusResp {
1323            pub disabled: bool,
1324        }
1325        let result =
1326            funs.db().get_dto::<StatusResp>(Query::select().column(rbum_item::Column::Disabled).from(rbum_item::Entity).and_where(Expr::col(rbum_item::Column::Id).eq(id))).await?;
1327        if let Some(result) = result {
1328            Ok(result.disabled)
1329        } else {
1330            Err(funs.err().not_found(
1331                &Self::get_obj_name(),
1332                "is_disabled",
1333                &format!("not found {}.{}", Self::get_obj_name(), id),
1334                "404-rbum-*-obj-not-exist",
1335            ))
1336        }
1337    }
1338}
1339
1340#[async_trait]
1341impl RbumCrudOperation<rbum_item_attr::ActiveModel, RbumItemAttrAddReq, RbumItemAttrModifyReq, RbumItemAttrSummaryResp, RbumItemAttrDetailResp, RbumItemAttrFilterReq>
1342    for RbumItemAttrServ
1343{
1344    fn get_table_name() -> &'static str {
1345        rbum_item_attr::Entity.table_name()
1346    }
1347
1348    async fn before_add_rbum(add_req: &mut RbumItemAttrAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
1349        Self::check_scope(&add_req.rel_rbum_item_id, RbumItemServ::get_table_name(), funs, ctx).await?;
1350        Self::check_scope(&add_req.rel_rbum_kind_attr_id, RbumKindAttrServ::get_table_name(), funs, ctx).await?;
1351        let rbum_kind_attr = RbumKindAttrServ::peek_rbum(&add_req.rel_rbum_kind_attr_id, &RbumKindAttrFilterReq::default(), funs, ctx).await?;
1352        if rbum_kind_attr.main_column {
1353            return Err(funs.err().bad_request(
1354                &Self::get_obj_name(),
1355                "add",
1356                "extension fields located in main table cannot be added using this function",
1357                "400-rbum-kind-attr-main-illegal",
1358            ));
1359        }
1360        if rbum_kind_attr.idx {
1361            return Err(funs.err().bad_request(
1362                &Self::get_obj_name(),
1363                "add",
1364                "index extension fields cannot be added using this function",
1365                "400-rbum-kind-attr-idx-illegal",
1366            ));
1367        }
1368        Ok(())
1369    }
1370
1371    async fn package_add(add_req: &RbumItemAttrAddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<rbum_item_attr::ActiveModel> {
1372        Ok(rbum_item_attr::ActiveModel {
1373            id: Set(TardisFuns::field.nanoid()),
1374            value: Set(add_req.value.to_string()),
1375            rel_rbum_item_id: Set(add_req.rel_rbum_item_id.to_string()),
1376            rel_rbum_kind_attr_id: Set(add_req.rel_rbum_kind_attr_id.to_string()),
1377            ..Default::default()
1378        })
1379    }
1380
1381    async fn package_modify(id: &str, modify_req: &RbumItemAttrModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<rbum_item_attr::ActiveModel> {
1382        Ok(rbum_item_attr::ActiveModel {
1383            id: Set(id.to_string()),
1384            value: Set(modify_req.value.to_string()),
1385            ..Default::default()
1386        })
1387    }
1388
1389    async fn package_query(is_detail: bool, filter: &RbumItemAttrFilterReq, _: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<SelectStatement> {
1390        let mut query = Query::select();
1391        query
1392            .columns(vec![
1393                (rbum_item_attr::Entity, rbum_item_attr::Column::Id),
1394                (rbum_item_attr::Entity, rbum_item_attr::Column::Value),
1395                (rbum_item_attr::Entity, rbum_item_attr::Column::RelRbumItemId),
1396                (rbum_item_attr::Entity, rbum_item_attr::Column::RelRbumKindAttrId),
1397                (rbum_item_attr::Entity, rbum_item_attr::Column::OwnPaths),
1398                (rbum_item_attr::Entity, rbum_item_attr::Column::Owner),
1399                (rbum_item_attr::Entity, rbum_item_attr::Column::CreateTime),
1400                (rbum_item_attr::Entity, rbum_item_attr::Column::UpdateTime),
1401            ])
1402            .expr_as(Expr::col((rbum_kind_attr::Entity, rbum_kind_attr::Column::Name)), Alias::new("rel_rbum_kind_attr_name"))
1403            .from(rbum_item_attr::Entity)
1404            .inner_join(
1405                rbum_kind_attr::Entity,
1406                Expr::col((rbum_kind_attr::Entity, rbum_kind_attr::Column::Id)).equals((rbum_item_attr::Entity, rbum_item_attr::Column::RelRbumKindAttrId)),
1407            );
1408        if let Some(rel_rbum_item_id) = &filter.rel_rbum_item_id {
1409            query.and_where(Expr::col((rbum_item_attr::Entity, rbum_item_attr::Column::RelRbumItemId)).eq(rel_rbum_item_id.to_string()));
1410        }
1411        if let Some(rel_rbum_kind_attr_id) = &filter.rel_rbum_kind_attr_id {
1412            query.and_where(Expr::col((rbum_item_attr::Entity, rbum_item_attr::Column::RelRbumKindAttrId)).eq(rel_rbum_kind_attr_id.to_string()));
1413        }
1414        query.with_filter(Self::get_table_name(), &filter.basic, is_detail, false, ctx);
1415        Ok(query)
1416    }
1417}
1418
1419impl RbumItemAttrServ {
1420    /// Get resource kind id and resource kind attribute definitions corresponding to resource item id
1421    ///
1422    /// 获取资源项对应资源类型id及资源类型所有属性定义
1423    async fn find_res_kind_id_and_res_kind_attrs_by_item_id(
1424        rbum_item_id: &str,
1425        secret: Option<bool>,
1426        funs: &TardisFunsInst,
1427        ctx: &TardisContext,
1428    ) -> TardisResult<(String, Vec<RbumKindAttrSummaryResp>)> {
1429        let rel_rbum_kind_id = RbumItemServ::peek_rbum(
1430            rbum_item_id,
1431            &RbumBasicFilterReq {
1432                with_sub_own_paths: true,
1433                ..Default::default()
1434            },
1435            funs,
1436            ctx,
1437        )
1438        .await?
1439        .rel_rbum_kind_id;
1440        let rbum_kind_attrs = RbumKindAttrServ::find_rbums(
1441            &RbumKindAttrFilterReq {
1442                basic: RbumBasicFilterReq {
1443                    rbum_kind_id: Some(rel_rbum_kind_id.clone()),
1444                    ..Default::default()
1445                },
1446                secret,
1447                ..Default::default()
1448            },
1449            None,
1450            None,
1451            funs,
1452            ctx,
1453        )
1454        .await?;
1455        Ok((rel_rbum_kind_id, rbum_kind_attrs))
1456    }
1457
1458    /// Add or modify resource item extended attributes
1459    ///
1460    /// 添加或修改资源项扩展属性
1461    pub async fn add_or_modify_item_attrs(add_req: &RbumItemAttrsAddOrModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
1462        // Implicit rel_rbum_kind_attr scope check
1463        let (rel_rbum_kind_id, rbum_kind_attrs) = Self::find_res_kind_id_and_res_kind_attrs_by_item_id(&add_req.rel_rbum_item_id, None, funs, ctx).await?;
1464        let in_main_table_attrs = rbum_kind_attrs.iter().filter(|i| add_req.values.contains_key(&i.name) && i.main_column).collect::<Vec<&RbumKindAttrSummaryResp>>();
1465        let in_ext_table_attrs = rbum_kind_attrs.iter().filter(|i| add_req.values.contains_key(&i.name) && !i.main_column).collect::<Vec<&RbumKindAttrSummaryResp>>();
1466        if !in_main_table_attrs.is_empty() {
1467            // Implicit rel_rbum_item scope check
1468            let main_table_name = RbumKindServ::peek_rbum(&rel_rbum_kind_id, &RbumKindFilterReq::default(), funs, ctx).await?.ext_table_name;
1469
1470            let mut update_statement = Query::update();
1471            update_statement.table(Alias::new(&main_table_name));
1472
1473            for in_main_table_attr in in_main_table_attrs {
1474                let column_val = if in_main_table_attr.secret && !in_main_table_attr.dyn_default_value.is_empty() {
1475                    Self::replace_url_placeholder(&in_main_table_attr.dyn_default_value, &add_req.values, funs).await?
1476                } else if in_main_table_attr.secret {
1477                    in_main_table_attr.default_value.clone()
1478                } else {
1479                    add_req.values.get(&in_main_table_attr.name).expect("ignore").clone()
1480                };
1481
1482                let column_name = Alias::new(&in_main_table_attr.name);
1483                update_statement.value(column_name, Value::from(column_val));
1484            }
1485            update_statement.and_where(Expr::col(ID_FIELD.clone()).eq(add_req.rel_rbum_item_id.as_str()));
1486            funs.db().execute(&update_statement).await?;
1487        }
1488
1489        if !in_ext_table_attrs.is_empty() {
1490            for in_ext_table_attr in in_ext_table_attrs {
1491                let column_val = if in_ext_table_attr.secret && !in_ext_table_attr.dyn_default_value.is_empty() {
1492                    Self::replace_url_placeholder(&in_ext_table_attr.dyn_default_value, &add_req.values, funs).await?
1493                } else if in_ext_table_attr.secret {
1494                    in_ext_table_attr.default_value.clone()
1495                } else {
1496                    add_req.values.get(&in_ext_table_attr.name).expect("ignore").clone()
1497                };
1498
1499                let exist_item_attr_ids = Self::find_id_rbums(
1500                    &RbumItemAttrFilterReq {
1501                        basic: Default::default(),
1502                        rel_rbum_item_id: Some(add_req.rel_rbum_item_id.to_string()),
1503                        rel_rbum_kind_attr_id: Some(in_ext_table_attr.id.to_string()),
1504                    },
1505                    None,
1506                    None,
1507                    funs,
1508                    ctx,
1509                )
1510                .await?;
1511                if exist_item_attr_ids.is_empty() {
1512                    Self::add_rbum(
1513                        &mut RbumItemAttrAddReq {
1514                            value: column_val,
1515                            rel_rbum_item_id: add_req.rel_rbum_item_id.to_string(),
1516                            rel_rbum_kind_attr_id: in_ext_table_attr.id.to_string(),
1517                        },
1518                        funs,
1519                        ctx,
1520                    )
1521                    .await?;
1522                } else {
1523                    Self::modify_rbum(exist_item_attr_ids.first().expect("ignore"), &mut RbumItemAttrModifyReq { value: column_val }, funs, ctx).await?;
1524                }
1525            }
1526        }
1527
1528        Ok(())
1529    }
1530
1531    /// Get resource item extended attributes
1532    ///
1533    /// 获取资源项扩展属性集合
1534    ///
1535    /// # Returns
1536    ///
1537    /// The key is the attribute name, and the value is the attribute value.
1538    pub async fn find_item_attr_values(rbum_item_id: &str, secret: Option<bool>, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<HashMap<String, String>> {
1539        let (rel_rbum_kind_id, rbum_kind_attrs) = Self::find_res_kind_id_and_res_kind_attrs_by_item_id(rbum_item_id, secret, funs, ctx).await?;
1540        let in_main_table_attrs = rbum_kind_attrs.iter().filter(|i| i.main_column).collect::<Vec<&RbumKindAttrSummaryResp>>();
1541        let has_in_ext_table_attrs = rbum_kind_attrs.iter().any(|i| !i.main_column);
1542
1543        let mut values: HashMap<String, String> = HashMap::new();
1544        if !in_main_table_attrs.is_empty() {
1545            let ext_table_name = RbumKindServ::peek_rbum(&rel_rbum_kind_id, &RbumKindFilterReq::default(), funs, ctx).await?.ext_table_name;
1546
1547            let mut select_statement = Query::select();
1548            select_statement.from(Alias::new(&ext_table_name));
1549            for in_main_table_attr in &in_main_table_attrs {
1550                let column_name = Alias::new(&in_main_table_attr.name);
1551                select_statement.column(column_name);
1552            }
1553            select_statement.and_where(Expr::col(ID_FIELD.clone()).eq(rbum_item_id));
1554            let select_statement = funs.db().raw_conn().get_database_backend().build(&select_statement);
1555            if let Some(row) = funs.db().raw_conn().query_one(select_statement).await? {
1556                for in_main_table_attr in &in_main_table_attrs {
1557                    let value: String = row.try_get("", &in_main_table_attr.name)?;
1558                    values.insert(in_main_table_attr.name.clone(), value);
1559                }
1560            }
1561        }
1562
1563        if has_in_ext_table_attrs {
1564            let attr_values = Self::find_rbums(
1565                &RbumItemAttrFilterReq {
1566                    rel_rbum_item_id: Some(rbum_item_id.to_string()),
1567                    ..Default::default()
1568                },
1569                None,
1570                None,
1571                funs,
1572                ctx,
1573            )
1574            .await?;
1575            for attr_value in attr_values {
1576                values.insert(attr_value.rel_rbum_kind_attr_name, attr_value.value);
1577            }
1578        }
1579        Ok(values)
1580    }
1581
1582    async fn replace_url_placeholder(url: &str, values: &HashMap<String, String>, funs: &TardisFunsInst) -> TardisResult<String> {
1583        let resp = if RbumKindAttrServ::url_has_placeholder(url)? {
1584            let url: String = RbumKindAttrServ::url_replace(url, values)?;
1585            if RbumKindAttrServ::url_has_placeholder(&url)? {
1586                return Err(funs.err().bad_request(
1587                    &Self::get_obj_name(),
1588                    "replace_url_placeholder",
1589                    "url processing failure",
1590                    "400-rbum-kind-attr-dyn-url-illegal",
1591                ));
1592            }
1593            funs.web_client().get_to_str(&url, None).await
1594        } else {
1595            funs.web_client().get_to_str(url, None).await
1596        };
1597        match resp {
1598            Ok(resp) => Ok(resp.body.unwrap_or_else(|| "".to_string())),
1599            Err(e) => {
1600                return Err(funs.err().bad_request(
1601                    &Self::get_obj_name(),
1602                    "replace_url_placeholder",
1603                    &format!("url processing failure: {}", e),
1604                    "400-rbum-kind-attr-dyn-url-illegal",
1605                ));
1606            }
1607        }
1608    }
1609}
1610
1611#[derive(Debug, sea_orm::FromQueryResult)]
1612pub struct CodeResp {
1613    pub code: String,
1614}