From f90a37fa86568c94b5ed9be14942e7e234db016e Mon Sep 17 00:00:00 2001
From: liyong <18434998025@163.com>
Date: 星期二, 27 一月 2026 17:48:24 +0800
Subject: [PATCH] refactor(stock): 删除getStockQuantity方法

---
 src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java |  535 +++++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 461 insertions(+), 74 deletions(-)

diff --git a/src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java b/src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
index 85f9719..cb5d600 100644
--- a/src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
+++ b/src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -1,45 +1,40 @@
 package com.ruoyi.home.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.approve.mapper.ApproveProcessMapper;
 import com.ruoyi.approve.pojo.ApproveProcess;
+import com.ruoyi.basic.mapper.CustomerMapper;
+import com.ruoyi.basic.mapper.ProductMapper;
 import com.ruoyi.basic.mapper.ProductModelMapper;
+import com.ruoyi.basic.mapper.SupplierManageMapper;
+import com.ruoyi.basic.pojo.Customer;
+import com.ruoyi.basic.pojo.Product;
 import com.ruoyi.basic.pojo.ProductModel;
+import com.ruoyi.basic.pojo.SupplierManage;
 import com.ruoyi.collaborativeApproval.mapper.NoticeMapper;
 import com.ruoyi.collaborativeApproval.pojo.Notice;
 import com.ruoyi.common.utils.SecurityUtils;
-import com.ruoyi.device.mapper.DeviceMaintenanceMapper;
 import com.ruoyi.device.mapper.DeviceRepairMapper;
-import com.ruoyi.device.pojo.DeviceMaintenance;
 import com.ruoyi.device.pojo.DeviceRepair;
 import com.ruoyi.dto.MapDto;
 import com.ruoyi.framework.security.LoginUser;
 import com.ruoyi.home.dto.*;
 import com.ruoyi.home.service.HomeService;
-import com.ruoyi.lavorissue.mapper.LavorIssueMapper;
-import com.ruoyi.lavorissue.pojo.LaborIssue;
-import com.ruoyi.procurementrecord.mapper.CustomStorageMapper;
 import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper;
-import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
-import com.ruoyi.procurementrecord.pojo.CustomStorage;
-import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut;
 import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage;
 import com.ruoyi.procurementrecord.utils.StockUtils;
 import com.ruoyi.production.dto.ProductOrderDto;
 import com.ruoyi.production.dto.ProductWorkOrderDto;
-import com.ruoyi.production.dto.ProductionProductMainDto;
 import com.ruoyi.production.mapper.ProductOrderMapper;
-import com.ruoyi.production.mapper.ProductProcessMapper;
 import com.ruoyi.production.mapper.ProductWorkOrderMapper;
-import com.ruoyi.production.mapper.ProductionProductMainMapper;
-import com.ruoyi.production.pojo.ProductOrder;
-import com.ruoyi.production.pojo.ProductProcess;
 import com.ruoyi.production.pojo.ProductWorkOrder;
 import com.ruoyi.project.system.domain.SysDept;
+import com.ruoyi.project.system.domain.SysUserDept;
 import com.ruoyi.project.system.mapper.SysDeptMapper;
+import com.ruoyi.project.system.mapper.SysUserDeptMapper;
 import com.ruoyi.purchase.mapper.PaymentRegistrationMapper;
 import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
 import com.ruoyi.purchase.pojo.PaymentRegistration;
@@ -49,9 +44,14 @@
 import com.ruoyi.sales.mapper.ReceiptPaymentMapper;
 import com.ruoyi.sales.mapper.SalesLedgerMapper;
 import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
+import com.ruoyi.project.system.domain.SysUser;
+import com.ruoyi.project.system.mapper.SysUserMapper;
 import com.ruoyi.sales.pojo.ReceiptPayment;
 import com.ruoyi.sales.pojo.SalesLedger;
 import com.ruoyi.sales.pojo.SalesLedgerProduct;
