# Web端商户已入账/未入账详情查询接口文档

## 模块概述

本接口用于查询商户的收入明细，支持查询已入账和未入账的收益详情。可以按时间范围、收入类型进行筛选，并提供分页功能。

---

## 接口信息

### 已入账/未入账详情查询

#### 接口详情

- **接口名称**: 已入账/未入账详情查询
- **接口路径**: `GET /incomeManage/getPaymentDetails`
- **请求方式**: GET
- **接口描述**: 查询商户的收入明细，区分已入账和未入账状态
- **登录验证**: ✅ 需要（通过 JWT token 自动获取店铺ID）
- **数据来源**: app端 `/alienStore/storeIncomeDetailsRecord/noYetPayment` 接口

---

## 业务逻辑说明

### 入账规则

#### **未入账 (paymentType=0)**:
- **时间范围**: 当前时间-3天内的记录
- **条件**: `cash_out_id IS NULL`（未绑定提现记录）
- **说明**: 3天内的收入还未到账期，不能提现

#### **已入账 (paymentType=1)**:
- **时间范围**: 当前时间-4天 至 当前时间-27天的记录
- **条件**: 创建时间在4~27天之间
- **说明**: 4-27天前的收入已到账期，可以提现

### 收入类型说明

| incomeType | 说明 | 备注 |
|-----------|------|------|
| 0 | 主页 | 包含优惠券(1)和团购券(2) |
| 1 | 优惠券 | 单独查询优惠券收入 |
| 2 | 代金券 | 单独查询代金券收入 |
| 3 | 套餐 | 单独查询套餐收入 |
| 4 | 联名卡 | 单独查询联名卡收入 |

---

## 请求参数

### Query 参数

| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|--------|------|------|--------|------|
| incomeType | Integer | 否 | - | 收入类型, 0:主页, 1:优惠券, 2:代金券, 3:套餐, 4:联名卡 |
| paymentType | Integer | 是 | - | 账期类型, 0:未入账, 1:已入账 |
| startTime | String | 是 | - | 开始时间（格式：yyyy-MM-dd） |
| endTime | String | 是 | - | 结束时间（格式：yyyy-MM-dd） |
| page | Integer | 否 | 1 | 页码（从1开始） |
| size | Integer | 否 | 10 | 每页条数 |

**说明**:
- `storeId` 从 JWT token 自动获取，无需传参
- `startTime` 和 `endTime` 用于自定义查询时间范围
- 系统会根据 `paymentType` 自动计算默认时间范围

---

## 请求示例

### 示例1: 查询未入账收入

```http
GET /incomeManage/getPaymentDetails?paymentType=0&startTime=2025-11-09&endTime=2025-11-11&page=1&size=10
Headers:
  Authorization: Bearer YOUR_JWT_TOKEN
```

```bash
curl -X GET "http://localhost:8080/incomeManage/getPaymentDetails?paymentType=0&startTime=2025-11-09&endTime=2025-11-11&page=1&size=10" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
```

---

### 示例2: 查询已入账的优惠券收入

```http
GET /incomeManage/getPaymentDetails?incomeType=1&paymentType=1&startTime=2025-10-01&endTime=2025-11-11&page=1&size=10
Headers:
  Authorization: Bearer YOUR_JWT_TOKEN
```

```bash
curl -X GET "http://localhost:8080/incomeManage/getPaymentDetails?incomeType=1&paymentType=1&startTime=2025-10-01&endTime=2025-11-11&page=1&size=10" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
```

---

### 示例3: 查询主页（优惠券+团购券）未入账收入

```http
GET /incomeManage/getPaymentDetails?incomeType=0&paymentType=0&startTime=2025-11-09&endTime=2025-11-11&page=1&size=10
Headers:
  Authorization: Bearer YOUR_JWT_TOKEN
```

---

## 响应参数

### 成功响应

