Skip to content
Clone

feat(project): add views usecase

zhenyi committed
parent b4f0dbd commit 4e9d913 Browse files

1 file changed

+191 −0

Added lib/project/features/views/usecase.rs View lib/project/features/views/usecase.rs
@@ -0,0 +1,191 @@
1use db::project::project_views as view_entity;
2use db::project::project_views::Column as Vc;
3use sea_orm::{
4 ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, Set,
5};
6use time::OffsetDateTime;
7use uuid::Uuid;
8
9use crate::error::{ProjectError, ProjectResult};
10use crate::features::lifecycle::usecase::log_activity;
11use crate::features::ProjectService;
12use crate::permission;
13
14use super::{CreateViewCmd, UpdateViewCmd, ViewPage, ViewView};
15
16pub async fn create_view(
17 svc: &ProjectService,
18 actor: Uuid,
19 space_id: Uuid,
20 project_id: Uuid,
21 cmd: CreateViewCmd,
22) -> ProjectResult<ViewView> {
23 crate::features::authz::require(
24 svc,
25 actor,
26 space_id,
27 project_id,
28 permission::MANAGE_VIEWS,
29 )
30 .await?;
31
32 let id = Uuid::new_v4();
33 let now = OffsetDateTime::now_utc();
34 let row = view_entity::ActiveModel {
35 id: Set(id),
36 project_id: Set(project_id),
37 creator_id: Set(actor),
38 name: Set(cmd.name),
39 description: Set(cmd.description),
40 filter: Set(cmd.filter),
41 sort_by: Set(cmd.sort_by),
42 group_by: Set(cmd.group_by),
43 layout: Set(cmd.layout),
44 is_shared: Set(cmd.is_shared),
45 sort_order: Set(0),
46 created_at: Set(now),
47 updated_at: Set(None),
48 }
49 .insert(&svc.db.writer())
50 .await?;
51
52 log_activity(svc, project_id, actor, "view_created", "view", Some(id), None).await;
53 Ok(view_view(&row))
54}
55
56pub async fn list_views(
57 svc: &ProjectService,
58 actor: Uuid,
59 space_id: Uuid,
60 project_id: Uuid,
61) -> ProjectResult<ViewPage> {
62 crate::features::authz::require(svc, actor, space_id, project_id, permission::VIEW_PROJECT)
63 .await?;
64 let rows = view_entity::Entity::find()
65 .filter(Vc::ProjectId.eq(project_id))
66 .order_by_asc(Vc::SortOrder)
67 .order_by_desc(Vc::CreatedAt)
68 .all(&svc.db.reader())
69 .await?;
70 let items = rows.iter().map(view_view).collect::<Vec<_>>();
71 let total = items.len() as u64;
72 Ok(ViewPage { total, items })
73}
74
75pub async fn get_view(
76 svc: &ProjectService,
77 actor: Uuid,
78 space_id: Uuid,
79 project_id: Uuid,
80 view_id: Uuid,
81) -> ProjectResult<ViewView> {
82 crate::features::authz::require(svc, actor, space_id, project_id, permission::VIEW_PROJECT)
83 .await?;
84 let row = view_entity::Entity::find_by_id(view_id)
85 .filter(Vc::ProjectId.eq(project_id))
86 .one(&svc.db.reader())
87 .await?
88 .ok_or_else(|| ProjectError::NotFound(format!("view {view_id} in project {project_id}")))?;
89 Ok(view_view(&row))
90}
91
92pub async fn update_view(
93 svc: &ProjectService,
94 actor: Uuid,
95 space_id: Uuid,
96 project_id: Uuid,
97 view_id: Uuid,
98 cmd: UpdateViewCmd,
99) -> ProjectResult<ViewView> {
100 crate::features::authz::require(
101 svc,
102 actor,
103 space_id,
104 project_id,
105 permission::MANAGE_VIEWS,
106 )
107 .await?;
108
109 let current = view_entity::Entity::find_by_id(view_id)
110 .filter(Vc::ProjectId.eq(project_id))
111 .one(&svc.db.reader())
112 .await?
113 .ok_or_else(|| ProjectError::NotFound(format!("view {view_id} in project {project_id}")))?;
114
115 let mut active: view_entity::ActiveModel = current.into();
116 if let Some(name) = cmd.name {
117 active.name = Set(name);
118 }
119 if cmd.description.is_some() {
120 active.description = Set(cmd.description);
121 }
122 if let Some(filter) = cmd.filter {
123 active.filter = Set(filter);
124 }
125 if let Some(sort_by) = cmd.sort_by {
126 active.sort_by = Set(sort_by);
127 }
128 if cmd.group_by.is_some() {
129 active.group_by = Set(cmd.group_by);
130 }
131 if let Some(layout) = cmd.layout {
132 active.layout = Set(layout);
133 }
134 if let Some(shared) = cmd.is_shared {
135 active.is_shared = Set(shared);
136 }
137 active.updated_at = Set(Some(OffsetDateTime::now_utc()));
138 let updated = active.update(&svc.db.writer()).await?;
139
140 log_activity(svc, project_id, actor, "view_updated", "view", Some(view_id), None).await;
141 Ok(view_view(&updated))
142}
143
144pub async fn delete_view(
145 svc: &ProjectService,
146 actor: Uuid,
147 space_id: Uuid,
148 project_id: Uuid,
149 view_id: Uuid,
150) -> ProjectResult<()> {
151 crate::features::authz::require(
152 svc,
153 actor,
154 space_id,
155 project_id,
156 permission::MANAGE_VIEWS,
157 )
158 .await?;
159
160 let deleted = view_entity::Entity::delete_many()
161 .filter(Vc::Id.eq(view_id))
162 .filter(Vc::ProjectId.eq(project_id))
163 .exec(&svc.db.writer())
164 .await?;
165 if deleted.rows_affected == 0 {
166 return Err(ProjectError::NotFound(format!(
167 "view {view_id} in project {project_id}"
168 )));
169 }
170
171 log_activity(svc, project_id, actor, "view_deleted", "view", Some(view_id), None).await;
172 Ok(())
173}
174
175fn view_view(m: &view_entity::Model) -> ViewView {
176 ViewView {
177 id: m.id,
178 project_id: m.project_id,
179 creator_id: m.creator_id,
180 name: m.name.clone(),
181 description: m.description.clone(),
182 filter: m.filter.clone(),
183 sort_by: m.sort_by.clone(),
184 group_by: m.group_by.clone(),
185 layout: m.layout.clone(),
186 is_shared: m.is_shared,
187 sort_order: m.sort_order,
188 created_at: m.created_at,
189 updated_at: m.updated_at,
190 }
191}
No newline at end of file