03-账期查询接口.md 18 KB

Web端商户账期查询接口文档

模块概述

本模块提供收入账期查询功能,支持按时间范围、收入类型、账期类型查询店铺的收入明细数据。


接口信息

账期查询

接口详情

  • 接口名称: 账期查询
  • 接口路径: GET /incomeManage/getPaymentCycle
  • 请求方式: GET
  • 接口描述: 查询门店的收入账期数据,支持未到账期和已到账期的查询,可按收入类型筛选
  • 登录验证: ✅ 需要(使用 @LoginRequired 注解)

请求参数

参数名 类型 必填 说明 示例值
storeId Integer 门店ID 103
incomeType Integer 收入类型
0:主页(优惠券+团购券)
1:优惠券
2:代金券
3:套餐
4:联名卡
0
paymentType Integer 账期类型
0:未到账期
1:已到账期
0
startTime String 开始时间(格式:yyyy-MM-dd) 2025-11-09
endTime String 结束时间(格式:yyyy-MM-dd) 2025-11-11
page Integer 页码(默认1) 1
size Integer 每页条数(默认10) 10

请求示例

GET /incomeManage/getPaymentCycle?storeId=103&incomeType=0&paymentType=0&startTime=2025-11-09&endTime=2025-11-11&page=1&size=10
curl "http://localhost:8080/incomeManage/getPaymentCycle?storeId=103&incomeType=0&paymentType=0&startTime=2025-11-09&endTime=2025-11-11&page=1&size=10" \
  -H "Authorization: Bearer YOUR_TOKEN"

响应参数

成功响应

{
    "code": 200,
    "success": true,
    "data": {
        "date": "2025-11-11 ~ 2025-11-14",
        "data": {
            "records": [
                {
                    "id": 12345,
                    "storeId": 103,
                    "userOrderId": 67890,
                    "incomeType": 1,
                    "businessId": 111,
                    "money": 9600,
                    "moneyStr": "96.00",
                    "commission": 400,
                    "cashOutId": null,
                    "createdTime": "2025-11-11T10:30:00",
                    "date": "2025-11-11",
                    "orderTime": "2025-11-11T10:00:00",
                    "checkTime": "2025-11-11T10:30:00",
                    "incomeTime": "2025-11-15T10:30:00",
                    "couponName": "100元代金券"
                }
            ],
            "total": 50,
            "size": 10,
            "current": 1,
            "pages": 5
        },
        "money": "4800.00"
    },
    "msg": "操作成功"
}

响应字段说明

字段名 类型 说明
data.date String 账期时间范围
data.data Object 分页数据对象
data.data.records Array 收入明细记录列表
data.data.records[].id Integer 收入记录ID
data.data.records[].storeId Integer 门店ID
data.data.records[].userOrderId Integer 用户订单ID
data.data.records[].incomeType Integer 收入类型 1-优惠券 2-团购券
data.data.records[].businessId Integer 业务ID(券ID)
data.data.records[].money Integer 收入金额(分)
data.data.records[].moneyStr String 收入金额(元,格式化后)
data.data.records[].commission Integer 抽成金额(分)
data.data.records[].cashOutId Integer 提现记录ID(null表示未提现)
data.data.records[].createdTime String 创建时间
data.data.records[].date String 日期(格式化)
data.data.records[].orderTime String 订单时间
data.data.records[].checkTime String 核销时间
data.data.records[].incomeTime String 到账时间(创建时间+4天)
data.data.records[].couponName String 券名称
data.data.total Integer 总记录数
data.data.size Integer 每页条数
data.data.current Integer 当前页码
data.data.pages Integer 总页数
data.money String 汇总金额(元)

失败响应

1. 门店不存在

{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "查询失败:门店不存在"
}

2. 未登录或登录过期

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

3. 参数错误

{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "查询失败:日期格式错误"
}

业务逻辑说明

账期类型说明

