|
|
@@ -1,4 +1,4 @@
|
|
|
-# 获取卖家交易评价列表接口文档
|
|
|
+# 获取卖家交易评价列表接口文档(分页版)
|
|
|
|
|
|
## 接口概述
|
|
|
|
|
|
@@ -6,16 +6,16 @@
|
|
|
|
|
|
| 项目 | 内容 |
|
|
|
|------|------|
|
|
|
-| 接口名称 | 获取用户作为卖家的交易评价列表 |
|
|
|
+| 接口名称 | 获取用户作为卖家的交易评价列表(分页) |
|
|
|
| 接口路径 | `/secondTradeRecord/getSellerEvaluationList` |
|
|
|
| 请求方式 | GET |
|
|
|
-| 接口版本 | v1.0 |
|
|
|
+| 接口版本 | v1.1(支持分页) |
|
|
|
| 开发时间 | 2025-11-18 |
|
|
|
| 接口状态 | ✅ 已完成 |
|
|
|
|
|
|
### 功能描述
|
|
|
|
|
|
-用于用户主页展示该用户作为卖家收到的所有交易评价,包含:
|
|
|
+用于用户主页展示该用户作为卖家收到的所有交易评价,**支持分页查询**,包含:
|
|
|
- 买家完整信息(昵称、头像、真实姓名、性别、简介等)
|
|
|
- 评价内容和评分
|
|
|
- 买家和卖家的信用积分
|
|
|
@@ -32,41 +32,81 @@
|
|
|
| 参数名 | 类型 | 必填 | 描述 | 示例值 | 默认值 |
|
|
|
|--------|------|------|------|--------|--------|
|
|
|
| sellerId | Integer | ✅ 是 | 卖家用户ID(主页用户ID) | 123 | - |
|
|
|
+| pageNum | Integer | ❌ 否 | 页码(从1开始) | 1 | 1 |
|
|
|
+| pageSize | Integer | ❌ 否 | 每页条数(最大100) | 10 | 10 |
|
|
|
+
|
|
|
+**参数说明:**
|
|
|
+- `pageNum`: 页码从1开始,传入小于1的值自动修正为1
|
|
|
+- `pageSize`: 每页条数,传入小于1的值自动修正为10,超过100自动限制为100
|
|
|
|
|
|
### 请求示例
|
|
|
|
|
|
#### cURL
|
|
|
|
|
|
```bash
|
|
|
+# 获取第1页,每页10条(使用默认值)
|
|
|
curl -X GET "http://localhost:8080/secondTradeRecord/getSellerEvaluationList?sellerId=123" \
|
|
|
-H "Authorization: Bearer your_token_here"
|
|
|
+
|
|
|
+# 获取第2页,每页20条
|
|
|
+curl -X GET "http://localhost:8080/secondTradeRecord/getSellerEvaluationList?sellerId=123&pageNum=2&pageSize=20" \
|
|
|
+ -H "Authorization: Bearer your_token_here"
|
|
|
```
|
|
|
|
|
|
#### JavaScript (Axios)
|
|
|
|
|
|
```javascript
|
|
|
+// 基本用法
|
|
|
axios.get('/secondTradeRecord/getSellerEvaluationList', {
|
|
|
params: {
|
|
|
- sellerId: 123
|
|
|
+ sellerId: 123,
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10
|
|
|
},
|
|
|
headers: {
|
|
|
'Authorization': 'Bearer your_token_here'
|
|
|
}
|
|
|
})
|
|
|
.then(response => {
|
|
|
- console.log(response.data);
|
|
|
+ const { records, total, pages, current, size } = response.data.data;
|
|
|
+ console.log(`当前第${current}页,每页${size}条`);
|
|
|
+ console.log(`共${total}条数据,${pages}页`);
|
|
|
+ console.log('评价列表:', records);
|
|
|
})
|
|
|
.catch(error => {
|
|
|
console.error(error);
|
|
|
});
|
|
|
+
|
|
|
+// 结合分页组件使用
|
|
|
+function loadEvaluations(sellerId, pageNum = 1, pageSize = 10) {
|
|
|
+ return axios.get('/secondTradeRecord/getSellerEvaluationList', {
|
|
|
+ params: { sellerId, pageNum, pageSize }
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+// 加载下一页
|
|
|
+function loadNextPage(currentPage) {
|
|
|
+ loadEvaluations(123, currentPage + 1, 10)
|
|
|
+ .then(response => {
|
|
|
+ // 处理数据
|
|
|
+ });
|
|
|
+}
|
|
|
```
|
|
|
|
|
|
#### Java (RestTemplate)
|
|
|
|
|
|
```java
|
|
|
-String url = "http://localhost:8080/secondTradeRecord/getSellerEvaluationList?sellerId=123";
|
|
|
+String url = "http://localhost:8080/secondTradeRecord/getSellerEvaluationList" +
|
|
|
+ "?sellerId=123&pageNum=1&pageSize=10";
|
|
|
RestTemplate restTemplate = new RestTemplate();
|
|
|
-R<List<SellerEvaluationVo>> result = restTemplate.getForObject(url, R.class);
|
|
|
+R<IPage<SellerEvaluationVo>> result = restTemplate.getForObject(url, R.class);
|
|
|
+
|
|
|
+IPage<SellerEvaluationVo> page = result.getData();
|
|
|
+System.out.println("总记录数:" + page.getTotal());
|
|
|
+System.out.println("总页数:" + page.getPages());
|
|
|
+System.out.println("当前页:" + page.getCurrent());
|
|
|
+System.out.println("每页条数:" + page.getSize());
|
|
|
+List<SellerEvaluationVo> records = page.getRecords();
|
|
|
```
|
|
|
|
|
|
---
|
|
|
@@ -79,7 +119,17 @@ R<List<SellerEvaluationVo>> result = restTemplate.getForObject(url, R.class);
|
|
|
|--------|------|------|
|
|
|
| code | Integer | 响应状态码,200表示成功 |
|
|
|
| msg | String | 响应消息 |
|
|
|
-| data | Array<SellerEvaluationVo> | 评价列表数据 |
|
|
|
+| data | IPage<SellerEvaluationVo> | 分页数据对象 |
|
|
|
+
|
|
|
+### IPage 分页对象结构
|
|
|
+
|
|
|
+| 字段名 | 类型 | 描述 | 示例值 |
|
|
|
+|--------|------|------|--------|
|
|
|
+| records | Array<SellerEvaluationVo> | 当前页的评价列表 | [...] |
|
|
|
+| total | Long | 总记录数 | 45 |
|
|
|
+| size | Long | 每页条数 | 10 |
|
|
|
+| current | Long | 当前页码 | 1 |
|
|
|
+| pages | Long | 总页数 | 5 |
|
|
|
|
|
|
### SellerEvaluationVo 对象结构
|
|
|
|
|
|
@@ -160,79 +210,52 @@ R<List<SellerEvaluationVo>> result = restTemplate.getForObject(url, R.class);
|
|
|
| D | 10-14次 | 严重违规 | 🔴 高风险 |
|
|
|
| E | ≥15次 | 极差 | 🔴 极高风险(最差) |
|
|
|
|
|
|
-**风控等级计算说明**:
|
|
|
-- 基于用户在 `second_risk_control_record` 表中的违规记录数量
|
|
|
-- 违规类型包括:洗钱嫌疑、账号异常、交易欺诈、异常发布
|
|
|
-- 等级实时计算,反映用户最新的信用状态
|
|
|
-
|
|
|
---
|
|
|
|
|
|
## 响应示例
|
|
|
|
|
|
-### 成功响应
|
|
|
+### 成功响应(有数据)
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"code": 200,
|
|
|
"msg": "成功",
|
|
|
- "data": [
|
|
|
- {
|
|
|
- "id": 1,
|
|
|
- "tradeNo": "S2025111800001",
|
|
|
- "goodsId": 100,
|
|
|
- "goodsTitle": "二手iPhone 13 128G 白色 95新",
|
|
|
- "goodsHomeImage": "https://cdn.example.com/goods/iphone13.jpg",
|
|
|
- "goodsPrice": 3999.00,
|
|
|
- "transactionAmount": 3800.00,
|
|
|
- "buyerId": 456,
|
|
|
- "buyerName": "张三",
|
|
|
- "buyerImage": "https://cdn.example.com/avatar/user456.jpg",
|
|
|
- "buyerPhone": "13800138000",
|
|
|
- "buyerRealName": "张三三",
|
|
|
- "buyerSex": "男",
|
|
|
- "buyerJianjie": "热爱数码产品,诚信交易",
|
|
|
- "buyerEvaluate": "商品质量很好,和描述一致,卖家态度也不错,推荐!",
|
|
|
- "buyerRating": 5,
|
|
|
- "buyerCreditScore": 450,
|
|
|
- "buyerCreditLevel": "A",
|
|
|
- "sellerId": 123,
|
|
|
- "sellerName": "李四",
|
|
|
- "sellerImage": "https://cdn.example.com/avatar/user123.jpg",
|
|
|
- "sellerCreditScore": 500,
|
|
|
- "sellerCreditLevel": "O",
|
|
|
- "transactionTime": "2025-11-18 10:00:00",
|
|
|
- "tradeStatus": 4,
|
|
|
- "createdTime": "2025-11-18 09:00:00"
|
|
|
- },
|
|
|
- {
|
|
|
- "id": 2,
|
|
|
- "tradeNo": "S2025111700025",
|
|
|
- "goodsId": 101,
|
|
|
- "goodsTitle": "小米手环7 黑色 全新未拆封",
|
|
|
- "goodsHomeImage": "https://cdn.example.com/goods/miband7.jpg",
|
|
|
- "goodsPrice": 199.00,
|
|
|
- "transactionAmount": 180.00,
|
|
|
- "buyerId": 789,
|
|
|
- "buyerName": "王五",
|
|
|
- "buyerImage": "https://cdn.example.com/avatar/user789.jpg",
|
|
|
- "buyerPhone": "13900139000",
|
|
|
- "buyerRealName": "王小五",
|
|
|
- "buyerSex": "女",
|
|
|
- "buyerJianjie": "喜欢运动健身",
|
|
|
- "buyerEvaluate": "东西不错,发货速度快",
|
|
|
- "buyerRating": 4,
|
|
|
- "buyerCreditScore": 380,
|
|
|
- "buyerCreditLevel": "O",
|
|
|
- "sellerId": 123,
|
|
|
- "sellerName": "李四",
|
|
|
- "sellerImage": "https://cdn.example.com/avatar/user123.jpg",
|
|
|
- "sellerCreditScore": 500,
|
|
|
- "sellerCreditLevel": "O",
|
|
|
- "transactionTime": "2025-11-17 15:30:00",
|
|
|
- "tradeStatus": 4,
|
|
|
- "createdTime": "2025-11-17 14:20:00"
|
|
|
- }
|
|
|
- ]
|
|
|
+ "data": {
|
|
|
+ "records": [
|
|
|
+ {
|
|
|
+ "id": 1,
|
|
|
+ "tradeNo": "S2025111800001",
|
|
|
+ "goodsId": 100,
|
|
|
+ "goodsTitle": "二手iPhone 13 128G 白色 95新",
|
|
|
+ "goodsHomeImage": "https://cdn.example.com/goods/iphone13.jpg",
|
|
|
+ "goodsPrice": 3999.00,
|
|
|
+ "transactionAmount": 3800.00,
|
|
|
+ "buyerId": 456,
|
|
|
+ "buyerName": "张三",
|
|
|
+ "buyerImage": "https://cdn.example.com/avatar/user456.jpg",
|
|
|
+ "buyerPhone": "13800138000",
|
|
|
+ "buyerRealName": "张三三",
|
|
|
+ "buyerSex": "男",
|
|
|
+ "buyerJianjie": "热爱数码产品,诚信交易",
|
|
|
+ "buyerEvaluate": "商品质量很好,和描述一致,卖家态度也不错,推荐!",
|
|
|
+ "buyerRating": 5,
|
|
|
+ "buyerCreditScore": 450,
|
|
|
+ "buyerCreditLevel": "A",
|
|
|
+ "sellerId": 123,
|
|
|
+ "sellerName": "李四",
|
|
|
+ "sellerImage": "https://cdn.example.com/avatar/user123.jpg",
|
|
|
+ "sellerCreditScore": 500,
|
|
|
+ "sellerCreditLevel": "O",
|
|
|
+ "transactionTime": "2025-11-18 10:00:00",
|
|
|
+ "tradeStatus": 4,
|
|
|
+ "createdTime": "2025-11-18 09:00:00"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "total": 45,
|
|
|
+ "size": 10,
|
|
|
+ "current": 1,
|
|
|
+ "pages": 5
|
|
|
+ }
|
|
|
}
|
|
|
```
|
|
|
|
|
|
@@ -242,7 +265,13 @@ R<List<SellerEvaluationVo>> result = restTemplate.getForObject(url, R.class);
|
|
|
{
|
|
|
"code": 200,
|
|
|
"msg": "成功",
|
|
|
- "data": []
|
|
|
+ "data": {
|
|
|
+ "records": [],
|
|
|
+ "total": 0,
|
|
|
+ "size": 10,
|
|
|
+ "current": 1,
|
|
|
+ "pages": 0
|
|
|
+ }
|
|
|
}
|
|
|
```
|
|
|
|
|
|
@@ -270,6 +299,29 @@ R<List<SellerEvaluationVo>> result = restTemplate.getForObject(url, R.class);
|
|
|
|
|
|
---
|
|
|
|
|
|
+## 分页计算说明
|
|
|
+
|
|
|
+### 分页参数
|
|
|
+
|
|
|
+- **pageNum**: 当前页码,从 1 开始
|
|
|
+- **pageSize**: 每页显示条数
|
|
|
+- **total**: 总记录数
|
|
|
+- **pages**: 总页数 = Math.ceil(total / pageSize)
|
|
|
+
|
|
|
+### 分页示例
|
|
|
+
|
|
|
+假设某卖家有 45 条评价:
|
|
|
+
|
|
|
+| pageNum | pageSize | 返回记录 | 页面显示 |
|
|
|
+|---------|----------|----------|----------|
|
|
|
+| 1 | 10 | 1-10 | 第1页,共5页,显示10条 |
|
|
|
+| 2 | 10 | 11-20 | 第2页,共5页,显示10条 |
|
|
|
+| 3 | 10 | 21-30 | 第3页,共5页,显示10条 |
|
|
|
+| 4 | 10 | 31-40 | 第4页,共5页,显示10条 |
|
|
|
+| 5 | 10 | 41-45 | 第5页,共5页,显示5条 |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
## 业务规则
|
|
|
|
|
|
### 查询规则
|
|
|
@@ -279,29 +331,23 @@ R<List<SellerEvaluationVo>> result = restTemplate.getForObject(url, R.class);
|
|
|
- ✅ 只返回未删除的记录(`delete_flag = 0`)
|
|
|
- ✅ 按创建时间倒序排列(最新评价在前)
|
|
|
|
|
|
-2. **数据关联**
|
|
|
+2. **分页限制**
|
|
|
+ - ✅ pageNum < 1 时自动修正为 1
|
|
|
+ - ✅ pageSize < 1 时自动修正为 10
|
|
|
+ - ✅ pageSize > 100 时自动限制为 100(防止数据量过大)
|
|
|
+
|
|
|
+3. **数据关联**
|
|
|
- 商品信息:通过 `goods_record_id` 关联 `second_goods_record` 表
|
|
|
- 买家信息:通过 `buyer_id` 关联 `life_user` 表
|
|
|
- 卖家信息:通过 `seller_id` 关联 `life_user` 表
|
|
|
- 信用积分:通过 `user_id` 关联 `second_user_credit` 表
|
|
|
- 风控等级:实时计算,查询 `second_risk_control_record` 表
|
|
|
|
|
|
-3. **性能优化**
|
|
|
- - 使用批量查询避免 N+1 问题
|
|
|
+4. **性能优化**
|
|
|
+ - 使用 MyBatis Plus 分页插件
|
|
|
+ - 批量查询避免 N+1 问题
|
|
|
- 关联数据使用 Map 索引,时间复杂度 O(1)
|
|
|
- - 分组查询:商品、用户、积分分别批量获取
|
|
|
-
|
|
|
-### 数据完整性
|
|
|
-
|
|
|
-1. **字段缺失处理**
|
|
|
- - 如果商品记录已删除,商品相关字段返回 `null`
|
|
|
- - 如果用户信息不存在,用户相关字段返回 `null`
|
|
|
- - 如果没有信用积分记录,`creditScore` 返回 `0`
|
|
|
- - 风控等级计算异常时返回默认等级 `"O"`
|
|
|
-
|
|
|
-2. **评分有效性**
|
|
|
- - 买家评分 `buyerRating` 有效范围:1-5 分
|
|
|
- - 如果未评分,该字段可能为 `null`
|
|
|
+ - 只查询当前页的交易记录,再批量关联其他信息
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -316,109 +362,171 @@ R<List<SellerEvaluationVo>> result = restTemplate.getForObject(url, R.class);
|
|
|
|
|
|
---
|
|
|
|
|
|
-## 使用场景
|
|
|
-
|
|
|
-### 1. 用户主页展示
|
|
|
-
|
|
|
-在用户主页展示该用户作为卖家的历史交易评价,供其他用户查看。
|
|
|
-
|
|
|
-**场景示例**:
|
|
|
-- 用户 A 点击用户 B 的主页
|
|
|
-- 展示用户 B 作为卖家收到的所有评价
|
|
|
-- 显示评价内容、评分、买家信息等
|
|
|
-
|
|
|
-### 2. 信用评估
|
|
|
-
|
|
|
-通过评价内容、评分和风控等级综合评估卖家的信誉度。
|
|
|
-
|
|
|
-**评估维度**:
|
|
|
-- 评价数量:评价越多,信誉参考价值越高
|
|
|
-- 平均评分:反映买家满意度
|
|
|
-- 风控等级:反映用户违规情况
|
|
|
-- 信用积分:反映用户交易活跃度
|
|
|
-
|
|
|
-### 3. 交易决策辅助
|
|
|
-
|
|
|
-买家在决定是否与某卖家交易时,可以查看其历史评价。
|
|
|
-
|
|
|
-**决策参考**:
|
|
|
-- 查看近期评价内容
|
|
|
-- 查看风控评分等级
|
|
|
-- 了解卖家信用积分
|
|
|
-- 参考其他买家的评价
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-## 技术实现
|
|
|
-
|
|
|
-### 实现方式
|
|
|
-
|
|
|
-| 技术点 | 实现方案 |
|
|
|
-|--------|----------|
|
|
|
-| 查询方式 | MyBatis Plus LambdaQueryWrapper |
|
|
|
-| 关联查询 | 批量查询 + Map 索引 |
|
|
|
-| 风控计算 | 实时查询违规记录并计算等级 |
|
|
|
-| 事务处理 | 无需事务(只读查询) |
|
|
|
-| 性能优化 | 批量查询避免 N+1 问题 |
|
|
|
-
|
|
|
-### 代码示例
|
|
|
-
|
|
|
-```java
|
|
|
-// 1. 查询交易记录
|
|
|
-LambdaQueryWrapper<SecondTradeRecord> tradeWrapper = new LambdaQueryWrapper<>();
|
|
|
-tradeWrapper.eq(SecondTradeRecord::getSellerId, sellerId)
|
|
|
- .eq(SecondTradeRecord::getDeleteFlag, 0)
|
|
|
- .isNotNull(SecondTradeRecord::getBuyerEvaluate)
|
|
|
- .ne(SecondTradeRecord::getBuyerEvaluate, "")
|
|
|
- .orderByDesc(SecondTradeRecord::getCreatedTime);
|
|
|
-List<SecondTradeRecord> tradeRecords = mapper.selectList(tradeWrapper);
|
|
|
-
|
|
|
-// 2. 批量查询商品信息
|
|
|
-LambdaQueryWrapper<SecondGoodsRecord> goodsWrapper = new LambdaQueryWrapper<>();
|
|
|
-goodsWrapper.in(SecondGoodsRecord::getId, goodsRecordIds);
|
|
|
-List<SecondGoodsRecord> goodsRecords = goodsRecordMapper.selectList(goodsWrapper);
|
|
|
-
|
|
|
-// 3. 批量查询用户信息
|
|
|
-LambdaQueryWrapper<LifeUser> userWrapper = new LambdaQueryWrapper<>();
|
|
|
-userWrapper.in(LifeUser::getId, buyerIds).eq(LifeUser::getDeleteFlag, 0);
|
|
|
-List<LifeUser> buyers = lifeUserMapper.selectList(userWrapper);
|
|
|
-
|
|
|
-// 4. 批量查询信用积分
|
|
|
-LambdaQueryWrapper<SecondUserCredit> creditWrapper = new LambdaQueryWrapper<>();
|
|
|
-creditWrapper.in(SecondUserCredit::getUserId, allUserIds)
|
|
|
- .eq(SecondUserCredit::getDeleteFlag, 0);
|
|
|
-List<SecondUserCredit> credits = secondUserCreditMapper.selectList(creditWrapper);
|
|
|
-
|
|
|
-// 5. 计算风控等级
|
|
|
-String creditLevel = calculateCreditLevel(userId);
|
|
|
+## 前端分页实现示例
|
|
|
+
|
|
|
+### Vue 3 + Element Plus
|
|
|
+
|
|
|
+```vue
|
|
|
+<template>
|
|
|
+ <div class="evaluation-list">
|
|
|
+ <!-- 评价列表 -->
|
|
|
+ <div v-for="item in evaluations" :key="item.id" class="evaluation-item">
|
|
|
+ <div class="buyer-info">
|
|
|
+ <img :src="item.buyerImage" :alt="item.buyerName" />
|
|
|
+ <span>{{ item.buyerName }}</span>
|
|
|
+ <el-rate :model-value="item.buyerRating" disabled />
|
|
|
+ </div>
|
|
|
+ <div class="evaluation-content">{{ item.buyerEvaluate }}</div>
|
|
|
+ <div class="evaluation-time">{{ item.createdTime }}</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 分页组件 -->
|
|
|
+ <el-pagination
|
|
|
+ v-model:current-page="pageNum"
|
|
|
+ v-model:page-size="pageSize"
|
|
|
+ :page-sizes="[10, 20, 50, 100]"
|
|
|
+ :total="total"
|
|
|
+ layout="total, sizes, prev, pager, next, jumper"
|
|
|
+ @size-change="handleSizeChange"
|
|
|
+ @current-change="handleCurrentChange"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { ref, onMounted } from 'vue';
|
|
|
+import axios from 'axios';
|
|
|
+
|
|
|
+const sellerId = ref(123);
|
|
|
+const pageNum = ref(1);
|
|
|
+const pageSize = ref(10);
|
|
|
+const total = ref(0);
|
|
|
+const evaluations = ref([]);
|
|
|
+
|
|
|
+// 加载评价列表
|
|
|
+const loadEvaluations = async () => {
|
|
|
+ try {
|
|
|
+ const response = await axios.get('/secondTradeRecord/getSellerEvaluationList', {
|
|
|
+ params: {
|
|
|
+ sellerId: sellerId.value,
|
|
|
+ pageNum: pageNum.value,
|
|
|
+ pageSize: pageSize.value
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ const { records, total: totalCount } = response.data.data;
|
|
|
+ evaluations.value = records;
|
|
|
+ total.value = totalCount;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载失败:', error);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 每页条数改变
|
|
|
+const handleSizeChange = (newSize) => {
|
|
|
+ pageSize.value = newSize;
|
|
|
+ pageNum.value = 1; // 重置到第一页
|
|
|
+ loadEvaluations();
|
|
|
+};
|
|
|
+
|
|
|
+// 当前页改变
|
|
|
+const handleCurrentChange = (newPage) => {
|
|
|
+ pageNum.value = newPage;
|
|
|
+ loadEvaluations();
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ loadEvaluations();
|
|
|
+});
|
|
|
+</script>
|
|
|
```
|
|
|
|
|
|
-### 数据库表
|
|
|
-
|
|
|
-| 表名 | 说明 | 关联字段 |
|
|
|
-|------|------|----------|
|
|
|
-| second_trade_record | 交易记录表 | 主表 |
|
|
|
-| second_goods_record | 商品记录表 | goods_record_id |
|
|
|
-| life_user | 用户表 | buyer_id, seller_id |
|
|
|
-| second_user_credit | 用户信用积分表 | user_id |
|
|
|
-| second_risk_control_record | 风控记录表 | user_id(用于计算等级) |
|
|
|
-
|
|
|
-### 相关文件
|
|
|
-
|
|
|
-1. **Controller**
|
|
|
- `alien-second/src/main/java/shop/alien/second/controller/SecondTradeRecordController.java`
|
|
|
-
|
|
|
-2. **Service Interface**
|
|
|
- `alien-second/src/main/java/shop/alien/second/service/SecondTradeRecordService.java`
|
|
|
-
|
|
|
-3. **Service Implementation**
|
|
|
- `alien-second/src/main/java/shop/alien/second/service/impl/SecondTradeRecordServiceImpl.java`
|
|
|
-
|
|
|
-4. **VO Object**
|
|
|
- `alien-entity/src/main/java/shop/alien/entity/second/vo/SellerEvaluationVo.java`
|
|
|
+### React + Ant Design
|
|
|
+
|
|
|
+```jsx
|
|
|
+import React, { useState, useEffect } from 'react';
|
|
|
+import { Pagination, Rate, Avatar, List } from 'antd';
|
|
|
+import axios from 'axios';
|
|
|
+
|
|
|
+function SellerEvaluationList({ sellerId }) {
|
|
|
+ const [pageNum, setPageNum] = useState(1);
|
|
|
+ const [pageSize, setPageSize] = useState(10);
|
|
|
+ const [total, setTotal] = useState(0);
|
|
|
+ const [evaluations, setEvaluations] = useState([]);
|
|
|
+ const [loading, setLoading] = useState(false);
|
|
|
+
|
|
|
+ // 加载评价列表
|
|
|
+ const loadEvaluations = async () => {
|
|
|
+ setLoading(true);
|
|
|
+ try {
|
|
|
+ const response = await axios.get('/secondTradeRecord/getSellerEvaluationList', {
|
|
|
+ params: { sellerId, pageNum, pageSize }
|
|
|
+ });
|
|
|
+
|
|
|
+ const { records, total: totalCount } = response.data.data;
|
|
|
+ setEvaluations(records);
|
|
|
+ setTotal(totalCount);
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载失败:', error);
|
|
|
+ } finally {
|
|
|
+ setLoading(false);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ loadEvaluations();
|
|
|
+ }, [pageNum, pageSize, sellerId]);
|
|
|
+
|
|
|
+ // 页码改变
|
|
|
+ const handlePageChange = (page, size) => {
|
|
|
+ setPageNum(page);
|
|
|
+ setPageSize(size);
|
|
|
+ };
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ <List
|
|
|
+ loading={loading}
|
|
|
+ dataSource={evaluations}
|
|
|
+ renderItem={(item) => (
|
|
|
+ <List.Item>
|
|
|
+ <List.Item.Meta
|
|
|
+ avatar={<Avatar src={item.buyerImage} />}
|
|
|
+ title={
|
|
|
+ <div>
|
|
|
+ <span>{item.buyerName}</span>
|
|
|
+ <Rate value={item.buyerRating} disabled style={{ marginLeft: 10 }} />
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ description={
|
|
|
+ <>
|
|
|
+ <div>{item.buyerEvaluate}</div>
|
|
|
+ <div style={{ color: '#999', fontSize: 12, marginTop: 5 }}>
|
|
|
+ {item.createdTime}
|
|
|
+ </div>
|
|
|
+ </>
|
|
|
+ }
|
|
|
+ />
|
|
|
+ </List.Item>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+
|
|
|
+ <Pagination
|
|
|
+ current={pageNum}
|
|
|
+ pageSize={pageSize}
|
|
|
+ total={total}
|
|
|
+ onChange={handlePageChange}
|
|
|
+ showSizeChanger
|
|
|
+ showQuickJumper
|
|
|
+ showTotal={(total) => `共 ${total} 条`}
|
|
|
+ pageSizeOptions={[10, 20, 50, 100]}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|
|
|
|
|
|
-5. **Entity**
|
|
|
- `alien-entity/src/main/java/shop/alien/entity/second/SecondTradeRecord.java`
|
|
|
+export default SellerEvaluationList;
|
|
|
+```
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -430,120 +538,34 @@ String creditLevel = calculateCreditLevel(userId);
|
|
|
- `sellerId` 为必传参数,用于查看指定用户的评价列表
|
|
|
- 不传或传空会返回错误:`"卖家ID不能为空"`
|
|
|
|
|
|
-2. **数据权限**
|
|
|
+2. **分页参数**
|
|
|
+ - `pageNum` 和 `pageSize` 为可选参数,有默认值
|
|
|
+ - pageSize 最大值限制为 100,超过自动修正
|
|
|
+
|
|
|
+3. **数据权限**
|
|
|
- 接口不做权限校验,任何人都可以查看用户主页的公开评价
|
|
|
- 如需增加权限控制,请联系后端开发人员
|
|
|
|
|
|
-3. **空数据处理**
|
|
|
+4. **空数据处理**
|
|
|
- 如果该用户没有收到任何评价,返回空数组 `[]`
|
|
|
- - 不会返回 `null`,前端可以直接遍历
|
|
|
+ - total 为 0,pages 为 0
|
|
|
+ - 前端需要处理空状态展示
|
|
|
|
|
|
-4. **字段可空性**
|
|
|
+5. **字段可空性**
|
|
|
- 部分字段可能为 `null`,前端需要做空值判断
|
|
|
- 建议使用可选链操作符或空值合并运算符
|
|
|
|
|
|
-5. **评分范围**
|
|
|
- - 买家评分 `buyerRating` 的有效范围是 1-5 分
|
|
|
- - 超出范围的数据为异常数据
|
|
|
-
|
|
|
-### 💡 最佳实践
|
|
|
-
|
|
|
-1. **前端展示建议**
|
|
|
- ```javascript
|
|
|
- // 显示买家昵称,为空时显示默认值
|
|
|
- const buyerName = evaluation.buyerName || '匿名用户';
|
|
|
-
|
|
|
- // 显示评分,为空时显示未评分
|
|
|
- const rating = evaluation.buyerRating ? `${evaluation.buyerRating}分` : '未评分';
|
|
|
-
|
|
|
- // 风控等级颜色映射
|
|
|
- const levelColorMap = {
|
|
|
- 'O': 'green', // 最好
|
|
|
- 'A': 'green',
|
|
|
- 'B': 'orange',
|
|
|
- 'C': 'orange',
|
|
|
- 'D': 'red',
|
|
|
- 'E': 'red' // 最差
|
|
|
- };
|
|
|
- ```
|
|
|
-
|
|
|
-2. **缓存策略**
|
|
|
- - 评价数据变化频率低,建议前端缓存
|
|
|
- - 缓存时间:5-10 分钟
|
|
|
- - 用户主动刷新时清除缓存
|
|
|
-
|
|
|
-3. **分页处理**
|
|
|
- - 当前接口返回所有评价
|
|
|
- - 如评价数量过多,建议前端实现虚拟滚动
|
|
|
- - 或联系后端增加分页功能
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-## 测试用例
|
|
|
-
|
|
|
-### 测试场景 1:正常查询
|
|
|
-
|
|
|
-**测试步骤**:
|
|
|
-1. 准备测试数据:用户 ID 为 123 的卖家有 3 条评价记录
|
|
|
-2. 调用接口:`GET /secondTradeRecord/getSellerEvaluationList?sellerId=123`
|
|
|
-3. 验证响应:返回 3 条评价数据
|
|
|
-
|
|
|
-**期望结果**:
|
|
|
-- 返回 code = 200
|
|
|
-- data 数组长度为 3
|
|
|
-- 每条数据包含完整字段
|
|
|
-- 按创建时间倒序排列
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-### 测试场景 2:无评价数据
|
|
|
-
|
|
|
-**测试步骤**:
|
|
|
-1. 准备测试数据:用户 ID 为 999 的卖家没有任何评价
|
|
|
-2. 调用接口:`GET /secondTradeRecord/getSellerEvaluationList?sellerId=999`
|
|
|
-3. 验证响应:返回空数组
|
|
|
-
|
|
|
-**期望结果**:
|
|
|
-- 返回 code = 200
|
|
|
-- data 为空数组 `[]`
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-### 测试场景 3:参数缺失
|
|
|
-
|
|
|
-**测试步骤**:
|
|
|
-1. 调用接口:`GET /secondTradeRecord/getSellerEvaluationList`(不传 sellerId)
|
|
|
-2. 验证响应:返回错误信息
|
|
|
-
|
|
|
-**期望结果**:
|
|
|
-- 返回 code = 500
|
|
|
-- msg = "获取评价列表失败: 卖家ID不能为空"
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-### 测试场景 4:风控等级计算
|
|
|
-
|
|
|
-**测试步骤**:
|
|
|
-1. 准备测试数据:
|
|
|
- - 买家 A(ID 456)有 2 条违规记录
|
|
|
- - 买家 B(ID 789)有 0 条违规记录
|
|
|
-2. 调用接口查询包含这两位买家的评价
|
|
|
-3. 验证风控等级
|
|
|
-
|
|
|
-**期望结果**:
|
|
|
-- 买家 A 的 `buyerCreditLevel` = "A"
|
|
|
-- 买家 B 的 `buyerCreditLevel` = "O"
|
|
|
-
|
|
|
---
|
|
|
|
|
|
## 版本历史
|
|
|
|
|
|
| 版本 | 日期 | 修改内容 | 修改人 |
|
|
|
|------|------|----------|--------|
|
|
|
-| v1.0 | 2025-11-18 | 初始版本,实现基本功能 | 开发团队 |
|
|
|
-| v1.0 | 2025-11-18 | 优化查询方式,使用 MyBatis Plus | 开发团队 |
|
|
|
-| v1.0 | 2025-11-18 | 增加风控评分等级计算 | 开发团队 |
|
|
|
-| v1.0 | 2025-11-18 | 增加更多用户信息字段 | 开发团队 |
|
|
|
+| v1.0 | 2025-11-18 | 初始版本,返回全部数据 | 开发团队 |
|
|
|
+| v1.1 | 2025-11-18 | **新增分页功能** | 开发团队 |
|
|
|
+| v1.1 | 2025-11-18 | 优化查询方式,使用 MyBatis Plus | 开发团队 |
|
|
|
+| v1.1 | 2025-11-18 | 增加风控评分等级计算 | 开发团队 |
|
|
|
+| v1.1 | 2025-11-18 | 增加更多用户信息字段 | 开发团队 |
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -557,5 +579,4 @@ String creditLevel = calculateCreditLevel(userId);
|
|
|
|
|
|
---
|
|
|
|
|
|
-*最后更新时间:2025-11-18*
|
|
|
-
|
|
|
+*最后更新时间:2025-11-18(v1.1 - 支持分页)*
|