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.
All checks were successful
Deploy yourpart (blue-green) / deploy (push) Successful in 3m12s

This commit is contained in:
Torsten Schulz (local)
2026-04-09 08:13:17 +02:00
parent 731c39dfa4
commit e460792357
4 changed files with 30 additions and 5 deletions

View File

@@ -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

View File

@@ -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).';

View File

@@ -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.

View File

@@ -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;
"#;