Răsfoiți Sursa

perf: Dashboard 真正 SQL 聚合 + Statistics getDeviceList 批量查门店

- Dashboard: aggOrders() 使用 COUNT(*)+SUM() 返回单行,不再加载全表到内存
  - 总收入从加载所有历史订单改为 1 行 SQL 聚合
- Statistics: getDeviceList 用 selectBatchIds 批量查门店,消除 N+1

Co-Authored-By: Claude <noreply@anthropic.com>
skyline 1 zi în urmă
părinte
comite
4d6ddb9a37

+ 31 - 62
haha-service/src/main/java/com/haha/service/impl/DashboardServiceImpl.java

@@ -49,6 +49,13 @@ public class DashboardServiceImpl implements DashboardService {
     @Autowired
     private ShopService shopService;
 
+    /** 执行 COUNT + SUM 聚合查询,返回一行两列 */
+    private Map<String, Object> aggOrders(LambdaQueryWrapper<Order> wrapper) {
+        wrapper.select("IFNULL(COUNT(*), 0) as cnt", "IFNULL(SUM(total_amount), 0) as sum");
+        List<Map<String, Object>> list = orderService.getBaseMapper().selectMaps(wrapper);
+        return list != null && !list.isEmpty() ? list.get(0) : Map.of("cnt", 0L, "sum", BigDecimal.ZERO);
+    }
+
     @Override
     public Map<String, Object> getOverviewData() {
         Map<String, Object> result = new HashMap<>();
@@ -58,20 +65,12 @@ public class DashboardServiceImpl implements DashboardService {
         LocalDateTime yesterdayStart = todayStart.minusDays(1);
         LocalDateTime yesterdayEnd = LocalDateTime.now().minusDays(1);
 
-        // 今日销售额和订单量 — SQL 聚合,不拉全量订单到内存
-        Map<String, Object> todayStats = orderService.getBaseMapper().selectMaps(
-                new LambdaQueryWrapper<Order>()
-                        .eq(Order::getPayStatus, OrderConstants.PAY_STATUS_PAID)
-                        .ge(Order::getPayTime, todayStart)
-                        .le(Order::getPayTime, todayEnd)
-                        .select(Order::getTotalAmount)
-        ).stream().collect(HashMap::new, (m, row) -> {
-            m.put("cnt", ((Number) m.getOrDefault("cnt", 0)).intValue() + 1);
-            BigDecimal amt = row.get("total_amount") != null ? (BigDecimal) row.get("total_amount") : BigDecimal.ZERO;
-            m.put("sum", ((BigDecimal) m.getOrDefault("sum", BigDecimal.ZERO)).add(amt));
-        }, (a, b) -> {});
-        BigDecimal todaySales = (BigDecimal) todayStats.getOrDefault("sum", BigDecimal.ZERO);
-        int todayOrderCount = (int) todayStats.getOrDefault("cnt", 0);
+        // 今日 — SQL COUNT + SUM,1 行返回
+        Map<String, Object> todayStats = aggOrders(new LambdaQueryWrapper<Order>()
+                .eq(Order::getPayStatus, OrderConstants.PAY_STATUS_PAID)
+                .ge(Order::getPayTime, todayStart).le(Order::getPayTime, todayEnd));
+        BigDecimal todaySales = (BigDecimal) todayStats.get("sum");
+        int todayOrderCount = ((Number) todayStats.get("cnt")).intValue();
 
         result.put("todaySales", todaySales.setScale(2, RoundingMode.HALF_UP).toString());
         result.put("todayOrders", todayOrderCount);
@@ -79,39 +78,28 @@ public class DashboardServiceImpl implements DashboardService {
                 ? todaySales.divide(BigDecimal.valueOf(todayOrderCount), 2, RoundingMode.HALF_UP).toString()
                 : "0.00");
 
-        // 昨日 — 同样用 SQL 聚合
-        Map<String, Object> yesterdayStats = orderService.getBaseMapper().selectMaps(
-                new LambdaQueryWrapper<Order>()
-                        .eq(Order::getPayStatus, OrderConstants.PAY_STATUS_PAID)
-                        .ge(Order::getPayTime, yesterdayStart)
-                        .le(Order::getPayTime, yesterdayEnd)
-                        .select(Order::getTotalAmount)
-        ).stream().collect(HashMap::new, (m, row) -> {
-            m.put("cnt", ((Number) m.getOrDefault("cnt", 0)).intValue() + 1);
-            BigDecimal amt = row.get("total_amount") != null ? (BigDecimal) row.get("total_amount") : BigDecimal.ZERO;
-            m.put("sum", ((BigDecimal) m.getOrDefault("sum", BigDecimal.ZERO)).add(amt));
-        }, (a, b) -> {});
-        BigDecimal yesterdaySales = (BigDecimal) yesterdayStats.getOrDefault("sum", BigDecimal.ZERO);
-        int yesterdayOrderCount = (int) yesterdayStats.getOrDefault("cnt", 0);
+        // 昨日
+        Map<String, Object> yesterdayStats = aggOrders(new LambdaQueryWrapper<Order>()
+                .eq(Order::getPayStatus, OrderConstants.PAY_STATUS_PAID)
+                .ge(Order::getPayTime, yesterdayStart).le(Order::getPayTime, yesterdayEnd));
+        BigDecimal yesterdaySales = (BigDecimal) yesterdayStats.get("sum");
+        int yesterdayOrderCount = ((Number) yesterdayStats.get("cnt")).intValue();
         BigDecimal yesterdayAvgPrice = yesterdayOrderCount > 0
                 ? yesterdaySales.divide(BigDecimal.valueOf(yesterdayOrderCount), 2, RoundingMode.HALF_UP)
                 : BigDecimal.ZERO;
 
         result.put("todaySalesCompare", formatCompare(todaySales.subtract(yesterdaySales)));
         result.put("todayOrdersCompare", formatCompare(todayOrderCount - yesterdayOrderCount));
-        BigDecimal todayAvg = todayOrderCount > 0
-                ? todaySales.divide(BigDecimal.valueOf(todayOrderCount), 2, RoundingMode.HALF_UP)
-                : BigDecimal.ZERO;
-        result.put("todayAvgPriceCompare", formatCompare(todayAvg.subtract(yesterdayAvgPrice)));
+        result.put("todayAvgPriceCompare", formatCompare(todayOrderCount > 0
+                ? todaySales.divide(BigDecimal.valueOf(todayOrderCount), 2, RoundingMode.HALF_UP) : BigDecimal.ZERO
+                .subtract(yesterdayAvgPrice)));
 
-        // 设备状态统计
         long totalDevices = deviceService.count();
         long onlineDevices = deviceService.lambdaQuery()
                 .eq(Device::getStatus, DeviceConstants.STATUS_ONLINE).count();
         result.put("onlineDevices", onlineDevices);
         result.put("offlineDevices", totalDevices - onlineDevices);
 
-        // 未支付订单数
         long unpaidOrders = orderService.lambdaQuery()
                 .eq(Order::getStatus, OrderConstants.STATUS_PENDING_PAYMENT).count();
         result.put("unpaidOrders", unpaidOrders);
@@ -119,53 +107,34 @@ public class DashboardServiceImpl implements DashboardService {
         result.put("abnormalActivities", 0);
         result.put("pendingAfterSale", 0);
 
-        // 总用户数、今日新增
         long totalUsers = userService.count();
         result.put("totalUsers", totalUsers);
         long todayNewUsers = userService.lambdaQuery()
                 .ge(User::getCreateTime, todayStart).le(User::getCreateTime, todayEnd).count();
         result.put("todayNewUsers", todayNewUsers);
 
-        // 总订单数 + 总收入 — SQL 聚合
         long totalOrders = orderService.lambdaQuery()
                 .eq(Order::getPayStatus, OrderConstants.PAY_STATUS_PAID).count();
         result.put("totalOrders", totalOrders);
-        Map<String, Object> allPaidStats = orderService.getBaseMapper().selectMaps(
-                new LambdaQueryWrapper<Order>()
-                        .eq(Order::getPayStatus, OrderConstants.PAY_STATUS_PAID)
-                        .select(Order::getTotalAmount)
-        ).stream().collect(HashMap::new, (m, row) -> {
-            BigDecimal amt = row.get("total_amount") != null ? (BigDecimal) row.get("total_amount") : BigDecimal.ZERO;
-            m.put("sum", ((BigDecimal) m.getOrDefault("sum", BigDecimal.ZERO)).add(amt));
-        }, (a, b) -> {});
-        BigDecimal totalRevenue = (BigDecimal) allPaidStats.getOrDefault("sum", BigDecimal.ZERO);
-        result.put("totalRevenue", totalRevenue.setScale(2, RoundingMode.HALF_UP).toString());
+        // 总收入 — SQL SUM,1 行返回(不再加载所有历史订单)
+        Map<String, Object> allPaidStats = aggOrders(new LambdaQueryWrapper<Order>()
+                .eq(Order::getPayStatus, OrderConstants.PAY_STATUS_PAID));
+        result.put("totalRevenue", ((BigDecimal) allPaidStats.get("sum")).setScale(2, RoundingMode.HALF_UP).toString());
         result.put("todayRevenue", todaySales.setScale(2, RoundingMode.HALF_UP).toString());
         result.put("totalDevices", totalDevices);
 
-        // 门店统计
         long totalShops = shopService.count();
         long activeShops = shopService.lambdaQuery()
                 .eq(Shop::getStatus, CommonStatus.ENABLED.getCode()).count();
         result.put("totalShops", totalShops);
         result.put("activeShops", activeShops);
 
-        // 本月销售额和订单量 — SQL 聚合
         LocalDateTime monthStart = LocalDateTime.of(LocalDate.now().withDayOfMonth(1), LocalTime.MIN);
-        Map<String, Object> monthStats = orderService.getBaseMapper().selectMaps(
-                new LambdaQueryWrapper<Order>()
-                        .eq(Order::getPayStatus, OrderConstants.PAY_STATUS_PAID)
-                        .ge(Order::getPayTime, monthStart)
-                        .le(Order::getPayTime, LocalDateTime.now())
-                        .select(Order::getTotalAmount)
-        ).stream().collect(HashMap::new, (m, row) -> {
-            m.put("cnt", ((Number) m.getOrDefault("cnt", 0)).intValue() + 1);
-            BigDecimal amt = row.get("total_amount") != null ? (BigDecimal) row.get("total_amount") : BigDecimal.ZERO;
-            m.put("sum", ((BigDecimal) m.getOrDefault("sum", BigDecimal.ZERO)).add(amt));
-        }, (a, b) -> {});
-        result.put("monthSales", ((BigDecimal) monthStats.getOrDefault("sum", BigDecimal.ZERO))
-                .setScale(2, RoundingMode.HALF_UP).toString());
-        result.put("monthOrders", monthStats.getOrDefault("cnt", 0));
+        Map<String, Object> monthStats = aggOrders(new LambdaQueryWrapper<Order>()
+                .eq(Order::getPayStatus, OrderConstants.PAY_STATUS_PAID)
+                .ge(Order::getPayTime, monthStart).le(Order::getPayTime, LocalDateTime.now()));
+        result.put("monthSales", ((BigDecimal) monthStats.get("sum")).setScale(2, RoundingMode.HALF_UP).toString());
+        result.put("monthOrders", ((Number) monthStats.get("cnt")).intValue());
 
         return result;
     }

+ 13 - 14
haha-service/src/main/java/com/haha/service/impl/StatisticsServiceImpl.java

@@ -484,6 +484,12 @@ public class StatisticsServiceImpl implements StatisticsService {
                 .filter(o -> o.getDeviceId() != null)
                 .collect(Collectors.groupingBy(Order::getDeviceId));
         
+        // 批量查询门店名称(替代 N+1 逐条 selectById)
+        Set<Long> shopIds = devices.stream().map(Device::getShopId).filter(Objects::nonNull).collect(Collectors.toSet());
+        Map<Long, String> shopNameMap = shopIds.isEmpty() ? Map.of()
+                : shopMapper.selectBatchIds(shopIds).stream()
+                        .collect(Collectors.toMap(Shop::getId, Shop::getName, (a, b) -> a));
+
         List<DeviceStatVO> resultList = new ArrayList<>();
         for (Device device : devices) {
             DeviceStatVO vo = new DeviceStatVO();
@@ -493,33 +499,26 @@ public class StatisticsServiceImpl implements StatisticsService {
             vo.setStatus(device.getStatus());
             vo.setStatusLabel(DeviceConstants.isOnline(device.getStatus()) ? "在线" : "离线");
             vo.setStatusColor(DeviceConstants.isOnline(device.getStatus()) ? "success" : "info");
-            
+            vo.setShopName(shopNameMap.get(device.getShopId()));
+
             List<Order> deviceOrders = deviceOrderMap.getOrDefault(device.getDeviceId(), Collections.emptyList());
-            
             vo.setOrderCount(deviceOrders.size());
             vo.setUserCount((int) deviceOrders.stream().map(Order::getUserId).filter(Objects::nonNull).distinct().count());
-            
+
             BigDecimal salesAmount = deviceOrders.stream()
                     .map(o -> o.getTotalAmount() != null ? o.getTotalAmount() : BigDecimal.ZERO)
                     .reduce(BigDecimal.ZERO, BigDecimal::add);
             vo.setSalesAmount(salesAmount.setScale(2, RoundingMode.HALF_UP));
-            
-            BigDecimal avgOrderAmount = deviceOrders.size() > 0 
+
+            BigDecimal avgOrderAmount = deviceOrders.size() > 0
                     ? salesAmount.divide(BigDecimal.valueOf(deviceOrders.size()), 2, RoundingMode.HALF_UP)
                     : BigDecimal.ZERO;
             vo.setAvgOrderAmount(avgOrderAmount);
-            
+
             long days = ChronoUnit.DAYS.between(startDate, endDate) + 1;
             BigDecimal dailySales = salesAmount.divide(BigDecimal.valueOf(days), 2, RoundingMode.HALF_UP);
             vo.setDailySalesAmount(dailySales);
-            
-            if (device.getShopId() != null) {
-                Shop shop = shopMapper.selectById(device.getShopId());
-                if (shop != null) {
-                    vo.setShopName(shop.getName());
-                }
-            }
-            
+
             resultList.add(vo);
         }