+import com.ruoyi.staff.mapper.StaffOnJobMapper;
+import com.ruoyi.staff.pojo.StaffOnJob;
+import com.ruoyi.stock.mapper.StockInventoryMapper;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -60,13 +60,11 @@
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.text.ParseException;
-import java.text.SimpleDateFormat;
 import java.time.*;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.time.temporal.TemporalAdjusters;
 import java.util.*;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
 /**
@@ -87,13 +85,7 @@
     private SalesLedgerProductMapper salesLedgerProductMapper;
 
     @Autowired
-    private ProcurementRecordOutMapper procurementRecordOutMapper;
-
-    @Autowired
     private ProcurementRecordMapper procurementRecordStorageMapper;
-
-    @Autowired
-    private CustomStorageMapper customStorageMapper;
 
     @Autowired
     private QualityInspectMapper qualityStatisticsMapper;
@@ -108,9 +100,6 @@
     private PaymentRegistrationMapper paymentRegistrationMapper;
 
     @Autowired
-    private LavorIssueMapper lavorIssueMapper;
-
-    @Autowired
     private SysDeptMapper sysDeptMapper;
 
     @Autowired
@@ -118,13 +107,25 @@
     @Autowired
     private ProductOrderMapper productOrderMapper;
     @Autowired
-    private ProductProcessMapper productProcessMapper;
-    @Autowired
     private ProductWorkOrderMapper productWorkOrderMapper;
     @Autowired
     private ProductModelMapper productModelMapper;
     @Autowired
+    private ProductMapper productMapper;
+    @Autowired
     private StockUtils stockUtils;
+    @Autowired
+    private StaffOnJobMapper staffOnJobMapper;
+    @Autowired
+    private CustomerMapper customerMapper;
+    @Autowired
+    private SupplierManageMapper supplierManageMapper;
+    @Autowired
+    private SysUserMapper sysUserMapper;
+    @Autowired
+    private SysUserDeptMapper sysUserDeptMapper;
+    private StockInventoryMapper stockInventoryMapper;
+
     @Override
     public HomeBusinessDto business() {
         // 鏋勫缓缁撴灉
@@ -136,7 +137,7 @@
         salesLedgerLambdaQueryWrapper.ge(SalesLedger::getEntryDate, currentMonth.atDay(1).atStartOfDay())  // 澶т簬绛変簬鏈湀绗竴澶�
                 .lt(SalesLedger::getEntryDate, currentMonth.plusMonths(1).atDay(1).atStartOfDay()); // 灏忎簬涓嬫湀绗竴澶�
         List<SalesLedger> salesLedgers = salesLedgerMapper.selectList(salesLedgerLambdaQueryWrapper);
-        if(!CollectionUtils.isEmpty(salesLedgers)){
+        if (!CollectionUtils.isEmpty(salesLedgers)) {
             // 鍚堣鍚堝悓閲戦
             BigDecimal contractAmount = salesLedgers.stream().map(SalesLedger::getContractAmount)
                     .filter(Objects::nonNull)
@@ -158,7 +159,7 @@
                 .lt(PurchaseLedger::getEntryDate, currentMonth.plusMonths(1).atDay(1).atStartOfDay()); // 灏忎簬涓嬫湀绗竴澶�
         // 鎵ц鏌ヨ骞惰绠楁�诲拰
         List<PurchaseLedger> purchaseLedgers = purchaseLedgerMapper.selectList(queryWrapper);
-        if(!CollectionUtils.isEmpty(purchaseLedgers)){
+        if (!CollectionUtils.isEmpty(purchaseLedgers)) {
             LambdaQueryWrapper<SalesLedgerProduct> salesLedgerProductMapperLambdaQueryWrapperCopy = new LambdaQueryWrapper<SalesLedgerProduct>();
             salesLedgerProductMapperLambdaQueryWrapperCopy.eq(SalesLedgerProduct::getType, 2)
                     .in(SalesLedgerProduct::getSalesLedgerId, purchaseLedgers.stream().map(PurchaseLedger::getId).collect(Collectors.toList()));
@@ -194,17 +195,9 @@
 //                .filter(Objects::nonNull)
 //                .reduce(BigDecimal.ZERO, BigDecimal::add);
 //        BigDecimal stock = stockAmount.add(customStockAmount).subtract(outboundAmount);
-        IPage<ProductModel> productModelIPage = productModelMapper.listPageProductModel(new Page<>(1, -1), new ProductModel());
-        if(!CollectionUtils.isEmpty(productModelIPage.getRecords())){
-            //鑾峰彇瑙勬牸id
-            List<Long> modelIds = productModelIPage.getRecords().stream().map(ProductModel::getId).collect(Collectors.toList());
-            BigDecimal stockQuantityTotal = modelIds.stream()
-                    .map(stockUtils::getStockQuantity)
-                    .map(map -> map.get("stockQuantity"))
-                    .filter(Objects::nonNull)
-                    .reduce(BigDecimal.ZERO, BigDecimal::add);
-            homeBusinessDto.setInventoryNum(stockQuantityTotal.setScale(2, RoundingMode.HALF_UP).toString());
-        }
+        BigDecimal stockQuantityTotal = stockInventoryMapper.selectTotal();
+        homeBusinessDto.setInventoryNum(stockQuantityTotal.setScale(2, RoundingMode.HALF_UP).toString());
+
 
         // 鑾峰彇褰撳ぉ鍏ュ簱鏁伴噺
         LambdaQueryWrapper<ProcurementRecordStorage> procurementRecordStorageLambdaQueryWrapper = new LambdaQueryWrapper<>();
@@ -254,9 +247,9 @@
         BigDecimal subtract = weekContractAmount.subtract(lastYearWeekContractAmount);
         String weekYny = "";
         // 鍛ㄥ悓姣�
-        if(subtract.compareTo(BigDecimal.ZERO) == 0 || lastYearWeekContractAmount.compareTo(BigDecimal.ZERO) == 0){
+        if (subtract.compareTo(BigDecimal.ZERO) == 0 || lastYearWeekContractAmount.compareTo(BigDecimal.ZERO) == 0) {
             weekYny = "0.00";
-        }else{
+        } else {
             weekYny = String.format("%.2f", subtract.divide(lastYearWeekContractAmount, 2, RoundingMode.HALF_UP).multiply(new BigDecimal("100")));
 
         }
@@ -279,9 +272,9 @@
         BigDecimal subtract1 = todayContractAmount.subtract(lastYearYesterdayContractAmount); // 淇敼锛氫娇鐢� todayContractAmount 鑰屼笉鏄� yesterdayContractAmount
         // 鏃ョ幆姣�
         String chain = "";
-        if(subtract1.compareTo(BigDecimal.ZERO) == 0 || lastYearYesterdayContractAmount.compareTo(BigDecimal.ZERO) == 0){
+        if (subtract1.compareTo(BigDecimal.ZERO) == 0 || lastYearYesterdayContractAmount.compareTo(BigDecimal.ZERO) == 0) {
             chain = "0.00";
-        }else{
+        } else {
             chain = String.format("%.2f", subtract1.divide(lastYearYesterdayContractAmount, 2, RoundingMode.HALF_UP).multiply(new BigDecimal("100")));
         }
 
@@ -295,14 +288,14 @@
         Map<String, BigDecimal> collect = salesLedgers.stream().collect(Collectors.groupingBy(SalesLedger::getCustomerName, Collectors.reducing(BigDecimal.ZERO,
                 SalesLedger::getContractAmount, BigDecimal::add)));
         List<MapDto> mapDtos = new ArrayList<>();
-        collect.forEach((k,v)->{
+        collect.forEach((k, v) -> {
             MapDto mapDto = new MapDto();
             mapDto.setName(k);
             // 淇敼锛氬皢閲戦鍊间繚鐣欎袱浣嶅皬鏁�
             mapDto.setValue(v.setScale(2, RoundingMode.HALF_UP).toString());
-            if(contractAmount.compareTo(new BigDecimal(0)) == 0){
+            if (contractAmount.compareTo(new BigDecimal(0)) == 0) {
                 mapDto.setRate("0");
-            }else{
+            } else {
                 mapDto.setRate(String.format("%.2f", v.divide(contractAmount, 2, RoundingMode.HALF_UP).multiply(new BigDecimal("100"))));
             }
             mapDtos.add(mapDto);
@@ -348,7 +341,7 @@
                     .filter(inspect -> inspect.getInspectType().equals(1))
                     .map(QualityInspect::getQuantity)
                     .reduce(BigDecimal.ZERO, BigDecimal::add);
-            processNum= processNum.add(reduce1);
+            processNum = processNum.add(reduce1);
             BigDecimal reduce2 = monthInspects.stream()
                     .filter(inspect -> inspect.getInspectType().equals(2))
                     .map(QualityInspect::getQuantity)
@@ -400,34 +393,33 @@
         LambdaQueryWrapper<ApproveProcess> approveProcessLambdaQueryWrapper = new LambdaQueryWrapper<>();
         approveProcessLambdaQueryWrapper.eq(ApproveProcess::getApproveDelete, 0)
                 .eq(ApproveProcess::getApproveUserCurrentId, loginUser.getUserId())
-                .ne(ApproveProcess::getApproveStatus, 2)
-                .eq(ApproveProcess::getTenantId, loginUser.getTenantId());
+                .ne(ApproveProcess::getApproveStatus, 2);
+//                .eq(ApproveProcess::getTenantId, loginUser.getTenantId());
         List<ApproveProcess> approveProcesses = approveProcessMapper.selectList(approveProcessLambdaQueryWrapper);
-        if(CollectionUtils.isEmpty(approveProcesses)){
+        if (CollectionUtils.isEmpty(approveProcesses)) {
             approveProcesses = new ArrayList<>();
         }
-        // 鏌ヨ鏈鐢ㄥ姵淇濊褰�
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
-
-        LaborIssue laborIssue1 = new LaborIssue();
-        laborIssue1.setAdoptedDate(new Date());
-        laborIssue1.setIssueDate(sdf.parse(sdf.format(new Date())));
-        List<LaborIssue> laborIssues = lavorIssueMapper.list(laborIssue1);
-        if(!CollectionUtils.isEmpty(laborIssues)){
-            for (LaborIssue laborIssue : laborIssues) {
-                ApproveProcess approveProcess = new ApproveProcess();
-                approveProcess.setApproveId(laborIssue.getOrderNo());
-                approveProcess.setApproveDeptName(sysDeptMapper.selectDeptById(loginUser.getTenantId()).getDeptName());
-                approveProcess.setApproveTime(laborIssue.getIssueDate());
-                approveProcess.setApproveReason(laborIssue.getDictTypeName() + "-" + laborIssue.getDictName() + "瓒呮椂鏈鍙�");
-                approveProcesses.add(approveProcess);
-            }
-        }
+//        // 鏌ヨ鏈鐢ㄥ姵淇濊褰�
+//        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+//
+//        LaborIssue laborIssue1 = new LaborIssue();
+//        laborIssue1.setAdoptedDate(new Date());
+//        laborIssue1.setIssueDate(sdf.parse(sdf.format(new Date())));
+//        List<LaborIssue> laborIssues = lavorIssueMapper.list(laborIssue1);  //staff_join_leave_record琛ㄨ鍒犻櫎
+//        if(!CollectionUtils.isEmpty(laborIssues)){
+//            for (LaborIssue laborIssue : laborIssues) {
+//                ApproveProcess approveProcess = new ApproveProcess();
+//                approveProcess.setApproveId(laborIssue.getOrderNo());
+//                approveProcess.setApproveDeptName(sysDeptMapper.selectDeptById(loginUser.getTenantId()).getDeptName());
+//                approveProcess.setApproveTime(laborIssue.getIssueDate());
+//                approveProcess.setApproveReason(laborIssue.getDictTypeName() + "-" + laborIssue.getDictName() + "瓒呮椂鏈鍙�");
+//                approveProcesses.add(approveProcess);
+//            }
+//        }
         return approveProcesses;
     }
 
     /**
-     *
      * @param type 1-鍛� 2-鏈� 3-瀛e害
      * @return
      */
