已修改31个文件
已添加1个文件
782 ■■■■■ 文件已修改
basic-server/src/main/resources/db/migration/postgresql/V20250603102701__create_table_coal_info.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
basic-server/src/main/resources/db/migration/postgresql/V20250606163300__create_table_coal_field.sql 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
basic-server/src/main/resources/db/migration/postgresql/V20250606170900__create_table_coal_plan.sql 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
basic-server/src/main/resources/db/migration/postgresql/V20250606171000__create_table_coal_value.sql 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/dto/PendingInventoryDto.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/entity/PendingInventory.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/entity/ProductionInventory.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/entity/ProductionMaster.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/service/InventorySummaryService.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/service/impl/InputInventoryRecordServiceImpl.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/service/impl/InventorySummaryServiceImpl.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/service/impl/OfficialInventoryServiceImpl.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/service/impl/PendingInventoryServiceImpl.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/service/impl/ProductionMasterServiceImpl.java 248 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/service/impl/PurchaseRegistrationServiceImpl.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/utils/InventoryUtils.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/resources/db/migration/postgresql/V20250604101800__create_table_production.sql 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/resources/db/migration/postgresql/V20250604104500__create_table_pending_inventory.sql 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/resources/db/migration/postgresql/V20250604111200__create_table_official_inventory.sql 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/resources/db/migration/postgresql/V20250610161312__create_table_tree.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/resources/db/migration/postgresql/V20250610161313__create_table_archive.sql 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/resources/db/migration/postgresql/V20250613093400__create_table_production_master.sql 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/resources/db/migration/postgresql/V20250613112800__create_table_production_inventory.sql 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/pom.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/basic/service/impl/StorageBlobServiceImpl.java 78 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/resources/db/migration/postgresql/V20250525003427__create_table_storage_blob.sql 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
basic-server/src/main/resources/db/migration/postgresql/V20250603102701__create_table_coal_info.sql
@@ -6,7 +6,6 @@
    maintainer_id    int4         NOT NULL    DEFAULT 0,                 -- ç»´æŠ¤äººï¼Œä¸å…è®¸ä¸ºç©º
    maintenance_date DATE         NOT NULL,                              -- ç»´æŠ¤æ—¥æœŸï¼Œä¸å…è®¸ä¸ºç©º
    -- æ–°å¢žå­—段
    deleted          int4         NOT NULL    DEFAULT 0,                 -- æ˜¯å¦åˆ é™¤ï¼ˆè½¯åˆ é™¤æ ‡å¿—)
    create_by        VARCHAR(255),                                       -- åˆ›å»ºäºº
    create_time      TIMESTAMP WITHOUT TIME ZONE, -- åˆ›å»ºæ—¶é—´ï¼Œé»˜è®¤å½“前时间
basic-server/src/main/resources/db/migration/postgresql/V20250606163300__create_table_coal_field.sql
@@ -1,17 +1,16 @@
-- åˆ›å»ºç…¤è´¨ä¿¡æ¯è¡¨
CREATE TABLE coal_field
(
    id                BIGSERIAL PRIMARY KEY,                              -- ä¸»é”®ID,自动递增
    id                BIGSERIAL PRIMARY KEY,           -- ä¸»é”®ID,自动递增
    fields            VARCHAR(255) NOT NULL,           --煤质字段
    field_name        VARCHAR(255) NOT NULL,           -- ç…¤è´¨æè¿°
    field_description VARCHAR(255),                    -- ç…¤è´¨æè¿°
    fields            VARCHAR(255) NOT NULL,                              --煤质字段
    field_name        VARCHAR(255) NOT NULL,                              -- ç…¤è´¨æè¿°
    field_description VARCHAR(255) NOT NULL,                              -- ç…¤è´¨æè¿°
    deleted           INT          NOT NULL    DEFAULT 0,                 -- è½¯åˆ é™¤æ ‡å¿—:0=未删除,1=已删除
    create_by         VARCHAR(255),                                       -- åˆ›å»ºäººç”¨æˆ·å
    create_time       TIMESTAMP WITHOUT TIME ZONE, -- åˆ›å»ºæ—¶é—´ï¼Œé»˜è®¤å½“前时间
    update_by         VARCHAR(255),                                       -- æœ€åŽæ›´æ–°äººç”¨æˆ·å
    update_time       TIMESTAMP WITHOUT TIME ZONE  -- æœ€åŽæ›´æ–°æ—¶é—´ï¼Œé»˜è®¤å½“前时间
    deleted           INT          NOT NULL DEFAULT 0, -- è½¯åˆ é™¤æ ‡å¿—:0=未删除,1=已删除
    create_by         VARCHAR(255),                    -- åˆ›å»ºäººç”¨æˆ·å
    create_time       TIMESTAMP WITHOUT TIME ZONE,     -- åˆ›å»ºæ—¶é—´ï¼Œé»˜è®¤å½“前时间
    update_by         VARCHAR(255),                    -- æœ€åŽæ›´æ–°äººç”¨æˆ·å
    update_time       TIMESTAMP WITHOUT TIME ZONE      -- æœ€åŽæ›´æ–°æ—¶é—´ï¼Œé»˜è®¤å½“前时间
);
-- è¡¨æ³¨é‡Š
basic-server/src/main/resources/db/migration/postgresql/V20250606170900__create_table_coal_plan.sql
@@ -1,17 +1,17 @@
-- åˆ›å»ºç…¤è´¨ä¿¡æ¯è¡¨
CREATE TABLE coal_plan
(
    id          BIGSERIAL PRIMARY KEY,                              -- ä¸»é”®ID,自动递增
    id          BIGSERIAL PRIMARY KEY,           -- ä¸»é”®ID,自动递增
    plan        VARCHAR(255) NOT NULL,           --煤质方案
    field_ids   VARCHAR(255) NOT NULL,           --煤质方案字段id
    coal_fields VARCHAR(255) NOT NULL,           -- ç…¤è´¨æ–¹æ¡ˆå­—段
    scheme_desc VARCHAR(255),                    -- å­—段描述
    plan        VARCHAR(255) NOT NULL,                              --煤质方案
    field_ids        VARCHAR(255) NOT NULL,                              --煤质方案字段id
    coal_fields BIGINT       NOT NULL,                              -- ç…¤è´¨æ–¹æ¡ˆå­—段
    deleted     INT          NOT NULL    DEFAULT 0,                 -- è½¯åˆ é™¤æ ‡å¿—:0=未删除,1=已删除
    create_by   VARCHAR(255),                                       -- åˆ›å»ºäººç”¨æˆ·å
    create_time TIMESTAMP WITHOUT TIME ZONE, -- åˆ›å»ºæ—¶é—´ï¼Œé»˜è®¤å½“前时间
    update_by   VARCHAR(255),                                       -- æœ€åŽæ›´æ–°äººç”¨æˆ·å
    update_time TIMESTAMP WITHOUT TIME ZONE  -- æœ€åŽæ›´æ–°æ—¶é—´ï¼Œé»˜è®¤å½“前时间
    deleted     INT          NOT NULL DEFAULT 0, -- è½¯åˆ é™¤æ ‡å¿—:0=未删除,1=已删除
    create_by   VARCHAR(255),                    -- åˆ›å»ºäººç”¨æˆ·å
    create_time TIMESTAMP WITHOUT TIME ZONE,     -- åˆ›å»ºæ—¶é—´ï¼Œé»˜è®¤å½“前时间
    update_by   VARCHAR(255),                    -- æœ€åŽæ›´æ–°äººç”¨æˆ·å
    update_time TIMESTAMP WITHOUT TIME ZONE      -- æœ€åŽæ›´æ–°æ—¶é—´ï¼Œé»˜è®¤å½“前时间
);
-- è¡¨æ³¨é‡Š
@@ -22,6 +22,7 @@
COMMENT ON COLUMN coal_plan.plan IS '煤质方案';
COMMENT ON COLUMN coal_plan.coal_fields IS '煤质方案字段';
COMMENT ON COLUMN coal_plan.field_ids IS '煤质方案字段id';
COMMENT ON COLUMN coal_plan.scheme_desc IS '字段描述';
COMMENT ON COLUMN coal_plan.deleted IS '软删除标志,0=未删除,1=已删除';
COMMENT ON COLUMN coal_plan.create_by IS '创建该记录的用户';
COMMENT ON COLUMN coal_plan.create_time IS '记录创建时间';
basic-server/src/main/resources/db/migration/postgresql/V20250606171000__create_table_coal_value.sql
@@ -2,8 +2,7 @@
CREATE TABLE coal_value
(
    id          BIGSERIAL PRIMARY KEY,           -- ä¸»é”®ID,自动递增
    plan_id     VARCHAR(255) NOT NULL,           --关联煤质方案主键ID
    plan_id     BIGINT NOT NULL,           --关联煤质方案主键ID
    coal_value  VARCHAR(255) NOT NULL,           -- å­—段值
    fields      VARCHAR(255) NOT NULL,           -- å­—段
    field_name  VARCHAR(255) NOT NULL,           -- å­—段名
main-business/src/main/java/com/ruoyi/business/dto/PendingInventoryDto.java
@@ -33,4 +33,9 @@
     * å­—段名
     */
    private String fieldName;
    /**
     * å­—段名
     */
    private Integer type;
}
main-business/src/main/java/com/ruoyi/business/entity/PendingInventory.java
@@ -25,6 +25,13 @@
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     *
     * ç”Ÿäº§åŠ å·¥id
     */
    @TableField(value = "master_id")
    private Long masterId;
    /**
     * ä¾›è´§å•†åç§°
     */
