gitdataai/libs/service/project/like.rs
2026-04-14 19:02:01 +08:00

200 lines
6.4 KiB
Rust

use crate::AppService;
use crate::error::AppError;
use models::projects::{project_audit_log, project_like};
use models::users::user;
use sea_orm::*;
use serde::{Deserialize, Serialize};
use session::Session;
use uuid::Uuid;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, utoipa::ToSchema)]
pub struct LikeUserInfo {
pub uid: Uuid,
pub username: String,
pub avatar_url: String,
}
impl AppService {
pub async fn project_like(&self, ctx: &Session, project_name: String) -> Result<(), AppError> {
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
let project = self.utils_find_project_by_name(project_name).await?;
let like_exists = project_like::Entity::find()
.filter(project_like::Column::User.eq(user_uid))
.filter(project_like::Column::Project.eq(project.id))
.one(&self.db)
.await?;
if like_exists.is_some() {
return Err(AppError::BadRequest(
"Already liked this project".to_string(),
));
}
project_like::Entity::insert(project_like::ActiveModel {
project: Set(project.id),
user: Set(user_uid),
created_at: Set(chrono::Utc::now()),
})
.exec(&self.db)
.await?;
let _ = self
.project_log_activity(
project.id,
None,
user_uid,
super::activity::ActivityLogParams {
event_type: "project_star".to_string(),
title: format!("{} starred the project", user_uid),
repo_id: None,
content: None,
event_id: None,
event_sub_id: None,
metadata: Some(serde_json::json!({
"project_name": project.name.clone(),
})),
is_private: false,
},
)
.await;
let log = project_audit_log::ActiveModel {
project: Set(project.id),
actor: Set(user_uid),
action: Set("project_like".to_string()),
details: Set(Some(serde_json::json!({
"project_name": project.name.clone(),
}))),
created_at: Set(chrono::Utc::now()),
..Default::default()
};
log.insert(&self.db).await?;
Ok(())
}
pub async fn project_unlike(
&self,
ctx: &Session,
project_name: String,
) -> Result<(), AppError> {
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
let project = self.utils_find_project_by_name(project_name).await?;
let like_exists = project_like::Entity::find()
.filter(project_like::Column::User.eq(user_uid))
.filter(project_like::Column::Project.eq(project.id))
.one(&self.db)
.await?;
if like_exists.is_none() {
return Err(AppError::NotFound("Like not found".to_string()));
}
project_like::Entity::delete_many()
.filter(project_like::Column::User.eq(user_uid))
.filter(project_like::Column::Project.eq(project.id))
.exec(&self.db)
.await?;
let _ = self
.project_log_activity(
project.id,
None,
user_uid,
super::activity::ActivityLogParams {
event_type: "project_unstar".to_string(),
title: format!("{} unstarred the project", user_uid),
repo_id: None,
content: None,
event_id: None,
event_sub_id: None,
metadata: Some(serde_json::json!({
"project_name": project.name.clone(),
})),
is_private: false,
},
)
.await;
let log = project_audit_log::ActiveModel {
project: Set(project.id),
actor: Set(user_uid),
action: Set("project_unlike".to_string()),
details: Set(Some(serde_json::json!({
"project_name": project.name.clone(),
}))),
created_at: Set(chrono::Utc::now()),
..Default::default()
};
log.insert(&self.db).await?;
Ok(())
}
pub async fn project_is_like(
&self,
ctx: &Session,
project_name: String,
) -> Result<bool, AppError> {
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
let project = self.utils_find_project_by_name(project_name).await?;
let like_exists = project_like::Entity::find()
.filter(project_like::Column::User.eq(user_uid))
.filter(project_like::Column::Project.eq(project.id))
.one(&self.db)
.await?;
Ok(like_exists.is_some())
}
pub async fn project_likes(&self, project_name: String) -> Result<u64, AppError> {
let project = self.utils_find_project_by_name(project_name).await?;
let likes = project_like::Entity::find()
.filter(project_like::Column::Project.eq(project.id))
.count(&self.db)
.await?;
Ok(likes)
}
pub async fn project_like_user_list(
&self,
project_name: String,
pager: crate::Pager,
) -> Result<Vec<LikeUserInfo>, AppError> {
let project = self.utils_find_project_by_name(project_name).await?;
let likes = project_like::Entity::find()
.filter(project_like::Column::Project.eq(project.id))
.order_by_desc(project_like::Column::CreatedAt)
.limit(pager.par_page as u64)
.offset(((pager.page - 1) * pager.par_page) as u64)
.all(&self.db)
.await?;
let user_uids: Vec<Uuid> = likes.into_iter().map(|like| like.user).collect();
let users = if user_uids.is_empty() {
vec![]
} else {
user::Entity::find()
.filter(user::Column::Uid.is_in(user_uids))
.all(&self.db)
.await?
.into_iter()
.map(|u| LikeUserInfo {
uid: u.uid,
username: u.username,
avatar_url: u.avatar_url.unwrap_or_default(),
})
.collect()
};
Ok(users)
}
}