Bläddra i källkod

feat(second): add entrust user page and detail query APIs

- Implemented paginated query for entrust users grouped by name and ID card
- Added detailed query API for entrust user including all related trade records
- Created VO classes for query parameters and result mapping
- Extended mapper with custom SQL queries for grouping and filtering
- Enhanced service layer with business logic for data aggregation
- Added comprehensive documentation for both new APIs
wxd 3 veckor sedan
förälder
incheckning
30948e4d10

+ 30 - 0
alien-entity/src/main/java/shop/alien/entity/second/vo/SecondEntrustUserDetailVo.java

@@ -0,0 +1,30 @@
+package shop.alien.entity.second.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import shop.alien.entity.second.SecondEntrustUser;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 二手委托人详情 VO
+ * </p>
+ *
+ * @author ssk
+ * @since 2025-11-21
+ */
+@Data
+@JsonInclude
+@ApiModel(value = "SecondEntrustUserDetailVo", description = "二手委托人详情对象")
+public class SecondEntrustUserDetailVo {
+
+    @ApiModelProperty(value = "委托人基本信息")
+    private SecondEntrustUser entrustUserInfo;
+
+    @ApiModelProperty(value = "关联的交易记录集合")
+    private List<SecondTradeRecordVo> tradeRecords;
+}
+

+ 41 - 0
alien-entity/src/main/java/shop/alien/entity/second/vo/SecondEntrustUserQueryVo.java

@@ -0,0 +1,41 @@
+package shop.alien.entity.second.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * <p>
+ * 二手委托人信息查询 VO(以人为维度)
+ * </p>
+ *
+ * @author ssk
+ * @since 2025-11-21
+ */
+@Data
+@JsonInclude
+@NoArgsConstructor
+@ApiModel(value = "SecondEntrustUserQueryVo", description = "二手委托人信息查询对象(以人为维度)")
+public class SecondEntrustUserQueryVo {
+
+    @ApiModelProperty(value = "分页页数")
+    private Integer pageNum;
+
+    @ApiModelProperty(value = "分页条数")
+    private Integer pageSize;
+
+    @ApiModelProperty(value = "委托人姓名")
+    private String entrustUserName;
+
+    @ApiModelProperty(value = "委托人身份证号")
+    private String entrustIdCard;
+
+    @ApiModelProperty(value = "委托人电话")
+    private String entrustUserPhone;
+
+    @ApiModelProperty(value = "交易编号")
+    private String entrustTradeNo;
+}
+

+ 39 - 0
alien-entity/src/main/java/shop/alien/entity/second/vo/SecondEntrustUserResultVo.java

@@ -0,0 +1,39 @@
+package shop.alien.entity.second.vo;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import shop.alien.entity.second.SecondEntrustUser;
+
+/**
+ * <p>
+ * 二手委托人信息结果 VO(以人为维度)
+ * </p>
+ *
+ * @author ssk
+ * @since 2025-11-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@JsonInclude
+@NoArgsConstructor
+@ApiModel(value = "SecondEntrustUserResultVo", description = "二手委托人信息结果对象(以人为维度)")
+public class SecondEntrustUserResultVo extends SecondEntrustUser {
+
+    @TableField(exist = false)
+    @ApiModelProperty(value = "委托记录总数")
+    private Integer entrustCount;
+
+    @TableField(exist = false)
+    @ApiModelProperty(value = "关联交易总数")
+    private Integer tradeCount;
+
+    @TableField(exist = false)
+    @ApiModelProperty(value = "最近委托时间")
+    private String latestEntrustTime;
+}
+

+ 48 - 0
alien-entity/src/main/java/shop/alien/mapper/second/SecondEntrustUserMapper.java

@@ -1,8 +1,16 @@
 package shop.alien.mapper.second;
 
+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 org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 import shop.alien.entity.second.SecondEntrustUser;
+import shop.alien.entity.second.vo.SecondEntrustUserResultVo;
+
+import java.util.List;
 
 /**
  * <p>
@@ -15,5 +23,45 @@ import shop.alien.entity.second.SecondEntrustUser;
 @Mapper
 public interface SecondEntrustUserMapper extends BaseMapper<SecondEntrustUser> {
 
+    /**
+     * 分页查询委托人信息列表(以姓名+身份证号为维度分组)
+     *
+     * @param page 分页对象
+     * @param wrapper 查询条件
+     * @return 委托人信息分页列表
+     */
+    @Select("select " +
+            "  MIN(entrust.id) as id, " +
+            "  entrust.entrust_user_name as entrustUserName, " +
+            "  entrust.entrust_id_card as entrustIdCard, " +
+            "  entrust.entrust_user_phone as entrustUserPhone, " +
+            "  entrust.entrust_id_card_img as entrustIdCardImg, " +
+            "  COUNT(DISTINCT entrust.id) as entrustCount, " +
+            "  COUNT(DISTINCT entrust.entrust_trade_id) as tradeCount, " +
+            "  MAX(entrust.created_time) as latestEntrustTime " +
+            "from second_entrust_user entrust " +
+            "left join second_trade_record trade on trade.id = entrust.entrust_trade_id " +
+            "${ew.customSqlSegment} " +
+            "group by entrust.entrust_user_name, entrust.entrust_id_card " +
+            "order by latestEntrustTime desc")
+    IPage<SecondEntrustUserResultVo> getEntrustUserPage(IPage<SecondEntrustUser> page, 
+                                                         @Param(Constants.WRAPPER) QueryWrapper<SecondEntrustUser> wrapper);
+
+    /**
+     * 根据姓名和身份证号查询所有委托记录
+     *
+     * @param entrustUserName 姓名
+     * @param entrustIdCard 身份证号
+     * @return 委托记录列表
+     */
+    @Select("select * from second_entrust_user " +
+            "where entrust_user_name = #{entrustUserName} " +
+            "and entrust_id_card = #{entrustIdCard} " +
+            "and delete_flag = 0 " +
+            "order by created_time desc")
+    List<SecondEntrustUser> getByUserNameAndIdCard(@Param("entrustUserName") String entrustUserName, 
+                                                     @Param("entrustIdCard") String entrustIdCard);
+
 }
 
+

+ 580 - 0
alien-second/doc/委托人信息分页查询接口文档.md

