Răsfoiți Sursa

feat(second): 新增卖家交易评价列表接口

- 在SecondTradeRecordController中新增获取卖家评价列表的GET接口
- 添加SellerEvaluationVo实体类用于封装评价数据
- 实现service层获取卖家评价列表的业务逻辑
-优化mapper层代码结构,移除不必要的导入
- 增加用户风控评分等级计算功能
- 完善异常处理和日志记录机制
wxd 4 săptămâni în urmă
părinte
comite
ac4283e6a7

+ 105 - 0
alien-entity/src/main/java/shop/alien/entity/second/vo/SellerEvaluationVo.java

@@ -0,0 +1,105 @@
+package shop.alien.entity.second.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * <p>
+ * 卖家交易评价VO
+ * </p>
+ *
+ * @author system
+ * @since 2025-11-18
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "SellerEvaluationVo对象", description = "卖家交易评价VO")
+public class SellerEvaluationVo {
+
+    @ApiModelProperty(value = "交易记录ID")
+    private Integer id;
+
+    @ApiModelProperty(value = "交易编号")
+    private String tradeNo;
+
+    @ApiModelProperty(value = "商品ID")
+    private Integer goodsId;
+
+    @ApiModelProperty(value = "商品标题")
+    private String goodsTitle;
+
+    @ApiModelProperty(value = "商品封面图片")
+    private String goodsHomeImage;
+
+    @ApiModelProperty(value = "商品价格")
+    private BigDecimal goodsPrice;
+
+    @ApiModelProperty(value = "交易金额")
+    private BigDecimal transactionAmount;
+
+    @ApiModelProperty(value = "买家ID")
+    private Integer buyerId;
+
+    @ApiModelProperty(value = "买家昵称")
+    private String buyerName;
+
+    @ApiModelProperty(value = "买家头像")
+    private String buyerImage;
+
+    @ApiModelProperty(value = "买家手机号")
+    private String buyerPhone;
+
+    @ApiModelProperty(value = "买家真实姓名")
+    private String buyerRealName;
+
+    @ApiModelProperty(value = "买家性别")
+    private String buyerSex;
+
+    @ApiModelProperty(value = "买家简介")
+    private String buyerJianjie;
+
+    @ApiModelProperty(value = "买家评价内容")
+    private String buyerEvaluate;
+
+    @ApiModelProperty(value = "买家评分(1-5分)")
+    private Integer buyerRating;
+
+    @ApiModelProperty(value = "买家信用积分")
+    private Integer buyerCreditScore;
+
+    @ApiModelProperty(value = "买家风控评分等级(A-E,O表示无记录)")
+    private String buyerCreditLevel;
+
+    @ApiModelProperty(value = "卖家ID")
+    private Integer sellerId;
+
+    @ApiModelProperty(value = "卖家昵称")
+    private String sellerName;
+
+    @ApiModelProperty(value = "卖家头像")
+    private String sellerImage;
+
+    @ApiModelProperty(value = "卖家信用积分")
+    private Integer sellerCreditScore;
+
+    @ApiModelProperty(value = "卖家风控评分等级(A-E,O表示无记录)")
+    private String sellerCreditLevel;
+
+    @ApiModelProperty(value = "交易时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date transactionTime;
+
+    @ApiModelProperty(value = "交易状态  1-待确认  2-已拒绝  3-待交易  4-交易成功  5-交易失败  6-交易取消")
+    private Integer tradeStatus;
+
+    @ApiModelProperty(value = "创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+}
+

+ 0 - 3
alien-entity/src/main/java/shop/alien/mapper/second/SecondTradeRecordMapper.java

@@ -6,15 +6,12 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Constants;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import shop.alien.entity.second.SecondTradeRecord;
 import shop.alien.entity.second.vo.SecondTradeRecordVo;
-import shop.alien.entity.store.vo.LifeFansVo;
 
-import java.util.Date;
 import java.util.List;
 
 /**

+ 561 - 0
alien-second/doc/获取卖家交易评价列表接口文档.md

@@ -0,0 +1,561 @@
+# 获取卖家交易评价列表接口文档
+
+## 接口概述
+
+### 接口信息
+
+| 项目 | 内容 |
+|------|------|
+| 接口名称 | 获取用户作为卖家的交易评价列表 |
+| 接口路径 | `/secondTradeRecord/getSellerEvaluationList` |
+| 请求方式 | GET |
+| 接口版本 | v1.0 |
+| 开发时间 | 2025-11-18 |
+| 接口状态 | ✅ 已完成 |
+
+### 功能描述
+
+用于用户主页展示该用户作为卖家收到的所有交易评价,包含:
+- 买家完整信息(昵称、头像、真实姓名、性别、简介等)
+- 评价内容和评分
+- 买家和卖家的信用积分
+- 买家和卖家的风控评分等级
+- 交易商品信息
+- 交易基本信息
+
+---
+
+## 请求参数
+
+### Query Parameters
+
+| 参数名 | 类型 | 必填 | 描述 | 示例值 | 默认值 |
+|--------|------|------|------|--------|--------|
+| sellerId | Integer | ✅ 是 | 卖家用户ID(主页用户ID) | 123 | - |
+
+### 请求示例
+
+#### cURL
+
+```bash
+curl -X GET "http://localhost:8080/secondTradeRecord/getSellerEvaluationList?sellerId=123" \
+  -H "Authorization: Bearer your_token_here"
+```
+
+#### JavaScript (Axios)
+
+```javascript
+axios.get('/secondTradeRecord/getSellerEvaluationList', {
+  params: {
+    sellerId: 123
+  },
+  headers: {
+    'Authorization': 'Bearer your_token_here'
+  }
+})
+.then(response => {
+  console.log(response.data);
+})
+.catch(error => {
+  console.error(error);
+});
+```
+
+#### Java (RestTemplate)
+
+```java
+String url = "http://localhost:8080/secondTradeRecord/getSellerEvaluationList?sellerId=123";
+RestTemplate restTemplate = new RestTemplate();
+R<List<SellerEvaluationVo>> result = restTemplate.getForObject(url, R.class);
+```
+
+---
+
+## 响应参数
+
+### 响应结构
+
+| 字段名 | 类型 | 描述 |
+|--------|------|------|
+| code | Integer | 响应状态码,200表示成功 |
+| msg | String | 响应消息 |
+| data | Array&lt;SellerEvaluationVo&gt; | 评价列表数据 |
+
+### SellerEvaluationVo 对象结构
+
+#### 交易基本信息
+
+| 字段名 | 类型 | 是否必返 | 描述 | 示例值 |
+|--------|------|----------|------|--------|
+| id | Integer | ✅ | 交易记录ID | 1 |
+| tradeNo | String | ✅ | 交易编号 | S2025111800001 |
+| transactionTime | String | ✅ | 交易时间 | 2025-11-18 10:00:00 |
+| transactionAmount | BigDecimal | ✅ | 交易金额(元) | 3800.00 |
+| tradeStatus | Integer | ✅ | 交易状态 | 4 |
+| createdTime | String | ✅ | 创建时间 | 2025-11-18 09:00:00 |
+
+#### 商品信息
+
+| 字段名 | 类型 | 是否必返 | 描述 | 示例值 |
+|--------|------|----------|------|--------|
+| goodsId | Integer | ✅ | 商品ID | 100 |
+| goodsTitle | String | ❌ | 商品标题 | 二手iPhone 13 128G 白色 95新 |
+| goodsHomeImage | String | ❌ | 商品封面图片URL | https://cdn.example.com/goods/iphone13.jpg |
+| goodsPrice | BigDecimal | ❌ | 商品价格(元) | 3999.00 |
+
+#### 买家信息
+
+| 字段名 | 类型 | 是否必返 | 描述 | 示例值 |
+|--------|------|----------|------|--------|
+| buyerId | Integer | ✅ | 买家ID | 456 |
+| buyerName | String | ❌ | 买家昵称 | 张三 |
+| buyerImage | String | ❌ | 买家头像URL | https://cdn.example.com/avatar/user456.jpg |
+| buyerPhone | String | ❌ | 买家手机号 | 13800138000 |
+| buyerRealName | String | ❌ | 买家真实姓名 | 张三三 |
+| buyerSex | String | ❌ | 买家性别 | 男 |
+| buyerJianjie | String | ❌ | 买家简介 | 热爱数码产品,诚信交易 |
+
+#### 买家评价信息
+
+| 字段名 | 类型 | 是否必返 | 描述 | 示例值 |
+|--------|------|----------|------|--------|
+| buyerEvaluate | String | ✅ | 买家评价内容 | 商品质量很好,和描述一致,卖家态度也不错,推荐! |
+| buyerRating | Integer | ❌ | 买家评分(1-5分) | 5 |
+| buyerCreditScore | Integer | ✅ | 买家信用积分 | 450 |
+| buyerCreditLevel | String | ✅ | 买家风控评分等级 | A |
+
+#### 卖家信息
+
+| 字段名 | 类型 | 是否必返 | 描述 | 示例值 |
+|--------|------|----------|------|--------|
+| sellerId | Integer | ✅ | 卖家ID | 123 |
+| sellerName | String | ❌ | 卖家昵称 | 李四 |
+| sellerImage | String | ❌ | 卖家头像URL | https://cdn.example.com/seller.jpg |
+| sellerCreditScore | Integer | ✅ | 卖家信用积分 | 500 |
+| sellerCreditLevel | String | ✅ | 卖家风控评分等级 | O |
+
+---
+
+## 枚举说明
+
+### 交易状态 (tradeStatus)
+
+| 状态值 | 状态名称 | 说明 |
+|--------|----------|------|
+| 1 | 待确认 | 买家发起交易请求,等待卖家确认 |
+| 2 | 已拒绝 | 卖家拒绝了交易请求 |
+| 3 | 待交易 | 双方确认交易,等待线下交易 |
+| 4 | 交易成功 | 交易完成,双方确认成功 |
+| 5 | 交易失败 | 交易失败 |
+| 6 | 交易取消 | 交易被取消 |
+
+### 风控评分等级 (creditLevel)
+
+| 等级 | 违规次数 | 等级说明 | 风险程度 |
+|------|----------|----------|----------|
+| O | 0次 | 无违规记录 | 🟢 无风险(最优) |
+| A | 1-2次 | 轻微违规 | 🟢 低风险 |
+| B | 3-5次 | 一般违规 | 🟡 中等风险 |
+| C | 6-9次 | 较严重违规 | 🟡 较高风险 |
+| 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"
+    }
+  ]
+}
+```
+
+### 空数据响应
+
+```json
+{
+  "code": 200,
+  "msg": "成功",
+  "data": []
+}
+```
+
+### 错误响应
+
+#### 参数缺失
+
+```json
+{
+  "code": 500,
+  "msg": "获取评价列表失败: 卖家ID不能为空",
+  "data": null
+}
+```
+
+#### 系统异常
+
+```json
+{
+  "code": 500,
+  "msg": "获取评价列表失败: 数据库连接异常",
+  "data": null
+}
+```
+
+---
+
+## 业务规则
+
+### 查询规则
+
+1. **数据过滤**
+   - ✅ 只返回有买家评价内容的记录(`buyer_evaluate IS NOT NULL AND buyer_evaluate != ''`)
+   - ✅ 只返回未删除的记录(`delete_flag = 0`)
+   - ✅ 按创建时间倒序排列(最新评价在前)
+
+2. **数据关联**
+   - 商品信息:通过 `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 问题
+   - 关联数据使用 Map 索引,时间复杂度 O(1)
+   - 分组查询:商品、用户、积分分别批量获取
+
+### 数据完整性
+
+1. **字段缺失处理**
+   - 如果商品记录已删除,商品相关字段返回 `null`
+   - 如果用户信息不存在,用户相关字段返回 `null`
+   - 如果没有信用积分记录,`creditScore` 返回 `0`
+   - 风控等级计算异常时返回默认等级 `"O"`
+
+2. **评分有效性**
+   - 买家评分 `buyerRating` 有效范围:1-5 分
+   - 如果未评分,该字段可能为 `null`
+
+---
+
+## 错误码说明
+
+| HTTP状态码 | code | msg | 说明 | 解决方案 |
+|-----------|------|-----|------|----------|
+| 200 | 200 | 成功 | 请求成功 | - |
+| 200 | 500 | 卖家ID不能为空 | 未传入 sellerId 参数 | 检查请求参数,确保传入 sellerId |
+| 200 | 500 | 获取评价列表失败: xxx | 系统异常 | 查看日志,联系后端开发人员 |
+| 500 | - | - | 服务器内部错误 | 联系后端开发人员 |
+
+---
+
+## 使用场景
+
+### 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);
+```
+
+### 数据库表
+
+| 表名 | 说明 | 关联字段 |
+|------|------|----------|
+| 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`
+
+5. **Entity**  
+   `alien-entity/src/main/java/shop/alien/entity/second/SecondTradeRecord.java`
+
+---
+
+## 注意事项
+
+### ⚠️ 重要提示
+
+1. **必传参数**
+   - `sellerId` 为必传参数,用于查看指定用户的评价列表
+   - 不传或传空会返回错误:`"卖家ID不能为空"`
+
+2. **数据权限**
+   - 接口不做权限校验,任何人都可以查看用户主页的公开评价
+   - 如需增加权限控制,请联系后端开发人员
+
+3. **空数据处理**
+   - 如果该用户没有收到任何评价,返回空数组 `[]`
+   - 不会返回 `null`,前端可以直接遍历
+
+4. **字段可空性**
+   - 部分字段可能为 `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 | 增加更多用户信息字段 | 开发团队 |
+
+---
+
+## 联系方式
+
+如有问题或建议,请联系:
+
+- **开发团队**:二手交易平台开发组
+- **服务名称**:alien-second
+- **文档路径**:`alien-second/doc/获取卖家交易评价列表接口文档.md`
+
+---
+
+*最后更新时间:2025-11-18*
+

+ 18 - 1
alien-second/src/main/java/shop/alien/second/controller/SecondTradeRecordController.java

@@ -1,7 +1,6 @@
 package shop.alien.second.controller;
 
 
-import com.alipay.api.kms.aliyun.utils.BackoffUtils;
 import io.swagger.annotations.*;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -10,6 +9,7 @@ import shop.alien.entity.result.BusinessException;
 import shop.alien.entity.result.R;
 import shop.alien.entity.second.SecondTradeRecord;
 import shop.alien.entity.second.vo.SecondTradeRecordVo;
+import shop.alien.entity.second.vo.SellerEvaluationVo;
 import shop.alien.second.service.SecondTradeRecordService;
 
 import java.util.List;
@@ -160,4 +160,21 @@ public class SecondTradeRecordController {
         log.info("SecondTradeRecordController.modifyTradeRecord?record={}", transactionTime);
         return R.data(secondTradeRecordService.modifyTradeRecord(type, tradeId, transactionTime, transactionLatitudeLongitude, transactionLatitudeLongitudeAddress, transactionLocation, messageId));
     }
+
+    @ApiOperation("获取用户作为卖家的交易评价列表")
+    @ApiOperationSupport(order = 12)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "sellerId", value = "卖家id(用户主页展示用,必传)", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getSellerEvaluationList")
+    public R<List<SellerEvaluationVo>> getSellerEvaluationList(@RequestParam(required = true) Integer sellerId) {
+        log.info("SecondTradeRecordController.getSellerEvaluationList?sellerId={}", sellerId);
+        try {
+            List<SellerEvaluationVo> evaluationList = secondTradeRecordService.getSellerEvaluationList(sellerId);
+            return R.data(evaluationList);
+        } catch (Exception e) {
+            log.error("SecondTradeRecordController.getSellerEvaluationList?error: {}", e.getMessage(), e);
+            return R.fail("获取评价列表失败: " + e.getMessage());
+        }
+    }
 }

+ 9 - 0
alien-second/src/main/java/shop/alien/second/service/SecondTradeRecordService.java

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
 import shop.alien.entity.result.BusinessException;
 import shop.alien.entity.second.SecondTradeRecord;
 import shop.alien.entity.second.vo.SecondTradeRecordVo;
+import shop.alien.entity.second.vo.SellerEvaluationVo;
 
 import java.util.List;
 
@@ -38,4 +39,12 @@ public interface SecondTradeRecordService extends IService<SecondTradeRecord> {
     SecondTradeRecord hasInTradeRecord(Integer sideId) throws Exception;
 
     boolean modifyTradeRecord(int type, Integer tradeId, String transactionTime, String transactionLatitudeLongitude, String transactionLatitudeLongitudeAddress, String transactionLocation, String messageId) throws Exception;
+
+    /**
+     * 获取用户作为卖家的交易评价列表
+     *
+     * @param sellerId 卖家ID(用户ID,必传)
+     * @return 卖家评价列表,包含买家信息、评价内容、评分、风控评分等级等
+     */
+    List<SellerEvaluationVo> getSellerEvaluationList(Integer sellerId) throws Exception;
 }

+ 195 - 1
alien-second/src/main/java/shop/alien/second/service/impl/SecondTradeRecordServiceImpl.java

@@ -5,7 +5,6 @@ import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -17,6 +16,7 @@ import shop.alien.entity.result.BusinessException;
 import shop.alien.entity.second.*;
 import shop.alien.mapper.second.SecondRiskControlRecordMapper;
 import shop.alien.entity.second.vo.SecondTradeRecordVo;
+import shop.alien.entity.second.vo.SellerEvaluationVo;
 import shop.alien.entity.store.LifeMessage;
 import shop.alien.entity.store.LifeNotice;
 import shop.alien.entity.store.LifeUser;
@@ -764,4 +764,198 @@ public class SecondTradeRecordServiceImpl extends ServiceImpl<SecondTradeRecordM
             throw new Exception(e);
         }
     }
