liu
17 小时以前 3e144b2eb8fc9447d2d092edb71db533d12abc52
src/main/java/com/ruoyi/staff/service/impl/StaffSalaryMainServiceImpl.java
@@ -15,6 +15,7 @@
import com.ruoyi.production.mapper.ProductionProductMainMapper;
import com.ruoyi.production.bean.dto.ProductionAccountDto;
import com.ruoyi.production.bean.dto.ProductionProductMainDto;
import com.ruoyi.production.enums.MajorCategoryEnum;
import com.ruoyi.production.pojo.ProductionAccount;
import com.ruoyi.technology.mapper.TechnologyOperationMapper;
import com.ruoyi.technology.pojo.TechnologyOperation;
@@ -361,7 +362,7 @@
    /**
     * 汇总员工当月月时间合计。
     * 规则:
     * 1. 小提琴 / 大提琴 / 贝斯:月产折合小提琴数量 / 工序标准数量 × 8 + 修补理工时
     * 1. 小提琴 / 中提琴 / 大提琴 / 贝斯:月产折合小提琴数量 / 工序标准数量 × 8 + 修补理工时
     * 2. 其他产品:当月实际产量 / 对应产品标准数量 × 8 + 修补理工时
     * 最终结果就是工资表里的月时间合计。
     */
@@ -380,11 +381,13 @@
            java.math.BigDecimal productStandardQuantity = toDecimal(item.get("productStandardQuantity"));
            java.math.BigDecimal violinFactor = toDecimal(item.get("violinFactor"));
            java.math.BigDecimal violaFactor = toDecimal(item.get("violaFactor"));
            java.math.BigDecimal midViolinFactor = toDecimal(item.get("midViolinFactor"));
            java.math.BigDecimal bassFactor = toDecimal(item.get("bassFactor"));
            String productName = item.get("productName") == null ? null : String.valueOf(item.get("productName"));
            // 归类依据用报工时已归集好的大类,不再拿产品名称做字符串比较
            String majorCategory = item.get("majorCategory") == null ? null : String.valueOf(item.get("majorCategory"));
            java.math.BigDecimal rowTotal = calculateOperationTime(
                    operationType,
                    productName,
                    majorCategory,
                    finishedNum,
                    workHour,
                    repairWorkHour,
@@ -392,6 +395,7 @@
                    productStandardQuantity,
                    violinFactor,
                    violaFactor,
                    midViolinFactor,
                    bassFactor
            );
            total = total.add(rowTotal).setScale(2, java.math.RoundingMode.HALF_UP);
