Files
yourpart-daemon/docs/FALUKANT_PRODUCTION_CERTIFICATE.md

68 lines
5.2 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Falukant: Produktionszertifikate (Daemon)
Die Zertifikatslogik läuft im **FalukantFamilyWorker** (`run_iteration`) **etwa 1× pro Minute** (`CERTIFICATE_RECALC_INTERVAL`), **nicht** in einem eigenen Worker-Thread. Der **24h-Daily-Block** enthält weiter Schuldturm und (bei Schema) Liebhaber/Ehe/Monatslogik — Zertifikat ist davon **getrennt**. Sie schreibt `falukant_user.certificate` fort (max. **+1** pro erfolgreicher Prüfung, keine normale Herabstufung außer Bankrott / Erbfolge).
Implementierung: `src/worker/falukant_certificate.rs` (`run_daily`).
## SQL
- `QUERY_GET_PRODUCTION_CERTIFICATE_INPUT_ROWS` Eingangsdaten je Falukant-User (Spielercharakter, Wissen, Produktionen, Ämter, Haus …)
- `QUERY_UPDATE_FALUKANT_USER_CERTIFICATE` Stufe + **`certificate_productions_count_since = NOW()`** (Migration `014`)
## Logik (Kurz, Spec §4)
- **certificateScore** (Gewichte): Wissen 0,45 · Produktion 0,30 · Amt 0,08 · Adel 0,05 · Ruf 0,07 · Haus 0,05
- **raw_target** aus Score-Schwellen: **<0,9** → 1, **≥0,9** → 2, **≥1,8** → 3, **≥2,8** → 4, **≥3,8** → 5
- **effective_target** mit Mindestanforderungen je Stufe (Spec §4.5)
- Aufstieg nur wenn `effective_target > current`**`current + 1`** (gegen `effective_target` und 5 begrenzt)
- **Bankrott** (`money <= -5000`): Zertifikat auf **1**, mit Event
### Wichtig: UI vs. Daemon („48 h kein Aufstieg“)
Die **Mindestanforderungen** (z.B. Wissen ≥ 28, Produktionen ≥ 15 für Stufe 3) sind **nur ein Teil**. Zusätzlich gilt eine **Obergrenze aus der gewichteten Wertung** (`certificateScore``raw_target`): Es wird die **höchste Stufe ≤ `raw_target`**, die **alle** Mindestanforderungen erfüllt (`effective_certificate_target` in `falukant_certificate.rs`).
Typische Folge: Die UI zeigt **nur** zwei grüne Häkchen (Wissen/Produktionen), der Spieler bleibt aber auf Stufe 2, weil:
1. **`raw_target` = 2** (Wertung **unter** 1,8) — dann ist Stufe 3 **fachlich ausgeschlossen**, auch wenn die Mindestzahlen für Stufe 3 erfüllt sind. Oft liegt die Wertung knapp unter 1,8, wenn z.B. **Produktionspunkte** im Daemon niedrig sind (Bucket &lt; 20 abgeschlossene Produktionen in `falukant_log.production` trotz höherer Anzeige in der UI).
2. **Abweichende Eingangsdaten** gegenüber der UI: anderer gewählter Charakter (`DISTINCT ON … c.id DESC`), andere Zählung `falukant_log.production` (`producer_id`), Geld/Bankrott, etc.
**Diagnose:** Daemon mit `YPDAEMON_CERT_VERBOSE=1` starten. Wenn jemand die Mindestanforderungen für die **nächste** Stufe erfüllt, aber **nicht** aufsteigt, erscheint eine Zeile `[falukant_certificate] fu_id=… bleibt auf Stufe …` mit `certificate_score`, `raw_target`, `effective_target` und Rohwerten.
**SQL:** `QUERY_GET_PRODUCTION_CERTIFICATE_INPUT_ROWS` in `src/worker/sql.rs` — für einen Betroffenen `falukant_user.id` filtern und mit der UI abgleichen.
## Politische Ämter
Rang aus **`political_office_type.name`** (Substring-Heuristik im Daemon, ohne DB-Änderung). Anpassung über `political_name_to_rank` in `falukant_certificate.rs`.
## Kirchliche Ämter
`officePoints` aus **`max(hierarchy_level)`** der aktiven `church_office`-Zeilen (gekappt 05).
## Abgeschlossene Produktionen
**`SUM(completion_count)`** aus `falukant_log.production` mit `producer_id = falukant_user.id` **oder** `character.id`, und **Zeitstempel** `>= certificate_productions_count_since` (Spalte auf `falukant_user`, Migration **`014`**). **`completion_count`** (Migration **`015`**): pro abgeschlossener Produktion +1; das Log **aggregiert** noch immer pro Tag/Produkt/Region (`quantity` per UPSERT), daher reicht **`COUNT(*)`** der Zeilen nicht — ohne Summe bliebe der Zähler bei vielen Abschlüssen gleich. **`NULL`** bei `certificate_productions_count_since`: alle passenden Log-Zeilen (Bestand bis zur ersten Stufenänderung nach Migration).
Bei jedem **Aufstieg**, **Bankrott** (Stufe 1) und **Erbfolge ohne Erben** setzt der Daemon **`certificate_productions_count_since = NOW()`** — die Mindestanforderungen für die **nächste** Stufe gelten damit nur für **neu** abgeschlossene Produktionen. **Logs werden dafür nicht gelöscht** (bleiben u.a. für Wissens-Updates / Preise); optional räumt `QUERY_DELETE_OLD_PRODUCTIONS` nur sehr alte Zeilen auf (aktuell **30 Tage** Retention, Speicherbegrenzung).
**UI:** Dieselbe Filterlogik wie der Daemon verwenden (`>= certificate_productions_count_since`), sonst weichen Anzeige und Aufstieg voneinander ab.
## Gewählter Charakter pro User
Bei mehreren lebenden Charakteren: **`DISTINCT ON (fu.id) … ORDER BY fu.id, c.id DESC`** — der Charakter mit der **höchsten** `character.id` (typisch zuletzt genutzter Slot), damit Wissen/Ruf/Ämter näher an der UI liegen.
## Events (WebSocket)
Bei Änderung der Stufe:
1. `falukantUpdateProductionCertificate` mit `reason`, `old_certificate`, `new_certificate`
2. `falukantUpdateStatus`
**`reason`:** `daily_recalculation` (normaler Aufstieg), `bankruptcy` (Geld ≤ 5000), `succession_no_heir` (Tod ohne Erben → Stufe 1).
`user_id` in den Events: **`app_user_id`** aus der Query (`COALESCE(fu.user_id, fu.id)`), sonst Fallback `falukant_user_id`.
## Nicht umgesetzt (optional / später)
- Feinere **Bankrott**-Definition
- **`political_office_history`** (nicht im Repo)