@@ -436,7 +428,7 @@
         LocalDate today = LocalDate.now();
         LocalDate startDate = null;
         LocalDate endDate = null;
-        switch (type){
+        switch (type) {
             case 1:
                 // 鑾峰彇鏈懆鍛ㄤ竴
                 startDate = today.with(DayOfWeek.MONDAY);
@@ -494,6 +486,7 @@
 
         return statisticsReceivablePayableDto;
     }
+
     public static <T> BigDecimal sumAmount(List<T> list, java.util.function.Function<T, BigDecimal> amountExtractor) {
         return list.stream()
                 // 鎻愬彇閲戦鏃讹紝灏唍ull鏇挎崲涓築igDecimal.ZERO
@@ -501,6 +494,7 @@
                 // 绱姞锛屽垵濮嬪�间负0锛岄伩鍏嶇┖娴侀棶棰�
                 .reduce(BigDecimal.ZERO, BigDecimal::add);
     }
+
     @Autowired
     private DeviceRepairMapper deviceRepairMapper;
 
@@ -543,9 +537,10 @@
         productionProgressDto.setTotalOrderCount(totalCount);
         productionProgressDto.setCompletedOrderCount(count);
         productionProgressDto.setUncompletedOrderCount(count2);
-        productionProgressDto.setPartialCompletedOrderCount(totalCount-count-count2);
+        productionProgressDto.setPartialCompletedOrderCount(totalCount - count - count2);
         return productionProgressDto;
     }
