//! 触发者语言解析。 //! //! AI 输出的语言优先跟随「谁触发了这次任务」:推送/开 PR 的人、仓库所属空间 //! 的所有者,最后回退到 `[ai].output_language`。这样同一个仓库在不同语言的 //! 协作者手里会得到各自语言的评审,而不是被钉死在部署时的单一语言。 use uuid::Uuid; impl super::AgentService { /// 按「触发者 → 空间所有者 → 配置缺省」的顺序解析输出语言。 pub(crate) async fn resolve_language( &self, actor_id: Option, fallback: Option<&str>, repo_id: Uuid, ) -> String { let configured = &self.config().ai.output_language; // 任务自带语言优先(例如手动触发时由接口解析好)。 if let Some(lang) = fallback.filter(|value| !value.trim().is_empty()) { return crate::languages::normalize(lang); } if let Some(uid) = actor_id && let Some(lang) = self.user_language(uid).await { return lang; } if let Some(uid) = self.repo_owner(repo_id).await && let Some(lang) = self.user_language(uid).await { return lang; } crate::languages::normalize(configured) } /// 读取用户偏好语言;`language` 优先,其次 `locale`。 async fn user_language(&self, uid: Uuid) -> Option { use models::users::user; use sea_orm::EntityTrait; let row = user::Entity::find_by_id(uid) .one(self.db_read()) .await .ok()??; let raw = row .language .as_deref() .filter(|value| !value.trim().is_empty()) .or_else(|| { row.locale .as_deref() .filter(|value| !value.trim().is_empty()) })?; let normalized = crate::languages::normalize(raw); (!normalized.is_empty() && normalized != crate::languages::AUTO).then_some(normalized) } /// 仓库所属空间的所有者 uid。 async fn repo_owner(&self, repo_id: Uuid) -> Option { use models::repo::repos; use sea_orm::EntityTrait; let repo = repos::Entity::find_by_id(repo_id) .one(self.db_read()) .await .ok()??; git::share::access::space_for_dir(self.db_read(), repo.dir_id) .await .ok()? .map(|space| space.owner_id) } }