basic-server/src/main/resources/db/migration/postgresql/V20250606163300__create_table_coal_field.sql
@@ -1,16 +1,16 @@ -- 创建煤质信息表 CREATE TABLE coal_field ( id BIGSERIAL PRIMARY KEY, -- 主键ID,自动递增 fields VARCHAR(255) NOT NULL, --煤质字段 field_name VARCHAR(255) NOT NULL, -- 煤质描述 field_description VARCHAR(255) NOT NULL, -- 煤质描述 id BIGSERIAL PRIMARY KEY, -- 主键ID,自动递增 fields VARCHAR(255) NOT NULL, --煤质字段 field_name VARCHAR(255) NOT NULL, -- 煤质描述 field_description VARCHAR(255), -- 煤质描述 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
@@ -4,7 +4,7 @@ id BIGSERIAL PRIMARY KEY, -- 主键ID,自动递增 plan VARCHAR(255) NOT NULL, --煤质方案 field_ids VARCHAR(255) NOT NULL, --煤质方案字段id coal_fields BIGINT NOT NULL, -- 煤质方案字段 coal_fields VARCHAR(255) NOT NULL, -- 煤质方案字段 scheme_desc VARCHAR(255), -- 字段描述 deleted INT NOT NULL DEFAULT 0, -- 软删除标志:0=未删除,1=已删除 basic-server/src/main/resources/db/migration/postgresql/V20250606171000__create_table_coal_value.sql
@@ -2,7 +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/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/resources/db/migration/postgresql/V20250604111200__create_table_official_inventory.sql
@@ -5,16 +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 BIGINT, -- 合并id merge_id VARCHAR(255), -- 合并id registration_date TIMESTAMP WITHOUT TIME ZONE, deleted INT NOT NULL DEFAULT 0, -- 软删除标志:0=未删除,1=已删除 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/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
@@ -8,7 +8,6 @@ import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.exception.user.*; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.MessageUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.ip.IpUtils; @@ -170,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())); } /**