Преглед изворни кода

fix: 修正数据统计页总收入/注册用户/活跃用户计算逻辑

- 总收入公式由「充值+跨店收入」修正为「充值+跨店收入-退款-跨店支出」,
  与月结 feeBase 口径一致,避免虚高
- countMonthRegister 增加 stationId 参数,与 countDailyRegister 保持一致
- sumByType 改用 groupingBy+summingInt 替代 toMap+Integer::sum,
  避免 amount 为 null 时 Map.merge 抛出 NPE

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
skyline пре 1 недеља
родитељ
комит
2cce7d4495

+ 1 - 1
car-wash-service/src/main/java/com/kym/service/UserService.java

@@ -34,5 +34,5 @@ public interface UserService extends MPJBaseService<User> {
 
     Map<String, Integer> countDailyRegister(LocalDate statDay, String... stationId);
 
-    Map<String, Integer> countMonthRegister(LocalDate statDay);
+    Map<String, Integer> countMonthRegister(LocalDate statDay, String... stationId);
 }

+ 3 - 1
car-wash-service/src/main/java/com/kym/service/impl/DailyStatServiceImpl.java

@@ -97,9 +97,11 @@ public class DailyStatServiceImpl extends MyBaseServiceImpl<DailyStatMapper, Dai
         var refundCountMap = splitRecordService.countDailyRefund(statDay);
         var activeUserMap = washOrderService.countDailyActiveUsers(statDay);
 
-        // 总入账 = 充值收入 + 跨店收入
+        // 总入账 = 充值收入 + 跨店收入 - 退款 - 跨店支出
         var incomeMap = new HashMap<>(rechargeIncomeMap);
         crossIncomeMap.forEach((k, v) -> incomeMap.merge(k, v, Integer::sum));