```json
{
    "code": 200,
    "success": true,
    "data": {
        "date": "2025-11-08 ~ 2025-11-11",
        "money": "128.50",
        "data": {
            "records": [
                {
                    "id": 1001,
                    "storeId": 103,
                    "money": 5000,
                    "moneyStr": "50.00",
                    "commission": 150,
                    "commissionStr": "1.50",
                    "incomeMoney": "51.50",
                    "incomeType": 1,
                    "couponName": "50元优惠券",
                    "cashOutId": null,
                    "date": "2025-11-10",
                    "commissionRate": "3",
                    "createdTime": "2025-11-10 14:30:00"
                },
                {
                    "id": 1002,
                    "storeId": 103,
                    "money": 7800,
                    "moneyStr": "78.00",
                    "commission": 234,
                    "commissionStr": "2.34",
                    "incomeMoney": "80.34",
                    "incomeType": 2,
                    "couponName": "100元团购券",
                    "cashOutId": null,
                    "date": "2025-11-09",
                    "commissionRate": "3",
                    "createdTime": "2025-11-09 16:20:00"
                }
            ],
            "total": 25,
            "size": 10,
            "current": 1,
            "pages": 3
        }
    },
    "msg": "操作成功"
}
```

### 响应字段说明

#### 顶层字段

| 字段名 | 类型 | 说明 |
|--------|------|------|
| code | Integer | 状态码（200-成功） |
| success | Boolean | 是否成功 |
| data | JSONObject | 返回数据 |
| msg | String | 提示信息 |

#### data 字段

| 字段名 | 类型 | 说明 |
|--------|------|------|
| date | String | 账期时间范围 |
| money | String | 总金额（元，保留2位小数） |
| data | Page | 分页数据 |

#### data.data 分页字段

| 字段名 | 类型 | 说明 |
|--------|------|------|
| records | Array | 收入明细列表 |
| total | Integer | 总记录数 |
| size | Integer | 每页条数 |
| current | Integer | 当前页码 |
| pages | Integer | 总页数 |

#### records 明细字段

| 字段名 | 类型 | 说明 |
|--------|------|------|
| id | Integer | 收入记录ID |
| storeId | Integer | 店铺ID |
| money | Integer | 收益金额（分） |
| moneyStr | String | 收益金额（元，格式化） |
| commission | Integer | 手续费（分） |
| commissionStr | String | 手续费（元，格式化） |
| incomeMoney | String | 售价（收益+手续费，元） |
| incomeType | Integer | 收入类型 |
| couponName | String | 优惠券名称 |
| cashOutId | Integer | 提现记录ID（null表示未提现） |
| date | String | 日期（yyyy-MM-dd） |
| commissionRate | String | 抽成比例 |
| createdTime | String | 创建时间 |

---

### 失败响应

#### 1. 未登录或 token 无效

```json
{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "请先登录"
}
```

#### 2. 参数错误

```json
{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "查询失败：时间格式错误"
}
```

#### 3. 查询异常

```json
{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "查询失败：{异常信息}"
}
```

---

## 业务场景

### 场景 1: 查询未入账收入（近3天）

**业务需求**: 商户想查看最近3天内产生的收入，这些收入还未到提现账期

**请求**:
```http
GET /incomeManage/getPaymentDetails?paymentType=0&startTime=2025-11-08&endTime=2025-11-11&page=1&size=10
Headers:
  Authorization: Bearer VALID_TOKEN
```

**响应**:
```json
{
    "code": 200,
    "success": true,
    "data": {
        "date": "2025-11-08 ~ 2025-11-11",
        "money": "256.80",
        "data": {
            "records": [...],
            "total": 15,
            "current": 1,
            "pages": 2
        }
    }
}
```

**业务逻辑**:
- ✅ 查询 `created_time > (当前时间 - 3天)` 的记录
- ✅ 且 `cash_out_id IS NULL`（未绑定提现）
- ✅ 按创建时间倒序排列
- ✅ 返回汇总金额和明细列表

---

### 场景 2: 查询已入账收入（4-27天前）

**业务需求**: 商户想查看可以提现的收入，这些收入已到账期

**请求**:
```http
GET /incomeManage/getPaymentDetails?paymentType=1&startTime=2025-10-15&endTime=2025-11-07&page=1&size=10
Headers:
  Authorization: Bearer VALID_TOKEN
```