@@ -0,0 +1,580 @@
+# 委托人信息分页查询接口文档(以人为维度)
+
+## 接口概述
+
+### 接口信息
+
+| 项目 | 内容 |
+|------|------|
+| 接口名称 | 委托人信息分页查询(以人为维度) |
+| 接口路径 | `/secondEntrustUser/page` |
+| 请求方式 | POST |
+| 接口版本 | v2.1 |
+| 开发时间 | 2025-11-21 |
+| 接口状态 | ✅ 已完成 |
+
+### 功能描述
+
+用于管理后台或业务系统查询二手交易委托人信息,**以"姓名+身份证号"为唯一维度进行分组统计**,因为同一个人可能有多条委托记录。
+
+**查询维度**:
+- 以委托人的"姓名+身份证号"组合作为唯一标识
+- 对同一个人的多条委托记录进行聚合统计
+- 返回该人的委托记录总数、关联交易总数、最近委托时间等统计信息
+
+**支持查询条件**:
+- 委托人姓名(模糊查询)
+- 身份证号(模糊查询)
+- 委托人电话(模糊查询)
+- **交易编号(模糊查询)** ✨新增
+
+---
+
+## 请求参数
+
+### Request Body (JSON)
+
+| 参数名 | 类型 | 必填 | 描述 | 示例值 | 默认值 |
+|--------|------|------|------|--------|--------|
+| pageNum | Integer | ❌ 否 | 页码(从1开始) | 1 | 1 |
+| pageSize | Integer | ❌ 否 | 每页条数 | 10 | 10 |
+| userName | String | ❌ 否 | 委托人姓名(模糊查询) | 张三 | - |
+| idCard | String | ❌ 否 | 身份证号(模糊查询) | 110101 | - |
+| userPhone | String | ❌ 否 | 委托人电话(模糊查询) | 138 | - |
+| tradeNo | String | ❌ 否 | 交易编号(模糊查询) | S202511 | - |
+
+**参数说明:**
+- `pageNum`: 页码从1开始,传入小于1的值自动修正为1
+- `pageSize`: 每页条数,传入小于1的值自动修正为10
+- `userName`: 支持模糊查询,可输入姓名的任意部分
+- `idCard`: 支持模糊查询,可输入身份证号的任意部分
+- `userPhone`: 支持模糊查询,可输入手机号的任意部分
+- `tradeNo`: 支持模糊查询,可输入交易编号的任意部分
+- **所有查询条件可单独使用或组合使用**
+- **结果按最近委托时间倒序排列,同一个人只显示一条记录**
+
+### 请求示例
+
+#### cURL
+
+```bash
+# 查询所有委托人(第1页,每页10条)
+curl -X POST "http://localhost:8080/secondEntrustUser/page" \
+  -H "Content-Type: application/json" \
+  -H "Authorization: Bearer your_token_here" \
+  -d '{
+    "pageNum": 1,
+    "pageSize": 10
+  }'
+
+# 根据委托人姓名查询
+curl -X POST "http://localhost:8080/secondEntrustUser/page" \
+  -H "Content-Type: application/json" \
+  -H "Authorization: Bearer your_token_here" \
+  -d '{
+    "pageNum": 1,
+    "pageSize": 10,
+    "userName": "张三"
+  }'
+
+# 根据身份证号查询
+curl -X POST "http://localhost:8080/secondEntrustUser/page" \
+  -H "Content-Type: application/json" \
+  -H "Authorization: Bearer your_token_here" \
+  -d '{
+    "pageNum": 1,
+    "pageSize": 10,
+    "idCard": "110101199001011234"
+  }'
+
+# 根据交易编号查询
+curl -X POST "http://localhost:8080/secondEntrustUser/page" \
+  -H "Content-Type: application/json" \
+  -H "Authorization: Bearer your_token_here" \
+  -d '{
+    "pageNum": 1,
+    "pageSize": 10,
+    "tradeNo": "S2025112100001"
+  }'
+
+# 组合查询(姓名+身份证号+交易编号)
+curl -X POST "http://localhost:8080/secondEntrustUser/page" \
+  -H "Content-Type: application/json" \
+  -H "Authorization: Bearer your_token_here" \
+  -d '{
+    "pageNum": 1,
+    "pageSize": 20,
+    "userName": "张",
+    "idCard": "110101",
+    "tradeNo": "S202511"
+  }'
+```
+
+#### JavaScript (Axios)
+
+```javascript
+// 查询委托人列表
+axios.post('/secondEntrustUser/page', {
+  pageNum: 1,
+  pageSize: 10,
+  userName: '张三',
+  idCard: '110101199001011234',
+  tradeNo: 'S2025112100001'
+}, {
+  headers: {
+    'Authorization': 'Bearer your_token_here'
+  }
+})
+.then(response => {
+  const { records, total, pages, current, size } = response.data.data;
+  console.log(`当前第${current}页,每页${size}条`);
+  console.log(`共${total}个委托人,${pages}页`);
+  
+  records.forEach(person => {
+    console.log(`姓名:${person.userName}`);
+    console.log(`身份证:${person.idCard}`);
+    console.log(`委托记录数:${person.entrustCount}`);
+    console.log(`关联交易数:${person.tradeCount}`);
+    console.log(`最近委托时间:${person.latestEntrustTime}`);
+  });
+})
+.catch(error => {
+  console.error(error);
+});
+```
+
+---
+
+## 响应参数
+
+### 响应结构
+
+| 字段名 | 类型 | 描述 |
+|--------|------|------|
+| code | Integer | 响应状态码,200表示成功 |
+| msg | String | 响应消息 |
+| data | IPage&lt;SecondEntrustUserResultVo&gt; | 分页数据对象 |
+
+### IPage 分页对象结构
+
+| 字段名 | 类型 | 描述 | 示例值 |
+|--------|------|------|--------|
+| records | Array&lt;SecondEntrustUserResultVo&gt; | 当前页的委托人列表(去重后) | [...] |
+| total | Long | 总人数(去重后) | 50 |
+| size | Long | 每页条数 | 10 |
+| current | Long | 当前页码 | 1 |
+| pages | Long | 总页数 | 5 |
+
+### SecondEntrustUserResultVo 对象结构(以人为维度)
+
+| 字段名 | 类型 | 是否必返 | 描述 | 示例值 |
+|--------|------|----------|------|--------|
+| id | Integer | ✅ | 该人最小的委托记录ID(用于标识) | 1 |
+| userName | String | ✅ | 委托人姓名 | 张三 |
+| idCard | String | ✅ | 委托人身份证号 | 110101199001011234 |
+| userPhone | String | ✅ | 委托人电话 | 13800138000 |
+| idCardImg | String | ❌ | 委托人身份证照片URL | https://cdn.example.com/idcard/123.jpg |
+| entrustCount | Integer | ✅ | 该人的委托记录总数 | 3 |
+| tradeCount | Integer | ✅ | 该人关联的交易总数 | 3 |
+| latestEntrustTime | String | ✅ | 最近一次委托时间 | 2025-11-21 15:30:00 |
+
+---
+
+## 响应示例
+
+### 成功响应(有数据)
+
+```json
+{
+  "code": 200,
+  "msg": "成功",
+  "data": {
+    "records": [
+      {
+        "id": 1,
+        "userName": "张三",
+        "idCard": "110101199001011234",
+        "userPhone": "13800138000",
+        "idCardImg": "https://cdn.example.com/idcard/123.jpg",
+        "entrustCount": 3,
+        "tradeCount": 3,
+        "latestEntrustTime": "2025-11-21 15:30:00"
+      },
+      {
+        "id": 5,
+        "userName": "李四",
+        "idCard": "110101199002021234",
+        "userPhone": "13900139000",
+        "idCardImg": "https://cdn.example.com/idcard/456.jpg",
+        "entrustCount": 2,
+        "tradeCount": 2,
+        "latestEntrustTime": "2025-11-21 14:20:00"
+      },
+      {
+        "id": 8,
+        "userName": "王五",
+        "idCard": "110101199003031234",
+        "userPhone": "13700137000",
+        "idCardImg": null,
+        "entrustCount": 1,
+        "tradeCount": 1,
+        "latestEntrustTime": "2025-11-21 10:00:00"
+      }
+    ],
+    "total": 50,
+    "size": 10,
+    "current": 1,
+    "pages": 5
+  }
+}
+```
+
+### 空数据响应
+
+```json
+{
+  "code": 200,
+  "msg": "成功",
+  "data": {
+    "records": [],
+    "total": 0,
+    "size": 10,
+    "current": 1,
+    "pages": 0
+  }
+}
+```
+
+### 错误响应
+
+```json
+{
+  "code": 500,
+  "msg": "查询委托人信息列表失败: 数据库连接异常",
+  "data": null
+}
+```
+
+---
+
+## 业务规则
+
+### 查询规则
+
+1. **数据去重**
+   - ✅ 以"姓名+身份证号"为唯一标识进行分组
+   - ✅ 同一个人的多条委托记录聚合为一条显示
+   - ✅ 只返回未删除的记录(`delete_flag = 0`)
+
+2. **统计信息**
+   - `entrustCount`: 统计该人的委托记录总数
+   - `tradeCount`: 统计该人关联的交易总数(去重)
+   - `latestEntrustTime`: 该人最近一次委托的时间
+
+3. **排序规则**
+   - ✅ 按最近委托时间倒序排列(最新的在前)
+   - ✅ 便于查看最近活跃的委托人
+
+4. **查询方式**
+   - ✅ 所有字符串类型条件均支持模糊查询(LIKE)
+   - ✅ 多条件之间为 AND 关系(同时满足)
+   - ✅ 空条件会被忽略
+   - ✅ **交易编号查询:查询该人关联的交易中包含指定编号的记录**
+
+5. **数据关联**
+   - 委托人记录:`second_entrust_user` 表
+   - 交易记录:通过 `trade_id` 关联 `second_trade_record` 表(用于交易编号查询)
+
+---
+
+## 使用场景示例
+
+### 场景1:查看所有委托人列表
+
+```json
+{
+  "pageNum": 1,
+  "pageSize": 10
+}
+```
+
+**返回结果**:按最近委托时间倒序返回所有委托人(去重),每个人只显示一条记录
+
+---
+
+### 场景2:根据姓名搜索委托人
+
+```json
+{
+  "pageNum": 1,
+  "pageSize": 10,
+  "userName": "张三"
+}
+```
+
+**返回结果**:姓名中包含"张三"的所有委托人
+
+---
+
+### 场景3:根据身份证号搜索
+
+```json
+{
+  "pageNum": 1,
+  "pageSize": 10,
+  "idCard": "110101"
+}
+```
+
+**返回结果**:身份证号中包含"110101"的所有委托人
+
+---
+
+### 场景4:根据交易编号搜索委托人
+
+```json
+{
+  "pageNum": 1,
+  "pageSize": 10,
+  "tradeNo": "S2025112100001"
+}
+```
+
+**返回结果**:参与过交易编号包含"S2025112100001"的所有委托人
+
+**说明**:如果张三有3笔交易(S2025112100001、S2025112100002、S2025112100003),当搜索"S2025112100001"时,会返回张三的记录。
+
+---
+
+### 场景5:组合精确查询
+
+```json
+{
+  "pageNum": 1,
+  "pageSize": 10,
+  "userName": "张三",
+  "idCard": "110101199001011234",
+  "tradeNo": "S202511"
+}
+```
+
+**返回结果**:姓名为"张三"、身份证号为"110101199001011234"、且参与过交易编号包含"S202511"的委托人
+
+---
+
+## 前端实现示例
+
+### Vue 3 + Element Plus
+
+```vue
+<template>
+  <div class="entrust-user-page">
+    <!-- 搜索表单 -->
+    <el-form :inline="true" :model="searchForm" class="search-form">
+      <el-form-item label="委托人姓名">
+        <el-input v-model="searchForm.userName" placeholder="请输入姓名" clearable />
+      </el-form-item>
+      <el-form-item label="身份证号">
+        <el-input v-model="searchForm.idCard" placeholder="请输入身份证号" clearable />
+      </el-form-item>
+      <el-form-item label="委托人电话">
+        <el-input v-model="searchForm.userPhone" placeholder="请输入电话" clearable />
+      </el-form-item>
+      <el-form-item label="交易编号">
+        <el-input v-model="searchForm.tradeNo" placeholder="请输入交易编号" clearable />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="handleSearch">查询</el-button>
+        <el-button @click="handleReset">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 数据表格 -->
+    <el-table :data="tableData" border stripe v-loading="loading">
+      <el-table-column prop="userName" label="委托人姓名" width="120" />
+      <el-table-column prop="idCard" label="身份证号" width="180" />
+      <el-table-column prop="userPhone" label="联系电话" width="130" />
+      <el-table-column prop="entrustCount" label="委托记录数" width="120" align="center">
+        <template #default="{ row }">
+          <el-tag>{{ row.entrustCount }} 条</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column prop="tradeCount" label="关联交易数" width="120" align="center">
+        <template #default="{ row }">
+          <el-tag type="success">{{ row.tradeCount }} 笔</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column prop="latestEntrustTime" label="最近委托时间" width="180" />
+      <el-table-column label="操作" width="150" fixed="right">
+        <template #default="{ row }">
+          <el-button type="primary" link @click="viewDetail(row)">查看详情</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 分页组件 -->
+    <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, reactive, onMounted } from 'vue';
+import { useRouter } from 'vue-router';
+import axios from 'axios';
+import { ElMessage } from 'element-plus';
+
+const router = useRouter();
+const pageNum = ref(1);
+const pageSize = ref(10);
+const total = ref(0);
+const tableData = ref([]);
+const loading = ref(false);
+
+const searchForm = reactive({
+  userName: '',
+  idCard: '',
+  userPhone: '',
+  tradeNo: ''
+});
+
+// 加载数据
+const loadData = async () => {
+  loading.value = true;
+  try {
+    const response = await axios.post('/secondEntrustUser/page', {
+      pageNum: pageNum.value,
+      pageSize: pageSize.value,
+      userName: searchForm.userName || null,
+      idCard: searchForm.idCard || null,
+      userPhone: searchForm.userPhone || null,
+      tradeNo: searchForm.tradeNo || null
+    });
+    
+    const { records, total: totalCount } = response.data.data;
+    tableData.value = records;
+    total.value = totalCount;
+  } catch (error) {
+    ElMessage.error('加载失败:' + error.message);
+  } finally {
+    loading.value = false;
+  }
+};
+
+// 查询
+const handleSearch = () => {
+  pageNum.value = 1;
+  loadData();
+};
+
+// 重置
+const handleReset = () => {
+  searchForm.userName = '';
+  searchForm.idCard = '';
+  searchForm.userPhone = '';
+  searchForm.tradeNo = '';
+  pageNum.value = 1;
+  loadData();
+};
+
+// 每页条数改变
+const handleSizeChange = (newSize) => {
+  pageSize.value = newSize;
+  pageNum.value = 1;
+  loadData();
+};
+
+// 当前页改变
+const handleCurrentChange = (newPage) => {
+  pageNum.value = newPage;
+  loadData();
+};
+
+// 查看详情
+const viewDetail = (row) => {
+  router.push({
+    path: '/entrust-user/detail',
+    query: {
+      userName: row.userName,
+      idCard: row.idCard
+    }
+  });
+};
+
+onMounted(() => {
+  loadData();
+});
+</script>
+```
+
+---
+
+## 错误码说明
+
+| HTTP状态码 | code | msg | 说明 | 解决方案 |
+|-----------|------|-----|------|----------|
+| 200 | 200 | 成功 | 请求成功 | - |
+| 200 | 500 | 查询委托人信息列表失败: xxx | 系统异常 | 查看日志,联系后端开发人员 |
+| 500 | - | - | 服务器内部错误 | 联系后端开发人员 |
+
+---
+
+## 注意事项
+
+### ⚠️ 重要提示
+
+1. **查询维度**
+   - 以"姓名+身份证号"为唯一标识
+   - 同一个人的多条委托记录会聚合为一条显示
+   - 返回该人的统计信息(委托记录数、交易数等)
+
+2. **交易编号查询说明** ✨
+   - 查询该委托人关联的交易中是否包含指定编号
+   - 如果该人有多笔交易,只要其中一笔包含指定编号,就会返回该人的记录
+   - 适用于通过交易编号反查委托人的场景
+
+3. **统计信息说明**
+   - `entrustCount`: 该人的委托记录总数
+   - `tradeCount`: 该人关联的交易总数(已去重)
+   - `latestEntrustTime`: 该人最近一次委托的时间
+
+4. **查看详情**
+   - 点击某个委托人后,使用"姓名+身份证号"查询详情
+   - 详情页面会展示该人所有的委托记录和交易信息
+
+5. **性能优化**
+   - 使用 GROUP BY 进行分组聚合
+   - 统计信息在SQL层面计算,性能优秀
+   - 交易编号查询通过 LEFT JOIN 关联交易表
+
+---
+
+## 版本历史
+
+| 版本 | 日期 | 修改内容 | 修改人 |
+|------|------|----------|--------|
+| v1.0 | 2025-11-21 | 初始版本,按记录查询 | 开发团队 |
+| v2.0 | 2025-11-21 | **重大更新:改为以人为维度查询,支持分组聚合** | 开发团队 |
+| v2.1 | 2025-11-21 | **新增:交易编号查询条件** | 开发团队 |
+
+---
+
+## 联系方式
+
+如有问题或建议,请联系:
+
+- **开发团队**:二手交易平台开发组
+- **服务名称**:alien-second
+- **Controller**:SecondEntrustUserController
+- **文档路径**:`alien-second/doc/委托人信息分页查询接口文档.md`
+
+---
+
+*最后更新时间:2025-11-21(v2.1 - 新增交易编号查询)*

