简介
本开发文档围绕 DouPHP 的评论系统,系统性说明评论的 CRUD、审核机制、分类管理、回复功能、权限控制、防垃圾策略、统计展示以及数据库设计与索引优化。重点解析 CommentService 的核心方法(创建、修改、删除、查询),并给出自定义字段、表情集成、搜索功能的实现思路与最佳实践。
项目结构
评论模块采用分层设计:
- API/前端控制器负责路由与请求处理
- 业务服务层封装评论核心逻辑(权限校验、内容校验、提交、列表构建)
- 数据模型层提供 ORM 查询与批量操作
- 后台管理与小程序接口分别复用核心服务,保证一致性
graph TB
subgraph "控制器层"
AC["后台控制器<br/>admin/controller/comment"]
FC["前台控制器<br/>front/controller/comment"]
APC["API 控制器<br/>api/controller/comment"]
end
subgraph "服务层"
CS_CORE["核心服务<br/>core/service/comment"]
CS_FRONT["前台服务<br/>front/service/comment"]
CS_ADMIN["后台服务<br/>admin/service/comment"]
end
subgraph "模型层"
M_ADMIN["后台模型<br/>admin/model/comment"]
M_FRONT["前台模型<br/>front/model/comment"]
end
subgraph "数据层"
DB["数据库表 comment / order_item / user"]
end
AC --> CS_ADMIN
FC --> CS_FRONT
APC --> CS_CORE
CS_ADMIN --> M_ADMIN
CS_FRONT --> M_FRONT
CS_CORE --> DB
M_ADMIN --> DB
M_FRONT --> DB
核心组件
- 核心评论服务(core/service/comment/CommentService)
- 提供通用能力:获取评论列表、星级渲染、水军检测、内容长度与语言检查、评论权限校验等
- 前台评论服务(front/service/comment/CommentService)
- 负责用户侧评论表单准备、提交流程、验证码、反垃圾(Honeypot)、XSS 过滤、写入评论与订单项标记
- 后台评论服务(admin/service/comment/CommentService)
- 负责后台列表构建、阅读标记、管理员回复、显示状态切换、批量删除、参数组跳转
- 模型层(admin/front model/comment/Comment)
- 定义字段映射、查询作用域、批量操作、关联信息获取(如商品名、昵称、回复)
架构总览
评论系统以“控制器→服务→模型→数据库”的分层架构组织,核心服务被前台与后台复用,确保行为一致。小程序通过 API 控制器调用核心服务获取评论列表。
sequenceDiagram
participant U as "用户/小程序"
participant API as "API 控制器"
participant CORE as "核心评论服务"
participant DB as "数据库"
U->>API : "GET /comment/list?module=&item_id=&page="
API->>CORE : "data(module, item_id, pageSize, page)"
CORE->>DB : "查询已显示评论(分页)"
DB-->>CORE : "评论列表"
CORE-->>API : "组装后的评论数据"
API-->>U : "返回 JSON"
详细组件分析
核心评论服务(core/service/comment/CommentService)
- 列表获取 data/getCommentList
- 按 module、item_id 筛选,仅取已显示评论,支持分页
- 附加用户昵称、管理员回复、星级渲染、图片附件
- 星级渲染 starBox
- 根据评分生成星数数组与数量
- 防灌水 isWater
- 基于 IP 统计未读且无回复的主评论数量,超过阈值视为灌水
- 内容校验 checkComment
- 可选强制包含中文;长度校验
- 权限校验 checkPermission
- 同一项目是否已评论
- 配置开关允许无订单评论时,校验订单项归属与可评状态
- 防止重复对同一订单项评论
flowchart TD
Start(["进入 checkPermission"]) --> CheckModuleItem["检查是否已对该项目评论"]
CheckModuleItem --> |是| ReturnExist["返回错误:已评论过该项目"]
CheckModuleItem --> |否| CheckOrderConfig{"是否允许无订单评论?"}
CheckOrderConfig --> |否| EndNoOrder["结束默认不允许"]
CheckOrderConfig --> |是| ValidateOrder["校验订单项存在且属于当前用户"]
ValidateOrder --> OrderValid{"订单项有效?"}
OrderValid --> |否| ReturnNoAllow["返回错误:不允许评论"]
OrderValid --> |是| CheckOrderComment["校验订单项是否已评论"]
CheckOrderComment --> |是| ReturnExist2["返回错误:已评论过该订单项"]
CheckOrderComment --> |否| Pass["通过"]
Pass --> End(["结束"])
前台评论服务(front/service/comment/CommentService)
- 表单准备 getPostFormData
- 校验订单项与权限,若不可评则重定向
- 组装订单、商品信息与链接
- 提交处理 processStore
- Honeypot 反机器人与时序校验
- 登录态校验、权限校验
- 内容非法字符与长度校验
- 可选验证码校验
- XSS 过滤后入库,更新订单项评论标记
- 用户评论列表 buildUserCommentList
- 按用户筛选、默认排序、分页
- 附加模块名称、商品名、用户昵称、管理员回复、星级、图片附件
sequenceDiagram
participant F as "前台页面"
participant FS as "前台评论服务"
participant CORE as "核心评论服务"
participant DB as "数据库"
F->>FS : "POST 提交评论"
FS->>FS : "Honeypot/时序校验"
FS->>FS : "登录态与权限校验"
FS->>CORE : "checkPermission(...)"
CORE-->>FS : "通过/错误"
FS->>FS : "内容校验/XSS过滤"
FS->>DB : "插入评论记录"
FS->>DB : "更新订单项 comment=1"
FS-->>F : "返回成功消息"
后台评论服务(admin/service/comment/CommentService)
- 列表构建 buildCommentListData
- 仅主贴(reply_id=0),分页
- 附加模块名、商品名、用户信息、管理员回复、星级、图片附件、显示/隐藏状态、阅读标记、IP、时间
- 阅读详情 buildCommentReadData
- 读取单条评论,格式化时间,附加信息,标记为已读
- 管理员回复 reply
- 插入回复记录(from=admin),记录审计日志
- 显示切换 toggleShowHtml
- 切换 if_show 并返回 HTML 片段
- 批量删除 deleteBatch
- 批量删除并记录审计日志
- 参数组跳转 redirectToCommentParameterGroup
- 初始化评论相关参数项并跳转到参数设置页
classDiagram
class AdminCommentService {
+buildCommentListData(page) array
+buildCommentReadData(id) array
+reply(id, itemName, content, adminId, ip) void
+toggleShowHtml(id) string
+deleteBatch(data) void
+redirectToCommentParameterGroup() void
}
class AdminCommentModel {
+whereRoot() Builder
+findById(id) Model
+findReplyByReplyId(replyId) Model
+findItemName(module, itemId) string
+findNicknameByUserId(userId) string
+markRead(id) void
+toggleShow(id) int
+insertReply(data) void
+deleteByIds(ids) void
}
AdminCommentService --> AdminCommentModel : "使用"
控制器与路由
- 后台控制器
- 列表、阅读、回复、显示切换、批量删除、参数设置跳转
- 前台入口控制器
- 登录后跳转至我的评论,否则跳转登录
- API 控制器
- 公开接口 listing 获取评论列表,分页大小来自配置
依赖关系分析
- 控制器依赖各自服务层
- 服务层依赖核心服务(前台/后台均复用 core/service/comment/CommentService)
- 模型层提供 ORM 查询与批量操作
- 数据层访问 comment、order_item、user 等表
graph LR
AC["后台控制器"] --> AS["后台服务"]
FC["前台控制器"] --> FS["前台服务"]
APC["API 控制器"] --> CS["核心服务"]
AS --> AM["后台模型"]
FS --> FM["前台模型"]
CS --> DB["数据库"]
AM --> DB
FM --> DB
性能与索引优化
- 查询优化
- 列表查询按 module、item_id、if_show 过滤,建议建立复合索引以提升分页效率
- 管理员回复通过 reply_id 查找,建议对 reply_id 建立索引
- 用户昵称与商品名通过 JOIN 或子查询获取,建议在 user.id、对应模块主键上建立索引
- 写操作优化
- 提交评论后更新订单项 comment 标志,避免频繁扫描,必要时对 order_item.id 建立索引
- 缓存与分页
- 列表分页大小可通过配置调整,减少单次返回数据量
- 安全与反垃圾
- Honeypot 与时序校验降低机器提交
- 内容非法字符过滤与长度限制减轻存储与渲染压力
- 基于 IP 的灌水检测减少恶意刷屏
故障排查指南
- 无法发表评论
- 检查是否已登录;权限校验是否通过(是否已评论该项目或订单项)
- 检查内容是否包含非法字符或长度不合规
- 若开启验证码,确认验证码是否正确
- 评论未显示
- 检查 if_show 是否为已显示状态
- 检查是否为主贴(reply_id=0)
- 管理员回复未生效
- 检查 reply_id 是否正确指向目标评论
- 检查 from 字段是否为 admin
- 批量删除无效
- 检查传入的 ID 列表是否合法
- 检查审计日志是否记录
结论
DouPHP 评论系统通过清晰的分层与复用核心服务,实现了跨端一致的评论能力。前台注重安全性与用户体验,后台提供完善的管理与审计。通过合理的权限校验、反垃圾策略与索引优化,系统在可扩展性与性能方面具备良好基础。开发者可按需扩展字段、表情与搜索能力,满足多样化业务场景。
附录:集成与扩展实践
自定义评论字段
- 在模型中扩展 fillable/casts,并在服务层写入与读取时处理新字段
- 示例路径参考:
- 后台评论模型 Comment.php:48-62
- 前台评论模型 Comment.php:33-43
- 在服务层写入时填充新字段,在列表构建时附加到返回数据
集成评论表情
- 在前台提交前将表情编码为文本或富文本格式,经 XSS 过滤后入库
- 在列表渲染时将表情还原为可显示内容
- 示例路径参考:
- 前台评论服务 CommentService.php:217-237
实现评论搜索
- 在列表查询中加入关键词匹配条件(content 字段模糊搜索)
- 结合分页与排序,提升检索体验
- 示例路径参考:
- 核心评论服务 CommentService.php:64-100
- 后台评论服务 CommentService.php:49-98
评论权限控制
- 统一通过核心服务的 checkPermission 进行权限判断
- 支持配置开关允许无订单评论,同时校验订单项归属与可评状态
- 示例路径参考:
- 核心评论服务 CommentService.php:166-191
防垃圾评论机制
- Honeypot 与时序校验
- 内容非法字符与长度限制
- 基于 IP 的灌水检测
- 示例路径参考:
- 前台评论服务 CommentService.php:172-205
- 核心评论服务 CommentService.php:127-139
评论统计功能
- 可在列表或详情页聚合统计:评论总数、平均星级、最近评论时间等
- 通过聚合查询或缓存方式计算,避免每次实时全表扫描
- 示例路径参考:
- 核心评论服务 CommentService.php:64-100