gitdataai/libs/service/project/message_favorite.rs

279 lines
9.6 KiB
Rust

use crate::AppService;
use crate::error::AppError;
use chrono::{DateTime, Utc};
use models::projects::{project_members, project_message_favorite};
use models::rooms::{room, room_message};
use sea_orm::sea_query::Expr;
use sea_orm::*;
use serde::{Deserialize, Serialize};
use session::Session;
use utoipa::{IntoParams, ToSchema};
use uuid::Uuid;
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema, IntoParams)]
pub struct ProjectMessageFavoriteQuery {
pub page: Option<u64>,
pub per_page: Option<u64>,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct ProjectMessageFavoriteItem {
pub uid: Uuid,
pub project_uid: Uuid,
pub room_id: Uuid,
pub room_name: String,
pub message_id: Uuid,
pub sender_id: Option<Uuid>,
pub sender_type: String,
pub display_name: Option<String>,
pub content: String,
pub content_type: String,
pub send_at: DateTime<Utc>,
pub favorited_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct ProjectMessageFavoriteResponse {
pub page: u64,
pub per_page: u64,
pub total: u64,
pub list: Vec<ProjectMessageFavoriteItem>,
}
impl AppService {
pub async fn project_message_favorites(
&self,
ctx: &Session,
project_name: String,
query: ProjectMessageFavoriteQuery,
) -> Result<ProjectMessageFavoriteResponse, AppError> {
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
let project = self.utils_find_project_by_name(project_name).await?;
self.require_project_member(project.id, user_uid).await?;
let page = std::cmp::max(query.page.unwrap_or(1), 1);
let per_page = std::cmp::min(std::cmp::max(query.per_page.unwrap_or(20), 1), 100);
let base = project_message_favorite::Entity::find()
.filter(project_message_favorite::Column::Project.eq(project.id))
.filter(project_message_favorite::Column::User.eq(user_uid));
let total = base.clone().count(&self.db).await?;
let rows = base
.join(
JoinType::InnerJoin,
project_message_favorite::Entity::belongs_to(room_message::Entity)
.from(project_message_favorite::Column::Message)
.to(room_message::Column::Id)
.into(),
)
.join(
JoinType::InnerJoin,
project_message_favorite::Entity::belongs_to(room::Entity)
.from(project_message_favorite::Column::Room)
.to(room::Column::Id)
.into(),
)
.filter(room_message::Column::Revoked.is_null())
.order_by_desc(project_message_favorite::Column::CreatedAt)
.select_only()
.column_as(project_message_favorite::Column::Uid, "uid")
.column_as(project_message_favorite::Column::Project, "project_uid")
.column_as(project_message_favorite::Column::Room, "room_id")
.column_as(room::Column::RoomName, "room_name")
.column_as(project_message_favorite::Column::Message, "message_id")
.column_as(room_message::Column::SenderId, "sender_id")
.column_as(Expr::cust("room_message.sender_type::text"), "sender_type")
.column_as(room_message::Column::Content, "content")
.column_as(
Expr::cust("room_message.content_type::text"),
"content_type",
)
.column_as(room_message::Column::SendAt, "send_at")
.column_as(project_message_favorite::Column::CreatedAt, "favorited_at")
.column_as(
Expr::cust(
"coalesce(room_message.sender_id::text, room_message.sender_type::text)",
),
"display_name",
)
.limit(per_page)
.offset((page - 1) * per_page)
.into_tuple::<(
Uuid,
Uuid,
Uuid,
String,
Uuid,
Option<Uuid>,
String,
String,
String,
DateTime<Utc>,
DateTime<Utc>,
Option<String>,
)>()
.all(&self.db)
.await?;
let list = rows
.into_iter()
.map(
|(
uid,
project_uid,
room_id,
room_name,
message_id,
sender_id,
sender_type,
content,
content_type,
send_at,
favorited_at,
display_name,
)| ProjectMessageFavoriteItem {
uid,
project_uid,
room_id,
room_name,
message_id,
sender_id,
sender_type,
display_name,
content,
content_type,
send_at,
favorited_at,
},
)
.collect();
Ok(ProjectMessageFavoriteResponse {
page,
per_page,
total,
list,
})
}
pub async fn project_message_favorite_add(
&self,
ctx: &Session,
project_name: String,
message_id: Uuid,
) -> Result<ProjectMessageFavoriteItem, AppError> {
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
let project = self
.utils_find_project_by_name(project_name.clone())
.await?;
self.require_project_member(project.id, user_uid).await?;
let message = room_message::Entity::find_by_id(message_id)
.one(&self.db)
.await?
.ok_or_else(|| AppError::NotFound("message".to_string()))?;
if message.revoked.is_some() {
return Err(AppError::NotFound("message".to_string()));
}
let room = room::Entity::find_by_id(message.room)
.one(&self.db)
.await?
.ok_or_else(|| AppError::NotFound("room".to_string()))?;
if room.project != project.id {
return Err(AppError::NotFound("message".to_string()));
}
if let Some(existing) = project_message_favorite::Entity::find()
.filter(project_message_favorite::Column::User.eq(user_uid))
.filter(project_message_favorite::Column::Message.eq(message.id))
.one(&self.db)
.await?
{
return Ok(ProjectMessageFavoriteItem {
uid: existing.uid,
project_uid: existing.project,
room_id: existing.room,
room_name: room.room_name,
message_id: existing.message,
sender_id: message.sender_id,
sender_type: message.sender_type.to_string(),
display_name: message
.sender_id
.map(|id| id.to_string())
.or_else(|| Some(message.sender_type.to_string())),
content: message.content,
content_type: message.content_type.to_string(),
send_at: message.send_at,
favorited_at: existing.created_at,
});
}
let created = project_message_favorite::ActiveModel {
uid: Set(Uuid::new_v4()),
project: Set(project.id),
room: Set(message.room),
message: Set(message.id),
user: Set(user_uid),
created_at: Set(Utc::now()),
}
.insert(&self.db)
.await?;
Ok(ProjectMessageFavoriteItem {
uid: created.uid,
project_uid: created.project,
room_id: created.room,
room_name: room.room_name,
message_id: created.message,
sender_id: message.sender_id,
sender_type: message.sender_type.to_string(),
display_name: message
.sender_id
.map(|id| id.to_string())
.or_else(|| Some(message.sender_type.to_string())),
content: message.content,
content_type: message.content_type.to_string(),
send_at: message.send_at,
favorited_at: created.created_at,
})
}
pub async fn project_message_favorite_remove(
&self,
ctx: &Session,
project_name: String,
message_id: Uuid,
) -> Result<(), AppError> {
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
let project = self.utils_find_project_by_name(project_name).await?;
self.require_project_member(project.id, user_uid).await?;
project_message_favorite::Entity::delete_many()
.filter(project_message_favorite::Column::Project.eq(project.id))
.filter(project_message_favorite::Column::User.eq(user_uid))
.filter(project_message_favorite::Column::Message.eq(message_id))
.exec(&self.db)
.await?;
Ok(())
}
async fn require_project_member(
&self,
project_id: Uuid,
user_id: Uuid,
) -> Result<(), AppError> {
let member = project_members::Entity::find()
.filter(project_members::Column::Project.eq(project_id))
.filter(project_members::Column::User.eq(user_id))
.one(&self.db)
.await?;
if member.is_some() {
Ok(())
} else {
Err(AppError::NoPower)
}
}
}