|
|
@@ -0,0 +1,1009 @@
|
|
|
+# Web端商户提现记录查询接口文档
|
|
|
+
|
|
|
+## 模块概述
|
|
|
+
|
|
|
+本模块提供提现记录查询功能,支持按时间范围、提现状态等条件筛选提现记录,并提供分页查询和统计信息。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 接口信息
|
|
|
+
|
|
|
+### 提现记录查询
|
|
|
+
|
|
|
+#### 接口详情
|
|
|
+
|
|
|
+- **接口名称**: 提现记录查询
|
|
|
+- **接口路径**: `GET /incomeManage/getCashOutRecordList`
|
|
|
+- **请求方式**: GET
|
|
|
+- **接口描述**: 查询指定门店的提现记录,支持按时间范围和提现状态筛选,返回分页数据和统计信息
|
|
|
+- **登录验证**: ✅ 需要(使用 `@LoginRequired` 注解)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 请求参数
|
|
|
+
|
|
|
+| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|
|
|
+|--------|------|------|------|--------|
|
|
|
+| storeId | Integer | 是 | 门店ID | 103 |
|
|
|
+| cashOutStartTime | String | 否 | 开始时间(格式:yyyy-MM-dd) | 2025-11-10 |
|
|
|
+| cashOutEndTime | String | 否 | 结束时间(格式:yyyy-MM-dd) | 2025-11-11 |
|
|
|
+| paymentStatus | String | 否 | 提现状态 | 1 |
|
|
|
+| page | Integer | 否 | 页码,默认1 | 1 |
|
|
|
+| size | Integer | 否 | 每页条数,默认10 | 10 |
|
|
|
+
|
|
|
+### 参数说明
|
|
|
+
|
|
|
+#### paymentStatus(提现状态)
|
|
|
+- `1`: 待审核
|
|
|
+- `2`: 审核不通过
|
|
|
+- `3`: 已通过
|
|
|
+- `4`: 已打款
|
|
|
+- `5`: 打款失败
|
|
|
+- 不传:查询所有状态
|
|
|
+
|
|
|
+#### 时间范围
|
|
|
+- **cashOutStartTime**: 查询此日期(含)之后创建的提现记录
|
|
|
+- **cashOutEndTime**: 查询此日期(含)之前创建的提现记录
|
|
|
+- 不传时间参数:查询所有时间的记录
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 请求示例
|
|
|
+
|
|
|
+### 示例1: 查询所有提现记录
|
|
|
+
|
|
|
+```http
|
|
|
+GET /incomeManage/getCashOutRecordList?storeId=103&page=1&size=10
|
|
|
+```
|
|
|
+
|
|
|
+```bash
|
|
|
+curl "http://localhost:8080/incomeManage/getCashOutRecordList?storeId=103&page=1&size=10" \
|
|
|
+ -H "Authorization: Bearer YOUR_TOKEN"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 示例2: 按时间范围查询
|
|
|
+
|
|
|
+```http
|
|
|
+GET /incomeManage/getCashOutRecordList?storeId=103&cashOutStartTime=2025-11-10&cashOutEndTime=2025-11-11&page=1&size=10
|
|
|
+```
|
|
|
+
|
|
|
+```bash
|
|
|
+curl "http://localhost:8080/incomeManage/getCashOutRecordList?storeId=103&cashOutStartTime=2025-11-10&cashOutEndTime=2025-11-11&page=1&size=10" \
|
|
|
+ -H "Authorization: Bearer YOUR_TOKEN"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 示例3: 查询待审核记录
|
|
|
+
|
|
|
+```http
|
|
|
+GET /incomeManage/getCashOutRecordList?storeId=103&paymentStatus=1&page=1&size=10
|
|
|
+```
|
|
|
+
|
|
|
+```bash
|
|
|
+curl "http://localhost:8080/incomeManage/getCashOutRecordList?storeId=103&paymentStatus=1&page=1&size=10" \
|
|
|
+ -H "Authorization: Bearer YOUR_TOKEN"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 示例4: 组合查询
|
|
|
+
|
|
|
+```http
|
|
|
+GET /incomeManage/getCashOutRecordList?storeId=103&cashOutStartTime=2025-11-10&cashOutEndTime=2025-11-11&paymentStatus=4&page=1&size=10
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 响应参数
|
|
|
+
|
|
|
+### 成功响应
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "code": 200,
|
|
|
+ "success": true,
|
|
|
+ "data": {
|
|
|
+ "cashOutRecordList": [
|
|
|
+ {
|
|
|
+ "id": 1001,
|
|
|
+ "storeId": 103,
|
|
|
+ "storeUserId": 114,
|
|
|
+ "money": 50000,
|
|
|
+ "commission": 0,
|
|
|
+ "cashOutType": 0,
|
|
|
+ "paymentStatus": 1,
|
|
|
+ "orderNo": "TX20251119001",
|
|
|
+ "aliOrderNo": null,
|
|
|
+ "paymentDate": "2025-11-19 10:30:00",
|
|
|
+ "payDate": "2025-11-19 10:30:00",
|
|
|
+ "settlementAccount": "13800138000",
|
|
|
+ "failReason": null,
|
|
|
+ "deleteFlag": 0,
|
|
|
+ "createdTime": "2025-11-19 10:30:00",
|
|
|
+ "updatedTime": "2025-11-19 10:30:00"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "id": 1000,
|
|
|
+ "storeId": 103,
|
|
|
+ "storeUserId": 114,
|
|
|
+ "money": 30000,
|
|
|
+ "commission": 0,
|
|
|
+ "cashOutType": 0,
|
|
|
+ "paymentStatus": 4,
|
|
|
+ "orderNo": "TX20251118001",
|
|
|
+ "aliOrderNo": "2025111822001234567890123456",
|
|
|
+ "paymentDate": "2025-11-18 15:20:00",
|
|
|
+ "payDate": "2025-11-18 15:25:00",
|
|
|
+ "settlementAccount": "alipay@example.com",
|
|
|
+ "failReason": null,
|
|
|
+ "deleteFlag": 0,
|
|
|
+ "createdTime": "2025-11-18 15:20:00",
|
|
|
+ "updatedTime": "2025-11-18 15:25:00"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ "cashOutAllMoney": 800.00,
|
|
|
+ "cashOutNum": 15
|
|
|
+ },
|
|
|
+ "msg": "操作成功"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 响应字段说明
|
|
|
+
|
|
|
+#### 外层字段
|
|
|
+
|
|
|
+| 字段名 | 类型 | 说明 |
|
|
|
+|--------|------|------|
|
|
|
+| data.cashOutRecordList | Array | 提现记录列表(当前页) |
|
|
|
+| data.cashOutAllMoney | BigDecimal | 提现总金额(仅统计待审核状态,单位:元) |
|
|
|
+| data.cashOutNum | Integer | 提现记录总数 |
|
|
|
+
|
|
|
+#### cashOutRecordList 字段
|
|
|
+
|
|
|
+| 字段名 | 类型 | 说明 |
|
|
|
+|--------|------|------|
|
|
|
+| id | Long | 提现记录ID |
|
|
|
+| storeId | Integer | 门店ID |
|
|
|
+| storeUserId | Integer | 用户ID |
|
|
|
+| money | Integer | 提现金额(单位:分) |
|
|
|
+| commission | Integer | 手续费(单位:分) |
|
|
|
+| cashOutType | Integer | 提现类型(0-全部提现) |
|
|
|
+| paymentStatus | Integer | 提现状态 |
|
|
|
+| orderNo | String | 提现订单号 |
|
|
|
+| aliOrderNo | String | 支付宝订单号 |
|
|
|
+| paymentDate | Date | 申请时间 |
|
|
|
+| payDate | Date | 打款时间 |
|
|
|
+| failReason | String | 失败原因 |
|
|
|
+| settlementAccount | String | 结算账户(支付宝账号或手机号) |
|
|
|
+| deleteFlag | Integer | 删除标识 |
|
|
|
+| createdTime | Date | 创建时间 |
|
|
|
+| updatedTime | Date | 更新时间 |
|
|
|
+
|
|
|
+**⚠️ 重要字段说明**:
|
|
|
+- `settlementAccount`: 通过 LEFT JOIN `store_user` 表查询得到
|
|
|
+ - 优先使用商户的支付宝账号(`alipay_account`)
|
|
|
+ - 如果没有支付宝账号,则使用手机号(`phone`)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 失败响应
|
|
|
+
|
|
|
+#### 1. 参数错误
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "code": 500,
|
|
|
+ "success": false,
|
|
|
+ "data": null,
|
|
|
+ "msg": "查询失败:门店ID不能为空"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### 2. 未登录或登录过期
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "code": 500,
|
|
|
+ "success": false,
|
|
|
+ "data": null,
|
|
|
+ "msg": "请先登录"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### 3. 系统异常
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "code": 500,
|
|
|
+ "success": false,
|
|
|
+ "data": null,
|
|
|
+ "msg": "查询失败:{异常信息}"
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 业务逻辑说明
|
|
|
+
|
|
|
+### 查询流程
|
|
|
+
|
|
|
+```
|
|
|
+┌─────────────────────────────────────────────┐
|
|
|
+│ 1. 接收查询参数 │
|
|
|
+│ - storeId (必填) │
|
|
|
+│ - cashOutStartTime (可选) │
|
|
|
+│ - cashOutEndTime (可选) │
|
|
|
+│ - paymentStatus (可选) │
|
|
|
+│ - page, size (分页) │
|
|
|
+└─────────────────┬───────────────────────────┘
|
|
|
+ ↓
|
|
|
+┌─────────────────────────────────────────────┐
|
|
|
+│ 2. 构建查询条件 │
|
|
|
+│ wrapper.eq(storeId) │
|
|
|
+│ IF paymentStatus != null │
|
|
|
+│ wrapper.eq(paymentStatus) │
|
|
|
+│ IF cashOutEndTime != null │
|
|
|
+│ wrapper.le(createdTime, endTime 23:59:59) │
|
|
|
+│ IF cashOutStartTime != null │
|
|
|
+│ wrapper.ge(createdTime, startTime 00:00:00)│
|
|
|
+│ wrapper.orderByDesc(createdTime) │
|
|
|
+└─────────────────┬───────────────────────────┘
|
|
|
+ ↓
|
|
|
+┌─────────────────────────────────────────────┐
|
|
|
+│ 3. 查询所有符合条件的记录 │
|
|
|
+│ List<StoreCashOutRecord> recordList │
|
|
|
+└─────────────────┬───────────────────────────┘
|
|
|
+ ↓
|
|
|
+┌─────────────────────────────────────────────┐
|
|
|
+│ 4. 手动分页 │
|
|
|
+│ IPage page = ListToPage.setPage(list, p, s)│
|
|
|
+└─────────────────┬───────────────────────────┘
|
|
|
+ ↓
|
|
|
+┌─────────────────────────────────────────────┐
|
|
|
+│ 5. 构建返回VO │
|
|
|
+│ vo.cashOutRecordList = page.getRecords() │
|
|
|
+│ vo.cashOutAllMoney = sum(money[status=1]) ÷ 100 │
|
|
|
+│ vo.cashOutNum = list.size() │
|
|
|
+└─────────────────┬───────────────────────────┘
|
|
|
+ ↓
|
|
|
+┌─────────────────────────────────────────────┐
|
|
|
+│ 6. 返回结果 │
|
|
|
+│ 返回分页数据和统计信息 │
|
|
|
+└─────────────────────────────────────────────┘
|
|
|
+```
|
|
|
+
|
|
|
+### 查询条件说明
|
|
|
+
|
|
|
+#### 1. 必填条件
|
|
|
+- **storeId**: 门店ID(必须)
|
|
|
+
|
|
|
+#### 2. 可选条件(AND关系)
|
|
|
+- **paymentStatus**: 提现状态筛选
|
|
|
+- **cashOutStartTime**: 开始时间(含当天 00:00:00)
|
|
|
+- **cashOutEndTime**: 结束时间(含当天 23:59:59)
|
|
|
+
|
|
|
+#### 3. 排序规则
|
|
|
+- 按创建时间倒序排序(最新的在前)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 数据库查询
|
|
|
+
|
|
|
+### 查询SQL
|
|
|
+
|
|
|
+```sql
|
|
|
+-- 使用自定义Mapper方法 selectCashoutRecordList
|
|
|
+SELECT scor.*,
|
|
|
+ IF(su.alipay_account IS NOT NULL, su.alipay_account, su.phone) AS settlement_account
|
|
|
+FROM store_cash_out_record scor
|
|
|
+LEFT JOIN store_user su ON su.store_id = scor.store_id AND su.delete_flag = 0
|
|
|
+WHERE scor.store_id = ?
|
|
|
+ AND scor.delete_flag = 0
|
|
|
+ AND (scor.payment_status = ? OR ? IS NULL)
|
|
|
+ AND (scor.created_time <= ? OR ? IS NULL)
|
|
|
+ AND (scor.created_time >= ? OR ? IS NULL)
|
|
|
+ORDER BY scor.created_time DESC
|
|
|
+```
|
|
|
+
|
|
|
+**SQL说明**:
|
|
|
+- 🔔 使用 LEFT JOIN 关联 `store_user` 表获取结算账户信息
|
|
|
+- 📌 `settlement_account` 字段逻辑:
|
|
|
+ - 优先返回 `alipay_account`(支付宝账号)
|
|
|
+ - 如果为空,返回 `phone`(手机号)
|
|
|
+- 🔄 表别名:`scor` = store_cash_out_record,`su` = store_user
|
|
|
+
|
|
|
+### 索引建议
|
|
|
+
|
|
|
+```sql
|
|
|
+-- store_cash_out_record 表索引
|
|
|
+ALTER TABLE store_cash_out_record
|
|
|
+ADD INDEX idx_store_status_time (store_id, payment_status, created_time);
|
|
|
+
|
|
|
+-- store_user 表索引(用于LEFT JOIN)
|
|
|
+ALTER TABLE store_user
|
|
|
+ADD INDEX idx_store_id_delete (store_id, delete_flag);
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 提现状态说明
|
|
|
+
|
|
|
+### payment_status 状态值
|
|
|
+
|
|
|
+| Status | 名称 | 描述 | 可查询 |
|
|
|
+|--------|------|------|--------|
|
|
|
+| 1 | 待审核 | 提现申请已提交,等待管理员审核 | ✅ |
|
|
|
+| 2 | 审核不通过 | 管理员拒绝提现申请 | ✅ |
|
|
|
+| 3 | 已通过 | 管理员审核通过,等待打款 | ✅ |
|
|
|
+| 4 | 已打款 | 支付宝转账成功 | ✅ |
|
|
|
+| 5 | 打款失败 | 支付宝转账失败 | ✅ |
|
|
|
+
|
|
|
+### 状态流转
|
|
|
+
|
|
|
+```
|
|
|
+1(待审核) ──审核通过──→ 3(已通过) ──打款成功──→ 4(已打款)
|
|
|
+ │ │
|
|
|
+ │ └──打款失败──→ 5(打款失败)
|
|
|
+ │
|
|
|
+ └──审核拒绝──→ 2(审核不通过)
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 金额计算规则
|
|
|
+
|
|
|
+### 金额单位转换
|
|
|
+
|
|
|
+| 数据库存储(分) | 显示(元) | 转换公式 |
|
|
|
+|------------------|-----------|----------|
|
|
|
+| 50000 | 500.00 | money ÷ 100 |
|
|
|
+| 30000 | 300.00 | money ÷ 100 |
|
|
|
+| 80000 | 800.00 | money ÷ 100 |
|
|
|
+
|
|
|
+### cashOutAllMoney 计算
|
|
|
+
|
|
|
+```java
|
|
|
+// 仅统计状态为1-待审核的记录总金额
|
|
|
+int totalMoney = recordList.stream()
|
|
|
+ .filter(item -> "1".equals(item.getPaymentStatus().toString()))
|
|
|
+ .mapToInt(StoreCashOutRecord::getMoney)
|
|
|
+ .sum();
|
|
|
+
|
|
|
+// 转换为元(向下取整)
|
|
|
+BigDecimal cashOutAllMoney = new BigDecimal(totalMoney)
|
|
|
+ .divide(new BigDecimal(100), 2, RoundingMode.DOWN);
|
|
|
+```
|
|
|
+
|
|
|
+**说明**:
|
|
|
+- 🔔 **重要变更(2025-11-20)**: 仅统计 `paymentStatus = 1`(待审核)状态的记录总金额
|
|
|
+- ⚠️ 统计的是所有符合条件且状态为待审核的记录,不是当前页
|
|
|
+- 使用 `RoundingMode.DOWN`(向下取整)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 业务场景
|
|
|
+
|
|
|
+### 场景 1: 查询所有提现记录
|
|
|
+
|
|
|
+**请求**:
|
|
|
+```http
|
|
|
+GET /incomeManage/getCashOutRecordList?storeId=103&page=1&size=10
|
|
|
+```
|
|
|
+
|
|
|
+**响应**:
|
|
|
+```json
|
|
|
+{
|
|
|
+ "code": 200,
|
|
|
+ "data": {
|
|
|
+ "cashOutRecordList": [...], // 第1页,10条记录
|
|
|
+ "cashOutAllMoney": 500.00, // 所有待审核记录总金额
|
|
|
+ "cashOutNum": 25 // 所有记录总数
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**说明**:
|
|
|
+- 不传任何筛选条件,返回该门店的所有提现记录
|
|
|
+- `cashOutAllMoney` 仅统计状态为"待审核"的记录总金额
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 场景 2: 查询指定时间范围
|
|
|
+
|
|
|
+**请求**:
|
|
|
+```http
|
|
|
+GET /incomeManage/getCashOutRecordList?storeId=103&cashOutStartTime=2025-11-10&cashOutEndTime=2025-11-11&page=1&size=10
|
|
|
+```
|
|
|
+
|
|
|
+**响应**:
|
|
|
+```json
|
|
|
+{
|
|
|
+ "code": 200,
|
|
|
+ "data": {
|
|
|
+ "cashOutRecordList": [...],
|
|
|
+ "cashOutAllMoney": 200.00, // 2025-11-10至2025-11-11期间待审核记录的总金额
|
|
|
+ "cashOutNum": 5 // 期间的总记录数
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**说明**:
|
|
|
+- 只返回指定时间范围内的提现记录
|
|
|
+- `cashOutAllMoney` 仅统计该时间范围内待审核状态的记录
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 场景 3: 查询待审核记录
|
|
|
+
|
|
|
+**请求**:
|
|
|
+```http
|
|
|
+GET /incomeManage/getCashOutRecordList?storeId=103&paymentStatus=1&page=1&size=10
|
|
|
+```
|
|
|
+
|
|
|
+**响应**:
|
|
|
+```json
|
|
|
+{
|
|
|
+ "code": 200,
|
|
|
+ "data": {
|
|
|
+ "cashOutRecordList": [...],
|
|
|
+ "cashOutAllMoney": 300.00, // 待审核记录总金额
|
|
|
+ "cashOutNum": 3 // 待审核记录总数
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**说明**: 只返回待审核状态的提现记录,`cashOutAllMoney` 统计所有待审核记录的总金额
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 场景 4: 组合查询
|
|
|
+
|
|
|
+**请求**:
|
|
|
+```http
|
|
|
+GET /incomeManage/getCashOutRecordList?storeId=103&cashOutStartTime=2025-11-01&cashOutEndTime=2025-11-30&paymentStatus=4&page=1&size=10
|
|
|
+```
|
|
|
+
|
|
|
+**响应**:
|
|
|
+```json
|
|
|
+{
|
|
|
+ "code": 200,
|
|
|
+ "data": {
|
|
|
+ "cashOutRecordList": [...],
|
|
|
+ "cashOutAllMoney": 0.00, // 11月已打款记录中待审核的总金额(0元,因为已打款状态不是待审核)
|
|
|
+ "cashOutNum": 10 // 11月已打款记录总数
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**说明**:
|
|
|
+- 查询11月已打款的提现记录
|
|
|
+- ⚠️ **注意**: 由于 `cashOutAllMoney` 只统计待审核状态,而查询条件是已打款状态,所以 `cashOutAllMoney` 为0
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 场景 5: 空结果
|
|
|
+
|
|
|
+**前置条件**: 指定条件下无记录
|
|
|
+
|
|
|
+**请求**:
|
|
|
+```http
|
|
|
+GET /incomeManage/getCashOutRecordList?storeId=103&paymentStatus=5&page=1&size=10
|
|
|
+```
|
|
|
+
|
|
|
+**响应**:
|
|
|
+```json
|
|
|
+{
|
|
|
+ "code": 200,
|
|
|
+ "data": {
|
|
|
+ "cashOutRecordList": [],
|
|
|
+ "cashOutAllMoney": 0.00,
|
|
|
+ "cashOutNum": 0
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 分页说明
|
|
|
+
|
|
|
+### 分页逻辑
|
|
|
+
|
|
|
+1. **查询全部**: 首先查询所有符合条件的记录
|
|
|
+2. **手动分页**: 使用 `ListToPage.setPage()` 进行内存分页
|
|
|
+3. **返回当前页**: `cashOutRecordList` 只包含当前页的记录
|
|
|
+4. **统计全部**: `cashOutNum` 统计所有记录,`cashOutAllMoney` 仅统计待审核状态的记录
|
|
|
+
|
|
|
+### 分页参数
|
|
|
+
|
|
|
+- **page**: 页码,从1开始
|
|
|
+- **size**: 每页条数,默认10
|
|
|
+- **total**: 总记录数(返回值中的 `cashOutNum`)
|
|
|
+
|
|
|
+### 分页示例
|
|
|
+
|
|
|
+**场景**: 共25条记录,每页10条
|
|
|
+
|
|
|
+- **第1页** (page=1, size=10): 显示第1-10条
|
|
|
+- **第2页** (page=2, size=10): 显示第11-20条
|
|
|
+- **第3页** (page=3, size=10): 显示第21-25条
|
|
|
+
|
|
|
+**统计信息**:
|
|
|
+- `cashOutNum = 25` (所有页的总记录数)
|
|
|
+- `cashOutAllMoney` = 所有25条记录中待审核状态的总金额
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 前端集成示例
|
|
|
+
|
|
|
+### Vue.js 示例
|
|
|
+
|
|
|
+```javascript
|
|
|
+export default {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ storeId: 103,
|
|
|
+ cashOutStartTime: '',
|
|
|
+ cashOutEndTime: '',
|
|
|
+ paymentStatus: '',
|
|
|
+ page: 1,
|
|
|
+ size: 10,
|
|
|
+ cashOutRecordList: [],
|
|
|
+ cashOutAllMoney: 0,
|
|
|
+ cashOutNum: 0
|
|
|
+ };
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ async getCashOutRecordList() {
|
|
|
+ try {
|
|
|
+ const params = {
|
|
|
+ storeId: this.storeId,
|
|
|
+ page: this.page,
|
|
|
+ size: this.size
|
|
|
+ };
|
|
|
+
|
|
|
+ // 可选参数
|
|
|
+ if (this.cashOutStartTime) {
|
|
|
+ params.cashOutStartTime = this.cashOutStartTime;
|
|
|
+ }
|
|
|
+ if (this.cashOutEndTime) {
|
|
|
+ params.cashOutEndTime = this.cashOutEndTime;
|
|
|
+ }
|
|
|
+ if (this.paymentStatus) {
|
|
|
+ params.paymentStatus = this.paymentStatus;
|
|
|
+ }
|
|
|
+
|
|
|
+ const response = await this.$axios.get('/incomeManage/getCashOutRecordList', { params });
|
|
|
+
|
|
|
+ if (response.data.success) {
|
|
|
+ const data = response.data.data;
|
|
|
+ this.cashOutRecordList = data.cashOutRecordList;
|
|
|
+ this.cashOutAllMoney = data.cashOutAllMoney;
|
|
|
+ this.cashOutNum = data.cashOutNum;
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('查询失败:', error);
|
|
|
+ this.$message.error('查询失败');
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 翻页
|
|
|
+ handlePageChange(page) {
|
|
|
+ this.page = page;
|
|
|
+ this.getCashOutRecordList();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.getCashOutRecordList();
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### React 示例
|
|
|
+
|
|
|
+```javascript
|
|
|
+import { useState, useEffect } from 'react';
|
|
|
+import axios from 'axios';
|
|
|
+
|
|
|
+function CashOutRecordList() {
|
|
|
+ const [storeId] = useState(103);
|
|
|
+ const [cashOutStartTime, setCashOutStartTime] = useState('');
|
|
|
+ const [cashOutEndTime, setCashOutEndTime] = useState('');
|
|
|
+ const [paymentStatus, setPaymentStatus] = useState('');
|
|
|
+ const [page, setPage] = useState(1);
|
|
|
+ const [size] = useState(10);
|
|
|
+ const [data, setData] = useState({
|
|
|
+ cashOutRecordList: [],
|
|
|
+ cashOutAllMoney: 0,
|
|
|
+ cashOutNum: 0
|
|
|
+ });
|
|
|
+
|
|
|
+ const getCashOutRecordList = async () => {
|
|
|
+ try {
|
|
|
+ const params = {
|
|
|
+ storeId,
|
|
|
+ page,
|
|
|
+ size,
|
|
|
+ ...(cashOutStartTime && { cashOutStartTime }),
|
|
|
+ ...(cashOutEndTime && { cashOutEndTime }),
|
|
|
+ ...(paymentStatus && { paymentStatus })
|
|
|
+ };
|
|
|
+
|
|
|
+ const response = await axios.get('/incomeManage/getCashOutRecordList', { params });
|
|
|
+
|
|
|
+ if (response.data.success) {
|
|
|
+ setData(response.data.data);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('查询失败:', error);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ getCashOutRecordList();
|
|
|
+ }, [page, cashOutStartTime, cashOutEndTime, paymentStatus]);
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ <h2>提现记录</h2>
|
|
|
+ <p>总记录数: {data.cashOutNum}</p>
|
|
|
+ <p>总金额: ¥{data.cashOutAllMoney}</p>
|
|
|
+
|
|
|
+ <ul>
|
|
|
+ {data.cashOutRecordList.map(record => (
|
|
|
+ <li key={record.id}>
|
|
|
+ 订单号: {record.orderNo} - 金额: ¥{(record.money / 100).toFixed(2)} - 状态: {record.paymentStatus}
|
|
|
+ </li>
|
|
|
+ ))}
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 测试用例
|
|
|
+
|
|
|
+### 测试场景1: 正常查询
|
|
|
+
|
|
|
+**请求**:
|
|
|
+```http
|
|
|
+GET /incomeManage/getCashOutRecordList?storeId=103&page=1&size=10
|
|
|
+```
|
|
|
+
|
|
|
+**预期结果**:
|
|
|
+- 返回提现记录列表
|
|
|
+- 包含统计信息(总金额、总数)
|
|
|
+- 按创建时间倒序排序
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 测试场景2: 时间范围查询
|
|
|
+
|
|
|
+**请求**:
|
|
|
+```http
|
|
|
+GET /incomeManage/getCashOutRecordList?storeId=103&cashOutStartTime=2025-11-10&cashOutEndTime=2025-11-11
|
|
|
+```
|
|
|
+
|
|
|
+**预期结果**:
|
|
|
+- 只返回2025-11-10至2025-11-11的记录
|
|
|
+- 包含这两天 00:00:00 到 23:59:59 的记录
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 测试场景3: 状态筛选
|
|
|
+
|
|
|
+**请求**:
|
|
|
+```http
|
|
|
+GET /incomeManage/getCashOutRecordList?storeId=103&paymentStatus=1
|
|
|
+```
|
|
|
+
|
|
|
+**预期结果**:
|
|
|
+- 只返回待审核状态的记录
|
|
|
+- 其他状态的记录不返回
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 测试场景4: 空结果
|
|
|
+
|
|
|
+**请求**:
|
|
|
+```http
|
|
|
+GET /incomeManage/getCashOutRecordList?storeId=999999&page=1&size=10
|
|
|
+```
|
|
|
+
|
|
|
+**预期结果**:
|
|
|
+```json
|
|
|
+{
|
|
|
+ "cashOutRecordList": [],
|
|
|
+ "cashOutAllMoney": 0.00,
|
|
|
+ "cashOutNum": 0
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 测试场景5: 分页测试
|
|
|
+
|
|
|
+**前置条件**: 共25条记录
|
|
|
+
|
|
|
+**请求**:
|
|
|
+```http
|
|
|
+GET /incomeManage/getCashOutRecordList?storeId=103&page=3&size=10
|
|
|
+```
|
|
|
+
|
|
|
+**预期结果**:
|
|
|
+- 返回第21-25条记录(5条)
|
|
|
+- `cashOutNum` 仍为25
|
|
|
+- `cashOutAllMoney` 为所有25条记录中待审核状态的总金额
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 注意事项
|
|
|
+
|
|
|
+### 1. 时间范围
|
|
|
+
|
|
|
+- ⚠️ **时间格式**: 必须是 `yyyy-MM-dd`
|
|
|
+- ⚠️ **开始时间**: 自动补充 `00:00:00`
|
|
|
+- ⚠️ **结束时间**: 自动补充 `23:59:59`
|
|
|
+- ⚠️ **时区**: 使用服务器时区
|
|
|
+
|
|
|
+### 2. 分页逻辑
|
|
|
+
|
|
|
+- ⚠️ **查询全部**: 先查询所有符合条件的记录
|
|
|
+- ⚠️ **内存分页**: 在内存中进行分页
|
|
|
+- ⚠️ **性能考虑**: 大数据量时可能影响性能
|
|
|
+- ⚠️ **统计准确**: `cashOutNum` 统计所有记录,`cashOutAllMoney` 仅统计待审核状态
|
|
|
+
|
|
|
+### 3. 金额单位
|
|
|
+
|
|
|
+- ⚠️ **数据库**: 分(Integer)
|
|
|
+- ⚠️ **接口返回**: 元(BigDecimal,保留2位小数)
|
|
|
+- ⚠️ **舍入模式**: DOWN(向下取整)
|
|
|
+
|
|
|
+### 4. 提现状态
|
|
|
+
|
|
|
+- ⚠️ **状态值**: 字符串类型
|
|
|
+- ⚠️ **可选参数**: 不传则查询所有状态
|
|
|
+- ⚠️ **多状态**: 目前不支持,需多次调用
|
|
|
+
|
|
|
+### 5. 查询性能
|
|
|
+
|
|
|
+- ⚠️ **索引**: 确保有合适的索引
|
|
|
+- ⚠️ **数据量**: 建议限制时间范围
|
|
|
+- ⚠️ **缓存**: 考虑使用Redis缓存热点数据
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 性能优化建议
|
|
|
+
|
|
|
+### 1. 数据库索引
|
|
|
+
|
|
|
+```sql
|
|
|
+-- 推荐的复合索引
|
|
|
+ALTER TABLE store_cash_out_record
|
|
|
+ADD INDEX idx_store_status_time (store_id, payment_status, created_time);
|
|
|
+
|
|
|
+-- 好处:
|
|
|
+-- 1. 覆盖最常用的查询条件
|
|
|
+-- 2. 支持排序优化
|
|
|
+-- 3. 提升查询速度
|
|
|
+```
|
|
|
+
|
|
|
+### 2. 分页优化
|
|
|
+
|
|
|
+**现状**: 内存分页(查全部->分页)
|
|
|
+
|
|
|
+**优化方案**:
|
|
|
+```java
|
|
|
+// 使用MyBatis-Plus的分页插件
|
|
|
+IPage<StoreCashOutRecord> page = new Page<>(pageNum, pageSize);
|
|
|
+IPage<StoreCashOutRecord> result = storeCashOutRecordMapper.selectPage(page, wrapper);
|
|
|
+```
|
|
|
+
|
|
|
+**优点**:
|
|
|
+- 数据库层面分页
|
|
|
+- 只查询当前页数据
|
|
|
+- 性能更好
|
|
|
+
|
|
|
+### 3. 缓存策略
|
|
|
+
|
|
|
+```java
|
|
|
+// Redis缓存热点数据
|
|
|
+@Cacheable(value = "cashOutRecordList",
|
|
|
+ key = "#storeId + ':' + #page + ':' + #size")
|
|
|
+public Object getCashOutRecordList(...) {
|
|
|
+ // ...
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 迁移说明
|
|
|
+
|
|
|
+### 原接口(app端)
|
|
|
+
|
|
|
+- **服务**: `alien-store`
|
|
|
+- **路径**: `/alienStore/storeCashOutRecord/getCashOutRecordList`
|
|
|
+- **Controller**: `StoreCashOutRecordController`
|
|
|
+- **Service**: `StoreCashOutRecordService.getCashOutRecordList()`
|
|
|
+
|
|
|
+### 新接口(web端)
|
|
|
+
|
|
|
+- **服务**: `alien-store-platform`
|
|
|
+- **路径**: `/incomeManage/getCashOutRecordList`
|
|
|
+- **Controller**: `IncomeManageController`
|
|
|
+- **Service**: `IncomeManageService.getCashOutRecordList()`
|
|
|
+
|
|
|
+### 差异说明
|
|
|
+
|
|
|
+| 项目 | app端 | web端 | 说明 |
|
|
|
+|------|-------|-------|------|
|
|
|
+| 接口路径 | `/getCashOutRecordList` | `/getCashOutRecordList` | 保持一致 |
|
|
|
+| 请求方式 | GET | GET | 保持一致 |
|
|
|
+| 参数名称 | 一致 | 一致 | 完全相同 |
|
|
|
+| 返回格式 | StoreCashOutRecordVo | StoreCashOutRecordVo | 保持一致 |
|
|
|
+| 业务逻辑 | ✅ | ✅ | 完全复用 |
|
|
|
+| 登录验证 | ❌ 无 | ✅ 有 | 增加验证 |
|
|
|
+| 日志记录 | 基础 | 详细 | 增强日志 |
|
|
|
+
|
|
|
+### 复用的核心组件
|
|
|
+
|
|
|
+1. **Mapper**:
|
|
|
+ - `StoreCashOutRecordMapper.selectCashoutRecordList()` - 自定义查询方法,LEFT JOIN store_user
|
|
|
+2. **Entity**:
|
|
|
+ - `StoreCashOutRecord` - 提现记录实体(包含 settlementAccount 字段)
|
|
|
+ - `StoreCashOutRecordVo` - 提现记录VO
|
|
|
+3. **Util**:
|
|
|
+ - `ListToPage` - 手动分页工具
|
|
|
+
|
|
|
+### 自定义Mapper SQL
|
|
|
+
|
|
|
+```java
|
|
|
+@Select("select scor.*, If(su.alipay_account is not null,su.alipay_account,su.phone) settlementAccount\n" +
|
|
|
+ "from store_cash_out_record scor\n" +
|
|
|
+ "left join store_user su on su.store_id = scor.store_id and su.delete_flag = 0\n" +
|
|
|
+ "${ew.customSqlSegment}")
|
|
|
+List<StoreCashOutRecord> selectCashoutRecordList(@Param(Constants.WRAPPER) QueryWrapper<StoreCashOutRecord> wrapper);
|
|
|
+```
|
|
|
+
|
|
|
+**说明**:
|
|
|
+- 使用 `@Select` 注解定义SQL
|
|
|
+- LEFT JOIN `store_user` 获取结算账户
|
|
|
+- `${ew.customSqlSegment}` 动态拼接WHERE、ORDER BY等条件
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 常见问题
|
|
|
+
|
|
|
+### Q1: cashOutAllMoney 是当前页还是所有记录的总金额?
|
|
|
+
|
|
|
+**答案**: 所有符合条件且状态为"待审核"(paymentStatus=1)记录的总金额,不是当前页。
|
|
|
+
|
|
|
+**注意**: 从2025-11-20开始,只统计待审核状态的提现金额,与app端逻辑保持一致。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Q2: 如何查询多个状态的记录?
|
|
|
+
|
|
|
+**答案**: 目前不支持,需要多次调用接口分别查询。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Q3: 时间参数必须都传吗?
|
|
|
+
|
|
|
+**答案**: 不需要。可以只传开始时间、只传结束时间,或都不传。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Q4: 分页是数据库分页还是内存分页?
|
|
|
+
|
|
|
+**答案**: 内存分页。先查询所有记录,再在内存中分页。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Q5: 如何获取总页数?
|
|
|
+
|
|
|
+**答案**: `totalPages = Math.ceil(cashOutNum / size)`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Q6: 金额为什么使用DOWN舍入模式?
|
|
|
+
|
|
|
+**答案**: 向下取整更保守,避免显示金额大于实际金额。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Q7: settlementAccount 是什么?如何获取的?
|
|
|
+
|
|
|
+**答案**:
|
|
|
+- **settlementAccount** 是结算账户,用于显示商户的提现账户信息
|
|
|
+- 获取逻辑:
|
|
|
+ 1. 通过 LEFT JOIN `store_user` 表查询
|
|
|
+ 2. 优先使用 `alipay_account`(支付宝账号)
|
|
|
+ 3. 如果支付宝账号为空,使用 `phone`(手机号)
|
|
|
+- SQL: `IF(su.alipay_account IS NOT NULL, su.alipay_account, su.phone)`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Q8: 为什么要用 QueryWrapper 而不是 LambdaQueryWrapper?
|
|
|
+
|
|
|
+**答案**:
|
|
|
+- `selectCashoutRecordList` 是自定义Mapper方法,使用了表别名(`scor`)
|
|
|
+- 需要使用 `QueryWrapper` 配合字符串字段名(如 `"scor.store_id"`)
|
|
|
+- `LambdaQueryWrapper` 无法使用表别名
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 更新日志
|
|
|
+
|
|
|
+### 2025-11-20
|
|
|
+
|
|
|
+**逻辑更新(重要)**:
|
|
|
+- 🔄 修改查询方法,使用 `selectCashoutRecordList` 代替 `selectList`
|
|
|
+- ✅ 添加 LEFT JOIN `store_user` 表,获取结算账户信息(`settlementAccount`)
|
|
|
+- ✅ 修改 `QueryWrapper` 使用表别名 `scor`(以配合自定义SQL)
|
|
|
+- ✅ 添加 `delete_flag = 0` 条件(软删除过滤)
|
|
|
+- 🔄 修改 `cashOutAllMoney` 计算逻辑,仅统计 `paymentStatus = 1`(待审核)状态的记录
|
|
|
+
|
|
|
+**变更原因**:
|
|
|
+- 同步app端完整业务逻辑
|
|
|
+- 提现记录需要显示结算账户信息(支付宝账号或手机号)
|
|
|
+- 需要过滤已删除的记录
|
|
|
+
|
|
|
+**影响范围**:
|
|
|
+- 响应字段新增 `settlementAccount`(结算账户)
|
|
|
+- 查询会关联 `store_user` 表,可能影响性能(建议添加索引)
|
|
|
+- `cashOutAllMoney` 仅统计待审核状态的提现总金额
|
|
|
+
|
|
|
+**涉及文件**:
|
|
|
+- `IncomeManageServiceImpl.java` - 更新 `getCashOutRecordList` 方法(line 352-390)
|
|
|
+- `StoreCashOutRecordMapper.java` - 使用 `selectCashoutRecordList` 方法
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### 2025-11-19
|
|
|
+
|
|
|
+**新增接口**:
|
|
|
+- ✅ `GET /incomeManage/getCashOutRecordList` - 提现记录查询
|
|
|
+
|
|
|
+**核心功能**:
|
|
|
+- ✅ 按门店ID查询提现记录
|
|
|
+- ✅ 支持时间范围筛选
|
|
|
+- ✅ 支持提现状态筛选
|
|
|
+- ✅ 分页查询
|
|
|
+- ✅ 统计总金额和总数
|
|
|
+- ✅ 按创建时间倒序排序
|
|
|
+- ✅ 详细日志记录
|
|
|
+- ✅ 完善异常处理
|
|
|
+
|
|
|
+**涉及文件**:
|
|
|
+- `IncomeManageController.java` - 更新
|
|
|
+- `IncomeManageService.java` - 更新
|
|
|
+- `IncomeManageServiceImpl.java` - 更新
|
|
|
+
|
|
|
+**代码质量**:
|
|
|
+- ✅ Linter检查:无错误
|
|
|
+- ✅ 日志记录:详细
|
|
|
+- ✅ 异常处理:完善
|
|
|
+- ✅ 代码注释:完整
|
|
|
+
|
|
|
+**与原接口一致性**:
|
|
|
+- ✅ 请求参数:完全相同
|
|
|
+- ✅ 返回格式:完全相同
|
|
|
+- ✅ 业务逻辑:完全复用
|
|
|
+
|
|
|
+**开发人员**: ssk
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+**文档版本**: v1.0
|
|
|
+**最后更新**: 2025-11-19
|
|
|
+**维护人员**: ssk
|
|
|
+
|
|
|
+
|
|
|
+
|