@@ -402,11 +406,11 @@
    /**
     * 计算单条工序的折算工时。
     * 这里是月时间合计的核心分支:
     * - 小提琴 / 大提琴 / 贝斯:月产折合小提琴数量 / 工序标准数量 × 8 + 修补理工时;
     * - 其他产品:当月实际产量 / 对应产品标准数量 × 8 + 修补理工时。
     * - 小提琴 / 中提琴 / 大提琴 / 贝斯:月产折合小提琴数量 / 工序标准数量 × 8 + 修补理工时;
     * - 其他产品(含未归类报工):当月实际产量 / 对应产品标准数量 × 8 + 修补理工时。
     */
    private java.math.BigDecimal calculateOperationTime(Integer operationType,
                                                        String productName,
                                                        String majorCategory,
                                                        java.math.BigDecimal finishedNum,
                                                        java.math.BigDecimal workHour,
                                                        java.math.BigDecimal repairWorkHour,
@@ -414,12 +418,12 @@
                                                        java.math.BigDecimal productStandardQuantity,
                                                        java.math.BigDecimal violinFactor,
                                                        java.math.BigDecimal violaFactor,
                                                        java.math.BigDecimal midViolinFactor,
                                                        java.math.BigDecimal bassFactor) {
        if (isInstrumentProduct(productName)) {
            java.math.BigDecimal foldedQuantity = calculateFoldedQuantity(
                    finishedNum,
                    resolveInstrumentFactor(productName, violinFactor, violaFactor, bassFactor)
            );
        java.math.BigDecimal instrumentFactor = resolveInstrumentFactor(
                majorCategory, violinFactor, violaFactor, midViolinFactor, bassFactor);
        if (instrumentFactor != null) {
            java.math.BigDecimal foldedQuantity = calculateFoldedQuantity(finishedNum, instrumentFactor);
            if (standardQuantity.compareTo(java.math.BigDecimal.ZERO) <= 0) {
                return java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP);
            }
@@ -440,9 +444,8 @@
    }
    /**
     * 按产品名称和配置系数折算产量。
     * 只对小提琴、大提琴、贝斯三类产品生效,
     * 返回的是可用于后续工序标准数量换算的统一折合产量。
     * 按产量与折算率折算。是否属于乐器由调用方用 {@link MajorCategoryEnum} 判定,
     * 这里只做乘法,返回的是可用于后续工序标准数量换算的统一折合产量。
     */
    private java.math.BigDecimal calculateFoldedQuantity(java.math.BigDecimal quantity,
                                                         java.math.BigDecimal factor) {
@@ -452,26 +455,41 @@
        return quantity.multiply(factor).setScale(2, java.math.RoundingMode.HALF_UP);
    }
    private boolean isInstrumentProduct(String productName) {
        return "小提琴".equals(productName)
                || "大提琴".equals(productName)
                || "贝斯".equals(productName);
    /**
     * 判断报工大类是否属于乐器。未归类({@code major_category} 为 null,
     * 如零件/原料类工序产出)返回 false,由调用方走“其他产品”分支。
     */
    private boolean isInstrumentCategory(String majorCategory) {
        return MajorCategoryEnum.isInstrument(majorCategory);
    }
    private java.math.BigDecimal resolveInstrumentFactor(String productName,
    /**
     * 按报工大类取对应折算率。
     *
     * @return {@code null} 表示不是乐器大类(含未归类),调用方据此走“其他产品”分支
     */
    private java.math.BigDecimal resolveInstrumentFactor(String majorCategory,
                                                         java.math.BigDecimal violinFactor,
                                                         java.math.BigDecimal violaFactor,
                                                         java.math.BigDecimal midViolinFactor,
                                                         java.math.BigDecimal bassFactor) {
        if ("小提琴".equals(productName)) {
            return violinFactor;
        MajorCategoryEnum category = MajorCategoryEnum.of(majorCategory);
        if (category == null) {
            return null;
        }
        if ("大提琴".equals(productName)) {
            return violaFactor;
        switch (category) {
            case VIOLIN:
                return violinFactor;
            case VIOLA:
                return midViolinFactor;
            case CELLO:
                // violaFactor 的列名沿用了 viola(中提琴),历史语义实为【大提琴】折算率,勿改
                return violaFactor;
            case BASS:
                return bassFactor;
            default:
                return null;
        }
        if ("贝斯".equals(productName)) {
            return bassFactor;
        }
        return java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP);
    }
    private String formatEntryDate(java.util.Date entryDate) {
@@ -606,7 +624,7 @@
        Map<String, List<ProductionProductMainDto>> grouped = accountList.stream()
                .filter(item -> item != null && item.getSchedulingUserName() != null)
                .filter(item -> instrumentSheet == isInstrumentProduct(item.getProductName()))
                .filter(item -> instrumentSheet == isInstrumentCategory(item.getMajorCategory()))
                .sorted(java.util.Comparator
                        .comparing(ProductionProductMainDto::getSchedulingUserName, java.util.Comparator.nullsLast(String::compareTo))
                        .thenComparing(item -> StringUtils.isEmpty(item.getProductName()) ? "" : item.getProductName())
@@ -625,7 +643,9 @@
            Map<String, java.math.BigDecimal> convertedMap = new LinkedHashMap<>();
            Map<String, java.math.BigDecimal> foldedMap = new LinkedHashMap<>();
            Map<String, java.math.BigDecimal> violinQuantityMap = new LinkedHashMap<>();
            Map<String, java.math.BigDecimal> violaQuantityMap = new LinkedHashMap<>();
            // 变量名按实际乐器命名:midViolin=中提琴、cello=大提琴(注意勿与历史列名 viola_factor 混淆)
            Map<String, java.math.BigDecimal> midViolinQuantityMap = new LinkedHashMap<>();
            Map<String, java.math.BigDecimal> celloQuantityMap = new LinkedHashMap<>();
            Map<String, java.math.BigDecimal> bassQuantityMap = new LinkedHashMap<>();
            for (Map<String, Object> column : columns) {
                String key = String.valueOf(column.get("key"));
@@ -633,7 +653,8 @@
                convertedMap.put(key, java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP));
                foldedMap.put(key, java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP));
                violinQuantityMap.put(key, java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP));
                violaQuantityMap.put(key, java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP));
                midViolinQuantityMap.put(key, java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP));
                celloQuantityMap.put(key, java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP));
                bassQuantityMap.put(key, java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP));
            }
            for (ProductionProductMainDto item : staffItems) {
@@ -655,11 +676,14 @@
                foldedMap.merge(key, foldedQty, java.math.BigDecimal::add);
                convertedMap.merge(key, convertedHours, java.math.BigDecimal::add);
                if (instrumentSheet) {
                    if ("小提琴".equals(item.getProductName())) {
                    MajorCategoryEnum category = MajorCategoryEnum.of(item.getMajorCategory());
                    if (category == MajorCategoryEnum.VIOLIN) {
                        violinQuantityMap.merge(key, quantity, java.math.BigDecimal::add);
                    } else if ("大提琴".equals(item.getProductName())) {
                        violaQuantityMap.merge(key, quantity, java.math.BigDecimal::add);
                    } else if ("贝斯".equals(item.getProductName())) {
                    } else if (category == MajorCategoryEnum.VIOLA) {
                        midViolinQuantityMap.merge(key, quantity, java.math.BigDecimal::add);
                    } else if (category == MajorCategoryEnum.CELLO) {
                        celloQuantityMap.merge(key, quantity, java.math.BigDecimal::add);
                    } else if (category == MajorCategoryEnum.BASS) {
                        bassQuantityMap.merge(key, quantity, java.math.BigDecimal::add);
                    }
                }
@@ -680,17 +704,29 @@
                createDisplayNumberCell(qtyRow, dataCol, null, numStyle);
                if (instrumentSheet) {
                    Row violaRow = sheet.createRow(rowIdx++);
                    violaRow.setHeightInPoints(24f);
                    createEdgeCell(sheet.getWorkbook(), violaRow, 0, "", numStyle, false, false, true, true);
                    createTextCell(violaRow, 1, "月产大", numStyle);
                    Row midViolinRow = sheet.createRow(rowIdx++);
                    midViolinRow.setHeightInPoints(24f);
                    createEdgeCell(sheet.getWorkbook(), midViolinRow, 0, "", numStyle, false, false, true, true);
                    createTextCell(midViolinRow, 1, "月产中", numStyle);
                    dataCol = 2;
                    for (Map<String, Object> column : columns) {
                        String key = String.valueOf(column.get("key"));
                        createDisplayNumberCell(violaRow, dataCol++, violaQuantityMap.getOrDefault(key, java.math.BigDecimal.ZERO), numStyle);
                        createDisplayNumberCell(midViolinRow, dataCol++, midViolinQuantityMap.getOrDefault(key, java.math.BigDecimal.ZERO), numStyle);
                    }
                    createDisplayNumberCell(violaRow, dataCol++, null, numStyle);
                    createDisplayNumberCell(violaRow, dataCol, null, numStyle);
                    createDisplayNumberCell(midViolinRow, dataCol++, null, numStyle);
                    createDisplayNumberCell(midViolinRow, dataCol, null, numStyle);
                    Row celloRow = sheet.createRow(rowIdx++);
                    celloRow.setHeightInPoints(24f);
                    createEdgeCell(sheet.getWorkbook(), celloRow, 0, "", numStyle, false, false, true, true);
                    createTextCell(celloRow, 1, "月产大", numStyle);
                    dataCol = 2;
                    for (Map<String, Object> column : columns) {
                        String key = String.valueOf(column.get("key"));
                        createDisplayNumberCell(celloRow, dataCol++, celloQuantityMap.getOrDefault(key, java.math.BigDecimal.ZERO), numStyle);
                    }
                    createDisplayNumberCell(celloRow, dataCol++, null, numStyle);
                    createDisplayNumberCell(celloRow, dataCol, null, numStyle);
                    Row bassRow = sheet.createRow(rowIdx++);
                    bassRow.setHeightInPoints(24f);
@@ -795,7 +831,8 @@
                if (product == null || StringUtils.isEmpty(product.getProductName())) {
                    continue;
                }
                if (isInstrumentProduct(product.getProductName())) {
                // 乐器类产品归到“成品部门”sheet,不在这里作为琴盒部等的列
                if (MajorCategoryEnum.match(product.getProductName()) != null) {
                    continue;
                }
                String key = product.getProductName();
@@ -1037,17 +1074,23 @@
            return java.math.BigDecimal.ZERO;
        }
        java.math.BigDecimal quantity = item.getQuantity() == null ? java.math.BigDecimal.ZERO : item.getQuantity();
        String productName = item.getProductName();
        if ("小提琴".equals(productName)) {
            return operation.getViolinFactor() == null ? java.math.BigDecimal.ZERO : quantity.multiply(operation.getViolinFactor());
        MajorCategoryEnum category = MajorCategoryEnum.of(item.getMajorCategory());
        if (category == null) {
            return java.math.BigDecimal.ZERO;
        }
        if ("大提琴".equals(productName)) {
            return operation.getViolaFactor() == null ? java.math.BigDecimal.ZERO : quantity.multiply(operation.getViolaFactor());
        switch (category) {
            case VIOLIN:
                return operation.getViolinFactor() == null ? java.math.BigDecimal.ZERO : quantity.multiply(operation.getViolinFactor());
            case VIOLA:
                return operation.getMidViolinFactor() == null ? java.math.BigDecimal.ZERO : quantity.multiply(operation.getMidViolinFactor());
            case CELLO:
                // violaFactor 的列名沿用了 viola(中提琴),历史语义实为【大提琴】系数,勿改
                return operation.getViolaFactor() == null ? java.math.BigDecimal.ZERO : quantity.multiply(operation.getViolaFactor());
            case BASS:
                return operation.getBassFactor() == null ? java.math.BigDecimal.ZERO : quantity.multiply(operation.getBassFactor());
            default:
                return java.math.BigDecimal.ZERO;
        }
        if ("贝斯".equals(productName)) {
            return operation.getBassFactor() == null ? java.math.BigDecimal.ZERO : quantity.multiply(operation.getBassFactor());
        }
        return java.math.BigDecimal.ZERO;
    }
    private Map<String, java.math.BigDecimal> buildStandardQuantityMap(List<String> operationNames) {