gongchunyi
2 天以前 8b6616572a6c73c3ae25df1302880cd3817d17f7
feat: 进销存大屏数据分析接口
已添加2个文件
已修改7个文件
389 ■■■■■ 文件已修改
src/main/java/com/ruoyi/home/controller/HomeController.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/home/mapper/HomeMapper.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/home/service/HomeService.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java 204 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/home/HomeMapper.xml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/SalesLedgerProductMapper.xml 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/stock/StockInventoryMapper.xml 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/home/controller/HomeController.java
@@ -7,6 +7,7 @@
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.home.dto.*;
import com.ruoyi.home.service.HomeService;
import com.ruoyi.dto.MapDto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
@@ -96,6 +97,41 @@
        return AjaxResult.success(list);
    }
    @GetMapping("/productSalesAnalysis")
    @ApiOperation("各产品销售金额分析")
    public AjaxResult productSalesAnalysis() {
        List<MapDto> list = homeService.productSalesAnalysis();
        return AjaxResult.success(list);
    }
    @GetMapping("/rawMaterialPurchaseAmountRatio")
    @ApiOperation("原材料采购金额占比")
    public AjaxResult rawMaterialPurchaseAmountRatio(){
        List<MapDto> list = homeService.rawMaterialPurchaseAmountRatio();
        return AjaxResult.success(list);
    }
    @GetMapping("/salesPurchaseStorageProductCount")
    @ApiOperation("销售-采购-储存产品数")
    public AjaxResult salesPurchaseStorageProductCount(){
        List<MapDto> list = homeService.salesPurchaseStorageProductCount();
        return AjaxResult.success(list);
    }
    @GetMapping("/productInOutAnalysis")
    @ApiOperation("产品出入库分析")
    public AjaxResult productInOutAnalysis(@RequestParam(value = "type", defaultValue = "1") Integer type){
        List<Map<String, Object>> result = homeService.productInOutAnalysis(type);
        return AjaxResult.success(result);
    }
    @GetMapping("/productTurnoverDays")
    @ApiOperation("产品周转天数")
    public AjaxResult productTurnoverDays(){
        List<MapDto> list = homeService.productTurnoverDays();
        return AjaxResult.success(list);
    }
    /********************************************************营销采购类**************************************************/
    @GetMapping("/business")
    @Log(title = "销售-采购-库存数据", businessType = BusinessType.OTHER)
@@ -130,6 +166,7 @@
    }
    /********************************************************质量类*****************************************************/
    @GetMapping("/qualityStatistics")
    @Log(title = "质量分析", businessType = BusinessType.OTHER)
src/main/java/com/ruoyi/home/mapper/HomeMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package com.ruoyi.home.mapper;
import com.ruoyi.dto.MapDto;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
 * é¦–页统计 Mapper
 *
 * @author antigravity
 */
@Mapper
public interface HomeMapper {
    /**
     * æŸ¥è¯¢äº§å“å‘¨è½¬å¤©æ•°
     *
     * @return äº§å“å‘¨è½¬å¤©æ•°åˆ—表
     */
    List<MapDto> productTurnoverDays();
}
src/main/java/com/ruoyi/home/service/HomeService.java
@@ -1,9 +1,8 @@
package com.ruoyi.home.service;
import com.ruoyi.approve.pojo.ApproveProcess;
import com.ruoyi.dto.MapDto;
import com.ruoyi.home.dto.*;
import com.ruoyi.production.dto.ProductOrderDto;
import com.ruoyi.production.dto.ProductWorkOrderDto;
import java.text.ParseException;
import java.util.List;
@@ -45,4 +44,14 @@
    ProductCategoryDistributionDto productCategoryDistribution();
    List<CustomerContributionRankingDto> customerContributionRanking(Integer type);
    List<MapDto> productSalesAnalysis();
    List<MapDto> rawMaterialPurchaseAmountRatio();
    List<MapDto> salesPurchaseStorageProductCount();
    List<Map<String, Object>> productInOutAnalysis(Integer type);
    List<MapDto> productTurnoverDays();
}
src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -2,8 +2,8 @@
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.home.mapper.HomeMapper;
import com.ruoyi.approve.mapper.ApproveProcessMapper;
import com.ruoyi.approve.pojo.ApproveProcess;
import com.ruoyi.basic.mapper.CustomerMapper;
@@ -12,7 +12,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 +85,9 @@
    private SalesLedgerProductMapper salesLedgerProductMapper;
    @Autowired
    private StockInventoryMapper stockInventoryMapper;
    @Autowired
    private ProcurementRecordMapper procurementRecordStorageMapper;
    @Autowired
