From 62fe432c21e9fd87d3fc7558c8c5a2863e220a98 Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期四, 29 一月 2026 18:00:34 +0800
Subject: [PATCH] feat: 财务分析大屏接口

---
 src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java |  585 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 582 insertions(+), 3 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 b66def3..828d6ae 100644
--- a/src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
+++ b/src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -2,8 +2,11 @@
 
 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.extension.plugins.pagination.Page;
+import com.ruoyi.account.mapper.AccountIncomeMapper;
+import com.ruoyi.account.pojo.AccountExpense;
+import com.ruoyi.home.mapper.HomeMapper;
+import com.ruoyi.account.mapper.AccountExpenseMapper;
 import com.ruoyi.approve.mapper.ApproveProcessMapper;
 import com.ruoyi.approve.pojo.ApproveProcess;
 import com.ruoyi.basic.mapper.CustomerMapper;
@@ -12,7 +15,6 @@
 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;
@@ -86,6 +88,9 @@
     private SalesLedgerProductMapper salesLedgerProductMapper;
 
     @Autowired
+    private StockInventoryMapper stockInventoryMapper;
+
+    @Autowired
     private ProcurementRecordMapper procurementRecordStorageMapper;
 
     @Autowired
@@ -125,8 +130,9 @@
     private SysUserMapper sysUserMapper;
     @Autowired
     private SysUserDeptMapper sysUserDeptMapper;
+
     @Autowired
-    private StockInventoryMapper stockInventoryMapper;
+    private HomeMapper homeMapper;
 
     @Override
     public HomeBusinessDto business() {
@@ -900,4 +906,577 @@
         }
         return result;
     }
