194c44c
//! 触发者语言解析。
//!
//! AI 输出的语言优先跟随「谁触发了这次任务」:推送/开 PR 的人、仓库所属空间
//! 的所有者,最后回退到 `[ai].output_language`。这样同一个仓库在不同语言的
//! 协作者手里会得到各自语言的评审,而不是被钉死在部署时的单一语言。
use uuid::Uuid;
impl super::AgentService {
/// 按「触发者 → 空间所有者 → 配置缺省」的顺序解析输出语言。
pub(crate) async fn resolve_language(
&self,
actor_id: Option<i64>,
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: i64) -> Option<String> {
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<i64> {
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)
}
}