# 评价管理 — 技术方案

> **依据：** 《评价管理功能需求.md》v1.0  
> **关联：** C 端《我的订单技术方案》v1.0、《商品详情内页技术方案》§2.4；平台《订单管理技术方案》v1.0.2、《关联需求分析.md》v1.6；商家《全部订单技术方案》v1.0.2、《商家端店铺上下文接口说明》  
> **范围：** 商家端 **商品管理 · 评价管理** — 当前店铺评价列表、详情、**商家回复**、**逻辑删除**；**不含** C 端发表评价（已有 `OrderReviewAppController`）、平台全站审核。  
> **菜单：** 挂载 **商品管理** 一级模块（与商品列表、店铺商品分类并列）。  
> **原则：** **无新建表**；读写 **`biz_goods_review`**；`shop_id = X-Shop-Id` 隔离；C 端只读 **`show_flag=1 AND del_flag=0`**；一行一评 **`uk_order_item_review`**。

---

## 1. 技术架构

| 项 | 选型 |
|----|------|
| 基础框架 | RuoYi **v3.9.2**（`springboot2` 分支） |
| 数据库 | **MySQL 5.7.39** |
| ORM / 权限 / 响应 | MyBatis；`@PreAuthorize`；`AjaxResult` / `TableDataInfo` |
| 店铺上下文 | `SellerShopContextInterceptor` + **`X-Shop-Id`** |
| 日志 | 回复、删除 `@Log` |
| 晒图 | 存 `review_pics` 逗号分隔 URL；VO 转 `List<String>` |

### 1.1 与 C 端 / 平台边界

| 维度 | C 端 · 我的订单 | **商家 · 评价管理** | C 端 · 商品详情 |
|------|-----------------|---------------------|-----------------|
| 路径 | `/api/order/**/review` | **`/agri/seller/review`** | `/api/goods/{id}/reviews` |
| 写评价 | **POST 提交**（已实现） | **无** | **无** |
| 读评价 | 按订单查 | 按 **shop** 列表 | 按 **goods_id** 分页 |
| 回复/删除 | **无** | **本模块** | **只读** 展示回复 |
| 表 | `biz_goods_review` | **同表** | **同表** |

### 1.2 协作链

```text
【C 端】订单已完成 → POST /api/order/{orderId}/review → INSERT biz_goods_review
            ↓
【登录】GET /agri/seller/context → X-Shop-Id
            ↓
【本模块】GET  /agri/seller/review/list
           GET  /agri/seller/review/{reviewId}
           PUT  /agri/seller/review/{reviewId}/reply
           DELETE /agri/seller/review/{reviewId}
            ↓
【C 端】GET /api/goods/{goodsId}/reviews → show_flag=1 AND del_flag=0
```

| 关联模块 | 协作 |
|----------|------|
| **C 端 · 我的订单** | `OrderReviewAppServiceImpl.insert` 写入；**一行一评**（`orderItemId`） |
| **C 端 · 商品详情** | `GoodsAppServiceImpl.listReviews` 只读；**GD14** |
| **商家 · 全部订单** | 详情 **只读** 关联 `order_no`（JOIN `biz_order`） |
| **订单模块** | 提交评价前置：订单 **status=3** |

### 1.3 模块落位

**文档目录：** `doc/店铺后台/商品管理/评价管理/`  
**菜单归属：** **商品管理** → 评价管理  
**后端包路径：** `com.ruoyi.web.modules.order`（评价数据由 **订单完成链** 写入，与 C 端 `OrderReviewAppController` **同包**；**不** 拆至 `goods` 包）

```text
baqing-shop/src/main/java/com/ruoyi/web/modules/order/
├── controller/
│   ├── OrderReviewAppController.java     # C 端 /api/order/**/review（已有）
│   └── SellerReviewController.java       # 商家 /agri/seller/review（待建）
├── service/
│   ├── IOrderReviewAppService.java       # C 端提交/列表（已有）
│   ├── ISellerReviewService.java         # 商家列表/详情/回复/删除（待建）
│   └── impl/
│       ├── OrderReviewAppServiceImpl.java
│       └── SellerReviewServiceImpl.java
├── domain/BizGoodsReview.java            # 已有 · +replyBy（待补）
├── dto/
│   ├── OrderReviewSubmitDTO.java         # C 端（已有）
│   ├── SellerReviewQuery.java            # 商家列表检索（待建）
│   └── SellerReviewReplyDTO.java         # 回复 Body（待建）
├── vo/
│   ├── OrderReviewDetailVO.java          # C 端（已有）
│   ├── SellerReviewListRowVO.java        # 商家列表行（待建）
│   └── SellerReviewDetailVO.java         # 商家详情（待建）
├── mapper/BizGoodsReviewMapper.java      # 扩展商家 SQL（待建）
└── support/OrderAppSupport.java          # splitPics/joinPics（已有）

baqing-shop/src/main/java/com/ruoyi/web/modules/home/
└── service/impl/GoodsAppServiceImpl.java # C 端按 goods_id 读（待接 Mapper）

sql/biz_goods_review.sql                  # 权威 DDL
```

