Enhance church office management in Falukant daemon: Introduced falukantUpdateChurch event for church applications and appointments, updated SQL queries for church office processing, and refactored the PoliticsWorker to streamline daily tasks related to church offices. Improved handling of church application scoring and interim appointments, enhancing overall church dynamics and character interactions.

This commit is contained in:
Torsten Schulz (local)
2026-03-23 11:02:19 +01:00
parent 708ffc3eda
commit 9d7f61a329
5 changed files with 796 additions and 222 deletions

View File

@@ -1772,11 +1772,12 @@ pub const QUERY_SET_LEARNING_DONE: &str = r#"
WHERE id = $1;
"#;
// Church Office Queries
// Church Office Queries (siehe docs/FALUKANT_CHURCH_DAEMON.md)
pub const QUERY_FIND_AVAILABLE_CHURCH_OFFICES: &str = r#"
SELECT
cot.id AS office_type_id,
cot.name AS office_type_name,
cot.hierarchy_level,
cot.seats_per_region,
cot.region_type,
r.id AS region_id,
@@ -1788,7 +1789,7 @@ pub const QUERY_FIND_AVAILABLE_CHURCH_OFFICES: &str = r#"
ON cot.id = co.office_type_id
AND co.region_id = r.id
WHERE tr.label_tr = cot.region_type
GROUP BY cot.id, cot.name, cot.seats_per_region, cot.region_type, r.id
GROUP BY cot.id, cot.name, cot.hierarchy_level, cot.seats_per_region, cot.region_type, r.id
HAVING COUNT(co.id) < cot.seats_per_region
ORDER BY cot.hierarchy_level ASC, r.id;
"#;
@@ -1821,6 +1822,8 @@ pub const QUERY_GET_CHURCH_OFFICE_REQUIREMENTS: &str = r#"
WHERE office_type_id = $1;
"#;
/// Optional für Backend/API; der Daemon nutzt `QUERY_GET_PENDING_CHURCH_APPLICATIONS_FOR_SCORING`.
#[allow(dead_code)]
pub const QUERY_GET_PENDING_CHURCH_APPLICATIONS: &str = r#"
SELECT
ca.id AS application_id,
@@ -1837,8 +1840,15 @@ pub const QUERY_GET_PENDING_CHURCH_APPLICATIONS: &str = r#"
ORDER BY cot.hierarchy_level ASC, ca.created_at ASC;
"#;
/// Voraussetzung: Migration `007_falukant_character_church_career.sql` (highest_church_hierarchy_ever).
pub const QUERY_CHECK_CHARACTER_ELIGIBILITY: &str = r#"
WITH character_info AS (
WITH prereq AS (
SELECT $2::int AS prereq_type_id,
CASE WHEN $2::int IS NULL THEN NULL ELSE (
SELECT hierarchy_level FROM falukant_type.church_office_type WHERE id = $2::int
) END AS prereq_hl
),
char_h AS (
SELECT
c.id AS character_id,
c.title_of_nobility,
@@ -1847,34 +1857,43 @@ pub const QUERY_CHECK_CHARACTER_ELIGIBILITY: &str = r#"
SELECT 1
FROM falukant_data.church_office co2
WHERE co2.character_id = c.id
) AS has_office
) AS has_office,
COALESCE(c.highest_church_hierarchy_ever, 0)::int AS highest_ever,
COALESCE((
SELECT MAX(cot2.hierarchy_level)
FROM falukant_data.church_office co2
JOIN falukant_type.church_office_type cot2 ON co2.office_type_id = cot2.id
WHERE co2.character_id = c.id
), 0) AS current_max_hl
FROM falukant_data.character c
LEFT JOIN falukant_type.title t ON c.title_of_nobility = t.id
WHERE c.id = $1
),
prerequisite_check AS (
SELECT
CASE
WHEN $2::int IS NULL THEN TRUE
ELSE EXISTS(
)
SELECT
ch.character_id,
ch.title_level,
ch.has_office,
CASE
WHEN pr.prereq_type_id IS NULL THEN TRUE
ELSE (
EXISTS(
SELECT 1
FROM falukant_data.church_office co
WHERE co.character_id = $1
AND co.office_type_id = $2::int
AND co.office_type_id = pr.prereq_type_id
)
END AS has_prerequisite
)
SELECT
ci.character_id,
ci.title_level,
ci.has_office,
pc.has_prerequisite,
OR (
pr.prereq_hl IS NOT NULL
AND GREATEST(ch.highest_ever, ch.current_max_hl) >= pr.prereq_hl
)
)
END AS has_prerequisite,
CASE
WHEN $3::int IS NULL THEN TRUE
ELSE COALESCE(ci.title_level, 0) >= $3::int
ELSE COALESCE(ch.title_level, 0) >= $3::int
END AS meets_title_requirement
FROM character_info ci
CROSS JOIN prerequisite_check pc;
FROM char_h ch
CROSS JOIN prereq pr;
"#;
pub const QUERY_APPROVE_CHURCH_APPLICATION: &str = r#"
@@ -1910,6 +1929,36 @@ pub const QUERY_APPROVE_CHURCH_APPLICATION: &str = r#"
AND co.character_id = updated_application.character_id
)
RETURNING id, office_type_id, character_id, region_id
),
upd_highest AS (
UPDATE falukant_data.character c
SET highest_church_hierarchy_ever = GREATEST(
COALESCE(c.highest_church_hierarchy_ever, 0),
io.hl
)::smallint
FROM (
SELECT io2.character_id, cot.hierarchy_level AS hl
FROM inserted_office io2
JOIN falukant_type.church_office_type cot ON cot.id = io2.office_type_id
) io
WHERE c.id = io.character_id
RETURNING c.id
),
remove_lower_ranked AS (
DELETE FROM falukant_data.church_office co
WHERE co.id IN (
SELECT co3.id
FROM falukant_data.church_office co3
JOIN falukant_type.church_office_type cot ON co3.office_type_id = cot.id
WHERE co3.character_id IN (SELECT character_id FROM inserted_office)
AND EXISTS (
SELECT 1
FROM falukant_data.church_office co2
JOIN falukant_type.church_office_type cot2 ON co2.office_type_id = cot2.id
WHERE co2.character_id = co3.character_id
AND cot2.hierarchy_level > cot.hierarchy_level
)
)
)
SELECT
id AS office_id,
@@ -1929,6 +1978,7 @@ pub const QUERY_REJECT_CHURCH_APPLICATION: &str = r#"
RETURNING id;
"#;
/// Nur NPC-Vorgesetzte: Spieler-Entscheidungen nicht per Timeout überschreiben.
pub const QUERY_GET_OLD_PENDING_CHURCH_APPLICATIONS: &str = r#"
SELECT
ca.id AS application_id,
@@ -1937,8 +1987,10 @@ pub const QUERY_GET_OLD_PENDING_CHURCH_APPLICATIONS: &str = r#"
ca.region_id,
ca.supervisor_id
FROM falukant_data.church_application ca
JOIN falukant_data.character sup ON sup.id = ca.supervisor_id
WHERE ca.status = 'pending'
AND ca.created_at <= NOW() - INTERVAL '36 hours'
AND sup.user_id IS NULL
ORDER BY ca.created_at ASC;
"#;
@@ -1976,6 +2028,36 @@ pub const QUERY_AUTO_APPROVE_CHURCH_APPLICATION: &str = r#"
AND co.character_id = updated_application.character_id
)
RETURNING id, office_type_id, character_id, region_id
),
upd_highest AS (
UPDATE falukant_data.character c
SET highest_church_hierarchy_ever = GREATEST(
COALESCE(c.highest_church_hierarchy_ever, 0),
io.hl
)::smallint
FROM (
SELECT io2.character_id, cot.hierarchy_level AS hl
FROM inserted_office io2
JOIN falukant_type.church_office_type cot ON cot.id = io2.office_type_id
) io
WHERE c.id = io.character_id
RETURNING c.id
),
remove_lower_ranked AS (
DELETE FROM falukant_data.church_office co
WHERE co.id IN (
SELECT co3.id
FROM falukant_data.church_office co3
JOIN falukant_type.church_office_type cot ON co3.office_type_id = cot.id
WHERE co3.character_id IN (SELECT character_id FROM inserted_office)
AND EXISTS (
SELECT 1
FROM falukant_data.church_office co2
JOIN falukant_type.church_office_type cot2 ON co2.office_type_id = cot2.id
WHERE co2.character_id = co3.character_id
AND cot2.hierarchy_level > cot.hierarchy_level
)
)
)
SELECT
id AS office_id,
@@ -2007,6 +2089,7 @@ pub const QUERY_CREATE_CHURCH_APPLICATION_JOB: &str = r#"
RETURNING id;
"#;
/// Nur NPCs: Spielerbewerbungen laufen über die UI.
pub const QUERY_GET_CHARACTERS_FOR_CHURCH_OFFICE: &str = r#"
SELECT DISTINCT
c.id AS character_id,
@@ -2018,6 +2101,7 @@ pub const QUERY_GET_CHARACTERS_FOR_CHURCH_OFFICE: &str = r#"
LEFT JOIN falukant_type.title t ON c.title_of_nobility = t.id
WHERE c.region_id = $1
AND c.health > 0
AND c.user_id IS NULL
AND NOT EXISTS(
SELECT 1
FROM falukant_data.church_office co
@@ -2027,6 +2111,124 @@ pub const QUERY_GET_CHARACTERS_FOR_CHURCH_OFFICE: &str = r#"
LIMIT $2;
"#;
pub const QUERY_COUNT_PENDING_CHURCH_APPS_BY_OFFICE_REGION: &str = r#"
SELECT COUNT(*)::int AS cnt
FROM falukant_data.church_application ca
WHERE ca.office_type_id = $1::int
AND ca.region_id = $2::int
AND ca.status = 'pending';
"#;
pub const QUERY_GET_CHURCH_OFFICE_OCCUPIED_COUNT: &str = r#"
SELECT COUNT(*)::int AS cnt
FROM falukant_data.church_office co
WHERE co.office_type_id = $1::int
AND co.region_id = $2::int;
"#;
pub const QUERY_IS_CHARACTER_NPC: &str = r#"
SELECT (c.user_id IS NULL) AS is_npc
FROM falukant_data.character c
WHERE c.id = $1::int;
"#;
pub const QUERY_GET_PENDING_CHURCH_APPLICATIONS_FOR_SCORING: &str = r#"
SELECT
ca.id AS application_id,
ca.office_type_id,
ca.character_id AS applicant_character_id,
ca.region_id,
ca.created_at,
cot.hierarchy_level AS office_hierarchy_level,
cot.seats_per_region,
COALESCE(sc.reputation, 50)::float8 AS supervisor_reputation,
COALESCE(ac.reputation, 50)::float8 AS applicant_reputation,
COALESCE(ac.highest_church_hierarchy_ever, 0)::int AS applicant_highest_ever,
COALESCE(t.level, 0)::int AS applicant_title_level,
COALESCE((
SELECT MAX(cot2.hierarchy_level)
FROM falukant_data.church_office co2
JOIN falukant_type.church_office_type cot2 ON co2.office_type_id = cot2.id
WHERE co2.character_id = ac.id
), 0)::int AS applicant_current_max_hierarchy,
(CURRENT_DATE - ac.birthdate::date)::int AS applicant_age_days
FROM falukant_data.church_application ca
JOIN falukant_data.character ac ON ac.id = ca.character_id
JOIN falukant_data.character sc ON sc.id = ca.supervisor_id
JOIN falukant_type.church_office_type cot ON cot.id = ca.office_type_id
LEFT JOIN falukant_type.title t ON t.id = ac.title_of_nobility
WHERE ca.status = 'pending'
AND ca.supervisor_id = $1::int
ORDER BY ca.created_at ASC;
"#;
pub const QUERY_INTERIM_APPOINT_CHURCH_OFFICE: &str = r#"
INSERT INTO falukant_data.church_office
(office_type_id, character_id, region_id, supervisor_id, created_at, updated_at)
SELECT $1::int, $2::int, $3::int, NULL, NOW(), NOW()
WHERE (
SELECT COUNT(*)::int
FROM falukant_data.church_office co
WHERE co.office_type_id = $1::int
AND co.region_id = $3::int
) < $4::int
AND NOT EXISTS (
SELECT 1 FROM falukant_data.church_office co
WHERE co.character_id = $2::int
AND co.office_type_id = $1::int
AND co.region_id = $3::int
)
RETURNING id, office_type_id, character_id, region_id;
"#;
pub const QUERY_UPDATE_CHARACTER_HIGHEST_CHURCH_FROM_OFFICE_TYPE: &str = r#"
UPDATE falukant_data.character c
SET highest_church_hierarchy_ever = GREATEST(
COALESCE(c.highest_church_hierarchy_ever, 0),
(SELECT cot.hierarchy_level FROM falukant_type.church_office_type cot WHERE cot.id = $2::int)
)::smallint
WHERE c.id = $1::int
RETURNING c.id;
"#;
pub const QUERY_FIND_INTERIM_CHURCH_NPC_CANDIDATE: &str = r#"
SELECT c.id AS character_id
FROM falukant_data.character c
WHERE c.region_id = $1::int
AND c.user_id IS NULL
AND c.health > 0
AND NOT EXISTS (
SELECT 1
FROM falukant_data.church_office co
JOIN falukant_type.church_office_type cot ON cot.id = co.office_type_id
WHERE co.character_id = c.id
AND cot.hierarchy_level >= (
SELECT hierarchy_level FROM falukant_type.church_office_type WHERE id = $2::int
)
)
ORDER BY COALESCE(c.reputation, 50) DESC,
COALESCE(c.highest_church_hierarchy_ever, 0) DESC
LIMIT 1;
"#;
pub const QUERY_REMOVE_LOWER_CHURCH_OFFICES_FOR_CHARACTER: &str = r#"
DELETE FROM falukant_data.church_office co
WHERE co.character_id = $1::int
AND co.id IN (
SELECT co3.id
FROM falukant_data.church_office co3
JOIN falukant_type.church_office_type cot ON co3.office_type_id = cot.id
WHERE co3.character_id = $1::int
AND EXISTS (
SELECT 1
FROM falukant_data.church_office co2
JOIN falukant_type.church_office_type cot2 ON co2.office_type_id = cot2.id
WHERE co2.character_id = co3.character_id
AND cot2.hierarchy_level > cot.hierarchy_level
)
);
"#;
// --- Falukant: Dienerschaft (siehe migrations/004_falukant_servants_daemon.sql) ---
pub const QUERY_SERVANTS_SCHEMA_READY: &str = r#"