未到账期(paymentType=0)

  • 定义: 距离核销时间不足3天,资金尚未到达可提现账期
  • 查询条件:
    • cash_out_id 为空(未绑定提现记录)
    • 创建时间 > 当前时间 - 3天
  • 账期时间范围: 当前时间-3天 ~ 当前时间

已到账期(paymentType=1)

  • 定义: 距离核销时间超过4天但不超过27天,资金已到达可提现账期
  • 查询条件:
    • cash_out_id 不为空(已绑定提现记录)
    • 创建时间在 [当前时间-27天, 当前时间-4天] 范围内
  • 账期时间范围: 当前时间-27天 ~ 当前时间-4天

收入类型说明

incomeType 名称 说明
0 主页 包含优惠券(1)和团购券(2)
1 优惠券 仅优惠券收入
2 代金券 仅代金券收入
3 套餐 仅套餐收入
4 联名卡 仅联名卡收入

查询流程

┌─────────────────────────────────────────────┐
│  1. 接收查询参数                              │
│  storeId, incomeType, paymentType,           │
│  startTime, endTime, page, size              │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  2. 构建查询条件(QueryWrapper)             │
│  - 根据 paymentType 设置账期条件             │
│  - 根据 storeId 筛选门店                     │
│  - 根据 startTime/endTime 设置时间范围       │
│  - 根据 incomeType 设置收入类型              │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  3. 查询收入明细记录(Mapper)               │
│  调用 selectRecordList(wrapper)              │
│  返回 List<StoreIncomeDetailsRecordVo>       │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  4. 查询门店信息                              │
│  获取抽成比例 commissionRate                 │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  5. 处理列表数据                              │
│  - 金额格式化(分→元,保留2位小数)           │
│  - 日期格式化(yyyy-MM-dd)                  │
│  - 设置抽成比例                              │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  6. 手动分页                                  │
│  使用 ListToPage.setPage(list, page, size)   │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  7. 计算汇总金额                              │
│  stream().mapToInt(money).sum()              │
│  转换为元并格式化                            │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  8. 返回结果                                  │
│  JSONObject包含: date, data, money           │
└─────────────────────────────────────────────┘

数据库查询

涉及的表

1. store_income_details_record(收入明细表)

查询SQL(简化版):

SELECT 
    sidr.id, 
    sidr.money, 
    sidr.commission, 
    sidr.created_time checkTime,
    luo.buy_time orderTime,
    ADDDATE(sidr.created_time, 4) incomeTime,
    sidr.income_type,
    tc.name couponName
FROM store_income_details_record sidr
LEFT JOIN order_coupon_middle ocm ON ocm.id = sidr.user_order_id
LEFT JOIN life_user_order luo ON luo.id = ocm.order_id
LEFT JOIN totalCoupon tc ON tc.id = sidr.business_id
WHERE sidr.store_id = ?
  AND sidr.created_time BETWEEN ? AND ?
  -- 根据 paymentType 添加条件
  AND (sidr.cash_out_id IS NULL OR sidr.cash_out_id IS NOT NULL)
  -- 根据 incomeType 添加条件
  AND sidr.income_type IN (1, 2) OR sidr.income_type = ?
ORDER BY sidr.created_time DESC

2. store_info(门店信息表)

SELECT * FROM store_info WHERE id = ?

金额计算规则

金额单位转换

数据库存储(分) 显示(元) 转换公式
9600 96.00 money ÷ 100
400 4.00 commission ÷ 100

汇总金额计算

// 1. 累加所有记录的金额(分)
int totalMoney = list.stream()
    .mapToInt(StoreIncomeDetailsRecord::getMoney)
    .sum();

// 2. 转换为元(保留2位小数)
String moneyStr = new BigDecimal(totalMoney)
    .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
    .toString();

示例:

记录1: 9600分
记录2: 8800分
记录3: 10200分
汇总: (9600 + 8800 + 10200) ÷ 100 = 286.00元

分页说明

手动分页实现

