194c44c
use export::notify::publish_notification;
use models::pull_request::{pull_request_reviewer, pull_requests};
use models::users::user_notifications::{ActiveModel as NotificationModel, NotificationType};
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
use time::OffsetDateTime;
use uuid::Uuid;
impl super::AgentService {
/// 评审完成后通知 PR 作者与当前待评审人。
///
/// 通知仅写库 + Redis 推送,前端按既有轮询/WS 机制刷新即可看到结果。
pub(crate) async fn notify_pull_ai(
&self,
pull: &pull_requests::Model,
bot: i64,
language: &str,
summary: &str,
findings: usize,
) {
let mut recipients: Vec<i64> = vec![pull.author_id];
match pull_request_reviewer::Entity::find()
.filter(pull_request_reviewer::Column::PullRequestId.eq(pull.id))
.all(self.db_read())
.await
{
Ok(rows) => {
for row in rows {
if !recipients.contains(&row.user_id) {
recipients.push(row.user_id);
}
}
}
Err(error) => tracing::warn!(%error, "ai review recipient lookup failed"),
}
recipients.retain(|uid| *uid != bot);
if recipients.is_empty() {
return;
}
let title = crate::notifications::review_title(language, pull.autoincrement_id);
let body = crate::notifications::review_body(language, findings, summary);
let data = serde_json::json!({
"kind": "ai_review",
"pull_request_id": pull.id,
"repo_id": pull.repo_id,
"findings": findings,
});
let now = OffsetDateTime::now_utc();
for uid in recipients {
let inserted = NotificationModel {
id: Set(Uuid::now_v7()),
user_id: Set(uid),
notification_type: Set(NotificationType::System),
title: Set(title.clone()),
body: Set(Some(body.clone())),
data_json: Set(Some(data.clone())),
actor_uid: Set(Some(bot)),
read_at: Set(None),
created_at: Set(now),
}
.insert(self.db_write())
.await;
match inserted {
Ok(row) => {
publish_notification(&self.state.redis, uid, row.to_event_value()).await;
}
Err(error) => tracing::warn!(%error, %uid, "ai review notification insert failed"),
}
}
}
}