我正在考虑设计一个在我的网站上使用的成就系统的最佳方法。数据库结构可以在 最好的方法来告诉3个或更多的连续记录丢失中找到,这个线程实际上是一个扩展,可以从开发人员那里获得想法。
我在这个网站上谈论很多关于徽章/成就系统的问题就是——这些都是空谈,没有代码。实际的代码实现示例在哪里?
我在这里提出一个设计,我希望人们可以贡献,并希望创建一个良好的设计编码可扩展的成就系统。我不是说这是最好的,远非如此,但它是一个可能的起点。
请随意贡献你的想法。
我的系统设计理念
It seems the general consensus is to create an "event based system" -- whenever a known event occurs like a post is created, deleted, etc it calls the event class like so..
$event->trigger('POST_CREATED', array('id' => 8));
然后,事件类发现哪些徽章正在“监听”这个事件,然后它 requires
该文件,并创建该类的一个实例,如下所示:
require '/badges/' . $file;
$badge = new $class;
然后调用默认事件,传递调用 trigger
时接收到的数据;
$badge->default_event($data);
徽章
这就是真正的奇迹发生的地方。每个徽章都有自己的查询/逻辑来决定是否应该授予徽章。每个徽章的格式如下:
class Badge_Name extends Badge
{
const _BADGE_500 = 'POST_500';
const _BADGE_300 = 'POST_300';
const _BADGE_100 = 'POST_100';
function get_user_post_count()
{
$escaped_user_id = mysql_real_escape_string($this->user_id);
$r = mysql_query("SELECT COUNT(*) FROM posts
WHERE userid='$escaped_user_id'");
if ($row = mysql_fetch_row($r))
{
return $row[0];
}
return 0;
}
function default_event($data)
{
$post_count = $this->get_user_post_count();
$this->try_award($post_count);
}
function try_award($post_count)
{
if ($post_count > 500)
{
$this->award(self::_BADGE_500);
}
else if ($post_count > 300)
{
$this->award(self::_BADGE_300);
}
else if ($post_count > 100)
{
$this->award(self::_BADGE_100);
}
}
}
award
函数来自一个扩展类 Badge
,它主要检查用户是否已经获得该徽章,如果没有,将更新徽章 db 表。徽章类还负责检索用户的所有徽章并以数组的形式返回(这样徽章就可以显示在用户配置文件中)
当系统首次在一个已经存在的站点上实现时会发生什么情况?
还有一个“ cron”作业查询,可以添加到每个徽章。之所以出现这种情况,是因为当徽章系统最初实施和初始化时,应该已经获得的徽章尚未颁发,因为这是一个基于事件的系统。所以一个 CRON 作业是根据每个徽章的需要运行的,以奖励任何需要的东西。例如,上面的 CRON 作业看起来是这样的:
class Badge_Name_Cron extends Badge_Name
{
function cron_job()
{
$r = mysql_query('SELECT COUNT(*) as post_count, user_id FROM posts');
while ($obj = mysql_fetch_object($r))
{
$this->user_id = $obj->user_id; //make sure we're operating on the right user
$this->try_award($obj->post_count);
}
}
}
由于上面的 cron 类扩展了主徽章类,所以它可以重用逻辑函数 try_award
我为此创建一个专门的查询的原因是,尽管我们可以“模拟”以前的事件,即通过每个用户的帖子,并触发事件类,如 $event->trigger()
,这将是非常缓慢的,特别是对于许多徽章。因此,我们转而创建一个优化的查询。
什么用户获得奖励? 所有关于奖励 其他用户根据事件
Badge
类 award
功能作用于 user_id
——它们总是会被授予奖励。默认情况下,徽章授予导致事件发生的人,即会话用户 ID (这对于 default_event
函数是正确的,尽管 CRON 作业显然循环遍历所有用户并授予单独的用户)
让我们举个例子,在一个编码挑战网站用户提交他们的编码条目。然后管理员对参赛作品进行判断,完成后将结果发布到挑战页面,供所有人查看。发生这种情况时,将调用 POSTED _ Results 事件。
如果你想为所有发布的条目的用户授予徽章,比如说,如果他们在前5名之内,你应该使用 cron 作业(尽管要记住,这将更新所有用户,而不仅仅是为了挑战的结果发布)
如果希望使用 cron 作业更新更具体的区域,那么让我们看看是否有办法将过滤参数添加到 cron 作业对象中,并让 cron _ job 函数使用它们。例如:
class Badge_Top5 extends Badge
{
const _BADGE_NAME = 'top5';
function try_award($position)
{
if ($position <= 5)
{
$this->award(self::_BADGE_NAME);
}
}
}
class Badge_Top5_Cron extends Badge_Top5
{
function cron_job($challenge_id = 0)
{
$where = '';
if ($challenge_id)
{
$escaped_challenge_id = mysql_real_escape_string($challenge_id);
$where = "WHERE challenge_id = '$escaped_challenge_id'";
}
$r = mysql_query("SELECT position, user_id
FROM challenge_entries
$where");
while ($obj = mysql_fetch_object($r))
{
$this->user_id = $obj->user_id; //award the correct user!
$this->try_award($obj->position);
}
}
即使没有提供参数,cron 函数仍将工作。