+ 631 - 0
alien-second/doc/委托人详情查询接口文档.md

@@ -0,0 +1,631 @@
+# 委托人详情查询接口文档(以人为维度)
+
+## 接口概述
+
+### 接口信息
+
+| 项目 | 内容 |
+|------|------|
+| 接口名称 | 根据姓名和身份证号获取委托人详情 |
+| 接口路径 | `/secondEntrustUser/detail` |
+| 请求方式 | GET |
+| 接口版本 | v2.0 |
+| 开发时间 | 2025-11-21 |
+| 接口状态 | ✅ 已完成 |
+
+### 功能描述
+
+根据委托人的**姓名和身份证号**获取该人的完整详情信息,**以人为维度聚合查询所有相关数据**,包含:
+- 委托人基本信息
+- **该人所有委托记录关联的所有交易记录列表**
+- 每条交易记录包含:
+  - 交易基本信息(交易编号、金额、状态等)
+  - 买卖双方信息(姓名、电话)
+  - **交易操作节点信息(交易流程)**
+  - 商品信息
+
+**查询逻辑**:
+1. 根据"姓名+身份证号"查询该人的所有委托记录
+2. 收集所有委托记录关联的交易ID
+3. 批量查询所有交易记录及详细信息
+4. 为每笔交易添加操作节点信息(参考 `/admin/secondGoods/detail` 接口逻辑)
+
+---
+
+## 请求参数
+
+### Query Parameters
+
+| 参数名 | 类型 | 必填 | 描述 | 示例值 |
+|--------|------|------|------|--------|
+| userName | String | ✅ 是 | 委托人姓名 | 张三 |
+| idCard | String | ✅ 是 | 委托人身份证号 | 110101199001011234 |
+
+### 请求示例
+
+#### cURL
+
+```bash
+# 获取委托人详情
+curl -X GET "http://localhost:8080/secondEntrustUser/detail?userName=张三&idCard=110101199001011234" \
+  -H "Authorization: Bearer your_token_here"
+```
+
+#### JavaScript (Axios)
+
+```javascript
+// 获取委托人详情
+axios.get('/secondEntrustUser/detail', {
+  params: {
+    userName: '张三',
+    idCard: '110101199001011234'
+  },
+  headers: {
+    'Authorization': 'Bearer your_token_here'
+  }
+})
+.then(response => {
+  const { entrustUserInfo, tradeRecords } = response.data.data;
+  console.log('委托人信息:', entrustUserInfo);
+  console.log('该人所有交易记录:', tradeRecords);
+  console.log('交易总数:', tradeRecords.length);
+})
+.catch(error => {
+  console.error(error);
+});
+```
+
+#### Java (RestTemplate)
+
+```java
+String url = "http://localhost:8080/secondEntrustUser/detail" +
+             "?userName=张三&idCard=110101199001011234";
+RestTemplate restTemplate = new RestTemplate();
+R<SecondEntrustUserDetailVo> result = restTemplate.getForObject(url, R.class);
+
+SecondEntrustUserDetailVo detailVo = result.getData();
+SecondEntrustUser entrustUserInfo = detailVo.getEntrustUserInfo();
+List<SecondTradeRecordVo> tradeRecords = detailVo.getTradeRecords();
+
+System.out.println("委托人姓名:" + entrustUserInfo.getUserName());
+System.out.println("该人关联的交易总数:" + tradeRecords.size());
+```
+
+---
+
+## 响应参数
+
+### 响应结构
+
+| 字段名 | 类型 | 描述 |
+|--------|------|------|
+| code | Integer | 响应状态码,200表示成功 |
+| msg | String | 响应消息 |
+| data | SecondEntrustUserDetailVo | 委托人详情对象 |
+
+### SecondEntrustUserDetailVo 对象结构
+
+| 字段名 | 类型 | 描述 |
+|--------|------|------|
+| entrustUserInfo | SecondEntrustUser | 委托人基本信息(该人的第一条记录) |
+| tradeRecords | Array&lt;SecondTradeRecordVo&gt; | 该人所有委托记录关联的交易记录集合 |
+
+### SecondEntrustUser 委托人信息结构
+
+| 字段名 | 类型 | 是否必返 | 描述 | 示例值 |
+|--------|------|----------|------|--------|
+| id | Integer | ✅ | 委托人记录ID | 1 |
+| tradeId | Integer | ✅ | 关联的交易ID | 100 |
+| userName | String | ✅ | 委托人姓名 | 张三 |
+| userPhone | String | ✅ | 委托人电话 | 13800138000 |
+| idCard | String | ✅ | 委托人身份证号 | 110101199001011234 |
+| idCardImg | String | ❌ | 委托人身份证照片URL | https://cdn.example.com/idcard/123.jpg |
+| deleteFlag | Integer | ✅ | 删除标记(0:未删除,1:已删除) | 0 |
+| createdTime | Date | ✅ | 创建时间 | 2025-11-21 10:00:00 |
+| updatedTime | Date | ✅ | 更新时间 | 2025-11-21 10:00:00 |
+
+### SecondTradeRecordVo 交易记录结构
+
+#### 交易基本信息
+
+| 字段名 | 类型 | 是否必返 | 描述 | 示例值 |
+|--------|------|----------|------|--------|
+| id | Integer | ✅ | 交易记录ID | 100 |
+| tradeNo | String | ✅ | 交易编号 | S2025112100001 |
+| goodsId | Integer | ✅ | 商品ID | 200 |
+| goodsRecordId | Integer | ✅ | 商品记录ID | 300 |
+| transactionAmount | BigDecimal | ✅ | 交易金额(元) | 3800.00 |
+| tradeStatus | Integer | ✅ | 交易状态 | 4 |
+| transactionTime | Date | ❌ | 交易时间 | 2025-11-21 14:00:00 |
+| transactionLocation | String | ❌ | 交易地点 | 北京市朝阳区xxx |
+| createdTime | Date | ✅ | 创建时间 | 2025-11-21 10:00:00 |
+
+#### 买卖双方信息
+
+| 字段名 | 类型 | 是否必返 | 描述 | 示例值 |
+|--------|------|----------|------|--------|
+| buyerId | Integer | ✅ | 买方ID | 456 |
+| buyerName | String | ❌ | 买方姓名 | 李四 |
+| buyerPhone | String | ❌ | 买方电话 | 13900139000 |
+| sellerId | Integer | ✅ | 卖方ID | 789 |
+| sellerName | String | ❌ | 卖方姓名 | 王五 |
+| sellerPhone | String | ❌ | 卖方电话 | 13700137000 |
+
+#### 交易操作节点信息
+
+| 字段名 | 类型 | 是否必返 | 描述 |
+|--------|------|----------|------|
+| operationJsonList | Array&lt;JSONObject&gt; | ✅ | 交易操作节点列表,展示完整交易流程 |
+
+每个操作节点包含:
+- `operationType`: 操作类型
+- `operationTime`: 操作时间
+- `operationUserId`: 操作人ID
+- `operationUserName`: 操作人姓名
+- `operationDesc`: 操作描述
+
+---
+
+## 响应示例
+
+### 成功响应(张三有3条委托记录)
+
+```json
+{
+  "code": 200,
+  "msg": "成功",
+  "data": {
+    "entrustUserInfo": {
+      "id": 1,
+      "tradeId": 100,
+      "userName": "张三",
+      "userPhone": "13800138000",
+      "idCard": "110101199001011234",
+      "idCardImg": "https://cdn.example.com/idcard/123.jpg",
+      "deleteFlag": 0,
+      "createdTime": "2025-11-21 10:00:00",
+      "updatedTime": "2025-11-21 10:00:00"
+    },
+    "tradeRecords": [
+      {
+        "id": 102,
+        "tradeNo": "S2025112100003",
+        "goodsId": 202,
+        "goodsRecordId": 302,
+        "transactionAmount": 2800.00,
+        "tradeStatus": 4,
+        "transactionTime": "2025-11-21 15:30:00",
+        "transactionLocation": "北京市海淀区xxx",
+        "buyerId": 456,
+        "buyerName": "李四",
+        "buyerPhone": "13900139000",
+        "sellerId": 789,
+        "sellerName": "王五",
+        "sellerPhone": "13700137000",
+        "createdTime": "2025-11-21 15:00:00",
+        "operationJsonList": [
+          {
+            "operationType": "CREATE",
+            "operationTime": "2025-11-21 15:00:00",
+            "operationUserId": 456,
+            "operationUserName": "李四",
+            "operationDesc": "创建交易"
+          },
+          {
+            "operationType": "COMPLETE",
+            "operationTime": "2025-11-21 15:30:00",
+            "operationUserId": 456,
+            "operationUserName": "李四",
+            "operationDesc": "完成交易"
+          }
+        ]
+      },
+      {
+        "id": 101,
+        "tradeNo": "S2025112100002",
+        "goodsId": 201,
+        "goodsRecordId": 301,
+        "transactionAmount": 4500.00,
+        "tradeStatus": 4,
+        "transactionTime": "2025-11-21 14:00:00",
+        "transactionLocation": "北京市朝阳区xxx",
+        "buyerId": 457,
+        "buyerName": "赵六",
+        "buyerPhone": "13600136000",
+        "sellerId": 788,
+        "sellerName": "孙七",
+        "sellerPhone": "13500135000",
+        "createdTime": "2025-11-21 13:30:00",
+        "operationJsonList": [
+          {
+            "operationType": "CREATE",
+            "operationTime": "2025-11-21 13:30:00",
+            "operationUserId": 457,
+            "operationUserName": "赵六",
+            "operationDesc": "创建交易"
+          },
+          {
+            "operationType": "COMPLETE",
+            "operationTime": "2025-11-21 14:00:00",
+            "operationUserId": 457,
+            "operationUserName": "赵六",
+            "operationDesc": "完成交易"
+          }
+        ]
+      },
+      {
+        "id": 100,
+        "tradeNo": "S2025112100001",
+        "goodsId": 200,
+        "goodsRecordId": 300,
+        "transactionAmount": 3800.00,
+        "tradeStatus": 4,
+        "transactionTime": "2025-11-21 10:00:00",
+        "transactionLocation": "北京市东城区xxx",
+        "buyerId": 455,
+        "buyerName": "周八",
+        "buyerPhone": "13400134000",
+        "sellerId": 787,
+        "sellerName": "吴九",
+        "sellerPhone": "13300133000",
+        "createdTime": "2025-11-21 09:30:00",
+        "operationJsonList": [
+          {
+            "operationType": "CREATE",
+            "operationTime": "2025-11-21 09:30:00",
+            "operationUserId": 455,
+            "operationUserName": "周八",
+            "operationDesc": "创建交易"
+          },
+          {
+            "operationType": "COMPLETE",
+            "operationTime": "2025-11-21 10:00:00",
+            "operationUserId": 455,
+            "operationUserName": "周八",
+            "operationDesc": "完成交易"
+          }
+        ]
+      }
+    ]
+  }
+}
+```
+
+**说明**:张三(身份证号 110101199001011234)有3条委托记录,关联了3笔交易,接口返回了他所有的交易信息,按创建时间倒序排列。
+
+### 委托人不存在
+
+```json
+{
+  "code": 500,
+  "msg": "委托人信息不存在",
+  "data": null
+}
+```
+
+### 参数缺失
+
+```json
+{
+  "code": 500,
+  "msg": "委托人姓名不能为空",
+  "data": null
+}
+```
+
+---
+
+## 业务规则
+
+### 查询规则
+
+1. **查询维度**
+   - ✅ 以"姓名+身份证号"为唯一标识
+   - ✅ 查询该人的所有委托记录
+   - ✅ 聚合该人所有委托记录关联的交易信息
+
+2. **数据聚合**
+   - 根据姓名和身份证号查询所有委托记录
+   - 收集所有委托记录的 `tradeId`
+   - 批量查询所有交易记录(自动去重)
+   - 只返回未删除的记录(`delete_flag = 0`)
+
+3. **交易记录排序**
+   - ✅ 按交易创建时间倒序排列(最新的在前)
+
+4. **交易详情处理**
+   - ✅ 每条交易记录包含完整的买卖双方信息
+   - ✅ **自动加载交易操作节点信息(参考商品详情接口逻辑)**
+   - ✅ 操作节点按时间顺序排列,展示完整交易流程
+
+5. **数据关联**
+   - 委托人记录:从 `second_entrust_user` 表根据姓名和身份证号查询
+   - 交易记录:从 `second_trade_record` 表根据 `tradeId` 列表查询
+   - 买卖双方:通过 `buyer_id` 和 `seller_id` 关联 `life_user` 表
+   - 操作节点:从 `second_trade_operation` 表获取交易流程
+
+---
+
+## 交易状态说明
+
+### 交易状态 (tradeStatus)
+
+| 状态值 | 状态名称 | 说明 |
+|--------|----------|------|
+| 1 | 待确认 | 买家发起交易请求,等待卖家确认 |
+| 2 | 已拒绝 | 卖家拒绝了交易请求 |
+| 3 | 待交易 | 双方确认交易,等待线下交易 |
+| 4 | 交易成功 | 交易完成,双方确认成功 |
+| 5 | 交易失败 | 交易失败 |
+| 6 | 交易取消 | 交易被取消 |
+
+---
+
+## 使用场景
+
+### 场景:查看某个委托人的完整交易历史
+
+从分页列表点击某个委托人后,使用该接口获取该人的完整信息。
+
+**步骤**:
+1. 用户在分页列表看到"张三(110101199001011234)有3条委托记录"
+2. 点击"查看详情"
+3. 调用接口:`GET /secondEntrustUser/detail?userName=张三&idCard=110101199001011234`
+4. 返回张三的基本信息和他所有的交易记录(3笔)
+5. 每笔交易都包含完整的交易流程节点
+
+**应用场景**:
+- 委托人详情页展示
+- 交易历史查询
+- 纠纷处理和审核
+- 数据分析和统计
+
+---
+
+## 前端实现示例
+
+### Vue 3 + Element Plus
+
+```vue
+<template>
+  <div class="entrust-user-detail">
+    <el-page-header @back="goBack" title="返回列表">
+      <template #content>
+        <span class="page-title">委托人详情</span>
+      </template>
+    </el-page-header>
+
+    <!-- 委托人基本信息 -->
+    <el-card class="info-card" v-if="entrustUserInfo" style="margin-top: 20px;">
+      <template #header>
+        <div class="card-header">
+          <span>委托人信息</span>
+        </div>
+      </template>
+      <el-descriptions :column="2" border>
+        <el-descriptions-item label="姓名">
+          {{ entrustUserInfo.userName }}
+        </el-descriptions-item>
+        <el-descriptions-item label="身份证号">
+          {{ entrustUserInfo.idCard }}
+        </el-descriptions-item>
+        <el-descriptions-item label="联系电话">
+          {{ entrustUserInfo.userPhone }}
+        </el-descriptions-item>
+        <el-descriptions-item label="首次委托时间">
+          {{ entrustUserInfo.createdTime }}
+        </el-descriptions-item>
+      </el-descriptions>
+    </el-card>
+
+    <!-- 交易记录列表 -->
+    <el-card class="trade-card" style="margin-top: 20px;">
+      <template #header>
+        <div class="card-header">
+          <span>关联交易记录(共 {{ tradeRecords.length }} 笔)</span>
+        </div>
+      </template>
+      
+      <el-timeline v-if="tradeRecords.length > 0">
+        <el-timeline-item
+          v-for="trade in tradeRecords"
+          :key="trade.id"
+          :timestamp="trade.createdTime"
+          placement="top"
+        >
+          <el-card>
+            <h4>交易编号:{{ trade.tradeNo }}</h4>
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <p>交易金额:<el-tag type="danger">¥{{ trade.transactionAmount }}</el-tag></p>
+                <p>交易状态:<el-tag :type="getStatusType(trade.tradeStatus)">
+                  {{ getTradeStatusText(trade.tradeStatus) }}
+                </el-tag></p>
+              </el-col>
+              <el-col :span="12">
+                <p>买方:{{ trade.buyerName }}({{ trade.buyerPhone }})</p>
+                <p>卖方:{{ trade.sellerName }}({{ trade.sellerPhone }})</p>
+              </el-col>
+            </el-row>
+            
+            <!-- 交易操作流程 -->
+            <el-divider content-position="left">交易流程</el-divider>
+            <el-steps
+              :active="trade.operationJsonList.length"
+              finish-status="success"
+              align-center
+            >
+              <el-step
+                v-for="(operation, index) in trade.operationJsonList"
+                :key="index"
+                :title="operation.operationDesc"
+                :description="`${operation.operationTime} - ${operation.operationUserName}`"
+              />
+            </el-steps>
+          </el-card>
+        </el-timeline-item>
+      </el-timeline>
+      
+      <el-empty v-else description="暂无交易记录" />
+    </el-card>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+import axios from 'axios';
+import { ElMessage } from 'element-plus';
+
+const route = useRoute();
+const router = useRouter();
+const userName = ref(route.query.userName);
+const idCard = ref(route.query.idCard);
+const entrustUserInfo = ref(null);
+const tradeRecords = ref([]);
+const loading = ref(false);
+
+// 交易状态映射
+const tradeStatusMap = {
+  1: '待确认',
+  2: '已拒绝',
+  3: '待交易',
+  4: '交易成功',
+  5: '交易失败',
+  6: '交易取消'
+};
+
+const getTradeStatusText = (status) => {
+  return tradeStatusMap[status] || '未知状态';
+};
+
+const getStatusType = (status) => {
+  const typeMap = {
+    1: 'info',
+    2: 'danger',
+    3: 'warning',
+    4: 'success',
+    5: 'danger',
+    6: 'info'
+  };
+  return typeMap[status] || 'info';
+};
+
+const goBack = () => {
+  router.back();
+};
+
+// 加载委托人详情
+const loadDetail = async () => {
+  loading.value = true;
+  try {
+    const response = await axios.get('/secondEntrustUser/detail', {
+      params: {
+        userName: userName.value,
+        idCard: idCard.value
+      }
+    });
+    
+    const data = response.data.data;
+    entrustUserInfo.value = data.entrustUserInfo;
+    tradeRecords.value = data.tradeRecords || [];
+  } catch (error) {
+    ElMessage.error('加载失败:' + error.message);
+  } finally {
+    loading.value = false;
+  }
+};
+
+onMounted(() => {
+  if (userName.value && idCard.value) {
+    loadDetail();
+  } else {
+    ElMessage.error('缺少必要参数');
+    router.back();
+  }
+});
+</script>
+
+<style scoped>
+.entrust-user-detail {
+  padding: 20px;
+}
+
+.page-title {
+  font-size: 18px;
+  font-weight: bold;
+}
+
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+</style>
+```
+
+---
+
+## 错误码说明
+
+| HTTP状态码 | code | msg | 说明 | 解决方案 |
+|-----------|------|-----|------|----------|
+| 200 | 200 | 成功 | 请求成功 | - |
+| 200 | 500 | 委托人信息不存在 | 该姓名和身份证号组合不存在 | 检查参数是否正确 |
+| 200 | 500 | 委托人姓名不能为空 | 未传入姓名参数 | 必须传入userName参数 |
+| 200 | 500 | 委托人身份证号不能为空 | 未传入身份证号参数 | 必须传入idCard参数 |
+| 200 | 500 | 获取委托人详情失败: xxx | 系统异常 | 查看日志,联系后端开发人员 |
+| 500 | - | - | 服务器内部错误 | 联系后端开发人员 |
+
+---
+
+## 注意事项
+
+### ⚠️ 重要提示
+
+1. **必传参数**
+   - `userName` 和 `idCard` 都是必传参数
+   - 两个参数必须同时传入且正确匹配
+
+2. **查询维度**
+   - 以"姓名+身份证号"为唯一标识
+   - 返回该人所有委托记录关联的所有交易信息
+   - 同一个人可能有多笔交易
+
+3. **交易记录**
+   - 交易记录按创建时间倒序排列
+   - 只返回未删除的交易记录
+   - 每笔交易都包含完整的操作节点信息
+
+4. **操作节点信息**
+   - 参考了 `/admin/secondGoods/detail` 接口的实现逻辑
+   - 展示完整的交易流程
+   - 包含操作时间、操作人、操作描述等信息
+
+5. **性能考虑**
+   - 如果该人关联的交易记录过多,可能影响响应速度
+   - 建议在前端做分页展示或懒加载
+
+---
+
+## 版本历史
+
+| 版本 | 日期 | 修改内容 | 修改人 |
+|------|------|----------|--------|
+| v1.0 | 2025-11-21 | 初始版本,根据主键ID查询 | 开发团队 |
+| v2.0 | 2025-11-21 | **重大更新:改为根据姓名+身份证号查询,以人为维度聚合交易** | 开发团队 |
+
+---
+
+## 联系方式
+
+如有问题或建议,请联系:
+
+- **开发团队**:二手交易平台开发组
+- **服务名称**:alien-second
+- **Controller**:SecondEntrustUserController
+- **文档路径**:`alien-second/doc/委托人详情查询接口文档.md`
+
+---
+
+*最后更新时间:2025-11-21(v2.0 - 以人为维度)*

