Explorar el Código

订单监测连接数据库

Gogs hace 1 mes
padre
commit
2a72e915b6

BIN
data/storage/入库数据/2025采购入库单_1119.xlsx


BIN
data/storage/入库数据/产品资料.xlsx


BIN
data/storage/半成品组装/半成品匹配成品编码明细.xlsx


BIN
data/storage/半成品组装/组装明细 - 1107.xlsx


BIN
data/storage/销售数据/2025年订单数据.xlsx


+ 155 - 0
dtm-system/src/main/java/com/dtm/order/domain/OrderAnalyticsRecord.java

@@ -0,0 +1,155 @@
+package com.dtm.order.domain;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+public class OrderAnalyticsRecord {
+    private String purchaseId;
+    private String orderId;
+    private String productId;
+    private String productTitle;
+    private String productProperties;
+    private String productMerchantCode;
+    private double orderPrice;
+    private int orderQuantity;
+    private String orderStatus;
+    private double orderPayable;
+    private double orderActualPayment;
+    private String orderRefundStatus;
+    private double orderRefundAmount;
+    private LocalDateTime createdTime;
+    private LocalDateTime paidTime;
+    private String purchasePaymentId;
+
+    public String getPurchaseId() {
+        return purchaseId;
+    }
+
+    public void setPurchaseId(String purchaseId) {
+        this.purchaseId = purchaseId;
+    }
+
+    public String getOrderId() {
+        return orderId;
+    }
+
+    public void setOrderId(String orderId) {
+        this.orderId = orderId;
+    }
+
+    public String getProductId() {
+        return productId;
+    }
+
+    public void setProductId(String productId) {
+        this.productId = productId;
+    }
+
+    public String getProductTitle() {
+        return productTitle;
+    }
+
+    public void setProductTitle(String productTitle) {
+        this.productTitle = productTitle;
+    }
+
+    public String getProductProperties() {
+        return productProperties;
+    }
+
+    public void setProductProperties(String productProperties) {
+        this.productProperties = productProperties;
+    }
+
+    public String getProductMerchantCode() {
+        return productMerchantCode;
+    }
+
+    public void setProductMerchantCode(String productMerchantCode) {
+        this.productMerchantCode = productMerchantCode;
+    }
+
+    public double getOrderPrice() {
+        return orderPrice;
+    }
+
+    public void setOrderPrice(double orderPrice) {
+        this.orderPrice = orderPrice;
+    }
+
+    public int getOrderQuantity() {
+        return orderQuantity;
+    }
+
+    public void setOrderQuantity(int orderQuantity) {
+        this.orderQuantity = orderQuantity;
+    }
+
+    public String getOrderStatus() {
+        return orderStatus;
+    }
+
+    public void setOrderStatus(String orderStatus) {
+        this.orderStatus = orderStatus;
+    }
+
+    public double getOrderPayable() {
+        return orderPayable;
+    }
+
+    public void setOrderPayable(double orderPayable) {
+        this.orderPayable = orderPayable;
+    }
+
+    public double getOrderActualPayment() {
+        return orderActualPayment;
+    }
+
+    public void setOrderActualPayment(double orderActualPayment) {
+        this.orderActualPayment = orderActualPayment;
+    }
+
+    public String getOrderRefundStatus() {
+        return orderRefundStatus;
+    }
+
+    public void setOrderRefundStatus(String orderRefundStatus) {
+        this.orderRefundStatus = orderRefundStatus;
+    }
+
+    public double getOrderRefundAmount() {
+        return orderRefundAmount;
+    }
+
+    public void setOrderRefundAmount(double orderRefundAmount) {
+        this.orderRefundAmount = orderRefundAmount;
+    }
+
+    public LocalDateTime getCreatedTime() {
+        return createdTime;
+    }
+
+    public void setCreatedTime(LocalDateTime createdTime) {
+        this.createdTime = createdTime;
+    }
+
+    public LocalDateTime getPaidTime() {
+        return paidTime;
+    }
+
+    public void setPaidTime(LocalDateTime paidTime) {
+        this.paidTime = paidTime;
+    }
+
+    public String getPurchasePaymentId() {
+        return purchasePaymentId;
+    }
+
+    public void setPurchasePaymentId(String purchasePaymentId) {
+        this.purchasePaymentId = purchasePaymentId;
+    }
+
+    public LocalDate getCreatedDate() {
+        return createdTime == null ? null : createdTime.toLocalDate();
+    }
+}

+ 40 - 0
dtm-system/src/main/java/com/dtm/order/domain/OrderFunnelSummary.java

@@ -0,0 +1,40 @@
+package com.dtm.order.domain;
+
+public class OrderFunnelSummary {
+    private Long paidWithin5Mins;
+    private Long paidBetween5And30Mins;
+    private Long paidAfter30Mins;
+    private Long unpaidOrders;
+
+    public Long getPaidWithin5Mins() {
+        return paidWithin5Mins;
+    }
+
+    public void setPaidWithin5Mins(Long paidWithin5Mins) {
+        this.paidWithin5Mins = paidWithin5Mins;
+    }
+
+    public Long getPaidBetween5And30Mins() {
+        return paidBetween5And30Mins;
+    }
+
+    public void setPaidBetween5And30Mins(Long paidBetween5And30Mins) {
+        this.paidBetween5And30Mins = paidBetween5And30Mins;
+    }
+
+    public Long getPaidAfter30Mins() {
+        return paidAfter30Mins;
+    }
+
+    public void setPaidAfter30Mins(Long paidAfter30Mins) {
+        this.paidAfter30Mins = paidAfter30Mins;
+    }
+
+    public Long getUnpaidOrders() {
+        return unpaidOrders;
+    }
+
+    public void setUnpaidOrders(Long unpaidOrders) {
+        this.unpaidOrders = unpaidOrders;
+    }
+}

+ 22 - 0
dtm-system/src/main/java/com/dtm/order/domain/OrderLeakageSummary.java

@@ -0,0 +1,22 @@
+package com.dtm.order.domain;
+
+public class OrderLeakageSummary {
+    private Double totalRefundAmount;
+    private Double totalSuccessAmount;
+
+    public Double getTotalRefundAmount() {
+        return totalRefundAmount;
+    }
+
+    public void setTotalRefundAmount(Double totalRefundAmount) {
+        this.totalRefundAmount = totalRefundAmount;
+    }
+
+    public Double getTotalSuccessAmount() {
+        return totalSuccessAmount;
+    }
+
+    public void setTotalSuccessAmount(Double totalSuccessAmount) {
+        this.totalSuccessAmount = totalSuccessAmount;
+    }
+}

+ 38 - 16
dtm-system/src/main/java/com/dtm/order/dto/CoPurchaseDTO.java