+
     @Override
     public ProductionTurnoverDto workInProcessTurnover() {
         ProductionTurnoverDto productionTurnoverDto = new ProductionTurnoverDto();
@@ -558,15 +553,15 @@
                 .map(ProductWorkOrder::getPlanQuantity)
                 .mapToLong(BigDecimal::longValue)
                 .sum();
-        if (sum == 0)return null;
+        if (sum == 0) return null;
         productionTurnoverDto.setTotalOrderCount(sum);//鎬诲湪鍒跺搧鏁伴噺
-        productionTurnoverDto.setAverageTurnoverDays(BigDecimal.valueOf(sum).divide(BigDecimal.valueOf(ChronoUnit.DAYS.between(LocalDateTime.now().minusMonths(1), LocalDateTime.now())),2,RoundingMode.HALF_UP));
+        productionTurnoverDto.setAverageTurnoverDays(BigDecimal.valueOf(sum).divide(BigDecimal.valueOf(ChronoUnit.DAYS.between(LocalDateTime.now().minusMonths(1), LocalDateTime.now())), 2, RoundingMode.HALF_UP));
         long completeQuantity = productWorkOrders.stream()
                 .filter(productWorkOrder -> productWorkOrder.getCompleteQuantity().compareTo(productWorkOrder.getPlanQuantity()) >= 0)
                 .map(ProductWorkOrder::getCompleteQuantity)
                 .mapToLong(BigDecimal::longValue)
                 .sum();
-        productionTurnoverDto.setTurnoverEfficiency(BigDecimal.valueOf(completeQuantity).divide(BigDecimal.valueOf(sum),2,RoundingMode.HALF_UP));
+        productionTurnoverDto.setTurnoverEfficiency(BigDecimal.valueOf(completeQuantity).divide(BigDecimal.valueOf(sum), 2, RoundingMode.HALF_UP));
         Map<String, List<ProductWorkOrderDto>> map = productWorkOrders.stream()
                 .filter(productWorkOrder -> productWorkOrder.getPlanQuantity().compareTo(productWorkOrder.getCompleteQuantity()) > 0)
                 .collect(Collectors.groupingBy(ProductWorkOrderDto::getProcessName));
@@ -581,6 +576,398 @@
         productionTurnoverDto.setProcessDetails(strings);
         productionTurnoverDto.setProcessQuantityDetails(processQuantityDetails);
         return productionTurnoverDto;
+    }
 
