我在想最好的方法来设计一个成就系统,在我的网站上使用。数据库结构可以在
Best way to tell 3 or more consecutive records missing
这个线程实际上是从开发人员那里获得想法的扩展。
我在这个网站上有很多关于徽章/成就系统的讨论,但问题是——都是讨论,没有代码。实际的代码实现示例在哪里?
我在这里提出了一个设计,我希望人们可以贡献,并希望创造一个良好的设计,编码可扩展的成就系统。我不是说这是最好的,远远不是,但这是一个可能的起点。
请随意发表你的意见。
我的系统设计思想
似乎一般的共识是创建一个“基于事件的系统”——每当一个已知的事件发生时,比如一篇文章被创建、删除等,它都会这样调用事件类。
$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
它基本上检查用户是否已经获得了徽章,如果没有,将更新徽章数据库表。徽章类还负责检索用户的所有徽章并将其以数组形式返回,等等(因此徽章可以显示在用户配置文件上)
当系统第一次在一个已经存在的站点上实现时呢?
还有一个“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()
会很慢,特别是对于很多徽章。所以我们创建了一个优化的查询。
哪个用户获奖?关于奖励
其他
基于事件的用户
这个
徽章
班
奖
作用于
user_id
--他们将永远获得这个奖。默认情况下,徽章授予导致事件发生的人,即会话用户ID(对于
default_event
函数,尽管cron作业显然会循环遍历所有用户并授予单独的用户)
所以让我们举个例子,在一个编码挑战网站上,用户提交他们的编码条目。管理员然后判断条目,完成后,将结果发布到challenge页面供所有人查看。发生这种情况时,将调用posted_results事件。
如果您想为所有发布的条目授予用户徽章,比方说,如果他们排名在前5名之内,那么您应该使用cron作业(尽管请记住,这将为所有用户更新,而不仅仅是为发布结果的挑战更新)
如果您希望使用cron job更新更具体的区域,那么让我们看看是否有方法将筛选参数添加到cron job对象中,并让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函数仍将工作。