使用 ListToPage.setPage() 工具类进行内存分页:

IPage<StoreIncomeDetailsRecordVo> pageResult = 
    ListToPage.setPage(list, page, size);

分页计算逻辑

总记录数: 50
每页条数: 10
总页数: Math.ceil(50 ÷ 10) = 5

第1页: records[0-9]
第2页: records[10-19]
第3页: records[20-29]
...

分页响应结构

{
    "records": [...],    // 当前页数据
    "total": 50,         // 总记录数
    "size": 10,          // 每页条数
    "current": 1,        // 当前页码
    "pages": 5           // 总页数
}

登录验证

验证机制

接口使用 @LoginRequired 注解进行登录验证,通过AOP切面实现:

  1. Token验证: 从请求中获取JWT Token并解析用户信息
  2. 用户类型验证: 确认用户类型为 storePlatform
  3. Redis Token验证: 检查Token是否在Redis中存在(未过期/未注销)

验证失败场景

场景 返回消息
Token无效或不存在 "请先登录"
用户类型不正确 "请先登录"
Token已过期或已注销 "请先登录"

异常处理

异常场景

异常情况 HTTP状态码 返回code 返回msg
门店不存在 200 500 "查询失败:门店不存在"
日期格式错误 200 500 "查询失败:日期格式错误"
未登录 200 500 "请先登录"
系统异常 200 500 "查询失败:{异常信息}"

日志记录

所有请求和异常都会记录详细日志:

// 请求日志
log.info("IncomeManageController.getPaymentCycle?storeId={}, incomeType={}, ...", ...);

// 查询日志
log.info("IncomeManageServiceImpl.getPaymentCycle - 查询到收入记录数: {}", list.size());

// 异常日志
log.error("IncomeManageController.getPaymentCycle ERROR: {}", e.getMessage(), e);

业务规则

账期时间计算规则

1. 未到账期时间范围

当前时间: 2025-11-14
未到账期范围: 2025-11-11 ~ 2025-11-14
计算公式: [当前时间-3天, 当前时间]

2. 已到账期时间范围

当前时间: 2025-11-14
已到账期范围: 2025-10-18 ~ 2025-11-10
计算公式: [当前时间-27天, 当前时间-4天]

3. 到账时间计算

核销时间: 2025-11-11 10:30:00
到账时间: 2025-11-15 10:30:00
计算公式: 核销时间 + 4天

收入类型筛选规则

if (incomeType == 0) {
    // 主页: 查询优惠券(1) + 团购券(2)
    wrapper.in("income_type", 1, 2);
} else if (incomeType != null) {
    // 指定类型: 精确匹配
    wrapper.eq("income_type", incomeType);
}
// incomeType为null: 不添加类型条件,查询所有

测试用例

测试场景1: 查询未到账期

请求:

GET /incomeManage/getPaymentCycle?storeId=103&paymentType=0&startTime=2025-11-09&endTime=2025-11-14&page=1&size=10

预期结果:

  • 返回未绑定提现记录的收入明细
  • date字段显示未到账期时间范围
  • 所有记录的cashOutId为null

测试场景2: 查询已到账期

请求:

GET /incomeManage/getPaymentCycle?storeId=103&paymentType=1&startTime=2025-10-18&endTime=2025-11-10&page=1&size=10

预期结果:

  • 返回已绑定提现记录的收入明细
  • date字段显示已到账期时间范围
  • 所有记录的cashOutId不为null

测试场景3: 查询主页收入

请求:

GET /incomeManage/getPaymentCycle?storeId=103&incomeType=0&startTime=2025-11-09&endTime=2025-11-14&page=1&size=10

预期结果:

  • 返回优惠券(1)和团购券(2)的收入记录
  • 不包含其他类型的收入

测试场景4: 分页测试

请求:

GET /incomeManage/getPaymentCycle?storeId=103&startTime=2025-11-01&endTime=2025-11-14&page=2&size=5