+        refundMap.forEach((k, v) -> incomeMap.merge(k, -v, Integer::sum));
+        crossExpendMap.forEach((k, v) -> incomeMap.merge(k, -v, Integer::sum));
 
         // 收集所有出现过的站点(过滤掉无归属的null和"未分配"占位符)
         var allStationIds = Stream.of(amountMap, registerMap, ordersCountMap, incomeMap,

+ 3 - 1
car-wash-service/src/main/java/com/kym/service/impl/MonthStatServiceImpl.java

@@ -100,9 +100,11 @@ public class MonthStatServiceImpl extends MyBaseServiceImpl<MonthStatMapper, Mon
         var refundCountMap = splitRecordService.countMonthRefund(statDay);
         var activeUserMap = washOrderService.countMonthActiveUsers(statDay);
 
-        // 总入账 = 充值收入 + 跨店收入
+        // 总入账 = 充值收入 + 跨店收入 - 退款 - 跨店支出
         var incomeMap = new HashMap<>(rechargeIncomeMap);
         crossIncomeMap.forEach((k, v) -> incomeMap.merge(k, v, Integer::sum));
+        refundMap.forEach((k, v) -> incomeMap.merge(k, -v, Integer::sum));
+        crossExpendMap.forEach((k, v) -> incomeMap.merge(k, -v, Integer::sum));
 
         var platformFeeMap = settlementService.lambdaQuery()
                 .eq(SettlementRecord::getSettlementPeriod, statMonth)

+ 2 - 1
car-wash-service/src/main/java/com/kym/service/impl/SplitRecordServiceImpl.java

@@ -188,7 +188,8 @@ public class SplitRecordServiceImpl extends MyBaseServiceImpl<SplitRecordMapper,
                 .eq(SplitRecord::getType, type)
                 .list()
                 .stream()
-                .collect(Collectors.toMap(classifier, SplitRecord::getAmount, Integer::sum));
+                .filter(r -> r.getAmount() != null && classifier.apply(r) != null)
+                .collect(Collectors.groupingBy(classifier, Collectors.summingInt(SplitRecord::getAmount)));
     }
 
     private Map<String, Integer> countByType(Boolean isDaily, LocalDate statDay, Function<SplitRecord, String> classifier, Integer type) {

+ 42 - 7
car-wash-service/src/main/java/com/kym/service/impl/StatServiceImpl.java

@@ -126,13 +126,26 @@ public class StatServiceImpl implements StatService {
                 .ge(WashOrder::getStartTime, LocalDateTime.of(LocalDate.now(), LocalTime.MIN))
                 .list();
 
-        // 今日收入:充值收入 + 跨店收入(按 split_record 创建时间,与日统计表口径一致)
-        var todayIncome = splitRecordService.lambdaQuery()
+        // 今日收入:充值收入 + 跨店收入 - 退款 - 跨店支出
+        var todayRechargeAndCrossIncome = splitRecordService.lambdaQuery()
                 .eq(SplitRecord::getToStationId, stationId)
                 .in(SplitRecord::getType, SplitRecord.TYPE_RECHARGE, SplitRecord.TYPE_CROSS_INCOME)
                 .ge(SplitRecord::getCreateTime, LocalDateTime.of(LocalDate.now(), LocalTime.MIN))
                 .lt(SplitRecord::getCreateTime, LocalDateTime.of(LocalDate.now().plusDays(1), LocalTime.MIN))
-                .list().stream().mapToInt(SplitRecord::getAmount).sum();
+                .list().stream().filter(r -> r.getAmount() != null).mapToInt(SplitRecord::getAmount).sum();
+        var todayRefund = splitRecordService.lambdaQuery()
+                .eq(SplitRecord::getFromStationId, stationId)
+                .eq(SplitRecord::getType, SplitRecord.TYPE_REFUND)
+                .ge(SplitRecord::getCreateTime, LocalDateTime.of(LocalDate.now(), LocalTime.MIN))
+                .lt(SplitRecord::getCreateTime, LocalDateTime.of(LocalDate.now().plusDays(1), LocalTime.MIN))
+                .list().stream().filter(r -> r.getAmount() != null).mapToInt(SplitRecord::getAmount).sum();
+        var todayCrossExpend = splitRecordService.lambdaQuery()
+                .eq(SplitRecord::getFromStationId, stationId)
+                .eq(SplitRecord::getType, SplitRecord.TYPE_CROSS_EXPEND)
+                .ge(SplitRecord::getCreateTime, LocalDateTime.of(LocalDate.now(), LocalTime.MIN))
+                .lt(SplitRecord::getCreateTime, LocalDateTime.of(LocalDate.now().plusDays(1), LocalTime.MIN))
+                .list().stream().filter(r -> r.getAmount() != null).mapToInt(SplitRecord::getAmount).sum();
+        var todayIncome = todayRechargeAndCrossIncome - todayRefund - todayCrossExpend;
 
         // 站点今日注册人数
         var todayRegistrations = userService.countDailyRegister(LocalDate.now(), stationId);
@@ -152,12 +165,25 @@ public class StatServiceImpl implements StatService {
                 .le(WashOrder::getStartTime, LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()).atTime(LocalTime.MAX))
                 .list();
         var monthConsumptionAmount = monthOrders.stream().filter(o -> o.getAmount() != null).mapToInt(WashOrder::getAmount).sum();
-        var monthIncome = splitRecordService.lambdaQuery()
+        var monthRechargeAndCrossIncome = splitRecordService.lambdaQuery()
                 .eq(SplitRecord::getToStationId, stationId)
                 .in(SplitRecord::getType, SplitRecord.TYPE_RECHARGE, SplitRecord.TYPE_CROSS_INCOME)
                 .ge(SplitRecord::getCreateTime, LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).atTime(LocalTime.MIN))
                 .lt(SplitRecord::getCreateTime, LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).plusMonths(1).atTime(LocalTime.MIN))