**前端（建议）：**

| 项 | 值 |
|----|-----|
| 页面 | `ruoyi-ui/src/views/agri/seller/review/index.vue` |
| API | `ruoyi-ui/src/api/agri/seller/review.js` |

---

## 2. 数据库设计

**无新建表。** 权威 DDL：[`sql/biz_goods_review.sql`](../../../../sql/biz_goods_review.sql)。

### 2.1 表 `biz_goods_review`

| 字段 | 商家列表 | 商家详情 | C 端读 | 商家写 | 说明 |
|------|:--------:|:--------:|:------:|:------:|------|
| review_id | ✓ | ✓ | ✓ | — | PK |
| shop_id | 过滤 | ✓ | — | — | **= X-Shop-Id** |
| goods_id | ✓ | ✓ | ✓ | — | C 端按商品分页 |
| order_item_id | — | ✓ | — | — | **uk_order_item_review** |
| order_item_id | — | ✓ | — | — | JOIN 明细取 **goods_name** |
| member_id | — | — | — | — | |
| member_nick_name | ✓ | ✓ | ✓ | — | 提交快照 |
| member_avatar | — | ✓ | ✓ | — | |
| content | ✓ | ✓ | ✓ | — | |
| score | ✓ | ✓ | ✓ | — | 1～5 |
| review_pics | ✓ | ✓ | ✓ | — | 逗号分隔 URL |
| goods_main_pic | ✓ | ✓ | ✓ | — | 评价对象主图快照 |
| goods_spec | ✓ | ✓ | ✓ | — | 规格快照 |
| reply_content | 推导状态 | ✓ | ✓ | **PUT reply** | null=未回复 |
| reply_by | — | ✓ | — | reply | 商家账号 |
| reply_time | — | ✓ | ✓ | reply | |
| show_flag | — | — | 须 `1` | — | 本期商家 **不改** |
| del_flag | 须 `0` | 须 `0` | 须 `0` | **DELETE→`2`** | 逻辑删除 |
| create_time | ✓ | ✓ | 排序 | — | |
| update_time | — | ✓ | — | reply/delete | |

> **goods_name：** DDL **无** 独立快照列；列表/详情 **JOIN** `biz_order_item`（`order_item_id`）或 `biz_order_item` 首行取 `goods_name`（RM4 功能口径）。

### 2.2 枚举

| 字段 | 值 | 含义 |
|------|-----|------|
| del_flag | `0` | 正常 |
| del_flag | `2` | **逻辑删除**（商家 DELETE） |
| show_flag | `1` | C 端可见（默认） |
| show_flag | `0` | 屏蔽（**非本期** 平台/商家） |

**回复状态（Query 推导，不落库）：**

| replyStatus | SQL |
|-------------|-----|
| `UNREPLIED` | `reply_content IS NULL OR reply_content = ''` |
| `REPLIED` | `reply_content IS NOT NULL AND reply_content <> ''` |

### 2.3 商家列表 SQL 条件

```sql
WHERE r.shop_id = #{shopId}
  AND r.del_flag = '0'
  -- replyStatus / score / member_nick_name / create_time / goodsName(JOIN)
ORDER BY r.create_time DESC, r.review_id DESC
```

**C 端商品评价（GD14）：**

```sql
WHERE goods_id = #{goodsId}
  AND show_flag = '1' AND del_flag = '0'
ORDER BY create_time DESC, review_id DESC
```

### 2.4 索引