+    @Override
+    public DeptStaffDistributionDto deptStaffDistribution() {
+        DeptStaffDistributionDto dto = new DeptStaffDistributionDto();
+        List<MapDto> items = new ArrayList<>();
+
+        // 鏌ヨ鎵�鏈夋甯镐笖鏈垹闄ょ殑閮ㄩ棬
+        List<SysDept> depts = sysDeptMapper.selectDeptList(new SysDept());
+        if (CollectionUtils.isEmpty(depts)) {
+            dto.setItems(items);
+            return dto;
+        }
+
+        long totalUsers = 0;
+        List<Map<String, Object>> countsByDept = new ArrayList<>();
+
+        for (SysDept dept : depts) {
+            if ("0".equals(dept.getStatus()) && "0".equals(dept.getDelFlag())) {
+                Long count = sysUserDeptMapper.selectCount(new LambdaQueryWrapper<SysUserDept>()
+                        .eq(SysUserDept::getDeptId, dept.getDeptId()));
+                if (count > 0) {
+                    Map<String, Object> map = new HashMap<>();
+                    map.put("name", dept.getDeptName());
+                    map.put("count", count);
+                    countsByDept.add(map);
+                    totalUsers += count;
+                }
+            }
+        }
+
+        if (totalUsers > 0) {
+            BigDecimal total = BigDecimal.valueOf(totalUsers);
+            for (Map<String, Object> map : countsByDept) {
+                MapDto mapDto = new MapDto();
+                mapDto.setName((String) map.get("name"));
+                Long count = (Long) map.get("count");
+                mapDto.setValue(count.toString());
+                mapDto.setRate(BigDecimal.valueOf(count).multiply(new BigDecimal("100"))
+                        .divide(total, 2, RoundingMode.HALF_UP).toString());
+                items.add(mapDto);
+            }
+        }
+
+        dto.setTotal(totalUsers);
+        dto.setItems(items);
+        return dto;
+    }
+
+    @Override
+    public HomeSummaryDto summaryStatistics() {
+        HomeSummaryDto dto = new HomeSummaryDto();
+        LocalDate now = LocalDate.now();
+        YearMonth currentMonth = YearMonth.from(now);
+        YearMonth prevMonth = currentMonth.minusMonths(1);
+
+        LocalDateTime currentMonthEnd = currentMonth.atEndOfMonth().atTime(23, 59, 59);
+        LocalDateTime prevMonthEnd = prevMonth.atEndOfMonth().atTime(23, 59, 59);
+
+        //  鎬诲伐浣滀汉鍛�
+        Long currentStaff = countStaff(currentMonthEnd);
+        Long prevStaff = countStaff(prevMonthEnd);
+        dto.setTotalStaff(currentStaff);
+        dto.setStaffGrowthRate(calculateMoM(currentStaff, prevStaff));
+
+        //  鎬诲鎴锋暟
+        Long currentCustomers = countCustomers(currentMonthEnd);
+        Long prevCustomers = countCustomers(prevMonthEnd);
+        dto.setTotalCustomer(currentCustomers);
+        dto.setCustomerGrowthRate(calculateMoM(currentCustomers, prevCustomers));
+
+        //  鎬讳緵搴斿晢鏁�
+        Long currentSuppliers = countSuppliers(currentMonthEnd);
+        Long prevSuppliers = countSuppliers(prevMonthEnd);
+        dto.setTotalSupplier(currentSuppliers);
+        dto.setSupplierGrowthRate(calculateMoM(currentSuppliers, prevSuppliers));
+
+        return dto;
+    }
+
+    private Long countStaff(LocalDateTime dateTime) {
+        Long sysUserCount = sysUserMapper.selectCount(new LambdaQueryWrapper<SysUser>()
+                .eq(SysUser::getDelFlag, "0")
+                .le(SysUser::getCreateTime, dateTime));
+        Long staffCountItem = staffOnJobMapper.selectCount(new LambdaQueryWrapper<StaffOnJob>()
+                .le(StaffOnJob::getCreateTime, dateTime));
+        return sysUserCount + staffCountItem;
+    }
+
+
+    private Long countCustomers(LocalDateTime dateTime) {
+        return customerMapper.selectCount(new LambdaQueryWrapper<Customer>()
+                .le(Customer::getMaintenanceTime, dateTime.toLocalDate()));
+    }
+
+    private Long countSuppliers(LocalDateTime dateTime) {
+        return supplierManageMapper.selectCount(new LambdaQueryWrapper<SupplierManage>()
+                .le(SupplierManage::getCreateTime, dateTime));
+    }
+
+    private String calculateMoM(Number current, Number prev) {
+        BigDecimal curVal = new BigDecimal(current.toString());
+        BigDecimal prevVal = new BigDecimal(prev.toString());
+        if (prevVal.compareTo(BigDecimal.ZERO) == 0) {
+            return curVal.compareTo(BigDecimal.ZERO) > 0 ? "100.00" : "0.00";
+        }
+        return curVal.subtract(prevVal)
+                .divide(prevVal, 4, RoundingMode.HALF_UP)
+                .multiply(new BigDecimal("100"))
+                .setScale(2, RoundingMode.HALF_UP)
+                .toString();
+    }
+
+    @Override
+    public List<SupplierPurchaseRankingDto> supplierPurchaseRanking(Integer type) {
+        LocalDate today = LocalDate.now();
+        LocalDate startDate;
+        LocalDate endDate;
+        switch (type) {
+            case 0: // 鍛�
+                startDate = today.with(DayOfWeek.MONDAY);
+                endDate = today.with(DayOfWeek.SUNDAY);
+                break;
+            case 1: // 鏈�
+                startDate = today.with(TemporalAdjusters.firstDayOfMonth());
+                endDate = today.with(TemporalAdjusters.lastDayOfMonth());
+                break;
+            case 2: // 瀛e害
+                Month currentMonth = today.getMonth();
+                Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
+                Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
+
+                startDate = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
+                endDate = today.withMonth(lastMonthOfQuarter.getValue()).with(TemporalAdjusters.lastDayOfMonth());
+                break;
+            default:
+                return new ArrayList<>();
+        }
+
+        QueryWrapper<PurchaseLedger> queryWrapper = new QueryWrapper<>();
+        queryWrapper.select("supplier_name", "SUM(contract_amount) as total_amount")
+                .ge("entry_date", startDate)
+                .le("entry_date", endDate)
+//                .ne("approval_status", 3)
+                .groupBy("supplier_name")
+                .orderByDesc("total_amount")
+                .last("LIMIT 5");
+
+        List<Map<String, Object>> maps = purchaseLedgerMapper.selectMaps(queryWrapper);
+        return maps.stream().map(map -> {
+            SupplierPurchaseRankingDto dto = new SupplierPurchaseRankingDto();
+            dto.setSupplierName(map.get("supplier_name") != null ? map.get("supplier_name").toString() : "");
+            Object amount = map.get("total_amount");
+            dto.setTotalAmount(amount != null ? new BigDecimal(amount.toString()) : BigDecimal.ZERO);
+            return dto;
+        }).collect(Collectors.toList());
+    }
+
+    @Override
+    public CustomerRevenueAnalysisDto customerRevenueAnalysis(Long customerId, Integer type) {
+        CustomerRevenueAnalysisDto dto = new CustomerRevenueAnalysisDto();
+        List<MapDto> items = new ArrayList<>();
+        LocalDate today = LocalDate.now();
+
+        LocalDate start;
+        LocalDate end;
+        boolean groupByMonth = false;
+
+        switch (type) {
+            case 0: // 鍛�
+                start = today.with(DayOfWeek.MONDAY);
+                end = today.with(DayOfWeek.SUNDAY);
+                break;
+            case 1: // 鏈�
+                start = today.with(TemporalAdjusters.firstDayOfMonth());
+                end = today.with(TemporalAdjusters.lastDayOfMonth());
+                break;
+            case 2: // 瀛e害
+                Month firstMonthOfQuarter = today.getMonth().firstMonthOfQuarter();
+                start = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
+                end = today.withMonth(firstMonthOfQuarter.plus(2).getValue()).with(TemporalAdjusters.lastDayOfMonth());
+                groupByMonth = true;
+                break;
+            default:
+                dto.setItems(items);
+                return dto;
+        }
+
+        List<SalesLedger> list = salesLedgerMapper.selectList(new LambdaQueryWrapper<SalesLedger>()
+                .eq(SalesLedger::getCustomerId, customerId)
+                .ge(SalesLedger::getEntryDate, start)
+                .le(SalesLedger::getEntryDate, end));
+
+        if (groupByMonth) {
+            for (int i = 0; i < 3; i++) {
+                LocalDate m = start.plusMonths(i);
+                String monthName = m.getMonthValue() + "鏈�";
+                BigDecimal sum = list.stream()
+                        .filter(l -> l.getEntryDate() != null)
+                        .filter(l -> {
+                            LocalDate ld = l.getEntryDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+                            return ld.getMonth() == m.getMonth() && ld.getYear() == m.getYear();
+                        })
+                        .map(SalesLedger::getContractAmount)
+                        .filter(Objects::nonNull)
+                        .reduce(BigDecimal.ZERO, BigDecimal::add);
+                MapDto mapDto = new MapDto();
+                mapDto.setName(monthName);
+                mapDto.setValue(sum.setScale(2, RoundingMode.HALF_UP).toString());
+                items.add(mapDto);
+            }
+        } else {
+            long days = ChronoUnit.DAYS.between(start, end) + 1;
+            for (int i = 0; i < days; i++) {
+                LocalDate d = start.plusDays(i);
+                String dayName = d.getMonthValue() + "/" + d.getDayOfMonth();
+                BigDecimal sum = list.stream()
+                        .filter(l -> l.getEntryDate() != null)
+                        .filter(l -> l.getEntryDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().equals(d))
+                        .map(SalesLedger::getContractAmount)
+                        .filter(Objects::nonNull)
+                        .reduce(BigDecimal.ZERO, BigDecimal::add);
+                MapDto mapDto = new MapDto();
+                mapDto.setName(dayName);
+                mapDto.setValue(sum.setScale(2, RoundingMode.HALF_UP).toString());
+                items.add(mapDto);
+            }
+        }
+
+        dto.setItems(items);
+        return dto;
+    }
+
+    @Override
+    public ProductCategoryDistributionDto productCategoryDistribution() {
+        ProductCategoryDistributionDto dto = new ProductCategoryDistributionDto();
+        List<ProductCategoryDistributionDto.MajorCategoryDto> majorItems = new ArrayList<>();
+
+        //  鎵�鏈夌殑浜у搧澶х被鍜屽皬绫�
+        List<Product> allProducts = productMapper.selectList(new LambdaQueryWrapper<Product>());
+        if (CollectionUtils.isEmpty(allProducts)) {
+            dto.setItems(majorItems);
+            return dto;
+        }
+
+        List<Product> majorCategories = allProducts.stream().filter(p -> p.getParentId() == null).collect(Collectors.toList());
+        List<Product> minorCategories = allProducts.stream().filter(p -> p.getParentId() != null).collect(Collectors.toList());
+
+        //  浠� sales_ledger_product 鎷垮埌姣忎釜浜у搧鐨勫瀷鍙峰簱瀛�
+        // 闇�鍖呭惈瑙勬牸鍨嬪彿浠ユ敮鎸佷笁绾у垎甯�
+        List<Map<String, Object>> quantityMaps = salesLedgerProductMapper.selectMaps(new QueryWrapper<SalesLedgerProduct>()
+                .select("product_id", "specification_model", "type", "SUM(quantity) as sum_qty")
+                .isNotNull("product_id")
+                .groupBy("product_id", "specification_model", "type"));
+
+        Map<Long, Map<String, Map<Integer, BigDecimal>>> modelStockGroups = new HashMap<>();
+        for (Map<String, Object> map : quantityMaps) {
+            Long productId = Long.parseLong(map.get("product_id").toString());
+            String model = map.get("specification_model") != null ? map.get("specification_model").toString() : "鏈煡鍨嬪彿";
+            Integer type = Integer.parseInt(map.get("type").toString());
+            BigDecimal sum = map.get("sum_qty") != null ? new BigDecimal(map.get("sum_qty").toString()) : BigDecimal.ZERO;
+
+            modelStockGroups.computeIfAbsent(productId, k -> new HashMap<>())
+                    .computeIfAbsent(model, k -> new HashMap<>())
+                    .put(type, sum);
+        }
+
+        //  搴撳瓨骞舵眹鎬�
+        Map<Long, List<MapDto>> productModelsMap = new HashMap<>();
+        Map<Long, BigDecimal> productTotalStockMap = new HashMap<>();
+
+        for (Long pid : modelStockGroups.keySet()) {
+            Map<String, Map<Integer, BigDecimal>> models = modelStockGroups.get(pid);
+            BigDecimal productStock = BigDecimal.ZERO;
+            List<MapDto> modelDtos = new ArrayList<>();
+
+            for (String modelName : models.keySet()) {
+                Map<Integer, BigDecimal> types = models.get(modelName);
+                BigDecimal procurement = types.getOrDefault(2, BigDecimal.ZERO);
+                BigDecimal sales = types.getOrDefault(1, BigDecimal.ZERO);
+                BigDecimal stock = procurement.subtract(sales);
+                if (stock.compareTo(BigDecimal.ZERO) < 0) stock = BigDecimal.ZERO;
+
+                MapDto modelDto = new MapDto();
+                modelDto.setName(modelName);
+                modelDto.setValue(stock.stripTrailingZeros().toPlainString());
+                modelDtos.add(modelDto);
+                productStock = productStock.add(stock);
+            }
+            productModelsMap.put(pid, modelDtos);
+            productTotalStockMap.put(pid, productStock);
+        }
+
+        BigDecimal totalInventory = productTotalStockMap.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add);
+
+        //  鍨嬪彿鍗犳瘮
+        if (totalInventory.compareTo(BigDecimal.ZERO) > 0) {
+            for (List<MapDto> dtos : productModelsMap.values()) {
+                for (MapDto m : dtos) {
+                    BigDecimal val = new BigDecimal(m.getValue());
+                    m.setRate(val.multiply(new BigDecimal("100"))
+                            .divide(totalInventory, 2, RoundingMode.HALF_UP).toString());
+                }
+            }
+        }
+
+        //  鍒嗗眰鏁版嵁
+        for (Product major : majorCategories) {
+            ProductCategoryDistributionDto.MajorCategoryDto majorDto = new ProductCategoryDistributionDto.MajorCategoryDto();
+            majorDto.setName(major.getProductName());
+
+            List<ProductCategoryDistributionDto.MinorCategoryDto> minorDtos = new ArrayList<>();
+            BigDecimal majorStock = BigDecimal.ZERO;
+
+            for (Product minor : minorCategories) {
+                if (major.getId().equals(minor.getParentId())) {
+                    BigDecimal stock = productTotalStockMap.getOrDefault(minor.getId(), BigDecimal.ZERO);
+                    ProductCategoryDistributionDto.MinorCategoryDto minorDto = new ProductCategoryDistributionDto.MinorCategoryDto();
+                    minorDto.setName(minor.getProductName());
+                    minorDto.setValue(stock.stripTrailingZeros().toPlainString());
+
+                    if (totalInventory.compareTo(BigDecimal.ZERO) > 0) {
+                        minorDto.setRate(stock.multiply(new BigDecimal("100"))
+                                .divide(totalInventory, 2, RoundingMode.HALF_UP).toString());
+                    } else {
+                        minorDto.setRate("0.00");
+                    }
+
+                    minorDto.setChildren(productModelsMap.getOrDefault(minor.getId(), new ArrayList<>()));
+                    minorDtos.add(minorDto);
+                    majorStock = majorStock.add(stock);
+                }
+            }
+
+            majorDto.setValue(majorStock.stripTrailingZeros().toPlainString());
+
+            if (totalInventory.compareTo(BigDecimal.ZERO) > 0) {
+                majorDto.setRate(majorStock.multiply(new BigDecimal("100"))
+                        .divide(totalInventory, 2, RoundingMode.HALF_UP).toString());
+            } else {
+                majorDto.setRate("0.00");
+            }
+            majorDto.setChildren(minorDtos);
+            majorItems.add(majorDto);
+        }
+
+        dto.setItems(majorItems);
+        return dto;
+    }
+
+    @Override
+    public List<CustomerContributionRankingDto> customerContributionRanking(Integer type) {
+        LocalDate today = LocalDate.now();
+        LocalDate startDate = null;
+        LocalDate endDate = null;
+        switch (type) {
+            case 0:
+                startDate = today.with(DayOfWeek.MONDAY);
+                endDate = today.with(DayOfWeek.SUNDAY);
+                break;
+            case 1:
+                startDate = today.with(TemporalAdjusters.firstDayOfMonth());
+                endDate = today.with(TemporalAdjusters.lastDayOfMonth());
+                break;
+            case 2:
+                Month currentMonth = today.getMonth();
+                Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
+                Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
+                startDate = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
+                endDate = today.withMonth(lastMonthOfQuarter.getValue()).with(TemporalAdjusters.lastDayOfMonth());
+                break;
+        }
+
+        QueryWrapper<SalesLedger> queryWrapper = new QueryWrapper<>();
+        queryWrapper.select("customer_name", "SUM(contract_amount) as total_amount")
+                .isNotNull("customer_name")
+                .groupBy("customer_name")
+                .orderByDesc("total_amount")
+                .last("LIMIT 5");
+
+        if (startDate != null && endDate != null) {
+            queryWrapper.between("entry_date", startDate, endDate);
+        }
+
+        List<Map<String, Object>> maps = salesLedgerMapper.selectMaps(queryWrapper);
+        List<CustomerContributionRankingDto> result = new ArrayList<>();
+        for (Map<String, Object> map : maps) {
+            CustomerContributionRankingDto rankingDto = new CustomerContributionRankingDto();
+            rankingDto.setCustomerName(map.get("customer_name").toString());
+            rankingDto.setTotalAmount(map.get("total_amount") != null ? new BigDecimal(map.get("total_amount").toString()) : BigDecimal.ZERO);
+            result.add(rankingDto);
+        }
+        return result;
     }
 }

--
Gitblit v1.9.3