-                .list().stream().mapToInt(SplitRecord::getAmount).sum();
+                .list().stream().filter(r -> r.getAmount() != null).mapToInt(SplitRecord::getAmount).sum();
+        var monthRefund = splitRecordService.lambdaQuery()
+                .eq(SplitRecord::getFromStationId, stationId)
+                .eq(SplitRecord::getType, SplitRecord.TYPE_REFUND)
+                .ge(SplitRecord::getCreateTime, LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).atTime(LocalTime.MIN))
+                .lt(SplitRecord::getCreateTime, LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).plusMonths(1).atTime(LocalTime.MIN))
+                .list().stream().filter(r -> r.getAmount() != null).mapToInt(SplitRecord::getAmount).sum();
+        var monthCrossExpend = splitRecordService.lambdaQuery()
+                .eq(SplitRecord::getFromStationId, stationId)
+                .eq(SplitRecord::getType, SplitRecord.TYPE_CROSS_EXPEND)
+                .ge(SplitRecord::getCreateTime, LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).atTime(LocalTime.MIN))
+                .lt(SplitRecord::getCreateTime, LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).plusMonths(1).atTime(LocalTime.MIN))
+                .list().stream().filter(r -> r.getAmount() != null).mapToInt(SplitRecord::getAmount).sum();
+        var monthIncome = monthRechargeAndCrossIncome - monthRefund - monthCrossExpend;
         var monthRegistrations = userService.countMonthRegister(LocalDate.now());
         var monthOrdersMap = washOrderService.countMonthOrders(LocalDate.now());
 
@@ -165,10 +191,19 @@ public class StatServiceImpl implements StatService {
         var allOrders = washOrderService.lambdaQuery()
                 .eq(WashOrder::getStationId, stationId)
                 .list();
-        var totalIncome = splitRecordService.lambdaQuery()
+        var totalRechargeAndCrossIncome = splitRecordService.lambdaQuery()
                 .eq(SplitRecord::getToStationId, stationId)
                 .in(SplitRecord::getType, SplitRecord.TYPE_RECHARGE, SplitRecord.TYPE_CROSS_INCOME)
-                .list().stream().mapToInt(SplitRecord::getAmount).sum();
+                .list().stream().filter(r -> r.getAmount() != null).mapToInt(SplitRecord::getAmount).sum();
+        var totalRefund = splitRecordService.lambdaQuery()
+                .eq(SplitRecord::getFromStationId, stationId)
+                .eq(SplitRecord::getType, SplitRecord.TYPE_REFUND)
+                .list().stream().filter(r -> r.getAmount() != null).mapToInt(SplitRecord::getAmount).sum();
+        var totalCrossExpend = splitRecordService.lambdaQuery()
+                .eq(SplitRecord::getFromStationId, stationId)
+                .eq(SplitRecord::getType, SplitRecord.TYPE_CROSS_EXPEND)
+                .list().stream().filter(r -> r.getAmount() != null).mapToInt(SplitRecord::getAmount).sum();
+        var totalIncome = totalRechargeAndCrossIncome - totalRefund - totalCrossExpend;
         var totalConsumptionAmount = allOrders.stream().filter(o -> o.getAmount() != null).mapToInt(WashOrder::getAmount).sum();
         var totalMembers = userService.lambdaQuery().eq(User::getStationId, stationId).count().intValue();
 

+ 4 - 1
car-wash-service/src/main/java/com/kym/service/impl/UserServiceImpl.java

@@ -352,12 +352,15 @@ public class UserServiceImpl extends MPJBaseServiceImpl<UserMapper, User> implem
      * @return
      */
     @Override
-    public Map<String, Integer> countMonthRegister(LocalDate statDay) {
+    public Map<String, Integer> countMonthRegister(LocalDate statDay, String... stationId) {
         var startTime = statDay.with(TemporalAdjusters.firstDayOfMonth()).atTime(LocalTime.MIN);
         var endTime = statDay.with(TemporalAdjusters.firstDayOfMonth()).plusMonths(1).atTime(LocalTime.MIN);
         LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
         wrapper.ge(User::getCreateTime, startTime);
         wrapper.lt(User::getCreateTime, endTime);
+        if (stationId.length > 0) {
+            wrapper.in(User::getStationId, Arrays.asList(stationId));
+        }
         return list(wrapper).stream().collect(Collectors.groupingBy(u -> u.getStationId() != null ? u.getStationId() : "未分配", Collectors.summingInt(o -> 1)));
     }