| 索引 | 用途 |
|------|------|
| `idx_shop_list (shop_id, show_flag, del_flag, create_time)` | 商家列表 |
| `idx_goods_list (goods_id, show_flag, del_flag, create_time)` | C 端全部评价 |
| `uk_order_item_review (order_item_id)` | 一行一评 |
| `idx_order_id (order_id)` | 按订单查评价 |

---

## 3. 接口设计

**基路径：** `/agri/seller/review`  
**上下文：** **`X-Shop-Id`** 必填

### 3.1 接口一览

| 方法 | 路径 | 权限 | 说明 |
|------|------|------|------|
| GET | `/list` | `agri:seller:review:list` | 分页列表 |
| GET | `/{reviewId}` | `agri:seller:review:query` | 详情 |
| PUT | `/{reviewId}/reply` | `agri:seller:review:reply` | 新增/修改回复 |
| DELETE | `/{reviewId}` | `agri:seller:review:remove` | 逻辑删除 |

### 3.2 列表 `GET /list`

**Query（SellerReviewQuery）：**

| 参数 | 说明 |
|------|------|
| pageNum, pageSize | 分页 |
| replyStatus | 空=全部；`UNREPLIED` / `REPLIED` |
| score | 1～5 |
| goodsName | 模糊（JOIN item.goods_name） |
| memberNickName | 模糊 |
| beginTime, endTime | create_time 区间 |

**固定条件：** `shop_id = requireShopId()`；`del_flag = '0'`。

**rows（SellerReviewListRowVO）：**

| 字段 | 说明 |
|------|------|
| reviewId | |
| memberNickName | |
| goodsId, goodsName, goodsMainPic, goodsSpec | 评价对象 |
| content | 摘要可截断 |
| reviewPics | URL 数组 |
| score | |
| createTime | |
| replyStatus | `UNREPLIED` / `REPLIED` |
| replyContent | 已回复时有值（列表可选） |

### 3.3 详情 `GET /{reviewId}`

**前置：** `shop_id` 匹配且 `del_flag='0'`。

**data（SellerReviewDetailVO）：**

| 分区 | 字段 |
|------|------|
| review | reviewId, score, content, reviewPics[], createTime |
| target | goodsId, goodsName, goodsMainPic, goodsSpec |
| member | memberNickName, memberAvatar |
| order | orderId, orderNo（JOIN biz_order） |
| reply | replyContent, replyBy, replyTime |

### 3.4 回复 `PUT /{reviewId}/reply`

**Body（SellerReviewReplyDTO）：**

```json
{ "replyContent": "感谢您的认可，欢迎再次光临。" }
```

| 规则 | 实现 |
|------|------|
| 校验 | `replyContent` 非空；长度 ≤500 |
| 效果 | UPDATE `reply_content, reply_by, reply_time, update_time` |
| 重复 | **允许修改**已有回复（RM5） |

### 3.5 删除 `DELETE /{reviewId}`

**前置：** `shop_id` 匹配；`del_flag='0'`。

**效果：**

```text
UPDATE biz_goods_review SET del_flag='2', update_time=NOW()
WHERE review_id=? AND shop_id=?
```

- C 端 `listReviews` **不再返回**；
- **uk_order_item_review** 仍占用 → 买家 **不可** 再评同一明细行（RM3）。

### 3.6 业务错误 msg

| 场景 | msg |
|------|-----|
| 未选店铺 | 请先选择当前店铺 |
| 越权/不存在 | 评价不存在或无权访问 |
| 回复内容为空 | 请填写回复内容 |
| 已删除 | 评价不存在或无权访问 |

---

## 4. Service 分层

```text
SellerReviewController
    → ISellerReviewService
        → selectSellerList(shopId, query)
        → getSellerDetail(shopId, reviewId)
        → reply(shopId, reviewId, dto, operator)
        → deleteReview(shopId, reviewId, operator)
        → BizGoodsReviewMapper
        → BizOrderMapper.selectById（orderNo）

OrderReviewAppController（C 端 · 已有）
    → IOrderReviewAppService.submit → insert

GoodsAppController（C 端 · 待完善）
    → GoodsAppServiceImpl.listReviews → BizGoodsReviewMapper.selectByGoodsId
```

**归属校验：**

```java
BizGoodsReview row = reviewMapper.selectById(reviewId);
if (row == null || !shopId.equals(row.getShopId()) || !"0".equals(row.getDelFlag())) {
    throw new ServiceException("评价不存在或无权访问");
}
```

---

## 5. C 端接口协作（已有 / 待完善）