+ 44 - 0
alien-second/src/main/java/shop/alien/second/controller/SecondEntrustUserController.java

@@ -1,5 +1,6 @@
 package shop.alien.second.controller;
 
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import io.swagger.annotations.*;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -8,6 +9,9 @@ import org.springframework.web.bind.annotation.*;
 import shop.alien.entity.result.R;
 import shop.alien.entity.second.SecondEntrustUser;
 import shop.alien.entity.second.vo.SecondEntrustUserDTO;
+import shop.alien.entity.second.vo.SecondEntrustUserDetailVo;
+import shop.alien.entity.second.vo.SecondEntrustUserQueryVo;
+import shop.alien.entity.second.vo.SecondEntrustUserResultVo;
 import shop.alien.second.service.SecondEntrustUserService;
 
 import java.util.List;
@@ -158,5 +162,45 @@ public class SecondEntrustUserController {
         }
     }
 
+    @ApiOperation("分页查询委托人信息列表")
+    @ApiOperationSupport(order = 7)
+    @PostMapping("/page")
+    public R<IPage<SecondEntrustUserResultVo>> getEntrustUserPage(@RequestBody SecondEntrustUserQueryVo queryVo) {
+        log.info("SecondEntrustUserController.getEntrustUserPage queryVo={}", queryVo);
+        try {
+            // 设置默认分页参数
+            if (queryVo.getPageNum() == null || queryVo.getPageNum() < 1) {
+                queryVo.setPageNum(1);
+            }
+            if (queryVo.getPageSize() == null || queryVo.getPageSize() < 1) {
+                queryVo.setPageSize(10);
+            }
+            
+            IPage<SecondEntrustUserResultVo> page = secondEntrustUserService.getEntrustUserPage(queryVo);
+            return R.data(page);
+        } catch (Exception e) {
+            log.error("SecondEntrustUserController.getEntrustUserPage error: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
+    @ApiOperation("根据姓名和身份证号获取委托人详情")
+    @ApiOperationSupport(order = 8)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "entrustUserName", value = "委托人姓名", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "entrustIdCard", value = "委托人身份证号", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/detail")
+    public R<SecondEntrustUserDetailVo> getEntrustUserDetail(@RequestParam String entrustUserName, @RequestParam String entrustIdCard) {
+        log.info("SecondEntrustUserController.getEntrustUserDetail entrustUserName={}, entrustIdCard={}", entrustUserName, entrustIdCard);
+        try {
+            SecondEntrustUserDetailVo detailVo = secondEntrustUserService.getEntrustUserDetail(entrustUserName, entrustIdCard);
+            return R.data(detailVo);
+        } catch (Exception e) {
+            log.error("SecondEntrustUserController.getEntrustUserDetail error: {}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        }
+    }
+
 }
 

+ 21 - 0
alien-second/src/main/java/shop/alien/second/service/SecondEntrustUserService.java

@@ -1,8 +1,12 @@
 package shop.alien.second.service;
 
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
 import shop.alien.entity.second.SecondEntrustUser;
 import shop.alien.entity.second.vo.SecondEntrustUserDTO;
+import shop.alien.entity.second.vo.SecondEntrustUserDetailVo;
+import shop.alien.entity.second.vo.SecondEntrustUserQueryVo;
+import shop.alien.entity.second.vo.SecondEntrustUserResultVo;
 
 import java.util.List;
 
@@ -65,5 +69,22 @@ public interface SecondEntrustUserService extends IService<SecondEntrustUser> {
      */
     List<SecondEntrustUser> getByUserPhone(String entrustUserPhone);
 
+    /**
+     * 分页查询委托人信息列表(以姓名+身份证号为维度)
+     *
+     * @param queryVo 查询条件
+     * @return 委托人信息分页列表
+     */
+    IPage<SecondEntrustUserResultVo> getEntrustUserPage(SecondEntrustUserQueryVo queryVo);
+
+    /**
+     * 根据姓名和身份证号获取委托人详情(包含该人所有委托记录的交易信息)
+     *
+     * @param entrustUserName 委托人姓名
+     * @param entrustIdCard 委托人身份证号
+     * @return 委托人详情
+     */
+    SecondEntrustUserDetailVo getEntrustUserDetail(String entrustUserName, String entrustIdCard) throws Exception;
+
 }
 

+ 134 - 0
alien-second/src/main/java/shop/alien/second/service/impl/SecondEntrustUserServiceImpl.java

@@ -1,18 +1,31 @@
 package shop.alien.second.service.impl;
 
+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.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import shop.alien.entity.second.SecondEntrustUser;
+import shop.alien.entity.second.SecondTradeRecord;
 import shop.alien.entity.second.vo.SecondEntrustUserDTO;
+import shop.alien.entity.second.vo.SecondEntrustUserDetailVo;
+import shop.alien.entity.second.vo.SecondEntrustUserQueryVo;
+import shop.alien.entity.second.vo.SecondEntrustUserResultVo;
+import shop.alien.entity.second.vo.SecondTradeRecordVo;
 import shop.alien.mapper.second.SecondEntrustUserMapper;
+import shop.alien.mapper.second.SecondTradeRecordMapper;
+import shop.alien.second.service.PlatformSecondTradeService;
 import shop.alien.second.service.SecondEntrustUserService;
 import org.apache.commons.lang3.StringUtils;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -29,6 +42,8 @@ import java.util.List;
 public class SecondEntrustUserServiceImpl extends ServiceImpl<SecondEntrustUserMapper, SecondEntrustUser> implements SecondEntrustUserService {
 
     private final SecondEntrustUserMapper secondEntrustUserMapper;
+    private final SecondTradeRecordMapper secondTradeRecordMapper;
+    private final PlatformSecondTradeService platformSecondTradeService;
 
     /**
      * 创建委托人信息
@@ -158,5 +173,124 @@ public class SecondEntrustUserServiceImpl extends ServiceImpl<SecondEntrustUserM
         return this.list(wrapper);
     }
 
+    /**
+     * 分页查询委托人信息列表(以姓名+身份证号为维度)
+     *
+     * @param queryVo 查询条件
+     * @return 委托人信息分页列表
+     */
+    @Override
+    public IPage<SecondEntrustUserResultVo> getEntrustUserPage(SecondEntrustUserQueryVo queryVo) {
+        log.info("SecondEntrustUserServiceImpl.getEntrustUserPage queryVo={}", queryVo);
+        try {
+            // 创建分页对象
+            Page<SecondEntrustUser> page = new Page<>(queryVo.getPageNum(), queryVo.getPageSize());
+            
+            // 构建查询条件(用于HAVING子句)
+            QueryWrapper<SecondEntrustUser> wrapper = new QueryWrapper<>();
+            
+            // 委托人姓名模糊查询
+            if (StringUtils.isNotBlank(queryVo.getEntrustUserName())) {
+                wrapper.apply("entrust.entrust_user_name LIKE CONCAT('%', {0}, '%')", queryVo.getEntrustUserName());
+            }
+            
+            // 身份证号模糊查询
+            if (StringUtils.isNotBlank(queryVo.getEntrustIdCard())) {
+                wrapper.apply("entrust.entrust_id_card LIKE CONCAT('%', {0}, '%')", queryVo.getEntrustIdCard());
+            }
+            
+            // 委托人电话模糊查询
+            if (StringUtils.isNotBlank(queryVo.getEntrustUserPhone())) {
+                wrapper.apply("entrust.entrust_user_phone LIKE CONCAT('%', {0}, '%')", queryVo.getEntrustUserPhone());
+            }
+            
+            // 交易编号模糊查询
+            if (StringUtils.isNotBlank(queryVo.getEntrustTradeNo())) {
+                wrapper.apply("trade.trade_no LIKE CONCAT('%', {0}, '%')", queryVo.getEntrustTradeNo());
+            }
+            // deleteFlag = 0
+            wrapper.eq("entrust.delete_flag", 0);
+            
+            // 执行分页查询
+            return secondEntrustUserMapper.getEntrustUserPage(page, wrapper);
+        } catch (Exception e) {
+            log.error("SecondEntrustUserServiceImpl.getEntrustUserPage error: {}", e.getMessage(), e);
+            throw new RuntimeException("查询委托人信息列表失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 根据姓名和身份证号获取委托人详情(包含该人所有委托记录的交易信息)
+     *
+     * @param entrustUserName 委托人姓名
+     * @param entrustIdCard 委托人身份证号
+     * @return 委托人详情
+     */
+    @Override
+    public SecondEntrustUserDetailVo getEntrustUserDetail(String entrustUserName, String entrustIdCard) throws Exception {
+        log.info("SecondEntrustUserServiceImpl.getEntrustUserDetail entrustUserName={}, entrustIdCard={}", entrustUserName, entrustIdCard);
+        try {
+            // 1. 根据姓名和身份证号查询该人的所有委托记录
+            List<SecondEntrustUser> entrustUsers = secondEntrustUserMapper.getByUserNameAndIdCard(entrustUserName, entrustIdCard);
+            
+            if (entrustUsers == null || entrustUsers.isEmpty()) {
+                log.warn("委托人信息不存在, entrustUserName={}, entrustIdCard={}", entrustUserName, entrustIdCard);
+                throw new RuntimeException("委托人信息不存在");
+            }
+            
+            // 2. 创建返回对象,使用第一条记录作为基本信息(同一个人的基本信息相同)
+            SecondEntrustUserDetailVo detailVo = new SecondEntrustUserDetailVo();
+            SecondEntrustUser firstRecord = entrustUsers.get(0);
+            detailVo.setEntrustUserInfo(firstRecord);
+            
+            // 3. 收集该人所有委托记录关联的交易ID
+            List<Integer> tradeIds = new ArrayList<>();
+            for (SecondEntrustUser entrustUser : entrustUsers) {
+                if (entrustUser.getEntrustTradeId() != null) {
+                    tradeIds.add(entrustUser.getEntrustTradeId());
+                }
+            }
+            
+            // 4. 查询所有关联的交易记录
+            List<SecondTradeRecordVo> tradeRecordVos = new ArrayList<>();
+            
+            if (!tradeIds.isEmpty()) {
+                // 去重
+                tradeIds = tradeIds.stream().distinct().collect(java.util.stream.Collectors.toList());
+                
+                // 批量查询交易记录
+                QueryWrapper<SecondTradeRecord> tradeWrapper = new QueryWrapper<>();
+                tradeWrapper.in("id", tradeIds)
+                        .eq("delete_flag", 0)
+                        .orderByDesc("created_time");
+                
+                List<SecondTradeRecord> tradeRecords = secondTradeRecordMapper.selectList(tradeWrapper);
+                
+                // 5. 处理每条交易记录,添加操作节点信息和用户信息
+                if (tradeRecords != null && !tradeRecords.isEmpty()) {
+                    for (SecondTradeRecord tradeRecord : tradeRecords) {
+                        // 获取完整的交易记录信息(包含买卖双方信息)
+                        SecondTradeRecordVo tradeRecordVo = secondTradeRecordMapper.getTradeRecordById(tradeRecord.getId());
+                        
+                        if (tradeRecordVo != null) {
+                            // 添加交易操作节点信息
+                            List<JSONObject> operationJsonList = platformSecondTradeService.getOperationJsonList(tradeRecord.getId());
+                            tradeRecordVo.setOperationJsonList(operationJsonList);
+                            
+                            tradeRecordVos.add(tradeRecordVo);
+                        }
+                    }
+                }
+            }
+            
+            detailVo.setTradeRecords(tradeRecordVos);
+            
+            return detailVo;
+        } catch (Exception e) {
+            log.error("SecondEntrustUserServiceImpl.getEntrustUserDetail error: {}", e.getMessage(), e);
+            throw new Exception("获取委托人详情失败: " + e.getMessage());
+        }
+    }
+
 }