diff --git a/crates/utopia-core/src/models.rs b/crates/utopia-core/src/models.rs index 69cd1673..f4fb8879 100644 --- a/crates/utopia-core/src/models.rs +++ b/crates/utopia-core/src/models.rs @@ -194,6 +194,9 @@ pub struct AuditEventView { pub target_kind: String, pub target_id: Option, pub detail: serde_json::Value, + /// NULL = 引擎自动(裁决器合并、一致性检查、推理物化……)。界面靠它把 + /// 「没有人」和「人已被移除」分开:后者 actor_id 还在,只是查不到显示名 + pub actor_id: Option, pub actor_name: Option, pub created_at: DateTime, } diff --git a/crates/utopia-store/src/audit.rs b/crates/utopia-store/src/audit.rs index c2c48000..9cd1e63e 100644 --- a/crates/utopia-store/src/audit.rs +++ b/crates/utopia-store/src/audit.rs @@ -101,7 +101,7 @@ pub async fn review_history( OR e.action LIKE 'conflict.%' OR e.action LIKE 'merge.%')"; let rows: Vec = sqlx::query_as(&format!( "SELECT e.id, e.action, e.target_kind, e.target_id, e.detail, - u.display_name AS actor_name, e.created_at + e.actor_id, u.display_name AS actor_name, e.created_at FROM audit_events e LEFT JOIN users u ON u.id = e.actor_id WHERE {COND} ORDER BY e.created_at DESC LIMIT $2 OFFSET $3" )) @@ -152,7 +152,7 @@ pub async fn list_for_kb( let rows: Vec = sqlx::query_as(&format!( "SELECT e.id, e.action, e.target_kind, e.target_id, e.detail, - u.display_name AS actor_name, e.created_at + e.actor_id, u.display_name AS actor_name, e.created_at FROM audit_events e LEFT JOIN users u ON u.id = e.actor_id {WHERE} ORDER BY e.created_at DESC LIMIT $6 OFFSET $7" diff --git a/web/src/api.ts b/web/src/api.ts index 59302ca0..4b835cad 100644 --- a/web/src/api.ts +++ b/web/src/api.ts @@ -132,6 +132,8 @@ export interface AuditEvent { target_kind: string; target_id: string | null; detail: Record; + /** null = 引擎自动(裁决器、一致性检查、推理);有 id 没名字 = 账号已移除 */ + actor_id: string | null; actor_name: string | null; created_at: string; } diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts index 05150fae..6b180e1e 100644 --- a/web/src/i18n/en.ts +++ b/web/src/i18n/en.ts @@ -1448,6 +1448,9 @@ export const en = { auditTotal: (n: number) => `${n} events`, activityEmpty: "Nothing recorded yet.", deletedUser: "a removed user", + // actor_id 为空的两种引擎动作:审阅队列里的自动裁决,和其余后台工作 + adjudicator: "AI adjudicator", + engine: "the engine", auditActions: { "entity_type.created": "created entity type", "entity_type.updated": "updated entity type", diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts index 68d90423..a598c984 100644 --- a/web/src/i18n/zh.ts +++ b/web/src/i18n/zh.ts @@ -1306,6 +1306,8 @@ export const zh: Strings = { auditTotal: (n: number) => `共 ${n} 条`, activityEmpty: "还没有记录。", deletedUser: "一位已移除的用户", + adjudicator: "AI 裁决器", + engine: "引擎", auditActions: { "entity_type.created": "创建了实体类型", "entity_type.updated": "更新了实体类型", diff --git a/web/src/pages/KbSettings.tsx b/web/src/pages/KbSettings.tsx index bed49999..d6eccbbf 100644 --- a/web/src/pages/KbSettings.tsx +++ b/web/src/pages/KbSettings.tsx @@ -492,7 +492,12 @@ function KbActivity({ kbId }: { kbId: string }) { - {e.actor_name ?? S.kbset.deletedUser} + {e.actor_name ?? + (e.actor_id + ? S.kbset.deletedUser + : e.action.startsWith("review.") + ? S.kbset.adjudicator + : S.kbset.engine)} {" "} {S.kbset.auditActions[e.action] ?? e.action}