Skip to content
Clone
02d8172
150 lines 4.8 KB Rust
use models::common::watch_mode::WatchMode;
use models::repo::{repo_watch, repos};
use sea_orm::{
ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect, Set,
TransactionTrait, Unchanged,
};
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use utoipa::ToSchema;
use uuid::Uuid;
use crate::context::facade::GitContext;
use crate::service::GitService;
use crate::share::errors::GitError;
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct RepoWatchDto {
pub user_id: i64,
pub repo_id: Uuid,
pub mode: WatchMode,
pub created_at: OffsetDateTime,
pub updated_at: OffsetDateTime,
}
#[derive(Debug, Clone, Deserialize, ToSchema)]
pub struct UpsertWatchCmd {
pub mode: WatchMode,
}
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct WatcherDto {
pub user_id: i64,
pub username: String,
pub display_name: String,
pub avatar_url: String,
pub mode: WatchMode,
pub watching_since: OffsetDateTime,
}
impl GitService {
pub async fn upsert_watch(
&self,
ctx: &GitContext,
user_id: i64,
cmd: UpsertWatchCmd,
) -> Result<RepoWatchDto, GitError> {
let db = self.db_write();
let repo_id = ctx.model.id;
let now = OffsetDateTime::now_utc();
let dto = TransactionTrait::transaction(db, |txn| {
Box::pin(async move {
let existing = repo_watch::Entity::find()
.filter(repo_watch::Column::UserId.eq(user_id))
.filter(repo_watch::Column::RepoId.eq(repo_id))
.one(txn)
.await?;
let saved = if let Some(row) = existing {
let mut am: repo_watch::ActiveModel = row.into();
am.mode = Set(cmd.mode);
am.updated_at = Set(now);
am.update(txn).await?
} else {
let am = repo_watch::ActiveModel {
id: Set(Uuid::now_v7()),
user_id: Set(user_id),
repo_id: Set(repo_id),
mode: Set(cmd.mode),
created_at: Set(now),
updated_at: Set(now),
};
am.insert(txn).await?
};
Ok::<repo_watch::Model, sea_orm::DbErr>(saved)
})
})
.await?;
let after = repo_watch::Entity::find()
.filter(repo_watch::Column::RepoId.eq(repo_id))
.all(db)
.await?
.len() as i32;
let am = repos::ActiveModel {
id: Unchanged(repo_id),
nums_watch: Set(after),
updated_at: Set(now),
..Default::default()
};
am.update(db).await?;
Ok(RepoWatchDto {
user_id: dto.user_id,
repo_id: dto.repo_id,
mode: dto.mode,
created_at: dto.created_at,
updated_at: dto.updated_at,
})
}
pub async fn get_watch_mode(
&self,
ctx: &GitContext,
user_id: i64,
) -> Result<Option<WatchMode>, GitError> {
let row = repo_watch::Entity::find()
.filter(repo_watch::Column::UserId.eq(user_id))
.filter(repo_watch::Column::RepoId.eq(ctx.model.id))
.one(self.db_read())
.await?;
Ok(row.map(|r| r.mode))
}
pub async fn list_watchers(
&self,
ctx: &GitContext,
mode: Option<WatchMode>,
limit: u64,
offset: u64,
) -> Result<Vec<WatcherDto>, GitError> {
let mut q = repo_watch::Entity::find()
.filter(repo_watch::Column::RepoId.eq(ctx.model.id))
.order_by_desc(repo_watch::Column::CreatedAt)
.limit(limit)
.offset(offset);
if let Some(m) = mode {
q = q.filter(repo_watch::Column::Mode.eq(m));
}
let rows = q.all(self.db_read()).await?;
if rows.is_empty() {
return Ok(Vec::new());
}
let user_ids: Vec<i64> = rows.iter().map(|r| r.user_id).collect();
let by_id = export::users::load_public_users(&self.state, &user_ids).await?;
Ok(rows
.into_iter()
.filter_map(|row| {
by_id.get(&row.user_id).map(|u| WatcherDto {
user_id: u.user_id,
username: u.username.clone(),
display_name: if u.displayname.trim().is_empty() {
u.username.clone()
} else {
u.displayname.clone()
},
avatar_url: export::resolve_user_avatar_url(u.user_id, &u.avatar_url),
mode: row.mode,
watching_since: row.created_at,
})
})
.collect())
}
}