我将为您设计一个PHP勋章自动发放系统,包含完整的逻辑和代码实现。

勋章自动发放系统设计
数据库设计
-- 勋章表 CREATE TABLE `badges` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100) NOT NULL COMMENT '勋章名称', `code` varchar(50) NOT NULL COMMENT '勋章唯一标识', `description` varchar(255) DEFAULT NULL COMMENT '勋章描述', `icon` varchar(255) DEFAULT NULL COMMENT '勋章图标路径', `conditions` text NOT NULL COMMENT '发放条件(JOSN格式)', `status` tinyint(1) DEFAULT '1' COMMENT '状态:1启用 0禁用', `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `unique_code` (`code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 用户勋章表 CREATE TABLE `user_badges` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL COMMENT '用户ID', `badge_id` int(11) NOT NULL COMMENT '勋章ID', `earned_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '获得时间', `source` varchar(50) DEFAULT NULL COMMENT '获得来源', PRIMARY KEY (`id`), UNIQUE KEY `unique_user_badge` (`user_id`, `badge_id`), KEY `idx_user_id` (`user_id`), KEY `idx_badge_id` (`badge_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 用户操作日志表(用于统计) CREATE TABLE `user_actions` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL, `action_type` varchar(50) NOT NULL COMMENT '操作类型', `action_data` text DEFAULT NULL COMMENT '操作数据(JSON)', `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_user_action` (`user_id`, `action_type`), KEY `idx_created_at` (`created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
核心类实现
<?php
class BadgeManager {
private $db;
private $cache;
public function __construct($db, $cache = null) {
$this->db = $db;
$this->cache = $cache ?: new RedisCache(); // 或者使用文件缓存
}
/**
* 检查并发放勋章
*/
public function checkAndAward($userId, $actionType = null) {
// 获取所有启用的勋章
$badges = $this->getAllEnabledBadges();
$awardedBadges = [];
foreach ($badges as $badge) {
// 跳过已获得的勋章
if ($this->hasBadge($userId, $badge['id'])) {
continue;
}
// 解析条件并检查
$conditions = json_decode($badge['conditions'], true);
if ($this->checkConditions($userId, $badge, $conditions, $actionType)) {
$this->awardBadge($userId, $badge['id'], $actionType);
$awardedBadges[] = $badge;
}
}
return $awardedBadges;
}
/**
* 获取所有启用的勋章
*/
private function getAllEnabledBadges() {
$cacheKey = 'all_enabled_badges';
$badges = $this->cache->get($cacheKey);
if (!$badges) {
$sql = "SELECT * FROM badges WHERE status = 1";
$result = $this->db->query($sql);
$badges = $result->fetch_all(MYSQLI_ASSOC);
$this->cache->set($cacheKey, $badges, 3600); // 缓存1小时
}
return $badges;
}
/**
* 检查用户是否已获得某勋章
*/
private function hasBadge($userId, $badgeId) {
$sql = "SELECT COUNT(*) as count FROM user_badges
WHERE user_id = ? AND badge_id = ?";
$stmt = $this->db->prepare($sql);
$stmt->bind_param("ii", $userId, $badgeId);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
return $row['count'] > 0;
}
/**
* 发放勋章
*/
private function awardBadge($userId, $badgeId, $source) {
$sql = "INSERT INTO user_badges (user_id, badge_id, source)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE id = id";
$stmt = $this->db->prepare($sql);
$stmt->bind_param("iis", $userId, $badgeId, $source);
$stmt->execute();
// 清除缓存
$this->clearUserCache($userId);
// 触发通知
$this->notifyUser($userId, $badgeId);
}
/**
* 检查发放条件
*/
private function checkConditions($userId, $badge, $conditions, $actionType) {
foreach ($conditions as $condition) {
$result = $this->checkSingleCondition($userId, $badge, $condition);
if (!$result) {
return false;
}
}
return true;
}
/**
* 检查单个条件
*/
private function checkSingleCondition($userId, $badge, $condition) {
$type = $condition['type'];
$value = $condition['value'];
switch ($type) {
case 'action_count':
return $this->checkActionCount($userId, $condition['action_type'], $value);
case 'continuous_days':
return $this->checkContinuousDays($userId, $value);
case 'total_days':
return $this->checkTotalDays($userId, $value);
case 'points':
return $this->checkPoints($userId, $value);
case 'level':
return $this->checkUserLevel($userId, $value);
case 'invite_count':
return $this->checkInviteCount($userId, $value);
case 'special_action':
return $this->checkSpecialAction($userId, $condition['action_type']);
default:
return false;
}
}
/**
* 检查操作次数
*/
private function checkActionCount($userId, $actionType, $requiredCount) {
$cacheKey = "user_action_count_{$userId}_{$actionType}";
$count = $this->cache->get($cacheKey);
if ($count === false) {
$sql = "SELECT COUNT(*) as count FROM user_actions
WHERE user_id = ? AND action_type = ?";
$stmt = $this->db->prepare($sql);
$stmt->bind_param("is", $userId, $actionType);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
$count = $row['count'];
$this->cache->set($cacheKey, $count, 600);
}
return $count >= $requiredCount;
}
/**
* 检查连续签到天数
*/
private function checkContinuousDays($userId, $requiredDays) {
$sql = "SELECT created_at FROM user_actions
WHERE user_id = ? AND action_type = 'daily_checkin'
ORDER BY created_at DESC LIMIT ?";
$stmt = $this->db->prepare($sql);
$stmt->bind_param("ii", $userId, $requiredDays);
$stmt->execute();
$result = $stmt->get_result();
$dates = [];
while ($row = $result->fetch_assoc()) {
$dates[] = date('Y-m-d', strtotime($row['created_at']));
}
if (count($dates) < $requiredDays) {
return false;
}
// 检查是否连续
$start = strtotime($dates[0]);
$expectedDate = $start;
foreach ($dates as $date) {
if (strtotime($date) != $expectedDate) {
return false;
}
$expectedDate = strtotime('+1 day', $expectedDate);
}
return true;
}
/**
* 检查总活跃天数
*/
private function checkTotalDays($userId, $requiredDays) {
$sql = "SELECT COUNT(DISTINCT DATE(created_at)) as days
FROM user_actions WHERE user_id = ?";
$stmt = $this->db->prepare($sql);
$stmt->bind_param("i", $userId);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
return $row['days'] >= $requiredDays;
}
/**
* 检查积分
*/
private function checkPoints($userId, $requiredPoints) {
$sql = "SELECT points FROM user_points WHERE user_id = ?";
$stmt = $this->db->prepare($sql);
$stmt->bind_param("i", $userId);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
return $row && $row['points'] >= $requiredPoints;
}
/**
* 检查用户等级
*/
private function checkUserLevel($userId, $requiredLevel) {
$sql = "SELECT level FROM user_levels WHERE user_id = ?";
$stmt = $this->db->prepare($sql);
$stmt->bind_param("i", $userId);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
return $row && $row['level'] >= $requiredLevel;
}
/**
* 清除用户缓存
*/
private function clearUserCache($userId) {
$this->cache->delete("user_badges_{$userId}");
$this->cache->delete("user_action_count_{$userId}_*");
}
/**
* 通知用户获得勋章
*/
private function notifyUser($userId, $badgeId) {
// 发送通知(邮件、短信、站内消息等)
$badge = $this->getBadgeById($badgeId);
$message = "恭喜你获得了【{$badge['name']}】勋章!";
// 这里可以实现通知逻辑
NotificationHelper::send($userId, $message);
}
}
事件触发器
<?php
class BadgeEventTrigger {
private $badgeManager;
public function __construct($db) {
$this->badgeManager = new BadgeManager($db);
}
/**
* 用户完成某个操作时触发
*/
public function onAction($userId, $actionType, $actionData = null) {
// 记录操作日志
$this->logAction($userId, $actionType, $actionData);
// 检查勋章
$awarded = $this->badgeManager->checkAndAward($userId, $actionType);
// 返回新获得的勋章
return $awarded;
}
/**
* 用户注册时
*/
public function onRegister($userId) {
// 用户注册奖励
$this->logAction($userId, 'register', []);
return $this->badgeManager->checkAndAward($userId, 'register');
}
/**
* 用户每日签到
*/
public function onDailyCheckin($userId) {
$this->logAction($userId, 'daily_checkin', []);
return $this->badgeManager->checkAndAward($userId, 'daily_checkin');
}
/**
* 用户完成任务
*/
public function onTaskComplete($userId, $taskId) {
$this->logAction($userId, 'task_complete', ['task_id' => $taskId]);
return $this->badgeManager->checkAndAward($userId, 'task_complete');
}
/**
* 记录用户操作
*/
private function logAction($userId, $actionType, $actionData) {
$sql = "INSERT INTO user_actions (user_id, action_type, action_data)
VALUES (?, ?, ?)";
$stmt = $this->db->prepare($sql);
$data = json_encode($actionData);
$stmt->bind_param("iss", $userId, $actionType, $data);
$stmt->execute();
}
}
勋章配置示例
// 勋章配置示例
$badgeConfigs = [
[
'name' => '新手勋章',
'code' => 'newbie',
'description' => '注册成为新用户',
'conditions' => [
['type' => 'action_count', 'action_type' => 'register', 'value' => 1]
]
],
[
'name' => '签到达人',
'code' => 'checkin_master',
'description' => '连续签到7天',
'conditions' => [
['type' => 'continuous_days', 'value' => 7]
]
],
[
'name' => '活跃分子',
'code' => 'active_member',
'description' => '累计活跃30天',
'conditions' => [
['type' => 'total_days', 'value' => 30]
]
],
[
'name' => '积分高手',
'code' => 'points_master',
'description' => '积分达到1000分',
'conditions' => [
['type' => 'points', 'value' => 1000]
]
],
[
'name' => '任务达人',
'code' => 'task_master',
'description' => '完成10个任务',
'conditions' => [
['type' => 'action_count', 'action_type' => 'task_complete', 'value' => 10]
]
],
[
'name' => '魅力之星',
'code' => 'popular',
'description' => '成功邀请5位好友',
'conditions' => [
['type' => 'invite_count', 'value' => 5]
]
]
];
使用示例
// 初始化
$db = new mysqli('localhost', 'username', 'password', 'database');
$trigger = new BadgeEventTrigger($db);
// 用户注册时
$userId = 123;
$newBadges = $trigger->onRegister($userId);
// 用户完成操作时
$newBadges = $trigger->onAction($userId, 'post_create', ['content' => '...']);
// 每日签到
$newBadges = $trigger->onDailyCheckin($userId);
// 完成任务
$newBadges = $trigger->onTaskComplete($userId, 456);
// 手动检查和发放(定时任务)
$badgeManager = new BadgeManager($db);
$allUsers = $badgeManager->getAllActiveUsers();
foreach ($allUsers as $user) {
$badgeManager->checkAndAward($user['id']);
}
扩展功能
// 添加定时检查任务
$schedule = new Schedule([
'daily' => function() {
// 每天凌晨执行
$badgeManager = new BadgeManager($db);
// 检查特殊勋章
$badgeManager->checkSpecialBadge();
},
'hourly' => function() {
// 每小时执行
$badgeManager->checkTimeBasedBadges();
}
]);
// 添加通知功能
class BadgeNotification {
public static function send($userId, $badgeName) {
// 站内信
Message::create($userId, "获得新勋章:$badgeName");
// 推送通知(可选)
PushNotification::send($userId, "恭喜获得 $badgeName 勋章!");
}
}
这个系统设计包含:
- 灵活的勋章条件配置
- 自动发放和手动检查
- 缓存优化性能
- 用户通知功能
- 可扩展的事件触发机制
您可以根据具体需求调整条件类型和发放逻辑。