**响应**:
```json
{
    "code": 200,
    "success": true,
    "data": {
        "date": "2025-10-15 ~ 2025-11-07",
        "money": "1580.50",
        "data": {
            "records": [...],
            "total": 42,
            "current": 1,
            "pages": 5
        }
    }
}
```

**业务逻辑**:
- ✅ 查询 `created_time BETWEEN (当前时间-27天) AND (当前时间-4天)` 的记录
- ✅ 按创建时间倒序排列
- ✅ 这些收入可以申请提现

---

### 场景 3: 按收入类型筛选（优惠券）

**业务需求**: 商户只想查看优惠券产生的收入

**请求**:
```http
GET /incomeManage/getPaymentDetails?incomeType=1&paymentType=0&startTime=2025-11-09&endTime=2025-11-11&page=1&size=10
Headers:
  Authorization: Bearer VALID_TOKEN
```

**响应**:
```json
{
    "code": 200,
    "success": true,
    "data": {
        "date": "2025-11-08 ~ 2025-11-11",
        "money": "86.20",
        "data": {
            "records": [
                {
                    "incomeType": 1,
                    "couponName": "50元优惠券",
                    "moneyStr": "50.00",
                    ...
                }
            ],
            "total": 5,
            "current": 1,
            "pages": 1
        }
    }
}
```

---

### 场景 4: 查询主页收入（优惠券+团购券）

**业务需求**: 商户想查看主页展示的收入汇总（包含优惠券和团购券）

**请求**:
```http
GET /incomeManage/getPaymentDetails?incomeType=0&paymentType=0&startTime=2025-11-09&endTime=2025-11-11&page=1&size=10
Headers:
  Authorization: Bearer VALID_TOKEN
```

**响应**:
```json
{
    "code": 200,
    "success": true,
    "data": {
        "date": "2025-11-08 ~ 2025-11-11",
        "money": "328.60",
        "data": {
            "records": [
                {"incomeType": 1, "couponName": "50元优惠券", ...},
                {"incomeType": 2, "couponName": "100元团购券", ...}
            ],
            "total": 18,
            "current": 1,
            "pages": 2
        }
    }
}
```

**业务逻辑**:
- ✅ `incomeType=0` 会自动查询 `income_type IN (1, 2)`
- ✅ 包含优惠券和团购券的收入

---

## 数据库查询逻辑

### SQL 示例（未入账）

```sql
SELECT sidr.*
FROM store_income_details_record sidr
WHERE sidr.store_id = ?
  AND sidr.cash_out_id IS NULL
  AND sidr.created_time > DATE_SUB(NOW(), INTERVAL 3 DAY)
  AND sidr.created_time BETWEEN ? AND ?
  AND sidr.income_type IN (1, 2)  -- 如果 incomeType=0
ORDER BY sidr.created_time DESC
```

---

### SQL 示例（已入账）

```sql
SELECT sidr.*
FROM store_income_details_record sidr
WHERE sidr.store_id = ?
  AND sidr.created_time BETWEEN DATE_SUB(NOW(), INTERVAL 27 DAY) 
                        AND DATE_SUB(NOW(), INTERVAL 4 DAY)
  AND sidr.created_time BETWEEN ? AND ?
  AND sidr.income_type = ?
ORDER BY sidr.created_time DESC
```

---

## 金额计算逻辑

### 单条记录计算

```java
// 1. 收益（分转元）
moneyStr = money / 100  // 例: 5000分 = 50.00元

// 2. 手续费（分转元）
commissionStr = commission / 100  // 例: 150分 = 1.50元

// 3. 售价（收益 + 手续费）
incomeMoney = (money + commission) / 100  // 例: (5000 + 150) / 100 = 51.50元
```

### 汇总计算

```java
// 所有记录的收益总和（分转元）
totalMoney = SUM(money) / 100
```

---

## 前端集成示例

### Vue.js 示例

