use api::users::UserDto; use models::pull_request::{pull_request_assignee, pull_requests}; use sea_orm::ActiveValue::Set; use sea_orm::{ ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, TransactionTrait, }; use serde::{Deserialize, Serialize}; use time::OffsetDateTime; use utoipa::ToSchema; use uuid::Uuid; use crate::errors::PullError; use crate::services::events::{EVENT_ASSIGNED, EVENT_UNASSIGNED}; use crate::services::reviews; use crate::services::service::PullService; use crate::share::authz; #[derive(Debug, Clone, Deserialize, ToSchema)] pub struct AssignCmd { /// 用户名或 UUID。 pub user: String, } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct PullAssigneeDto { pub id: Uuid, pub user: Option, pub created_at: OffsetDateTime, } impl PullService { pub async fn list_assignees(&self, pull_id: Uuid) -> Result, PullError> { let rows = pull_request_assignee::Entity::find() .filter(pull_request_assignee::Column::PullRequestId.eq(pull_id)) .order_by_asc(pull_request_assignee::Column::CreatedAt) .all(self.db_read()) .await?; let users = self .actors .briefs(&reviews::unique_ids(rows.iter().map(|row| row.user_id))) .await?; Ok(rows .iter() .map(|row| PullAssigneeDto { id: row.id, user: users.get(&row.user_id).cloned(), created_at: row.created_at, }) .collect()) } pub async fn add_assignee( &self, pull: &pull_requests::Model, caller: i64, reference: &str, ) -> Result, PullError> { authz::require_unlocked(pull)?; let uid = self.actors.resolve(reference).await?; let existing = pull_request_assignee::Entity::find() .filter(pull_request_assignee::Column::PullRequestId.eq(pull.id)) .filter(pull_request_assignee::Column::UserId.eq(uid)) .one(self.db_read()) .await?; if existing.is_none() { let event_pull = pull.clone(); let this = self.clone(); self.db_write() .transaction::<_, (), PullError>(move |tx| { Box::pin(async move { pull_request_assignee::ActiveModel { id: Set(Uuid::now_v7()), pull_request_id: Set(event_pull.id), user_id: Set(uid), created_at: Set(OffsetDateTime::now_utc()), } .insert(tx) .await?; this.record_event_with( tx, &event_pull, Some(caller), EVENT_ASSIGNED, None, Some(uid.to_string()), ) .await?; Ok(()) }) }) .await?; } self.list_assignees(pull.id).await } pub async fn remove_assignee( &self, pull: &pull_requests::Model, caller: i64, reference: &str, ) -> Result, PullError> { authz::require_unlocked(pull)?; let uid = self.actors.resolve(reference).await?; let event_pull = pull.clone(); let this = self.clone(); self.db_write() .transaction::<_, (), PullError>(move |tx| { Box::pin(async move { let deleted = pull_request_assignee::Entity::delete_many() .filter(pull_request_assignee::Column::PullRequestId.eq(event_pull.id)) .filter(pull_request_assignee::Column::UserId.eq(uid)) .exec(tx) .await?; if deleted.rows_affected > 0 { this.record_event_with( tx, &event_pull, Some(caller), EVENT_UNASSIGNED, None, Some(uid.to_string()), ) .await?; } Ok(()) }) }) .await?; self.list_assignees(pull.id).await } }