Переглянути джерело

feat(income): add payment details and income summary APIs

- Added getPaymentDetails API for querying account entry/exit details
- Implemented getIncomeSummary API for income statistics aggregation
- Removed storeId parameter from verifyCoupon method signature
- Updated verifyCoupon to fetch storeId from login context
- Fixed type casting issue for storeId in income record creation
- Enhanced income detail query with custom time range support
- Added income type filtering logic for main page (coupons + group buys)
- Improved account balance calculation with pending withdrawals deduction
- Extended StoreIncomeDetailsRecordVo with price field
- Updated mapper SQL to include coupon code and order price fields
- Added comprehensive API documentation for new income management endpoints
wxd 3 тижнів тому
батько
коміт
376d36d3d6

+ 4 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreIncomeDetailsRecordVo.java

@@ -30,6 +30,10 @@ public class StoreIncomeDetailsRecordVo extends StoreIncomeDetailsRecord {
     @ApiModelProperty(value = "收入金额")
     String incomeMoney;
 
+    @ApiModelProperty(value = "订单价格(原价)")
+    String price;
+
+
     @ApiModelProperty(value = "手续费Str")
     String commissionStr;
 

+ 1 - 1
alien-entity/src/main/java/shop/alien/mapper/StoreIncomeDetailsRecordMapper.java

@@ -20,7 +20,7 @@ import java.util.List;
 public interface StoreIncomeDetailsRecordMapper extends BaseMapper<StoreIncomeDetailsRecord> {
 
     @Select("select income.id, income.money, income.commission, income.created_time checkTime, uorder.created_time orderTime, date_add(income.created_time, interval 3 day) incomeTime, " +
-            "       income_type, uorder.quan_code, coupon.name couponName, store.store_name, if(lfo.store_id is not null ,'true','false') refundType " +
+            "       income_type, uorder.quan_code,  uorder.price, coupon.name couponName, coupon.coupon_code, store.store_name, if(lfo.store_id is not null ,'true','false') refundType " +
             "from store_income_details_record income " +
             "join store_info store on store.id = income.store_id " +
             "left join life_user_order uorder on uorder.id = income.user_order_id " +

+ 3 - 3
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/CouponManageController.java

@@ -49,11 +49,11 @@ public class CouponManageController {
             @ApiImplicitParam(name = "quanCode", value = "券码", dataType = "String", paramType = "query", required = true)
     })
     @GetMapping("/verifyCoupon")
-    public R<Map<String, String>> verifyCoupon(@RequestParam("storeId") String storeId,
+    public R<Map<String, String>> verifyCoupon(
                                                 @RequestParam("quanCode") String quanCode) {
-        log.info("CouponManageController.verifyCoupon?storeId={}, quanCode={}", storeId, quanCode);
+        log.info("CouponManageController.verifyCoupon? quanCode={}", quanCode);
         try {
-            Map<String, String> result = couponManageService.verifyCoupon(storeId, quanCode);
+            Map<String, String> result = couponManageService.verifyCoupon(quanCode);
             return R.data(result);
         } catch (Exception e) {
             log.error("CouponManageController.verifyCoupon ERROR: {}", e.getMessage(), e);

+ 46 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/controller/IncomeManageController.java

@@ -172,5 +172,51 @@ public class IncomeManageController {
             return R.fail("快速提现失败:" + e.getMessage());
         }
     }
+
+    @ApiOperation("已入账/未入账详情查询")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "incomeType", value = "收入类型, 0:主页, 1:优惠券, 2:代金券, 3:套餐, 4:联名卡", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "paymentType", value = "账期类型, 0:未入账, 1:已入账", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "startTime", value = "开始时间(格式:yyyy-MM-dd)", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "endTime", value = "结束时间(格式:yyyy-MM-dd)", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "page", value = "页数", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "size", value = "每页条数", dataType = "Integer", paramType = "query", required = true)
+    })
+    @GetMapping("/getPaymentDetails")
+    public R<com.alibaba.fastjson2.JSONObject> getPaymentDetails(
+            @RequestParam(value = "incomeType", required = false) Integer incomeType,
+            @RequestParam("paymentType") Integer paymentType,
+            @RequestParam("startTime") String startTime,
+            @RequestParam("endTime") String endTime,
+            @RequestParam(value = "page", defaultValue = "1") int page,
+            @RequestParam(value = "size", defaultValue = "10") int size) {
+        log.info("IncomeManageController.getPaymentDetails? incomeType={}, paymentType={}, startTime={}, endTime={}, page={}, size={}",
+                incomeType, paymentType, startTime, endTime, page, size);
+        try {
+            Integer storeId = LoginUserUtil.getCurrentStoreId();
+            com.alibaba.fastjson2.JSONObject result = incomeManageService.getPaymentDetails(
+                    storeId, incomeType, paymentType, startTime, endTime, page, size);
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("IncomeManageController.getPaymentDetails ERROR: {}", e.getMessage(), e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperation("收入汇总统计")
+    @ApiOperationSupport(order = 8)
+    @GetMapping("/getIncomeSummary")
+    public R<Map<String, Object>> getIncomeSummary() {
+        log.info("IncomeManageController.getIncomeSummary - 查询收入汇总统计");
+        try {
+            Integer storeId = LoginUserUtil.getCurrentStoreId();
+            Map<String, Object> result = incomeManageService.getIncomeSummary(storeId);
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("IncomeManageController.getIncomeSummary ERROR: {}", e.getMessage(), e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
 }
 

+ 1 - 2
alien-store-platform/src/main/java/shop/alien/storeplatform/service/CouponManageService.java

@@ -23,10 +23,9 @@ public interface CouponManageService {
     /**
      * 核销订单
      *
-     * @param storeId  门店ID
      * @param quanCode 券码
      * @return 核销结果
      */
-    Map<String, String> verifyCoupon(String storeId, String quanCode);
+    Map<String, String> verifyCoupon(String quanCode);
 }
 

+ 24 - 0
alien-store-platform/src/main/java/shop/alien/storeplatform/service/IncomeManageService.java

@@ -81,6 +81,30 @@ public interface IncomeManageService {
      * @return 提现结果
      */
     R<?> applyFastCashOut(Integer storeId, String payPassword, Integer withdrawalMoney);
+
+    /**
+     * 已入账/未入账详情查询
+     *
+     * @param storeId     门店ID
+     * @param incomeType  收入类型, 0:主页, 1:优惠券, 2:代金券, 3:套餐, 4:联名卡
+     * @param paymentType 账期类型,0:未入账,1:已入账
+     * @param startTime   开始时间(格式:yyyy-MM-dd)
+     * @param endTime     结束时间(格式:yyyy-MM-dd)
+     * @param page        页数
+     * @param size        条数
+     * @return 入账详情
+     */
+    JSONObject getPaymentDetails(Integer storeId, Integer incomeType, Integer paymentType, 
+                                 String startTime, String endTime, int page, int size);
+
+    /**
+     * 收入汇总统计
+     * 包含:可提现金额、今日收益、已入账总数、未入账总数
+     *
+     * @param storeId 门店ID
+     * @return 收入汇总统计数据
+     */
+    Map<String, Object> getIncomeSummary(Integer storeId);
 }
 
 

+ 6 - 4
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/CouponManageServiceImpl.java

@@ -13,6 +13,7 @@ import shop.alien.entity.result.R;
 import shop.alien.entity.store.*;
 import shop.alien.mapper.*;
 import shop.alien.storeplatform.service.CouponManageService;
+import shop.alien.storeplatform.util.LoginUserUtil;
 import shop.alien.util.common.constant.OrderStatusEnum;
 
 import java.math.BigDecimal;
@@ -531,14 +532,15 @@ public class CouponManageServiceImpl implements CouponManageService {
     /**
      * 核销订单
      *
-     * @param storeId  门店ID
      * @param quanCode 券码
      * @return 核销结果
      */
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public Map<String, String> verifyCoupon(String storeId, String quanCode) {
-        log.info("CouponManageServiceImpl.verifyCoupon - 开始核销订单: storeId={}, quanCode={}", storeId, quanCode);
+    public Map<String, String> verifyCoupon(String quanCode) {
+        log.info("CouponManageServiceImpl.verifyCoupon - 开始核销订单: quanCode={}", quanCode);
+        // 获取登录信息
+        Integer storeId = LoginUserUtil.getCurrentStoreId();
 
         Map<String, String> resultMap = new HashMap<>();
         String lockKey = "coupon:use:" + quanCode;
@@ -674,7 +676,7 @@ public class CouponManageServiceImpl implements CouponManageService {
 
             // 11. 插入收入明细记录
             StoreIncomeDetailsRecord record = new StoreIncomeDetailsRecord();
-            record.setStoreId(Integer.parseInt(storeId));
+            record.setStoreId(storeId);
             record.setUserOrderId(orderCouponMiddle.getId());
             record.setIncomeType(lifeUserOrder.getCouponType());
             record.setBusinessId(orderCouponMiddle.getCouponId());

+ 366 - 1
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/IncomeManageServiceImpl.java

@@ -72,7 +72,7 @@ public class IncomeManageServiceImpl extends ServiceImpl<StoreIncomeDetailsRecor
             log.debug("IncomeManageServiceImpl.getPaymentCycle - 未到账期: date={}", jsonObject.get("date"));
         } else if (paymentType != null && paymentType == 1) {
             // 已到账期: 已绑定提现记录 & 当前时间-4~27天大于创建时间
-            wrapper.isNotNull("sidr.cash_out_id");
+//            wrapper.isNotNull("sidr.cash_out_id");
             wrapper.between("sidr.created_time", DateUtils.calcDays(new Date(), -27), DateUtils.calcDays(new Date(), -4));
             Date startDate = DateUtils.calcDays(now, -27);
             Date endDate = DateUtils.calcDays(now, -4);
@@ -636,5 +636,370 @@ public class IncomeManageServiceImpl extends ServiceImpl<StoreIncomeDetailsRecor
             throw new RuntimeException("快速提现失败:" + e.getMessage(), e);
         }
     }
+
+    /**
+     * 已入账/未入账详情查询
+     *
+     * @param storeId     门店ID
+     * @param incomeType  收入类型, 0:主页, 1:优惠券, 2:代金券, 3:套餐, 4:联名卡
+     * @param paymentType 账期类型,0:未入账,1:已入账
+     * @param startTime   开始时间
+     * @param endTime     结束时间
+     * @param page        页数
+     * @param size        条数
+     * @return 入账详情
+     */
+    @Override
+    public com.alibaba.fastjson2.JSONObject getPaymentDetails(Integer storeId, Integer incomeType, Integer paymentType,
+                                                               String startTime, String endTime, int page, int size) {
+        log.info("IncomeManageServiceImpl.getPaymentDetails - 查询入账详情: storeId={}, incomeType={}, paymentType={}, startTime={}, endTime={}, page={}, size={}",
+                storeId, incomeType, paymentType, startTime, endTime, page, size);
+
+        QueryWrapper<StoreIncomeDetailsRecord> wrapper = new QueryWrapper<>();
+        com.alibaba.fastjson2.JSONObject result = new com.alibaba.fastjson2.JSONObject();
+        Date now = new Date();
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+
+        // 根据 paymentType 设置时间范围和条件
+        if (paymentType == 0) {
+            // 未入账: 当前时间-3天内的记录
+            wrapper.isNull("sidr.cash_out_id");
+            wrapper.gt("sidr.created_time", DateUtils.calcDays(new Date(), -3));
+            Date startDate = DateUtils.calcDays(now, -3);
+            result.put("date", df.format(startDate) + " ~ " + df.format(now));
+            log.info("IncomeManageServiceImpl.getPaymentDetails - 未入账时间范围: {} ~ {}", df.format(startDate), df.format(now));
+        } else {
+            // 已入账: 当前时间-4~27天的记录
+            wrapper.between("sidr.created_time", DateUtils.calcDays(new Date(), -27), DateUtils.calcDays(new Date(), -4));
+            Date startDate = DateUtils.calcDays(now, -27);
+            Date endDate = DateUtils.calcDays(now, -4);
+            result.put("date", df.format(startDate) + " ~ " + df.format(endDate));
+            log.info("IncomeManageServiceImpl.getPaymentDetails - 已入账时间范围: {} ~ {}", df.format(startDate), df.format(endDate));
+        }
+
+        // 店铺ID条件
+        if (storeId != null) {
+            wrapper.eq("sidr.store_id", storeId);
+        }
+
+        // 自定义时间范围(用户输入的时间范围)
+        LocalDate startDate = LocalDate.parse(startTime);
+        LocalDate endDate = LocalDate.parse(endTime);
+        LocalDateTime startOfDay = startDate.atStartOfDay();
+        LocalDateTime endOfDay = endDate.atTime(LocalTime.MAX);
+        wrapper.between("sidr.created_time", startOfDay, endOfDay)
+                .orderByDesc("sidr.created_time");
+
+        // 收入类型条件
+        if (null != incomeType) {
+            if (0 == incomeType) {
+                // 主页: 包含优惠券和团购券
+                wrapper.in("sidr.income_type", CouponTypeEnum.COUPON.getCode(), CouponTypeEnum.GROUP_BUY.getCode());
+            } else {
+                wrapper.eq("sidr.income_type", incomeType);
+            }
+        }
+
+        // 查询收入明细列表
+        List<StoreIncomeDetailsRecordVo> list = storeIncomeDetailsRecordMapper.selectRecordList(wrapper);
+        log.info("IncomeManageServiceImpl.getPaymentDetails - 查询到记录数: {}", list.size());
+
+        // 查询店铺信息
+        StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
+
+        // 处理每条记录的数据格式
+        for (StoreIncomeDetailsRecordVo record : list) {
+            // 空值检查
+            if (record == null) {
+                continue;
+            }
+            
+            StoreIncomeDetailsRecordVo vo = new StoreIncomeDetailsRecordVo();
+            BeanUtils.copyProperties(record, vo);
+
+            // 转换金额格式(分转元,保留2位小数)
+            record.setMoneyStr(new BigDecimal(vo.getMoney())
+                    .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString());
+
+            // 设置日期
+            String format = df.format(record.getCreatedTime());
+            record.setDate(format);
+
+            // 设置抽成比例
+            record.setCommissionRate(storeInfo.getCommissionRate());
+
+            // 计算售价(收益 + 手续费)
+            record.setIncomeMoney(new BigDecimal(vo.getMoney())
+                    .add(new BigDecimal(vo.getCommission()))
+                    .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString());
+
+            // 设置手续费
+            record.setCommissionStr(new BigDecimal(vo.getCommission())
+                    .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString());
+
+            vo.setCommissionRate(storeInfo.getCommissionRate());
+        }
+
+        // 分页处理
+        result.put("data", ListToPage.setPage(list, page, size));
+
+        // 计算总金额(元)
+        BigDecimal totalMoney = new BigDecimal(list.stream()
+                .mapToInt(StoreIncomeDetailsRecord::getMoney)
+                .sum())
+                .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
+        result.put("money", totalMoney.toString());
+
+        log.info("IncomeManageServiceImpl.getPaymentDetails - 查询完成: 总金额={}元, 记录数={}",
+                totalMoney, list.size());
+
+        return result;
+    }
+
+    /**
+     * 收入汇总统计
+     * 整合四项关键数据:可提现金额、今日收益、已入账、未入账
+     *
+     * @param storeId 门店ID
+     * @return 收入汇总统计数据
+     */
+    @Override
+    public Map<String, Object> getIncomeSummary(Integer storeId) {
+        log.info("IncomeManageServiceImpl.getIncomeSummary - 开始查询收入汇总: storeId={}", storeId);
+
+        Map<String, Object> result = new HashMap<>();
+
+        try {
+            // ==================== 1. 账户余额 & 可提现金额 ====================
+            calculateAccountBalance(storeId, result);
+
+            // ==================== 2. 今日收益 ====================
+            calculateTodayIncome(storeId, result);
+
+            // ==================== 3. 未入账统计(近3天) ====================
+            calculateNotPaidIncome(storeId, result);
+
+            // ==================== 4. 已入账统计(4-27天) ====================
+            calculatePaidIncome(storeId, result);
+
+            log.info("IncomeManageServiceImpl.getIncomeSummary - 查询完成: result={}", result);
+
+        } catch (Exception e) {
+            log.error("IncomeManageServiceImpl.getIncomeSummary - 查询失败: storeId={}, error={}", 
+                    storeId, e.getMessage(), e);
+            throw new RuntimeException("查询收入汇总失败:" + e.getMessage(), e);
+        }
+
+        return result;
+    }
+
+    /**
+     * 计算账户余额和可提现金额
+     * 
+     * 业务逻辑:
+     * 1. 账户余额 = 用户账户中的总余额(store_user.money)
+     * 2. 可提现金额 = 4-27天内未绑定提现的收入 - 待审核/已通过的提现金额
+     * 
+     * @param storeId 门店ID
+     * @param result  结果集
+     */
+    private void calculateAccountBalance(Integer storeId, Map<String, Object> result) {
+        log.info("IncomeManageServiceImpl.calculateAccountBalance - 开始计算账户余额和可提现金额");
+
+        // ---------- 1.1 查询账户总余额 ----------
+        LambdaQueryWrapper<StoreUser> userWrapper = new LambdaQueryWrapper<>();
+        userWrapper.eq(StoreUser::getStoreId, storeId);
+        StoreUser storeUser = storeUserMapper.selectOne(userWrapper);
+
+        if (storeUser == null) {
+            log.warn("IncomeManageServiceImpl.calculateAccountBalance - 未找到店铺用户: storeId={}", storeId);
+            result.put("balance", "0.00");
+            result.put("cashOutMoney", "0.00");
+            return;
+        }
+
+        // 账户总余额(分转元)
+        String balance = new BigDecimal(storeUser.getMoney())
+                .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
+                .toString();
+        result.put("balance", balance);
+        log.info("IncomeManageServiceImpl.calculateAccountBalance - 账户余额: {}元", balance);
+
+        // ---------- 1.2 查询4-27天内未绑定提现的收入 ----------
+        Date now = new Date();
+        Date startDate = DateUtils.calcDays(now, -27);  // 27天前
+        Date endDate = DateUtils.calcDays(now, -4);      // 4天前
+
+        LambdaQueryWrapper<StoreIncomeDetailsRecord> incomeWrapper = new LambdaQueryWrapper<>();
+        incomeWrapper.between(StoreIncomeDetailsRecord::getCreatedTime, startDate, endDate)
+                .isNull(StoreIncomeDetailsRecord::getCashOutId)  // 未绑定提现记录
+                .eq(StoreIncomeDetailsRecord::getStoreId, storeId);
+
+        List<StoreIncomeDetailsRecord> incomeList = this.list(incomeWrapper);
+        int availableIncome = incomeList.stream()
+                .mapToInt(StoreIncomeDetailsRecord::getMoney)
+                .sum();
+
+        log.info("IncomeManageServiceImpl.calculateAccountBalance - 4-27天内可提现收入: {}分, 记录数: {}", 
+                availableIncome, incomeList.size());
+
+        // ---------- 1.3 查询待审核和已通过的提现金额 ----------
+        QueryWrapper<StoreCashOutRecord> cashOutWrapper = new QueryWrapper<>();
+        cashOutWrapper.eq("store_id", storeId)
+                .in("payment_status", "1", "3")  // 1-待审核, 3-已通过
+                .eq("delete_flag", "0");
+
+        List<StoreCashOutRecord> cashOutRecords = storeCashOutRecordMapper.selectList(cashOutWrapper);
+        int pendingCashOut = cashOutRecords.stream()
+                .collect(Collectors.summingInt(StoreCashOutRecord::getMoney));
+
+        log.info("IncomeManageServiceImpl.calculateAccountBalance - 待审核/已通过提现: {}分, 记录数: {}", 
+                pendingCashOut, cashOutRecords.size());
+
+        // ---------- 1.4 计算可提现金额 ----------
+        // 可提现金额 = 可提现收入 - 审核中的提现
+        int cashOutMoney = availableIncome - pendingCashOut;
+        String cashOutMoneyStr = new BigDecimal(Math.max(cashOutMoney, 0))  // 确保不为负数
+                .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
+                .toString();
+        result.put("cashOutMoney", cashOutMoneyStr);
+
+        log.info("IncomeManageServiceImpl.calculateAccountBalance - 可提现金额: {}元", cashOutMoneyStr);
+    }
+
+    /**
+     * 计算今日收益
+     * 
+     * 业务逻辑:
+     * 统计当天00:00:00 ~ 23:59:59所有收入的总和
+     * 
+     * @param storeId 门店ID
+     * @param result  结果集
+     */
+    private void calculateTodayIncome(Integer storeId, Map<String, Object> result) {
+        log.info("IncomeManageServiceImpl.calculateTodayIncome - 开始计算今日收益");
+
+        // ---------- 2.1 获取今天的日期范围 ----------
+        Date now = new Date();
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+        String today = df.format(now);
+        String startTime = today + " 00:00:00";
+        String endTime = today + " 23:59:59";
+
+        // ---------- 2.2 查询今天的收入记录 ----------
+        LambdaQueryWrapper<StoreIncomeDetailsRecord> todayWrapper = new LambdaQueryWrapper<>();
+        todayWrapper.between(StoreIncomeDetailsRecord::getCreatedTime, startTime, endTime)
+                .eq(StoreIncomeDetailsRecord::getStoreId, storeId);
+
+        List<StoreIncomeDetailsRecord> todayList = this.list(todayWrapper);
+
+        // ---------- 2.3 汇总今日收益 ----------
+        int todayIncome = todayList.stream()
+                .mapToInt(StoreIncomeDetailsRecord::getMoney)
+                .sum();
+
+        String todayIncomeStr = new BigDecimal(todayIncome)
+                .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
+                .toString();
+        result.put("todayIncome", todayIncomeStr);
+
+        log.info("IncomeManageServiceImpl.calculateTodayIncome - 今日收益: {}元, 记录数: {}", 
+                todayIncomeStr, todayList.size());
+    }
+
+    /**
+     * 计算未入账统计(近3天)
+     * 
+     * 业务逻辑:
+     * 1. 时间范围:当前时间 - 3天 ~ 当前时间
+     * 2. 条件:未绑定提现记录(cash_out_id IS NULL)
+     * 3. 说明:这部分收入还未到提现账期,不能提现
+     * 
+     * @param storeId 门店ID
+     * @param result  结果集
+     */
+    private void calculateNotPaidIncome(Integer storeId, Map<String, Object> result) {
+        log.info("IncomeManageServiceImpl.calculateNotPaidIncome - 开始计算未入账统计");
+
+        Date now = new Date();
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+
+        // ---------- 3.1 计算时间范围 ----------
+        Date notPaidStartDate = DateUtils.calcDays(now, -3);  // 3天前
+        String notPaidDateRange = df.format(notPaidStartDate) + " ~ " + df.format(now);
+        result.put("notPaidDateRange", notPaidDateRange);
+
+        log.info("IncomeManageServiceImpl.calculateNotPaidIncome - 未入账时间范围: {}", notPaidDateRange);
+
+        // ---------- 3.2 查询未入账收入(近3天,未绑定提现) ----------
+        QueryWrapper<StoreIncomeDetailsRecord> notPaidWrapper = new QueryWrapper<>();
+        notPaidWrapper.isNull("sidr.cash_out_id")  // 未绑定提现记录
+                .gt("sidr.created_time", notPaidStartDate)  // 大于3天前
+                .eq("sidr.store_id", storeId);
+
+        List<StoreIncomeDetailsRecordVo> notPaidList = storeIncomeDetailsRecordMapper.selectRecordList(notPaidWrapper);
+
+        // ---------- 3.3 汇总未入账金额和数量 ----------
+        int notPaidMoney = notPaidList.stream()
+                .mapToInt(StoreIncomeDetailsRecord::getMoney)
+                .sum();
+
+        String notPaidMoneyStr = new BigDecimal(notPaidMoney)
+                .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
+                .toString();
+
+        result.put("notPaidMoney", notPaidMoneyStr);
+        result.put("notPaidCount", notPaidList.size());
+
+        log.info("IncomeManageServiceImpl.calculateNotPaidIncome - 未入账: {}元, 记录数: {}", 
+                notPaidMoneyStr, notPaidList.size());
+    }
+
+    /**
+     * 计算已入账统计(4-27天)
+     * 
+     * 业务逻辑:
+     * 1. 时间范围:当前时间 - 27天 ~ 当前时间 - 4天
+     * 2. 说明:这部分收入已到提现账期,可以申请提现
+     * 
+     * @param storeId 门店ID
+     * @param result  结果集
+     */
+    private void calculatePaidIncome(Integer storeId, Map<String, Object> result) {
+        log.info("IncomeManageServiceImpl.calculatePaidIncome - 开始计算已入账统计");
+
+        Date now = new Date();
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+
+        // ---------- 4.1 计算时间范围 ----------
+        Date paidStartDate = DateUtils.calcDays(now, -27);  // 27天前
+        Date paidEndDate = DateUtils.calcDays(now, -4);      // 4天前
+        String paidDateRange = df.format(paidStartDate) + " ~ " + df.format(paidEndDate);
+        result.put("paidDateRange", paidDateRange);
+
+        log.info("IncomeManageServiceImpl.calculatePaidIncome - 已入账时间范围: {}", paidDateRange);
+
+        // ---------- 4.2 查询已入账收入(4-27天前) ----------
+        QueryWrapper<StoreIncomeDetailsRecord> paidWrapper = new QueryWrapper<>();
+        paidWrapper.between("sidr.created_time", paidStartDate, paidEndDate)
+                .eq("sidr.store_id", storeId);
+
+        List<StoreIncomeDetailsRecordVo> paidList = storeIncomeDetailsRecordMapper.selectRecordList(paidWrapper);
+
+        // ---------- 4.3 汇总已入账金额和数量 ----------
+        int paidMoney = paidList.stream()
+                .mapToInt(StoreIncomeDetailsRecord::getMoney)
+                .sum();
+
+        String paidMoneyStr = new BigDecimal(paidMoney)
+                .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
+                .toString();
+
+        result.put("paidMoney", paidMoneyStr);
+        result.put("paidCount", paidList.size());
+
+        log.info("IncomeManageServiceImpl.calculatePaidIncome - 已入账: {}元, 记录数: {}", 
+                paidMoneyStr, paidList.size());
+    }
 }
 

+ 789 - 0
alien-store-platform/接口文档/30-已入账未入账详情查询接口.md

@@ -0,0 +1,789 @@
+# 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
+

+ 724 - 0
alien-store-platform/接口文档/31-收入汇总统计接口.md

@@ -0,0 +1,724 @@
+# Web端商户收入汇总统计接口文档
+
+## 模块概述
+
+本接口提供收入数据的汇总统计功能,一次性返回可提现金额、今日收益、已入账总数、未入账总数四项关键数据,方便前端首页展示。
+
+---
+
+## 接口信息
+
+### 收入汇总统计
+
+#### 接口详情
+
+- **接口名称**: 收入汇总统计
+- **接口路径**: `GET /incomeManage/getIncomeSummary`
+- **请求方式**: GET
+- **接口描述**: 一次性查询可提现金额、今日收益、已入账金额/数量、未入账金额/数量
+- **登录验证**: ✅ 需要(通过 JWT token 自动获取店铺ID)
+- **数据来源**: 复用三个现有接口的业务逻辑
+
+---
+
+## 业务逻辑说明
+
+### 数据来源接口
+
+本接口整合了以下三个app端接口的数据:
+
+| 数据项 | app端接口 | 业务逻辑 |
+|--------|----------|---------|
+| 可提现金额 | `/alienStore/storeIncomeDetailsRecord/accountBalance` | 4-27天内未绑定提现的收入 - 待审核/已通过的提现金额 |
+| 今日收益 | `/alienStore/storeIncomeDetailsRecord/todayIncome` | 当天00:00:00 ~ 23:59:59的收入总和 |
+| 未入账 | `/alienStore/storeIncomeDetailsRecord/noYetPayment?paymentType=0` | 近3天内未绑定提现的收入 |
+| 已入账 | `/alienStore/storeIncomeDetailsRecord/noYetPayment?paymentType=1` | 4-27天内的收入 |
+
+---
+
+### 数据计算规则
+
+#### **1. 可提现金额**
+
+```java
+// 查询4-27天内未绑定提现记录的收入
+cashOutMoney = SUM(money WHERE created_time BETWEEN (NOW() - 27天) AND (NOW() - 4天) 
+                              AND cash_out_id IS NULL)
+
+// 减去待审核和已通过的提现金额
+totalCashOutAmount = SUM(money WHERE payment_status IN (1, 3) AND delete_flag = 0)
+
+// 可提现金额
+cashOutMoney = cashOutMoney - totalCashOutAmount
+```
+
+**说明**:
+- ✅ 只统计4-27天前的收入(已到提现账期)
+- ✅ 减去已申请但未到账的提现金额
+- ✅ payment_status: 1-待审核, 3-已通过
+
+---
+
+#### **2. 今日收益**
+
+```java
+// 查询今天的收入总和
+todayIncome = SUM(money WHERE created_time BETWEEN '今日 00:00:00' AND '今日 23:59:59')
+```
+
+**说明**:
+- ✅ 统计当天所有收入
+- ✅ 包含所有收入类型(优惠券、代金券、团购券等)
+
+---
+
+#### **3. 未入账(近3天)**
+
+```java
+// 查询近3天未绑定提现的收入
+notPaidMoney = SUM(money WHERE created_time > (NOW() - 3天) 
+                              AND cash_out_id IS NULL)
+notPaidCount = COUNT(*)
+```
+
+**说明**:
+- ✅ 统计近3天的收入
+- ✅ 未绑定提现记录
+- ✅ 还未到提现账期
+
+---
+
+#### **4. 已入账(4-27天)**
+
+```java
+// 查询4-27天前的收入
+paidMoney = SUM(money WHERE created_time BETWEEN (NOW() - 27天) AND (NOW() - 4天))
+paidCount = COUNT(*)
+```
+
+**说明**:
+- ✅ 统计4-27天前的收入
+- ✅ 已到提现账期
+- ✅ 可以申请提现
+
+---
+
+## 请求参数
+
+**无参数**
+
+从 JWT token 自动获取店铺ID(`storeId`)
+
+---
+
+## 请求示例
+
+```http
+GET /incomeManage/getIncomeSummary
+Headers:
+  Authorization: Bearer YOUR_JWT_TOKEN
+```
+
+```bash
+curl -X GET "http://localhost:8080/incomeManage/getIncomeSummary" \
+  -H "Authorization: Bearer YOUR_JWT_TOKEN"
+```
+
+---
+
+## 响应参数
+
+### 成功响应
+
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": {
+        "balance": "3580.50",
+        "cashOutMoney": "1256.80",
+        "todayIncome": "128.30",
+        "notPaidMoney": "385.60",
+        "notPaidCount": 12,
+        "notPaidDateRange": "2025-11-18 ~ 2025-11-21",
+        "paidMoney": "1580.20",
+        "paidCount": 45,
+        "paidDateRange": "2025-10-25 ~ 2025-11-17"
+    },
+    "msg": "操作成功"
+}
+```
+
+### 响应字段说明
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| code | Integer | 状态码(200-成功) |
+| success | Boolean | 是否成功 |
+| data | Object | 汇总数据 |
+| msg | String | 提示信息 |
+
+#### data 字段详细说明
+
+| 字段名 | 类型 | 单位 | 说明 |
+|--------|------|------|------|
+| **balance** | String | 元 | 账户总余额 |
+| **cashOutMoney** | String | 元 | 可提现金额(4-27天内可提现的收入) |
+| **todayIncome** | String | 元 | 今日收益(当天所有收入) |
+| **notPaidMoney** | String | 元 | 未入账金额(近3天的收入) |
+| **notPaidCount** | Integer | 条 | 未入账记录数 |
+| **notPaidDateRange** | String | - | 未入账时间范围 |
+| **paidMoney** | String | 元 | 已入账金额(4-27天的收入) |
+| **paidCount** | Integer | 条 | 已入账记录数 |
+| **paidDateRange** | String | - | 已入账时间范围 |
+
+---
+
+### 失败响应
+
+#### 1. 未登录或 token 无效
+
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "请先登录"
+}
+```
+
+#### 2. 查询异常
+
+```json
+{
+    "code": 500,
+    "success": false,
+    "data": null,
+    "msg": "查询失败:{异常信息}"
+}
+```
+
+---
+
+## 业务场景
+
+### 场景 1: 首页数据展示
+
+**业务需求**: 商户登录后,首页需要展示收入概况的关键数据
+
+**请求**:
+```http
+GET /incomeManage/getIncomeSummary
+Headers:
+  Authorization: Bearer VALID_TOKEN
+```
+
+**响应**:
+```json
+{
+    "code": 200,
+    "success": true,
+    "data": {
+        "balance": "3580.50",
+        "cashOutMoney": "1256.80",
+        "todayIncome": "128.30",
+        "notPaidMoney": "385.60",
+        "notPaidCount": 12,
+        "paidMoney": "1580.20",
+        "paidCount": 45
+    }
+}
+```
+
+**前端展示**:
+```
+┌────────────────────────────────────────┐
+│          收入概况                       │
+├────────────────────────────────────────┤
+│  账户余额: ¥3,580.50                   │
+│  可提现金额: ¥1,256.80                 │
+│  今日收益: ¥128.30                     │
+│                                        │
+│  未入账: ¥385.60 (12笔)                │
+│  已入账: ¥1,580.20 (45笔)              │
+└────────────────────────────────────────┘
+```
+
+---
+
+### 场景 2: 数据刷新
+
+**业务需求**: 用户下拉刷新或手动刷新收入数据
+
+**请求**:
+```http
+GET /incomeManage/getIncomeSummary
+Headers:
+  Authorization: Bearer VALID_TOKEN
+```
+
+**业务逻辑**:
+- ✅ 重新计算所有统计数据
+- ✅ 返回最新的收入概况
+- ✅ 前端更新展示
+
+---
+
+### 场景 3: 定时刷新
+
+**业务需求**: 前端每隔一定时间自动刷新数据
+
+**实现方式**:
+```javascript
+setInterval(() => {
+    fetchIncomeSummary();
+}, 60000);  // 每分钟刷新一次
+```
+
+---
+
+## 数据关系说明
+
+### 金额之间的关系
+
+```
+账户余额 (balance)
+    = 历史所有收入 - 已提现金额
+
+可提现金额 (cashOutMoney)
+    = 4-27天内未绑定提现的收入 
+    - 待审核的提现金额 
+    - 已通过的提现金额
+
+今日收益 (todayIncome)
+    = 今天所有收入
+
+未入账 (notPaidMoney)
+    = 近3天未绑定提现的收入
+    (还未到提现账期)
+
+已入账 (paidMoney)
+    = 4-27天的收入
+    (已到提现账期,可以提现)
+```
+
+---
+
+### 时间范围关系
+
+```
+时间轴: ←────────────────────────────────→
+       -27天  -4天  -3天  今天
+
+已入账:  ├─────┤
+         4-27天
+
+未入账:          ├──┤
+                3天内
+
+今日收益:             │
+                    今天
+```
+
+---
+
+## 前端集成示例
+
+### Vue.js 示例
+
+```javascript
+export default {
+    data() {
+        return {
+            summary: {
+                balance: '0.00',
+                cashOutMoney: '0.00',
+                todayIncome: '0.00',
+                notPaidMoney: '0.00',
+                notPaidCount: 0,
+                paidMoney: '0.00',
+                paidCount: 0,
+                notPaidDateRange: '',
+                paidDateRange: ''
+            },
+            loading: false
+        };
+    },
+    methods: {
+        async fetchIncomeSummary() {
+            this.loading = true;
+            try {
+                const response = await this.$axios.get('/incomeManage/getIncomeSummary');
+                
+                if (response.data.success) {
+                    this.summary = response.data.data;
+                    this.$message.success('数据加载成功');
+                } else {
+                    this.$message.error(response.data.msg);
+                }
+            } catch (error) {
+                console.error('查询失败:', error);
+                this.$message.error('数据加载失败,请稍后重试');
+            } finally {
+                this.loading = false;
+            }
+        },
+        
+        // 格式化金额显示
+        formatMoney(amount) {
+            return '¥' + parseFloat(amount).toLocaleString('zh-CN', {
+                minimumFractionDigits: 2,
+                maximumFractionDigits: 2
+            });
+        },
+        
+        // 下拉刷新
+        onRefresh() {
+            this.fetchIncomeSummary();
+        }
+    },
+    mounted() {
+        this.fetchIncomeSummary();
+        
+        // 每分钟自动刷新
+        this.timer = setInterval(() => {
+            this.fetchIncomeSummary();
+        }, 60000);
+    },
+    beforeDestroy() {
+        if (this.timer) {
+            clearInterval(this.timer);
+        }
+    }
+}
+```
+
+---
+
+### React 示例
+
+```javascript
+import { useState, useEffect } from 'react';
+import axios from 'axios';
+import { Card, Statistic, Row, Col, Spin } from 'antd';
+import { WalletOutlined, DollarOutlined, RiseOutlined } from '@ant-design/icons';
+
+function IncomeSummary() {
+    const [summary, setSummary] = useState({
+        balance: '0.00',
+        cashOutMoney: '0.00',
+        todayIncome: '0.00',
+        notPaidMoney: '0.00',
+        notPaidCount: 0,
+        paidMoney: '0.00',
+        paidCount: 0
+    });
+    const [loading, setLoading] = useState(false);
+    
+    const fetchData = async () => {
+        setLoading(true);
+        try {
+            const response = await axios.get('/incomeManage/getIncomeSummary');
+            
+            if (response.data.success) {
+                setSummary(response.data.data);
+            }
+        } catch (error) {
+            console.error('查询失败:', error);
+        } finally {
+            setLoading(false);
+        }
+    };
+    
+    useEffect(() => {
+        fetchData();
+        
+        // 每分钟自动刷新
+        const timer = setInterval(fetchData, 60000);
+        
+        return () => clearInterval(timer);
+    }, []);
+    
+    return (
+        <Spin spinning={loading}>
+            <Row gutter={16}>
+                <Col span={6}>
+                    <Card>
+                        <Statistic
+                            title="账户余额"
+                            value={summary.balance}
+                            prefix="¥"
+                            precision={2}
+                            valueStyle={{ color: '#3f8600' }}
+                            suffix={<WalletOutlined />}
+                        />
+                    </Card>
+                </Col>
+                
+                <Col span={6}>
+                    <Card>
+                        <Statistic
+                            title="可提现金额"
+                            value={summary.cashOutMoney}
+                            prefix="¥"
+                            precision={2}
+                            valueStyle={{ color: '#cf1322' }}
+                            suffix={<DollarOutlined />}
+                        />
+                    </Card>
+                </Col>
+                
+                <Col span={6}>
+                    <Card>
+                        <Statistic
+                            title="今日收益"
+                            value={summary.todayIncome}
+                            prefix="¥"
+                            precision={2}
+                            valueStyle={{ color: '#1890ff' }}
+                            suffix={<RiseOutlined />}
+                        />
+                    </Card>
+                </Col>
+                
+                <Col span={6}>
+                    <Card>
+                        <Statistic
+                            title="未入账"
+                            value={summary.notPaidMoney}
+                            prefix="¥"
+                            precision={2}
+                            suffix={`(${summary.notPaidCount}笔)`}
+                        />
+                    </Card>
+                </Col>
+            </Row>
+            
+            <Row gutter={16} style={{ marginTop: 16 }}>
+                <Col span={12}>
+                    <Card>
+                        <Statistic
+                            title="已入账"
+                            value={summary.paidMoney}
+                            prefix="¥"
+                            precision={2}
+                            suffix={`(${summary.paidCount}笔)`}
+                        />
+                        <p style={{ marginTop: 8, color: '#999' }}>
+                            {summary.paidDateRange}
+                        </p>
+                    </Card>
+                </Col>
+            </Row>
+        </Spin>
+    );
+}
+```
+
+---
+
+## 测试用例
+
+### 测试场景1: 正常查询
+
+**前置条件**:
+- 用户已登录
+- 店铺ID=103
+- 有收入记录
+
+**执行步骤**:
+1. 发送请求: `GET /incomeManage/getIncomeSummary`
+
+**预期结果**:
+- 返回成功
+- `balance` > 0
+- `cashOutMoney` >= 0
+- `todayIncome` >= 0
+- `notPaidMoney` >= 0, `notPaidCount` >= 0
+- `paidMoney` >= 0, `paidCount` >= 0
+- 包含时间范围字段
+
+---
+
+### 测试场景2: 无收入记录
+
+**前置条件**:
+- 用户已登录
+- 新店铺,无任何收入
+
+**执行步骤**:
+1. 发送请求: `GET /incomeManage/getIncomeSummary`
+
+**预期结果**:
+- 返回成功
+- 所有金额字段 = "0.00"
+- 所有数量字段 = 0
+
+---
+
+### 测试场景3: 只有今日收入
+
+**前置条件**:
+- 用户已登录
+- 只有今天有收入记录
+
+**执行步骤**:
+1. 发送请求: `GET /incomeManage/getIncomeSummary`
+
+**预期结果**:
+- `todayIncome` > 0
+- `notPaidMoney` > 0(今天的收入也在3天内)
+- `paidMoney` = 0(没有4-27天前的收入)
+
+---
+
+## 常见问题
+
+### Q1: 为什么可提现金额比已入账金额少?
+
+**答案**: 
+- 可提现金额 = 已入账金额 - 待审核的提现 - 已通过的提现
+- 如果有提现申请在审核中或已通过,会从可提现金额中扣除
+
+---
+
+### Q2: 今日收益会计入未入账吗?
+
+**答案**: 会的。
+- 今日收益统计当天所有收入
+- 未入账统计近3天的收入(包括今天)
+- 所以今日收益会包含在未入账中
+
+---
+
+### Q3: 账户余额和可提现金额有什么区别?
+
+**答案**:
+- **账户余额**: 所有历史收入 - 已提现金额(总余额)
+- **可提现金额**: 4-27天内可以立即提现的金额(扣除审核中的提现)
+
+---
+
+### Q4: 已入账的收入一定可以提现吗?
+
+**答案**: 不一定。
+- 已入账 = 4-27天前的收入(已到账期)
+- 可提现金额 = 已入账 - 审核中的提现 - 已通过的提现
+- 如果已有提现申请,可提现金额会相应减少
+
+---
+
+### Q5: 时间范围是如何计算的?
+
+**答案**:
+- **未入账**: 当前时间 - 3天 ~ 当前时间
+- **已入账**: 当前时间 - 27天 ~ 当前时间 - 4天
+- 时间范围会随着查询时间动态变化
+
+---
+
+## 注意事项
+
+### 1. 数据实时性
+
+- ⚠️ 数据基于查询时刻实时计算
+- ⚠️ 建议前端设置定时刷新(如每分钟)
+- ⚠️ 或提供手动刷新按钮
+
+### 2. 金额单位
+
+- ⚠️ 数据库存储单位:**分**
+- ⚠️ 接口返回单位:**元**(保留2位小数)
+- ⚠️ 转换公式:元 = 分 / 100
+
+### 3. 时间范围
+
+- ⚠️ 时间范围是动态的,基于当前时间计算
+- ⚠️ 未入账:3天内
+- ⚠️ 已入账:4-27天
+- ⚠️ 可提现:4-27天内未绑定提现的
+
+### 4. 性能考虑
+
+- ⚠️ 涉及多次数据库查询
+- ⚠️ 建议适当缓存(如Redis,5分钟过期)
+- ⚠️ 避免频繁调用
+
+---
+
+## 性能优化建议
+
+### 1. 缓存方案
+
+```java
+@Cacheable(value = "incomeSummary", key = "#storeId", unless = "#result == null")
+public Map<String, Object> getIncomeSummary(Integer storeId) {
+    // ... 查询逻辑
+}
+```
+
+**配置**:
+```yaml
+spring:
+  cache:
+    type: redis
+    redis:
+      time-to-live: 300000  # 5分钟过期
+```
+
+---
+
+### 2. 异步查询
+
+```java
+// 使用CompletableFuture并行查询
+CompletableFuture<String> balanceFuture = CompletableFuture.supplyAsync(() -> getBalance(storeId));
+CompletableFuture<String> todayIncomeFuture = CompletableFuture.supplyAsync(() -> getTodayIncome(storeId));
+CompletableFuture<Map> notPaidFuture = CompletableFuture.supplyAsync(() -> getNotPaidData(storeId));
+CompletableFuture<Map> paidFuture = CompletableFuture.supplyAsync(() -> getPaidData(storeId));
+
+// 等待所有查询完成
+CompletableFuture.allOf(balanceFuture, todayIncomeFuture, notPaidFuture, paidFuture).join();
+```
+
+---
+
+## 更新日志
+
+### 2025-11-21 (v1.0)
+
+**新增接口**:
+- ✅ `GET /incomeManage/getIncomeSummary` - 收入汇总统计
+
+**核心功能**:
+- ✅ 一次性返回4项关键数据
+- ✅ 账户余额
+- ✅ 可提现金额
+- ✅ 今日收益
+- ✅ 已入账/未入账金额和数量
+
+**数据来源**:
+- 复用三个现有接口的业务逻辑
+- `/accountBalance` - 可提现金额
+- `/todayIncome` - 今日收益
+- `/noYetPayment` - 已入账/未入账
+
+**涉及文件**:
+- `IncomeManageController.java` - 新增 `getIncomeSummary` 接口
+- `IncomeManageService.java` - 新增方法定义
+- `IncomeManageServiceImpl.java` - 新增方法实现
+
+**代码质量**:
+- ✅ Linter检查:无错误
+- ✅ 日志记录:详细
+- ✅ 异常处理:完善
+- ✅ 代码注释:完整
+
+**开发人员**: ssk
+
+---
+
+**文档版本**: v1.0  
+**最后更新**: 2025-11-21  
+**维护人员**: ssk
+