校园论坛课设三大件:DFA 敏感词过滤、点赞防刷、热度排序,答辩不会被问倒的实现
课程设计项目分享系列,面向大四计算机专业同学。文末附三周排期和答辩必问清单。
一、选题价值:论坛是"中间件含量"最高的课设
很多同学觉得论坛老套,恰恰相反——社区类系统是 Redis、并发控制、文本处理最好的练兵场。同样一个"发帖回帖",别人做的是 CRUD,你做出这三个硬核点:
- 敏感词过滤:DFA(确定有限自动机)算法,1 万词库毫秒级扫描;
- 点赞防刷:一个赞的并发一致性,牵出幂等、原子计数、防机器人三层话题;
- 热度排序:不是简单按时间,而是带时间衰减的加权分,讲得出公式就赢了。
二、技术选型
三、库表设计
CREATE TABLE post (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
author_id BIGINT NOT NULL,
title VARCHAR(120) NOT NULL,
content TEXT NOT NULL,
status TINYINT DEFAULT 1 COMMENT '1正常2审核中3已删除',
like_count INT NOT NULL DEFAULT 0 COMMENT 'Redis 计数的落库快照',
heat DECIMAL(10,4) COMMENT '热度分快照,用于 MySQL 排序兜底',
created_at DATETIME,
INDEX idx_heat (heat), INDEX idx_created (created_at)
);
CREATE TABLE like_flow (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
post_id BIGINT, user_id BIGINT,
action TINYINT COMMENT '1点赞2取消',
created_at DATETIME,
UNIQUE KEY uk_post_user (post_id, user_id) -- 一人一帖只有一条有效记录
);
CREATE TABLE sensitive_word (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
word VARCHAR(50) NOT NULL UNIQUE,
level TINYINT COMMENT '1拦截2替换*3放行统计',
enabled TINYINT DEFAULT 1
);
四、核心难点一:DFA 敏感词过滤
朴素做法是词库逐个 contains,1 万词 × 每帖 2KB 文本,性能不可接受。DFA 把词库建成字符树(Trie)+ 失败指针的简化版,扫描一次文本、时间复杂度 O(n):
public class SensitiveFilter {
private final Map<Character, Object> root = new HashMap<>();
public void load(List<String> words) { // 全量重建后原子替换引用
Map<Character, Object> newRoot = new HashMap<>();
for (String w : words) {
Map<Character, Object> cur = newRoot;
for (char c : w.toCharArray()) {
cur = (Map<Character, Object>) cur
.computeIfAbsent(c, k -> new HashMap<Character, Object>());
}
cur.put('\0', Boolean.TRUE); // 词尾标记
}
install(newRoot); // volatile 引用替换,读无锁
}
/** 返回命中的词与位置;level=1 直接拒绝发布,level=2 替换为 * */
public List<Hit> scan(String text) {
List<Hit> hits = new ArrayList<>();
for (int i = 0; i < text.length(); i++) {
Map<Character, Object> cur = root; int j = i;
while (j < text.length() && cur.containsKey(text.charAt(j))) {
cur = (Map<Character, Object>) cur.get(text.charAt(j++));
if (cur.containsKey('\0')) hits.add(new Hit(i, j, text.substring(i, j)));
}
}
return hits;
}
}
三个工程细节答辩必讲:① 词库热更新用"全量重建 + 引用原子替换",避免更新期间读到半棵树;② 变体对抗(赌 博、繁体、全角)在 scan 前做一次归一化;③ 分级处置:一级词拦截、二级词打星、三级词只统计给风控看。
五、核心难点二:点赞的一致性与防刷
点赞看似简单,并发下三个经典问题:重复点赞、计数不准、刷赞。方案:
public Result like(long postId, long userId) {
String key = "post:likes:" + postId;
// 1. Set 去重:SADD 返回 0 说明已赞,幂等返回
Long added = redis.opsForSet().add(key, String.valueOf(userId));
if (added == null || added == 0) return Result.ok("已赞过");
// 2. 原子计数:INCR 与 SADD 用 Lua 打包,保证同生同死
// 3. 异步落库:like_flow 插入 + like_count 定时快照(每 30s 从 Redis SCARD 同步)
// ——Redis 是实时值,MySQL 是兜底快照,Redis 挂了也不丢账
stringRedisTemplate.execute(LIKE_LUA, List.of(key, "post:cnt:" + postId));
mq.asyncSaveFlow(postId, userId, 1);
return Result.ok();
}
防刷三层:① 一人一帖一赞(Set 天然去重);② 频率限制:INCR user:like_rate:{uid}:{minute} + 60s 过期,超过 30 次/分钟进冷却;③ 注册时间 < 7 天且点赞间隔方差不大于阈值的账号标记可疑,点赞不进计数只进流水(影子赞),管理员复核。影子赞这个概念能讲清楚,答辩基本稳了。
六、核心难点三:热度排序公式
"按时间排"新帖霸屏,"按点赞排"老帖永驻。参考 Reddit/HackerNews 思路做带时间衰减的加权热度:
heat = (10×like + 5×comment + 3×collect − 4×report)
× 质量系数(正文长度/图片/原创声明)
× e^(−Δt / 12h) -- 半衰期 12 小时的指数衰减
实现上 ZSet 实时分 + MySQL heat 快照兜底排序:
// 每次点赞/评论/举报触发该帖重算;榜单 ZSet: board:heat 分数即 heat
double heat = rawScore * qualityFactor * Math.exp(-hoursSince / 12.0);
redis.opsForZSet().add("board:heat", String.valueOf(postId), heat);
答辩演示杀手锏:管理端放一个"时间滑块",拖动 Δt 实时看榜单变化——老帖衰减、新帖冒头,一秒讲明白公式的作用。
七、页面清单
前端五页:帖子流(热度/最新双 Tab)、帖子详情(点赞评论收藏)、个人主页(发帖回帖记录)、管理端词库管理(增删改 + 批量导入 + 分级)、管理端风控看板(拦截统计、可疑账号列表、影子赞复核)。
八、三周排期
九、加分项与答辩必问
加分项:回收站与软删除——删帖 30 天内可恢复,status=3 不参与查询但流水保留,配合操作日志构成完整审计链。
答辩必问三连:
- DFA 和直接遍历词库差多少? 压测数据:1 万词库、2KB 文本,朴素遍历约 45ms,DFA 约 0.8ms,差 50 倍以上;且 DFA 性能随词库增长近似平稳。
- Redis 挂了点赞会不会丢? 不会丢账:SADD 前先写 MQ,消费端落
like_flow;恢复后用流水重建 Set,like_count快照做兜底。设计原则是"流水是真相,缓存是视图"。 - 热度公式怎么防"标题党"? 质量系数里正文过短 (<50 字) 降权、纯图片无文字降权;举报数负向权重且管理员核实后回滚——公式是手段,治理是目的。
这套项目写完,技术文档(系统设计、库表说明、压测报告)工作量不小,赶时间可以用《论文侠》生成初稿再自己改,提纲免费、参考文献可指定篇数。
下一篇分享"食堂人流预测",关注不迷路。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)