一、原生评价体系数据表拆解
1.1 评价主表(ls_goods_comment)
LikeShop 的商品评价数据存储在 ls_goods_comment 表中,该表通过 order_goods_id 关联订单商品项,确保只有购买过的用户才能评价;评价记录包含商品评分、服务评分、物流评分、描述相符评分四个维度,以及评价内容文本和评价图片;评价列表接口 /api/goods_comment_lists 返回的核心字段包括:goods_comment(商品星级)、service_comment(服务星级)、express_comment(物流星级)、comment(评价内容)、image(图片数组)、reply(商家回复)、nickname / avatar(用户信息)、create_time(评价时间)。
1.2 评价状态与审核机制
原生系统已具备基础评价审核能力;后台商品评价管理页面支持通过商品名称、用户昵称、评价类型、回复状态、审核状态、评价时间等条件搜索筛选评价;在"通用设置"中可以配置"开启后会有评论(首次评价、追评)需要后台审核后才能在前端显示",审核状态控制评价是否对外展示。
1.3 原生评价体系的四个核心缺口
缺口一:无追评能力。原生评价表仅存储首次评价内容,用户收货后无法对已评价的商品进行补充评价。虽然后台"通用设置"中存在追评相关的配置项,但数据表层面缺乏追评内容的独立存储结构。
缺口二:晒图审核粒度粗。原生的审核状态是评价级别的开关(整条评价审核通过/驳回),无法针对单张图片进行精细审核,也无法处理"文字通过但图片违规"的背景。
缺口三:无评价标签体系。原生评价列表中仅展示评价文本和星级,不支持"质量好""物流快"等结构化标签的生成、存储和筛选;用户无法通过标签快速定位自己关心的评价维度。
缺口四:无差评预警机制。差评产生后没有任何自动通知机制,商家需要主动登录后台才能发现,容易错过最佳处理时间窗口。
1.4 评价数据流转链路
一次完整的评价数据流转为:用户提交评价 → 图片异步上传 → 评价记录写入 ls_goods_comment → 审核状态控制前端展示 → 评价列表接口返回数据 → 商品详情页渲染评价卡片;二开需要在这条链路的写入端(追评、标签)、审核端(晒图审核)和读取端(标签筛选、预警通知)分别注入扩展逻辑。
二、追评功能开发
2.1 需求分析
追评的核心业务约束是:用户必须在收货后的一定时间窗口内进行追评;时间窗口过短,用户来不及体验商品;时间窗口过长,追评与初始评价的时间间隔过大,参考价值降低;推荐将追评窗口设置为收货后 7-30 天,具体天数由后台配置。
2.2 数据库扩展
不新建独立追评表,而是在 ls_goods_comment 表中扩展追评字段,保持"一单一条评价记录"的数据模型:
ALTER TABLE `ls_goods_comment`
ADD COLUMN `is_append` tinyint NOT NULL DEFAULT 0 COMMENT '是否为追评:0-首次评价,1-追评' AFTER `reply`,
ADD COLUMN `append_comment` varchar(1000) NOT NULL DEFAULT '' COMMENT '追评内容' AFTER `is_append`,
ADD COLUMN `append_image` text COMMENT '追评图片,JSON数组' AFTER `append_comment`,
ADD COLUMN `append_time` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '追评时间' AFTER `append_image`,
ADD COLUMN `append_status` tinyint NOT NULL DEFAULT 0 COMMENT '追评审核状态:0-待审核,1-已通过,2-已驳回' AFTER `append_time`,
ADD COLUMN `append_reply` varchar(500) NOT NULL DEFAULT '' COMMENT '商家对追评的回复' AFTER `append_status`;
ALTER TABLE `ls_goods_comment`
ADD INDEX `idx_append_time` (`append_time`);
2.3 追评时间窗口校验
在追评接口中,基于订单的收货时间计算追评截止时间:
class CommentAppendService
{
/**
* 追评提交
* @param int $commentId 评价ID
* @param int $userId 用户ID
* @param string $content 追评内容
* @param array $images 追评图片
*/
public function append($commentId, $userId, $content, $images = [])
{
// 查询评价记录
$comment = GoodsCommentModel::where('id', $commentId)
->where('user_id', $userId)
->find();
if (!$comment) {
throw new Exception('评价记录不存在');
}
// 校验是否已追评
if ($comment['is_append'] == 1 && $comment['append_time'] > 0) {
throw new Exception('您已对该评价进行过追评');
}
// 获取订单收货时间
$orderGoods = OrderGoodsModel::find($comment['order_goods_id']);
$order = OrderModel::find($orderGoods['order_id']);
if ($order['order_status'] < 3) { // 3=已收货
throw new Exception('订单尚未收货,无法追评');
}
$receiveTime = $order['receive_time'] ?: $order['update_time'];
$appendDays = ConfigModel::get('comment_append_days', 15);
$deadline = $receiveTime + $appendDays * 86400;
if (time() > $deadline) {
throw new Exception("追评时间已过(收货后{$appendDays}天内可追评)");
}
// 追评内容敏感词校验
if (SensitiveWordService::check($content)) {
throw new Exception('追评内容包含敏感词,请修改后提交');
}
// 写入追评数据
$needAudit = ConfigModel::get('comment_append_audit', 1);
GoodsCommentModel::where('id', $commentId)->update([
'is_append' => 1,
'append_comment'=> $content,
'append_image' => json_encode($images, JSON_UNESCAPED_UNICODE),
'append_time' => time(),
'append_status' => $needAudit ? 0 : 1,
]);
// 追评后触发标签提取和差评预警检查
event('CommentAppendSubmit', [
'comment_id' => $commentId,
'user_id' => $userId,
'content' => $content,
]);
return ['need_audit' => $needAudit];
}
}
2.4 追评入口的前端展示
在我的订单列表和商品评价列表中,对满足追评条件的评价展示追评入口;接口在返回评价数据时增加以下字段:
{
"can_append": 1,
"append_deadline": "2026-10-24 23:59:59",
"append_deadline_desc": "剩余 8 天可追评",
"is_append": 0,
"append_comment": "",
"append_image": [],
"append_reply": ""
}
can_append 的计算逻辑:订单已收货 + 未追评 + 当前时间在追评截止时间之前;追评成功后,评价卡片中追加展示追评内容、追评图片(带"追评"角标)和商家对追评的回复。
2.5 后台配置
在"通用设置 → 评价设置"中增加:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| 追评开关 | 开关 | 开启 | 是否允许用户追评 |
| 追评时间窗口 | 数字输入(天) | 15 | 收货后 N 天内可追评 |
| 追评是否需要审核 | 开关 | 开启 | 追评内容是否需后台审核 |
三、晒图审核功能开发
3.1 需求分析
晒图审核的核心是"先审后上":用户提交评价和图片后,图片进入审核队列,审核通过后才对外展示;审核状态控制在图片级别,而非评价级别,支持"文字通过但某张图片驳回"的精细场景。
3.2 数据库扩展
将评价图片从 JSON 字段升级为独立的图片记录表,支持逐张审核:
CREATE TABLE `ls_comment_image` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`comment_id` int UNSIGNED NOT NULL COMMENT '评价ID',
`image_url` varchar(500) NOT NULL DEFAULT '' COMMENT '图片地址',
`image_type` tinyint NOT NULL DEFAULT 1 COMMENT '图片类型:1-首次评价图片,2-追评图片',
`audit_status` tinyint NOT NULL DEFAULT 0 COMMENT '审核状态:0-待审核,1-已通过,2-已驳回',
`audit_admin_id` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '审核人ID',
`audit_time` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '审核时间',
`reject_reason` varchar(255) NOT NULL DEFAULT '' COMMENT '驳回原因',
`create_time` int UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `idx_comment_id` (`comment_id`),
KEY `idx_audit_status` (`audit_status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='评价图片审核记录表';
3.3 图片上传与审核流程
第一步:图片上传接口。评价提交时,图片先通过独立的文件上传接口上传,返回图片 URL 列表;上传接口对图片做基础校验:格式限制 jpg/png/webp,单张大小 ≤ 5MB,单次最多 9 张。
第二步:评价提交时关联图片。评价数据写入 ls_goods_comment 后,将图片 URL 逐条写入 ls_comment_image 表,audit_status 默认为 0 (待审核)。
第三步:后台审核操作。后台新增"晒图审核"列表页,按 audit_status = 0 筛选待审核图片;审核操作支持单张通过/驳回;驳回时必填驳回原因。
第四步:前端展示控制。评价列表接口只返回 audit_status = 1 (已通过)的图片;对于评价文本已通过但图片仍在审核中的情况,前端展示"图片审核中"的占位提示。
/**
* 获取评价的展示图片(仅返回审核通过的)
*/
public function getVisibleImages($commentId)
{
return CommentImageModel::where('comment_id', $commentId)
->where('audit_status', 1)
->column('image_url');
}
3.4 后台配置
在"通用设置 → 评价设置"中增加:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| 晒图审核开关 | 开关 | 开启 | 图片是否需后台审核 |
| 自动审核通过比例 | 数字输入(%) | 0 | 0 表示全部人工审核,100 表示全部自动通过 |
| 单次最多上传图片数 | 数字输入 | 9 | 评价图片数量上限 |
| 图片大小上限(MB) | 数字输入 | 5 | 单张图片文件大小限制 |
四、自定义评价标签功能开发
4.1 需求分析
评价标签体系包含两个维度:平台预置标签 (如"质量好""物流快""包装精美""客服态度好")和用户自定义标签;平台预置标签由运营在后台维护,用户在评价时从预警标签中勾选;也支持用户输入自定义标签,经过敏感词过滤和频次统计后沉淀为新的预置标签。
4.2 数据库扩展
新增标签定义表和评价-标签关联表:
-- 评价标签定义表
CREATE TABLE `ls_comment_tag` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`tag_name` varchar(30) NOT NULL COMMENT '标签名称',
`tag_type` tinyint NOT NULL DEFAULT 1 COMMENT '标签类型:1-好评标签,2-中评标签,3-差评标签',
`is_preset` tinyint NOT NULL DEFAULT 1 COMMENT '是否预置:1-平台预置,2-用户自定义沉淀',
`use_count` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '被使用次数',
`sort` int NOT NULL DEFAULT 0 COMMENT '排序',
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:0-禁用,1-启用',
`create_time` int UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `idx_tag_type_status` (`tag_type`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='评价标签定义表';
-- 评价-标签关联表
CREATE TABLE `ls_comment_tag_relation` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`comment_id` int UNSIGNED NOT NULL COMMENT '评价ID',
`tag_id` int UNSIGNED NOT NULL COMMENT '标签ID',
`goods_id` int UNSIGNED NOT NULL COMMENT '商品ID,冗余字段,加速按商品聚合查询',
`create_time` int UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_comment_tag` (`comment_id`, `tag_id`),
KEY `idx_goods_tag` (`goods_id`, `tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='评价标签关联表';
4.3 标签选择与自定义逻辑
评价提交时,前端展示当前商品已关联的热门标签(按 use_count 降序),用户可多选(最多 5 个);同时提供"自定义标签"输入框,用户输入后经敏感词校验通过即可关联。
/**
* 评价提交时处理标签
*/
public function handleTags($commentId, $goodsId, $tagIds = [], $customTags = [])
{
// 处理预置标签
foreach ($tagIds as $tagId) {
$tag = CommentTagModel::where('id', $tagId)
->where('status', 1)->find();
if (!$tag) continue;
CommentTagRelationModel::create([
'comment_id' => $commentId,
'tag_id' => $tagId,
'goods_id' => $goodsId,
'create_time'=> time(),
]);
// 标签使用次数递增(使用 Redis 原子递增,定时同步到数据库)
Redis::incr("comment:tag:use_count:{$tagId}");
}
// 处理自定义标签
foreach ($customTags as $tagName) {
$tagName = trim($tagName);
if (empty($tagName) || mb_strlen($tagName) > 10) continue;
if (SensitiveWordService::check($tagName)) continue;
// 查询是否已存在同名标签
$existTag = CommentTagModel::where('tag_name', $tagName)
->where('status', 1)->find();
if ($existTag) {
$tagId = $existTag['id'];
} else {
// 创建新标签,标记为用户自定义
$tagId = CommentTagModel::create([
'tag_name' => $tagName,
'tag_type' => 1,
'is_preset' => 2,
'status' => 1,
'create_time'=> time(),
])->id;
}
CommentTagRelationModel::create([
'comment_id' => $commentId,
'tag_id' => $tagId,
'goods_id' => $goodsId,
'create_time'=> time(),
]);
}
}
4.4 标签筛选接口
在评价列表接口中增加标签筛选参数,支持按标签 ID 过滤评价:
public function getCommentList($goodsId, $tagId = 0, $page = 1, $size = 15)
{
$query = GoodsCommentModel::alias('c')
->where('c.goods_id', $goodsId)
->where('c.audit_status', 1); // 只查已审核通过的
if ($tagId > 0) {
$query->join('comment_tag_relation r', 'c.id = r.comment_id')
->where('r.tag_id', $tagId);
}
$list = $query->order('c.create_time', 'desc')
->page($page, $size)
->select();
// 批量查询每条评价的标签
$commentIds = array_column($list->toArray(), 'id');
$tagMap = CommentTagRelationModel::alias('r')
->join('comment_tag t', 'r.tag_id = t.id')
->whereIn('r.comment_id', $commentIds)
->column('t.tag_name, t.id', 'r.comment_id');
return ['list' => $list, 'tag_map' => $tagMap];
}
4.5 商品维度的标签聚合
在商品详情页的评价模块中,展示该商品的热门标签云,点击标签即可筛选评价。标签聚合数据通过定时任务计算,避免实时统计:
// 定时任务:每日凌晨统计商品标签使用次数
public function aggregateTagStats()
{
$goodsList = GoodsModel::where('status', 1)->column('id');
foreach ($goodsList as $goodsId) {
$tags = CommentTagRelationModel::alias('r')
->join('comment_tag t', 'r.tag_id = t.id')
->where('r.goods_id', $goodsId)
->where('t.status', 1)
->group('r.tag_id')
->field('r.tag_id, t.tag_name, count(*) as cnt')
->order('cnt', 'desc')
->limit(10)
->select();
Redis::set("goods:comment:tags:{$goodsId}", json_encode($tags), 86400);
}
}
4.6 后台配置
在"商品管理 → 评价标签"中增加标签管理页面,支持标签的增删改查、启用/禁用、排序;预置标签初始化数据建议包含:
| 标签名称 | 类型 | 预置 |
|---|---|---|
| 质量好 | 好评 | 是 |
| 物流快 | 好评 | 是 |
| 包装精美 | 好评 | 是 |
| 客服态度好 | 好评 | 是 |
| 性价比高 | 好评 | 是 |
| 与描述相符 | 好评 | 是 |
| 做工粗糙 | 差评 | 是 |
| 物流慢 | 差评 | 是 |
| 色差大 | 差评 | 是 |
五、差评自动预警通知
5.1 需求分析
差评预警的核心是"实时发现 + 多渠道通知";预警触发条件有两个:星级触发 (商品评分 ≤ 3 星)和关键词触发(评价文本包含"差评""垃圾""退货""投诉"等负面关键词);预警通知渠道支持站内消息、短信、企业微信/钉钉 Webhook。
5.2 预警触发逻辑
在评价提交和追评提交的事件监听中,接入差评检测:
class CommentWarningService
{
/**
* 评价提交后检查是否触发预警
*/
public function check($commentId)
{
$comment = GoodsCommentModel::find($commentId);
if (!$comment) return;
$warning = false;
$reasons = [];
// 条件一:星级触发
$minStar = ConfigModel::get('comment_warning_min_star', 3);
if ($comment['goods_comment'] <= $minStar) {
$warning = true;
$reasons[] = "商品评分{$comment['goods_comment']}星(≤{$minStar}星)";
}
// 条件二:关键词触发
$keywords = ConfigModel::get('comment_warning_keywords', '差评,垃圾,退货,投诉,被骗,假货');
$keywordArr = array_filter(explode(',', $keywords));
foreach ($keywordArr as $kw) {
if (mb_strpos($comment['comment'], $kw) !== false) {
$warning = true;
$reasons[] = "命中敏感词「{$kw}」";
break;
}
}
// 条件三:追评内容触发
if ($comment['append_comment']) {
if ($comment['append_status'] == 1) {
foreach ($keywordArr as $kw) {
if (mb_strpos($comment['append_comment'], $kw) !== false) {
$warning = true;
$reasons[] = "追评命中敏感词「{$kw}」";
break;
}
}
}
}
if (!$warning) return;
// 防重复预警
$cacheKey = "comment:warning:sent:{$commentId}";
if (Redis::get($cacheKey)) return;
Redis::setex($cacheKey, 3600, 1);
// 发送预警通知
$this->sendWarning($comment, $reasons);
}
private function sendWarning($comment, $reasons)
{
$goods = GoodsModel::find($comment['goods_id']);
$message = "【差评预警】\n"
. "商品:{$goods['name']}\n"
. "用户:{$comment['nickname']}\n"
. "评分:{$comment['goods_comment']}星\n"
. "内容:" . mb_substr($comment['comment'], 0, 100) . "\n"
. "触发原因:" . implode(';', $reasons) . "\n"
. "时间:" . date('Y-m-d H:i:s', $comment['create_time']);
// 渠道一:站内消息通知商家
MessageService::sendToShop($goods['shop_id'], '差评预警', $message);
// 渠道二:企业微信/钉钉 Webhook
$webhookUrl = ConfigModel::get('comment_warning_webhook', '');
if ($webhookUrl) {
HttpClient::post($webhookUrl, [
'msgtype' => 'text',
'text' => ['content' => $message],
]);
}
// 渠道三:短信通知(可选,仅对高优先级差评)
if ($comment['goods_comment'] <= 2) {
$shopMobile = ShopModel::where('id', $goods['shop_id'])
->value('mobile');
if ($shopMobile) {
SmsService::send($shopMobile, $message);
}
}
// 写入预警记录
CommentWarningLogModel::create([
'comment_id' => $comment['id'],
'goods_id' => $comment['goods_id'],
'shop_id' => $goods['shop_id'],
'reasons' => json_encode($reasons, JSON_UNESCAPED_UNICODE),
'create_time'=> time(),
]);
}
}
5.3 预警记录管理
新增预警日志表,便于商家和平台追溯差评处理情况:
CREATE TABLE `ls_comment_warning_log` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT,
`comment_id` int UNSIGNED NOT NULL COMMENT '评价ID',
`goods_id` int UNSIGNED NOT NULL COMMENT '商品ID',
`shop_id` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '店铺ID',
`reasons` text COMMENT '触发原因,JSON数组',
`is_handled` tinyint NOT NULL DEFAULT 0 COMMENT '是否已处理:0-未处理,1-已处理',
`handle_remark` varchar(500) NOT NULL DEFAULT '' COMMENT '处理备注',
`handle_time` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '处理时间',
`create_time` int UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `idx_shop_handled` (`shop_id`, `is_handled`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='差评预警记录表';
5.4 后台配置
在"通用设置 → 评价设置"中增加"差评预警"配置区块:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| 差评预警开关 | 开关 | 开启 | 是否启用差评预警 |
| 预警星级阈值 | 数字选择(1-5) | 3 | 评分 ≤ 此值触发预警 |
| 预警关键词 | 文本输入(逗号分隔) | 差评,垃圾,退货,投诉 | 命中任一关键词触发预警 |
| 企业微信/钉钉 Webhook | URL 输入 | 空 | 预警消息推送地址 |
| 短信通知 | 开关 | 关闭 | 2星及以下差评是否短信通知 |
六、完整开发步骤总结
6.1 数据库变更清单
| 操作 | 表名 | 变更内容 |
|---|---|---|
| ALTER | ls_goods_comment | 新增 is_append、append_comment、append_image、append_time、append_status、append_reply 字段 |
| CREATE | ls_comment_image | 新建评价图片审核记录表 |
| CREATE | ls_comment_tag | 新建评价标签定义表 |
| CREATE | ls_comment_tag_relation | 新建评价标签关联表 |
| CREATE | ls_comment_warning_log | 新建差评预警记录表 |
6.2 后端开发清单
| 模块 | 文件/类 | 工作内容 |
|---|---|---|
| 数据层 | CommentImageModel | 新建 Model |
| 数据层 | CommentTagModel | 新建 Model |
| 数据层 | CommentTagRelationModel | 新建 Model |
| 数据层 | CommentWarningLogModel | 新建 Model |
| 业务层 | CommentAppendService | 新建 Service,封装追评提交和窗口校验逻辑 |
| 业务层 | CommentTagService | 新建 Service,封装标签选择、自定义标签处理和聚合统计 |
| 业务层 | CommentWarningService | 新建 Service,封装差评检测和多渠道通知 |
| 业务层 | CommentLogic | 改造评价列表接口,接入标签筛选和审核图片过滤 |
| 后台接口 | CommentController | 新增晒图审核接口(批量通过/驳回)、标签管理接口 |
| 后台接口 | CommentController | 新增预警记录查询和标记已处理接口 |
| 事件 | CommentSubmitEvent | 评价提交后触发标签处理和差评预警检查 |
| 事件 | CommentAppendSubmitEvent | 追评提交后触发差评预警检查 |
| 定时任务 | Crontab.php | 新增商品标签聚合统计任务 |
6.3 后台前端开发清单
| 页面 | 工作内容 |
|---|---|
| 商品评价 → 晒图审核 | 新增待审核图片列表,支持单张/批量通过和驳回,驳回时填写原因 |
| 商品评价 → 评价列表 | 增加追评内容展示列、标签列、审核状态筛选 |
| 商品管理 → 评价标签 | 新增标签管理页面(增删改查、启用/禁用、排序) |
| 通用设置 → 评价设置 | 增加追评配置、晒图审核配置、差评预警配置三个区块 |
| 商品评价 → 预警记录 | 新增预警记录列表,展示触发原因和处理状态 |
6.4 开发注意事项
二开规范方面:遵循 LikeShop 的"扩展优先"原则,追评、标签、预警逻辑分别封装在独立的 Service 中;评价列表接口的改造通过新增可选参数实现,不改变原有返回结构,保证前端兼容性。
数据一致性方面:追评写入时使用 is_append 字段做幂等校验,防止用户重复提交;标签使用次数的递增先走 Redis,定时同步到数据库,避免高并发下的行锁竞争。
预警防骚扰方面:晒图审核支持批量操作,后台列表按提交时间倒序排列,待审核数量在菜单栏以角标形式展示,避免审核积压。
性能优化方面:商品标签聚合结果缓存在 Redis 中,TTL 设为 1 天,每日凌晨由定时任务刷新;评价标签关联表建立 goods_id + tag_id 联合索引,加速按商品和标签的筛选查询。