@@ -3,12 +3,14 @@ package com.dtm.order.dto;
 public class CoPurchaseDTO {
 
     private String productA;
-    private String productAId; // <-- 鏂板锛佸晢鍝丄鐨勨€滆韩浠借瘉鍙封€?
+    private String productAId;
     private String productB;
-    private String productBId; // <-- 鏂板锛佸晢鍝丅鐨勨€滆韩浠借瘉鍙封€?
+    private String productBId;
     private Long coPurchaseCount;
 
-    // 鏋勯€犲嚱鏁颁篃瑕佸崌绾э紒
+    public CoPurchaseDTO() {
+    }
+
     public CoPurchaseDTO(String productA, String productAId, String productB, String productBId, Long coPurchaseCount) {
         this.productA = productA;
         this.productAId = productAId;
@@ -17,23 +19,43 @@ public class CoPurchaseDTO {
         this.coPurchaseCount = coPurchaseCount;
     }
 
-    // --- 涓嬮潰鏄墍鏈夊瓧娈电殑Getter鍜孲etter ---
-    // hina甯偍鎶婃柊澧炵殑涔熼兘琛ヤ笂鍟︼紒
+    public String getProductA() {
+        return productA;
+    }
 
-    public String getProductA() { return productA; }
-    public void setProductA(String productA) { this.productA = productA; }
+    public void setProductA(String productA) {
+        this.productA = productA;
+    }
 
-    public String getProductAId() { return productAId; }
-    public void setProductAId(String productAId) { this.productAId = productAId; }
+    public String getProductAId() {
+        return productAId;
+    }
 
-    public String getProductB() { return productB; }
-    public void setProductB(String productB) { this.productB = productB; }
+    public void setProductAId(String productAId) {
+        this.productAId = productAId;
+    }
 
-    public String getProductBId() { return productBId; }
-    public void setProductBId(String productBId) { this.productBId = productBId; }
+    public String getProductB() {
+        return productB;
+    }
 
-    public Long getCoPurchaseCount() { return coPurchaseCount; }
-    public void setCoPurchaseCount(Long coPurchaseCount) { this.coPurchaseCount = coPurchaseCount; }
-}
+    public void setProductB(String productB) {
+        this.productB = productB;
+    }
+
+    public String getProductBId() {
+        return productBId;
+    }
+
+    public void setProductBId(String productBId) {
+        this.productBId = productBId;
+    }
 
+    public Long getCoPurchaseCount() {
+        return coPurchaseCount;
+    }
 
+    public void setCoPurchaseCount(Long coPurchaseCount) {
+        this.coPurchaseCount = coPurchaseCount;
+    }
+}

+ 3 - 0
dtm-system/src/main/java/com/dtm/order/dto/ProductDTO.java

