From ee501b0b1d5071b3f230f93f88d5dfd0a052f309 Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Sat, 3 Jan 2026 02:27:20 +0100 Subject: [PATCH] dsff --- config/community.php | 71 ++++++++++++++++++------------- schema.sql | 20 +++++++++ src/App/Community.php | 98 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 149 insertions(+), 40 deletions(-) diff --git a/config/community.php b/config/community.php index 981c7c1..f3b73e5 100644 --- a/config/community.php +++ b/config/community.php @@ -1,35 +1,48 @@ [ - 'event_participation' => 0.3, // war 0.1 - 'event_create' => 1.5, // war 1.0 - 'forum_question' => 0.4, // leicht runter - 'forum_answer' => 1.0, // bleibt Leitwert - 'invite' => 0.3, // etwas entschärft -], - 'caps' => [ - 'event_participation_per_day' => 1.0, - 'forum_answer_per_day' => 5.0, - 'invite_total' => 5.0, + 'actions' => [ + 'event' => [ + 'create' => [ + 'points' => 1.5, + 'caps' => ['daily' => null, 'total' => null], + 'bonuses' => ['first' => 2.0], + ], + 'participation' => [ + 'points' => 0.3, + 'caps' => ['daily' => 1.0, 'total' => null], + 'bonuses' => ['first' => 1.0], + ], + ], + 'forum' => [ + 'question' => [ + 'points' => 0.4, + 'caps' => ['daily' => null, 'total' => null], + 'bonuses' => [], + ], + 'answer' => [ + 'points' => 1.0, + 'caps' => ['daily' => 5.0, 'total' => null], + 'bonuses' => ['first_helpful_5' => 2.0], + ], + ], + 'invite' => [ + 'points' => 0.3, + 'caps' => ['daily' => null, 'total' => 5.0], + 'bonuses' => [], + ], ], - 'bonuses' => [ - 'first_event_join' => 1.0, - 'first_event_create' => 2.0, - 'first_helpful_5' => 2.0, -], // Levels with thresholds and optional icons -'levels' => [ - ['min' => 0, 'label' => 'Neuer Vater'], - ['min' => 5, 'label' => 'Ankommender Vater'], - ['min' => 25, 'label' => 'Aktiver Vater'], - ['min' => 75, 'label' => 'Engagierter Vater'], - ['min' => 150, 'label' => 'Unterstützender Vater'], - ['min' => 300, 'label' => 'Erfahrener Vater'], - ['min' => 500, 'label' => 'Mentor-Vater'], - ['min' => 750, 'label' => 'Community-Vater'], - ['min' => 1000, 'label' => 'Säule der Väter-Community'], - ['min' => 1500, 'label' => 'Vater der Gemeinschaft'], -], + 'levels' => [ + ['min' => 0, 'label' => 'Neuer Vater'], + ['min' => 5, 'label' => 'Ankommender Vater'], + ['min' => 25, 'label' => 'Aktiver Vater'], + ['min' => 75, 'label' => 'Engagierter Vater'], + ['min' => 150, 'label' => 'Unterstützender Vater'], + ['min' => 300, 'label' => 'Erfahrener Vater'], + ['min' => 500, 'label' => 'Mentor-Vater'], + ['min' => 750, 'label' => 'Community-Vater'], + ['min' => 1000, 'label' => 'Säule der Väter-Community'], + ['min' => 1500, 'label' => 'Vater der Gemeinschaft'], + ], ]; diff --git a/schema.sql b/schema.sql index 8ed8022..a507f41 100644 --- a/schema.sql +++ b/schema.sql @@ -122,6 +122,26 @@ CREATE TABLE forum_posts ( INDEX idx_fp_thread (thread_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +-- Punkte-Tracking (persistent, config-Änderungen wirken nur auf neue Einträge) +CREATE TABLE user_points ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + user_id BIGINT UNSIGNED NOT NULL, + action VARCHAR(64) NOT NULL, + amount DECIMAL(10,2) NOT NULL, + meta JSON NULL, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_up_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, + INDEX idx_up_user_action (user_id, action), + INDEX idx_up_created (created_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE user_points_totals ( + user_id BIGINT UNSIGNED PRIMARY KEY, + total DECIMAL(12,2) NOT NULL DEFAULT 0, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + CONSTRAINT fk_upt_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + -- Session-Handling (neutral, keine sensiblen Inhalte) CREATE TABLE sessions ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, diff --git a/src/App/Community.php b/src/App/Community.php index 51c1a17..38661fe 100644 --- a/src/App/Community.php +++ b/src/App/Community.php @@ -88,18 +88,94 @@ final class Community public function computePoints(int $userId): float { - $pointsCfg = $this->config['points'] ?? []; - $eventCreated = (int)$this->pdo->query("SELECT COUNT(*) FROM events WHERE created_by = {$userId}")->fetchColumn(); - $eventParticipants = (float)$this->pdo->query("SELECT COUNT(*) FROM event_participants WHERE user_id = {$userId}")->fetchColumn(); - $threadCount = (int)$this->pdo->query("SELECT COUNT(*) FROM forum_threads WHERE user_id = {$userId}")->fetchColumn(); - $answerCount = (int)$this->pdo->query("SELECT COUNT(*) FROM forum_posts WHERE user_id = {$userId}")->fetchColumn(); - $invites = 0; + // Primär: aggregierte Werte aus user_points_totals, Fallback: Summe aus user_points + $stmt = $this->pdo->prepare('SELECT total FROM user_points_totals WHERE user_id = :uid'); + $stmt->execute([':uid' => $userId]); + $total = $stmt->fetchColumn(); + if ($total !== false && $total !== null) { + return (float)$total; + } - return $threadCount * ($pointsCfg['forum_question'] ?? 0.5) - + $answerCount * ($pointsCfg['forum_answer'] ?? 1.0) - + $eventCreated * ($pointsCfg['event_create'] ?? 1.0) - + $eventParticipants * ($pointsCfg['event_participation'] ?? 0.1) - + $invites * ($pointsCfg['invite'] ?? 0.5); + $stmt = $this->pdo->prepare('SELECT COALESCE(SUM(amount),0) FROM user_points WHERE user_id = :uid'); + $stmt->execute([':uid' => $userId]); + return (float)$stmt->fetchColumn(); + } + + /** + * Vergibt Punkte persistent und berücksichtigt Caps/Bonis gemäß config actions. + */ + public function addPoints(int $userId, string $group, string $key, array $meta = []): float + { + $actions = $this->config['actions'][$group][$key] ?? null; + if (!$actions || empty($actions['points'])) { + return 0.0; + } + $basePoints = (float)$actions['points']; + + // Boni (einfacher first-Check) + $bonusPoints = 0.0; + if (!empty($actions['bonuses'])) { + if (isset($actions['bonuses']['first'])) { + $bonusPoints += (float)$actions['bonuses']['first']; + } + if (isset($actions['bonuses']['first_helpful_5']) && isset($meta['helpful_count']) && (int)$meta['helpful_count'] >= 5) { + $bonusPoints += (float)$actions['bonuses']['first_helpful_5']; + } + } + + $amount = $basePoints + $bonusPoints; + if ($amount <= 0) { + return 0.0; + } + + $caps = $actions['caps'] ?? []; + $capDaily = $caps['daily'] ?? null; + $capTotal = $caps['total'] ?? null; + + $todayStart = (new \DateTimeImmutable('today'))->format('Y-m-d 00:00:00'); + $todayEnd = (new \DateTimeImmutable('today'))->format('Y-m-d 23:59:59'); + + $actionKey = $group . '.' . $key; + + if ($capDaily !== null) { + $stmt = $this->pdo->prepare("SELECT COALESCE(SUM(amount),0) FROM user_points WHERE user_id = :uid AND action = :action AND created_at BETWEEN :s AND :e"); + $stmt->execute([ + ':uid' => $userId, + ':action' => $actionKey, + ':s' => $todayStart, + ':e' => $todayEnd, + ]); + $usedToday = (float)$stmt->fetchColumn(); + $remaining = max(0.0, (float)$capDaily - $usedToday); + if ($remaining <= 0) { + return 0.0; + } + $amount = min($amount, $remaining); + } + + if ($capTotal !== null) { + $stmt = $this->pdo->prepare("SELECT COALESCE(SUM(amount),0) FROM user_points WHERE user_id = :uid AND action = :action"); + $stmt->execute([':uid' => $userId, ':action' => $actionKey]); + $usedTotal = (float)$stmt->fetchColumn(); + $remaining = max(0.0, (float)$capTotal - $usedTotal); + if ($remaining <= 0) { + return 0.0; + } + $amount = min($amount, $remaining); + } + + $stmt = $this->pdo->prepare('INSERT INTO user_points (user_id, action, amount, meta) VALUES (:uid, :action, :amount, :meta)'); + $stmt->execute([ + ':uid' => $userId, + ':action' => $actionKey, + ':amount' => $amount, + ':meta' => $meta ? json_encode($meta) : null, + ]); + + $stmt = $this->pdo->prepare('INSERT INTO user_points_totals (user_id, total) VALUES (:uid, :amt) ON DUPLICATE KEY UPDATE total = total + VALUES(total)'); + $stmt->execute([':uid' => $userId, ':amt' => $amount]); + + return $amount; } public function membershipLevel(float $points): array