+
+    /**
+     * 获取用户作为卖家的交易评价列表
+     *
+     * @param sellerId 卖家ID(用户ID,必传)
+     * @return 卖家评价列表
+     */
+    @Override
+    public List<SellerEvaluationVo> getSellerEvaluationList(Integer sellerId) throws Exception {
+        try {
+            log.info("SecondTradeRecordServiceImpl.getSellerEvaluationList(): sellerId={}", sellerId);
+            
+            // sellerId必传校验
+            if (sellerId == null) {
+                throw new Exception("卖家ID不能为空");
+            }
+            
+            // 使用MyBatis Plus方式查询交易记录
+            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 = secondTradeRecordMapper.selectList(tradeWrapper);
+            
+            if (tradeRecords.isEmpty()) {
+                log.info("SecondTradeRecordServiceImpl.getSellerEvaluationList(): 未查询到评价记录");
+                return new java.util.ArrayList<>();
+            }
+            
+            // 获取所有商品记录ID、买家ID和卖家ID
+            List<Integer> goodsRecordIds = tradeRecords.stream()
+                    .map(SecondTradeRecord::getGoodsRecordId)
+                    .filter(Objects::nonNull)
+                    .distinct()
+                    .collect(Collectors.toList());
+            
+            List<Integer> buyerIds = tradeRecords.stream()
+                    .map(SecondTradeRecord::getBuyerId)
+                    .filter(Objects::nonNull)
+                    .distinct()
+                    .collect(Collectors.toList());
+            
+            // 批量查询商品记录信息
+            java.util.Map<Integer, SecondGoodsRecord> goodsRecordMap = new java.util.HashMap<>();
+            if (!goodsRecordIds.isEmpty()) {
+                LambdaQueryWrapper<SecondGoodsRecord> goodsWrapper = new LambdaQueryWrapper<>();
+                goodsWrapper.in(SecondGoodsRecord::getId, goodsRecordIds);
+                List<SecondGoodsRecord> goodsRecords = secondGoodsRecordMapper.selectList(goodsWrapper);
+                goodsRecordMap = goodsRecords.stream()
+                        .collect(Collectors.toMap(SecondGoodsRecord::getId, g -> g));
+            }
+            
+            // 批量查询买家用户信息
+            java.util.Map<Integer, LifeUser> buyerMap = new java.util.HashMap<>();
+            if (!buyerIds.isEmpty()) {
+                LambdaQueryWrapper<LifeUser> userWrapper = new LambdaQueryWrapper<>();
+                userWrapper.in(LifeUser::getId, buyerIds)
+                        .eq(LifeUser::getDeleteFlag, 0);
+                List<LifeUser> buyers = lifeUserMapper.selectList(userWrapper);
+                buyerMap = buyers.stream()
+                        .collect(Collectors.toMap(LifeUser::getId, u -> u));
+            }
+            
+            // 查询卖家信息
+            LifeUser seller = lifeUserMapper.selectById(sellerId);
+            
+            // 批量查询买家和卖家的信用积分
+            List<Integer> allUserIds = new java.util.ArrayList<>(buyerIds);
+            allUserIds.add(sellerId);
+            java.util.Map<Integer, SecondUserCredit> creditMap = new java.util.HashMap<>();
+            if (!allUserIds.isEmpty()) {
+                LambdaQueryWrapper<SecondUserCredit> creditWrapper = new LambdaQueryWrapper<>();
+                creditWrapper.in(SecondUserCredit::getUserId, allUserIds)
+                        .eq(SecondUserCredit::getDeleteFlag, 0);
+                List<SecondUserCredit> credits = secondUserCreditMapper.selectList(creditWrapper);
+                creditMap = credits.stream()
+                        .collect(Collectors.toMap(SecondUserCredit::getUserId, c -> c));
+            }
+            
+            // 组装返回结果
+            List<SellerEvaluationVo> evaluationList = new java.util.ArrayList<>();
+            for (SecondTradeRecord trade : tradeRecords) {
+                SellerEvaluationVo vo = new SellerEvaluationVo();
+                
+                // 交易基本信息
+                vo.setId(trade.getId());
+                vo.setTradeNo(trade.getTradeNo());
+                vo.setGoodsId(trade.getGoodsId());
+                vo.setTransactionAmount(trade.getTransactionAmount());
+                vo.setBuyerId(trade.getBuyerId());
+                vo.setBuyerEvaluate(trade.getBuyerEvaluate());
+                vo.setBuyerRating(trade.getBuyerRating());
+                vo.setTransactionTime(trade.getTransactionTime());
+                vo.setTradeStatus(trade.getTradeStatus());
+                vo.setCreatedTime(trade.getCreatedTime());
+                
+                // 商品信息
+                SecondGoodsRecord goodsRecord = goodsRecordMap.get(trade.getGoodsRecordId());
+                if (goodsRecord != null) {
+                    vo.setGoodsTitle(goodsRecord.getTitle());
+                    vo.setGoodsHomeImage(goodsRecord.getHomeImage());
+                    vo.setGoodsPrice(goodsRecord.getPrice());
+                }
+                
+                // 买家用户信息
+                LifeUser buyer = buyerMap.get(trade.getBuyerId());
+                if (buyer != null) {
+                    vo.setBuyerName(buyer.getUserName());
+                    vo.setBuyerImage(buyer.getUserImage());
+                    vo.setBuyerPhone(buyer.getUserPhone());
+                    vo.setBuyerRealName(buyer.getRealName());
+                    vo.setBuyerSex(buyer.getUserSex());
+                    vo.setBuyerJianjie(buyer.getJianjie());
+                }
+                
+                // 买家信用积分和风控评分等级
+                SecondUserCredit buyerCredit = creditMap.get(trade.getBuyerId());
+                if (buyerCredit != null) {
+                    vo.setBuyerCreditScore(buyerCredit.getUserPoints());
+                } else {
+                    vo.setBuyerCreditScore(0);
+                }
+                vo.setBuyerCreditLevel(calculateCreditLevel(trade.getBuyerId()));
+                
+                // 卖家信息
+                vo.setSellerId(sellerId);
+                if (seller != null) {
+                    vo.setSellerName(seller.getUserName());
+                    vo.setSellerImage(seller.getUserImage());
+                }
+                
+                // 卖家信用积分和风控评分等级
+                SecondUserCredit sellerCredit = creditMap.get(sellerId);
+                if (sellerCredit != null) {
+                    vo.setSellerCreditScore(sellerCredit.getUserPoints());
+                } else {
+                    vo.setSellerCreditScore(0);
+                }
+                vo.setSellerCreditLevel(calculateCreditLevel(sellerId));
+                
+                evaluationList.add(vo);
+            }
+            
+            log.info("SecondTradeRecordServiceImpl.getSellerEvaluationList(): 查询到{}条评价记录", evaluationList.size());
+            return evaluationList;
+        } catch (Exception e) {
+            log.error("SecondTradeRecordServiceImpl.getSellerEvaluationList(): Error Msg={}", e.getMessage(), e);
+            throw new Exception("获取卖家评价列表失败: " + e.getMessage());
+        }
+    }
+    
+    /**
+     * 计算用户风控评分等级
+     * 根据用户违规次数计算等级:O(无记录) < A(1-2次) < B(3-5次) < C(6-9次) < D(10-14次) < E(≥15次)
+     *
+     * @param userId 用户ID
+     * @return 风控评分等级
+     */
+    private String calculateCreditLevel(Integer userId) {
+        try {
+            if (userId == null) {
+                return "O";
+            }
+            
+            // 查询用户违规记录数量
+            LambdaQueryWrapper<SecondRiskControlRecord> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(SecondRiskControlRecord::getUserId, userId)
+                    .eq(SecondRiskControlRecord::getDeleteFlag, 0);
+            
+            Integer count = secondRiskControlRecordMapper.selectCount(wrapper);
+            int violationCount = count != null ? count : 0;
+            
+            // 根据违规次数确定等级
+            if (violationCount == 0) {
+                return "O";  // 无记录
+            } else if (violationCount >= 15) {
+                return "E";  // 最差
+            } else if (violationCount >= 10) {
+                return "D";
+            } else if (violationCount >= 6) {
+                return "C";
+            } else if (violationCount >= 3) {
+                return "B";
+            } else {
+                return "A";  // 1-2次违规
+            }
+        } catch (Exception e) {
+            log.error("SecondTradeRecordServiceImpl.calculateCreditLevel(): Error Msg={}", e.getMessage(), e);
+            return "O";  // 异常时返回默认等级
+        }
+    }
 }