@@ -5,6 +5,9 @@ public class ProductDTO {
     private String sku;
     private Double totalSales;
 
+    public ProductDTO() {
+    }
+
     // 鏋勯€犲嚱鏁?
      public ProductDTO(String sku, String name, Double totalSales) {
         this.sku = sku;

+ 37 - 0
dtm-system/src/main/java/com/dtm/order/mapper/OrderAnalyticsMapper.java

@@ -0,0 +1,37 @@
+package com.dtm.order.mapper;
+
+import com.dtm.order.domain.OrderFunnelSummary;
+import com.dtm.order.domain.OrderAnalyticsRecord;
+import com.dtm.order.domain.OrderLeakageSummary;
+import com.dtm.order.dto.CoPurchaseDTO;
+import com.dtm.order.dto.ProductDTO;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface OrderAnalyticsMapper {
+    Long countOrders();
+
+    String selectMaxOrderDate();
+
+    List<OrderAnalyticsRecord> selectOrders(
+            @Param("startDate") String startDate,
+            @Param("endDate") String endDate
+    );
+
+    Double selectTotalGmv(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    List<ProductDTO> selectTop5Products(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    Double selectTop5SalesTotal(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    List<Double> selectPaymentAmounts(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    OrderLeakageSummary selectLeakageSummary(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    Double selectAveragePaymentSeconds(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    OrderFunnelSummary selectPaymentFunnel(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    List<CoPurchaseDTO> selectCoPurchaseRules(@Param("skuKeyword") String skuKeyword);
+}

+ 183 - 44
dtm-system/src/main/java/com/dtm/order/service/AnalysisService.java

@@ -1,7 +1,11 @@
 package com.dtm.order.service;
 
+import com.dtm.order.domain.OrderAnalyticsRecord;
+import com.dtm.order.domain.OrderFunnelSummary;
+import com.dtm.order.domain.OrderLeakageSummary;
 import com.dtm.order.dto.CoPurchaseDTO;
 import com.dtm.order.dto.ProductDTO;
+import com.dtm.order.mapper.OrderAnalyticsMapper;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
@@ -27,32 +31,45 @@ public class AnalysisService {
 
 
     private final OrderDataStore orderDataStore;
+    private final OrderAnalyticsMapper orderAnalyticsMapper;
 
     @Autowired
-    public AnalysisService(OrderDataStore orderDataStore) {
+    public AnalysisService(OrderDataStore orderDataStore, OrderAnalyticsMapper orderAnalyticsMapper) {
         this.orderDataStore = orderDataStore;
+        this.orderAnalyticsMapper = orderAnalyticsMapper;
     }
 
     public Double calculateTotalGMV(String startDate, String endDate) {
-        LocalDateRange range = parseRange(startDate, endDate);
-        double sum = 0.0;
-        for (OrderRecord order : orderDataStore.getOrders()) {
-            if (range.contains(order.getCreatedDate())) {
-                sum += order.getOrderActualPayment();
+        if (hasDatabaseOrders()) {
+            try {
+                Double total = orderAnalyticsMapper.selectTotalGmv(startDate, endDate);
+                return total == null ? 0.0 : total;
+            } catch (Exception ignored) {
             }
         }
+
+        double sum = 0.0;
+        for (OrderAnalyticsRecord order : getAnalyticsOrders(startDate, endDate)) {
+            sum += order.getOrderActualPayment();
+        }
         return sum;
     }
 
     public List<ProductDTO> getTop5Products(String startDate, String endDate) {
-        LocalDateRange range = parseRange(startDate, endDate);
+        if (hasDatabaseOrders()) {
+            try {
+                List<ProductDTO> products = orderAnalyticsMapper.selectTop5Products(startDate, endDate);
+                if (products != null) {
+                    return products;
+                }
+            } catch (Exception ignored) {
+            }
+        }
+
         Map<String, Double> salesBySku = new HashMap<>();
         Map<String, String> nameBySku = new HashMap<>();
 
-        for (OrderRecord order : orderDataStore.getOrders()) {
-            if (!range.contains(order.getCreatedDate())) {
-                continue;
-            }
+        for (OrderAnalyticsRecord order : getAnalyticsOrders(startDate, endDate)) {
             String sku = order.getProductMerchantCode();
             if (sku == null || sku.isEmpty()) {
                 sku = order.getProductId();
@@ -79,19 +96,23 @@ public class AnalysisService {
     }
 
     public Map<String, Double> calculateP80AndRBig(String startDate, String endDate) {
-        LocalDateRange range = parseRange(startDate, endDate);
-        List<Double> payments = new ArrayList<>();
-
-        for (OrderRecord order : orderDataStore.getOrders()) {
-            if (!range.contains(order.getCreatedDate())) {
-                continue;
-            }
-            double payment = order.getOrderActualPayment();
-            if (payment <= 0 && order.getOrderPayable() > 0) {
-                payment = order.getOrderPayable();
+        List<Double> payments = null;
+        if (hasDatabaseOrders()) {
+            try {
+                payments = orderAnalyticsMapper.selectPaymentAmounts(startDate, endDate);
+            } catch (Exception ignored) {
             }
-            if (payment > 0) {
-                payments.add(payment);
+        }
+        if (payments == null) {
+            payments = new ArrayList<>();
+            for (OrderAnalyticsRecord order : getAnalyticsOrders(startDate, endDate)) {
+                double payment = order.getOrderActualPayment();
+                if (payment <= 0 && order.getOrderPayable() > 0) {
+                    payment = order.getOrderPayable();
+                }
+                if (payment > 0) {
+                    payments.add(payment);
+                }
             }
         }
 
@@ -124,14 +145,27 @@ public class AnalysisService {
     }
 
     public Map<String, Double> calculateLeakageRate(String startDate, String endDate) {
-        LocalDateRange range = parseRange(startDate, endDate);
+        if (hasDatabaseOrders()) {
+            try {
+                OrderLeakageSummary summary = orderAnalyticsMapper.selectLeakageSummary(startDate, endDate);
+                if (summary != null) {
+                    double totalRefundAmount = valueOrZero(summary.getTotalRefundAmount());
+                    double totalSuccessAmount = valueOrZero(summary.getTotalSuccessAmount());
+                    double leakageRate = (totalSuccessAmount > 0) ? (totalRefundAmount / totalSuccessAmount) * 100 : 0.0;
+                    Map<String, Double> result = new HashMap<>();
+                    result.put("totalRefundAmount", totalRefundAmount);
+                    result.put("totalSuccessAmount", totalSuccessAmount);
+                    result.put("leakageRatePercent", leakageRate);
+                    return result;
+                }
+            } catch (Exception ignored) {
+            }
+        }
+
         double totalRefundAmount = 0.0;
         double totalSuccessAmount = 0.0;
 
-        for (OrderRecord order : orderDataStore.getOrders()) {
-            if (!range.contains(order.getCreatedDate())) {
-                continue;
-            }
+        for (OrderAnalyticsRecord order : getAnalyticsOrders(startDate, endDate)) {
             double payment = order.getOrderActualPayment();
             if (payment <= 0 && order.getOrderPayable() > 0) {
                 payment = order.getOrderPayable();
@@ -162,7 +196,8 @@ public class AnalysisService {
         long refundAmountCount = 0;
         long refundStatusCount = 0;
 
-        for (OrderRecord order : orderDataStore.getOrders()) {
+        List<OrderAnalyticsRecord> orders = getAnalyticsOrders(null, null);
+        for (OrderAnalyticsRecord order : orders) {
             String refundStatus = normalize(order.getOrderRefundStatus());
             if (!refundStatus.isEmpty()) {
                 statusCounts.merge(refundStatus, 1L, Long::sum);
@@ -185,7 +220,7 @@ public class AnalysisService {
         }
 
         Map<String, Object> result = new HashMap<>();
-        result.put("orders", orderDataStore.getOrders().size());
+        result.put("orders", orders.size());
         result.put("refundAmountSum", refundAmountSum);
         result.put("refundAmountCount", refundAmountCount);
         result.put("refundStatusCount", refundStatusCount);
@@ -198,11 +233,21 @@ public class AnalysisService {
     }
 
     public List<CoPurchaseDTO> findCoPurchaseRules(String skuKeyword) {
+        if (hasDatabaseOrders()) {
+            try {
+                List<CoPurchaseDTO> rules = orderAnalyticsMapper.selectCoPurchaseRules(normalize(skuKeyword));
+                if (rules != null) {
+                    return rules;
+                }
+            } catch (Exception ignored) {
+            }
+        }
+
         String normalizedKeyword = normalize(skuKeyword).toLowerCase();
         Map<String, Set<String>> purchaseProducts = new HashMap<>();
         Map<String, String> productTitle = new HashMap<>();
 
-        for (OrderRecord order : orderDataStore.getOrders()) {
+        for (OrderAnalyticsRecord order : getAnalyticsOrders(null, null)) {
             String purchaseKey = order.getPurchaseId();
             if (purchaseKey == null || purchaseKey.isEmpty()) {
                 purchaseKey = order.getPurchasePaymentId();
@@ -273,13 +318,17 @@ public class AnalysisService {
     }
 
     public Double calculateAveragePaymentTime(String startDate, String endDate) {
-        LocalDateRange range = parseRange(startDate, endDate);
+        if (hasDatabaseOrders()) {
+            try {
+                Double average = orderAnalyticsMapper.selectAveragePaymentSeconds(startDate, endDate);
+                return average == null ? 0.0 : average;
+            } catch (Exception ignored) {
+            }
+        }
+
         double totalSeconds = 0.0;
         long count = 0;
-        for (OrderRecord order : orderDataStore.getOrders()) {
-            if (!range.contains(order.getCreatedDate())) {
-                continue;
-            }
+        for (OrderAnalyticsRecord order : getAnalyticsOrders(startDate, endDate)) {
             if (order.getCreatedTime() == null || order.getPaidTime() == null) {
                 continue;
             }
@@ -293,16 +342,27 @@ public class AnalysisService {
     }
 
     public Map<String, Long> analyzePaymentDecisionFunnel(String startDate, String endDate) {
-        LocalDateRange range = parseRange(startDate, endDate);
+        if (hasDatabaseOrders()) {
+            try {
+                OrderFunnelSummary summary = orderAnalyticsMapper.selectPaymentFunnel(startDate, endDate);
+                if (summary != null) {
+                    Map<String, Long> funnelData = new HashMap<>();
+                    funnelData.put("paidWithin5Mins", valueOrZero(summary.getPaidWithin5Mins()));
+                    funnelData.put("paidBetween5And30Mins", valueOrZero(summary.getPaidBetween5And30Mins()));
+                    funnelData.put("paidAfter30Mins", valueOrZero(summary.getPaidAfter30Mins()));
+                    funnelData.put("unpaidOrders", valueOrZero(summary.getUnpaidOrders()));
+                    return funnelData;
+                }
+            } catch (Exception ignored) {
+            }
+        }
+
         long paidWithin5Mins = 0;
         long paidBetween5And30Mins = 0;
         long paidAfter30Mins = 0;
         long unpaidOrders = 0;
 
-        for (OrderRecord order : orderDataStore.getOrders()) {
-            if (!range.contains(order.getCreatedDate())) {
-                continue;
-            }
+        for (OrderAnalyticsRecord order : getAnalyticsOrders(startDate, endDate)) {
             if (order.getCreatedTime() == null) {
                 continue;
             }
@@ -346,6 +406,18 @@ public class AnalysisService {
     }
 
     public Double calculateTop5Percentage(String startDate, String endDate) {
+        if (hasDatabaseOrders()) {
+            try {
+                Double top5Sum = orderAnalyticsMapper.selectTop5SalesTotal(startDate, endDate);
+                Double totalGmv = orderAnalyticsMapper.selectTotalGmv(startDate, endDate);
+                if (totalGmv == null || totalGmv <= 0) {
+                    return 0.0;
+                }
+                return (valueOrZero(top5Sum) / totalGmv) * 100;
+            } catch (Exception ignored) {
+            }
+        }
+
         List<ProductDTO> top5List = getTop5Products(startDate, endDate);
         if (top5List == null || top5List.isEmpty()) {
             return 0.0;
@@ -361,13 +433,72 @@ public class AnalysisService {
     }
 
     public String getMaxOrderDate() {
-        LocalDate maxDate = orderDataStore.getMaxOrderDate();
-        if (maxDate != null) {
-            return maxDate.toString();
+        String maxDate = getDatabaseMaxOrderDate();
+        if (maxDate != null && !maxDate.isEmpty()) {
+            return maxDate;
+        }
+        LocalDate localMaxDate = orderDataStore.getMaxOrderDate();
+        if (localMaxDate != null) {
+            return localMaxDate.toString();
         }
         return LocalDate.now().toString();
     }
 
+    private List<OrderAnalyticsRecord> getAnalyticsOrders(String startDate, String endDate) {
+        if (hasDatabaseOrders()) {
+            List<OrderAnalyticsRecord> databaseOrders = orderAnalyticsMapper.selectOrders(startDate, endDate);
+            if (databaseOrders != null) {
+                return databaseOrders;
+            }
+        }
+        return convertOrders(orderDataStore.getOrders(), startDate, endDate);
+    }
+
+    private String getDatabaseMaxOrderDate() {
+        if (!hasDatabaseOrders()) {
+            return null;
+        }
+        return orderAnalyticsMapper.selectMaxOrderDate();
+    }
+
+    private boolean hasDatabaseOrders() {
+        try {
+            Long count = orderAnalyticsMapper.countOrders();
+            return count != null && count > 0;
+        } catch (Exception ignored) {
+            return false;
+        }
+    }
+
+    private List<OrderAnalyticsRecord> convertOrders(List<OrderRecord> orders, String startDate, String endDate) {
+        LocalDateRange range = parseRange(startDate, endDate);
+        List<OrderAnalyticsRecord> result = new ArrayList<>();
+        for (OrderRecord order : orders) {
+            if (!range.contains(order.getCreatedDate())) {
+                continue;
+            }
+            OrderAnalyticsRecord item = new OrderAnalyticsRecord();
+            item.setPurchaseId(order.getPurchaseId());
+            item.setOrderId(order.getOrderId());
+            item.setProductId(order.getProductId());
+            item.setProductTitle(order.getProductTitle());
+            item.setProductProperties(order.getProductProperties());
+            item.setProductMerchantCode(order.getProductMerchantCode());
+            item.setOrderPrice(order.getOrderPrice());
+            item.setOrderQuantity(order.getOrderQuantity());
+            item.setOrderStatus(order.getOrderStatus());
+            item.setOrderPayable(order.getOrderPayable());
+            item.setOrderActualPayment(order.getOrderActualPayment());
+            item.setOrderRefundStatus(order.getOrderRefundStatus());
+            item.setOrderRefundAmount(order.getOrderRefundAmount());
+            item.setCreatedTime(order.getCreatedTime());
+            item.setPaidTime(order.getPaidTime());
+            item.setPurchasePaymentId(order.getPurchasePaymentId());
+            result.add(item);
+        }
+        return result;
+    }
+
     private LocalDateRange parseRange(String startDate, String endDate) {
         LocalDate start = parseDate(startDate);
         LocalDate end = parseDate(endDate);
@@ -406,6 +537,14 @@ public class AnalysisService {
         return value == null ? "" : value.trim();
     }
 
+    private double valueOrZero(Double value) {
+        return value == null ? 0.0 : value;
+    }
+
+    private long valueOrZero(Long value) {
+        return value == null ? 0L : value;
+    }
+
     private static class LocalDateRange {
         private final LocalDate start;
         private final LocalDate end;

+ 36 - 0
dtm-system/src/main/java/com/dtm/order/shop/mapper/ShopValueMapper.java

@@ -0,0 +1,36 @@
+package com.dtm.order.shop.mapper;
+
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+import java.util.Map;
+
+public interface ShopValueMapper {
+    Long countRows();
+
+    String selectMaxStatDate();
+
+    List<Map<String, Object>> selectPlatformOrderCount(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    List<Map<String, Object>> selectPlatformSalesAmount(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    List<Map<String, Object>> selectUnitContribution(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    List<Map<String, Object>> selectChannelContribution(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    List<Map<String, Object>> selectPlatformContribution(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    List<Map<String, Object>> selectTopProducts(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    Double selectTotalSales(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    List<Map<String, Object>> selectCrossSellingProducts(
+            @Param("startDate") String startDate,
+            @Param("endDate") String endDate,
+            @Param("skuKeyword") String skuKeyword
+    );
+
+    List<Map<String, Object>> selectDepartmentEfficiency(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    List<Map<String, Object>> selectChannelDiversity(@Param("startDate") String startDate, @Param("endDate") String endDate);
+}

+ 157 - 1
dtm-system/src/main/java/com/dtm/order/shop/service/ShopAnalysisService.java

@@ -1,5 +1,6 @@
 package com.dtm.order.shop.service;
 
+import com.dtm.order.shop.mapper.ShopValueMapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.MultipartFile;
@@ -18,10 +19,12 @@ import java.util.Set;
 public class ShopAnalysisService {
 
     private final ShopSalesDataStore dataStore;
+    private final ShopValueMapper shopValueMapper;
 
     @Autowired
-    public ShopAnalysisService(ShopSalesDataStore dataStore) {
+    public ShopAnalysisService(ShopSalesDataStore dataStore, ShopValueMapper shopValueMapper) {
         this.dataStore = dataStore;
+        this.shopValueMapper = shopValueMapper;
     }
 
     public int importAllShopSalesData() {
@@ -37,11 +40,21 @@ public class ShopAnalysisService {
     }
 
     public String getMaxSalesDate() {
+        String databaseMaxDate = getDatabaseMaxSalesDate();
+        if (databaseMaxDate != null && !databaseMaxDate.isEmpty()) {
+            return databaseMaxDate;
+        }
         LocalDate maxDate = dataStore.getMaxSalesDate();
         return maxDate == null ? LocalDate.now().toString() : maxDate.toString();
     }
 
     public Map<String, Double> getChannelSalesContribution(String startDate, String endDate) {
+        if (hasDatabaseRows()) {
+            Map<String, Double> databaseData = toDoubleMap(shopValueMapper.selectPlatformOrderCount(startDate, endDate));
+            if (!databaseData.isEmpty()) {
+                return databaseData;
+            }
+        }
         Map<String, Double> contributionData = new HashMap<>();
         for (ShopSalesRecord record : filterRecords(startDate, endDate, null)) {
             contributionData.merge(record.getPlatformName(), 1.0, Double::sum);
@@ -50,6 +63,12 @@ public class ShopAnalysisService {
     }
 
     public Map<String, Double> getChannelRoiValue(String startDate, String endDate) {
+        if (hasDatabaseRows()) {
+            Map<String, Double> databaseData = toDoubleMap(shopValueMapper.selectPlatformSalesAmount(startDate, endDate));
+            if (!databaseData.isEmpty()) {
+                return databaseData;
+            }
+        }
         Map<String, Double> resultData = new HashMap<>();
         for (ShopSalesRecord record : filterRecords(startDate, endDate, null)) {
             resultData.merge(record.getPlatformName(), record.getSalesAmount(), Double::sum);
@@ -58,6 +77,12 @@ public class ShopAnalysisService {
     }
 
     public List<Map<String, Object>> getUnitContribution(String startDate, String endDate) {
+        if (hasDatabaseRows()) {
+            List<Map<String, Object>> databaseData = shopValueMapper.selectUnitContribution(startDate, endDate);
+            if (databaseData != null && !databaseData.isEmpty()) {
+                return databaseData;
+            }
+        }
         Map<String, Double> volumeByUnit = new HashMap<>();
         Map<String, Double> amountByUnit = new HashMap<>();
         for (ShopSalesRecord record : filterRecords(startDate, endDate, null)) {
@@ -68,6 +93,12 @@ public class ShopAnalysisService {
     }
 
     public List<Map<String, Object>> getChannelTotalContribution(String startDate, String endDate) {
+        if (hasDatabaseRows()) {
+            List<Map<String, Object>> databaseData = shopValueMapper.selectChannelContribution(startDate, endDate);
+            if (databaseData != null && !databaseData.isEmpty()) {
+                return databaseData;
+            }
+        }
         Map<String, Double> volumeByChannel = new HashMap<>();
         Map<String, Double> amountByChannel = new HashMap<>();
         for (ShopSalesRecord record : filterRecords(startDate, endDate, null)) {
@@ -78,6 +109,12 @@ public class ShopAnalysisService {
     }
 
     public List<Map<String, Object>> getPlatformTotalContribution(String startDate, String endDate) {
+        if (hasDatabaseRows()) {
+            List<Map<String, Object>> databaseData = shopValueMapper.selectPlatformContribution(startDate, endDate);
+            if (databaseData != null && !databaseData.isEmpty()) {
+                return databaseData;
+            }
+        }
         Map<String, Double> volumeByPlatform = new HashMap<>();
         Map<String, Double> amountByPlatform = new HashMap<>();
         for (ShopSalesRecord record : filterRecords(startDate, endDate, null)) {
@@ -88,6 +125,22 @@ public class ShopAnalysisService {
     }
 
     public Map<String, Object> getTopProductContribution(String startDate, String endDate) {
+        if (hasDatabaseRows()) {
+            List<Map<String, Object>> topProducts = shopValueMapper.selectTopProducts(startDate, endDate);
+            Double totalSales = shopValueMapper.selectTotalSales(startDate, endDate);
+            if (topProducts != null && !topProducts.isEmpty()) {
+                double top5SalesSum = topProducts.stream()
+                        .mapToDouble(row -> toDouble(getMapValue(row, "salesAmount")))
+                        .sum();
+                double total = totalSales == null ? 0.0 : totalSales;
+                Map<String, Object> finalResult = new HashMap<>();
+                finalResult.put("top5Products", topProducts);
+                finalResult.put("top5TotalSales", top5SalesSum);
+                finalResult.put("totalSales", total);
+                finalResult.put("contributionRatio", total > 0 ? top5SalesSum / total : 0.0);
+                return finalResult;
+            }
+        }
         double totalSales = 0.0;
         Map<String, Double> productSales = new HashMap<>();
         for (ShopSalesRecord record : filterRecords(startDate, endDate, null)) {
@@ -120,6 +173,12 @@ public class ShopAnalysisService {
     }
 
     public List<Map<String, Object>> getCrossSellingProducts(String startDate, String endDate, String skuKeyword) {
+        if (hasDatabaseRows()) {
+            List<Map<String, Object>> databaseData = shopValueMapper.selectCrossSellingProducts(startDate, endDate, normalize(skuKeyword));
+            if (databaseData != null && !databaseData.isEmpty()) {
+                return databaseData;
+            }
+        }
         Map<String, Set<String>> platformByProduct = new HashMap<>();
         for (ShopSalesRecord record : filterRecords(startDate, endDate, skuKeyword)) {
             platformByProduct
@@ -142,6 +201,12 @@ public class ShopAnalysisService {
     }
 
     public Map<String, Double> getDepartmentOperationalEfficiency(String startDate, String endDate) {
+        if (hasDatabaseRows()) {
+            Map<String, Double> databaseData = toDoubleMap(shopValueMapper.selectDepartmentEfficiency(startDate, endDate));
+            if (!databaseData.isEmpty()) {
+                return databaseData;
+            }
+        }
         Map<String, Double> sumByUnit = new HashMap<>();
         Map<String, Long> countByUnit = new HashMap<>();
         for (ShopSalesRecord record : filterRecords(startDate, endDate, null)) {
@@ -159,6 +224,12 @@ public class ShopAnalysisService {
     }
 
     public Map<String, Long> getChannelProductDiversity(String startDate, String endDate) {
+        if (hasDatabaseRows()) {
+            Map<String, Long> databaseData = toLongMap(shopValueMapper.selectChannelDiversity(startDate, endDate));
+            if (!databaseData.isEmpty()) {
+                return databaseData;
+            }
+        }
         Map<String, Set<String>> productsByChannel = new HashMap<>();
         for (ShopSalesRecord record : filterRecords(startDate, endDate, null)) {
             productsByChannel
@@ -241,6 +312,91 @@ public class ShopAnalysisService {
         return value == null ? "" : value.trim();
     }
 
+    private boolean hasDatabaseRows() {
+        try {
+            Long count = shopValueMapper.countRows();
+            return count != null && count > 0;
+        } catch (Exception ignored) {
+            return false;
+        }
+    }
+
+    private String getDatabaseMaxSalesDate() {
+        if (!hasDatabaseRows()) {
+            return null;
+        }
+        return shopValueMapper.selectMaxStatDate();
+    }
+
+    private Map<String, Double> toDoubleMap(List<Map<String, Object>> rows) {
+        Map<String, Double> result = new HashMap<>();
+        if (rows == null) {
+            return result;
+        }
+        for (Map<String, Object> row : rows) {
+            String name = toStringValue(getMapValue(row, "name"));
+            if (!name.isEmpty()) {
+                result.put(name, toDouble(getMapValue(row, "value")));
+            }
+        }
+        return result;
+    }
+
+    private Map<String, Long> toLongMap(List<Map<String, Object>> rows) {
+        Map<String, Long> result = new HashMap<>();
+        if (rows == null) {
+            return result;
+        }
+        for (Map<String, Object> row : rows) {
+            String name = toStringValue(getMapValue(row, "name"));
+            if (!name.isEmpty()) {
+                result.put(name, Math.round(toDouble(getMapValue(row, "value"))));
+            }
+        }
+        return result;
+    }
+
+    private String toStringValue(Object value) {
+        return value == null ? "" : String.valueOf(value).trim();
+    }
+
+    private Object getMapValue(Map<String, Object> row, String key) {
+        if (row == null || key == null) {
+            return null;
+        }
+        if (row.containsKey(key)) {
+            return row.get(key);
+        }
+        String lowerKey = key.toLowerCase();
+        String upperKey = key.toUpperCase();
+        if (row.containsKey(lowerKey)) {
+            return row.get(lowerKey);
+        }
+        if (row.containsKey(upperKey)) {
+            return row.get(upperKey);
+        }
+        for (Map.Entry<String, Object> entry : row.entrySet()) {
+            if (key.equalsIgnoreCase(entry.getKey())) {
+                return entry.getValue();
+            }
+        }
+        return null;
+    }
+
+    private double toDouble(Object value) {
+        if (value instanceof Number) {
+            return ((Number) value).doubleValue();
+        }
+        if (value == null) {
+            return 0.0;
+        }
+        try {
+            return Double.parseDouble(String.valueOf(value));
+        } catch (NumberFormatException e) {
+            return 0.0;
+        }
+    }
+
     private static class LocalDateRange {
         private final LocalDate start;
         private final LocalDate end;

+ 251 - 0
dtm-system/src/main/resources/mapper/order/OrderAnalyticsMapper.xml

@@ -0,0 +1,251 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.dtm.order.mapper.OrderAnalyticsMapper">
+
+    <resultMap id="OrderAnalyticsRecordResult" type="com.dtm.order.domain.OrderAnalyticsRecord">
+        <result property="purchaseId" column="purchase_id"/>
+        <result property="orderId" column="order_id"/>
+        <result property="productId" column="product_id"/>
+        <result property="productTitle" column="product_title"/>
+        <result property="productProperties" column="product_properties"/>
+        <result property="productMerchantCode" column="product_merchant_code"/>
+        <result property="orderPrice" column="order_price"/>
+        <result property="orderQuantity" column="order_quantity"/>
+        <result property="orderStatus" column="order_status"/>
+        <result property="orderPayable" column="order_payable"/>
+        <result property="orderActualPayment" column="order_actual_payment"/>
+        <result property="orderRefundStatus" column="order_refund_status"/>
+        <result property="orderRefundAmount" column="order_refund_amount"/>
+        <result property="createdTime" column="created_time"/>
+        <result property="paidTime" column="paid_time"/>
+        <result property="purchasePaymentId" column="purchase_payment_id"/>
+    </resultMap>
+
+    <resultMap id="ProductResult" type="com.dtm.order.dto.ProductDTO">
+        <result property="sku" column="sku"/>
+        <result property="name" column="name"/>
+        <result property="totalSales" column="total_sales"/>
+    </resultMap>
+
+    <resultMap id="CoPurchaseResult" type="com.dtm.order.dto.CoPurchaseDTO">
+        <result property="productA" column="product_a"/>
+        <result property="productAId" column="product_a_id"/>
+        <result property="productB" column="product_b"/>
+        <result property="productBId" column="product_b_id"/>
+        <result property="coPurchaseCount" column="co_purchase_count"/>
+    </resultMap>
+
+    <resultMap id="OrderLeakageSummaryResult" type="com.dtm.order.domain.OrderLeakageSummary">
+        <result property="totalRefundAmount" column="total_refund_amount"/>
+        <result property="totalSuccessAmount" column="total_success_amount"/>
+    </resultMap>
+
+    <resultMap id="OrderFunnelSummaryResult" type="com.dtm.order.domain.OrderFunnelSummary">
+        <result property="paidWithin5Mins" column="paid_within_5_mins"/>
+        <result property="paidBetween5And30Mins" column="paid_between_5_and_30_mins"/>
+        <result property="paidAfter30Mins" column="paid_after_30_mins"/>
+        <result property="unpaidOrders" column="unpaid_orders"/>
+    </resultMap>
+
+    <sql id="dateRangeConditions">
+        <if test="startDate != null and startDate != ''">
+            AND create_time &gt;= STR_TO_DATE(CONCAT(#{startDate}, ' 00:00:00'), '%Y-%m-%d %H:%i:%s')
+        </if>
+        <if test="endDate != null and endDate != ''">
+            AND create_time &lt; DATE_ADD(
+                STR_TO_DATE(CONCAT(#{endDate}, ' 00:00:00'), '%Y-%m-%d %H:%i:%s'),
+                INTERVAL 1 DAY
+            )
+        </if>
+    </sql>
+
+    <sql id="selectOrderAnalyticsColumns">
+        SELECT
+            order_id AS purchase_id,
+            order_id AS order_id,
+            sku AS product_id,
+            title AS product_title,
+            attrs AS product_properties,
+            sku AS product_merchant_code,
+            IFNULL(price, 0) AS order_price,
+            IFNULL(quantity, 0) AS order_quantity,
+            order_status AS order_status,
+            IFNULL(pay_amount, 0) AS order_payable,
+            IFNULL(paid_amount, 0) AS order_actual_payment,
+            refund_status AS order_refund_status,
+            CASE
+                WHEN refund_amount REGEXP '^-?[0-9]+(\\.[0-9]+)?$' THEN CAST(refund_amount AS DECIMAL(12,2))
+                ELSE 0
+            END AS order_refund_amount,
+            create_time AS created_time,
+            pay_time AS paid_time,
+            pay_number AS purchase_payment_id
+        FROM dtm_order_main
+    </sql>
+
+    <select id="countOrders" resultType="java.lang.Long">
+        SELECT COUNT(1) FROM dtm_order_main
+    </select>
+
+    <select id="selectMaxOrderDate" resultType="java.lang.String">
+        SELECT DATE_FORMAT(MAX(create_time), '%Y-%m-%d')
+        FROM dtm_order_main
+        WHERE create_time IS NOT NULL
+    </select>
+
+    <select id="selectOrders" resultMap="OrderAnalyticsRecordResult">
+        <include refid="selectOrderAnalyticsColumns"/>
+        <where>
+            <include refid="dateRangeConditions"/>
+        </where>
+    </select>
+
+    <select id="selectTotalGmv" resultType="java.lang.Double">
+        SELECT IFNULL(SUM(IFNULL(paid_amount, 0)), 0)
+        FROM dtm_order_main
+        <where>
+            <include refid="dateRangeConditions"/>
+        </where>
+    </select>
+
+    <select id="selectTop5Products" resultMap="ProductResult">
+        SELECT
+            sku AS sku,
+            COALESCE(MAX(NULLIF(title, '')), sku) AS name,
+            IFNULL(SUM(IFNULL(paid_amount, 0)), 0) AS total_sales
+        FROM dtm_order_main
+        <where>
+            sku IS NOT NULL
+            AND sku != ''
+            <include refid="dateRangeConditions"/>
+        </where>
+        GROUP BY sku
+        ORDER BY total_sales DESC
+        LIMIT 5
+    </select>
+
+    <select id="selectTop5SalesTotal" resultType="java.lang.Double">
+        SELECT IFNULL(SUM(top_product.total_sales), 0)
+        FROM (
+            SELECT IFNULL(SUM(IFNULL(paid_amount, 0)), 0) AS total_sales
+            FROM dtm_order_main
+            <where>
+                sku IS NOT NULL
+                AND sku != ''
+                <include refid="dateRangeConditions"/>
+            </where>
+            GROUP BY sku
+            ORDER BY total_sales DESC
+            LIMIT 5
+        ) top_product
+    </select>
+
+    <select id="selectPaymentAmounts" resultType="java.lang.Double">
+        SELECT payment_amount
+        FROM (
+            SELECT
+                CASE
+                    WHEN IFNULL(paid_amount, 0) &lt;= 0 AND IFNULL(pay_amount, 0) &gt; 0 THEN IFNULL(pay_amount, 0)
+                    ELSE IFNULL(paid_amount, 0)
+                END AS payment_amount
+            FROM dtm_order_main
+            <where>
+                <include refid="dateRangeConditions"/>
+            </where>
+        ) payment_data
+        WHERE payment_amount &gt; 0
+    </select>
+
+    <select id="selectLeakageSummary" resultMap="OrderLeakageSummaryResult">
+        SELECT
+            IFNULL(SUM(refund_amount_value), 0) AS total_refund_amount,
+            IFNULL(SUM(CASE WHEN payment_amount &gt; 0 THEN payment_amount ELSE 0 END), 0) AS total_success_amount
+        FROM (
+            SELECT
+                CASE
+                    WHEN IFNULL(paid_amount, 0) &lt;= 0 AND IFNULL(pay_amount, 0) &gt; 0 THEN IFNULL(pay_amount, 0)
+                    ELSE IFNULL(paid_amount, 0)
+                END AS payment_amount,
+                CASE
+                    WHEN refund_amount REGEXP '^-?[0-9]+(\\.[0-9]+)?$' THEN CAST(refund_amount AS DECIMAL(12,2))
+                    ELSE 0
+                END AS refund_amount_value
+            FROM dtm_order_main
+            <where>
+                <include refid="dateRangeConditions"/>
+            </where>
+        ) leakage_data
+    </select>
+
+    <select id="selectAveragePaymentSeconds" resultType="java.lang.Double">
+        SELECT IFNULL(AVG(TIMESTAMPDIFF(SECOND, create_time, pay_time)), 0)
+        FROM dtm_order_main
+        <where>
+            create_time IS NOT NULL
+            AND pay_time IS NOT NULL
+            AND pay_time &gt;= create_time
+            <include refid="dateRangeConditions"/>
+        </where>
+    </select>
+
+    <select id="selectPaymentFunnel" resultMap="OrderFunnelSummaryResult">
+        SELECT
+            IFNULL(SUM(CASE WHEN pay_time IS NOT NULL AND pay_time &gt;= create_time AND TIMESTAMPDIFF(SECOND, create_time, pay_time) &lt;= 300 THEN 1 ELSE 0 END), 0) AS paid_within_5_mins,
+            IFNULL(SUM(CASE WHEN pay_time IS NOT NULL AND pay_time &gt;= create_time AND TIMESTAMPDIFF(SECOND, create_time, pay_time) &gt; 300 AND TIMESTAMPDIFF(SECOND, create_time, pay_time) &lt;= 1800 THEN 1 ELSE 0 END), 0) AS paid_between_5_and_30_mins,
+            IFNULL(SUM(CASE WHEN pay_time IS NOT NULL AND pay_time &gt;= create_time AND TIMESTAMPDIFF(SECOND, create_time, pay_time) &gt; 1800 THEN 1 ELSE 0 END), 0) AS paid_after_30_mins,
+            IFNULL(SUM(CASE WHEN pay_time IS NULL THEN 1 ELSE 0 END), 0) AS unpaid_orders
+        FROM dtm_order_main
+        <where>
+            create_time IS NOT NULL
+            <include refid="dateRangeConditions"/>
+        </where>
+    </select>
+
+    <select id="selectCoPurchaseRules" resultMap="CoPurchaseResult">
+        SELECT
+            COALESCE(MAX(a.title), a.sku) AS product_a,
+            a.sku AS product_a_id,
+            COALESCE(MAX(b.title), b.sku) AS product_b,
+            b.sku AS product_b_id,
+            COUNT(1) AS co_purchase_count
+        FROM (
+            SELECT
+                pay_number AS purchase_key,
+                sku,
+                MAX(NULLIF(title, '')) AS title
+            FROM dtm_order_main
+            WHERE pay_number IS NOT NULL
+              AND pay_number != ''
+              AND sku IS NOT NULL
+              AND sku != ''
+            GROUP BY pay_number, sku
+        ) a
+        INNER JOIN (
+            SELECT
+                pay_number AS purchase_key,
+                sku,
+                MAX(NULLIF(title, '')) AS title
+            FROM dtm_order_main
+            WHERE pay_number IS NOT NULL
+              AND pay_number != ''
+              AND sku IS NOT NULL
+              AND sku != ''
+            GROUP BY pay_number, sku
+        ) b
+            ON a.purchase_key = b.purchase_key
+           AND a.sku &lt; b.sku
+        <where>
+            <if test="skuKeyword != null and skuKeyword != ''">
+                AND (
+                    a.sku LIKE CONCAT('%', #{skuKeyword}, '%')
+                    OR b.sku LIKE CONCAT('%', #{skuKeyword}, '%')
+                )
+            </if>
+        </where>
+        GROUP BY a.sku, b.sku
+        ORDER BY co_purchase_count DESC
+        LIMIT 50
+    </select>
+</mapper>

+ 164 - 0
dtm-system/src/main/resources/mapper/order/shop/ShopValueMapper.xml

@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.dtm.order.shop.mapper.ShopValueMapper">
+
+    <sql id="statDateExpression">
+        COALESCE(stat_date, STR_TO_DATE(CONCAT(stat_year, '-', stat_month, '-', stat_day), '%Y-%c-%e'))
+    </sql>
+
+    <sql id="dateRangeConditions">
+        <if test="startDate != null and startDate != ''">
+            AND <include refid="statDateExpression"/> &gt;= STR_TO_DATE(#{startDate}, '%Y-%m-%d')
+        </if>
+        <if test="endDate != null and endDate != ''">
+            AND <include refid="statDateExpression"/> &lt; DATE_ADD(STR_TO_DATE(#{endDate}, '%Y-%m-%d'), INTERVAL 1 DAY)
+        </if>
+    </sql>
+
+    <select id="countRows" resultType="java.lang.Long">
+        SELECT COUNT(1) FROM dtm_shop_value_main
+    </select>
+
+    <select id="selectMaxStatDate" resultType="java.lang.String">
+        SELECT DATE_FORMAT(MAX(<include refid="statDateExpression"/>), '%Y-%m-%d')
+        FROM dtm_shop_value_main
+        WHERE <include refid="statDateExpression"/> IS NOT NULL
+    </select>
+
+    <select id="selectPlatformOrderCount" resultType="java.util.HashMap">
+        SELECT platform_name AS name, COUNT(1) AS value
+        FROM dtm_shop_value_main
+        <where>
+            platform_name IS NOT NULL
+            AND platform_name != ''
+            <include refid="dateRangeConditions"/>
+        </where>
+        GROUP BY platform_name
+        ORDER BY value DESC
+    </select>
+
+    <select id="selectPlatformSalesAmount" resultType="java.util.HashMap">
+        SELECT platform_name AS name, IFNULL(SUM(IFNULL(sales_amount, 0)), 0) AS value
+        FROM dtm_shop_value_main
+        <where>
+            platform_name IS NOT NULL
+            AND platform_name != ''
+            <include refid="dateRangeConditions"/>
+        </where>
+        GROUP BY platform_name
+        ORDER BY value DESC
+    </select>
+
+    <select id="selectUnitContribution" resultType="java.util.HashMap">
+        SELECT
+            business_unit_name AS name,
+            IFNULL(SUM(IFNULL(quantity, 0)), 0) AS totalVolume,
+            IFNULL(SUM(IFNULL(sales_amount, 0)), 0) AS totalAmount
+        FROM dtm_shop_value_main
+        <where>
+            business_unit_name IS NOT NULL
+            AND business_unit_name != ''
+            <include refid="dateRangeConditions"/>
+        </where>
+        GROUP BY business_unit_name
+        ORDER BY totalAmount DESC
+    </select>
+
+    <select id="selectChannelContribution" resultType="java.util.HashMap">
+        SELECT
+            channel_name AS name,
+            IFNULL(SUM(IFNULL(quantity, 0)), 0) AS totalVolume,
+            IFNULL(SUM(IFNULL(sales_amount, 0)), 0) AS totalAmount
+        FROM dtm_shop_value_main
+        <where>
+            channel_name IS NOT NULL
+            AND channel_name != ''
+            <include refid="dateRangeConditions"/>
+        </where>
+        GROUP BY channel_name
+        ORDER BY totalAmount DESC
+    </select>
+
+    <select id="selectPlatformContribution" resultType="java.util.HashMap">
+        SELECT
+            platform_name AS name,
+            IFNULL(SUM(IFNULL(quantity, 0)), 0) AS totalVolume,
+            IFNULL(SUM(IFNULL(sales_amount, 0)), 0) AS totalAmount
+        FROM dtm_shop_value_main
+        <where>
+            platform_name IS NOT NULL
+            AND platform_name != ''
+            <include refid="dateRangeConditions"/>
+        </where>
+        GROUP BY platform_name
+        ORDER BY totalAmount DESC
+    </select>
+
+    <select id="selectTopProducts" resultType="java.util.HashMap">
+        SELECT product_code AS productCode, IFNULL(SUM(IFNULL(sales_amount, 0)), 0) AS salesAmount
+        FROM dtm_shop_value_main
+        <where>
+            product_code IS NOT NULL
+            AND product_code != ''
+            <include refid="dateRangeConditions"/>
+        </where>
+        GROUP BY product_code
+        ORDER BY salesAmount DESC
+        LIMIT 5
+    </select>
+
+    <select id="selectTotalSales" resultType="java.lang.Double">
+        SELECT IFNULL(SUM(IFNULL(sales_amount, 0)), 0)
+        FROM dtm_shop_value_main
+        <where>
+            <include refid="dateRangeConditions"/>
+        </where>
+    </select>
+
+    <select id="selectCrossSellingProducts" resultType="java.util.HashMap">
+        SELECT product_code AS productCode, COUNT(DISTINCT platform_name) AS platformCount
+        FROM dtm_shop_value_main
+        <where>
+            product_code IS NOT NULL
+            AND product_code != ''
+            AND platform_name IS NOT NULL
+            AND platform_name != ''
+            <include refid="dateRangeConditions"/>
+            <if test="skuKeyword != null and skuKeyword != ''">
+                AND product_code LIKE CONCAT('%', #{skuKeyword}, '%')
+            </if>
+        </where>
+        GROUP BY product_code
+        HAVING COUNT(DISTINCT platform_name) &gt;= 2
+        ORDER BY platformCount DESC
+        LIMIT 1000
+    </select>
+
+    <select id="selectDepartmentEfficiency" resultType="java.util.HashMap">
+        SELECT business_unit_name AS name, IFNULL(AVG(IFNULL(sales_amount, 0)), 0) AS value
+        FROM dtm_shop_value_main
+        <where>
+            business_unit_name IS NOT NULL
+            AND business_unit_name != ''
+            <include refid="dateRangeConditions"/>
+        </where>
+        GROUP BY business_unit_name
+        ORDER BY value DESC
+    </select>
+
+    <select id="selectChannelDiversity" resultType="java.util.HashMap">
+        SELECT channel_name AS name, COUNT(DISTINCT product_code) AS value
+        FROM dtm_shop_value_main
+        <where>
+            channel_name IS NOT NULL
+            AND channel_name != ''
+            AND product_code IS NOT NULL
+            AND product_code != ''
+            <include refid="dateRangeConditions"/>
+        </where>
+        GROUP BY channel_name
+        ORDER BY value DESC
+    </select>
+</mapper>