我正在构建一个与StackExchange徽章工作方式非常相似的徽章系统。为了查询单个徽章的资格,似乎最佳解决方案是在表中放置一个SQL查询,以便它可以作为cron作业的一部分执行。
这是我正在考虑使用的查询样式:
SELECT u.user_id
FROM users u
WHERE (
SELECT COUNT(*)
FROM comments c
WHERE c.user_id = u.user_id
) > 0
AND [$badge_id] NOT IN (
SELECT b.badge_id
FROM user_badges b
WHERE b.user_id = u.user_id
)
表格看起来像这样:
badge_id | name | description | level | hash | query
---------+-----------------+---------------+-------+-----------------------------------------------------------------------------------------------------------------
1 | Hello World | First comment | 1 | 50a7c570f... | u.user_id FROM users u WHERE ( SELECT COUNT(*) FROM comments c WHERE c.user_id = u.user_id ) > 0
2 | Into the Breach | First thread | 1 | 6b01e9348... | u.user_id FROM users u WHERE ( SELECT COUNT(*) FROM threads t WHERE t.user_id = u.user_id ) > 0
从数据库中获取查询,前缀为SELECT
,然后追加AND [$badge_id] NOT IN (...)
子查询条件。然后,它将[$badge_id]
替换为当前徽章ID。执行查询并返回应给予徽章的用户ID列表。
哈希字段用于使用PHP脚本中定义的密钥存储查询的HMAC-SHA1哈希值。这背后的想法是,即使发生SQL注入攻击,攻击者也无法访问脚本中的密钥,也无法伪造更危险的查询。
我有两个问题:
我也很想知道StackExchange代码是如何做到的。
如果您有任何疑问,请随时提出。
干杯。
答案 0 :(得分:0)
这听起来不错。如果您更改了架构怎么办?现在你必须去使表中的每一行无效。如果愿意,我可能会使用触发器来记录“触发”徽章的操作。然后,一个cron脚本可以授予徽章。
在您的情况下,这是一个非常简单的查询,告诉我是否需要获得评论徽章,所以我会直接从CRON处理:
SELECT * FROM comments
LEFT JOIN users_badges ON users_badges.user_id = comments.user_id AND users_badges.description = 'First Comment'
WHERE users_badges.badge_id IS NULL
GROUP BY comments.user_id
理论上,您可以为每个返回的结果插入徽章记录。这不是我的头脑,但你明白了。
对于更复杂的查询,我仍然可能会插入某种活动表来简化操作。这也有助于将您的实施分开一点 - 如果将来徽章的“描述”发生变化,您会怎么做?规范化将有助于这种事情。