```javascript
export default {
    data() {
        return {
            paymentType: 0,  // 0-未入账, 1-已入账
            incomeType: null,
            startTime: '2025-11-09',
            endTime: '2025-11-11',
            page: 1,
            size: 10,
            loading: false,
            tableData: [],
            totalMoney: '0.00',
            dateRange: '',
            total: 0
        };
    },
    methods: {
        async fetchPaymentDetails() {
            this.loading = true;
            try {
                const params = {
                    paymentType: this.paymentType,
                    startTime: this.startTime,
                    endTime: this.endTime,
                    page: this.page,
                    size: this.size
                };
                
                if (this.incomeType !== null) {
                    params.incomeType = this.incomeType;
                }
                
                const response = await this.$axios.get('/incomeManage/getPaymentDetails', { params });
                
                if (response.data.success) {
                    const data = response.data.data;
                    this.tableData = data.data.records;
                    this.totalMoney = data.money;
                    this.dateRange = data.date;
                    this.total = data.data.total;
                    this.$message.success('查询成功');
                } else {
                    this.$message.error(response.data.msg);
                }
            } catch (error) {
                console.error('查询失败:', error);
                this.$message.error('查询失败，请稍后重试');
            } finally {
                this.loading = false;
            }
        },
        
        handlePageChange(page) {
            this.page = page;
            this.fetchPaymentDetails();
        },
        
        switchPaymentType(type) {
            this.paymentType = type;
            this.page = 1;
            this.fetchPaymentDetails();
        }
    },
    mounted() {
        this.fetchPaymentDetails();
    }
}
```

---

### React 示例

```javascript
import { useState, useEffect } from 'react';
import axios from 'axios';
import { Table, Radio, DatePicker, Select } from 'antd';

function PaymentDetails() {
    const [paymentType, setPaymentType] = useState(0);
    const [incomeType, setIncomeType] = useState(null);
    const [dateRange, setDateRange] = useState(['2025-11-09', '2025-11-11']);
    const [page, setPage] = useState(1);
    const [size, setSize] = useState(10);
    const [loading, setLoading] = useState(false);
    const [tableData, setTableData] = useState([]);
    const [totalMoney, setTotalMoney] = useState('0.00');
    const [total, setTotal] = useState(0);
    
    const fetchData = async () => {
        setLoading(true);
        try {
            const params = {
                paymentType,
                startTime: dateRange[0],
                endTime: dateRange[1],
                page,
                size
            };
            
            if (incomeType !== null) {
                params.incomeType = incomeType;
            }
            
            const response = await axios.get('/incomeManage/getPaymentDetails', { params });
            
            if (response.data.success) {
                const data = response.data.data;
                setTableData(data.data.records);
                setTotalMoney(data.money);
                setTotal(data.data.total);
            }
        } catch (error) {
            console.error('查询失败:', error);
        } finally {
            setLoading(false);
        }
    };
    
    useEffect(() => {
        fetchData();
    }, [paymentType, dateRange, page, incomeType]);
    
    return (
        <div>
            <Radio.Group value={paymentType} onChange={e => setPaymentType(e.target.value)}>
                <Radio.Button value={0}>未入账</Radio.Button>
                <Radio.Button value={1}>已入账</Radio.Button>
            </Radio.Group>
            
            <Select 
                value={incomeType} 
                onChange={setIncomeType}
                placeholder="选择收入类型"
                allowClear
            >
                <Select.Option value={0}>主页</Select.Option>
                <Select.Option value={1}>优惠券</Select.Option>
                <Select.Option value={2}>代金券</Select.Option>
                <Select.Option value={3}>套餐</Select.Option>
                <Select.Option value={4}>联名卡</Select.Option>
            </Select>
            
            <div>总金额: ¥{totalMoney}</div>
            
            <Table 
                dataSource={tableData}
                loading={loading}
                pagination={{
                    current: page,
                    pageSize: size,
                    total: total,
                    onChange: setPage
                }}
            />
        </div>
    );
}
```

---

## 测试用例

### 测试场景1: 查询未入账收入

**前置条件**:
- 用户已登录
- 店铺ID=103
- 近3天有5笔收入记录

**执行步骤**:
1. 发送请求: `GET /incomeManage/getPaymentDetails?paymentType=0&startTime=2025-11-09&endTime=2025-11-11&page=1&size=10`

**预期结果**:
- 返回成功
- `date` = "2025-11-08 ~ 2025-11-11"
- `money` = 所有记录的收益总和
- `data.total` = 5
- 所有记录的 `cashOutId` = null

---

### 测试场景2: 查询已入账收入