预期结果:

  • 返回第2页数据(records[5-9])
  • total字段为总记录数
  • pages字段为总页数

测试场景5: 门店不存在

请求:

GET /incomeManage/getPaymentCycle?storeId=999999&startTime=2025-11-09&endTime=2025-11-14&page=1&size=10

预期结果:

  • 返回失败响应
  • msg为"查询失败:门店不存在"

迁移说明

原接口(app端)

  • 服务: alien-store
  • 路径: /alienStore/storeIncomeDetailsRecord/noYetPayment
  • Controller: StoreIncomeDetailsRecordController
  • Service: StoreIncomeDetailsRecordService.noYetPayment()

新接口(web端)

  • 服务: alien-store-platform
  • 路径: /incomeManage/getPaymentCycle
  • Controller: IncomeManageController
  • Service: IncomeManageService.getPaymentCycle()

差异说明

项目 app端 web端 说明
接口路径 /noYetPayment /getPaymentCycle 更符合web端命名规范
登录验证 ✅ 有(@LoginRequired 增加登录验证
返回类型 JSONObject JSONObject 保持一致
业务逻辑 完全复用
日志记录 基础 详细 增强日志记录

复用的核心组件

  1. Mapper: StoreIncomeDetailsRecordMapper.selectRecordList()
  2. Entity: StoreIncomeDetailsRecord, StoreIncomeDetailsRecordVo
  3. Enum: CouponTypeEnum
  4. Util: ListToPage, DateUtils

性能优化建议

1. 数据库索引

确保以下字段有索引:

-- store_income_details_record表
ALTER TABLE store_income_details_record 
ADD INDEX idx_store_created (store_id, created_time);

ALTER TABLE store_income_details_record 
ADD INDEX idx_cashout_created (cash_out_id, created_time);

ALTER TABLE store_income_details_record 
ADD INDEX idx_income_type (income_type);

2. 查询优化

  • 使用时间范围索引加速查询
  • 限制查询时间跨度(建议≤3个月)
  • 对大数据量结果进行分页处理

3. 缓存策略

对于频繁查询的数据,可以考虑:

  • Redis缓存热点店铺的账期数据
  • 设置合理的过期时间(如5分钟)
  • 在数据变更时清除缓存

注意事项

1. 日期格式

  • ✅ 使用 yyyy-MM-dd 格式(如:2025-11-09)
  • ❌ 不支持其他格式(如:2025/11/09, 20251109)

2. 时间范围

  • 建议查询时间跨度不超过3个月
  • startTime 必须 ≤ endTime
  • 超大时间范围可能导致查询超时

3. 金额精度

  • 数据库存储单位:分(整数)
  • 前端显示单位:元(保留2位小数)
  • 使用 BigDecimal 进行金额计算

4. 分页限制

  • 单页最大条数建议不超过100
  • 使用内存分页,大数据量时注意性能

5. 登录验证

  • 所有请求必须携带有效Token
  • Token过期需要重新登录
  • 用户类型必须为 storePlatform

更新日志

2025-11-14

新增接口:

  • GET /incomeManage/getPaymentCycle - 账期查询

核心功能:

  • ✅ 支持未到账期/已到账期查询
  • ✅ 支持按收入类型筛选(主页、优惠券、代金券等)
  • ✅ 支持自定义时间范围查询
  • ✅ 金额自动格式化(分→元)
  • ✅ 手动分页支持
  • ✅ 汇总金额计算
  • ✅ 登录验证(@LoginRequired
  • ✅ 详细日志记录
  • ✅ 完善异常处理

涉及文件:

  • IncomeManageController.java - 新建
  • IncomeManageService.java - 新建
  • IncomeManageServiceImpl.java - 新建

代码质量:

  • ✅ Linter检查:无错误
  • ✅ 空指针检查:已添加
  • ✅ 日志记录:详细
  • ✅ 异常处理:完善

开发人员: ssk


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