+
+    @Override
+    public List<MapDto> productSalesAnalysis() {
+        // 鑾峰彇鎵�鏈変骇鍝佸ぇ绫荤殑閿�鍞
+        List<Map<String, Object>> analysisResults = salesLedgerProductMapper.selectProductSalesAnalysis();
+        if (CollectionUtils.isEmpty(analysisResults)) {
+            return new ArrayList<>();
+        }
+
+        // 璁$畻鎬婚攢鍞
+        BigDecimal totalSalesAmount = analysisResults.stream()
+                .map(r -> (BigDecimal) r.get("value"))
+                .filter(Objects::nonNull)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+        return analysisResults.stream()
+                .map(result -> {
+                    MapDto mapDto = new MapDto();
+                    String name = (String) result.get("name");
+                    BigDecimal value = (BigDecimal) result.get("value");
+
+                    mapDto.setName(name);
+                    mapDto.setValue(value != null ? value.setScale(2, RoundingMode.HALF_UP).toString() : "0.00");
+
+                    if (totalSalesAmount.compareTo(BigDecimal.ZERO) == 0 || value == null) {
+                        mapDto.setRate("0.00");
+                    } else {
+                        String rate = value.divide(totalSalesAmount, 4, RoundingMode.HALF_UP)
+                                .multiply(new BigDecimal("100"))
+                                .setScale(2, RoundingMode.HALF_UP)
+                                .toString();
+                        mapDto.setRate(rate);
+                    }
+                    return mapDto;
+                })
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public List<MapDto> rawMaterialPurchaseAmountRatio() {
+        // 鑾峰彇鎵�鏈夊師鏉愭枡鐨勯噰璐
+        List<Map<String, Object>> analysisResults = salesLedgerProductMapper.selectRawMaterialPurchaseAnalysis();
+        if (CollectionUtils.isEmpty(analysisResults)) {
+            return new ArrayList<>();
+        }
+
+        // 璁$畻鎬婚噰璐
+        BigDecimal totalPurchaseAmount = analysisResults.stream()
+                .map(r -> (BigDecimal) r.get("value"))
+                .filter(Objects::nonNull)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+        return analysisResults.stream()
+                .map(result -> {
+                    MapDto mapDto = new MapDto();
+                    String name = (String) result.get("name");
+                    BigDecimal value = (BigDecimal) result.get("value");
+
+                    mapDto.setName(name);
+                    mapDto.setValue(value != null ? value.setScale(2, RoundingMode.HALF_UP).toString() : "0.00");
+
+                    if (totalPurchaseAmount.compareTo(BigDecimal.ZERO) == 0 || value == null) {
+                        mapDto.setRate("0.00");
+                    } else {
+                        String rate = value.divide(totalPurchaseAmount, 4, RoundingMode.HALF_UP)
+                                .multiply(new BigDecimal("100"))
+                                .setScale(2, RoundingMode.HALF_UP)
+                                .toString();
+                        mapDto.setRate(rate);
+                    }
+                    return mapDto;
+                })
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public List<MapDto> salesPurchaseStorageProductCount() {
+        LocalDate now = LocalDate.now();
+        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+
+        String currentMonthStart = now.with(TemporalAdjusters.firstDayOfMonth()).format(dtf);
+        String currentMonthNow = now.format(dtf);
+
+        LocalDate lastMonth = now.minusMonths(1);
+        String lastMonthStart = lastMonth.with(TemporalAdjusters.firstDayOfMonth()).format(dtf);
+        String lastMonthEnd = lastMonth.with(TemporalAdjusters.lastDayOfMonth()).format(dtf);
+
+        // 閿�鍞�
+        int currentSales = salesLedgerProductMapper.selectProductCountByTypeAndDate(1, currentMonthStart, currentMonthNow);
+        int lastSales = salesLedgerProductMapper.selectProductCountByTypeAndDate(1, lastMonthStart, lastMonthEnd);
+
+        // 閲囪喘
+        int currentPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, currentMonthStart, currentMonthNow);
+        int lastPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, lastMonthStart, lastMonthEnd);
+
+        // 鍌ㄥ瓨
+        int currentStorage = stockInventoryMapper.selectStorageProductCountByDate(currentMonthStart, currentMonthNow);
+        int lastStorage = stockInventoryMapper.selectStorageProductCountByDate(lastMonthStart, lastMonthEnd);
+
+        List<MapDto> list = new ArrayList<>();
+        list.add(createMapDto("閿�鍞骇鍝佹暟", currentSales, lastSales));
+        list.add(createMapDto("閲囪喘浜у搧鏁�", currentPurchase, lastPurchase));
+        list.add(createMapDto("鍌ㄥ瓨浜у搧鏁�", currentStorage, lastStorage));
+
+        return list;
+    }
+
+    private MapDto createMapDto(String name, int current, int last) {
+        MapDto dto = new MapDto();
+        dto.setName(name);
+        dto.setValue(String.valueOf(current));
+
+        if (last == 0) {
+            dto.setRate(current > 0 ? "100.00" : "0.00");
+        } else {
+            BigDecimal curDec = new BigDecimal(current);
+            BigDecimal lastDec = new BigDecimal(last);
+            // 澧為暱鐜�
+            String rate = curDec.subtract(lastDec)
+                    .divide(lastDec, 4, RoundingMode.HALF_UP)
+                    .multiply(new BigDecimal("100"))
+                    .setScale(2, RoundingMode.HALF_UP)
+                    .toString();
+            dto.setRate(rate);
+        }
+        return dto;
+    }
+
+    @Override
+    public List<Map<String, Object>> productInOutAnalysis(Integer type) {
+        String targetName;
+        if (type == 1) {
+            targetName = "鍘熸潗鏂�";
+        } else if (type == 2) {
+            targetName = "鎴愬搧";
+        } else if (type == 3) {
+            targetName = "鍗婃垚鍝�";
+        } else {
+            return new ArrayList<>();
+        }
+
+        LambdaQueryWrapper<Product> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(Product::getProductName, targetName).isNull(Product::getParentId);
+        Product rootCategory = productMapper.selectOne(queryWrapper);
+
+        if (rootCategory == null) {
+            log.warn("鏈壘鍒板悕绉颁负 {} 鐨勯《绾т骇鍝佸垎绫�", targetName);
+            return new ArrayList<>();
+        }
+        Long rootCategoryId = rootCategory.getId();
+
+        LocalDate now = LocalDate.now();
+        DateTimeFormatter displayDtf = DateTimeFormatter.ofPattern("M/d");
+
+        String startDate = now.minusDays(6).toString();
+        String endDate = now.toString();
+
+        List<Map<String, Object>> inCounts = stockInventoryMapper.selectDailyStockInCounts(rootCategoryId, startDate, endDate + " 23:59:59");
+
+        List<Map<String, Object>> outCounts = stockInventoryMapper.selectDailyStockOutCounts(rootCategoryId, startDate, endDate + " 23:59:59");
+
+        Map<LocalDate, BigDecimal> inMap = inCounts.stream()
+                .collect(Collectors.toMap(
+                        m -> ((java.sql.Date) m.get("date")).toLocalDate(),
+                        m -> (BigDecimal) m.get("count"),
+                        BigDecimal::add
+                ));
+
+        Map<LocalDate, BigDecimal> outMap = outCounts.stream()
+                .collect(Collectors.toMap(
+                        m -> ((java.sql.Date) m.get("date")).toLocalDate(),
+                        m -> (BigDecimal) m.get("count"),
+                        BigDecimal::add
+                ));
+
+        List<Map<String, Object>> result = new ArrayList<>();
+
+        for (int i = 6; i >= 0; i--) {
+            LocalDate date = now.minusDays(i);
+
+            Map<String, Object> dataPoint = new LinkedHashMap<>();
+            dataPoint.put("date", date.format(displayDtf)); // 1/23
+            dataPoint.put("inCount", inMap.getOrDefault(date, BigDecimal.ZERO));
+            dataPoint.put("outCount", outMap.getOrDefault(date, BigDecimal.ZERO));
+
+            result.add(dataPoint);
+        }
+
+        return result;
+    }
+
+    @Override
+    public List<MapDto> productTurnoverDays() {
+        return homeMapper.productTurnoverDays();
+    }
+
+    @Autowired
+    private AccountExpenseMapper accountExpenseMapper;
+
+    @Autowired
+    private AccountIncomeMapper accountIncomeMapper;
+
+
+    public List<Map<String, Object>> incomeExpenseAnalysis(Integer type) {
+
+        LocalDate today = LocalDate.now();
+        LocalDate startDate;
+        LocalDate endDate;
+        String dateFormat;
+        DateTimeFormatter formatter;
+        List<String> xAxis = new ArrayList<>();
+
+        // 1. 鏃堕棿鑼冨洿 & X 杞�
+        if (type == 3) {
+            // 鏈搴︼紙鎸夋湀锛�
+            Month currentMonth = today.getMonth();
+            Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
+            startDate = today.withMonth(firstMonthOfQuarter.getValue())
+                    .with(TemporalAdjusters.firstDayOfMonth());
+            endDate = startDate.plusMonths(2)
+                    .with(TemporalAdjusters.lastDayOfMonth());
+
+            dateFormat = "%Y-%m";
+            formatter = DateTimeFormatter.ofPattern("yyyy-MM");
+
+            LocalDate tmp = startDate;
+            while (!tmp.isAfter(endDate)) {
+                xAxis.add(tmp.format(formatter));
+                tmp = tmp.plusMonths(1);
+            }
+
+        } else {
+            // 鍛� / 鏈堬紙鎸夊ぉ锛�
+            dateFormat = "%Y-%m-%d";
+            formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+
+            if (type == 1) {
+                startDate = today.with(DayOfWeek.MONDAY);
+                endDate = today.with(DayOfWeek.SUNDAY);
+            } else {
+                startDate = today.with(TemporalAdjusters.firstDayOfMonth());
+                endDate = today.with(TemporalAdjusters.lastDayOfMonth());
+            }
+
+            LocalDate tmp = startDate;
+            while (!tmp.isAfter(endDate)) {
+                xAxis.add(tmp.format(formatter));
+                tmp = tmp.plusDays(1);
+            }
+        }
+
+        String startStr = startDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+        String endStr = endDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+
+        // 2. 鏌ヨ鏁版嵁
+        List<IncomeExpenseAnalysisDto> incomeList = accountIncomeMapper.selectIncomeStats(startStr, endStr, dateFormat);
+
+//        List<IncomeExpenseAnalysisDto> purchaseList =
+//                purchaseLedgerMapper.selectPurchaseStats(startStr, endStr, dateFormat);
+
+        List<IncomeExpenseAnalysisDto> expenseList = accountExpenseMapper.selectAccountExpenseStats(startStr, endStr, dateFormat);
+
+        // 3. 杞� Map锛堣嚜鍔ㄥ悎骞讹級
+        Map<String, BigDecimal> incomeMap = incomeList.stream()
+                .collect(Collectors.toMap(
+                        IncomeExpenseAnalysisDto::getDateStr,
+                        IncomeExpenseAnalysisDto::getAmount,
+                        BigDecimal::add));
+
+//        Map<String, BigDecimal> purchaseMap = purchaseList.stream()
+//                .collect(Collectors.toMap(
+//                        IncomeExpenseAnalysisDto::getDateStr,
+//                        IncomeExpenseAnalysisDto::getAmount,
+//                        BigDecimal::add));
+
+        Map<String, BigDecimal> expenseMap = expenseList.stream()
+                .collect(Collectors.toMap(
+                        IncomeExpenseAnalysisDto::getDateStr,
+                        IncomeExpenseAnalysisDto::getAmount,
+                        BigDecimal::add));
+
+        // 4. 缁勮杩斿洖
+        List<Map<String, Object>> result = new ArrayList<>();
+
+        for (String dateStr : xAxis) {
+            Map<String, Object> item = new HashMap<>();
+            item.put("date", dateStr);
+
+            // 鏀跺叆
+            BigDecimal income = incomeMap.getOrDefault(dateStr, BigDecimal.ZERO);
+            item.put("income", income.setScale(2, RoundingMode.HALF_UP));
+
+            // 鏀嚭 = 閲囪喘 + 璐㈠姟鏀嚭
+//            BigDecimal purchase = purchaseMap.getOrDefault(dateStr, BigDecimal.ZERO);
+            BigDecimal expense = expenseMap.getOrDefault(dateStr, BigDecimal.ZERO);
+//            BigDecimal totalExpense = purchase.add(expense);
+            BigDecimal totalExpense = expense;
+
+            item.put("expense", totalExpense.setScale(2, RoundingMode.HALF_UP));
+
+            result.add(item);
+        }
+
+        return result;
+    }
+
+
+    @Override
+    public List<MapDto> profitTrendAnalysis() {
+        LocalDate today = LocalDate.now();
+        LocalDate startDate = today.minusMonths(11).with(TemporalAdjusters.firstDayOfMonth());
+        LocalDate endDate = today.with(TemporalAdjusters.lastDayOfMonth());
+
+        String dateFormat = "%Y-%m";
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
+
+        List<String> months = new ArrayList<>();
+        LocalDate temp = startDate;
+        while (!temp.isAfter(endDate)) {
+            months.add(temp.format(formatter));
+            temp = temp.plusMonths(1);
+        }
+
+        String startStr = startDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+        String endStr = endDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+
+//        List<IncomeExpenseAnalysisDto> incomeList = salesLedgerMapper.selectIncomeStats(startStr, endStr, dateFormat);
+        List<IncomeExpenseAnalysisDto> incomeList = new ArrayList<>();
+        List<IncomeExpenseAnalysisDto> purchaseList = purchaseLedgerMapper.selectPurchaseStats(startStr, endStr, dateFormat);
+        List<IncomeExpenseAnalysisDto> expenseList = accountExpenseMapper.selectAccountExpenseStats(startStr, endStr, dateFormat);
+
+        Map<String, BigDecimal> incomeMap = incomeList.stream().collect(Collectors.toMap(IncomeExpenseAnalysisDto::getDateStr, IncomeExpenseAnalysisDto::getAmount, BigDecimal::add));
+        Map<String, BigDecimal> purchaseMap = purchaseList.stream().collect(Collectors.toMap(IncomeExpenseAnalysisDto::getDateStr, IncomeExpenseAnalysisDto::getAmount, BigDecimal::add));
+        Map<String, BigDecimal> expenseMap = expenseList.stream().collect(Collectors.toMap(IncomeExpenseAnalysisDto::getDateStr, IncomeExpenseAnalysisDto::getAmount, BigDecimal::add));
+
+        List<MapDto> result = new ArrayList<>();
+
+        for (String month : months) {
+            MapDto dto = new MapDto();
+            dto.setName(month);
+
+            BigDecimal income = incomeMap.getOrDefault(month, BigDecimal.ZERO);
+
+            BigDecimal purchase = purchaseMap.getOrDefault(month, BigDecimal.ZERO);
+             income = BigDecimal.ZERO;
+            BigDecimal expense = expenseMap.getOrDefault(month, BigDecimal.ZERO);
+            BigDecimal totalExpense = purchase.add(expense);
+
+            BigDecimal profit = income.subtract(totalExpense);
+
+            dto.setValue(profit.setScale(2, RoundingMode.HALF_UP).toString());
+            result.add(dto);
+        }
+
+        return result;
+    }
+
+    @Override
+    public List<MapDto> expenseCompositionAnalysis(Integer type) {
+        List<MapDto> result = new ArrayList<>();
+
+        if (type == 2) {
+            List<MapDto> customerList = salesLedgerMapper.selectCustomerSalesComposition();
+            if (customerList != null) {
+                result.addAll(customerList);
+            }
+        } else {
+            BigDecimal rawMaterialAmount = salesLedgerProductMapper.selectRawMaterialExpense();
+            MapDto rawMaterialDto = new MapDto();
+            rawMaterialDto.setName("鍘熸潗鏂�");
+            rawMaterialDto.setValue(rawMaterialAmount != null ? rawMaterialAmount.toString() : "0");
+            result.add(rawMaterialDto);
+
+            List<MapDto> expenseList = accountExpenseMapper.selectExpenseComposition(null, null);
+            if (expenseList != null) {
+                result.addAll(expenseList);
+            }
+        }
+
+        BigDecimal total = BigDecimal.ZERO;
+        for (MapDto dto : result) {
+            if (dto.getValue() != null) {
+                total = total.add(new BigDecimal(dto.getValue()));
+            }
+        }
+
+        if (total.compareTo(BigDecimal.ZERO) > 0) {
+            for (MapDto dto : result) {
+                if (dto.getValue() != null) {
+                    BigDecimal val = new BigDecimal(dto.getValue());
+                    String rate = val.divide(total, 4, RoundingMode.HALF_UP)
+                            .multiply(new BigDecimal("100"))
+                            .setScale(2, RoundingMode.HALF_UP)
+                            .toString();
+                    dto.setRate(rate);
+                } else {
+                    dto.setRate("0.00");
+                }
+            }
+        } else {
+            for (MapDto dto : result) {
+                dto.setRate("0.00");
+            }
+        }
+
+        return result;
+    }
+
+
+    @Override
+    public MonthlyIncomeDto monthlyIncome() {
+        MonthlyIncomeDto dto = new MonthlyIncomeDto();
+        LocalDate now = LocalDate.now();
+        YearMonth currentMonth = YearMonth.from(now);
+        LocalDateTime startOfMonth = currentMonth.atDay(1).atStartOfDay();
+        LocalDateTime endOfMonth = currentMonth.atEndOfMonth().atTime(23, 59, 59);
+
+        LambdaQueryWrapper<SalesLedgerProduct> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SalesLedgerProduct::getType, 1);
+        wrapper.ge(SalesLedgerProduct::getRegisterDate, startOfMonth);
+        wrapper.le(SalesLedgerProduct::getRegisterDate, endOfMonth);
+
+        List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(wrapper);
+
+        if (CollectionUtils.isEmpty(products)) {
+            return dto;
+        }
+
+        BigDecimal collected = products.stream()
+                .map(SalesLedgerProduct::getInvoiceTotal)
+                .filter(Objects::nonNull)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        dto.setMonthlyIncome(collected);
+
+        BigDecimal overdue = products.stream()
+                .map(SalesLedgerProduct::getPendingInvoiceTotal)
+                .filter(Objects::nonNull)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        dto.setOverdueNum(overdue);
+
+        BigDecimal total = collected.add(overdue);
+
+        if (total.compareTo(BigDecimal.ZERO) > 0) {
+            String collectionRate = collected.divide(total, 4, RoundingMode.HALF_UP)
+                    .multiply(new BigDecimal("100"))
+                    .setScale(2, RoundingMode.HALF_UP)
+                    .toString();
+            dto.setCollectionRate(collectionRate);
+
+            String overdueRate = overdue.divide(total, 4, RoundingMode.HALF_UP)
+                    .multiply(new BigDecimal("100"))
+                    .setScale(2, RoundingMode.HALF_UP)
+                    .toString();
+            dto.setOverdueRate(overdueRate);
+        }
+
+        return dto;
+    }
+
+    public MonthlyExpenditureDto monthlyExpenditure() {
+
+        MonthlyExpenditureDto dto = new MonthlyExpenditureDto();
+
+        //  褰撴湀鏃堕棿鑼冨洿
+        LocalDate now = LocalDate.now();
+        YearMonth currentMonth = YearMonth.from(now);
+        LocalDateTime startOfMonth = currentMonth.atDay(1).atStartOfDay();
+        LocalDateTime endOfMonth = currentMonth.atEndOfMonth().atTime(23, 59, 59);
+
+        //  閲囪喘鍙拌处锛坱ype = 2锛�
+        LambdaQueryWrapper<SalesLedgerProduct> purchaseWrapper = new LambdaQueryWrapper<>();
+        purchaseWrapper.eq(SalesLedgerProduct::getType, 2);
+        purchaseWrapper.ge(SalesLedgerProduct::getRegisterDate, startOfMonth);
+        purchaseWrapper.le(SalesLedgerProduct::getRegisterDate, endOfMonth);
+
+        List<SalesLedgerProduct> purchaseProducts =
+                salesLedgerProductMapper.selectList(purchaseWrapper);
+
+        BigDecimal rawMaterialCost = BigDecimal.ZERO;   // 鍘熸潗鏂欐垚鏈�
+        BigDecimal paidAmount = BigDecimal.ZERO;        // 宸蹭粯娆鹃噾棰�
+        BigDecimal pendingAmount = BigDecimal.ZERO;     // 寰呬粯娆鹃噾棰�
+
+        if (!CollectionUtils.isEmpty(purchaseProducts)) {
+            for (SalesLedgerProduct p : purchaseProducts) {
+
+                if (p.getTaxInclusiveTotalPrice() != null) {
+                    rawMaterialCost = rawMaterialCost.add(p.getTaxInclusiveTotalPrice());
+                }
+
+                if (p.getTicketsTotal() != null) {
+                    paidAmount = paidAmount.add(p.getTicketsTotal());
+                }
+
+                if (p.getPendingTicketsTotal() != null) {
+                    pendingAmount = pendingAmount.add(p.getPendingTicketsTotal());
+                }
+            }
+        }
+
+        //  鍏朵粬璐圭敤
+        LambdaQueryWrapper<AccountExpense> expenseWrapper = new LambdaQueryWrapper<>();
+        expenseWrapper.ge(AccountExpense::getExpenseDate,
+                java.sql.Date.valueOf(currentMonth.atDay(1)));
+        expenseWrapper.le(AccountExpense::getExpenseDate,
+                java.sql.Date.valueOf(currentMonth.atEndOfMonth()));
+
+        List<AccountExpense> expenses =
+                accountExpenseMapper.selectList(expenseWrapper);
+
+        BigDecimal otherExpense = BigDecimal.ZERO;
+        if (!CollectionUtils.isEmpty(expenses)) {
+            for (AccountExpense e : expenses) {
+                if (e.getExpenseMoney() != null) {
+                    otherExpense = otherExpense.add(e.getExpenseMoney());
+                }
+            }
+        }
+
+        //  鏈堝害鎬绘敮鍑�
+//        BigDecimal monthlyExpenditure = rawMaterialCost.add(otherExpense);
+        BigDecimal monthlyExpenditure = otherExpense;
+        dto.setMonthlyExpenditure(monthlyExpenditure);
+
+        // 宸蹭粯娆� 梅锛堝凡浠樻 + 寰呬粯娆撅級
+        BigDecimal totalPayable = paidAmount.add(pendingAmount);
+        if (totalPayable.compareTo(BigDecimal.ZERO) > 0) {
+            String paymentRate = paidAmount
+                    .divide(totalPayable, 4, RoundingMode.HALF_UP)
+                    .multiply(BigDecimal.valueOf(100))
+                    .setScale(2, RoundingMode.HALF_UP)
+                    .toString();
+            dto.setPaymentRate(paymentRate);
+        }
+
+        // 鍞彴璐︼紙type = 1锛�
+        LambdaQueryWrapper<SalesLedgerProduct> salesWrapper = new LambdaQueryWrapper<>();
+        salesWrapper.eq(SalesLedgerProduct::getType, 1);
+        salesWrapper.ge(SalesLedgerProduct::getRegisterDate, startOfMonth);
+        salesWrapper.le(SalesLedgerProduct::getRegisterDate, endOfMonth);
+
+        List<SalesLedgerProduct> salesProducts =
+                salesLedgerProductMapper.selectList(salesWrapper);
+
+        BigDecimal revenue = BigDecimal.ZERO;
+        if (!CollectionUtils.isEmpty(salesProducts)) {
+            for (SalesLedgerProduct s : salesProducts) {
+                if (s.getInvoiceAmount() != null) {
+                    revenue = revenue.add(s.getInvoiceAmount());
+                }
+            }
+        }
+
+        //  姣涘埄娑� & 鍒╂鼎鐜�
+        if (revenue.compareTo(BigDecimal.ZERO) > 0) {
+
+            // 姣涘埄娑� = 閿�鍞敹鍏� - 鍘熸潗鏂欐垚鏈�
+            BigDecimal grossProfit = revenue.subtract(rawMaterialCost);
+            dto.setGrossProfit(grossProfit);
+
+            // 鍒╂鼎鐜� = (閿�鍞敹鍏� - 鏈堝害鎬绘敮鍑�) / 閿�鍞敹鍏�
+            BigDecimal profit = revenue.subtract(monthlyExpenditure);
+            String profitMarginRate = profit
+                    .divide(revenue, 4, RoundingMode.HALF_UP)
+                    .multiply(BigDecimal.valueOf(100))
+                    .setScale(2, RoundingMode.HALF_UP)
+                    .toString();
+
+            dto.setProfitMarginRate(profitMarginRate);
+        }
+
+        return dto;
+    }
+
 }

--
Gitblit v1.9.3