**前置条件**:
- 用户已登录
- 4-27天前有10笔收入记录

**执行步骤**:
1. 发送请求: `GET /incomeManage/getPaymentDetails?paymentType=1&startTime=2025-10-15&endTime=2025-11-07&page=1&size=10`

**预期结果**:
- 返回成功
- `date` = "2025-10-15 ~ 2025-11-07"
- `data.total` = 10
- 记录的创建时间在4-27天之间

---

### 测试场景3: 按收入类型筛选

**前置条件**:
- 近3天有优惠券收入3笔、团购券收入2笔

**执行步骤**:
1. 发送请求: `GET /incomeManage/getPaymentDetails?incomeType=1&paymentType=0&startTime=2025-11-09&endTime=2025-11-11&page=1&size=10`

**预期结果**:
- 返回成功
- 只返回优惠券收入（incomeType=1）
- `data.total` = 3

---

## 常见问题

### Q1: 未入账和已入账的时间范围是固定的吗？

**答案**: 是的。
- **未入账**: 固定为当前时间-3天内
- **已入账**: 固定为当前时间-4天至-27天
- `startTime` 和 `endTime` 用于进一步筛选时间范围

---

### Q2: incomeType=0 和不传 incomeType 有什么区别？

**答案**:
- **incomeType=0**: 查询优惠券(1)和团购券(2)的收入
- **不传**: 查询所有类型的收入

---

### Q3: money 和 incomeMoney 有什么区别？

**答案**:
- **money**: 商户实际收益（已扣除手续费）
- **incomeMoney**: 售价（收益 + 手续费）
- 关系: `incomeMoney = money + commission`

---

### Q4: 为什么有些记录有 cashOutId，有些没有？

**答案**:
- **有 cashOutId**: 已绑定提现记录，收益已申请提现
- **无 cashOutId**: 未绑定提现记录，收益还未提现

---

### Q5: 分页查询时，total 是指什么？

**答案**: `total` 是符合查询条件的总记录数，不是总页数。
- 总页数 = `pages` 字段
- 当前页 = `current` 字段

---

## 注意事项

### 1. 时间范围理解

- ⚠️ 系统自动根据 `paymentType` 计算默认时间范围
- ⚠️ `startTime` 和 `endTime` 用于进一步筛选
- ⚠️ 实际查询是两个时间范围的交集

### 2. 金额单位

- ⚠️ 数据库存储单位：**分**
- ⚠️ 接口返回单位：**元**（保留2位小数）
- ⚠️ 转换公式：元 = 分 / 100

### 3. 收入类型

- ⚠️ `incomeType=0` 是特殊值，表示主页（优惠券+团购券）
- ⚠️ 其他值直接对应具体的收入类型
- ⚠️ 不传表示查询所有类型

### 4. 分页注意

- ⚠️ 使用内存分页（ListToPage）
- ⚠️ 先查询所有符合条件的记录，再分页
- ⚠️ 数据量大时可能影响性能

---

## 更新日志

### 2025-11-21 (v1.0)

**新增接口**:
- ✅ `GET /incomeManage/getPaymentDetails` - 已入账/未入账详情查询

**核心功能**:
- ✅ 区分未入账（3天内）和已入账（4-27天）
- ✅ 支持按收入类型筛选
- ✅ 支持自定义时间范围
- ✅ 支持分页查询
- ✅ 返回汇总金额和明细列表
- ✅ 金额自动转换（分转元）
- ✅ 自动计算售价、手续费

**数据来源**:
- app端 `/alienStore/storeIncomeDetailsRecord/noYetPayment` 接口
- 完全复用业务逻辑

**涉及文件**:
- `IncomeManageController.java` - 新增 `getPaymentDetails` 接口
- `IncomeManageService.java` - 新增方法定义
- `IncomeManageServiceImpl.java` - 新增方法实现

**代码质量**:
- ✅ Linter检查：无错误（1个警告可忽略）
- ✅ 日志记录：详细
- ✅ 异常处理：完善
- ✅ 代码注释：完整

**开发人员**: ssk

---

**文档版本**: v1.0  
**最后更新**: 2025-11-21  
**维护人员**: ssk