@@ -35,11 +42,6 @@
     */
    @TableField(value = "coal_id")
    private Long coalId;
    /**
     * ç…¤ç§
     */
    @TableField(value = "coal")
    private String coal;
    /**
     * å•位
     */
main-business/src/main/java/com/ruoyi/business/entity/ProductionInventory.java
@@ -31,11 +31,7 @@
     */
    @TableField(value = "coal_id")
    private Long coalId;
    /**
     * ç…¤ç§
     */
    @TableField(value = "coal")
    private String coal;
    /**
     * åº“存数量
     */
main-business/src/main/java/com/ruoyi/business/entity/ProductionMaster.java
@@ -28,11 +28,6 @@
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * ç…¤ç§
     */
    @TableField(value = "coal")
    private String coal;
    /**
     * ç…¤ç§id
     */
    @TableField(value = "coal_id")
main-business/src/main/java/com/ruoyi/business/service/InventorySummaryService.java
@@ -1,9 +1,11 @@
package com.ruoyi.business.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.business.dto.OfficialInventoryDto;
import com.ruoyi.business.dto.PendingInventoryDto;
import com.ruoyi.business.entity.InventorySummary;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/**
 * <p>
@@ -21,4 +23,11 @@
     * @param officialInventoryDto æ­£å¼åº“å­˜
     */
    int updateInventory(PendingInventoryDto pendingInventoryDto, OfficialInventoryDto officialInventoryDto);
    /**
     * æ‰¹é‡æ›´æ–°åº“å­˜
     * @param ids åº“å­˜id
     * @return
     */
    int updateInventorySummary(List<Long> ids);
}
main-business/src/main/java/com/ruoyi/business/service/impl/InputInventoryRecordServiceImpl.java
@@ -11,6 +11,7 @@
import com.ruoyi.business.mapper.InventorySummaryMapper;
import com.ruoyi.business.service.InputInventoryRecordService;
import com.ruoyi.business.service.InventorySummaryService;
import com.ruoyi.business.utils.InventoryUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@@ -19,6 +20,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import static com.ruoyi.business.constant.InventoryRecordConstant.OFFICIAL_INVENTORY;
import static com.ruoyi.business.constant.InventoryRecordConstant.PENDING_INVENTORY;
@@ -37,6 +39,7 @@
    private final InputInventoryRecordMapper inputInventoryRecordMapper;
    private final InventorySummaryService inventorySummaryService;
    private final InventorySummaryMapper inventorySummaryMapper;
    private final InventoryUtils inventoryUtils;
    @Override
    public int insertInputInventoryRecord(PendingInventoryDto pendingInventoryDto, OfficialInventoryDto officialInventoryDto, BigDecimal quantity) {
@@ -62,36 +65,41 @@
    @Override
    public int deleteInputInventoryRecord(List<Long> ids) {
        if (CollectionUtils.isNotEmpty(ids)) {
            List<InputInventoryRecord> inputInventoryRecords = inputInventoryRecordMapper.selectBatchIds(ids);
            // æ ¹æ®id进行降序排序
            List<InputInventoryRecord> inputInventoryRecordList = inputInventoryRecords.stream()
                    .sorted(Comparator.comparing(InputInventoryRecord::getId).reversed())
                    .toList();
            String InventoryType = inputInventoryRecords.get(0).getInventoryType();
            List<Long> inventoryIds = inputInventoryRecords.stream().map(InputInventoryRecord::getInventoryId).toList();
            List<InventorySummary> inventorySummaries = inventorySummaryMapper.selectList(new LambdaQueryWrapper<InventorySummary>()
                    .eq(InventorySummary::getInventoryType, InventoryType)
                    .in(InventorySummary::getInventoryId, inventoryIds));
            if (CollectionUtils.isNotEmpty(inventorySummaries)) {
                List<InventorySummary> updates = new ArrayList<>();
                for (InventorySummary inventorySummary : inventorySummaries) {
                    for (InputInventoryRecord inputInventoryRecord : inputInventoryRecords) {
                        // å¦‚果节点上的入库记录id大于变更的id,说明此id前所有入库记录都要重新计算
                        if (Objects.equals(inventorySummary.getInventoryId(), inputInventoryRecord.getInventoryId()) && inventorySummary.getInputEndRecordId() > inputInventoryRecord.getId()) {
                            inventorySummary.setInputEndRecordId(inputInventoryRecord.getId());
                            updates.add(inventorySummary);
                        }
                    }
                }
                // é‡ç½®ä»–们的节点最终id
                inventorySummaryMapper.updateById(updates);
            }
        if (CollectionUtils.isEmpty(ids)) {
            throw new RuntimeException("请传入要删除的id记录");
        }
        // todo é‡æ–°è®¡ç®—节点库存
        // todo æ›´æ–°åº“存实施数据
        List<InputInventoryRecord> inputInventoryRecords = inputInventoryRecordMapper.selectBatchIds(ids);
        // æ ¹æ®id进行降序排序
        List<InputInventoryRecord> inputInventoryRecordList = inputInventoryRecords.stream()
                .sorted(Comparator.comparing(InputInventoryRecord::getId).reversed())
                .toList();
        String InventoryType = inputInventoryRecords.get(0).getInventoryType();
        List<Long> inventoryIds = inputInventoryRecords.stream().map(InputInventoryRecord::getInventoryId).toList().stream().distinct().collect(Collectors.toList());
        List<InventorySummary> inventorySummaries = inventorySummaryMapper.selectList(new LambdaQueryWrapper<InventorySummary>()
                .eq(InventorySummary::getInventoryType, InventoryType)
                .in(InventorySummary::getInventoryId, inventoryIds));
        if (CollectionUtils.isNotEmpty(inventorySummaries)) {
            List<InventorySummary> updates = new ArrayList<>();
            for (InventorySummary inventorySummary : inventorySummaries) {
                for (InputInventoryRecord inputInventoryRecord : inputInventoryRecordList) {
                    // å¦‚果节点上的入库记录id大于变更的id,说明此id前所有入库记录都要重新计算
                    if (Objects.equals(inventorySummary.getInventoryId(), inputInventoryRecord.getInventoryId()) && inventorySummary.getInputEndRecordId() > inputInventoryRecord.getId()) {
                        inventorySummary.setInputEndRecordId(inputInventoryRecord.getId());
                        updates.add(inventorySummary);
                    }
                }
            }
            // é‡ç½®èŠ‚ç‚¹æœ€ç»ˆid
            inventorySummaryMapper.updateById(updates);
        }
        // é‡æ–°è®¡ç®—节点库存
        inventorySummaryService.updateInventorySummary(ids);
        // æ›´æ–°åº“存实时数据
        inventoryUtils.updateInventoryByIds(inventoryIds, inputInventoryRecords.get(0).getInventoryType());
        // åˆ é™¤å…¥åº“记录
main-business/src/main/java/com/ruoyi/business/service/impl/InventorySummaryServiceImpl.java
@@ -87,4 +87,29 @@
            return officialInventoryMapper.updateById(officialInventory);
        }
    }
    @Override
    public int updateInventorySummary(List<Long> ids) {
        List<InventorySummary> inventorySummaries = inventorySummaryMapper.selectBatchIds(ids);
        for (InventorySummary inventorySummary : inventorySummaries) {
            // æŸ¥è¯¢èŠ‚ç‚¹ä¹‹å‰æ‰€æœ‰å…¥åº“è®°å½•
            List<InputInventoryRecord> inputInventoryRecords = inputInventoryRecordMapper.selectList(new LambdaQueryWrapper<InputInventoryRecord>()
                    .eq(InputInventoryRecord::getInventoryId, inventorySummary.getInventoryId())
                    .eq(InputInventoryRecord::getInventoryType, inventorySummary.getInventoryType())
                    .lt(InputInventoryRecord::getId, inventorySummary.getInputEndRecordId()));
            // æŸ¥è¯¢èŠ‚ç‚¹ä¹‹å‰æ‰€æœ‰å‡ºåº“è®°å½•
            List<OutputInventoryRecord> outputInventoryRecords = outputInventoryRecordMapper.selectList(new LambdaQueryWrapper<OutputInventoryRecord>()
                    .eq(OutputInventoryRecord::getInventoryId, inventorySummary.getInventoryId())
                    .eq(OutputInventoryRecord::getInventoryType, inventorySummary.getInventoryType())
                    .lt(OutputInventoryRecord::getId, inventorySummary.getOutputEndRecordId()));
            // è®¡ç®—库存数量
            BigDecimal inputQuantity = inputInventoryRecords.stream().map(InputInventoryRecord::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal outputQuantity = outputInventoryRecords.stream().map(OutputInventoryRecord::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
            inventorySummary.setInventoryQuantity(inputQuantity.subtract(outputQuantity));
        }
        return inventorySummaryMapper.updateById(inventorySummaries).size();
    }
}
main-business/src/main/java/com/ruoyi/business/service/impl/OfficialInventoryServiceImpl.java
@@ -155,6 +155,7 @@
        // 2. æ’入新库存记录
        OfficialInventory officialInventory = new OfficialInventory();
        BeanUtils.copyProperties(officialInventoryDto, officialInventory);
        officialInventory.setId(null);
        officialInventory.setMergeId(ids.toString());
        officialInventory.setRegistrantId(SecurityUtils.getLoginUser().getUser().getUserName());
        if (officialInventoryMapper.insert(officialInventory) <= 0) {
main-business/src/main/java/com/ruoyi/business/service/impl/PendingInventoryServiceImpl.java
@@ -168,6 +168,7 @@
                coalValue.setCoalValue(value);
                coalValue.setFields(key);
                coalValue.setFieldName(fieldName);
                coalValue.setType(String.valueOf(1));
                i = coalValueMapper.insert(coalValue);
            }
        }
main-business/src/main/java/com/ruoyi/business/service/impl/ProductionMasterServiceImpl.java
@@ -19,7 +19,6 @@
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -116,140 +115,157 @@
    @Override
    @Transactional
    public int addOrEditPM(ProductionMasterDto productionMasterDto) {
        // 1. ç›´æŽ¥è®¡ç®—聚合值,避免创建多余列表
        BigDecimal totalPurchasePrice = BigDecimal.ZERO;
        BigDecimal totalLaborCost = BigDecimal.ZERO;
        BigDecimal totalEnergyConsumptionCost = BigDecimal.ZERO;
        BigDecimal totalTotalCost = BigDecimal.ZERO;
        BigDecimal totalEquipmentDepreciation = BigDecimal.ZERO;
        BigDecimal totalProductionQuantity = BigDecimal.ZERO;
    public int addOrEditPM(ProductionMasterDto dto) {
        Long masterId = dto.getId();
        for (Production production : productionMasterDto.getProductionList()) {
            totalPurchasePrice = totalPurchasePrice.add(production.getPurchasePrice());
            totalLaborCost = totalLaborCost.add(production.getLaborCost());
            totalEnergyConsumptionCost = totalEnergyConsumptionCost.add(production.getEnergyConsumptionCost());
            totalTotalCost = totalTotalCost.add(production.getTotalCost());
            totalEquipmentDepreciation = totalEquipmentDepreciation.add(production.getEquipmentDepreciation());
            totalProductionQuantity = production.getProductionQuantity().add(totalProductionQuantity);
        // ç¼–辑场景:回滚旧库存并删除旧记录
        if (masterId != null) {
            rollbackOldInventory(masterId);
            deleteChildRecords(masterId);
        }
        //煤种字段
        List<Long> coalIds = productionMasterDto.getProductionList().stream()
                .map(Production::getCoalId)
                .collect(Collectors.toList());
        List<CoalInfo> coalInfos = coalInfoMapper.selectList(new LambdaQueryWrapper<CoalInfo>().in(CoalInfo::getId, coalIds));
        // æ ¡éªŒä½¿ç”¨é‡å¹¶å‡å°‘库存
        validateAndReduceInventory(dto.getProductionInventoryList());
        // 2. åˆ›å»ºä¸»è¡¨å¯¹è±¡
        ProductionMaster productionMaster = new ProductionMaster();
        productionMaster.setProductionQuantity(totalProductionQuantity);
        productionMaster.setTotalCost(totalTotalCost);
        productionMaster.setEquipmentDepreciation(totalEquipmentDepreciation);
        productionMaster.setEnergyConsumptionCost(totalEnergyConsumptionCost);
        productionMaster.setLaborCost(totalLaborCost);
        productionMaster.setCoal(coalInfos.stream().map(CoalInfo::getCoal).collect(Collectors.joining(",")));
        productionMaster.setCoalId(coalIds.stream().map(String::valueOf).collect(Collectors.joining(",")));
        // æž„造主表实体对象
        ProductionMaster master = buildProductionMaster(dto);
        Long masterId = productionMasterDto.getId();
        productionMaster.setId(masterId);
        // 3. ç»Ÿä¸€å­è¡¨å¤„理逻辑
        // æ’入或更新主表
        if (masterId == null) {
            productionMasterMapper.insert(productionMaster);
            masterId = productionMaster.getId(); // èŽ·å–æ–°ç”Ÿæˆçš„ID
            productionMasterMapper.insert(master);
            masterId = master.getId();
        } else {
            // åˆ é™¤å…³è”子表数据
            productionMapper.delete(new LambdaQueryWrapper<Production>()
                    .eq(Production::getProductionMasterId, masterId));
            productionInventoryMapper.delete(new LambdaQueryWrapper<ProductionInventory>()
                    .eq(ProductionInventory::getProductionMasterId, masterId));
            productionMasterMapper.updateById(productionMaster);
            master.setId(masterId);
            productionMasterMapper.updateById(master);
        }
        //库存更新
        for (ProductionInventory productionInventory : productionMasterDto.getProductionInventoryList()) {
            OfficialInventory officialInventory = officialInventoryMapper.selectById(productionInventory.getOfficialId());
            BigDecimal subtract = officialInventory.getInventoryQuantity().subtract(new BigDecimal(productionInventory.getUsedQuantity()));
            if (subtract.compareTo(BigDecimal.ZERO) < 0) {
                throw new BaseException("库存不足");
            }
            officialInventory.setInventoryQuantity(subtract);
            officialInventoryMapper.updateById(officialInventory);
        }
        // 4. æ‰¹é‡æ’入子表数据
        batchInsertProductions(masterId, productionMasterDto.getProductionList());
        batchInsertInventories(masterId, productionMasterDto.getProductionInventoryList());
        //5. æ’入到待入库
        for (Production production : productionMasterDto.getProductionList()) {
            PendingInventory pendingInventory = new PendingInventory();
            pendingInventory.setCoalId(production.getCoalId());
            pendingInventory.setInventoryQuantity(production.getProductionQuantity());
            pendingInventory.setSupplierName("生产加工入库");
            pendingInventory.setTotalPriceIncludingTax(production.getTotalCost());
            pendingInventory.setPriceIncludingTax(production.getPurchasePrice());
        }
        // æ‰¹é‡æ’入生产记录与库存记录
        batchInsertProductions(masterId, dto.getProductionList());
        batchInsertInventories(masterId, dto.getProductionInventoryList());
        // æ’入待入库数据
        insertPendingInventory(dto.getProductionList());
        return 1;
    }
    // æ‰¹é‡æ’入生产数据
    private void batchInsertProductions(Long masterId, List<Production> productions) {
        if (productions.isEmpty()) {
            return;
        }
        // 1. æ”¶é›†æ‰€æœ‰éœ€è¦æŸ¥è¯¢çš„coalId
        List<Long> coalIds = productions.stream()
                .map(Production::getCoalId)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
    /**
     * å›žæ»šæ—§çš„库存数据(将库存数量还原)
     */
    private void rollbackOldInventory(Long masterId) {
        List<ProductionInventory> oldInventories = productionInventoryMapper.selectList(
                new LambdaQueryWrapper<ProductionInventory>().eq(ProductionInventory::getProductionMasterId, masterId));
        // 2. æ‰¹é‡æŸ¥è¯¢coalInfo数据
        Map<Long, CoalInfo> coalInfoMap = coalIds.isEmpty() ?
                Collections.emptyMap() :
                coalInfoMapper.selectList(new LambdaQueryWrapper<CoalInfo>().in(CoalInfo::getId, coalIds))
                        .stream()
                        .collect(Collectors.toMap(CoalInfo::getId, Function.identity()));
        if (coalInfoMap.isEmpty()){
            throw new BaseException("煤种信息不存在");
        }
        // 3. å‡†å¤‡æ‰¹é‡æ’入数据
        List<Production> batchInsertList = productions.stream()
                .map(production -> {
                    Production p = new Production(); // åˆ›å»ºæ–°å¯¹è±¡é¿å…å‰¯ä½œç”¨
                    BeanUtils.copyProperties(production, p);
                    // å¤åˆ¶å¿…要字段
                    p.setProductionMasterId(masterId);
                    p.setCoalId(production.getCoalId());
                    return p;
                })
                .collect(Collectors.toList());
        if (!batchInsertList.isEmpty()) {
            for (Production production : batchInsertList) {
                production.setId(null);
                productionMapper.insert(production);
        for (ProductionInventory oldInv : oldInventories) {
            OfficialInventory inv = officialInventoryMapper.selectById(oldInv.getOfficialId());
            if (inv != null) {
                inv.setInventoryQuantity(inv.getInventoryQuantity().add(new BigDecimal(oldInv.getUsedQuantity())));
                officialInventoryMapper.updateById(inv);
            }
        }
    }
    // æ‰¹é‡æ’入库存数据
    private void batchInsertInventories(Long masterId, List<ProductionInventory> inventories) {
        List<ProductionInventory> insertList = inventories.stream()
                .peek(inv -> {
                    inv.setId(null);
                    inv.setProductionMasterId(masterId);
                })
                .collect(Collectors.toList());
    /**
     * åˆ é™¤æ—§çš„子表数据及待入库数据
     */
    private void deleteChildRecords(Long masterId) {
        productionMapper.delete(new LambdaQueryWrapper<Production>().eq(Production::getProductionMasterId, masterId));
        productionInventoryMapper.delete(new LambdaQueryWrapper<ProductionInventory>().eq(ProductionInventory::getProductionMasterId, masterId));
        pendingInventoryMapper.delete(new LambdaQueryWrapper<PendingInventory>().eq(PendingInventory::getMasterId, masterId));
    }
        if (!insertList.isEmpty()) {
            for (ProductionInventory inventory : inventories) {
                inventory.setId(null);
                inventory.setProductionMasterId(masterId);
                productionInventoryMapper.insert(inventory);
    /**
     * æ ¡éªŒæ¯æ¡ä½¿ç”¨é‡æ˜¯å¦è¶³å¤Ÿï¼Œå¹¶å‡å°‘正式库存数量
     */
    private void validateAndReduceInventory(List<ProductionInventory> inventoryList) {
        for (ProductionInventory inv : inventoryList) {
            OfficialInventory official = officialInventoryMapper.selectById(inv.getOfficialId());
            BigDecimal used = new BigDecimal(inv.getUsedQuantity());
            if (official.getInventoryQuantity().compareTo(used) < 0) {
                throw new BaseException("库存不足");
            }
            official.setInventoryQuantity(official.getInventoryQuantity().subtract(used));
            officialInventoryMapper.updateById(official);
        }
    }
    /**
     * æž„造主表对象并聚合字段值(如总成本、煤种等)
     */
    private ProductionMaster buildProductionMaster(ProductionMasterDto dto) {
        BigDecimal totalPurchase = BigDecimal.ZERO;
        BigDecimal totalLabor = BigDecimal.ZERO;
        BigDecimal totalEnergy = BigDecimal.ZERO;
        BigDecimal totalCost = BigDecimal.ZERO;
        BigDecimal totalDepreciation = BigDecimal.ZERO;
        BigDecimal totalQuantity = BigDecimal.ZERO;
        List<Long> coalIds = new ArrayList<>();
        for (Production p : dto.getProductionList()) {
            totalPurchase = totalPurchase.add(p.getPurchasePrice());
            totalLabor = totalLabor.add(p.getLaborCost());
            totalEnergy = totalEnergy.add(p.getEnergyConsumptionCost());
            totalCost = totalCost.add(p.getTotalCost());
            totalDepreciation = totalDepreciation.add(p.getEquipmentDepreciation());
            totalQuantity = totalQuantity.add(p.getProductionQuantity());
            coalIds.add(p.getCoalId());
        }
        List<CoalInfo> coalInfos = coalInfoMapper.selectList(new LambdaQueryWrapper<CoalInfo>().in(CoalInfo::getId, coalIds));
        ProductionMaster master = new ProductionMaster();
        master.setProductionQuantity(totalQuantity);
        master.setTotalCost(totalCost);
        master.setLaborCost(totalLabor);
        master.setEnergyConsumptionCost(totalEnergy);
        master.setEquipmentDepreciation(totalDepreciation);
        master.setCoalId(coalIds.stream().map(String::valueOf).collect(Collectors.joining(",")));
        return master;
    }
    /**
     * æ‰¹é‡æ’入生产子表数据
     */
    private void batchInsertProductions(Long masterId, List<Production> list) {
        if (list.isEmpty()) return;
        for (Production p : list) {
            Production copy = new Production();
            BeanUtils.copyProperties(p, copy);
            copy.setId(null);
            copy.setProductionMasterId(masterId);
            productionMapper.insert(copy);
        }
    }
    /**
     * æ‰¹é‡æ’入库存使用子表数据
     */
    private void batchInsertInventories(Long masterId, List<ProductionInventory> list) {
        if (list.isEmpty()) return;
        for (ProductionInventory p : list) {
            p.setId(null);
            p.setProductionMasterId(masterId);
            productionInventoryMapper.insert(p);
        }
    }
    /**
     * å°†åŠ å·¥äº§ç”Ÿçš„äº§å“è®°å½•åˆ°å¾…å…¥åº“è¡¨
     */
    private void insertPendingInventory(List<Production> list) {
        for (Production p : list) {
            PendingInventory pending = new PendingInventory();
            pending.setCoalId(p.getCoalId());
            pending.setInventoryQuantity(p.getProductionQuantity());
            pending.setSupplierName("生产加工入库");
            pending.setTotalPriceIncludingTax(p.getTotalCost());
            pending.setPriceIncludingTax(p.getPurchasePrice());
            pending.setPriceIncludingTax(p.getPurchasePrice());
            pendingInventoryMapper.insert(pending);
        }
    }
main-business/src/main/java/com/ruoyi/business/service/impl/PurchaseRegistrationServiceImpl.java
@@ -88,7 +88,6 @@
                // é‡‡è´­ç™»è®°æˆåŠŸï¼ŒåŒæ­¥åˆ›å»ºå¾…å…¥åº“è®°å½•
                PendingInventory pendingInventory = createPendingInventory(purchaseRegistration);
                pendingInventory.setSupplierName(supply.getSupplierName());
                pendingInventory.setCoal(coalInfo.getCoal());
                return pendingInventoryMapper.insert(pendingInventory);
            }
            return insertCount;
main-business/src/main/java/com/ruoyi/business/utils/InventoryUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
package com.ruoyi.business.utils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.business.constant.InventoryRecordConstant;
import com.ruoyi.business.entity.*;
import com.ruoyi.business.mapper.*;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.List;
@Component
public class InventoryUtils {
    @Autowired
    private InventorySummaryMapper inventorySummaryMapper;
    @Autowired
    private InputInventoryRecordMapper inputInventoryRecordMapper;
    @Autowired
    private OutputInventoryRecordMapper outputInventoryRecordMapper;
    @Autowired
    private OfficialInventoryMapper officialInventoryMapper;
    @Autowired
    private PendingInventoryMapper pendingInventoryMapper;
    /**
     * æ›´æ–°åº“å­˜
     */
    public void updateInventoryByIds(List<Long> ids, String type) {
        if (StringUtils.isEmpty(type)) {
            throw new RuntimeException("请指定库存类型");
        }
        for (Long id : ids) {
            // èŽ·å–åº“å­˜èŠ‚ç‚¹
            InventorySummary inventorySummary = inventorySummaryMapper.selectOne(new LambdaQueryWrapper<InventorySummary>()
                    .eq(InventorySummary::getInventoryId, id)
                    .eq(InventorySummary::getInventoryType, type));
            Long inputEndRecordId = inventorySummary != null ? inventorySummary.getInputEndRecordId() : 0L;
            Long outputEndRecordId = inventorySummary != null ? inventorySummary.getOutputEndRecordId() : 0L;
            List<InputInventoryRecord> inputInventoryRecords = inputInventoryRecordMapper.selectList(new LambdaQueryWrapper<InputInventoryRecord>()
                    .eq(InputInventoryRecord::getInventoryId, id)
                    .eq(InputInventoryRecord::getInventoryType, type)
                    .gt(InputInventoryRecord::getId, inputEndRecordId));
            List<OutputInventoryRecord> outputInventoryRecords = outputInventoryRecordMapper.selectList(new LambdaQueryWrapper<OutputInventoryRecord>()
                    .eq(OutputInventoryRecord::getInventoryId, id)
                    .eq(OutputInventoryRecord::getInventoryType, type)
                    .gt(OutputInventoryRecord::getId, outputEndRecordId));
            // èŽ·å–åº“å­˜æ•°æ®
            BigDecimal inputQuantity = inputInventoryRecords.stream().map(InputInventoryRecord::getQuantity).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            BigDecimal outputQuantity = outputInventoryRecords.stream().map(OutputInventoryRecord::getQuantity).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            // è®¡ç®—这次库存数据
            BigDecimal quantity = inventorySummary != null ? inventorySummary.getInventoryQuantity().add(inputQuantity.subtract(outputQuantity)) : inputQuantity.subtract(outputQuantity);
            if (inventorySummary != null) {
                if (type.equals(InventoryRecordConstant.OFFICIAL_INVENTORY)) {
                    OfficialInventory update = new OfficialInventory();
                    update.setInventoryQuantity(quantity);
                    update.setId(id);
                    officialInventoryMapper.updateById(update);
                } else {
                    PendingInventory update = new PendingInventory();
                    update.setInventoryQuantity(quantity);
                    update.setId(id);
                    pendingInventoryMapper.updateById(update);
                }
            }
        }
    }
}
main-business/src/main/resources/db/migration/postgresql/V20250604101800__create_table_production.sql
@@ -4,7 +4,6 @@
    id                      BIGSERIAL PRIMARY KEY,             -- ä¸»é”®ID
    production_master_id    BIGINT         NOT NULL DEFAULT 0, -- ç”Ÿäº§ä¸»è¡¨ID
    coal_id                 BIGINT         NOT NULL DEFAULT 0, -- ç…¤ç§ID
    coal                    VARCHAR(50)    NOT NULL,           -- ç…¤ç§
    production_quantity     INT            NOT NULL,           -- ç”Ÿäº§æ•°é‡
    labor_cost              DECIMAL(10, 2) NOT NULL,           -- äººå·¥æˆæœ¬
    energy_consumption_cost DECIMAL(10, 2) NOT NULL,           -- èƒ½è€—成本
@@ -28,7 +27,6 @@
-- æ·»åŠ å­—æ®µæ³¨é‡Š
COMMENT ON COLUMN production.id IS '主键ID';
COMMENT ON COLUMN production.coal_id IS '煤种ID';
COMMENT ON COLUMN production.coal IS '煤种';
COMMENT ON COLUMN production.production_quantity IS '生产数量';
COMMENT ON COLUMN production.labor_cost IS '人工成本';
COMMENT ON COLUMN production.energy_consumption_cost IS '能耗成本';
main-business/src/main/resources/db/migration/postgresql/V20250604104500__create_table_pending_inventory.sql
@@ -1,27 +1,33 @@
-- åˆ›å»ºå¾…入库表
CREATE TABLE pending_inventory
(
    id                        BIGSERIAL PRIMARY KEY,                             -- ä¸»é”®ID
    supplier_name             VARCHAR(255)   NOT NULL,                           -- ä¾›è´§å•†åç§°
    coal                      VARCHAR(50)    NOT NULL,                           -- ç…¤ç§
    unit                      VARCHAR(50)    NOT NULL,                           -- å•位
    inventory_quantity        DECIMAL(10, 2) NOT NULL,                           -- åº“存数量
    price_including_tax       DECIMAL(10, 2) NOT NULL,                           -- å•价(含税)
    total_price_including_tax DECIMAL(10, 2) NOT NULL,                           -- æ€»ä»·ï¼ˆå«ç¨Žï¼‰
    registrant                VARCHAR(50)    NOT NULL,                           -- ç™»è®°äºº
    registration_time         TIMESTAMP      NOT NULL DEFAULT CURRENT_TIMESTAMP, -- ç™»è®°æ—¶é—´
    id                         BIGSERIAL PRIMARY KEY,                            -- ä¸»é”®ID
    supplier_name              VARCHAR(255)               NOT NULL,              -- ä¾›è´§å•†åç§°
    coal                       VARCHAR(50)                NOT NULL,              -- ç…¤ç§
    unit                       VARCHAR(50)                NOT NULL,              -- å•位
    inventory_quantity         NUMERIC(10,2)              NOT NULL,              -- åº“存数量
    price_including_tax        NUMERIC(10,2)              NOT NULL,              -- å•价(含税)
    total_price_including_tax  NUMERIC(10,2)              NOT NULL,              -- æ€»ä»·ï¼ˆå«ç¨Žï¼‰
    registrant                 VARCHAR(50),                                      -- ç™»è®°äºº
    registration_time          TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP,          -- ç™»è®°æ—¶é—´
    price_excluding_tax        VARCHAR(255),                                     -- å•价(不含税)
    total_price_excluding_tax  VARCHAR(255),                                     -- æ€»ä»·ï¼ˆä¸å«ç¨Žï¼‰
    registrant_id              VARCHAR(32),                                      -- ç™»è®°äººID
    registration_date          DATE,                                             -- ç™»è®°æ—¥æœŸ
    supplier_id                BIGINT,                                           -- ä¾›è´§å•†ID
    coal_id                    BIGINT,                                            -- ç…¤ç§ID
    deleted                   INT            NOT NULL DEFAULT 0,                 -- è½¯åˆ é™¤æ ‡å¿—:0=未删除,1=已删除
    create_by                 VARCHAR(255),                                      -- åˆ›å»ºäººç”¨æˆ·å
    create_time               TIMESTAMP WITHOUT TIME ZONE,                       -- åˆ›å»ºæ—¶é—´ï¼Œé»˜è®¤å½“前时间
    update_by                 VARCHAR(255),                                      -- æœ€åŽæ›´æ–°äººç”¨æˆ·å
    update_time               TIMESTAMP WITHOUT TIME ZONE                        -- æœ€åŽæ›´æ–°æ—¶é—´ï¼Œé»˜è®¤å½“前时间
    deleted                    INTEGER         DEFAULT 0,                        -- è½¯åˆ é™¤æ ‡å¿—,0=未删除,1=已删除
    create_by                  VARCHAR(255),                                     -- åˆ›å»ºè¯¥è®°å½•的用户
    create_time                TIMESTAMP(6),                                     -- è®°å½•创建时间
    update_by                  VARCHAR(255),                                     -- æœ€åŽä¿®æ”¹è¯¥è®°å½•的用户
    update_time                TIMESTAMP(6)                                     -- è®°å½•最后更新时间
);
-- æ·»åŠ è¡¨æ³¨é‡Š
COMMENT ON TABLE pending_inventory IS '待入库表';
-- æ·»åŠ å­—æ®µæ³¨é‡Š
-- å­—段注释
COMMENT ON COLUMN pending_inventory.id IS '主键ID';
COMMENT ON COLUMN pending_inventory.supplier_name IS '供货商名称';
COMMENT ON COLUMN pending_inventory.coal IS '煤种';
@@ -31,9 +37,16 @@
COMMENT ON COLUMN pending_inventory.total_price_including_tax IS '总价(含税)';
COMMENT ON COLUMN pending_inventory.registrant IS '登记人';
COMMENT ON COLUMN pending_inventory.registration_time IS '登记时间';
COMMENT ON COLUMN pending_inventory.price_excluding_tax IS '单价(不含税)';
COMMENT ON COLUMN pending_inventory.total_price_excluding_tax IS '总价(不含税)';
COMMENT ON COLUMN pending_inventory.registrant_id IS '登记人ID';
COMMENT ON COLUMN pending_inventory.registration_date IS '登记日期';
COMMENT ON COLUMN pending_inventory.supplier_id IS '供货商ID';
COMMENT ON COLUMN pending_inventory.coal_id IS '煤种ID';
COMMENT ON COLUMN pending_inventory.deleted IS '软删除标志,0=未删除,1=已删除';
COMMENT ON COLUMN pending_inventory.create_by IS '创建该记录的用户';
COMMENT ON COLUMN pending_inventory.create_time IS '记录创建时间';
COMMENT ON COLUMN pending_inventory.update_by IS '最后修改该记录的用户';
COMMENT ON COLUMN pending_inventory.update_time IS '记录最后更新时间';
COMMENT ON COLUMN pending_inventory.update_time IS '记录最后更新时间';
main-business/src/main/resources/db/migration/postgresql/V20250604111200__create_table_official_inventory.sql
@@ -5,15 +5,16 @@
    supplier_name             VARCHAR(255)   NOT NULL,           -- ä¾›è´§å•†åç§°
    coal                      VARCHAR(50)    NOT NULL,           -- ç…¤ç§
    unit                      VARCHAR(50)    NOT NULL,           -- å•位
    inventory_quantity        DECIMAL(10, 2) NOT NULL,           -- åº“存数量
    inventory_quantity        DECIMAL(10, 0),                    -- åº“存数量
    price_including_tax       DECIMAL(10, 2) NOT NULL,           -- å•价(含税)
    total_price_including_tax DECIMAL(10, 2) NOT NULL,           -- æ€»ä»·ï¼ˆå«ç¨Žï¼‰
    price_excluding_tax       DECIMAL(10, 2) NOT NULL,           -- ä¸å«ç¨Žå•ä»·
    total_price_excluding_tax DECIMAL(10, 2) NOT NULL,           -- ä¸å«ç¨Žæ€»ä»·
    pending_replenishment     DECIMAL(10, 2) NOT NULL,           -- å¾…补库
    pending_replenishment     DECIMAL(10, 0),                    -- å¾…补库
    registrant_id             VARCHAR(50)    NOT NULL,           -- ç™»è®°äººid
    type             VARCHAR(50)    NOT NULL,           -- ç™»è®°äººid
    type                      VARCHAR(50),                       -- ç±»åž‹       1 é‡‡è´­/ 2 æ­£å¼   å…¥åº“
    pending_id                BIGINT,                            -- å¾…入库id
    merge_id                  VARCHAR(255),                            -- åˆå¹¶id
    registration_date         TIMESTAMP WITHOUT TIME ZONE,
    deleted                   INT            NOT NULL DEFAULT 0, -- è½¯åˆ é™¤æ ‡å¿—:0=未删除,1=已删除
@@ -37,6 +38,7 @@
COMMENT ON COLUMN official_inventory.pending_replenishment IS '待补库';
COMMENT ON COLUMN official_inventory.registrant_id IS '登记人id';
COMMENT ON COLUMN official_inventory.registration_date IS '登记日期';
COMMENT ON COLUMN official_inventory.merge_id IS '合并id';
COMMENT ON COLUMN official_inventory.deleted IS '软删除标志,0=未删除,1=已删除';
COMMENT ON COLUMN official_inventory.create_by IS '创建该记录的用户';
main-business/src/main/resources/db/migration/postgresql/V20250610161312__create_table_tree.sql
@@ -2,7 +2,6 @@
CREATE TABLE tree
(
    id          BIGSERIAL PRIMARY KEY,                              -- ä¸»é”®ID,自动递增
    name        VARCHAR(255) NOT NULL,                              -- åç§°
    parent_id   BIGINT,                                             -- çˆ¶id
main-business/src/main/resources/db/migration/postgresql/V20250610161313__create_table_archive.sql
@@ -1,21 +1,20 @@
-- åˆ›å»ºæ¡£æ¡ˆè¡¨
CREATE TABLE archive
(
    id          BIGSERIAL PRIMARY KEY,                              -- ä¸»é”®ID,自动递增
    id          BIGSERIAL PRIMARY KEY,                                 -- ä¸»é”®ID
    name        VARCHAR(255)                          NOT NULL,        -- æ¡£æ¡ˆåç§°
    type        VARCHAR(255)                          NOT NULL,        -- æ¡£æ¡ˆç±»åž‹ï¼Œä¾‹å¦‚:合同、报告、证件等
    status      VARCHAR(50)                           NOT NULL,        -- æ¡£æ¡ˆçŠ¶æ€ï¼Œä¾‹å¦‚ï¼šæœ‰æ•ˆã€è¿‡æœŸã€ä½œåºŸ
    name        VARCHAR(255) NOT NULL,                              -- æ¡£æ¡ˆåç§°
    type        VARCHAR(255) NOT NULL,                              -- æ¡£æ¡ˆç±»åž‹ï¼ˆå¦‚:合同、报告、证件等)
    status      VARCHAR(50)  NOT NULL,                              -- çŠ¶æ€ï¼ˆå¦‚ï¼šæœ‰æ•ˆã€è¿‡æœŸã€ä½œåºŸï¼‰
    deleted     INT          NOT NULL    DEFAULT 0,                 -- è½¯åˆ é™¤æ ‡å¿—:0=未删除,1=已删除
    create_by   VARCHAR(255),                                       -- åˆ›å»ºäººç”¨æˆ·å
    create_time TIMESTAMP WITHOUT TIME ZONE, -- åˆ›å»ºæ—¶é—´ï¼Œé»˜è®¤å½“前时间
    update_by   VARCHAR(255),                                       -- æœ€åŽæ›´æ–°äººç”¨æˆ·å
    update_time TIMESTAMP WITHOUT TIME ZONE  -- æœ€åŽæ›´æ–°æ—¶é—´ï¼Œé»˜è®¤å½“前时间-- ç™»è®°æ—¥æœŸ
    deleted     INTEGER                     DEFAULT 0 NOT NULL,        -- è½¯åˆ é™¤æ ‡å¿—,0=未删除,1=已删除
    create_by   VARCHAR(255),                                          -- åˆ›å»ºè¯¥è®°å½•的用户
    create_time TIMESTAMP(6),                                          -- è®°å½•创建时间,默认当前时间
    update_by   VARCHAR(255),                                          -- æœ€åŽä¿®æ”¹è¯¥è®°å½•的用户
    update_time TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP, -- æœ€åŽæ›´æ–°æ—¶é—´ï¼Œé»˜è®¤å½“前时间
    tree_id     BIGINT                                                 -- æ ‘节点ID
);
-- è¡¨æ³¨é‡Š
COMMENT ON TABLE archive IS '档案信息表,记录系统中各类档案的基本信息';
COMMENT ON TABLE archive IS '档案信息表';
-- å­—段注释
COMMENT ON COLUMN archive.id IS '主键ID';
@@ -26,4 +25,5 @@
COMMENT ON COLUMN archive.create_by IS '创建该记录的用户';
COMMENT ON COLUMN archive.create_time IS '记录创建时间';
COMMENT ON COLUMN archive.update_by IS '最后修改该记录的用户';
COMMENT ON COLUMN archive.update_time IS '记录最后更新时间';
COMMENT ON COLUMN archive.update_time IS '记录最后更新时间';
COMMENT ON COLUMN archive.tree_id IS '树节点ID';
main-business/src/main/resources/db/migration/postgresql/V20250613093400__create_table_production_master.sql
@@ -2,7 +2,7 @@
CREATE TABLE production_master
(
    id                      BIGSERIAL PRIMARY KEY,             -- ä¸»é”®ID
    coal                    VARCHAR(50)    NOT NULL,           -- ç…¤ç§
    coal_id                 BIGINT,                            -- ç…¤ç§ID
    production_quantity     INT            NOT NULL,           -- ç”Ÿäº§æ•°é‡
    labor_cost              DECIMAL(10, 2) NOT NULL,           -- äººå·¥æˆæœ¬
    energy_consumption_cost DECIMAL(10, 2) NOT NULL,           -- èƒ½è€—成本
@@ -24,7 +24,7 @@
-- æ·»åŠ å­—æ®µæ³¨é‡Š
COMMENT ON COLUMN production_master.id IS '主键ID';
COMMENT ON COLUMN production_master.coal IS '煤种';
COMMENT ON COLUMN production_master.coal_id IS '煤种ID';
COMMENT ON COLUMN production_master.production_quantity IS '生产数量';
COMMENT ON COLUMN production_master.labor_cost IS '人工成本';
COMMENT ON COLUMN production_master.energy_consumption_cost IS '能耗成本';
main-business/src/main/resources/db/migration/postgresql/V20250613112800__create_table_production_inventory.sql
@@ -4,10 +4,9 @@
    id                   BIGSERIAL PRIMARY KEY,          -- ä¸»é”®ID
    production_master_id BIGINT      NOT NULL DEFAULT 0, -- ç”Ÿäº§ä¸»è¡¨ID
    coal_id              BIGINT      NOT NULL DEFAULT 0, -- ç…¤ç§ID
    coal                 VARCHAR(50) NOT NULL,           -- ç…¤ç§
    official_id              BIGINT      NOT NULL DEFAULT 0, -- åº“å­˜ID
    inventory_quantity   INT         NOT NULL,           -- åº“存数量
    used_quantity        INT         NOT NULL,           -- ä½¿ç”¨æ•°é‡
    deleted              INT         NOT NULL DEFAULT 0, -- è½¯åˆ é™¤æ ‡å¿—:0=未删除,1=已删除
    create_by            VARCHAR(255),                   -- åˆ›å»ºäººç”¨æˆ·å
@@ -23,7 +22,7 @@
COMMENT ON COLUMN production_inventory.id IS '主键ID';
COMMENT ON COLUMN production_inventory.production_master_id IS '生产主表ID';
COMMENT ON COLUMN production_inventory.coal_id IS '煤种ID';
COMMENT ON COLUMN production_inventory.coal IS '煤种';
COMMENT ON COLUMN production_inventory.official_id IS '库存ID';
COMMENT ON COLUMN production_inventory.inventory_quantity IS '库存数量';
COMMENT ON COLUMN production_inventory.used_quantity IS '使用数量';
pom.xml
@@ -18,7 +18,6 @@
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>17</java.version>
        <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
        <mybatis-spring-boot.version>3.0.3</mybatis-spring-boot.version>
        <druid.version>1.2.23</druid.version>
        <bitwalker.version>1.21</bitwalker.version>
        <swagger.version>3.0.0</swagger.version>
ruoyi-common/pom.xml
@@ -119,12 +119,6 @@
            <artifactId>jakarta.servlet-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.16</version>
        </dependency>
        <!-- minio -->
        <dependency>
            <groupId>io.minio</groupId>
ruoyi-common/src/main/java/com/ruoyi/basic/service/impl/StorageBlobServiceImpl.java
@@ -10,16 +10,19 @@
import com.ruoyi.basic.mapper.StorageBlobMapper;
import com.ruoyi.basic.service.StorageBlobService;
import com.ruoyi.common.core.domain.MinioResult;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.exception.file.InvalidExtensionException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.MinioUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import lombok.RequiredArgsConstructor;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@@ -53,29 +56,76 @@
        List<StorageBlobDTO> storageBlobDTOs = new ArrayList<>();
        for (MultipartFile file : files) {
            try {
                validateFileExtension(file);
                MinioResult res = minioUtils.upload(bucketName, file, false);
                StorageBlobDTO dto = new StorageBlobDTO();
                dto.setContentType(file.getContentType());
                dto.setBucketFilename(res.getBucketFileName());
                dto.setOriginalFilename(res.getOriginalName());
                dto.setByteSize(file.getSize());
                dto.setKey(IdUtils.simpleUUID());
                dto.setBucketName(bucketName);
                dto.setUrl(minioUtils.getPreviewUrl(res.getBucketFileName(), bucketName, false));
                dto.setDownloadUrl(minioUtils.getDownloadUrl(res.getBucketFileName(), bucketName));
                if (type != null){
                    dto.setType(type);
                }
                // æ’入数据库
                StorageBlobDTO dto = buildStorageBlobDTO(file, res, bucketName, type);
                storageBlobMapper.insert(dto);
                storageBlobDTOs.add(dto);
            } catch (InvalidExtensionException e) {
                throw new RuntimeException("minio文件上传异常:" + e);
                throw new RuntimeException("不支持的文件类型:" + file.getOriginalFilename(), e);
            } catch (Exception e) {
                throw new RuntimeException("上传文件失败:" + file.getOriginalFilename(), e);
            }
        }
        return storageBlobDTOs;
    }
    private void validateFileExtension(MultipartFile file) throws InvalidExtensionException {
        String filename = file.getOriginalFilename();
        String extension = FilenameUtils.getExtension(filename).toLowerCase();
        List<String> allowedExtensions = Arrays.asList(
                // å›¾ç‰‡
                "jpg", "jpeg", "png", "gif", "bmp", "webp", "tiff", "ico", "svg",
                // æ–‡æ¡£
                "pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "rtf", "md", "csv", "odt",
                // è§†é¢‘
                "mp4", "mov", "avi", "wmv", "flv", "mkv", "webm", "mpeg", "3gp","MOV",
                // éŸ³é¢‘
                "mp3", "wav", "ogg", "aac", "flac", "m4a", "wma", "amr",
                // åŽ‹ç¼©åŒ…
                "zip", "rar", "7z", "tar", "gz", "bz2", "xz",
                // ç¼–程代码文件
                "java", "py", "js", "ts", "html", "css", "cpp", "c", "cs", "json", "xml", "sql", "yaml", "yml", "sh", "bat",
                // å®‰è£…程序 & äºŒè¿›åˆ¶
                "exe", "apk", "dmg", "msi", "bin", "iso",
                // è®¾è®¡ç±»
                "psd", "ai", "xd", "sketch", "fig"
        );
        if (!allowedExtensions.contains(extension)) {
            throw new BaseException("文件类型不被允许:" + extension);
        }
    }
    private StorageBlobDTO buildStorageBlobDTO(MultipartFile file, MinioResult res, String bucketName, Long type) {
        StorageBlobDTO dto = new StorageBlobDTO();
        dto.setContentType(file.getContentType());
        dto.setBucketFilename(res.getBucketFileName());
        dto.setOriginalFilename(res.getOriginalName());
        dto.setByteSize(file.getSize());
        dto.setKey(IdUtils.simpleUUID());
        dto.setBucketName(bucketName);
        dto.setUrl(minioUtils.getPreviewUrl(res.getBucketFileName(), bucketName, false));
        dto.setDownloadUrl(minioUtils.getDownloadUrl(res.getBucketFileName(), bucketName));
        if (type != null) {
            dto.setType(type);
        }
        return dto;
    }
    @Override
    public int deleteStorageBlobs(StorageAttachment attachment) {
        List<StorageAttachment> attachments = storageAttachmentMapper.selectList(new LambdaQueryWrapper<StorageAttachment>()
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java
@@ -1,11 +1,5 @@
package com.ruoyi.common.utils.file;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Objects;
import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException;
@@ -14,6 +8,13 @@
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.Seq;
import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Objects;
/**
 * æ–‡ä»¶ä¸Šä¼ å·¥å…·ç±»
@@ -25,7 +26,7 @@
    /**
     * é»˜è®¤å¤§å° 50M
     */
    public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024L;
    public static final long DEFAULT_MAX_SIZE = 1000 * 1024 * 1024L;
    /**
     * é»˜è®¤çš„æ–‡ä»¶åæœ€å¤§é•¿åº¦ 100
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java
@@ -22,9 +22,9 @@
    public static final String[] FLASH_EXTENSION = { "swf", "flv" };
    public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
            "asf", "rm", "rmvb" };
            "asf", "rm", "rmvb","mov","MOV" };
    public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" };
    public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb","mov","MOV" };
    public static final String[] DEFAULT_ALLOWED_EXTENSION = {
            // å›¾ç‰‡
@@ -34,7 +34,7 @@
            // åŽ‹ç¼©æ–‡ä»¶
            "rar", "zip", "gz", "bz2",
            // è§†é¢‘格式
            "mp4", "avi", "rmvb",
            "mp4", "avi", "rmvb","mov","MOV",
            // pdf
            "pdf" };
ruoyi-common/src/main/resources/db/migration/postgresql/V20250525003427__create_table_storage_blob.sql
@@ -3,22 +3,32 @@
CREATE TABLE storage_blob
(
    id                bigserial PRIMARY KEY,
    create_time        timestamp    default now() NOT NULL,
    create_time       timestamp    default now() NOT NULL,
    key               varchar(150) DEFAULT ''    NOT NULL,
    content_type      varchar(100) DEFAULT ''    NOT NULL,
    original_filename varchar(255) DEFAULT ''    NOT NULL,
    bucket_filename   varchar(255) DEFAULT ''    NOT NULL,
    bucket_name       varchar(255) DEFAULT ''    NOT NULL,
    byte_size         bigint       DEFAULT 0     NOT NULL,
    type              varchar(150),
    UNIQUE (key)
);
COMMENT ON TABLE storage_blob IS '通用文件上传的附件信息';
COMMENT
ON TABLE storage_blob IS '通用文件上传的附件信息';
COMMENT ON COLUMN storage_blob.key IS '资源id';
COMMENT ON COLUMN storage_blob.content_type IS '资源类型,例如JPG图片的资源类型为image/jpg';
COMMENT ON COLUMN storage_blob.original_filename IS '原文件名称';
COMMENT ON COLUMN storage_blob.bucket_filename IS '存储桶中文件名';
COMMENT ON COLUMN storage_blob.bucket_name IS '存储桶名';
COMMENT ON COLUMN storage_blob.byte_size IS '资源尺寸(字节)';
COMMENT
ON COLUMN storage_blob.key IS '资源id';
COMMENT
ON COLUMN storage_blob.content_type IS '资源类型,例如JPG图片的资源类型为image/jpg';
COMMENT
ON COLUMN storage_blob.original_filename IS '原文件名称';
COMMENT
ON COLUMN storage_blob.bucket_filename IS '存储桶中文件名';
COMMENT
ON COLUMN storage_blob.bucket_name IS '存储桶名';
COMMENT
ON COLUMN storage_blob.byte_size IS '资源尺寸(字节)';
COMMENT
ON COLUMN storage_blob.type IS '0生产前 1生产后 2生产问题';
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java
@@ -1,12 +1,5 @@
package com.ruoyi.framework.web.service;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
@@ -14,12 +7,7 @@
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.user.BlackListException;
import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.CaptchaExpireException;
import com.ruoyi.common.exception.user.UserNotExistsException;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.exception.user.*;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils;
@@ -28,6 +16,13 @@
import com.ruoyi.framework.security.context.AuthenticationContextHolder;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysUserService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
/**
 * ç™»å½•校验方法
@@ -174,8 +169,8 @@
    {
        SysUser sysUser = new SysUser();
        sysUser.setUserId(userId);
        sysUser.setLoginIp(IpUtils.getIpAddr());
        sysUser.setLoginDate(DateUtils.getNowDate());
//        sysUser.setLoginIp(IpUtils.getIpAddr());
//        sysUser.setLoginDate(DateUtils.getNowDate());
        userService.updateUserProfile(sysUser);
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
@@ -1,6 +1,5 @@
package com.ruoyi.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.common.core.domain.entity.SysUser;
import org.apache.ibatis.annotations.Param;
@@ -8,14 +7,13 @@
/**
 * ç”¨æˆ·è¡¨ æ•°æ®å±‚
 *
 *
 * @author ruoyi
 */
public interface SysUserMapper
extends BaseMapper<SysUser> {
public interface SysUserMapper{
    /**
     * æ ¹æ®æ¡ä»¶åˆ†é¡µæŸ¥è¯¢ç”¨æˆ·åˆ—表
     *
     *
     * @param sysUser ç”¨æˆ·ä¿¡æ¯
     * @return ç”¨æˆ·ä¿¡æ¯é›†åˆä¿¡æ¯
     */
@@ -23,7 +21,7 @@
    /**
     * æ ¹æ®æ¡ä»¶åˆ†é¡µæŸ¥è¯¢å·²é…ç”¨æˆ·è§’色列表
     *
     *
     * @param user ç”¨æˆ·ä¿¡æ¯
     * @return ç”¨æˆ·ä¿¡æ¯é›†åˆä¿¡æ¯
     */
@@ -31,7 +29,7 @@
    /**
     * æ ¹æ®æ¡ä»¶åˆ†é¡µæŸ¥è¯¢æœªåˆ†é…ç”¨æˆ·è§’色列表
     *
     *
     * @param user ç”¨æˆ·ä¿¡æ¯
     * @return ç”¨æˆ·ä¿¡æ¯é›†åˆä¿¡æ¯
     */
@@ -39,7 +37,7 @@
    /**
     * é€šè¿‡ç”¨æˆ·åæŸ¥è¯¢ç”¨æˆ·
     *
     *
     * @param userName ç”¨æˆ·å
     * @return ç”¨æˆ·å¯¹è±¡ä¿¡æ¯
     */
@@ -47,7 +45,7 @@
    /**
     * é€šè¿‡ç”¨æˆ·ID查询用户
     *
     *
     * @param userId ç”¨æˆ·ID
     * @return ç”¨æˆ·å¯¹è±¡ä¿¡æ¯
     */
@@ -55,7 +53,7 @@
    /**
     * æ–°å¢žç”¨æˆ·ä¿¡æ¯
     *
     *
     * @param user ç”¨æˆ·ä¿¡æ¯
     * @return ç»“æžœ
     */
@@ -63,7 +61,7 @@
    /**
     * ä¿®æ”¹ç”¨æˆ·ä¿¡æ¯
     *
     *
     * @param user ç”¨æˆ·ä¿¡æ¯
     * @return ç»“æžœ
     */
@@ -71,16 +69,16 @@
    /**
     * ä¿®æ”¹ç”¨æˆ·å¤´åƒ
     *
     *
     * @param userName ç”¨æˆ·å
     * @param avatar å¤´åƒåœ°å€
     * @param avatar   å¤´åƒåœ°å€
     * @return ç»“æžœ
     */
    public int updateUserAvatar(@Param("userName") String userName, @Param("avatar") String avatar);
    /**
     * é‡ç½®ç”¨æˆ·å¯†ç 
     *
     *
     * @param userName ç”¨æˆ·å
     * @param password å¯†ç 
     * @return ç»“æžœ
@@ -89,7 +87,7 @@
    /**
     * é€šè¿‡ç”¨æˆ·ID删除用户
     *
     *
     * @param userId ç”¨æˆ·ID
     * @return ç»“æžœ
     */
@@ -97,7 +95,7 @@
    /**
     * æ‰¹é‡åˆ é™¤ç”¨æˆ·ä¿¡æ¯
     *
     *
     * @param userIds éœ€è¦åˆ é™¤çš„用户ID
     * @return ç»“æžœ
     */
@@ -105,7 +103,7 @@
    /**
     * æ ¡éªŒç”¨æˆ·åç§°æ˜¯å¦å”¯ä¸€
     *
     *
     * @param userName ç”¨æˆ·åç§°
     * @return ç»“æžœ
     */
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
@@ -302,6 +302,8 @@
    @Override
    public int updateUserProfile(SysUser user) {
        return userMapper.updateUser(user);
//          return userMapper.update(user,new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserId, user.getUserId()));
    }
    /**