| 方法 | 路径 | 说明 | 状态 |
|------|------|------|------|
| POST | `/api/order/{orderId}/review` | 买家提交 | **已实现** |
| GET | `/api/order/{orderId}/review` | 按订单查看 | **已实现** |
| GET | `/api/order/review/list` | 待评/已评列表 | **已实现** |
| GET | `/api/goods/{goodsId}/reviews` | 商品全部评价 GD14 | **待接 Mapper** |

> 商家 **回复/删除** 后，C 端 **无需** 额外接口；读 SQL 自动过滤。

---

## 6. 菜单与权限

**上级菜单：** **商品管理**（与商品列表、店铺商品分类同级）

| 菜单 | 路由 | 组件 | 权限前缀 |
|------|------|------|----------|
| 评价管理 | `/agri/seller/review` | `agri/seller/review/index` | `agri:seller:review` |

> **说明：** 路由前缀 `/agri/seller/review` **不变**；仅 **菜单挂载位置** 由订单管理调整至 **商品管理**。

| 权限 | 说明 |
|------|------|
| `agri:seller:review:list` | 列表 |
| `agri:seller:review:query` | 详情 |
| `agri:seller:review:reply` | 回复 |
| `agri:seller:review:remove` | 删除 |

---

## 7. Mapper 扩展（待建）

| 方法 | 说明 |
|------|------|
| `selectById(Long reviewId)` | 详情/校验 |
| `selectSellerList(shopId, query)` | 商家分页 + JOIN item/order |
| `selectByGoodsId(goodsId)` | C 端 GD14 |
| `updateReply(review)` | 回复 |
| `updateDelFlag(reviewId, shopId)` | del_flag→2 |

---

## 8. 测试要点

| 类型 | 要点 |
|------|------|
| 单测 | shop 隔离；回复空校验；删除后 C 端 SQL 不可见 |
| 接口 | `SellerReviewControllerTest`：X-Shop-Id、越权、回复改内容 |
| 协作 | C 端 submit 后商家可见；delete 后 goods reviews 为空 |

---

## 9. 实现状态

| 项 | 状态 |
|----|------|
| DDL `sql/biz_goods_review.sql` | **已有** |
| `BizGoodsReviewMapper` 基础 CRUD | **部分**（insert/selectByOrderId） |
| C 端 `OrderReviewAppServiceImpl` | **已实现** |
| `SellerReviewController` / `ISellerReviewService` | **待建** |
| `GoodsAppServiceImpl.listReviews` | **桩**（返回空） |
| 商家菜单 SQL | **待配置** |
| 前端页 | **待实现** |

---

## 10. 需求规则映射

| 规则 | 实现 |
|------|------|
| RM1 | `shop_id` 全链路 |
| RM2 | 仅 `del_flag=0` 入列表 |
| RM3 | DELETE 不释放 uk_order_item_review |
| RM4 | 快照字段 + JOIN goods_name |
| RM5 | PUT reply 可覆盖 |
| RM6 | del_flag=2 |
| RM7 | ORDER BY create_time DESC |
| RM8 | SellerReviewQuery |
| RM12 | C 端读同表过滤 |

---

## 11. 非本期

| 项 | 说明 |
|----|------|
| show_flag 商家屏蔽 | 仅 delete |
| 平台全站审核 | 另册 |
| 批量回复/导出 | 非本期 |
| goods_name 冗余列 | 可选优化；首期 JOIN |

---

## 12. 修订记录

| 版本 | 说明 |
|------|------|
| **v1.2** | 对齐 C 端 **一行一评**（`uk_order_item_review`）；迁移脚本 `biz_goods_review_order_item_uk.sql` |
| **v1.0** | 首版；无新建表；`/agri/seller/review` 四接口；共用 biz_goods_review；对齐 C 端已建评价写入 |
| **v1.1** | 模块目录与菜单归属调整至 **商品管理**；后端仍落 `order` 包 |

---

*文档版本：v1.2 · 目录：`doc/店铺后台/商品管理/评价管理/` · DDL：[`sql/biz_goods_review.sql`](../../../../sql/biz_goods_review.sql)、[`sql/biz_goods_review_order_item_uk.sql`](../../../../sql/biz_goods_review_order_item_uk.sql) · 关联《评价管理功能需求.md》v1.2 · C 端见《我的订单技术方案.md》v1.2*
