diff --git a/backend/migrations/20260402140000-add-certificate-productions-count-since.cjs b/backend/migrations/20260402140000-add-certificate-productions-count-since.cjs new file mode 100644 index 0000000..7e22432 --- /dev/null +++ b/backend/migrations/20260402140000-add-certificate-productions-count-since.cjs @@ -0,0 +1,22 @@ +/* eslint-disable */ +'use strict'; + +module.exports = { + async up(queryInterface) { + await queryInterface.sequelize.query(` + ALTER TABLE falukant_data.falukant_user + ADD COLUMN IF NOT EXISTS certificate_productions_count_since TIMESTAMPTZ; + `); + await queryInterface.sequelize.query(` + COMMENT ON COLUMN falukant_data.falukant_user.certificate_productions_count_since IS + 'Daemon/UI: Zählt nur falukant_log.production-Zeilen mit COALESCE(production_timestamp, production_date::timestamp) >= diesem Wert; bei Stufenänderung (Aufstieg/Bankrott/Erbfolge) auf NOW() (YpDaemon QUERY_UPDATE_FALUKANT_USER_CERTIFICATE). NULL = alle passenden Log-Zeilen bis zur ersten Stufenänderung nach Migration. Kein Löschen der Logs zum Reset.'; + `); + }, + + async down(queryInterface) { + await queryInterface.sequelize.query(` + ALTER TABLE falukant_data.falukant_user + DROP COLUMN IF EXISTS certificate_productions_count_since; + `); + } +}; diff --git a/backend/migrations/README.md b/backend/migrations/README.md new file mode 100644 index 0000000..5d689ac --- /dev/null +++ b/backend/migrations/README.md @@ -0,0 +1,13 @@ +# Backend-Migrationen (Sequelize) + +Migrationen in diesem Ordner werden mit dem Sequelize-CLI ausgeführt (Konfiguration siehe Projekt-Root / `backend`). + +## Falukant: Zertifikat und Produktionszählung + +| Datei | Inhalt | +|--------|--------| +| `20260402140000-add-certificate-productions-count-since.cjs` | Spalte `falukant_data.falukant_user.certificate_productions_count_since` (`TIMESTAMPTZ`, nullable) inkl. Kommentar. Setzt die DB-Grundlage dafür, dass Daemon, Backend und UI dieselbe Periode für „abgeschlossene Produktionen“ nutzen (Filter mit `COALESCE(production_timestamp, production_date::timestamp)` ab diesem Zeitpunkt; `NULL` = bisherige Historie). | + +Eine parallele SQL-Migration im Daemon-Repository (z. B. `014_falukant_certificate_productions_count_since.sql`) kann dieselbe Spalte anlegen, wenn das Deployment dort getrennt ist – Schema doppelt anlegen vermeiden. + +Details zur Zähl- und Retention-Logik: `docs/FALUKANT_PRODUCTION_CERTIFICATE.md`. diff --git a/backend/models/falukant/data/user.js b/backend/models/falukant/data/user.js index 1c1d331..5a69be0 100644 --- a/backend/models/falukant/data/user.js +++ b/backend/models/falukant/data/user.js @@ -29,6 +29,10 @@ FalukantUser.init({ type: DataTypes.INTEGER, allowNull: false, defaultValue: 1}, + certificateProductionsCountSince: { + type: DataTypes.DATE, + allowNull: true + }, mainBranchRegionId: { type: DataTypes.INTEGER, allowNull: true diff --git a/backend/services/falukantService.js b/backend/services/falukantService.js index d532abe..c36d7ac 100644 --- a/backend/services/falukantService.js +++ b/backend/services/falukantService.js @@ -14,7 +14,6 @@ import TitleBenefit from '../models/falukant/type/title_benefit.js'; import Branch from '../models/falukant/data/branch.js'; import BranchType from '../models/falukant/type/branch.js'; import Production from '../models/falukant/data/production.js'; -import DayProduction from '../models/falukant/log/dayproduction.js'; import ProductType from '../models/falukant/type/product.js'; import Knowledge from '../models/falukant/data/product_knowledge.js'; import Inventory from '../models/falukant/data/inventory.js'; @@ -976,7 +975,7 @@ class FalukantService extends BaseService { ] }, ], - attributes: ['id', 'money', 'creditAmount', 'todayCreditTaken', 'certificate'] + attributes: ['id', 'money', 'creditAmount', 'todayCreditTaken', 'certificate', 'certificateProductionsCountSince'] }); if (!u) throw new Error('User not found'); if (u.certificate == null) { @@ -2952,6 +2951,36 @@ class FalukantService extends BaseService { return candidates[0] || { rank: 0, name: null }; } + /** + * Zertifikat: abgeschlossene Produktionen über alle Regionen/Niederlassungen. + * Pro (Produkt, Kalendertag) nur ein Zähler – mehrere Niederlassungen in verschiedenen Regionen werden zusammengeführt. + * Filter bei gesetztem countSince wie Daemon (GET_PRODUCTION_CERTIFICATE_INPUT_ROWS): + * COALESCE(production_timestamp, production_date::timestamp) >= countSince. + * + * @param {number} producerId falukant_user.id + * @param {Date|null|undefined} countSince null/undefined = gesamte Historie (Bestand / vor erster Stufenänderung) + */ + async getCertificateCompletedProductionCount(producerId, countSince) { + const sinceClause = countSince + ? ' AND COALESCE(production_timestamp, production_date::timestamp) >= :countSince' + : ''; + const replacements = { producerId }; + if (countSince) replacements.countSince = countSince; + const rows = await sequelize.query( + ` + SELECT COUNT(*)::int AS cnt + FROM ( + SELECT 1 + FROM falukant_log.production + WHERE producer_id = :producerId${sinceClause} + GROUP BY product_id, production_date + ) AS sub + `, + { replacements, type: sequelize.QueryTypes.SELECT } + ); + return Number(rows[0]?.cnt ?? 0); + } + async buildCertificateProgress(user) { const character = user?.character || await FalukantCharacter.findOne({ where: { userId: user.id }, @@ -2961,9 +2990,10 @@ class FalukantService extends BaseService { return null; } + const productionsSince = user.certificateProductionsCountSince ?? null; const [avgKnowledge, completedProductions, highestPoliticalOffice, highestChurchOffice, house, title] = await Promise.all([ this.calculateAverageKnowledge(character.id), - DayProduction.count({ where: { producerId: user.id } }), + this.getCertificateCompletedProductionCount(user.id, productionsSince), this.getHighestPoliticalOfficeInfo(user.id), this.getHighestChurchOfficeInfo(user.id), UserHouse.findOne({ @@ -3077,6 +3107,9 @@ class FalukantService extends BaseService { scoreRequirementMet, minimumRequirementsMet, readyForNextCertificate: scoreRequirementMet && minimumRequirementsMet, + certificateProductionsCountSince: productionsSince + ? new Date(productionsSince).toISOString() + : null, }; } @@ -4112,6 +4145,10 @@ class FalukantService extends BaseService { } await candidate.update({ userId: user.id }); + await FalukantUser.update( + { certificateProductionsCountSince: new Date() }, + { where: { id: user.id } } + ); return { success: true, heirId: candidate.id }; } diff --git a/docs/FALUKANT_PRODUCTION_CERTIFICATE.md b/docs/FALUKANT_PRODUCTION_CERTIFICATE.md new file mode 100644 index 0000000..8a1e0a9 --- /dev/null +++ b/docs/FALUKANT_PRODUCTION_CERTIFICATE.md @@ -0,0 +1,56 @@ +# Falukant: Produktions-Log und Zertifikatszählung (Abgleich Daemon ↔ Backend/UI) + +## Ziel + +Die sichtbare Zertifikatsvorschau (Backend `buildCertificateProgress` / UI) soll dieselbe **abgeschlossene Produktionen**-Logik verwenden wie der Zertifikats-Daemon. Außerdem soll klar sein, wie Log-Zeilen aufbewahrt und bereinigt werden – **ohne** breites Löschen nur zum Zurücksetzen der Zählung. + +## Spalte `certificate_productions_count_since` + +- Tabelle: `falukant_data.falukant_user` +- Typ: `TIMESTAMPTZ`, nullable +- **Semantik:** Nach einem **Zertifikatsaufstieg**, **Bankrott** oder **Erbfolge** (und gleichartigen Stufenänderungen im Daemon) wird der Zeitpunkt auf `NOW()` gesetzt. Alle Zählungen für Mindestanforderungen / Produktionspunkte berücksichtigen nur noch Log-Zeilen mit effektivem Zeitpunkt **≥** diesem Wert. +- **`NULL`:** Wie bisher die gesamte relevante Historie zählen (Bestand bis zur ersten Stufenänderung nach Einführung der Spalte). + +Daemon-seitig typisch: `QUERY_UPDATE_FALUKANT_USER_CERTIFICATE` setzt bei jeder Stufenänderung `certificate_productions_count_since = NOW()`. + +## Effektiver Zeitpunkt einer Log-Zeile + +Überall, wo der Daemon und das Backend dieselbe Bedeutung brauchen: + +```sql +COALESCE(pl.production_timestamp, pl.production_date::timestamp) +``` + +- `production_timestamp` fehlt oder ist unzuverlässig (z. B. ältere Upserts): Fallback auf Kalendertag `production_date`. +- So wird **nicht** nur „Produktionen für den laufenden Kalendertag“ gewertet, sondern konsistent der gespeicherte Zeitbezug der Zeile – abgestimmt mit dem Daemon (`QUERY_GET_PRODUCTION_CERTIFICATE_INPUT_ROWS`). + +Backend-Implementierung: `getCertificateCompletedProductionCount` in `backend/services/falukantService.js` filtert bei gesetztem `certificateProductionsCountSince` mit derselben `COALESCE`-Bedingung. + +## Log-Retention (30 Tage) + +`falukant_log.production` wird u. a. für Wissens-Updates, Preis-/Producer-Events und Zertifikatszählung genutzt. **Nicht** sinnvoll: alle Logs nach einem Aufstieg löschen, nur um neu zu zählen. + +Stattdessen: + +- Zähler-Reset über **`certificate_productions_count_since`** (siehe oben). +- Alte Zeilen werden mit **Retention 30 Tage** bereinigt, damit die Tabelle nicht unbegrenzt wächst. + +In diesem Repo: **`UserCharacterWorker`** führt stündlich `QUERY_DELETE_OLD_PRODUCTIONS` aus: + +```sql +DELETE FROM falukant_log.production +WHERE COALESCE(production_timestamp, production_date::timestamp) < NOW() - INTERVAL '30 days'; +``` + +Parallel löscht derselbe Worker weiterhin einzelne Zeilen nach dem Wissens-Update (`QUERY_DELETE_LOG_ENTRY`), sobald die Zeile „vom Vortag“ verarbeitet wurde (`QUERY_UPDATE_GET_ITEMS_TO_UPDATE` mit `COALESCE` für das Datumsfenster). + +## Migrationen + +- **Sequelize (dieses Repo):** `backend/migrations/20260402140000-add-certificate-productions-count-since.cjs` – fügt die Spalte und den DB-Kommentar hinzu. +- **Externer Daemon / SQL-Pfad:** falls vorhanden z. B. `migrations/014_falukant_certificate_productions_count_since.sql` – inhaltlich dieselbe Spalte; nur eine Quelle der Wahrheit fürs Schema nötig. + +Weitere Hinweise: `backend/migrations/README.md`. + +## Verwandte Spezifikation + +Ausführlicheres Fachkonzept (Stufen, Score, Events): [`FALUKANT_PRODUCTION_CERTIFICATE_SPEC.md`](./FALUKANT_PRODUCTION_CERTIFICATE_SPEC.md). diff --git a/docs/FALUKANT_PRODUCTION_CERTIFICATE_SPEC.md b/docs/FALUKANT_PRODUCTION_CERTIFICATE_SPEC.md index 636c25e..65af8e0 100644 --- a/docs/FALUKANT_PRODUCTION_CERTIFICATE_SPEC.md +++ b/docs/FALUKANT_PRODUCTION_CERTIFICATE_SPEC.md @@ -16,9 +16,8 @@ Dieses Dokument beschreibt: Wichtig: -- Die nötigen DB-Grundlagen sind bereits vorhanden. -- Der Daemon muss keine neuen Schemaänderungen erwarten. -- Bestehende Felder wie `falukant_data.falukant_user.certificate` und `falukant_type.product.category` bleiben die führende Basis. +- Zusätzlich zu `falukant_user.certificate` gibt es `falukant_user.certificate_productions_count_since` (Zeitpunkt, ab dem Produktions-Log-Zeilen für Zertifikats-Mindestanforderungen zählen; siehe [`FALUKANT_PRODUCTION_CERTIFICATE.md`](./FALUKANT_PRODUCTION_CERTIFICATE.md)). +- Bestehende Felder wie `falukant_data.falukant_user.certificate` und `falukant_type.product.category` bleiben die führende Basis für die Produktfreigabe. ## 2. Bestehende technische Basis @@ -26,6 +25,8 @@ Bereits vorhanden: - `falukant_data.falukant_user.certificate` - aktuelle Produktionsfreigabe des Spielers +- `falukant_data.falukant_user.certificate_productions_count_since` (optional, empfohlen) + - ab diesem Zeitpunkt zählen Produktions-Log-Zeilen für Zertifikats-Mindestanforderungen (`NULL` = volle Historie); Details: [`FALUKANT_PRODUCTION_CERTIFICATE.md`](./FALUKANT_PRODUCTION_CERTIFICATE.md) - `falukant_type.product.category` - erforderliche Zertifikatsstufe des Produkts - `falukant_data.knowledge` @@ -109,7 +110,7 @@ Für jeden Spielercharakter mit `falukant_user`: - `avgKnowledge` - Durchschnitt aus `falukant_data.knowledge.knowledge` des Spielercharakters - `completedProductions` - - Anzahl abgeschlossener Produktionen des Spielers + - Anzahl abgeschlossener Produktionen des Spielers (Daemon und Backend/UI: aus `falukant_log.production`, gruppiert nach Produkt und Kalendertag, gefiltert ab `certificate_productions_count_since` mit `COALESCE(production_timestamp, production_date::timestamp)`; siehe [`FALUKANT_PRODUCTION_CERTIFICATE.md`](./FALUKANT_PRODUCTION_CERTIFICATE.md)) - `highestPoliticalOfficeRank` - höchster politischer Amtsrang - `highestChurchOfficeRank` @@ -317,11 +318,10 @@ Diese Aktionen verändern nur die Eingangsgrößen. Die eigentliche Zertifikatsa ## 5.2 Daemon-Hinweis -Für den Daemon gilt ausdrücklich: +Für den Daemon gilt: -- die relevanten DB-Felder sind bereits vorhanden -- es müssen für diese Funktion keine zusätzlichen Schemaänderungen mehr eingeplant werden -- der Daemon soll direkt mit den vorhandenen Tabellen arbeiten +- Zertifikatsstufe und Produktkategorien wie oben; für die Produktionszählung nach Stufenwechsel ist `certificate_productions_count_since` vorgesehen (Migration siehe Repo / [`FALUKANT_PRODUCTION_CERTIFICATE.md`](./FALUKANT_PRODUCTION_CERTIFICATE.md)). +- Der Daemon soll dieselbe `COALESCE(production_timestamp, production_date::timestamp)`-Logik wie Backend und UI verwenden, damit Daily-Prüfung und Oberfläche übereinstimmen. ## 5.3 Empfohlener Daily-Ablauf @@ -482,6 +482,6 @@ Fertig ist die erste Version, wenn: - nur Produkte mit `product.category <= falukant_user.certificate` produzierbar sind - der Daemon die Zertifikatsprüfung genau einmal täglich ausführt -- der Daemon bei Aufstieg das Zertifikat fortschreibt +- der Daemon bei Aufstieg das Zertifikat fortschreibt und `certificate_productions_count_since` setzt - die UI auf das Zertifikats-Event gezielt reagiert -- keine neuen DB-Änderungen für diese Funktion nötig sind +- Backend/UI dieselbe Produktionszählung wie der Daemon verwenden (siehe [`FALUKANT_PRODUCTION_CERTIFICATE.md`](./FALUKANT_PRODUCTION_CERTIFICATE.md)) diff --git a/frontend/scripts/ceb-patches/falukant-patch.json b/frontend/scripts/ceb-patches/falukant-patch.json index 1c99e92..817540a 100644 --- a/frontend/scripts/ceb-patches/falukant-patch.json +++ b/frontend/scripts/ceb-patches/falukant-patch.json @@ -2,6 +2,7 @@ "falukant": { "overview": { "certificate": { + "productionsSince": "Mga produksyon sugod sa {date} ra ang ihap (sukad sa katapusang pag-asenso, bangkaruta, o panununod).", "factor": { "avgKnowledge": "Average kahibalo", "completedProductions": "Nahuman produksyons", diff --git a/frontend/src/i18n/locales/ceb/falukant.json b/frontend/src/i18n/locales/ceb/falukant.json index 900e567..f75adb9 100644 --- a/frontend/src/i18n/locales/ceb/falukant.json +++ b/frontend/src/i18n/locales/ceb/falukant.json @@ -158,6 +158,7 @@ }, "factors": "Karon nga mga bili", "requirements": "Mga kinahanglanon sa sunod nga level", + "productionsSince": "Mga produksyon sugod sa {date} ra ang ihap (sukad sa katapusang pag-asenso, bangkaruta, o panununod).", "factor": { "avgKnowledge": "Average kahibalo", "completedProductions": "Nahuman produksyons", diff --git a/frontend/src/i18n/locales/de/falukant.json b/frontend/src/i18n/locales/de/falukant.json index 3466ad7..e59011d 100644 --- a/frontend/src/i18n/locales/de/falukant.json +++ b/frontend/src/i18n/locales/de/falukant.json @@ -178,6 +178,7 @@ }, "factors": "Aktuelle Werte", "requirements": "Bedingungen für die nächste Stufe", + "productionsSince": "Nur Produktionen ab {date} zählen (seit letztem Aufstieg, Bankrott oder Erbfolge).", "factor": { "avgKnowledge": "Durchschnittliches Wissen", "completedProductions": "Abgeschlossene Produktionen", diff --git a/frontend/src/i18n/locales/en/falukant.json b/frontend/src/i18n/locales/en/falukant.json index 53183d7..816bb09 100644 --- a/frontend/src/i18n/locales/en/falukant.json +++ b/frontend/src/i18n/locales/en/falukant.json @@ -159,6 +159,7 @@ }, "factors": "Current values", "requirements": "Requirements for the next level", + "productionsSince": "Only productions on or after {date} count (since last promotion, bankruptcy, or succession).", "factor": { "avgKnowledge": "Average knowledge", "completedProductions": "Completed productions", diff --git a/frontend/src/i18n/locales/es/falukant.json b/frontend/src/i18n/locales/es/falukant.json index f876993..901bcb4 100644 --- a/frontend/src/i18n/locales/es/falukant.json +++ b/frontend/src/i18n/locales/es/falukant.json @@ -177,6 +177,7 @@ }, "factors": "Valores actuales", "requirements": "Condiciones para el siguiente nivel", + "productionsSince": "Solo cuentan producciones desde el {date} (desde el último ascenso, quiebra o sucesión).", "factor": { "avgKnowledge": "Conocimiento medio", "completedProductions": "Producciones completadas", diff --git a/frontend/src/i18n/locales/fr/falukant.json b/frontend/src/i18n/locales/fr/falukant.json index bed01ff..5c94db2 100644 --- a/frontend/src/i18n/locales/fr/falukant.json +++ b/frontend/src/i18n/locales/fr/falukant.json @@ -177,6 +177,7 @@ }, "factors": "Valeurs actuelles", "requirements": "Conditions pour la prochaine étape", + "productionsSince": "Seules les productions à partir du {date} comptent (depuis la dernière promotion, faillite ou succession).", "factor": { "avgKnowledge": "Connaissance moyenne", "completedProductions": "Productions terminées", diff --git a/frontend/src/views/falukant/OverviewView.vue b/frontend/src/views/falukant/OverviewView.vue index d72dbcb..a4fd058 100644 --- a/frontend/src/views/falukant/OverviewView.vue +++ b/frontend/src/views/falukant/OverviewView.vue @@ -131,6 +131,16 @@
{{ $t('falukant.overview.certificate.factor.completedProductions') }} {{ certificateProgress.currentValues.completedProductions }} +

