Refactor Falukant certificate management: Consolidated certificate logic into the FalukantFamilyWorker's daily processing, removing the separate FalukantCertificateWorker. Updated SQL queries to include app_user_id and enhanced documentation for clarity on certificate scoring and daily recalculation logic.
This commit is contained in:
40
docs/FALUKANT_DIRECTOR_PRODUCTION_COST.md
Normal file
40
docs/FALUKANT_DIRECTOR_PRODUCTION_COST.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Director: Stückkosten Produktion (Balancing)
|
||||
|
||||
## Problem
|
||||
|
||||
Die DB wählt das „beste“ Produkt über `QUERY_GET_BEST_PRODUCTION` (Worth-Formel mit `- 6 * ftp.category`), während der Daemon früher **nur** `certificate * 6` pro Stück abgebucht hat – **ohne** die Produktklasse. Dadurch konnten Stückkosten und Worth-Ranking auseinanderlaufen.
|
||||
|
||||
## Aktuelle Formel (Rust, `DirectorWorker::calc_one_piece_cost`)
|
||||
|
||||
```
|
||||
raw = certificate × PRODUCTION_COST_PER_CERT_LEVEL
|
||||
+ product_category × PRODUCTION_COST_PER_PRODUCT_CATEGORY
|
||||
effektiv = raw × (1 − headroom_discount)
|
||||
```
|
||||
|
||||
- `product_category` kommt aus `QUERY_GET_BEST_PRODUCTION` (`ftp.category`).
|
||||
- **Headroom** = `max(0, certificate − product_category)`
|
||||
Wenn du **nicht** am Klassenlimit produzierst (Zertifikat höher als nötig für das Produkt), gibt es einen kleinen Rabatt (Effizienz / Reserve).
|
||||
|
||||
Konstanten in `src/worker/director.rs` (anpassen zum Feintuning):
|
||||
|
||||
| Konstante | Standard | Bedeutung |
|
||||
|-----------|----------|-----------|
|
||||
| `PRODUCTION_COST_PER_CERT_LEVEL` | `6.0` | wie früher `×6` pro Zertifikatsstufe |
|
||||
| `PRODUCTION_COST_PER_PRODUCT_CATEGORY` | `1.0` | Material pro Produktklasse |
|
||||
| `PRODUCTION_HEADROOM_DISCOUNT_PER_STEP` | `0.035` | Rabatt pro Headroom-Stufe |
|
||||
| `PRODUCTION_HEADROOM_DISCOUNT_CAP` | `0.14` | maximaler Gesamtrabatt |
|
||||
|
||||
## Spielerfortschritt
|
||||
|
||||
- **Höheres Zertifikat** allein erhöht die Basis-Stückkosten linear – entspricht **besserer** Produktpalette (`ftp.category <= certificate` in SQL).
|
||||
- **Höhere Produktklasse** erhöht `raw` über `product_category` (bessere Ware = mehr Material).
|
||||
- **Zertifikat über der Produktklasse** (Headroom) senkt die effektiven Kosten – Belohnung, wenn du nicht immer nur am Limit produzierst.
|
||||
|
||||
## Worth in SQL
|
||||
|
||||
Die Worth-Zeile in `QUERY_GET_BEST_PRODUCTION` sollte bei größeren Formeländerungen **mit** angepasst werden, damit der Director weiterhin sinnvoll sortiert.
|
||||
|
||||
## Parallelproduktionen
|
||||
|
||||
`MAX_PARALLEL_PRODUCTIONS` (aktuell 2) bestimmt, wie viele Linien pro Tick Geld binden – unabhängig von der Stückkostenformel; bei Liquiditätsproblemen ggf. auf `1` setzen.
|
||||
@@ -1,18 +1,20 @@
|
||||
# Falukant: Produktionszertifikate (Daemon)
|
||||
|
||||
Der **`FalukantCertificateWorker`** berechnet einmal täglich die Zielstufe und schreibt `falukant_user.certificate` fort (max. **+1** pro Tag, keine normale Herabstufung).
|
||||
Die Zertifikatslogik läuft **ausschließlich im Daily-Tick** von `FalukantFamilyWorker` (`process_daily`, 24h), nicht in einem eigenen Worker-Thread. Sie schreibt `falukant_user.certificate` fort (max. **+1** pro Tag, keine normale Herabstufung).
|
||||
|
||||
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
|
||||
|
||||
## Logik (Kurz)
|
||||
## Logik (Kurz, Spec §4)
|
||||
|
||||
- `certificateScore` aus gewichteten Punktwerten (Wissen, Produktion, Amt, Adel, Ruf, Haus)
|
||||
- `raw_target` aus Score-Schwellen (1.2 / 2.1 / 3.0 / 4.0)
|
||||
- `effective_target` mit Mindestanforderungen je Stufe (Spec §4.5)
|
||||
- Aufstieg nur wenn `effective_target > current` → **`current + 1`** (gegen `effective_target` begrenzt)
|
||||
- **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
|
||||
|
||||
## Politische Ämter
|
||||
@@ -25,7 +27,7 @@ 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` (Zeilen = aggregierte Log-Einträge).
|
||||
**`COUNT(*)`** aus `falukant_log.production` mit `producer_id = falukant_user.id`.
|
||||
|
||||
## Events (WebSocket)
|
||||
|
||||
@@ -34,6 +36,8 @@ Bei Änderung der Stufe:
|
||||
1. `falukantUpdateProductionCertificate` mit `reason: "daily_recalculation"`, `old_certificate`, `new_certificate`
|
||||
2. `falukantUpdateStatus`
|
||||
|
||||
`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)
|
||||
|
||||
- **Tod ohne Erben** / Zertifikats-Reset
|
||||
|
||||
@@ -16,7 +16,7 @@ use worker::{
|
||||
CharacterCreationWorker, ConnectionPool, DirectorWorker, EventsWorker, HouseWorker,
|
||||
PoliticsWorker, ProduceWorker, StockageManager, TransportWorker, UndergroundWorker,
|
||||
UserCharacterWorker, ValueRecalculationWorker, WeatherWorker, Worker,
|
||||
FalukantFamilyWorker, FalukantCertificateWorker,
|
||||
FalukantFamilyWorker,
|
||||
};
|
||||
|
||||
static KEEP_RUNNING: AtomicBool = AtomicBool::new(true);
|
||||
@@ -142,10 +142,6 @@ fn create_workers(pool: ConnectionPool, broker: MessageBroker) -> Vec<Box<dyn Wo
|
||||
pool.clone(),
|
||||
broker.clone(),
|
||||
)),
|
||||
Box::new(FalukantCertificateWorker::new(
|
||||
pool.clone(),
|
||||
broker.clone(),
|
||||
)),
|
||||
Box::new(HouseWorker::new(pool.clone(), broker.clone())),
|
||||
Box::new(PoliticsWorker::new(pool.clone(), broker.clone())),
|
||||
Box::new(TransportWorker::new(pool.clone(), broker.clone())),
|
||||
|
||||
@@ -1,132 +1,98 @@
|
||||
//! Produktionszertifikat: tägliche Neuberechnung von `falukant_user.certificate`.
|
||||
//! Spec: docs/FALUKANT_PRODUCTION_CERTIFICATE.md
|
||||
//! Produktionszertifikat: tägliche Neuberechnung von `falukant_user.certificate` im **FalukantFamilyWorker-Daily-Tick**
|
||||
//! (nicht in einem eigenen Worker-Thread). Spec: `docs/FALUKANT_PRODUCTION_CERTIFICATE.md` und
|
||||
//! „Falukant: Produktionszertifikate – Fach- und Integrationsspezifikation“.
|
||||
|
||||
use crate::db::{ConnectionPool, DbError, Row};
|
||||
use crate::db::{DbError, Row};
|
||||
use crate::message_broker::MessageBroker;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use super::base::{BaseWorker, Worker, WorkerState};
|
||||
use super::base::BaseWorker;
|
||||
use crate::worker::sql::{
|
||||
QUERY_GET_PRODUCTION_CERTIFICATE_INPUT_ROWS, QUERY_UPDATE_FALUKANT_USER_CERTIFICATE,
|
||||
};
|
||||
|
||||
const DAILY_INTERVAL: Duration = Duration::from_secs(24 * 3600);
|
||||
|
||||
/// Wenn `money` darunter liegt, gilt der Spieler als bankrott → Zertifikat auf Stufe 1.
|
||||
/// Wenn `money` darunter liegt, gilt der Spieler als bankrott → Zertifikat auf Stufe 1 (Spec §4.7).
|
||||
const BANKRUPTCY_MONEY_THRESHOLD: f64 = -5000.0;
|
||||
|
||||
pub struct FalukantCertificateWorker {
|
||||
base: BaseWorker,
|
||||
}
|
||||
/// Einmal pro Daily-Tick (`FalukantFamilyWorker::process_daily`).
|
||||
pub fn run_daily(base: &BaseWorker, broker: &MessageBroker) -> Result<(), DbError> {
|
||||
let pool = &base.pool;
|
||||
let mut conn = pool
|
||||
.get()
|
||||
.map_err(|e| DbError::new(format!("DB-Verbindung fehlgeschlagen: {e}")))?;
|
||||
|
||||
impl FalukantCertificateWorker {
|
||||
pub fn new(pool: ConnectionPool, broker: MessageBroker) -> Self {
|
||||
Self {
|
||||
base: BaseWorker::new("FalukantCertificateWorker", pool, broker),
|
||||
conn.prepare("cert_rows", QUERY_GET_PRODUCTION_CERTIFICATE_INPUT_ROWS)?;
|
||||
conn.prepare("cert_upd", QUERY_UPDATE_FALUKANT_USER_CERTIFICATE)?;
|
||||
let rows = conn.execute("cert_rows", &[])?;
|
||||
|
||||
for row in rows {
|
||||
let fu_id = parse_i32(&row, "falukant_user_id", -1);
|
||||
if fu_id < 0 {
|
||||
continue;
|
||||
}
|
||||
let app_uid = parse_i32(&row, "app_user_id", -1);
|
||||
let event_uid = if app_uid > 0 { app_uid } else { fu_id };
|
||||
|
||||
let current = parse_i32(&row, "certificate", 1).clamp(1, 5);
|
||||
let money = parse_f64(&row, "money", 0.0);
|
||||
let avg_knowledge = parse_f64(&row, "avg_knowledge", 0.0);
|
||||
let completed = parse_i64(&row, "completed_production_count", 0);
|
||||
let max_church_hierarchy = parse_i32(&row, "max_church_hierarchy", 0);
|
||||
let pol_names = row
|
||||
.get("political_office_names")
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let reputation = parse_f64(&row, "reputation", 50.0);
|
||||
let title_level = parse_i32(&row, "title_level", 0);
|
||||
let house_position = parse_i32(&row, "house_position", 0);
|
||||
|
||||
// Bankrott: Herabsetzung (Spec §4.7)
|
||||
if money <= BANKRUPTCY_MONEY_THRESHOLD && current > 1 {
|
||||
conn.execute("cert_upd", &[&1_i32, &fu_id])?;
|
||||
publish_certificate_event(broker, event_uid, current, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
let knowledge_points = knowledge_points_from_avg(avg_knowledge);
|
||||
let production_points = production_points_from_count(completed);
|
||||
let political_rank = max_political_rank_from_names(&pol_names);
|
||||
let church_rank = church_rank_from_hierarchy(max_church_hierarchy);
|
||||
let highest_office_rank = political_rank.max(church_rank).min(5);
|
||||
let office_points = highest_office_rank.min(5);
|
||||
let nobility_points = (title_level - 1).clamp(0, 5);
|
||||
let reputation_points = reputation_points_from_rep(reputation);
|
||||
let house_points = house_points_from_position(house_position);
|
||||
|
||||
let certificate_score = knowledge_points as f64 * 0.45
|
||||
+ production_points as f64 * 0.30
|
||||
+ office_points as f64 * 0.08
|
||||
+ nobility_points as f64 * 0.05
|
||||
+ reputation_points as f64 * 0.07
|
||||
+ house_points as f64 * 0.05;
|
||||
|
||||
let raw_target = raw_target_from_score(certificate_score);
|
||||
let effective_target = effective_certificate_target(
|
||||
raw_target,
|
||||
avg_knowledge,
|
||||
completed,
|
||||
office_points,
|
||||
nobility_points,
|
||||
reputation_points,
|
||||
house_points,
|
||||
);
|
||||
|
||||
let new_certificate = if effective_target > current {
|
||||
(current + 1).min(effective_target).min(5)
|
||||
} else {
|
||||
current
|
||||
};
|
||||
|
||||
if new_certificate != current {
|
||||
conn.execute("cert_upd", &[&new_certificate, &fu_id])?;
|
||||
publish_certificate_event(broker, event_uid, current, new_certificate);
|
||||
}
|
||||
}
|
||||
|
||||
fn run_loop(pool: ConnectionPool, broker: MessageBroker, state: Arc<WorkerState>) {
|
||||
let mut last: Option<std::time::Instant> = None;
|
||||
while state.running_worker.load(Ordering::Relaxed) {
|
||||
let now = std::time::Instant::now();
|
||||
let run = match last {
|
||||
None => true,
|
||||
Some(t) => now.saturating_duration_since(t) >= DAILY_INTERVAL,
|
||||
};
|
||||
if run {
|
||||
if let Err(e) = Self::process_daily(&pool, &broker) {
|
||||
eprintln!("[FalukantCertificateWorker] process_daily: {e}");
|
||||
}
|
||||
last = Some(now);
|
||||
}
|
||||
for _ in 0..60 {
|
||||
if !state.running_worker.load(Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(Duration::from_secs(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn process_daily(pool: &ConnectionPool, broker: &MessageBroker) -> Result<(), DbError> {
|
||||
let mut conn = pool
|
||||
.get()
|
||||
.map_err(|e| DbError::new(format!("DB-Verbindung fehlgeschlagen: {e}")))?;
|
||||
|
||||
conn.prepare("cert_rows", QUERY_GET_PRODUCTION_CERTIFICATE_INPUT_ROWS)?;
|
||||
conn.prepare("cert_upd", QUERY_UPDATE_FALUKANT_USER_CERTIFICATE)?;
|
||||
let rows = conn.execute("cert_rows", &[])?;
|
||||
|
||||
for row in rows {
|
||||
let fu_id = parse_i32(&row, "falukant_user_id", -1);
|
||||
if fu_id < 0 {
|
||||
continue;
|
||||
}
|
||||
let current = parse_i32(&row, "certificate", 1).clamp(1, 127);
|
||||
let money = parse_f64(&row, "money", 0.0);
|
||||
let avg_knowledge = parse_f64(&row, "avg_knowledge", 0.0);
|
||||
let completed = parse_i64(&row, "completed_production_count", 0);
|
||||
let max_church_hierarchy = parse_i32(&row, "max_church_hierarchy", 0);
|
||||
let pol_names = row
|
||||
.get("political_office_names")
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let reputation = parse_f64(&row, "reputation", 50.0);
|
||||
let title_level = parse_i32(&row, "title_level", 0);
|
||||
let house_position = parse_i32(&row, "house_position", 0);
|
||||
|
||||
// Bankrott: Herabsetzung erlaubt (Spec)
|
||||
if money <= BANKRUPTCY_MONEY_THRESHOLD && current > 1 {
|
||||
conn.execute("cert_upd", &[&1_i32, &fu_id])?;
|
||||
publish_certificate_event(broker, fu_id, current, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
let knowledge_points = knowledge_points_from_avg(avg_knowledge);
|
||||
let production_points = production_points_from_count(completed);
|
||||
let political_rank = max_political_rank_from_names(&pol_names);
|
||||
let church_rank = church_rank_from_hierarchy(max_church_hierarchy);
|
||||
let highest_office_rank = political_rank.max(church_rank).min(5);
|
||||
let office_points = highest_office_rank.min(5);
|
||||
let nobility_points = (title_level - 1).clamp(0, 5);
|
||||
let reputation_points = reputation_points_from_rep(reputation);
|
||||
let house_points = house_points_from_position(house_position);
|
||||
|
||||
let certificate_score = knowledge_points as f64 * 0.35
|
||||
+ production_points as f64 * 0.20
|
||||
+ office_points as f64 * 0.15
|
||||
+ nobility_points as f64 * 0.10
|
||||
+ reputation_points as f64 * 0.10
|
||||
+ house_points as f64 * 0.10;
|
||||
|
||||
let raw_target = raw_target_from_score(certificate_score);
|
||||
let effective_target = effective_certificate_target(
|
||||
raw_target,
|
||||
avg_knowledge,
|
||||
completed,
|
||||
office_points,
|
||||
nobility_points,
|
||||
reputation_points,
|
||||
house_points,
|
||||
);
|
||||
|
||||
let new_certificate = if effective_target > current {
|
||||
(current + 1).min(effective_target)
|
||||
} else {
|
||||
current
|
||||
};
|
||||
|
||||
if new_certificate != current {
|
||||
conn.execute("cert_upd", &[&new_certificate, &fu_id])?;
|
||||
publish_certificate_event(broker, fu_id, current, new_certificate);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn publish_certificate_event(broker: &MessageBroker, user_id: i32, old_c: i32, new_c: i32) {
|
||||
@@ -189,7 +155,6 @@ fn production_points_from_count(n: i64) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Kirchliche Ämter: `hierarchy_level` auf 0–5 begrenzen (Daemon).
|
||||
fn church_rank_from_hierarchy(h: i32) -> i32 {
|
||||
if h <= 0 {
|
||||
0
|
||||
@@ -198,7 +163,7 @@ fn church_rank_from_hierarchy(h: i32) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Politische Amtsnamen → Rang 1–5 (konfigurierbar im Daemon).
|
||||
/// Politische Amtsnamen → Rang 1–5 (Heuristik im Daemon, ohne DB-Änderung).
|
||||
fn political_name_to_rank(name: &str) -> i32 {
|
||||
let n = name.to_lowercase();
|
||||
if n.contains("reich")
|
||||
@@ -282,72 +247,36 @@ fn house_points_from_position(position: i32) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Spec §4.6 Schwellen für die Zielstufe (vor Mindestanforderungen).
|
||||
fn raw_target_from_score(score: f64) -> i32 {
|
||||
if score >= 4.0 {
|
||||
if score >= 3.8 {
|
||||
5
|
||||
} else if score >= 3.0 {
|
||||
} else if score >= 2.8 {
|
||||
4
|
||||
} else if score >= 2.1 {
|
||||
} else if score >= 1.8 {
|
||||
3
|
||||
} else if score >= 1.2 {
|
||||
} else if score >= 0.9 {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
fn status_one(
|
||||
office_points: i32,
|
||||
nobility_points: i32,
|
||||
reputation_points: i32,
|
||||
house_points: i32,
|
||||
) -> bool {
|
||||
office_points >= 1
|
||||
|| nobility_points >= 1
|
||||
|| reputation_points >= 2
|
||||
|| house_points >= 1
|
||||
}
|
||||
|
||||
fn status_count_cert4(
|
||||
office_points: i32,
|
||||
nobility_points: i32,
|
||||
reputation_points: i32,
|
||||
house_points: i32,
|
||||
) -> i32 {
|
||||
fn cert5_two_of_social(office_points: i32, nobility_points: i32, house_points: i32) -> bool {
|
||||
let mut c = 0;
|
||||
if office_points >= 1 {
|
||||
if office_points >= 2 {
|
||||
c += 1;
|
||||
}
|
||||
if nobility_points >= 1 {
|
||||
c += 1;
|
||||
}
|
||||
if reputation_points >= 2 {
|
||||
c += 1;
|
||||
}
|
||||
if house_points >= 1 {
|
||||
c += 1;
|
||||
}
|
||||
c
|
||||
}
|
||||
|
||||
fn cert5_extra_two(
|
||||
office_points: i32,
|
||||
nobility_points: i32,
|
||||
house_points: i32,
|
||||
) -> i32 {
|
||||
let mut c = 0;
|
||||
if office_points >= 2 {
|
||||
c += 1;
|
||||
}
|
||||
if nobility_points >= 2 {
|
||||
c += 1;
|
||||
}
|
||||
if house_points >= 2 {
|
||||
c += 1;
|
||||
}
|
||||
c
|
||||
c >= 2
|
||||
}
|
||||
|
||||
/// Mindestanforderungen je Stufe (Spec §4.5).
|
||||
fn meets_min_for_level(
|
||||
level: i32,
|
||||
avg_knowledge: f64,
|
||||
@@ -359,38 +288,27 @@ fn meets_min_for_level(
|
||||
) -> bool {
|
||||
match level {
|
||||
1 => true,
|
||||
2 => avg_knowledge >= 25.0 && completed >= 5,
|
||||
3 => {
|
||||
avg_knowledge >= 40.0
|
||||
&& completed >= 20
|
||||
&& status_one(
|
||||
office_points,
|
||||
nobility_points,
|
||||
reputation_points,
|
||||
house_points,
|
||||
)
|
||||
}
|
||||
2 => avg_knowledge >= 15.0 && completed >= 4,
|
||||
3 => avg_knowledge >= 28.0 && completed >= 15,
|
||||
4 => {
|
||||
avg_knowledge >= 55.0
|
||||
&& completed >= 60
|
||||
&& status_count_cert4(
|
||||
office_points,
|
||||
nobility_points,
|
||||
reputation_points,
|
||||
house_points,
|
||||
) >= 2
|
||||
avg_knowledge >= 45.0
|
||||
&& completed >= 45
|
||||
&& (office_points >= 1
|
||||
|| nobility_points >= 1
|
||||
|| reputation_points >= 2
|
||||
|| house_points >= 2)
|
||||
}
|
||||
5 => {
|
||||
avg_knowledge >= 70.0
|
||||
&& completed >= 150
|
||||
&& reputation_points >= 3
|
||||
&& cert5_extra_two(office_points, nobility_points, house_points) >= 2
|
||||
avg_knowledge >= 60.0
|
||||
&& completed >= 110
|
||||
&& reputation_points >= 2
|
||||
&& cert5_two_of_social(office_points, nobility_points, house_points)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Höchste Stufe ≤ `raw_target`, die alle Mindestanforderungen erfüllt.
|
||||
/// Höchste Stufe ≤ `raw_target`, die alle Mindestanforderungen erfüllt (Spec §4.6).
|
||||
fn effective_certificate_target(
|
||||
raw_target: i32,
|
||||
avg_knowledge: f64,
|
||||
@@ -416,23 +334,3 @@ fn effective_certificate_target(
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
impl Worker for FalukantCertificateWorker {
|
||||
fn start_worker_thread(&mut self) {
|
||||
let pool = self.base.pool.clone();
|
||||
let broker = self.base.broker.clone();
|
||||
|
||||
self.base
|
||||
.start_worker_with_loop(move |state: Arc<WorkerState>| {
|
||||
FalukantCertificateWorker::run_loop(pool.clone(), broker.clone(), state);
|
||||
});
|
||||
}
|
||||
|
||||
fn stop_worker_thread(&mut self) {
|
||||
self.base.stop_worker();
|
||||
}
|
||||
|
||||
fn enable_watchdog(&mut self) {
|
||||
self.base.start_watchdog();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +158,9 @@ impl FalukantFamilyWorker {
|
||||
if let Err(e) = super::falukant_debtors::run_daily(&self.base, &self.base.broker) {
|
||||
eprintln!("[FalukantFamilyWorker] falukant_debtors::run_daily: {e}");
|
||||
}
|
||||
if let Err(e) = super::falukant_certificate::run_daily(&self.base, &self.base.broker) {
|
||||
eprintln!("[FalukantFamilyWorker] falukant_certificate::run_daily: {e}");
|
||||
}
|
||||
|
||||
let mut conn = self
|
||||
.base
|
||||
|
||||
@@ -33,5 +33,4 @@ pub use transport::TransportWorker;
|
||||
pub use weather::WeatherWorker;
|
||||
pub use events::EventsWorker;
|
||||
pub use falukant_family::FalukantFamilyWorker;
|
||||
pub use falukant_certificate::FalukantCertificateWorker;
|
||||
|
||||
|
||||
@@ -3135,6 +3135,7 @@ pub const QUERY_INSERT_CHILD_RELATION_LOVER: &str = r#"
|
||||
pub const QUERY_GET_PRODUCTION_CERTIFICATE_INPUT_ROWS: &str = r#"
|
||||
SELECT DISTINCT ON (fu.id)
|
||||
fu.id AS falukant_user_id,
|
||||
COALESCE(fu.user_id, fu.id)::int AS app_user_id,
|
||||
COALESCE(fu.certificate, 1)::int AS certificate,
|
||||
COALESCE(fu.money, 0)::float8 AS money,
|
||||
c.id AS character_id,
|
||||
@@ -3179,7 +3180,8 @@ pub const QUERY_GET_PRODUCTION_CERTIFICATE_INPUT_ROWS: &str = r#"
|
||||
|
||||
pub const QUERY_UPDATE_FALUKANT_USER_CERTIFICATE: &str = r#"
|
||||
UPDATE falukant_data.falukant_user
|
||||
SET certificate = $1::int
|
||||
SET certificate = $1::int,
|
||||
updated_at = NOW()
|
||||
WHERE id = $2::int;
|
||||
"#;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user