Integrate death log functionality into character and event processing: Added death context handling in CharacterCreationWorker and EventsWorker to enhance user notifications related to character deaths. Updated SQL queries for retrieving deceased context and modified notification logic to support detailed death notifications. Enhanced user notification methods to accommodate both short event names and JSON payloads for improved clarity in messaging.

This commit is contained in:
Torsten Schulz (local)
2026-03-30 10:11:55 +02:00
parent 1013af594d
commit ac059f688d
7 changed files with 336 additions and 30 deletions

107
src/worker/death_log.rs Normal file
View File

@@ -0,0 +1,107 @@
//! Kontext für `falukant_log.notification` bei Charaktertod (Direktor, Partner, Eltern).
//! `tr` enthält JSON mit Verstorbenem, Ansässigkeit, Alter, Ehe/Kind/Liebschaft.
//!
//! **Aufruf vor** `DELETE` auf `relationship` / `child_relation`, sonst sind Partner/Kinder/Geliebte leer.
//!
//! **Geliebte** sind keine eigene Tabelle: `falukant_data.relationship` + Typ `falukant_type.relationship.tr = 'lover'`,
//! Zusatzdaten in `relationship_state`. Zählt nur, wenn `relationship_state.active` nicht `false` ist (wie Backend-UI).
//! `lover_role` (`secret_affair` / `lover` / `mistress_or_favorite`) wird für die Liste **nicht** gefiltert.
//!
//! Kinder aus Liebschaft sind in `child_relation.birth_context = 'lover'` markiert; zusätzlich unter `linked.lover_birth_children`.
use crate::db::{DbConnection, DbError, Row};
use serde_json::{json, Value};
use crate::worker::sql::{
QUERY_DEATH_LOG_CHARACTER_BASE, QUERY_DEATH_LOG_CHILD_DISPLAY_NAMES,
QUERY_DEATH_LOG_CHILD_LOVER_BIRTH_DISPLAY_NAMES, QUERY_DEATH_LOG_LOVER_DISPLAY_NAMES,
QUERY_DEATH_LOG_SPOUSE_DISPLAY_NAMES,
};
fn collect_display_names(rows: Vec<Row>, key: &str) -> Vec<String> {
let mut out = Vec::new();
for r in rows {
if let Some(s) = r.get(key) {
let t = s.trim();
if !t.is_empty() {
out.push(t.to_string());
}
}
}
out
}
/// Lädt Verstorbenen + Ehepartner, Kinder, **aktive Geliebte** (`tr = 'lover'`, `active IS NOT FALSE`).
/// Muss ausgeführt werden, bevor Beziehungen aus der DB gelöscht werden.
pub fn build_deceased_context(conn: &mut DbConnection, deceased_character_id: i32) -> Result<Value, DbError> {
conn.prepare("death_base", QUERY_DEATH_LOG_CHARACTER_BASE)?;
let base_rows = conn.execute("death_base", &[&deceased_character_id])?;
let base = base_rows.first().ok_or_else(|| {
DbError::new(format!(
"Todes-Log: Charakter {deceased_character_id} nicht gefunden"
))
})?;
let character_id = base
.get("character_id")
.and_then(|v| v.parse::<i32>().ok())
.unwrap_or(deceased_character_id);
let display_name = base
.get("display_name")
.cloned()
.unwrap_or_else(|| character_id.to_string());
let region_id = base
.get("region_id")
.and_then(|v| v.parse::<i32>().ok());
let region_label = base
.get("region_label")
.cloned()
.unwrap_or_default();
let age_years = base
.get("age_years")
.and_then(|v| v.parse::<i32>().ok())
.unwrap_or(0);
conn.prepare("death_spouse", QUERY_DEATH_LOG_SPOUSE_DISPLAY_NAMES)?;
let spouse_rows = conn.execute("death_spouse", &[&deceased_character_id])?;
let spouses = collect_display_names(spouse_rows, "display_name");
conn.prepare("death_child", QUERY_DEATH_LOG_CHILD_DISPLAY_NAMES)?;
let child_rows = conn.execute("death_child", &[&deceased_character_id])?;
let children = collect_display_names(child_rows, "display_name");
conn.prepare("death_child_lover_birth", QUERY_DEATH_LOG_CHILD_LOVER_BIRTH_DISPLAY_NAMES)?;
let child_lover_rows = conn.execute("death_child_lover_birth", &[&deceased_character_id])?;
let lover_birth_children = collect_display_names(child_lover_rows, "display_name");
conn.prepare("death_lover", QUERY_DEATH_LOG_LOVER_DISPLAY_NAMES)?;
let lover_rows = conn.execute("death_lover", &[&deceased_character_id])?;
let lovers = collect_display_names(lover_rows, "display_name");
Ok(json!({
"deceased": {
"character_id": character_id,
"display_name": display_name,
"region_id": region_id,
"region_label": region_label,
"age_years": age_years,
},
"linked": {
"spouses": spouses,
"children": children,
"lover_birth_children": lover_birth_children,
"lovers": lovers,
},
}))
}
/// Vollständiges `tr`-/WebSocket-JSON mit `event` und `user_id`.
pub fn wrap_death_notification(context: &Value, event: &str, user_id: i32) -> String {
json!({
"event": event,
"user_id": user_id,
"deceased": context.get("deceased"),
"linked": context.get("linked"),
})
.to_string()
}