use git::context::facade::GitContext; use git::service::commits::CommitStatusDto; use models::pull_request::pull_requests; use serde::Serialize; use utoipa::ToSchema; use crate::errors::PullError; use crate::services::approvals::MergeGateDto; use crate::services::service::PullService; #[derive(Debug, Clone, Serialize, ToSchema)] pub struct PullChecksDto { pub head_sha: String, pub statuses: Vec, } #[derive(Debug, Clone, Serialize, ToSchema)] pub struct PullGateDto { pub head_sha: String, pub mergeable: bool, pub conflicts: Vec, pub gate: MergeGateDto, pub pending_review_requests: i32, } impl PullService { pub async fn pull_checks( &self, space_name: String, repo_name: String, pull: &pull_requests::Model, ) -> Result { let repo_ctx = self.git.context(space_name, repo_name).await?; let statuses = self .git .list_commit_statuses(&repo_ctx, pull.head_sha.clone()) .await?; Ok(PullChecksDto { head_sha: pull.head_sha.clone(), statuses, }) } /// 合并门禁预览:不改动任何数据。 pub async fn pull_gate( &self, ctx: &GitContext, pull: &pull_requests::Model, caller: Option, admin: bool, ) -> Result { let plan = self.plan_for(ctx, pull).await?; let summary = crate::services::changes::PullSummaryDto { head_sha: plan.head_sha.clone(), base_sha: plan.base_sha.clone(), merge_base: plan.merge_base.clone(), mergeable: plan.clean, mergeable_state: if plan.clean { "clean".into() } else { "dirty".into() }, conflicts: plan.conflicts.clone(), commits_ahead: plan.head_only, commits_behind: plan.base_only, additions: pull.additions, deletions: pull.deletions, files_changed: pull.files_changed, }; let caller = caller.ok_or(PullError::Unauthorized)?; let gate = self.merge_gate(ctx, pull, caller, admin, &summary).await?; let pending = self.pending_requests(pull.id).await?; Ok(PullGateDto { head_sha: plan.head_sha, mergeable: plan.clean, conflicts: plan.conflicts, gate, pending_review_requests: pending, }) } }