bios_basic/rbum/serv/
rbum_rel_serv.rs

1use std::collections::HashMap;
2
3use async_trait::async_trait;
4use itertools::Itertools;
5use tardis::basic::dto::TardisContext;
6use tardis::basic::result::TardisResult;
7use tardis::db::reldb_client::IdResp;
8use tardis::db::sea_orm::sea_query::*;
9use tardis::db::sea_orm::IdenStatic;
10use tardis::db::sea_orm::*;
11use tardis::web::web_resp::TardisPage;
12use tardis::TardisFuns;
13use tardis::TardisFunsInst;
14
15use crate::rbum::domain::{rbum_item, rbum_kind_attr, rbum_rel, rbum_rel_attr, rbum_rel_env, rbum_set, rbum_set_cate};
16use crate::rbum::dto::rbum_filer_dto::{RbumBasicFilterReq, RbumRelExtFilterReq, RbumRelFilterReq, RbumSetCateFilterReq, RbumSetItemFilterReq};
17use crate::rbum::dto::rbum_rel_agg_dto::{RbumRelAggAddReq, RbumRelAggResp};
18use crate::rbum::dto::rbum_rel_attr_dto::{RbumRelAttrAddReq, RbumRelAttrDetailResp, RbumRelAttrModifyReq};
19use crate::rbum::dto::rbum_rel_dto::RbumRelEnvCheckReq;
20use crate::rbum::dto::rbum_rel_dto::{RbumRelAddReq, RbumRelBoneResp, RbumRelCheckReq, RbumRelDetailResp, RbumRelModifyReq, RbumRelSimpleFindReq};
21use crate::rbum::dto::rbum_rel_env_dto::{RbumRelEnvAddReq, RbumRelEnvDetailResp, RbumRelEnvModifyReq};
22use crate::rbum::rbum_enumeration::{RbumRelEnvKind, RbumRelFromKind, RbumSetCateLevelQueryKind};
23use crate::rbum::serv::rbum_crud_serv::{NameResp, RbumCrudOperation, RbumCrudQueryPackage};
24use crate::rbum::serv::rbum_item_serv::RbumItemServ;
25use crate::rbum::serv::rbum_kind_serv::RbumKindAttrServ;
26use crate::rbum::serv::rbum_set_serv::{RbumSetCateServ, RbumSetItemServ, RbumSetServ};
27
28use super::rbum_cert_serv::RbumCertServ;
29
30pub struct RbumRelServ;
31
32pub struct RbumRelAttrServ;
33
34pub struct RbumRelEnvServ;
35
36#[async_trait]
37impl RbumCrudOperation<rbum_rel::ActiveModel, RbumRelAddReq, RbumRelModifyReq, RbumRelDetailResp, RbumRelDetailResp, RbumRelFilterReq> for RbumRelServ {
38    fn get_table_name() -> &'static str {
39        rbum_rel::Entity.table_name()
40    }
41
42    async fn before_add_rbum(add_req: &mut RbumRelAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
43        let rel_rbum_table_name = match add_req.from_rbum_kind {
44            RbumRelFromKind::Item => RbumItemServ::get_table_name(),
45            RbumRelFromKind::Set => RbumSetServ::get_table_name(),
46            RbumRelFromKind::SetCate => RbumSetCateServ::get_table_name(),
47            RbumRelFromKind::Cert => RbumCertServ::get_table_name(),
48        };
49        if RbumRelFromKind::Cert == add_req.from_rbum_kind {
50            RbumCertServ::check_ownership(&add_req.from_rbum_id, funs, ctx).await?;
51        } else {
52            // The relationship check is changed from check_ownership to check_scope.
53            // for example, the account corresponding to the tenant can be associated to the app,
54            // where the account belongs to the tenant but scope=1, so it can be used by the application.
55            //
56            // 这里的关系检查从check_ownership改为check_scope。
57            // 比如租户对应的账户可以关联到应用,账户属于租户但scope=1,所以可以被应用使用。
58            Self::check_scope(&add_req.from_rbum_id, rel_rbum_table_name, funs, ctx).await?;
59        }
60
61        if add_req.to_rbum_item_id.trim().is_empty() {
62            return Err(funs.err().bad_request(&Self::get_obj_name(), "add", "to_rbum_item_id can not be empty", "400-rbum-rel-not-empty-item"));
63        }
64        // It may not be possible to get the data of to_rbum_item_id when there are multiple database instances.
65        //
66        // 当存在多个数据库实例时,可能无法获取to_rbum_item_id的数据。
67        if !add_req.to_is_outside {
68            Self::check_scope(&add_req.to_rbum_item_id, RbumItemServ::get_table_name(), funs, ctx).await?;
69        }
70        Ok(())
71    }
72
73    async fn package_add(add_req: &RbumRelAddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<rbum_rel::ActiveModel> {
74        Ok(rbum_rel::ActiveModel {
75            id: Set(TardisFuns::field.nanoid()),
76            tag: Set(add_req.tag.to_string()),
77            note: Set(add_req.note.as_ref().unwrap_or(&"".to_string()).to_string()),
78            from_rbum_kind: Set(add_req.from_rbum_kind.to_int()),
79            from_rbum_id: Set(add_req.from_rbum_id.to_string()),
80            to_rbum_item_id: Set(add_req.to_rbum_item_id.to_string()),
81            to_own_paths: Set(add_req.to_own_paths.to_string()),
82            ext: Set(add_req.ext.as_ref().unwrap_or(&"".to_string()).to_string()),
83            ..Default::default()
84        })
85    }
86
87    async fn package_modify(id: &str, modify_req: &RbumRelModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<rbum_rel::ActiveModel> {
88        let mut rbum_rel = rbum_rel::ActiveModel {
89            id: Set(id.to_string()),
90            ..Default::default()
91        };
92        if let Some(tag) = &modify_req.tag {
93            rbum_rel.tag = Set(tag.to_string());
94        }
95        if let Some(note) = &modify_req.note {
96            rbum_rel.note = Set(note.to_string());
97        }
98        if let Some(ext) = &modify_req.ext {
99            rbum_rel.ext = Set(ext.to_string());
100        }
101        Ok(rbum_rel)
102    }
103
104    async fn before_delete_rbum(id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<RbumRelDetailResp>> {
105        let mut query = Query::select();
106        query.column(rbum_rel::Column::Id).from(rbum_rel::Entity).and_where(Expr::col(rbum_rel::Column::Id).eq(id)).cond_where(all![any![
107            Expr::col(rbum_rel::Column::OwnPaths).like(format!("{}%", ctx.own_paths).as_str()),
108            Expr::col(rbum_rel::Column::ToOwnPaths).like(format!("{}%", ctx.own_paths).as_str())
109        ]]);
110        if funs.db().count(&query).await? == 0 {
111            return Err(funs.err().not_found(
112                &Self::get_obj_name(),
113                "delete",
114                &format!("ownership {}.{} is illegal by {}", Self::get_obj_name(), id, ctx.owner),
115                "404-rbum-*-ownership-illegal",
116            ));
117        }
118        Self::check_exist_before_delete(id, RbumRelAttrServ::get_table_name(), rbum_rel_attr::Column::RelRbumRelId.as_str(), funs).await?;
119        Self::check_exist_before_delete(id, RbumRelEnvServ::get_table_name(), rbum_rel_env::Column::RelRbumRelId.as_str(), funs).await?;
120        Ok(None)
121    }
122
123    async fn package_query(_: bool, filter: &RbumRelFilterReq, _: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<SelectStatement> {
124        let from_rbum_item_table = Alias::new("fromRbumItem");
125        let to_rbum_item_table = Alias::new("toRbumItem");
126        let mut query = Query::select();
127        query
128            .columns(vec![
129                (rbum_rel::Entity, rbum_rel::Column::Id),
130                (rbum_rel::Entity, rbum_rel::Column::Tag),
131                (rbum_rel::Entity, rbum_rel::Column::Note),
132                (rbum_rel::Entity, rbum_rel::Column::FromRbumKind),
133                (rbum_rel::Entity, rbum_rel::Column::FromRbumId),
134                (rbum_rel::Entity, rbum_rel::Column::ToRbumItemId),
135                (rbum_rel::Entity, rbum_rel::Column::ToOwnPaths),
136                (rbum_rel::Entity, rbum_rel::Column::Ext),
137                (rbum_rel::Entity, rbum_rel::Column::OwnPaths),
138                (rbum_rel::Entity, rbum_rel::Column::Owner),
139                (rbum_rel::Entity, rbum_rel::Column::CreateTime),
140                (rbum_rel::Entity, rbum_rel::Column::UpdateTime),
141            ])
142            .expr_as(
143                Expr::col((from_rbum_item_table.clone(), rbum_item::Column::Name)).if_null(""),
144                Alias::new("from_rbum_item_name"),
145            )
146            .expr_as(Expr::col((rbum_set::Entity, rbum_set::Column::Name)).if_null(""), Alias::new("from_rbum_set_name"))
147            .expr_as(
148                Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::Name)).if_null(""),
149                Alias::new("from_rbum_set_cate_name"),
150            )
151            .expr_as(
152                Expr::col((to_rbum_item_table.clone(), rbum_item::Column::Name)).if_null(""),
153                Alias::new("to_rbum_item_name"),
154            )
155            .from(rbum_rel::Entity)
156            .join_as(
157                JoinType::LeftJoin,
158                rbum_item::Entity,
159                from_rbum_item_table.clone(),
160                all![
161                    Expr::col((from_rbum_item_table.clone(), rbum_item::Column::Id)).equals((rbum_rel::Entity, rbum_rel::Column::FromRbumId)),
162                    Expr::col((rbum_rel::Entity, rbum_rel::Column::FromRbumKind)).eq(RbumRelFromKind::Item.to_int())
163                ],
164            )
165            .left_join(
166                rbum_set::Entity,
167                all![
168                    Expr::col((rbum_set::Entity, rbum_set::Column::Id)).equals((rbum_rel::Entity, rbum_rel::Column::FromRbumId)),
169                    Expr::col((rbum_rel::Entity, rbum_rel::Column::FromRbumKind)).eq(RbumRelFromKind::Set.to_int())
170                ],
171            )
172            .left_join(
173                rbum_set_cate::Entity,
174                all![
175                    Expr::col((rbum_set_cate::Entity, rbum_set_cate::Column::Id)).equals((rbum_rel::Entity, rbum_rel::Column::FromRbumId)),
176                    Expr::col((rbum_rel::Entity, rbum_rel::Column::FromRbumKind)).eq(RbumRelFromKind::SetCate.to_int())
177                ],
178            )
179            .join_as(
180                JoinType::LeftJoin,
181                rbum_item::Entity,
182                to_rbum_item_table.clone(),
183                Expr::col((to_rbum_item_table.clone(), rbum_item::Column::Id)).equals((rbum_rel::Entity, rbum_rel::Column::ToRbumItemId)),
184            );
185
186        if let Some(tag) = &filter.tag {
187            query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::Tag)).eq(tag.to_string()));
188        }
189        if let Some(from_rbum_kind) = &filter.from_rbum_kind {
190            query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::FromRbumKind)).eq(from_rbum_kind.to_int()));
191        }
192        if let Some(from_rbum_id) = &filter.from_rbum_id {
193            query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::FromRbumId)).eq(from_rbum_id.to_string()));
194        }
195        if let Some(to_rbum_item_id) = &filter.to_rbum_item_id {
196            query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::ToRbumItemId)).eq(to_rbum_item_id.to_string()));
197        }
198        if let Some(from_rbum_scope_levels) = &filter.from_rbum_scope_levels {
199            query.and_where(Expr::col((from_rbum_item_table, rbum_item::Column::ScopeLevel)).is_in(from_rbum_scope_levels.clone()));
200        }
201        if let Some(to_rbum_item_scope_levels) = &filter.to_rbum_item_scope_levels {
202            query.and_where(Expr::col((to_rbum_item_table, rbum_item::Column::ScopeLevel)).is_in(to_rbum_item_scope_levels.clone()));
203        }
204        if let Some(to_own_paths) = &filter.to_own_paths {
205            query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::ToOwnPaths)).eq(to_own_paths.to_string()));
206        }
207        if let Some(ext_eq) = &filter.ext_eq {
208            query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::Ext)).eq(ext_eq.to_string()));
209        }
210        if let Some(ext_like) = &filter.ext_like {
211            query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::Ext)).like(format!("%{ext_like}%").as_str()));
212        }
213        query.with_filter(Self::get_table_name(), &filter.basic, true, false, ctx);
214        Ok(query)
215    }
216}
217
218impl RbumRelServ {
219    /// Add a simple relationship
220    ///
221    /// 添加简单的关联关系
222    ///
223    /// The relationship source is the ``resource item``.
224    ///
225    /// 关联的来源方为``资源项``。
226    pub async fn add_simple_rel(tag: &str, from_rbum_id: &str, to_rbum_item_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
227        RbumRelServ::add_rbum(
228            &mut RbumRelAddReq {
229                tag: tag.to_string(),
230                note: None,
231                from_rbum_kind: RbumRelFromKind::Item,
232                from_rbum_id: from_rbum_id.to_string(),
233                to_rbum_item_id: to_rbum_item_id.to_string(),
234                to_own_paths: ctx.own_paths.to_string(),
235                to_is_outside: false,
236                ext: None,
237            },
238            funs,
239            ctx,
240        )
241        .await?;
242        Ok(())
243    }
244
245    /// Add a relationship
246    ///
247    /// 添加关联关系
248    pub async fn add_rel(add_req: &mut RbumRelAggAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
249        let rbum_rel_id = Self::add_rbum(&mut add_req.rel, funs, ctx).await?;
250        for attr in &add_req.attrs {
251            RbumRelAttrServ::add_rbum(
252                &mut RbumRelAttrAddReq {
253                    is_from: attr.is_from,
254                    value: attr.value.to_string(),
255                    name: attr.name.clone(),
256                    rel_rbum_rel_id: rbum_rel_id.to_string(),
257                    rel_rbum_kind_attr_id: attr.rel_rbum_kind_attr_id.clone(),
258                    record_only: attr.record_only,
259                },
260                funs,
261                ctx,
262            )
263            .await?;
264        }
265        for env in &add_req.envs {
266            RbumRelEnvServ::add_rbum(
267                &mut RbumRelEnvAddReq {
268                    kind: env.kind.clone(),
269                    value1: env.value1.to_string(),
270                    value2: env.value2.clone(),
271                    rel_rbum_rel_id: rbum_rel_id.to_string(),
272                },
273                funs,
274                ctx,
275            )
276            .await?;
277        }
278        Ok(rbum_rel_id)
279    }
280
281    /// Find the relationship target ids of the specified condition
282    ///
283    /// 查找指定条件的关联目标id集合
284    pub async fn find_from_id_rels(
285        tag: &str,
286        from_rbum_kind: &RbumRelFromKind,
287        with_sub: bool,
288        from_rbum_id: &str,
289        desc_sort_by_create: Option<bool>,
290        desc_sort_by_update: Option<bool>,
291        funs: &TardisFunsInst,
292        ctx: &TardisContext,
293    ) -> TardisResult<Vec<String>> {
294        Self::find_from_simple_rels(tag, from_rbum_kind, with_sub, from_rbum_id, desc_sort_by_create, desc_sort_by_update, funs, ctx)
295            .await
296            .map(|r| r.into_iter().map(|item| item.rel_id).collect())
297    }
298
299    /// Find the relationship target summary information set of the specified condition
300    ///
301    /// 查找指定条件的关联目标概要信息集合
302    pub async fn find_from_simple_rels(
303        tag: &str,
304        from_rbum_kind: &RbumRelFromKind,
305        with_sub: bool,
306        from_rbum_id: &str,
307        desc_sort_by_create: Option<bool>,
308        desc_sort_by_update: Option<bool>,
309        funs: &TardisFunsInst,
310        ctx: &TardisContext,
311    ) -> TardisResult<Vec<RbumRelBoneResp>> {
312        Self::find_rbums(
313            &RbumRelFilterReq {
314                basic: RbumBasicFilterReq {
315                    with_sub_own_paths: with_sub,
316                    ..Default::default()
317                },
318                tag: Some(tag.to_string()),
319                from_rbum_kind: Some(from_rbum_kind.clone()),
320                from_rbum_id: Some(from_rbum_id.to_string()),
321                ..Default::default()
322            },
323            desc_sort_by_create,
324            desc_sort_by_update,
325            funs,
326            ctx,
327        )
328        .await
329        .map(|r| r.into_iter().map(|item| RbumRelBoneResp::new(item, true)).collect())
330    }
331
332    /// Find the relationship aggregation detail information set of the specified condition
333    ///
334    /// 查找指定条件的关联聚合信息集合
335    pub async fn find_from_rels(
336        tag: &str,
337        from_rbum_kind: &RbumRelFromKind,
338        with_sub: bool,
339        from_rbum_id: &str,
340        desc_sort_by_create: Option<bool>,
341        desc_sort_by_update: Option<bool>,
342        funs: &TardisFunsInst,
343        ctx: &TardisContext,
344    ) -> TardisResult<Vec<RbumRelAggResp>> {
345        Self::find_rels(
346            &RbumRelFilterReq {
347                basic: RbumBasicFilterReq {
348                    with_sub_own_paths: with_sub,
349                    ..Default::default()
350                },
351                tag: Some(tag.to_string()),
352                from_rbum_kind: Some(from_rbum_kind.clone()),
353                from_rbum_id: Some(from_rbum_id.to_string()),
354                ..Default::default()
355            },
356            desc_sort_by_create,
357            desc_sort_by_update,
358            funs,
359            ctx,
360        )
361        .await
362    }
363
364    /// Page to get the relationship target ids of the specified condition
365    ///
366    /// 分页查找指定条件的关联目标id集合
367    pub async fn paginate_from_id_rels(
368        tag: &str,
369        from_rbum_kind: &RbumRelFromKind,
370        with_sub: bool,
371        from_rbum_id: &str,
372        page_number: u32,
373        page_size: u32,
374        desc_sort_by_create: Option<bool>,
375        desc_sort_by_update: Option<bool>,
376        funs: &TardisFunsInst,
377        ctx: &TardisContext,
378    ) -> TardisResult<TardisPage<String>> {
379        let result = Self::paginate_from_simple_rels(
380            tag,
381            from_rbum_kind,
382            with_sub,
383            from_rbum_id,
384            page_number,
385            page_size,
386            desc_sort_by_create,
387            desc_sort_by_update,
388            funs,
389            ctx,
390        )
391        .await?;
392        Ok(TardisPage {
393            page_size: result.page_size,
394            page_number: result.page_number,
395            total_size: result.total_size,
396            records: result.records.into_iter().map(|item| item.rel_id).collect(),
397        })
398    }
399
400    /// Page to get the relationship target summary information set of the specified condition
401    ///
402    /// 分页查找指定条件的关联目标概要信息集合
403    pub async fn paginate_from_simple_rels(
404        tag: &str,
405        from_rbum_kind: &RbumRelFromKind,
406        with_sub: bool,
407        from_rbum_id: &str,
408        page_number: u32,
409        page_size: u32,
410        desc_sort_by_create: Option<bool>,
411        desc_sort_by_update: Option<bool>,
412        funs: &TardisFunsInst,
413        ctx: &TardisContext,
414    ) -> TardisResult<TardisPage<RbumRelBoneResp>> {
415        let result = Self::paginate_rbums(
416            &RbumRelFilterReq {
417                basic: RbumBasicFilterReq {
418                    with_sub_own_paths: with_sub,
419                    ..Default::default()
420                },
421                tag: Some(tag.to_string()),
422                from_rbum_kind: Some(from_rbum_kind.clone()),
423                from_rbum_id: Some(from_rbum_id.to_string()),
424                ..Default::default()
425            },
426            page_number,
427            page_size,
428            desc_sort_by_create,
429            desc_sort_by_update,
430            funs,
431            ctx,
432        )
433        .await?;
434        Ok(TardisPage {
435            page_size: result.page_size,
436            page_number: result.page_number,
437            total_size: result.total_size,
438            records: result.records.into_iter().map(|item| RbumRelBoneResp::new(item, true)).collect(),
439        })
440    }
441
442    /// Page to get the relationship aggregation detail information set of the specified condition
443    ///
444    /// 分页查找指定条件的关联聚合信息集合
445    pub async fn paginate_from_rels(
446        tag: &str,
447        from_rbum_kind: &RbumRelFromKind,
448        with_sub: bool,
449        from_rbum_id: &str,
450        page_number: u32,
451        page_size: u32,
452        desc_sort_by_create: Option<bool>,
453        desc_sort_by_update: Option<bool>,
454        funs: &TardisFunsInst,
455        ctx: &TardisContext,
456    ) -> TardisResult<TardisPage<RbumRelAggResp>> {
457        Self::paginate_rels(
458            &RbumRelFilterReq {
459                basic: RbumBasicFilterReq {
460                    with_sub_own_paths: with_sub,
461                    ..Default::default()
462                },
463                tag: Some(tag.to_string()),
464                from_rbum_kind: Some(from_rbum_kind.clone()),
465                from_rbum_id: Some(from_rbum_id.to_string()),
466                ..Default::default()
467            },
468            page_number,
469            page_size,
470            desc_sort_by_create,
471            desc_sort_by_update,
472            funs,
473            ctx,
474        )
475        .await
476    }
477
478    /// Statistics the number of the specified condition
479    ///
480    /// 统计指定条件的关联记录数
481    pub async fn count_from_rels(tag: &str, from_rbum_kind: &RbumRelFromKind, with_sub: bool, from_rbum_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<u64> {
482        Self::count_rels(
483            &RbumRelFilterReq {
484                basic: RbumBasicFilterReq {
485                    with_sub_own_paths: with_sub,
486                    ..Default::default()
487                },
488                tag: Some(tag.to_string()),
489                from_rbum_kind: Some(from_rbum_kind.clone()),
490                from_rbum_id: Some(from_rbum_id.to_string()),
491                ..Default::default()
492            },
493            funs,
494            ctx,
495        )
496        .await
497    }
498
499    /// Find the relationship source ids of the specified condition
500    ///
501    /// 查找指定条件的关联来源id集合
502    pub async fn find_to_id_rels(
503        tag: &str,
504        to_rbum_item_id: &str,
505        desc_sort_by_create: Option<bool>,
506        desc_sort_by_update: Option<bool>,
507        funs: &TardisFunsInst,
508        ctx: &TardisContext,
509    ) -> TardisResult<Vec<String>> {
510        Self::find_to_simple_rels(tag, to_rbum_item_id, desc_sort_by_create, desc_sort_by_update, funs, ctx).await.map(|r| r.into_iter().map(|item| item.rel_id).collect())
511    }
512
513    /// Find the relationship source summary information set of the specified condition
514    ///
515    /// 查找指定条件的关联来源概要信息集合
516    pub async fn find_to_simple_rels(
517        tag: &str,
518        to_rbum_item_id: &str,
519        desc_sort_by_create: Option<bool>,
520        desc_sort_by_update: Option<bool>,
521        funs: &TardisFunsInst,
522        ctx: &TardisContext,
523    ) -> TardisResult<Vec<RbumRelBoneResp>> {
524        Self::find_rbums(
525            &RbumRelFilterReq {
526                basic: RbumBasicFilterReq {
527                    own_paths: Some(ctx.own_paths.to_string()),
528                    with_sub_own_paths: true,
529                    ignore_scope: true,
530                    ..Default::default()
531                },
532                tag: Some(tag.to_string()),
533                to_rbum_item_id: Some(to_rbum_item_id.to_string()),
534                ..Default::default()
535            },
536            desc_sort_by_create,
537            desc_sort_by_update,
538            funs,
539            ctx,
540        )
541        .await
542        .map(|r| r.into_iter().map(|item| RbumRelBoneResp::new(item, false)).collect())
543    }
544
545    /// Find the relationship aggregation detail information set of the specified condition
546    ///
547    /// 查找指定条件的关联聚合信息集合
548    pub async fn find_to_rels(
549        tag: &str,
550        to_rbum_item_id: &str,
551        desc_sort_by_create: Option<bool>,
552        desc_sort_by_update: Option<bool>,
553        funs: &TardisFunsInst,
554        ctx: &TardisContext,
555    ) -> TardisResult<Vec<RbumRelAggResp>> {
556        Self::find_rels(
557            &RbumRelFilterReq {
558                basic: RbumBasicFilterReq {
559                    own_paths: Some(ctx.own_paths.to_string()),
560                    with_sub_own_paths: true,
561                    ignore_scope: true,
562                    ..Default::default()
563                },
564                tag: Some(tag.to_string()),
565                to_rbum_item_id: Some(to_rbum_item_id.to_string()),
566                ..Default::default()
567            },
568            desc_sort_by_create,
569            desc_sort_by_update,
570            funs,
571            ctx,
572        )
573        .await
574    }
575
576    /// Page to get the relationship source ids of the specified condition
577    ///
578    /// 分页查找指定条件的关联来源id集合
579    pub async fn paginate_to_id_rels(
580        tag: &str,
581        to_rbum_item_id: &str,
582        page_number: u32,
583        page_size: u32,
584        desc_sort_by_create: Option<bool>,
585        desc_sort_by_update: Option<bool>,
586        funs: &TardisFunsInst,
587        ctx: &TardisContext,
588    ) -> TardisResult<TardisPage<String>> {
589        let result = Self::paginate_to_simple_rels(tag, to_rbum_item_id, page_number, page_size, desc_sort_by_create, desc_sort_by_update, funs, ctx).await?;
590        Ok(TardisPage {
591            page_size: result.page_size,
592            page_number: result.page_number,
593            total_size: result.total_size,
594            records: result.records.into_iter().map(|item| item.rel_id).collect(),
595        })
596    }
597
598    /// Page to get the relationship source summary information set of the specified condition
599    ///
600    /// 分页查找指定条件的关联来源概要信息集合
601    pub async fn paginate_to_simple_rels(
602        tag: &str,
603        to_rbum_item_id: &str,
604        page_number: u32,
605        page_size: u32,
606        desc_sort_by_create: Option<bool>,
607        desc_sort_by_update: Option<bool>,
608        funs: &TardisFunsInst,
609        ctx: &TardisContext,
610    ) -> TardisResult<TardisPage<RbumRelBoneResp>> {
611        let result = Self::paginate_rbums(
612            &RbumRelFilterReq {
613                basic: RbumBasicFilterReq {
614                    own_paths: Some(ctx.own_paths.to_string()),
615                    with_sub_own_paths: true,
616                    ignore_scope: true,
617                    ..Default::default()
618                },
619                tag: Some(tag.to_string()),
620                to_rbum_item_id: Some(to_rbum_item_id.to_string()),
621                ..Default::default()
622            },
623            page_number,
624            page_size,
625            desc_sort_by_create,
626            desc_sort_by_update,
627            funs,
628            ctx,
629        )
630        .await?;
631        Ok(TardisPage {
632            page_size: result.page_size,
633            page_number: result.page_number,
634            total_size: result.total_size,
635            records: result.records.into_iter().map(|item| RbumRelBoneResp::new(item, false)).collect(),
636        })
637    }
638
639    /// Page to get the relationship aggregation detail information set of the specified condition
640    ///
641    /// 分页查找指定条件的关联聚合信息集合
642    pub async fn paginate_to_rels(
643        tag: &str,
644        to_rbum_item_id: &str,
645        page_number: u32,
646        page_size: u32,
647        desc_sort_by_create: Option<bool>,
648        desc_sort_by_update: Option<bool>,
649        funs: &TardisFunsInst,
650        ctx: &TardisContext,
651    ) -> TardisResult<TardisPage<RbumRelAggResp>> {
652        Self::paginate_rels(
653            &RbumRelFilterReq {
654                basic: RbumBasicFilterReq {
655                    own_paths: Some(ctx.own_paths.to_string()),
656                    with_sub_own_paths: true,
657                    ignore_scope: true,
658                    ..Default::default()
659                },
660                tag: Some(tag.to_string()),
661                to_rbum_item_id: Some(to_rbum_item_id.to_string()),
662                ..Default::default()
663            },
664            page_number,
665            page_size,
666            desc_sort_by_create,
667            desc_sort_by_update,
668            funs,
669            ctx,
670        )
671        .await
672    }
673
674    /// Statistics the number of the specified condition
675    ///
676    /// 统计指定条件的关联记录数
677    pub async fn count_to_rels(tag: &str, to_rbum_item_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<u64> {
678        Self::count_rels(
679            &RbumRelFilterReq {
680                basic: RbumBasicFilterReq {
681                    own_paths: Some(ctx.own_paths.to_string()),
682                    with_sub_own_paths: true,
683                    ignore_scope: true,
684                    ..Default::default()
685                },
686                tag: Some(tag.to_string()),
687                to_rbum_item_id: Some(to_rbum_item_id.to_string()),
688                ..Default::default()
689            },
690            funs,
691            ctx,
692        )
693        .await
694    }
695
696    /// Statistics the number of the specified condition
697    ///
698    /// 统计指定条件的关联记录数
699    async fn count_rels(filter: &RbumRelFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<u64> {
700        RbumRelServ::count_rbums(filter, funs, ctx).await
701    }
702
703    /// Find the relationship summary information set of the specified condition
704    ///
705    /// 查找指定条件的关联概要信息集合
706    ///
707    /// If ``package_to_info = true``, return the relationship target information, otherwise, return the relationship source information.
708    ///
709    /// 当 ``package_to_info = true`` 时返回关联目标信息,反之,返回关联来源信息。
710    pub async fn find_simple_rels(
711        filter: &RbumRelFilterReq,
712        desc_sort_by_create: Option<bool>,
713        desc_sort_by_update: Option<bool>,
714        package_to_info: bool,
715        funs: &TardisFunsInst,
716        ctx: &TardisContext,
717    ) -> TardisResult<Vec<RbumRelBoneResp>> {
718        RbumRelServ::find_rbums(filter, desc_sort_by_create, desc_sort_by_update, funs, ctx)
719            .await
720            .map(|r| r.into_iter().map(|item| RbumRelBoneResp::new(item, package_to_info)).collect())
721    }
722
723    /// Find the relationship aggregation detail information set of the specified condition
724    ///
725    /// 查找指定条件的关联聚合信息集合
726    pub async fn find_rels(
727        filter: &RbumRelFilterReq,
728        desc_sort_by_create: Option<bool>,
729        desc_sort_by_update: Option<bool>,
730        funs: &TardisFunsInst,
731        ctx: &TardisContext,
732    ) -> TardisResult<Vec<RbumRelAggResp>> {
733        let rbum_rels = RbumRelServ::find_rbums(filter, desc_sort_by_create, desc_sort_by_update, funs, ctx).await?;
734        Self::package_agg_rels(rbum_rels, filter, funs, ctx).await
735    }
736
737    /// Page to get the relationship summary information set of the specified condition
738    ///
739    /// 分页查找指定条件的关联概要信息集合
740    ///
741    /// If ``package_to_info = true``, return the relationship target information, otherwise, return the relationship source information.
742    ///
743    /// 当 ``package_to_info = true`` 时返回关联目标信息,反之,返回关联来源信息。
744    pub async fn paginate_simple_rels(
745        filter: &RbumRelFilterReq,
746        page_number: u32,
747        page_size: u32,
748        desc_sort_by_create: Option<bool>,
749        desc_sort_by_update: Option<bool>,
750        package_to_info: bool,
751        funs: &TardisFunsInst,
752        ctx: &TardisContext,
753    ) -> TardisResult<TardisPage<RbumRelBoneResp>> {
754        let result = RbumRelServ::paginate_rbums(filter, page_number, page_size, desc_sort_by_create, desc_sort_by_update, funs, ctx).await?;
755        Ok(TardisPage {
756            page_size: result.page_size,
757            page_number: result.page_number,
758            total_size: result.total_size,
759            records: result.records.into_iter().map(|item| RbumRelBoneResp::new(item, package_to_info)).collect(),
760        })
761    }
762
763    /// Page to get the relationship aggregation detail information set of the specified condition
764    ///
765    /// 分页查找指定条件的关联聚合信息集合
766    pub async fn paginate_rels(
767        filter: &RbumRelFilterReq,
768        page_number: u32,
769        page_size: u32,
770        desc_sort_by_create: Option<bool>,
771        desc_sort_by_update: Option<bool>,
772        funs: &TardisFunsInst,
773        ctx: &TardisContext,
774    ) -> TardisResult<TardisPage<RbumRelAggResp>> {
775        let rbum_rels = RbumRelServ::paginate_rbums(filter, page_number, page_size, desc_sort_by_create, desc_sort_by_update, funs, ctx).await?;
776        let result = Self::package_agg_rels(rbum_rels.records, filter, funs, ctx).await?;
777        Ok(TardisPage {
778            page_size: page_size as u64,
779            page_number: page_number as u64,
780            total_size: rbum_rels.total_size,
781            records: result,
782        })
783    }
784
785    /// Package relationship aggregation information
786    ///
787    /// 组装关联聚合信息
788    async fn package_agg_rels(rels: Vec<RbumRelDetailResp>, filter: &RbumRelFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Vec<RbumRelAggResp>> {
789        let mut result = Vec::with_capacity(rels.len());
790        for rel in rels {
791            let rbum_rel_id = rel.id.to_string();
792            let resp = RbumRelAggResp {
793                rel,
794                attrs: RbumRelAttrServ::find_rbums(
795                    &RbumRelExtFilterReq {
796                        basic: filter.basic.clone(),
797                        rel_rbum_rel_id: Some(rbum_rel_id.clone()),
798                    },
799                    None,
800                    None,
801                    funs,
802                    ctx,
803                )
804                .await?,
805                envs: RbumRelEnvServ::find_rbums(
806                    &RbumRelExtFilterReq {
807                        basic: filter.basic.clone(),
808                        rel_rbum_rel_id: Some(rbum_rel_id.clone()),
809                    },
810                    None,
811                    None,
812                    funs,
813                    ctx,
814                )
815                .await?,
816            };
817            result.push(resp);
818        }
819        Ok(result)
820    }
821
822    /// Find the relationship id set of the specified condition
823    ///
824    /// 查找指定条件的关联id集合
825    pub async fn find_rel_ids(find_req: &RbumRelSimpleFindReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Vec<String>> {
826        let ids = funs.db().find_dtos::<IdResp>(&Self::package_simple_rel_query(find_req, ctx)).await?.iter().map(|i| i.id.to_string()).collect::<Vec<String>>();
827        Ok(ids)
828    }
829
830    /// Check whether the relationship of the specified simple condition exists
831    ///
832    /// 检查指定的简单条件的关联是否存在
833    pub async fn check_simple_rel(find_req: &RbumRelSimpleFindReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<bool> {
834        funs.db().count(&Self::package_simple_rel_query(find_req, ctx)).await.map(|i| i > 0)
835    }
836
837    fn package_simple_rel_query(find_req: &RbumRelSimpleFindReq, ctx: &TardisContext) -> SelectStatement {
838        let mut query = Query::select();
839        query.column(rbum_rel::Column::Id).from(rbum_rel::Entity);
840        if let Some(tag) = &find_req.tag {
841            query.and_where(Expr::col(rbum_rel::Column::Tag).eq(tag.to_string()));
842        }
843        if let Some(from_rbum_kind) = &find_req.from_rbum_kind {
844            query.and_where(Expr::col(rbum_rel::Column::FromRbumKind).eq(from_rbum_kind.to_int()));
845        }
846        if let Some(from_rbum_id) = &find_req.from_rbum_id {
847            query.and_where(Expr::col(rbum_rel::Column::FromRbumId).eq(from_rbum_id.to_string()));
848        }
849        if let Some(to_rbum_item_id) = &find_req.to_rbum_item_id {
850            query.and_where(Expr::col(rbum_rel::Column::ToRbumItemId).eq(to_rbum_item_id.to_string()));
851        }
852        if let Some(from_own_paths) = &find_req.from_own_paths {
853            query.and_where(Expr::col(rbum_rel::Column::OwnPaths).eq(from_own_paths.to_string()));
854        }
855        if let Some(to_rbum_own_paths) = &find_req.to_rbum_own_paths {
856            query.and_where(Expr::col(rbum_rel::Column::ToOwnPaths).eq(to_rbum_own_paths.to_string()));
857        }
858        query.cond_where(all![any![
859            Expr::col(rbum_rel::Column::OwnPaths).like(format!("{}%", ctx.own_paths).as_str()),
860            Expr::col(rbum_rel::Column::ToOwnPaths).like(format!("{}%", ctx.own_paths).as_str())
861        ]]);
862        query
863    }
864
865    /// Check whether the relationship of the specified condition exists
866    ///
867    /// 检查指定的条件的关联是否存在
868    pub async fn check_rel(check_req: &RbumRelCheckReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<bool> {
869        // 1. Check whether the direct association exists
870        //
871        // 1. 检查直接关联是否存在
872        if Self::do_check_rel(
873            &check_req.tag,
874            Some(vec![check_req.from_rbum_kind.clone()]),
875            Some(vec![check_req.from_rbum_id.clone()]),
876            Some(check_req.to_rbum_item_id.clone()),
877            &check_req.from_attrs,
878            &check_req.to_attrs,
879            &check_req.envs,
880            funs,
881            ctx,
882        )
883        .await?
884        {
885            return Ok(true);
886        }
887        // 2. Get the resource set categories(nodes)
888        //
889        // 2. 获取对应的资源集分类(节点)集合
890        //
891        // rel_rbum_set_cates : HashMap<set id, Vec(category id, system code)>
892        let rel_rbum_set_cates = if check_req.from_rbum_kind == RbumRelFromKind::Item {
893            RbumSetItemServ::find_rbums(
894                &RbumSetItemFilterReq {
895                    basic: Default::default(),
896                    rel_rbum_item_ids: Some(vec![check_req.from_rbum_id.clone()]),
897                    ..Default::default()
898                },
899                None,
900                None,
901                funs,
902                ctx,
903            )
904            .await?
905            .into_iter()
906            .into_group_map_by(|i| i.rel_rbum_set_id.clone())
907            .into_iter()
908            .map(|(set_id, cates)| {
909                (
910                    set_id,
911                    cates.into_iter().map(|cate| (cate.rel_rbum_set_cate_id.unwrap_or_default(), cate.rel_rbum_set_cate_sys_code.unwrap_or_default())).collect(),
912                )
913            })
914            .collect::<HashMap<String, Vec<(String, String)>>>()
915        } else if check_req.from_rbum_kind == RbumRelFromKind::SetCate {
916            let set_cate = RbumSetCateServ::peek_rbum(&check_req.from_rbum_id, &RbumSetCateFilterReq::default(), funs, ctx).await?;
917            HashMap::from([(set_cate.rel_rbum_set_id.clone(), vec![(set_cate.id, set_cate.sys_code)])])
918        } else {
919            return Ok(false);
920        };
921
922        for (set_id, cates) in rel_rbum_set_cates {
923            // 3. Get the parent id set of the associated resource set category
924            //
925            // 3. 获取关联的资源集分类的父级id集合
926            let mut set_cate_parent_ids = RbumSetCateServ::find_id_rbums(
927                &RbumSetCateFilterReq {
928                    basic: Default::default(),
929                    rel_rbum_set_id: Some(set_id.clone()),
930                    sys_codes: Some(cates.iter().map(|i| i.1.clone()).collect()),
931                    sys_code_query_kind: Some(RbumSetCateLevelQueryKind::Parent),
932                    ..Default::default()
933                },
934                None,
935                None,
936                funs,
937                ctx,
938            )
939            .await?;
940            if check_req.from_rbum_kind == RbumRelFromKind::Item {
941                // If the source type of the request is ``item``, the source id needs to be added to the parent id set.
942                // Because the source type has been switched, the source id needs to be rejudged.
943                //
944                // 如果请求的来源类型是``item``, 则需要把来源id加入到父级id集合中。因为后续切换了来源类型,故这个来源id需要重新判断。
945                set_cate_parent_ids.insert(0, check_req.from_rbum_id.clone());
946            }
947            // Add the id of the resource set to the parent id set.
948            //
949            // 把资源集的id也添加进来。
950            set_cate_parent_ids.push(set_id);
951
952            // 4. Check whether the association on the resource set/resource set category(node) exists
953            //
954            // 4. 检查资源集/资源集分类(节点)上的关联是否存在
955            if Self::do_check_rel(
956                &check_req.tag,
957                // Two source types are used here, and the ids of these two types are nanoid, so the conflict probability is very low.
958                //
959                // 这里使用了两个来源类型,这两个类型的id都是nanoid,冲突的概率很低。
960                Some(vec![RbumRelFromKind::SetCate, RbumRelFromKind::Set]),
961                Some(set_cate_parent_ids),
962                Some(check_req.to_rbum_item_id.clone()),
963                &check_req.from_attrs,
964                &check_req.to_attrs,
965                &check_req.envs,
966                funs,
967                ctx,
968            )
969            .await?
970            {
971                return Ok(true);
972            }
973        }
974        Ok(false)
975    }
976
977    /// Execute the relationship check
978    ///
979    ///
980    /// 执行关联关系检查
981    ///
982    /// This function's core SQL is similar to the following:
983    ///
984    /// ```sql
985    /// select
986    ///  rel.id,
987    ///  attr_cond.attr_count,
988    ///  attr_all.attr_count
989    ///from
990    ///  rbum_rel rel
991    /// -- Fetch the number of related attribute records with matching conditions
992    ///  left join (
993    ///    select
994    ///      rel_rbum_rel_id,
995    ///      count(1) attr_count
996    ///    from
997    ///      rbum_rel_attr attr
998    ///    where
999    ///      attr.rel_rbum_kind_attr_id = 'a001'
1000    ///      and attr.value = 'jzy'
1001    ///      or attr.rel_rbum_kind_attr_id = 'a002'
1002    ///      and attr.value = '30'
1003    ///    GROUP by
1004    ///      rel_rbum_rel_id
1005    ///  ) attr_cond on attr_cond.rel_rbum_rel_id = rel.id
1006    /// -- Fetch the number of records of all related attributes
1007    ///  left join (
1008    ///    select
1009    ///      rel_rbum_rel_id,
1010    ///      count(1) attr_count
1011    ///    from
1012    ///      rbum_rel_attr attr
1013    ///    GROUP by
1014    ///      rel_rbum_rel_id
1015    ///  ) attr_all on attr_all.rel_rbum_rel_id = rel.id
1016    ///where
1017    ///  rel.from_rbum_id = 'f01'
1018    ///  and (
1019    ///  -- The number of related attribute records with matching conditions is equal to the number of all related attribute records
1020    ///    attr_cond is null and attr_all is null
1021    ///    or attr_cond.attr_count = attr_all.attr_count
1022    ///  )
1023    ///
1024    /// ```
1025    async fn do_check_rel(
1026        tag: &str,
1027        from_rbum_kinds: Option<Vec<RbumRelFromKind>>,
1028        from_rbum_ids: Option<Vec<String>>,
1029        to_rbum_item_id: Option<String>,
1030        from_attrs: &HashMap<String, String>,
1031        to_attrs: &HashMap<String, String>,
1032        envs: &Vec<RbumRelEnvCheckReq>,
1033        funs: &TardisFunsInst,
1034        ctx: &TardisContext,
1035    ) -> TardisResult<bool> {
1036        let mut query = Query::select();
1037        query.column((rbum_rel::Entity, rbum_rel::Column::Id)).from(rbum_rel::Entity);
1038        query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::Tag)).eq(tag));
1039        if let Some(from_rbum_kinds) = from_rbum_kinds {
1040            query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::FromRbumKind)).is_in(from_rbum_kinds.into_iter().map(|i| i.to_int()).collect::<Vec<_>>()));
1041        }
1042        if let Some(from_rbum_ids) = from_rbum_ids {
1043            query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::FromRbumId)).is_in(from_rbum_ids));
1044        }
1045        if let Some(to_rbum_item_id) = to_rbum_item_id {
1046            query.and_where(Expr::col((rbum_rel::Entity, rbum_rel::Column::ToRbumItemId)).eq(to_rbum_item_id));
1047        }
1048        query.cond_where(all![any![
1049            Expr::col((rbum_rel::Entity, rbum_rel::Column::OwnPaths)).like(format!("{}%", ctx.own_paths).as_str()),
1050            Expr::col((rbum_rel::Entity, rbum_rel::Column::ToOwnPaths)).like(format!("{}%", ctx.own_paths).as_str())
1051        ]]);
1052        if !from_attrs.is_empty() || !to_attrs.is_empty() {
1053            let attr_table_without_cond = Alias::new(format!("{}_without_cond", RbumRelAttrServ::get_table_name()));
1054            let attr_table_with_cond = Alias::new(format!("{}_with_cond", RbumRelAttrServ::get_table_name()));
1055
1056            query.join_subquery(
1057                JoinType::LeftJoin,
1058                Query::select()
1059                    .column(rbum_rel_attr::Column::RelRbumRelId)
1060                    .expr_as(Expr::col(rbum_rel_attr::Column::RelRbumRelId).count(), Alias::new("attr_count"))
1061                    .from(rbum_rel_attr::Entity)
1062                    .and_where(Expr::col(rbum_rel_attr::Column::RecordOnly).eq(false))
1063                    .group_by_col(rbum_rel_attr::Column::RelRbumRelId)
1064                    .take(),
1065                attr_table_without_cond.clone(),
1066                Expr::col((attr_table_without_cond.clone(), rbum_rel_attr::Column::RelRbumRelId)).equals((rbum_rel::Entity, rbum_rel::Column::Id)),
1067            );
1068
1069            let mut attr_conds = Cond::any();
1070            for (name, value) in from_attrs {
1071                attr_conds = attr_conds.add(all![
1072                    Expr::col(rbum_rel_attr::Column::Name).eq(name),
1073                    Expr::col(rbum_rel_attr::Column::Value).eq(value),
1074                    Expr::col(rbum_rel_attr::Column::IsFrom).eq(true)
1075                ]);
1076            }
1077            for (name, value) in to_attrs {
1078                attr_conds = attr_conds.add(all![
1079                    Expr::col(rbum_rel_attr::Column::Name).eq(name),
1080                    Expr::col(rbum_rel_attr::Column::Value).eq(value),
1081                    Expr::col(rbum_rel_attr::Column::IsFrom).eq(false)
1082                ]);
1083            }
1084            query.join_subquery(
1085                JoinType::LeftJoin,
1086                Query::select()
1087                    .column(rbum_rel_attr::Column::RelRbumRelId)
1088                    .expr_as(Expr::col(rbum_rel_attr::Column::RelRbumRelId).count(), Alias::new("attr_count"))
1089                    .from(rbum_rel_attr::Entity)
1090                    .and_where(Expr::col(rbum_rel_attr::Column::RecordOnly).eq(false))
1091                    .cond_where(attr_conds)
1092                    .group_by_col(rbum_rel_attr::Column::RelRbumRelId)
1093                    .take(),
1094                attr_table_with_cond.clone(),
1095                Expr::col((attr_table_with_cond.clone(), rbum_rel_attr::Column::RelRbumRelId)).equals((rbum_rel::Entity, rbum_rel::Column::Id)),
1096            );
1097
1098            query.cond_where(any![
1099                all![
1100                    Expr::col((attr_table_without_cond.clone(), Alias::new("attr_count"))).is_null(),
1101                    Expr::col((attr_table_with_cond.clone(), Alias::new("attr_count"))).is_null()
1102                ],
1103                Expr::col((attr_table_without_cond.clone(), Alias::new("attr_count"))).eq(Expr::col((attr_table_with_cond.clone(), Alias::new("attr_count"))))
1104            ]);
1105        } else {
1106            // The incoming association property is empty, so the actual association property is also required to be empty
1107            //
1108            // 传入的关联属性为空,所以要求实际的关联属性也为空
1109            query.left_join(
1110                rbum_rel_attr::Entity,
1111                all![
1112                    Expr::col((rbum_rel_attr::Entity, rbum_rel_attr::Column::RelRbumRelId)).equals((rbum_rel::Entity, rbum_rel::Column::Id)),
1113                    Expr::col((rbum_rel_attr::Entity, rbum_rel_attr::Column::RecordOnly)).eq(false)
1114                ],
1115            );
1116
1117            query.and_where(Expr::col((rbum_rel_attr::Entity, rbum_rel_attr::Column::Id)).is_null());
1118        }
1119
1120        if !envs.is_empty() {
1121            let env_table_without_cond = Alias::new(format!("{}_without_cond", RbumRelEnvServ::get_table_name()));
1122            let env_table_with_cond = Alias::new(format!("{}_with_cond", RbumRelEnvServ::get_table_name()));
1123
1124            query.join_subquery(
1125                JoinType::LeftJoin,
1126                Query::select()
1127                    .column(rbum_rel_env::Column::RelRbumRelId)
1128                    .expr_as(Expr::col(rbum_rel_env::Column::RelRbumRelId).count(), Alias::new("env_count"))
1129                    .from(rbum_rel_env::Entity)
1130                    .group_by_col(rbum_rel_env::Column::RelRbumRelId)
1131                    .take(),
1132                env_table_without_cond.clone(),
1133                Expr::col((env_table_without_cond.clone(), rbum_rel_env::Column::RelRbumRelId)).equals((rbum_rel::Entity, rbum_rel::Column::Id)),
1134            );
1135            let mut env_conds = Cond::any();
1136            for env in envs {
1137                match env.kind {
1138                    RbumRelEnvKind::DatetimeRange | RbumRelEnvKind::TimeRange => match env.value.parse::<i64>() {
1139                        Ok(num) => {
1140                            env_conds = env_conds.add(all![
1141                                Expr::col(rbum_rel_env::Column::Kind).eq(env.kind.to_int()),
1142                                Expr::expr(Func::cast_as(Expr::col(rbum_rel_env::Column::Value1), Alias::new("INTEGER"))).lte(num),
1143                                Expr::expr(Func::cast_as(Expr::col(rbum_rel_env::Column::Value2), Alias::new("INTEGER"))).gte(num)
1144                            ]);
1145                        }
1146                        Err(_) => {
1147                            return Err(funs.err().bad_request(
1148                                &Self::get_obj_name(),
1149                                "check",
1150                                &format!("env value {} is not a number", env.value),
1151                                "400-rbum-rel-env-value-not-number",
1152                            ));
1153                        }
1154                    },
1155                    RbumRelEnvKind::CallFrequency | RbumRelEnvKind::CallCount => match env.value.parse::<i64>() {
1156                        Ok(num) => {
1157                            env_conds = env_conds.add(all![
1158                                Expr::col(rbum_rel_env::Column::Kind).eq(env.kind.to_int()),
1159                                Expr::expr(Func::cast_as(Expr::col(rbum_rel_env::Column::Value1), Alias::new("INTEGER"))).gte(num)
1160                            ]);
1161                        }
1162                        Err(_) => {
1163                            return Err(funs.err().bad_request(
1164                                &Self::get_obj_name(),
1165                                "check",
1166                                &format!("env value {} is not a number", env.value),
1167                                "400-rbum-rel-env-value-not-number",
1168                            ));
1169                        }
1170                    },
1171                    RbumRelEnvKind::Ips => {
1172                        env_conds = env_conds.add(all![
1173                            Expr::col(rbum_rel_env::Column::Kind).eq(env.kind.to_int()),
1174                            Expr::col(rbum_rel_env::Column::Value1).like(format!("%{}%", env.value))
1175                        ]);
1176                    }
1177                }
1178            }
1179            query.join_subquery(
1180                JoinType::LeftJoin,
1181                Query::select()
1182                    .column(rbum_rel_env::Column::RelRbumRelId)
1183                    .expr_as(Expr::col(rbum_rel_env::Column::RelRbumRelId).count(), Alias::new("env_count"))
1184                    .from(rbum_rel_env::Entity)
1185                    .cond_where(env_conds)
1186                    .group_by_col(rbum_rel_env::Column::RelRbumRelId)
1187                    .take(),
1188                env_table_with_cond.clone(),
1189                Expr::col((env_table_with_cond.clone(), rbum_rel_env::Column::RelRbumRelId)).equals((rbum_rel::Entity, rbum_rel::Column::Id)),
1190            );
1191            query.cond_where(any![
1192                all![
1193                    Expr::col((env_table_without_cond.clone(), Alias::new("env_count"))).is_null(),
1194                    Expr::col((env_table_with_cond.clone(), Alias::new("env_count"))).is_null()
1195                ],
1196                Expr::col((env_table_without_cond.clone(), Alias::new("env_count"))).eq(Expr::col((env_table_with_cond.clone(), Alias::new("env_count"))))
1197            ]);
1198        } else {
1199            // The incoming association environment is empty, so the actual association environment is also required to be empty
1200            //
1201            // 传入的关联环境为空,所以要求实际的关联环境也为空
1202            query.left_join(
1203                rbum_rel_env::Entity,
1204                Expr::col((rbum_rel_env::Entity, rbum_rel_env::Column::RelRbumRelId)).equals((rbum_rel::Entity, rbum_rel::Column::Id)),
1205            );
1206
1207            query.and_where(Expr::col((rbum_rel_env::Entity, rbum_rel_env::Column::Id)).is_null());
1208        }
1209
1210        funs.db().count(&query).await.map(|i| i > 0)
1211    }
1212
1213    /// Delete the relationship(Including all conditions)
1214    ///
1215    /// 删除关联关系(包含所有限定条件)
1216    pub async fn delete_rel_with_ext(id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<u64> {
1217        let rbum_rel_env_ids = RbumRelEnvServ::find_id_rbums(
1218            &RbumRelExtFilterReq {
1219                basic: Default::default(),
1220                rel_rbum_rel_id: Some(id.to_string()),
1221            },
1222            None,
1223            None,
1224            funs,
1225            ctx,
1226        )
1227        .await?;
1228        let rbum_rel_attr_ids = RbumRelAttrServ::find_id_rbums(
1229            &RbumRelExtFilterReq {
1230                basic: Default::default(),
1231                rel_rbum_rel_id: Some(id.to_string()),
1232            },
1233            None,
1234            None,
1235            funs,
1236            ctx,
1237        )
1238        .await?;
1239        for rbum_rel_env_id in rbum_rel_env_ids {
1240            RbumRelEnvServ::delete_rbum(&rbum_rel_env_id, funs, ctx).await?;
1241        }
1242        for rbum_rel_attr_id in rbum_rel_attr_ids {
1243            RbumRelAttrServ::delete_rbum(&rbum_rel_attr_id, funs, ctx).await?;
1244        }
1245        RbumRelServ::delete_rbum(id, funs, ctx).await
1246    }
1247}
1248
1249#[async_trait]
1250impl RbumCrudOperation<rbum_rel_attr::ActiveModel, RbumRelAttrAddReq, RbumRelAttrModifyReq, RbumRelAttrDetailResp, RbumRelAttrDetailResp, RbumRelExtFilterReq> for RbumRelAttrServ {
1251    fn get_table_name() -> &'static str {
1252        rbum_rel_attr::Entity.table_name()
1253    }
1254
1255    async fn before_add_rbum(add_req: &mut RbumRelAttrAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
1256        Self::check_ownership_with_table_name(&add_req.rel_rbum_rel_id, RbumRelServ::get_table_name(), funs, ctx).await?;
1257        if let Some(rel_rbum_kind_attr_id) = &add_req.rel_rbum_kind_attr_id {
1258            Self::check_scope(rel_rbum_kind_attr_id, RbumKindAttrServ::get_table_name(), funs, ctx).await?;
1259        }
1260        Ok(())
1261    }
1262
1263    async fn package_add(add_req: &RbumRelAttrAddReq, funs: &TardisFunsInst, _: &TardisContext) -> TardisResult<rbum_rel_attr::ActiveModel> {
1264        let rbum_rel_attr_name = if let Some(rel_rbum_kind_attr_id) = &add_req.rel_rbum_kind_attr_id {
1265            funs.db()
1266                .get_dto::<NameResp>(
1267                    Query::select().column(rbum_kind_attr::Column::Name).from(rbum_kind_attr::Entity).and_where(Expr::col(rbum_kind_attr::Column::Id).eq(rel_rbum_kind_attr_id)),
1268                )
1269                .await?
1270                .ok_or_else(|| {
1271                    funs.err().not_found(
1272                        &Self::get_obj_name(),
1273                        "add",
1274                        &format!("not found rbum_kind_attr {}", rel_rbum_kind_attr_id),
1275                        "404-rbum-rel-not-exist-kind-attr",
1276                    )
1277                })?
1278                .name
1279        } else if let Some(name) = &add_req.name {
1280            name.to_string()
1281        } else {
1282            return Err(funs.err().not_found(
1283                &Self::get_obj_name(),
1284                "add",
1285                "[rel_rbum_kind_attr_id] and [name] cannot be empty at the same time",
1286                "400-rbum-rel-name-require",
1287            ));
1288        };
1289
1290        if funs
1291            .db()
1292            .count(
1293                Query::select()
1294                    .column(rbum_rel_attr::Column::Id)
1295                    .from(rbum_rel_attr::Entity)
1296                    .and_where(Expr::col(rbum_rel_attr::Column::RelRbumRelId).eq(&add_req.rel_rbum_rel_id))
1297                    .and_where(Expr::col(rbum_rel_attr::Column::Name).eq(&rbum_rel_attr_name)),
1298            )
1299            .await?
1300            > 0
1301        {
1302            return Err(funs.err().conflict(
1303                &Self::get_obj_name(),
1304                "add",
1305                &format!("name {} already exists", rbum_rel_attr_name),
1306                "409-rbum-*-name-exist",
1307            ));
1308        }
1309
1310        Ok(rbum_rel_attr::ActiveModel {
1311            id: Set(TardisFuns::field.nanoid()),
1312            is_from: Set(add_req.is_from),
1313            value: Set(add_req.value.to_string()),
1314            name: Set(rbum_rel_attr_name),
1315            record_only: Set(add_req.record_only),
1316            rel_rbum_kind_attr_id: Set(add_req.rel_rbum_kind_attr_id.as_ref().unwrap_or(&"".to_string()).to_string()),
1317            rel_rbum_rel_id: Set(add_req.rel_rbum_rel_id.to_string()),
1318            ..Default::default()
1319        })
1320    }
1321
1322    async fn package_modify(id: &str, modify_req: &RbumRelAttrModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<rbum_rel_attr::ActiveModel> {
1323        let mut rbum_rel_attr = rbum_rel_attr::ActiveModel {
1324            id: Set(id.to_string()),
1325            ..Default::default()
1326        };
1327        rbum_rel_attr.value = Set(modify_req.value.to_string());
1328        Ok(rbum_rel_attr)
1329    }
1330
1331    async fn package_query(_: bool, filter: &RbumRelExtFilterReq, _: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<SelectStatement> {
1332        let mut query = Query::select();
1333        query
1334            .columns(vec![
1335                (rbum_rel_attr::Entity, rbum_rel_attr::Column::Id),
1336                (rbum_rel_attr::Entity, rbum_rel_attr::Column::IsFrom),
1337                (rbum_rel_attr::Entity, rbum_rel_attr::Column::Value),
1338                (rbum_rel_attr::Entity, rbum_rel_attr::Column::Name),
1339                (rbum_rel_attr::Entity, rbum_rel_attr::Column::RecordOnly),
1340                (rbum_rel_attr::Entity, rbum_rel_attr::Column::RelRbumKindAttrId),
1341                (rbum_rel_attr::Entity, rbum_rel_attr::Column::RelRbumRelId),
1342                (rbum_rel_attr::Entity, rbum_rel_attr::Column::OwnPaths),
1343                (rbum_rel_attr::Entity, rbum_rel_attr::Column::Owner),
1344                (rbum_rel_attr::Entity, rbum_rel_attr::Column::CreateTime),
1345                (rbum_rel_attr::Entity, rbum_rel_attr::Column::UpdateTime),
1346            ])
1347            .expr_as(
1348                Expr::col((rbum_kind_attr::Entity, rbum_kind_attr::Column::Name)).if_null(""),
1349                Alias::new("rel_rbum_kind_attr_name"),
1350            )
1351            .from(rbum_rel_attr::Entity)
1352            .left_join(
1353                rbum_kind_attr::Entity,
1354                Expr::col((rbum_kind_attr::Entity, rbum_kind_attr::Column::Id)).equals((rbum_rel_attr::Entity, rbum_rel_attr::Column::RelRbumKindAttrId)),
1355            );
1356        if let Some(rel_rbum_rel_id) = &filter.rel_rbum_rel_id {
1357            query.and_where(Expr::col((rbum_rel_attr::Entity, rbum_rel_attr::Column::RelRbumRelId)).eq(rel_rbum_rel_id.to_string()));
1358        }
1359        query.with_filter(Self::get_table_name(), &filter.basic, true, false, ctx);
1360        Ok(query)
1361    }
1362}
1363
1364#[async_trait]
1365impl RbumCrudOperation<rbum_rel_env::ActiveModel, RbumRelEnvAddReq, RbumRelEnvModifyReq, RbumRelEnvDetailResp, RbumRelEnvDetailResp, RbumRelExtFilterReq> for RbumRelEnvServ {
1366    fn get_table_name() -> &'static str {
1367        rbum_rel_env::Entity.table_name()
1368    }
1369
1370    async fn before_add_rbum(add_req: &mut RbumRelEnvAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
1371        Self::check_ownership_with_table_name(&add_req.rel_rbum_rel_id, RbumRelServ::get_table_name(), funs, ctx).await?;
1372        Ok(())
1373    }
1374
1375    async fn package_add(add_req: &RbumRelEnvAddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<rbum_rel_env::ActiveModel> {
1376        Ok(rbum_rel_env::ActiveModel {
1377            id: Set(TardisFuns::field.nanoid()),
1378            kind: Set(add_req.kind.to_int()),
1379            value1: Set(add_req.value1.to_string()),
1380            value2: Set(add_req.value2.as_ref().unwrap_or(&"".to_string()).to_string()),
1381            rel_rbum_rel_id: Set(add_req.rel_rbum_rel_id.to_string()),
1382            ..Default::default()
1383        })
1384    }
1385
1386    async fn package_modify(id: &str, modify_req: &RbumRelEnvModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<rbum_rel_env::ActiveModel> {
1387        let mut rbum_rel_env = rbum_rel_env::ActiveModel {
1388            id: Set(id.to_string()),
1389            ..Default::default()
1390        };
1391        if let Some(value1) = &modify_req.value1 {
1392            rbum_rel_env.value1 = Set(value1.to_string());
1393        }
1394        if let Some(value2) = &modify_req.value2 {
1395            rbum_rel_env.value2 = Set(value2.to_string());
1396        }
1397        Ok(rbum_rel_env)
1398    }
1399
1400    async fn package_query(_: bool, filter: &RbumRelExtFilterReq, _: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<SelectStatement> {
1401        let mut query = Query::select();
1402        query
1403            .columns(vec![
1404                (rbum_rel_env::Entity, rbum_rel_env::Column::Id),
1405                (rbum_rel_env::Entity, rbum_rel_env::Column::Kind),
1406                (rbum_rel_env::Entity, rbum_rel_env::Column::Value1),
1407                (rbum_rel_env::Entity, rbum_rel_env::Column::Value2),
1408                (rbum_rel_env::Entity, rbum_rel_env::Column::RelRbumRelId),
1409                (rbum_rel_env::Entity, rbum_rel_env::Column::OwnPaths),
1410                (rbum_rel_env::Entity, rbum_rel_env::Column::Owner),
1411                (rbum_rel_env::Entity, rbum_rel_env::Column::CreateTime),
1412                (rbum_rel_env::Entity, rbum_rel_env::Column::UpdateTime),
1413            ])
1414            .from(rbum_rel_env::Entity);
1415
1416        if let Some(rel_rbum_rel_id) = &filter.rel_rbum_rel_id {
1417            query.and_where(Expr::col((rbum_rel_env::Entity, rbum_rel_env::Column::RelRbumRelId)).eq(rel_rbum_rel_id.to_string()));
1418        }
1419        query.with_filter(Self::get_table_name(), &filter.basic, true, false, ctx);
1420        Ok(query)
1421    }
1422}