上下投票(Upvote / Downvote)
P0对帖子与评论均可投票,支持投票、取消、切换方向三种状态。
实现要点:voting 表用 (user_id, target_type, target_id) 唯一索引,value ∈ {1, -1};重复请求幂等返回。前端乐观更新,服务端返回权威计数。
Module 04 · V1.0 · Voting & Ranking
社区的心跳,用算法替代人工编辑分发
实现幂等、可撤销、可加权、抗刷的投票系统,并把票数与时间衰减合成排序分数,驱动所有信息流的排序。
排序决定了什么内容被看见,而「被看见」直接决定创作者的激励。这是社区产品最核心的杠杆,必须最早做对。
共 8 个功能点,每个功能点给出实现要点,可直接作为开发任务的描述。
对帖子与评论均可投票,支持投票、取消、切换方向三种状态。
实现要点:voting 表用 (user_id, target_type, target_id) 唯一索引,value ∈ {1, -1};重复请求幂等返回。前端乐观更新,服务端返回权威计数。
综合票数与时间的排序公式,保证新鲜内容有机会超越老内容。
实现要点:score = log10(max(|v|,1)) × sign(v) + age_seconds / 45000。对数项压缩票数差距(1 票与 1000 票不至于天差地别),第二项随时间线性增长,约 12.5 小时抵消一个数量级的票数差。
Hot(热度)、New(最新)、Top(最高分,支持 小时/天/周/月/年/全部 时间窗)、Rising(上升中)、Controversial(争议)。
实现要点:Hot/New 走 Redis ZSet 直接取;Top 按时间窗在数据库按分数排序(有覆盖索引);Controversial 按 min(up, down) 且 up 与 down 接近度排序;Rising 用近期票速度(票数 / 时间)排序。
依据信任等级与 Karma 加权;可疑账号的票权重降为 0(仍记录但不计分)。
实现要点:权重表按 trust_level 映射(TL0=0、TL1=0.5、TL2=1、TL3=1.5、TL4=2)。反作弊服务命中则把该用户近期所有票标记为 weight=0 并触发分数重算。
评论可选择「最佳(Best)」「最新(New)」「争议(Controversial)」「最旧(Old)」四种排序,并记忆用户偏好。
实现要点:排序偏好写入 user_settings,服务端在返回评论树时按偏好组装顺序;每个层级应用相同的排序策略(保持树结构的语义一致)。
热度分数写入 Redis 有序集合 + 数据库快照,避免每次请求实时聚合。
实现要点:投票后异步重算该条目的 hot_score 并 ZADD;定时任务(每 10 分钟)批量重算近期内容,防止时间项长期未更新导致排序失真。
设备指纹、IP 聚集、注册时间、行为序列、投票图聚类的多层检测。
实现要点:规则层拦截明显异常(同 IP 多账号互相投票),模型层对投票关系图做社区发现,识别互投团伙并整体降权。
可配置为精确展示、模糊展示(1.2k)、或发布初期隐藏票数(防止从众效应)。
实现要点:隐藏期内仅作者可见真实票数;展示时统一使用紧凑格式,避免长数字撑破布局。
2 条关键交互的分步流程,按用户体验顺序描述,可直接作为前端开发与可用性测试脚本。
点击向上箭头,立即变为主色高亮、计数 +1,进入乐观状态。
再次点击同一箭头则取消投票,计数回落,按钮恢复灰色。
点击向下箭头则切换为反对,计数从 +1 变为 -1(净变化 -2)。
服务端返回后静默校正;若失败则回滚并有轻微抖动动画提示。
未登录用户点击投票弹出登录引导,登录后自动补投该次投票。
列表顶部 Tab 切换 Hot / New / Top,切换时保留滚动位置的比例而非绝对像素。
选择 Top 时展开时间窗下拉(今天/本周/本月/今年/全部)。
排序偏好写入本地与账号,下次进入自动应用。
6 条硬性约束。这些规则应当在服务层强校验,而非仅在前端提示。
一个用户对同一目标只能有一个有效投票记录。
投票可撤销,撤销后不得计入任何统计。
帖子发布后 1 小时内禁止作者对自己的帖子投票(自投无效,直接忽略)。
同一 IP 24 小时内对同一目标的多账号投票只计一次。
Hot 分数中时间项每 45000 秒累计 1 分,约 12.5 小时抵消 10 倍票数差。
投票计数与 Karma 更新允许最终一致,但用户自身的票状态必须强一致(读己之写)。
| Hot 热度 | log10 票数 + 时间衰减 | 首页默认、社区默认 | 兼顾质量与新度 |
| New 最新 | created_at DESC | 追新用户、突发事件 | 绝对时序,零延迟 |
| Top 最高分 | score DESC(时间窗过滤) | 精华回顾、周报 | 需要时间窗限定 |
| Rising 上升 | 近期票速度 | 发现早期优质内容 | 需滑动窗口计数 |
| Controversial 争议 | up 与 down 接近且总数高 | 争议话题、辩论 | 需双向票数据 |
本模块涉及的 6 张表。完整字段、索引与说明见「数据表设计」页。
本模块的设计参照了哪些产品,具体参照了什么。
Hot 公式的经典实现,对数压缩 + 时间衰减。
Hacker News
Score = (v-1)/(age+2)^1.8,更激进的时间衰减。
Lobsters
低 Karma 用户投票权被削弱,隐性降权值得借鉴。
Lemmy
物化视图预计算排序分数,读取性能优异。
本模块交付时应当达到的量化目标。未达标即视为该模块未完成。
竞品踩过的坑,以及本模块在实现时最容易犯的错误。
不要用单独的 upvote_count 字段做排序输入,必须把时间项写进分数,否则老内容永久霸榜。
投票计数用 COUNT(*) 实时统计会在热门帖上打爆数据库,必须走增量计数器。
排序分数若长期不重算,时间项会失真,必须定时回填。
前端乐观更新必须有回滚路径,否则网络抖动会造成数据与现实不符。