@@ -125,8 +127,9 @@
    private SysUserMapper sysUserMapper;
    @Autowired
    private SysUserDeptMapper sysUserDeptMapper;
    @Autowired
    private StockInventoryMapper stockInventoryMapper;
    private HomeMapper homeMapper;
    @Override
    public HomeBusinessDto business() {
@@ -900,4 +903,199 @@
        }
        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();
    }
}
src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java
@@ -12,6 +12,7 @@
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
/**
 * äº§å“ä¿¡æ¯Mapper接口
@@ -33,4 +34,10 @@
    IPage<ProcurementBusinessSummaryDto> procurementBusinessSummaryListPage(Page page,@Param("req") ProcurementBusinessSummaryDto procurementBusinessSummaryDto);
    List<LossProductModelDto> selectProductBomStructure(@Param("salesLedegerId") Long salesLedegerId);
    List<Map<String, Object>> selectProductSalesAnalysis();
    List<Map<String, Object>> selectRawMaterialPurchaseAnalysis();
    int selectProductCountByTypeAndDate(@Param("type") Integer type, @Param("startDate") String startDate, @Param("endDate") String endDate);
}
src/main/java/com/ruoyi/stock/mapper/StockInventoryMapper.java
@@ -13,6 +13,7 @@
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
/**
 * <p>
@@ -40,5 +41,11 @@
    BigDecimal selectTotal();
    int selectStorageProductCountByDate(@Param("startDate") String startDate, @Param("endDate") String endDate);
    List<Map<String, Object>> selectDailyStockInCounts(@Param("rootCategoryId") Long rootCategoryId, @Param("startDate") String startDate, @Param("endDate") String endDate);
    List<Map<String, Object>> selectDailyStockOutCounts(@Param("rootCategoryId") Long rootCategoryId, @Param("startDate") String startDate, @Param("endDate") String endDate);
    BigDecimal selectTotalByDate( @Param("now") LocalDate now);
}
src/main/resources/mapper/home/HomeMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
<?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.ruoyi.home.mapper.HomeMapper">
    <select id="productTurnoverDays" resultType="com.ruoyi.dto.MapDto">
        SELECT p.product_name                                                      AS name,
               DATEDIFF(IFNULL(MAX(sor.create_time), NOW()), MAX(sir.create_time)) AS value
        FROM product p
                 JOIN product_model pm ON p.id = pm.product_id
                 JOIN stock_in_record sir ON pm.id = sir.product_model_id
                 LEFT JOIN stock_out_record sor ON pm.id = sor.product_model_id
        GROUP BY p.id, p.product_name
    </select>
</mapper>
src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
@@ -147,7 +147,7 @@
                       LEFT JOIN product_structure ps ON pb.id = ps.bom_id
                       LEFT JOIN product_model pm1 ON pm1.id = ps.product_model_id
                       LEFT JOIN product p ON p.id = pm1.product_id
              WHERE sl.id = #{salesLedegerId}) A
               WHERE sl.id = #{salesLedegerId}) A
        group by a.model, a.product_name, a.unit
    </select>
    <select id="selectProduct" resultType="com.ruoyi.sales.pojo.SalesLedgerProduct">
@@ -158,4 +158,46 @@
        from product_model pm
        left join product p on pm.product_id = p.id
    </select>
</mapper>
    <select id="selectProductSalesAnalysis" resultType="java.util.LinkedHashMap">
        SELECT
            product_category as name,
            SUM( tax_inclusive_total_price ) AS value
        FROM
            sales_ledger_product
        WHERE
            type = 1
        GROUP BY
            product_category
        ORDER BY
            value DESC
        LIMIT 5
    </select>
    <select id="selectRawMaterialPurchaseAnalysis" resultType="java.util.LinkedHashMap">
        SELECT
            pr.product_name AS name,
            SUM( slp.tax_inclusive_total_price ) AS value
        FROM
            sales_ledger_product slp
            JOIN product pr ON slp.product_id = pr.id
        WHERE
            slp.type = 2
            AND pr.parent_id = ( SELECT id FROM product WHERE product_name = '原材料' )
        GROUP BY
            pr.id,
            pr.product_name
        ORDER BY
            value DESC
        LIMIT 5
    </select>
    <select id="selectProductCountByTypeAndDate" resultType="int">
        SELECT COUNT(*)
        FROM sales_ledger_product
        WHERE type = #{type}
        AND register_date &gt;= #{startDate}
        AND register_date &lt;= #{endDate}
    </select>
</mapper>
src/main/resources/mapper/stock/StockInventoryMapper.xml
@@ -185,7 +185,7 @@
        p.product_name
    </select>
    <select id="selectTotal" resultType="java.math.BigDecimal">
        select ifnull(sum(qualitity),0)
        select ifnull(sum(qualitity), 0)
        from stock_inventory
    </select>
    <select id="selectTotalByDate" resultType="java.math.BigDecimal">
@@ -195,4 +195,41 @@
           create_time &gt;= #{now} and create_time &lt; DATE_ADD(#{now}, INTERVAL 1 DAY)
    </select>
    <select id="selectStorageProductCountByDate" resultType="int">
        SELECT COUNT(*)
        FROM (SELECT create_time
              FROM stock_inventory
              UNION ALL
              SELECT create_time
              FROM stock_uninventory) combined
        WHERE create_time &gt;= #{startDate}
          AND create_time &lt;= #{endDate}
    </select>
    <select id="selectDailyStockInCounts" resultType="java.util.Map">
        SELECT DATE(sir.create_time) AS date,
               SUM(sir.stock_in_num) AS count
        FROM stock_in_record sir
                 JOIN product_model pm ON sir.product_model_id = pm.id
                 JOIN product p ON pm.product_id = p.id
        WHERE (p.parent_id = #{rootCategoryId} OR p.id = #{rootCategoryId})
          AND sir.create_time &gt;= #{startDate}
          AND sir.create_time &lt;= #{endDate}
        GROUP BY DATE(sir.create_time)
        ORDER BY DATE(sir.create_time) ASC
    </select>
    <select id="selectDailyStockOutCounts" resultType="java.util.Map">
        SELECT DATE(sor.create_time)  AS date,
               SUM(sor.stock_out_num) AS count
        FROM stock_out_record sor
                 JOIN product_model pm ON sor.product_model_id = pm.id
                 JOIN product p ON pm.product_id = p.id
        WHERE (p.parent_id = #{rootCategoryId} OR p.id = #{rootCategoryId})
          AND sor.create_time &gt;= #{startDate}
          AND sor.create_time &lt;= #{endDate}
        GROUP BY DATE(sor.create_time)
        ORDER BY DATE(sor.create_time) ASC
    </select>
</mapper>