From e460792357230651db346239f97e122e05f7ffc6 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Thu, 9 Apr 2026 08:13:17 +0200 Subject: [PATCH] Enhance production certificate logic and SQL queries: Updated the `QUERY_UPDATE_FALUKANT_USER_CERTIFICATE` to include `certificate_productions_count_since` for tracking production counts since the last level change. Modified the `QUERY_GET_PRODUCTION_CERTIFICATE_INPUT_ROWS` to filter completed productions based on this timestamp. Improved documentation for clarity on the new logic and its implications for user progression and data retention. --- docs/FALUKANT_PRODUCTION_CERTIFICATE.md | 8 ++++++-- ...kant_certificate_productions_count_since.sql | 6 ++++++ migrations/README.md | 4 ++++ src/worker/sql.rs | 17 ++++++++++++++--- 4 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 migrations/014_falukant_certificate_productions_count_since.sql diff --git a/docs/FALUKANT_PRODUCTION_CERTIFICATE.md b/docs/FALUKANT_PRODUCTION_CERTIFICATE.md index 7857168..478787c 100644 --- a/docs/FALUKANT_PRODUCTION_CERTIFICATE.md +++ b/docs/FALUKANT_PRODUCTION_CERTIFICATE.md @@ -7,7 +7,7 @@ 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` – Update der Stufe +- `QUERY_UPDATE_FALUKANT_USER_CERTIFICATE` – Stufe + **`certificate_productions_count_since = NOW()`** (Migration `014`) ## Logik (Kurz, Spec §4) @@ -40,7 +40,11 @@ Rang aus **`political_office_type.name`** (Substring-Heuristik im Daemon, ohne D ## Abgeschlossene Produktionen -**`COUNT(*)`** aus `falukant_log.production` mit `producer_id = falukant_user.id` **oder** `character.id` (Backend kann je nach Kontext die eine oder andere ID schreiben). +**`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_falukant_certificate_productions_count_since.sql`**). **`NULL`** bei dieser Spalte: 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 diff --git a/migrations/014_falukant_certificate_productions_count_since.sql b/migrations/014_falukant_certificate_productions_count_since.sql new file mode 100644 index 0000000..0340dbb --- /dev/null +++ b/migrations/014_falukant_certificate_productions_count_since.sql @@ -0,0 +1,6 @@ +-- Zertifikat: abgeschlossene Produktionen für Mindestanforderungen / Produktionspunkte seit letztem Stufenwechsel +ALTER TABLE falukant_data.falukant_user + ADD COLUMN IF NOT EXISTS certificate_productions_count_since TIMESTAMPTZ; + +COMMENT ON COLUMN falukant_data.falukant_user.certificate_productions_count_since IS + 'Daemon: Zählt nur Produktionen in falukant_log.production mit Zeitstempel >= diesem Wert; bei Aufstieg/Bankrott/Erbfolge auf NOW() gesetzt (YpDaemon falukant_certificate). NULL = alle bisherigen Zeilen (Bestand vor Migration).'; diff --git a/migrations/README.md b/migrations/README.md index 83f26ff..324e247 100644 --- a/migrations/README.md +++ b/migrations/README.md @@ -33,3 +33,7 @@ Die Join-Spalte auf `political_office_benefit` heißt im Repo **`political_offic ## `013_falukant_political_daily_salary.sql` Spalte **`falukant_data.falukant_user.last_political_daily_salary_on`** (Datum): Idempotenz für **`political_benefits::run_daily_political_salary`** — einmal pro Tag Gutschrift; Beträge aus JSON-Feld **`daily_salary`** (`tr`/`benefitType` = `daily_salary`) oder gestufter Daemon-Fallback nach Amts-Rang. + +## `014_falukant_certificate_productions_count_since.sql` + +Spalte **`falukant_data.falukant_user.certificate_productions_count_since`**: Zertifikats-**Produktionszählung** (Mindestwerte + Produktionspunkte) ab diesem Zeitpunkt; Daemon setzt bei **Aufstieg/Bankrott/Erbfolge** auf `NOW()`. **`NULL`** = bis zur ersten Änderung weiterhin alle passenden Log-Zeilen zählen. diff --git a/src/worker/sql.rs b/src/worker/sql.rs index 2dcf473..cb887b8 100644 --- a/src/worker/sql.rs +++ b/src/worker/sql.rs @@ -2518,9 +2518,11 @@ pub const QUERY_UPDATE_PRODUCT_KNOWLEDGE_USER: &str = r#" AND k.product_id = 10; "#; +/// Aufräumen alter Log-Zeilen (Speicher); Zertifikats-Mindest-Produktionen seit Aufstieg siehe +/// `certificate_productions_count_since` — kein „Reset“ über Löschen nötig. pub const QUERY_DELETE_OLD_PRODUCTIONS: &str = r#" DELETE FROM falukant_log.production flp - WHERE DATE(flp.production_timestamp) < CURRENT_DATE; + WHERE DATE(flp.production_timestamp) < CURRENT_DATE - INTERVAL '30 days'; "#; pub const QUERY_GET_PRODUCERS_LAST_DAY: &str = r#" @@ -3691,6 +3693,7 @@ pub const QUERY_INSERT_CHILD_RELATION_LOVER: &str = r#" /// Ein Spielercharakter pro Falukant-User (bei mehreren lebenden: **höchste** `character.id`, /// typischerweise zuletzt aktiver Slot — konsistent mit UI, das oft den Hauptcharakter nutzt). +/// `completed_production_count`: Produktionen seit `certificate_productions_count_since` (Migration `014`); **NULL** = alle Log-Zeilen (Bestand vor erstem Aufstieg nach Migration). pub const QUERY_GET_PRODUCTION_CERTIFICATE_INPUT_ROWS: &str = r#" SELECT DISTINCT ON (fu.id) fu.id AS falukant_user_id, @@ -3708,8 +3711,14 @@ pub const QUERY_GET_PRODUCTION_CERTIFICATE_INPUT_ROWS: &str = r#" COALESCE(( SELECT COUNT(*)::bigint FROM falukant_log.production pl - WHERE pl.producer_id = fu.id - OR pl.producer_id = c.id + WHERE (pl.producer_id = fu.id OR pl.producer_id = c.id) + AND ( + fu.certificate_productions_count_since IS NULL + OR COALESCE( + pl.production_timestamp, + pl.production_date::timestamp + ) >= fu.certificate_productions_count_since + ) ), 0) AS completed_production_count, COALESCE(( SELECT MAX(cot.hierarchy_level)::int @@ -3738,9 +3747,11 @@ pub const QUERY_GET_PRODUCTION_CERTIFICATE_INPUT_ROWS: &str = r#" ORDER BY fu.id, c.id DESC; "#; +/// Setzt bei jeder Stufenänderung `certificate_productions_count_since` (Mindest-Produktionen / PP neu ab Aufstieg). pub const QUERY_UPDATE_FALUKANT_USER_CERTIFICATE: &str = r#" UPDATE falukant_data.falukant_user SET certificate = $1::int, + certificate_productions_count_since = NOW(), updated_at = NOW() WHERE id = $2::int; "#;