当前位置: 首页 > news >正文

山东大学软件学院2026项目实训个人博客(九)

项目名称:基于AI大模型 的智能考研社区

撰写日期:2026年6月11日

本周的任务是完成帖子管理(/post)模块的开发,按照API文档的规划,实现帖子的增删查、点赞收藏、评论回复、历史记录与推荐等功能。

Post模块开发过程

1.模块说明

点赞表分片:post_like表按userId % 4分片,对应物理表post_like_0~post_like_3

历史记录分片:post_history表按userId % 4分片,对应物理表post_history_0~post_history_3

系统消息通知: 点赞、评论、回复等互动操作需通过聊天系统以系统消息身份通知帖主(MessageConstant.TYPE_SYSTEM,发送方 ID 为-1

AI 自动审核: 若开启 AI 自动审批,帖子/评论创建后会通过消息队列触发 AI 审核

功能全景 :

- 内容发布 :创建帖子,绑定标签,可选 AI 自动审核

- 互动系统 :点赞/取消点赞、收藏/取消收藏、评论/删除评论、回复评论/删除回复

- 查询检索 :搜索公开帖子、查看我的帖子、查看我的收藏、查看浏览历史

- 帖子详情 :分页展示评论(含子回复嵌套),自动记录浏览历史

- 推荐流 :基于 post_recommend 权重表 + Redis 去重,智能刷新首页帖子

- 系统通知 :点赞、评论、回复时通过 WebSocket + 数据库双通道推送系统消息

2.技术架构

(1)分层架构

Post 模块采用经典的三层架构,Controller 仅负责参数接收与结果封装,Service 承载全部业务逻辑:

PostController(接口层,17 个 POST 端点)

▼ 依赖注入(@RequiredArgsConstructor)
IPostService(接口层,19 个方法签名)


PostServiceImpl(服务层,~928 行完整实现)

├── PostMapper(标准帖子 CRUD)
├── PostLikeMapper(点赞,分表 _0~3)
├── PostHistoryMapper(历史记录,分表 _0~3)
├── PostCollectMapper(收藏)
├── PostCommentMapper(评论)
├── PostCommentReplyMapper(评论回复)
├── PostLabelMapper(帖子-标签关联)
├── PostRecommendMapper(推荐权重)
├── LabelMapper(标签)
├── UserMapper(用户)
├── AIAuditMapper(AI 审核配置)
└── MessageMapper(系统消息)

(2)分表策略

背景 :点赞和历史记录在高并发场景下数据量巨大,单表性能瓶颈显著。

实现方案 :使用 MyBatis-Plus 的 DynamicTableNameInnerInterceptor 实现动态表名替换。

用户请求


PostServiceImpl.like()
│ ① userId % 4 → suffix = "0"|"1"|"2"|"3"
│ ② DynamicTableHolder.setSuffix(suffix)
│ ③ postLikeMapper.insert(postLike)
│ ↓
│ MybatisPlusInterceptor 拦截 SQL
│ ↓
│ tableName = "post_like" → "post_like_0"(动态拼装)
│ ↓
│ INSERT INTO post_like_0 (...) VALUES (...)


DynamicTableHolder.remove()(finally 块确保清理)

涉及文件:

- config/DynamicTableHolder.java :ThreadLocal 持有当前分表后缀
- config/MybatisPlusConfig.java :注册 DynamicTableNameInnerInterceptor ,拦截 post_like 和 post_history 两张表


⚠️ 关键注意:每次分表操作必须在 finally 块中调用 DynamicTableHolder.remove() 清理 ThreadLocal,防止线程复用导致的分表串扰。

(3)系统通知机制

Post 模块首次在业务代码中实现了完整的 messageMapper.insert() 数据库写入,修复了前两个模块(Dialog 和 Admin)中系统消息只构造未入库的历史遗留问题。

PostServiceImpl.sendSystemMessage(receiverId, content)

├── ① messageMapper.insert(message) ← 数据库持久化
│ Message.senderId = -1 (SYSTEM_USER_ID)
│ Message.type = 2 (TYPE_SYSTEM)

└── ② messageServer.sendToUser(receiverId, wsPackage)
WebSocketMessagePackage { type: 2, data: content,
timestamp }
→ 在线用户实时收到通知
→ 离线用户下次登录通过 Message 接口拉取

通知触发时机:

操作通知内容模板接收方
点赞帖子"用户{userId} 点赞了你的帖子《{title}》"帖主
评论帖子"用户{userId} 评论了你的帖子《{title}》:{前50字}..."帖主
回复评论"用户{userId} 回复了你的评论:{前50字}..."原评论作者
子回复"用户{userId} 回复了你的评论:{前50字}..."被回复人

(4)AI 自动审批集成


参考 Admin 模块的 AI 审核设计,Post 模块遵循相同的"检查配置 → 异步发送 → AI 消费"模式。在 add() 、 comment() 、 replyComment() 三个写操作中统一调用 getAIAuditConfig() 判断是否需要自动审核:

AIAudit aiAudit = getAIAuditConfig(); if (aiAudit != null && aiAudit.getIsAiAudit() != null && aiAudit. getIsAiAudit() == 1) { Map<String, Object> auditMsg = new HashMap<>(); auditMsg.put("contentId", post.getId()); auditMsg.put("contentType", 1); // 1=帖子, 2=评论, 3=回复 rabbitMQUtils.sendAsync(RabbitMQConfig.AI_AUDIT_QUEUE, JSON. toJSONString(auditMsg)); }

3.开发任务清单

任务 1:新建 5 个 Mapper 接口

由于 PostCommentMapper 和 PostCommentReplyMapper 已在 Admin 模块开发中创建,本次只需新建剩余 5 个:

Mapper对应表
PostLikeMapper.javapost_like
PostCollectMapper.javapost_collect
PostLabelMapper.javapost_label
PostRecommendMapper.javapost_recommend
PostHistoryMapper.javapost_history

任务 2:新建分表基础设施

(1)DynamicTableHolder.java

ThreadLocal 持有当前线程的分表后缀( "0" / "1" / "2" / "3" ),提供 setSuffix() 、 getSuffix() 、 remove() 三个静态方法。

(2)MybatisPlusConfig.java

注册 MybatisPlusInterceptor Bean,内嵌 DynamicTableNameInnerInterceptor 。表名处理逻辑:

(sql, tableName) -> { String suffix = DynamicTableHolder.getSuffix(); if (suffix != null && ("post_like".equals(tableName) || "post_history".equals(tableName))) { return tableName + "_" + suffix; } return tableName; }

任务 3:扩展 IPostService 接口

在原有的 12 个方法基础上,新增 7 个方法签名:

新增方法对应接口说明

List<PostListVO> listMyPostList(PostQueryDTO)

/listMyPost我的帖子(标题→内容→标签三级匹配)
void comment(CommentDTO)/comment评论帖子 + AI 审核 + 系统通知
void undoComment(BaseRequest)/undoComment删除评论(级联删除子回复)
void replyComment(CommentDTO)/replyComment回复评论 / 子回复
void undoReplyComment(BaseRequest)/undoReplyComment删除回复评论
List<PostListVO> freshPost()/freshPost推荐流刷新

最终 IPostService 共 19 个方法签名 。

任务 4:实现 PostServiceImpl

这是本次开发最核心的文件,完整实现 19 个业务方法。以下按功能分组介绍:

(1)内容创建与删除

① add(PostDTO) — 创建帖子@Transactional

流程:

1. 从 BaseContext.getCurrentId() 获取当前用户
2. 构建 Post 对象, status=0 (待审核),计数器全为 0
3. PojoUtil.initCreate() 填充 createTime / updateTime / deleted
4. save(post) 插入数据库
5. 处理标签:新标签自动创建( type=1 表示帖子标签),建立 post_label 关联
6. 检查 AI 自动审核配置 → 可选发送 AI 审核请求到 ai-audit-routing-key

② delete(BaseRequest) — 逻辑删除@Transactional

流程:

1. 校验帖子存在且属于当前用户
2. 设置 deleted=1 ,更新 updateTime

(2)点赞系统(分表)

③ like(BaseRequest) — 点赞@Transactional

1. 校验帖子存在
2. DynamicTableHolder.setSuffix(userId % 4)
3. 查询 post_like_X 表:是否已点赞 → 幂等返回
4. 插入点赞记录到分表
5. UPDATE post SET like_count = like_count + 1
6. 向帖主发送系统消息(自己给自己点赞不通知)
7. finally: DynamicTableHolder.remove()

④ undoLike(BaseRequest) — 取消点赞@Transactional

1. 查分表获取点赞记录 → 不存在则幂等返回
2. 删除分表记录
3. UPDATE post SET like_count = GREATEST(like_count - 1, 0) ← 防
负数

(3)收藏系统

⑤ collect(BaseRequest) — 收藏@Transactional

直接操作 post_collect 表(无分表),先查重再插入,使用 setSql("collect_count = collect_count + 1") 更新计数。

⑥ undoCollect(BaseRequest) — 取消收藏@Transactional

逻辑与取消点赞一致,使用 GREATEST(collect_count - 1, 0) 防负数。

(4)帖子详情

⑦ detail(PostDetailDTO) — 查看帖子详情@Transactional

1. 查询帖子 + 帖主信息
2. 构造 PostVO 填充基本字段
3. 分表查询当前用户是否已点赞 → isLike
4. 查询当前用户是否已收藏 → isCollect
5. 查询帖子标签列表 → labels
6. 分页查询评论(Page<PostComment>)
7. 每条评论嵌套查询子回复 → CommentVO.children
8. 记录浏览历史(addToHistory)
- 已存在 → 更新 recentTime
- 不存在 → INSERT 到 post_history_X

评论嵌套结构 :

CommentVO
├── id, userId, userName, userAvatar, text
├── sendTime, status
└── children: List<CommentReplyVO>
├── id, userId, userName, userAvatar, text
├── commentId(所属评论 ID)
├── isSubReply(是否子回复)
├── subUserId, subUserName(被回复用户)
└── sendTime, status

(5)搜索与列表

⑧ listPostList(PostQueryDTO) — 搜索帖子

公共帖子搜索, deleted=0 AND status=1 (只展示通过的),支持 title/text keyword 模糊匹配,按 createTime 降序。

⑨ listMyPostList(PostQueryDTO) — 我的帖子

先按标题+内容 keyword 匹配,返回结果后再补充标签匹配的结果(去重后追加到列表末尾)。

⑩ listMyCollectList(PostQueryDTO) — 我的收藏

查询 post_collect 表获取收藏列表,过滤已删除帖子,支持 keyword 在标题/内容/标签三级匹配。

⑪ listMyHistoryList(PostQueryDTO) — 我的历史

从 post_history_X 分表查询,按 recentTime 降序,同样支持三级 keyword 匹配过滤。

⑫ deleteMyHistory(BaseRequest) — 删除历史

物理删除分表中的指定记录。

(6)评论系统

⑬ comment(CommentDTO) — 发表评论@Transactional

1. 校验帖子存在
2. INSERT post_comment(status=0 待审核)
3. UPDATE post SET comment_count = comment_count + 1
4. 可选 AI 自动审核(contentType=2)
5. 向帖主发送系统消息(截断正文至 50 字)

⑭ undoComment(BaseRequest) — 删除评论@Transactional

1. 校验评论存在且属于当前用户
2. 设置评论 deleted=1
3. 物理删除该评论下的所有子回复
4. UPDATE post SET comment_count = GREATEST(comment_count - (1 + 子回复数), 0)

⑮ replyComment(CommentDTO) — 回复评论@Transactional

分支处理:

若isSubReply=1 (回复的是回复):
1. 查询父回复 postCommentReplyId
2. 新建子回复, subUserId = 父回复.userId
3. 向父回复作者发系统消息

若isSubReply=0 (回复的是评论):
1. 查询父评论 postCommentId
2. 新建一级回复, subUserId = 父评论.userId
3. 查询所属帖子标题
4. 向父评论作者发系统消息

⑯ undoReplyComment(BaseRequest) — 删除回复@Transactional

物理删除回复记录,更新 comment_count - 1 。

任务 5:修复 PostController

将 6 个之前返回 null 或为空的接口连接到 Service 层:

接口修改前修改后
/listMyPostreturn Result.success()postService.listMyPostList(postQueryDTO)
/freshPostreturn nullpostService.freshPost()
/commentreturn Result.success()postService.comment(commentDTO)
/undoCommentreturn Result.success()postService.undoComment(request)
/replyCommentreturn Result.success()postService.replyComment(commentDTO)
/undoReplyCommentreturn Result.success()postService.undoReplyComment(request)

任务 6:修复 PostRecommend 实体

PostRecommend.java 原有代码缺少 @Data 注解,导致 Lombok 未生成 getter/setter,Lambda 方法引用(如 PostRecommend::getUserId )全部编译失败。

修复内容:

- 添加 @Data 注解
- 添加 @TableId(type = IdType.AUTO) 主键策略
- 添加 @Schema(description) 统一 Swagger 规范
- 补充 IdType 、 TableId 、 Schema 导入

4.遗留与展望

1. listPost() 方法 : IPostService.listPost() 返回 PostVO (详情对象)但接收 PostQueryDTO (搜索参数),语义不匹配。当前保留空实现,公共搜索已通过 listPostList() 覆盖。建议后续统一为 List<PostListVO> 返回类型。

2. CommentDTO 字段复用 : postCommentId 在 comment() 中作为帖子 ID,在 replyComment() 中作为评论 ID,字段命名有歧义。建议后续拆分为 postId 和 commentId 两个字段。

3. 推荐算法 :当前 freshPost() 依赖 post_recommend 权重表作为外部数据源,推荐数据的生成(如基于内容相似度、协同过滤)需要 AI 端或其他离线任务配合。

4. 评论分页 :当前 detail() 中评论使用 MyBatis-Plus 分页,但子回复是每条评论独立查询。评论量大时建议子回复也做分页或限制条数。

http://www.jsqmd.com/news/1001020/

相关文章:

  • 深耕全域智能营销九载,好客搜以技术实力赋能商家流量增长
  • Windows Server 2008专用RAID驱动整合包:覆盖AMD/NVIDIA/LSI/Adaptec/HighPoint等主流阵列卡芯片
  • 防排烟玻璃棉厂家求推荐 5项标准避坑 - 速递信息
  • 河北墙板厂家实力排行:5家合规企业核心维度对比 - 奔跑123
  • 水下声线追踪与分层声场仿真工具:MATLAB可运行代码+声线图绘制指南
  • 3分钟快速解决Windows热键冲突:Hotkey Detective完整终极指南
  • 2026年6月上海手表维修网点最新评测报告:盛时钟表维修实力领跑行业 - 速递信息
  • 2026年探秘西宸天街:连锁网咖里哪些环境让人赞不绝口?
  • i.MX31 SoC架构解析:ARM11核心、硬件加速与DVFS电源管理设计
  • D2DX:如何让20年前的暗黑破坏神2在现代PC上流畅运行?
  • 河北墙板厂家实力排行:合规与定制能力双维度测评 - 奔跑123
  • 无向图的Hierholzer算法流程(一)
  • 掌握Obsidian笔记迁移:使用Rust工具实现无损Markdown转换
  • NanaZip:Windows 11时代的压缩技术革新与生态演进
  • 无人场站数智升级:黎阳之光以视频孪生构建油气行业“无人值守新范式”
  • 国内高尔凡石笼网厂家实测排行:合规性与产能对比 - 奔跑123
  • 2026年GEO引擎网站建站公司推荐:优质服务商深度解析 - 速递信息
  • 从粒子滤波到精准定位:一文搞懂ROS AMCL核心参数背后的数学原理
  • 中文对话模型PyTorch实现:带BeamSearch解码与预训练词向量的seq2seq完整工程
  • LangChain4j 中如何实现结构化输出(Structured Output)?请说明其使用场景和常用实现方式
  • 2026广州瓷砖空鼓维修哪家好?地砖墙砖翘起起拱专业修复推荐 - 苏易修缮
  • 高性能汽车MCU MPC564xA:双发射核心与异构架构如何重塑动力总成控制
  • 2026上海爱马仕包包回收推荐:5家机构横评收的顶占据首位 - 奢侈品回收评测
  • 政策先行,技术就绪——L4重卡“编队试点、单车测试”行业现状深度解析! - 新闻快传
  • 智能架构转换:Python与Virtuoso Skill无缝系统集成方案
  • 鞍山黄金+手表回收门店推荐排行TOP1|专业鉴定团队,名表全品类通收,透明估价 - 速递信息
  • 2026佛山军事夏令营全维度科普指南:辨清行业乱象,优选正规成长营地 - 19120507004
  • 抖音视频去水印神器:三步获取纯净版短视频的终极指南
  • 无锡靠谱奢侈品全品类回收店推荐|一诺回收:全国直营连锁,高价透明,安心变现 - 速递信息
  • Pearcleaner终极实战指南:高效清理macOS残留文件的专业工具深度解析