# 团购收益查询接口
## 接口信息
- **接口名称**: 团购收益查询
- **接口路径**: `/incomeManage/getGroupIncome`
- **请求方式**: `GET`
- **接口描述**: 查询指定日期内的团购收益详情,包括收益金额、手续费、券数量、退款金额等统计信息,以及分页的收入明细记录列表
---
## 请求参数
### Query参数
| 参数名 | 类型 | 必填 | 描述 | 示例 |
|--------|------|------|------|------|
| date | String | 是 | 查询日期(格式:yyyy-MM-dd) | 2025-11-11 |
| incomeType | Integer | 否 | 收入类型
0: 主页(优惠券+团购券)
1: 优惠券
2: 代金券
3: 套餐
4: 联名卡
不传则查询所有类型 | 0 |
| page | Integer | 否 | 页码,默认1 | 1 |
| size | Integer | 否 | 每页条数,默认10 | 10 |
### Headers
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| token | String | 是 | 用户登录凭证(JWT Token) |
### 说明
- **门店ID**:通过`LoginUserUtil`从当前登录用户token中自动获取,无需前端传递
- **收入类型特殊处理**:前端展示逻辑中,当`incomeType=0`时,会同时查询优惠券(code=1)和团购券(code=2)
- **账期过滤**:仅查询未绑定提现记录的收入(`cashOutId`为空)
---
## 响应数据
### 响应格式
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"storeId": 103,
"storeName": "测试门店",
"date": "2025-11-11",
"commissionRate": 5,
"incomeMoney": "100.00",
"commissionStr": "5.00",
"noYetPaymentMoney": "95.00",
"couponCount": 10,
"refundMoney": 2.50,
"incomeDetailsRecordVoList": [
{
"id": 1001,
"storeId": 103,
"orderId": "ORDER123456",
"money": 1000,
"moneyStr": "10.00",
"commission": 50,
"incomeType": 1,
"refundType": "false",
"checkTime": "2025-11-11 10:30:00",
"createdTime": "2025-11-11 10:30:00"
},
{
"id": 1002,
"storeId": 103,
"orderId": "ORDER123457",
"money": 2000,
"moneyStr": "20.00",
"commission": 100,
"incomeType": 2,
"refundType": "false",
"checkTime": "2025-11-11 14:20:00",
"createdTime": "2025-11-11 14:20:00"
}
]
}
}
```
### 响应字段说明
#### 主要字段
| 字段名 | 类型 | 描述 |
|--------|------|------|
| storeId | Integer | 门店ID |
| storeName | String | 门店名称 |
| date | String | 查询日期 |
| commissionRate | Integer | 佣金率(百分比) |
| incomeMoney | String | 售价总额(收入+手续费,单位:元) |
| commissionStr | String | 手续费总额(单位:元) |
| noYetPaymentMoney | String | 实际收益金额(售价-手续费,单位:元) |
| couponCount | Integer | 券数量(正常订单数 - 退款订单数) |
| refundMoney | BigDecimal | 退款金额(单位:元) |
| incomeDetailsRecordVoList | Array | 收入明细记录列表(分页后的数据) |
#### incomeDetailsRecordVoList数组元素字段
| 字段名 | 类型 | 描述 |
|--------|------|------|
| id | Long | 收入明细记录ID |
| storeId | Integer | 门店ID |
| orderId | String | 订单ID |
| money | Integer | 收入金额(单位:分) |
| moneyStr | String | 收入金额(单位:元,格式化后) |
| commission | Integer | 手续费(单位:分) |
| incomeType | Integer | 收入类型(1:优惠券, 2:代金券, 3:套餐, 4:联名卡) |
| refundType | String | 是否退款("true":已退款, "false":未退款) |
| checkTime | String | 核销时间 |
| createdTime | String | 创建时间 |
### 响应说明
1. **金额单位转换**
- 数据库存储单位:分(Integer)
- 返回给前端单位:元(String,保留2位小数)
2. **收益计算逻辑**
- **售价总额(incomeMoney)** = 收入(money) + 手续费(commission)
- **手续费总额(commissionStr)** = 所有记录的commission之和
- **实际收益(noYetPaymentMoney)** = 收入(money)之和(存入时已减去手续费)
3. **退款处理**
- 根据`refundType`字段分组统计
- `refundType="false"`: 正常订单,计入明细列表
- `refundType="true"`: 退款订单,只统计退款金额
4. **券数量计算**
- 券数量 = 正常订单数量 - 退款订单数量
5. **分页说明**
- 明细列表(`incomeDetailsRecordVoList`)按照分页参数返回
- 统计数据(总金额、券数量、退款金额)基于所有符合条件的记录计算
---
## 业务逻辑
### 查询流程
```
1. 从token中获取当前登录用户的storeId
↓
2. 构建查询条件
- 时间范围:指定日期的00:00:00 ~ 23:59:59
- 账期过滤:cashOutId为空(未绑定提现记录)
- 门店过滤:storeId匹配
- 收入类型:根据incomeType参数筛选
↓
3. 查询收入明细记录(基础统计)
- 计算总收入(allIncome)
- 计算总手续费(commission)
↓
4. 查询店铺信息
- 获取店铺名称、佣金率
↓
5. 查询明细记录列表(含退款标识)
- 通过自定义Mapper查询(getIncomeList)
- 包含退款标识字段
↓
6. 按退款类型分组
- refundType="false": 正常订单
- refundType="true": 退款订单
↓
7. 计算统计信息
- 售价总额 = 收入 + 手续费
- 券数量 = 正常订单数 - 退款订单数
- 退款金额 = 退款订单金额之和
↓
8. 手动分页(ListToPage.setPage)
- 对明细列表进行内存分页
↓
9. 返回结果
```
### 收入类型处理
```java
// incomeType参数说明
if (incomeType == null) {
// 不传:查询所有类型
// 不添加incomeType筛选条件
} else if (incomeType == 0) {
// 0: 主页(优惠券+团购券)
// 查询incomeType IN (1, 2)
wrapper.in(StoreIncomeDetailsRecord::getIncomeType,
CouponTypeEnum.COUPON.getCode(), // 1
CouponTypeEnum.GROUP_BUY.getCode()); // 2
} else {
// 1/2/3/4: 具体类型
// 查询incomeType = 指定值
wrapper.eq(StoreIncomeDetailsRecord::getIncomeType, incomeType);
}
```
### 账期过滤规则
- 仅查询未绑定提现记录的收入(`cashOutId IS NULL`)
- 这部分收入代表"可提现"但尚未提现的金额
- 已提现的收入(`cashOutId`不为空)不在此接口查询范围
---
## 错误响应
### 门店不存在
```json
{
"code": 500,
"msg": "查询失败:门店不存在",
"data": null
}
```
### 未登录
```json
{
"code": 500,
"msg": "查询失败:用户未登录",
"data": null
}
```
### 日期格式错误
```json
{
"code": 500,
"msg": "查询失败:日期格式错误",
"data": null
}
```
---
## 调用示例
### 请求示例
```bash
# 查询指定日期的主页收益(优惠券+团购券)
GET /incomeManage/getGroupIncome?date=2025-11-11&incomeType=0&page=1&size=10
Headers:
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# 查询指定日期的优惠券收益
GET /incomeManage/getGroupIncome?date=2025-11-11&incomeType=1&page=1&size=10
Headers:
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# 查询指定日期的所有类型收益
GET /incomeManage/getGroupIncome?date=2025-11-11&page=1&size=20
Headers:
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
---
## 代码实现
### Controller
```java
@ApiOperation("团购收益查询")
@GetMapping("/getGroupIncome")
public R> getGroupIncome(
@RequestParam("date") String date,
@RequestParam(value = "incomeType", required = false) Integer incomeType,
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "size", defaultValue = "10") Integer size) {
log.info("IncomeManageController.getGroupIncome?date={}, incomeType={}, page={}, size={}",
date, incomeType, page, size);
try {
Integer storeId = LoginUserUtil.getCurrentStoreId();
Object result = incomeManageService.getGroupIncome(storeId, date, incomeType, page, size);
return R.data(result);
} catch (Exception e) {
log.error("IncomeManageController.getGroupIncome ERROR: {}", e.getMessage(), e);
return R.fail("查询失败:" + e.getMessage());
}
}
```
### Service关键逻辑
```java
@Override
public Object getGroupIncome(Integer storeId, String date, Integer incomeType, Integer page, Integer size) {
// 1. 构建查询条件
LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
wrapper.between(StoreIncomeDetailsRecord::getCreatedTime, date + " 00:00:00", date + " 23:59:59")
.isNull(StoreIncomeDetailsRecord::getCashOutId)
.eq(StoreIncomeDetailsRecord::getStoreId, storeId);
// 2. 收入类型筛选
if (null != incomeType && 0 == incomeType) {
wrapper.in(StoreIncomeDetailsRecord::getIncomeType,
CouponTypeEnum.COUPON.getCode(), CouponTypeEnum.GROUP_BUY.getCode());
} else if (null != incomeType) {
wrapper.eq(StoreIncomeDetailsRecord::getIncomeType, incomeType);
}
// 3. 查询并统计
// ... 省略详细实现
return vo;
}
```
---
## 注意事项
1. **日期格式严格要求**
- 必须为`yyyy-MM-dd`格式
- 例如:`2025-11-11`
- 错误格式会导致SQL查询异常
2. **收入类型说明**
- 前端展示时,`incomeType=0`表示"主页",实际查询优惠券+团购券
- 建议前端根据业务需要选择合适的收入类型参数
3. **分页机制**
- 使用内存分页(`ListToPage.setPage`)
- 统计数据(总金额、券数量、退款金额)基于全量数据
- 明细列表基于分页参数返回
4. **金额精度**
- 所有金额计算使用`BigDecimal`
- 四舍五入保留2位小数(`RoundingMode.HALF_UP`)
5. **性能考虑**
- 单日数据量通常不大,内存分页可接受
- 如需跨多日查询,建议在数据库层面实现分页
6. **账期规则**
- 收入创建后3天才能提现
- 此接口查询的是"可提现"金额
- 已提现金额不在查询范围
---
## 相关接口
- [账期查询接口](./18-账期查询接口.md) - 查询可提现和不可提现的收入明细
- [账户余额查询接口](./19-查询账户余额接口.md) - 查询账户总余额和可提现金额
- [提现申请接口](./23-提现申请接口.md) - 发起提现申请
- [提现记录查询接口](./24-提现记录查询接口.md) - 查询提现记录
---
## 更新日志
| 版本 | 日期 | 说明 | 作者 |
|------|------|------|------|
| v1.0 | 2025-11-19 | 从app端迁移到web端,使用`LoginUserUtil`获取storeId | ssk |
---
**文档生成时间**: 2025-11-19
**接口版本**: v1.0
**维护人**: AI Assistant