# 团购收益查询接口 ## 接口信息 - **接口名称**: 团购收益查询 - **接口路径**: `/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