+ {{ + $t('falukant.overview.certificate.productionsSince', { + date: formatDate(certificateProgress.certificateProductionsCountSince), + }) + }} +

{{ $t('falukant.overview.certificate.factor.reputation') }} @@ -907,6 +917,13 @@ export default { color: var(--color-text-secondary); } +.certificate-panel__productions-since { + margin: 6px 0 0; + font-size: 0.85rem; + line-height: 1.35; + color: var(--color-text-secondary); +} + .certificate-panel__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); diff --git a/src/produce_worker.cpp b/src/produce_worker.cpp index dfaeae2..d15d574 100644 --- a/src/produce_worker.cpp +++ b/src/produce_worker.cpp @@ -194,8 +194,12 @@ void ProduceWorker::addProductionToLog(int regionId, int userId, int productId, ConnectionGuard connGuard(pool); auto &db = connGuard.get(); db.prepare("QUERY_INSERT_UPDATE_PRODUCTION_LOG", QUERY_INSERT_UPDATE_PRODUCTION_LOG); - db.execute("QUERY_INSERT_UPDATE_PRODUCTION_LOG", { std::to_string(regionId), std::to_string(productId), - std::to_string(productId), std::to_string(userId) }); + db.execute("QUERY_INSERT_UPDATE_PRODUCTION_LOG", { + std::to_string(regionId), + std::to_string(productId), + std::to_string(quantity), + std::to_string(userId), + }); } catch (const std::exception &e) { } diff --git a/src/usercharacterworker.cpp b/src/usercharacterworker.cpp index 8cd0a9e..0a567f5 100644 --- a/src/usercharacterworker.cpp +++ b/src/usercharacterworker.cpp @@ -30,6 +30,7 @@ void UserCharacterWorker::run() { processCharacterEvents(); updateCharactersMood(); handleCredits(); + deleteOldProductionLogs(); } catch (const std::exception &e) { std::cerr << "[UserCharacterWorker] Fehler in processCharacterEvents: " << e.what() << std::endl; } @@ -233,6 +234,17 @@ void UserCharacterWorker::setNewMoney(int falukantUserId, double newAmount) { }); } +void UserCharacterWorker::deleteOldProductionLogs() { + try { + ConnectionGuard connGuard(pool); + auto &db = connGuard.get(); + db.prepare("QUERY_DELETE_OLD_PRODUCTIONS", QUERY_DELETE_OLD_PRODUCTIONS); + db.execute("QUERY_DELETE_OLD_PRODUCTIONS"); + } catch (const std::exception &e) { + std::cerr << "[UserCharacterWorker] Fehler in deleteOldProductionLogs: " << e.what() << std::endl; + } +} + void UserCharacterWorker::recalculateKnowledge() { setCurrentStep("Get character data"); ConnectionGuard connGuard(pool); diff --git a/src/usercharacterworker.h b/src/usercharacterworker.h index eba378b..2c0a45b 100644 --- a/src/usercharacterworker.h +++ b/src/usercharacterworker.h @@ -25,6 +25,7 @@ private: int calculateHealthChange(int age); void handleCharacterDeath(int characterId); void recalculateKnowledge(); + void deleteOldProductionLogs(); void processPregnancies(); void handleCredits(); void setHeir(int characterId); @@ -61,7 +62,13 @@ private: static constexpr const char *QUERY_UPDATE_GET_ITEMS_TO_UPDATE = R"( SELECT id, product_id, producer_id, quantity FROM falukant_log.production p - WHERE p.production_timestamp::date < current_date + WHERE (COALESCE(p.production_timestamp, p.production_date::timestamp))::date < CURRENT_DATE + )"; + + /** Log-Retention: ältere Zeilen entfernen (Daemon/UI-Zertifikatszählung braucht Historie nur begrenzt). */ + static constexpr const char *QUERY_DELETE_OLD_PRODUCTIONS = R"( + DELETE FROM falukant_log.production + WHERE COALESCE(production_timestamp, production_date::timestamp) < NOW() - INTERVAL '30 days' )"; static constexpr const char *QUERY_UPDATE_GET_CHARACTER_IDS = R"( diff --git a/src/valuerecalculationworker.cpp b/src/valuerecalculationworker.cpp index 7dd86e1..982eb8c 100644 --- a/src/valuerecalculationworker.cpp +++ b/src/valuerecalculationworker.cpp @@ -71,8 +71,10 @@ void ValueRecalculationWorker::calculateProductKnowledge() { const auto userId = std::stoi(user.at("producer_id")); sendMessageToFalukantUsers(userId, message); } - db.prepare("QUERY_DELETE_OLD_PRODUCTIONS", QUERY_DELETE_OLD_PRODUCTIONS); - db.execute("QUERY_DELETE_OLD_PRODUCTIONS"); + /* Kein DELETE mehr auf falukant_log.production um Mitternacht: Die Einträge werden für + * Zertifikatsfortschritt (Backend + Daemon) benötigt. + * UserCharacterWorker: nach Wissens-Update pro Zeile löschen; zusätzlich stündlich Retention + * QUERY_DELETE_OLD_PRODUCTIONS (30 Tage, COALESCE(timestamp, production_date)). */ } void ValueRecalculationWorker::calculateRegionalSellPrice() { diff --git a/src/valuerecalculationworker.h b/src/valuerecalculationworker.h index 8bb8d2a..d76973a 100644 --- a/src/valuerecalculationworker.h +++ b/src/valuerecalculationworker.h @@ -42,21 +42,16 @@ private: SET knowledge = LEAST(100, k.knowledge + 1) FROM falukant_data."character" c JOIN falukant_log.production p - ON DATE(p.production_timestamp) = CURRENT_DATE - INTERVAL '1 day' + ON DATE(COALESCE(p.production_timestamp, p.production_date::timestamp)) = CURRENT_DATE - INTERVAL '1 day' WHERE c.id = k.character_id AND c.user_id = 18 AND k.product_id = 10 )"; - static constexpr const char *QUERY_DELETE_OLD_PRODUCTIONS = R"( - delete from falukant_log.production flp - where date(flp.production_timestamp) < CURRENT_DATE - )"; - static constexpr const char *QUERY_GET_PRODUCERS_LAST_DAY = R"( select p."producer_id" from falukant_log.production p - where date(p."production_timestamp") = CURRENT_DATE - interval '1 day' + where date(COALESCE(p.production_timestamp, p.production_date::timestamp)) = CURRENT_DATE - interval '1 day' group by producer_id )";