From 93daa0916a6d76275886e704f6735cb91c3baf4e Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期三, 01 四月 2026 10:51:29 +0800
Subject: [PATCH] feat: 生产成本核算功能接口
---
src/main/java/com/ruoyi/production/service/IProductionSettlementBatchesService.java | 15 +
src/main/java/com/ruoyi/production/pojo/ProductionSettlementBatches.java | 18
src/main/java/com/ruoyi/production/pojo/ProductionSettlementDetails.java | 29 -
src/main/java/com/ruoyi/production/service/impl/ProductionSettlementBatchesServiceImpl.java | 466 +++++++++++++++++++++++++++++++++--
doc/宁夏-中盛建材.sql | 20 +
src/main/java/com/ruoyi/production/dto/ProductionSettlementDto.java | 31 ++
src/main/java/com/ruoyi/production/dto/ProductionSettlementTotalDto.java | 33 ++
src/main/java/com/ruoyi/production/controller/ProductionSettlementBatchesController.java | 55 +++
src/main/java/com/ruoyi/production/dto/ProductionSettlementDetailsDto.java | 41 +++
src/main/java/com/ruoyi/production/enums/ProductionSettlementEnum.java | 42 +++
10 files changed, 676 insertions(+), 74 deletions(-)
diff --git "a/doc/\345\256\201\345\244\217-\344\270\255\347\233\233\345\273\272\346\235\220.sql" "b/doc/\345\256\201\345\244\217-\344\270\255\347\233\233\345\273\272\346\235\220.sql"
index 3593ec5..67f4c6f 100644
--- "a/doc/\345\256\201\345\244\217-\344\270\255\347\233\233\345\273\272\346\235\220.sql"
+++ "b/doc/\345\256\201\345\244\217-\344\270\255\347\233\233\345\273\272\346\235\220.sql"
@@ -510,4 +510,22 @@
KEY `idx_batch_id` (`batch_id`),
KEY `idx_product_id` (`product_id`) COMMENT '鏂逛究鎸変骇鍝佹煡璇㈠巻鍙叉垚鏈姣�'
) ENGINE = InnoDB
- DEFAULT CHARSET = utf8mb4 COMMENT ='鐢熶骇鎴愭湰鏍哥畻瀵规瘮鏄庣粏琛�';
\ No newline at end of file
+ DEFAULT CHARSET = utf8mb4 COMMENT ='鐢熶骇鎴愭湰鏍哥畻瀵规瘮鏄庣粏琛�';
+
+ALTER TABLE `product-inventory-management-zsjc`.`production_settlement_batches`
+ DROP INDEX `idx_period`;
+
+ALTER TABLE `product-inventory-management-zsjc`.`production_settlement_batches`
+ DROP COLUMN `batch_name`,
+ DROP COLUMN `status`;
+
+ALTER TABLE `product-inventory-management-zsjc`.`production_settlement_details`
+ DROP COLUMN `actual_qty`,
+ DROP COLUMN `actual_price`,
+ DROP COLUMN `actual_total`,
+ DROP COLUMN `diff_qty`,
+ DROP COLUMN `diff_price`,
+ DROP COLUMN `diff_total`;
+
+ALTER TABLE `product-inventory-management-zsjc`.`production_settlement_batches`
+ MODIFY COLUMN `period_time` varchar(255) NULL DEFAULT NULL COMMENT '鏍哥畻褰掑睘骞存湀' AFTER `id`;
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/production/controller/ProductionSettlementBatchesController.java b/src/main/java/com/ruoyi/production/controller/ProductionSettlementBatchesController.java
index 64f222f..dfddd33 100644
--- a/src/main/java/com/ruoyi/production/controller/ProductionSettlementBatchesController.java
+++ b/src/main/java/com/ruoyi/production/controller/ProductionSettlementBatchesController.java
@@ -1,19 +1,22 @@
package com.ruoyi.production.controller;
+import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.production.dto.ProductionSettlementDetailsDto;
+import com.ruoyi.production.dto.ProductionSettlementDto;
+import com.ruoyi.production.dto.ProductionSettlementTotalDto;
+import com.ruoyi.production.dto.SettlementImportDto;
import com.ruoyi.production.service.IProductionSettlementBatchesService;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
-import java.time.LocalDate;
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+import java.util.Map;
/**
* <p>
@@ -33,9 +36,45 @@
@PostMapping("/import")
@ApiOperation("瀵煎叆鐢熶骇鎴愭湰鏍哥畻琛�")
- public AjaxResult importProductionSettlement(@RequestParam("file") MultipartFile file, @RequestParam(required = false) LocalDate periodTime, @RequestParam(required = false) String batchName) {
- productionSettlementBatchesService.importProductionSettlement(file, periodTime, batchName);
+ public AjaxResult importProductionSettlement(@RequestParam("file") MultipartFile file, ProductionSettlementDto dto) {
+ productionSettlementBatchesService.importProductionSettlement(file, dto);
return AjaxResult.success();
}
+ @GetMapping("/downloadTemplate")
+ @ApiOperation("鐢熶骇鎴愭湰鏍哥畻瀵煎叆妯℃澘")
+ public void export(HttpServletResponse response) {
+ ExcelUtil<SettlementImportDto> excelUtil = new ExcelUtil<>(SettlementImportDto.class);
+ excelUtil.importTemplateExcel(response, "鐢熶骇鎴愭湰鏍哥畻瀵煎叆妯℃澘");
+ }
+
+ @GetMapping("/getSettlement")
+ @ApiOperation("鑾峰彇鐢熶骇鎴愭湰鏍哥畻鏁版嵁")
+ public AjaxResult getSettlement(ProductionSettlementDto dto) {
+ Map<String, List<ProductionSettlementDetailsDto>> map = productionSettlementBatchesService.getSettlement(dto);
+ return AjaxResult.success(map);
+ }
+
+ @GetMapping("/getProductTypes")
+ @ApiOperation("鑾峰彇瀵瑰簲鏈堜唤瀵煎叆鐨勪骇鍝佺被鍒�")
+ public AjaxResult getProductTypes(ProductionSettlementDto dto) {
+ List<String> list = productionSettlementBatchesService.getProductTypes(dto);
+ return AjaxResult.success(list);
+ }
+
+ @GetMapping("/getSubjectNames")
+ @ApiOperation("鑾峰彇瀵瑰簲鏈堜唤瀵煎叆鐨勭鐩被鍒�")
+ public AjaxResult getSubjectNames(ProductionSettlementDto dto) {
+ List<String> list = productionSettlementBatchesService.getSubjectNames(dto);
+ return AjaxResult.success(list);
+ }
+
+ @GetMapping("/getTotalCosts")
+ @ApiOperation("鑾峰彇鎴愭湰鍚堣鏁版嵁")
+ public AjaxResult getTotalCosts(ProductionSettlementDto dto) {
+ ProductionSettlementTotalDto totalCosts = productionSettlementBatchesService.getTotalCosts(dto);
+ return AjaxResult.success(totalCosts);
+ }
+
+
}
diff --git a/src/main/java/com/ruoyi/production/dto/ProductionSettlementDetailsDto.java b/src/main/java/com/ruoyi/production/dto/ProductionSettlementDetailsDto.java
new file mode 100644
index 0000000..0e39fdc
--- /dev/null
+++ b/src/main/java/com/ruoyi/production/dto/ProductionSettlementDetailsDto.java
@@ -0,0 +1,41 @@
+package com.ruoyi.production.dto;
+
+import com.ruoyi.production.pojo.ProductionSettlementDetails;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+/**
+ * <br>
+ *
+ * </br>
+ *
+ * @author deslrey
+ * @version 1.0
+ * @since 2026/04/01 9:16
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class ProductionSettlementDetailsDto extends ProductionSettlementDetails {
+
+ @ApiModelProperty(value = "瀹為檯鑰楅噺")
+ private BigDecimal actualQty;
+
+ @ApiModelProperty(value = "瀹為檯鍗曚环")
+ private BigDecimal actualPrice;
+
+ @ApiModelProperty(value = "瀹為檯鎬绘垚鏈�")
+ private BigDecimal actualTotal;
+
+ @ApiModelProperty(value = "鑰楅噺宸紓")
+ private String diffQty;
+
+ @ApiModelProperty(value = "鍗曚环宸紓")
+ private String diffPrice;
+
+ @ApiModelProperty(value = "鎬绘垚鏈樊寮�")
+ private String diffTotal;
+
+}
diff --git a/src/main/java/com/ruoyi/production/dto/ProductionSettlementDto.java b/src/main/java/com/ruoyi/production/dto/ProductionSettlementDto.java
new file mode 100644
index 0000000..b9e7658
--- /dev/null
+++ b/src/main/java/com/ruoyi/production/dto/ProductionSettlementDto.java
@@ -0,0 +1,31 @@
+package com.ruoyi.production.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * <br>
+ * 鐢熶骇鎴愭湰鏍哥畻鏌ヨ鍙傛暟Dto
+ * </br>
+ *
+ * @author deslrey
+ * @version 1.0
+ * @since 2026/04/01
+ */
+@Data
+@ApiModel(value = "ProductionSettlementDto", description = "鐢熶骇鎴愭湰鏍哥畻鏌ヨ鍙傛暟Dto")
+public class ProductionSettlementDto {
+
+ @ApiModelProperty("鏍哥畻鎴愭湰鏃ユ湡")
+ private String periodTime;
+
+ @ApiModelProperty("鏍哥畻浜у搧绫诲瀷")
+ private String productType;
+
+ @ApiModelProperty("鏍哥畻鎴愭湰绉戠洰")
+ private String subjectName;
+
+ @ApiModelProperty("鏍哥畻鎴愭湰绫诲瀷")
+ private String costType;
+}
diff --git a/src/main/java/com/ruoyi/production/dto/ProductionSettlementTotalDto.java b/src/main/java/com/ruoyi/production/dto/ProductionSettlementTotalDto.java
new file mode 100644
index 0000000..26f2c75
--- /dev/null
+++ b/src/main/java/com/ruoyi/production/dto/ProductionSettlementTotalDto.java
@@ -0,0 +1,33 @@
+package com.ruoyi.production.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * <br>
+ * 鐢熶骇鎴愭湰鏍哥畻鍚堣鏁版嵁 Dto
+ * </br>
+ *
+ * @author deslrey
+ * @version 1.0
+ * @since 2026/04/01
+ */
+@Data
+@ApiModel(value = "ProductionSettlementTotalDto", description = "鐢熶骇鎴愭湰鏍哥畻鍚堣鏁版嵁")
+public class ProductionSettlementTotalDto {
+
+ @ApiModelProperty("鏍囧噯鎴愭湰鍚堣")
+ private BigDecimal budgetTotal;
+
+ @ApiModelProperty("瀹為檯鎴愭湰鍚堣")
+ private BigDecimal actualTotal;
+
+ @ApiModelProperty("宸紓鍚堣")
+ private BigDecimal diffTotal;
+
+ @ApiModelProperty("宸紓鐜�")
+ private String diffRate;
+}
diff --git a/src/main/java/com/ruoyi/production/enums/ProductionSettlementEnum.java b/src/main/java/com/ruoyi/production/enums/ProductionSettlementEnum.java
new file mode 100644
index 0000000..87e294b
--- /dev/null
+++ b/src/main/java/com/ruoyi/production/enums/ProductionSettlementEnum.java
@@ -0,0 +1,42 @@
+package com.ruoyi.production.enums;
+
+import lombok.Getter;
+
+/**
+ * <br>
+ * 浜у搧绫诲瀷-绉戠洰鍚嶇О
+ * </br>
+ *
+ * @author deslrey
+ * @version 1.0
+ * @since 2026/03/31
+ */
+@Getter
+public enum ProductionSettlementEnum {
+ MATERIAL_COST("鏉愭枡鎴愭湰", true),
+ ENERGY_COST("鑳借�楁垚鏈�", false);
+
+ private final String name;
+ private final boolean requiresProduct;
+
+ ProductionSettlementEnum(String name, boolean requiresProduct) {
+ this.name = name;
+ this.requiresProduct = requiresProduct;
+ }
+
+ /**
+ * 鍒ゆ柇鏄惁涓烘潗鏂欐垚鏈�
+ *
+ * @param name 绉戠洰鍚嶇О
+ * @return boolean
+ */
+ public static boolean isMaterialCost(String name) {
+ for (ProductionSettlementEnum e : values()) {
+ if (e.getName().equals(name)) {
+ return e.isRequiresProduct();
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/src/main/java/com/ruoyi/production/pojo/ProductionSettlementBatches.java b/src/main/java/com/ruoyi/production/pojo/ProductionSettlementBatches.java
index 87e8e97..208d4dd 100644
--- a/src/main/java/com/ruoyi/production/pojo/ProductionSettlementBatches.java
+++ b/src/main/java/com/ruoyi/production/pojo/ProductionSettlementBatches.java
@@ -1,11 +1,11 @@
package com.ruoyi.production.pojo;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.*;
+
import java.time.LocalDate;
-import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import java.io.Serializable;
+
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -24,7 +24,7 @@
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("production_settlement_batches")
-@ApiModel(value="ProductionSettlementBatches瀵硅薄", description="鐢熶骇鎴愭湰鏍哥畻鎵规涓昏〃")
+@ApiModel(value = "ProductionSettlementBatches瀵硅薄", description = "鐢熶骇鎴愭湰鏍哥畻鎵规涓昏〃")
public class ProductionSettlementBatches implements Serializable {
private static final long serialVersionUID = 1L;
@@ -34,21 +34,17 @@
private Long id;
@ApiModelProperty(value = "鏍哥畻褰掑睘鏈堜唤")
- private LocalDate periodTime;
-
- @ApiModelProperty(value = "鎵规鍚嶇О")
- private String batchName;
-
- @ApiModelProperty(value = "鐘舵�侊細0-浠呴绠楋紝1-缁撶畻璁$畻涓紝2-宸插畬鎴愮粨绠楋紝3-宸查攣瀹�")
- private Integer status;
+ private String periodTime;
@ApiModelProperty(value = "瀵煎叆鐢ㄦ埛")
private String createUser;
@ApiModelProperty(value = "鍒涘缓鏃ユ湡")
+ @TableField(value = "create_time", fill = FieldFill.INSERT)
private LocalDateTime createTime;
@ApiModelProperty(value = "绉熸埛ID")
+ @TableField(value = "tenant_id", fill = FieldFill.INSERT)
private Long tenantId;
diff --git a/src/main/java/com/ruoyi/production/pojo/ProductionSettlementDetails.java b/src/main/java/com/ruoyi/production/pojo/ProductionSettlementDetails.java
index a9fd729..d8e0db7 100644
--- a/src/main/java/com/ruoyi/production/pojo/ProductionSettlementDetails.java
+++ b/src/main/java/com/ruoyi/production/pojo/ProductionSettlementDetails.java
@@ -1,10 +1,11 @@
package com.ruoyi.production.pojo;
import java.math.BigDecimal;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
+
+import com.baomidou.mybatisplus.annotation.*;
+
import java.io.Serializable;
+
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -23,7 +24,7 @@
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("production_settlement_details")
-@ApiModel(value="ProductionSettlementDetails瀵硅薄", description="鐢熶骇鎴愭湰鏍哥畻瀵规瘮鏄庣粏琛�")
+@ApiModel(value = "ProductionSettlementDetails瀵硅薄", description = "鐢熶骇鎴愭湰鏍哥畻瀵规瘮鏄庣粏琛�")
public class ProductionSettlementDetails implements Serializable {
private static final long serialVersionUID = 1L;
@@ -56,26 +57,8 @@
@ApiModelProperty(value = "棰勭畻鎬绘垚鏈�")
private BigDecimal budgetTotal;
- @ApiModelProperty(value = "瀹為檯鑰楅噺")
- private BigDecimal actualQty;
-
- @ApiModelProperty(value = "瀹為檯鍗曚环")
- private BigDecimal actualPrice;
-
- @ApiModelProperty(value = "瀹為檯鎬绘垚鏈�")
- private BigDecimal actualTotal;
-
- @ApiModelProperty(value = "鑰楅噺宸紓")
- private BigDecimal diffQty;
-
- @ApiModelProperty(value = "鍗曚环宸紓")
- private BigDecimal diffPrice;
-
- @ApiModelProperty(value = "鎬绘垚鏈樊寮�")
- private BigDecimal diffTotal;
-
@ApiModelProperty(value = "绉熸埛ID")
+ @TableField(value = "tenant_id", fill = FieldFill.INSERT)
private Long tenantId;
-
}
diff --git a/src/main/java/com/ruoyi/production/service/IProductionSettlementBatchesService.java b/src/main/java/com/ruoyi/production/service/IProductionSettlementBatchesService.java
index 53c24b0..8fecd09 100644
--- a/src/main/java/com/ruoyi/production/service/IProductionSettlementBatchesService.java
+++ b/src/main/java/com/ruoyi/production/service/IProductionSettlementBatchesService.java
@@ -1,10 +1,14 @@
package com.ruoyi.production.service;
+import com.ruoyi.production.dto.ProductionSettlementDetailsDto;
+import com.ruoyi.production.dto.ProductionSettlementDto;
+import com.ruoyi.production.dto.ProductionSettlementTotalDto;
import com.ruoyi.production.pojo.ProductionSettlementBatches;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.multipart.MultipartFile;
-import java.time.LocalDate;
+import java.util.List;
+import java.util.Map;
/**
* <p>
@@ -16,6 +20,13 @@
*/
public interface IProductionSettlementBatchesService extends IService<ProductionSettlementBatches> {
- void importProductionSettlement(MultipartFile file, LocalDate periodTime, String batchName);
+ void importProductionSettlement(MultipartFile file, ProductionSettlementDto dto);
+ Map<String, List<ProductionSettlementDetailsDto>> getSettlement(ProductionSettlementDto dto);
+
+ List<String> getProductTypes(ProductionSettlementDto dto);
+
+ List<String> getSubjectNames(ProductionSettlementDto dto);
+
+ ProductionSettlementTotalDto getTotalCosts(ProductionSettlementDto dto);
}
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionSettlementBatchesServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionSettlementBatchesServiceImpl.java
index 7a6bcb2..b036f7e 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionSettlementBatchesServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionSettlementBatchesServiceImpl.java
@@ -4,22 +4,35 @@
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.energy.pojo.Energy;
+import com.ruoyi.energy.pojo.EnergyConsumptionDetail;
+import com.ruoyi.energy.service.EnergyConsumptionDetailService;
+import com.ruoyi.energy.service.EnergyService;
+import com.ruoyi.production.dto.ProductionSettlementDetailsDto;
+import com.ruoyi.production.dto.ProductionSettlementDto;
+import com.ruoyi.production.dto.ProductionSettlementTotalDto;
import com.ruoyi.production.dto.SettlementImportDto;
-import com.ruoyi.production.pojo.ProductionSettlementBatches;
+import com.ruoyi.production.enums.ProductionSettlementEnum;
+import com.ruoyi.production.pojo.*;
import com.ruoyi.production.mapper.ProductionSettlementBatchesMapper;
-import com.ruoyi.production.pojo.ProductionSettlementDetails;
-import com.ruoyi.production.service.IProductionSettlementBatchesService;
+import com.ruoyi.production.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.ruoyi.production.service.IProductionSettlementDetailsService;
+import com.ruoyi.project.system.domain.SysDictData;
+import com.ruoyi.project.system.mapper.SysDictDataMapper;
+import com.ruoyi.production.utils.UnitUtils;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
-import java.util.List;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
import java.util.stream.Collectors;
/**
@@ -31,58 +44,453 @@
* @since 2026-03-30
*/
@Service
+@Slf4j
public class ProductionSettlementBatchesServiceImpl extends ServiceImpl<ProductionSettlementBatchesMapper, ProductionSettlementBatches> implements IProductionSettlementBatchesService {
@Autowired
private IProductionSettlementDetailsService productionSettlementDetailsService;
+ @Autowired
+ private ProductMaterialService productMaterialService;
+
+ @Autowired
+ private ProductMaterialSkuService productMaterialSkuService;
+
+ @Autowired
+ private ProductionProductMainService productionProductMainService;
+
+ @Autowired
+ private ProductionProductInputService productionProductInputService;
+
+ @Autowired
+ private IProductionOrderStructureService productionOrderStructureService;
+
+ @Autowired
+ private ProductOrderService productOrderService;
+
+ @Autowired
+ private IProductionOrderRouteService productionOrderRouteService;
+
+ @Autowired
+ private EnergyService energyService;
+
+ @Autowired
+ private EnergyConsumptionDetailService energyConsumptionDetailService;
+
+ @Autowired
+ private SysDictDataMapper sysDictDataMapper;
+
@Override
@Transactional(rollbackFor = Exception.class)
- public void importProductionSettlement(MultipartFile file, LocalDate periodTime, String batchName) {
+ public void importProductionSettlement(MultipartFile file, ProductionSettlementDto dto) {
if (file == null || file.isEmpty()) {
throw new ServiceException("瀵煎叆澶辫触锛屾枃浠朵笉鑳戒负绌�");
}
+ // 鏍哥畻鏈堜唤锛屽鏋滀笉濉垯榛樿涓哄綋鍓嶆湀
+ String finalPeriodTime = parsePeriodTime(dto != null ? dto.getPeriodTime() : null);
List<SettlementImportDto> list;
try {
- ExcelUtil<SettlementImportDto> util = new ExcelUtil<>(SettlementImportDto.class);
- list = util.importExcel(file.getInputStream());
+ list = new ExcelUtil<>(SettlementImportDto.class).importExcel(file.getInputStream());
} catch (Exception e) {
- log.error("瀵煎叆鐢熶骇鎴愭湰鏍哥畻澶辫触", e);
+ log.error("瑙f瀽Excel澶辫触", e);
throw new ServiceException("瑙f瀽Excel鏂囦欢澶辫触锛�" + e.getMessage());
}
- if (StringUtils.isEmpty(list)) {
+ if (list == null || list.isEmpty()) {
throw new ServiceException("瀵煎叆鏁版嵁涓嶈兘涓虹┖锛�");
}
+ // 濡傛灉璇ユ湀鎵规宸插瓨鍦紝鍏堟竻闄ゆ棫鎵规鍙婂叾鏄庣粏锛屽啀閲嶆柊瀵煎叆
+ ProductionSettlementBatches existBatch = this.lambdaQuery()
+ .eq(ProductionSettlementBatches::getPeriodTime, finalPeriodTime)
+ .one();
+ if (existBatch != null) {
+ productionSettlementDetailsService.lambdaUpdate()
+ .eq(ProductionSettlementDetails::getBatchId, existBatch.getId())
+ .remove();
+ this.removeById(existBatch.getId());
+ log.info("宸叉竻闄� {} 鏈堜唤鐨勬棫鏍哥畻鏁版嵁锛屾壒娆D锛歿}", finalPeriodTime, existBatch.getId());
+ }
+
ProductionSettlementBatches batch = new ProductionSettlementBatches();
- batch.setBatchName(StringUtils.isNotEmpty(batchName) ? batchName : periodTime + "鎴愭湰鏍哥畻瀵煎叆");
- batch.setPeriodTime(periodTime != null ? periodTime : LocalDate.now());
- batch.setStatus(0);
- batch.setCreateTime(LocalDateTime.now());
+ batch.setPeriodTime(finalPeriodTime);
batch.setCreateUser(SecurityUtils.getUsername());
this.save(batch);
- List<ProductionSettlementDetails> detailList = list.stream().map(dto -> {
+ String lastProductType = "";
+ String lastCategory = "";
+
+ List<ProductionSettlementDetails> detailList = new ArrayList<>();
+ int rowIndex = 2;
+ for (SettlementImportDto importDto : list) {
ProductionSettlementDetails detail = new ProductionSettlementDetails();
detail.setBatchId(batch.getId());
- detail.setProductType(dto.getProductType());
- detail.setCategory(dto.getCategory());
- detail.setSubjectName(dto.getSubjectName());
- detail.setBudgetQty(dto.getBudgetQty());
- detail.setBudgetPrice(dto.getBudgetPrice());
- detail.setBudgetTotal(dto.getBudgetTotal());
- detail.setActualQty(BigDecimal.ZERO);
- detail.setActualPrice(BigDecimal.ZERO);
- detail.setActualTotal(BigDecimal.ZERO);
- detail.setDiffQty(BigDecimal.ZERO);
- detail.setDiffPrice(BigDecimal.ZERO);
- detail.setDiffTotal(BigDecimal.ZERO);
- return detail;
- }).collect(Collectors.toList());
+ if (StringUtils.isNotBlank(importDto.getProductType())) {
+ lastProductType = importDto.getProductType();
+ }
+ if (StringUtils.isNotBlank(importDto.getCategory())) {
+ lastCategory = importDto.getCategory();
+ }
+
+ detail.setProductType(lastProductType);
+ detail.setCategory(lastCategory);
+ detail.setSubjectName(importDto.getSubjectName());
+ detail.setBudgetQty(importDto.getBudgetQty() != null ? importDto.getBudgetQty() : BigDecimal.ZERO);
+ detail.setBudgetPrice(importDto.getBudgetPrice() != null ? importDto.getBudgetPrice() : BigDecimal.ZERO);
+ detail.setBudgetTotal(importDto.getBudgetTotal() != null ? importDto.getBudgetTotal() : BigDecimal.ZERO);
+
+ if (ProductionSettlementEnum.isMaterialCost(detail.getCategory())) {
+ ProductMaterial product = productMaterialService.lambdaQuery()
+ .eq(ProductMaterial::getProductName, importDto.getSubjectName())
+ .one();
+ if (product != null) {
+ detail.setProductId(product.getId());
+ } else {
+ log.warn("绗� {} 琛岋紝鏈壘鍒扮鐩骇鍝侊細{}", rowIndex, importDto.getSubjectName());
+ }
+ }
+ detailList.add(detail);
+ rowIndex++;
+ }
productionSettlementDetailsService.saveBatch(detailList);
}
+
+ @Override
+ public Map<String, List<ProductionSettlementDetailsDto>> getSettlement(ProductionSettlementDto dto) {
+ String finalPeriodTime = parsePeriodTime(dto != null ? dto.getPeriodTime() : null);
+
+ YearMonth yearMonth = YearMonth.parse(finalPeriodTime, DateTimeFormatter.ofPattern("yyyy-MM"));
+ LocalDate date = yearMonth.atDay(1);
+ ProductionSettlementBatches batch = this.lambdaQuery()
+ .eq(ProductionSettlementBatches::getPeriodTime, finalPeriodTime)
+ .one();
+
+ if (batch == null) {
+ return new HashMap<>();
+ }
+
+ String filterProductType = dto != null ? dto.getProductType() : null;
+ String filterSubjectName = dto != null ? dto.getSubjectName() : null;
+ String filterCostType = dto != null ? dto.getCostType() : null;
+
+ List<ProductionSettlementDetails> details = productionSettlementDetailsService.lambdaQuery()
+ .eq(ProductionSettlementDetails::getBatchId, batch.getId())
+ .eq(StringUtils.isNotBlank(filterProductType), ProductionSettlementDetails::getProductType, filterProductType)
+ .eq(StringUtils.isNotBlank(filterSubjectName), ProductionSettlementDetails::getSubjectName, filterSubjectName)
+ .eq(StringUtils.isNotBlank(filterCostType), ProductionSettlementDetails::getCategory, filterCostType)
+ .list();
+
+ List<ProductionSettlementDetailsDto> dtoList = details.stream().map(d -> {
+ ProductionSettlementDetailsDto detailDto = new ProductionSettlementDetailsDto();
+ detailDto.setBatchId(d.getBatchId());
+ detailDto.setProductId(d.getProductId());
+ detailDto.setProductType(d.getProductType());
+ detailDto.setCategory(d.getCategory());
+ detailDto.setSubjectName(d.getSubjectName());
+ detailDto.setBudgetQty(d.getBudgetQty());
+ detailDto.setBudgetPrice(d.getBudgetPrice());
+ detailDto.setBudgetTotal(d.getBudgetTotal());
+ return detailDto;
+ }).collect(Collectors.toList());
+
+ LocalDateTime start = date.atStartOfDay();
+ LocalDateTime end = start.plusMonths(1);
+
+ // 鑾峰彇浜у搧鍒嗙被
+ List<SysDictData> sysDictDataList = sysDictDataMapper.selectDictDataByType("product_type");
+ Map<Long, String> dictCodeMap = sysDictDataList.stream()
+ .collect(Collectors.toMap(SysDictData::getDictCode, SysDictData::getDictLabel, (k1, k2) -> k1));
+
+ // 鑾峰彇鐗╂枡娑堣�楁暟鎹�
+ List<ProductionProductMain> mains = productionProductMainService.lambdaQuery()
+ .ge(ProductionProductMain::getCreateTime, start)
+ .lt(ProductionProductMain::getCreateTime, end)
+ .list();
+
+ List<Long> mainIds = mains.stream().map(ProductionProductMain::getId).collect(Collectors.toList());
+ List<Long> orderIds = mains.stream().map(ProductionProductMain::getProductOrderId).distinct().collect(Collectors.toList());
+
+ Map<Long, List<ProductionProductInput>> inputsByMain = new HashMap<>();
+ Map<Long, ProductOrder> orderMap = new HashMap<>();
+ Map<Long, ProductionOrderRoute> orderRouteMap = new HashMap<>();
+ Map<Long, List<ProductionOrderStructure>> structuresByOrder = new HashMap<>();
+
+ if (!mainIds.isEmpty()) {
+ inputsByMain = productionProductInputService.lambdaQuery()
+ .in(ProductionProductInput::getProductMainId, mainIds)
+ .list()
+ .stream()
+ .collect(Collectors.groupingBy(ProductionProductInput::getProductMainId));
+ }
+
+ if (!orderIds.isEmpty()) {
+ orderMap = productOrderService.lambdaQuery()
+ .in(ProductOrder::getId, orderIds)
+ .list()
+ .stream()
+ .collect(Collectors.toMap(ProductOrder::getId, o -> o));
+
+ orderRouteMap = productionOrderRouteService.lambdaQuery()
+ .in(ProductionOrderRoute::getOrderId, orderIds)
+ .list()
+ .stream()
+ .collect(Collectors.toMap(ProductionOrderRoute::getOrderId, r -> r, (k1, k2) -> k1));
+
+ structuresByOrder = productionOrderStructureService.lambdaQuery()
+ .in(ProductionOrderStructure::getOrderId, orderIds)
+ .list()
+ .stream()
+ .collect(Collectors.groupingBy(ProductionOrderStructure::getOrderId));
+ }
+
+ // 鑾峰彇鑳借�楁暟鎹�
+ LocalDate startDate = date;
+ LocalDate endDate = date.plusMonths(1);
+ List<EnergyConsumptionDetail> energyDetails = energyConsumptionDetailService.lambdaQuery()
+ .ge(EnergyConsumptionDetail::getMeterReadingDate, startDate)
+ .lt(EnergyConsumptionDetail::getMeterReadingDate, endDate)
+ .list();
+
+ List<Energy> energies = energyService.list();
+ Map<String, Energy> energyMap = new HashMap<>();
+ for (Energy energy : energies) {
+ if (energy.getEnergyName() != null) {
+ energyMap.put(energy.getEnergyName(), energy);
+ }
+ }
+
+ List<ProductMaterialSku> allSkus = productMaterialSkuService.list();
+ Map<Long, Long> skuToMaterialMap = allSkus.stream()
+ .filter(sku -> sku.getProductId() != null)
+ .collect(Collectors.toMap(ProductMaterialSku::getId, ProductMaterialSku::getProductId, (k1, k2) -> k1));
+
+ // 璁$畻瀹為檯鍊�
+ for (ProductionSettlementDetailsDto detail : dtoList) {
+ BigDecimal actualQty = BigDecimal.ZERO;
+ BigDecimal actualTotalCost = BigDecimal.ZERO;
+
+ if (ProductionSettlementEnum.isMaterialCost(detail.getCategory())) {
+ if (detail.getProductId() != null) {
+ for (ProductionProductMain main : mains) {
+ ProductOrder order = orderMap.get(main.getProductOrderId());
+ if (order == null) continue;
+
+ String targetType = detail.getProductType() != null ? detail.getProductType().trim() : "";
+ boolean typeMatch = "缁煎悎".equals(targetType);
+
+ if (!typeMatch) {
+ ProductionOrderRoute route = orderRouteMap.get(main.getProductOrderId());
+ if (route != null && route.getDictCode() != null) {
+ String dictLabel = dictCodeMap.get(route.getDictCode());
+ if (dictLabel != null && dictLabel.trim().equals(targetType)) {
+ typeMatch = true;
+ }
+ }
+ }
+
+ if (!typeMatch && order.getStrength() != null && order.getStrength().trim().equals(targetType)) {
+ typeMatch = true;
+ }
+
+ if (typeMatch) {
+ List<ProductionProductInput> mainInputs = inputsByMain.get(main.getId());
+ if (mainInputs != null) {
+ for (ProductionProductInput input : mainInputs) {
+ Long inputMaterialId = skuToMaterialMap.get(input.getProductId());
+ if (Objects.equals(inputMaterialId, detail.getProductId())) {
+ BigDecimal rawQty = input.getQuantity() != null ? input.getQuantity() : BigDecimal.ZERO;
+
+ List<ProductionOrderStructure> orderStructures = structuresByOrder.get(order.getId());
+ BigDecimal unitPrice = BigDecimal.ZERO;
+ String unit = "";
+ if (orderStructures != null) {
+ for (ProductionOrderStructure structure : orderStructures) {
+ Long structureMaterialId = skuToMaterialMap.get(structure.getProductModelId());
+ if (Objects.equals(structureMaterialId, detail.getProductId())) {
+ unitPrice = structure.getUnitPrice() != null ? structure.getUnitPrice() : BigDecimal.ZERO;
+ unit = structure.getUnit();
+ break;
+ }
+ }
+ }
+ actualTotalCost = actualTotalCost.add(rawQty.multiply(unitPrice));
+ BigDecimal tonnage = UnitUtils.convertValueToTon(rawQty, unit);
+ actualQty = actualQty.add(tonnage);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ detail.setActualQty(actualQty);
+ detail.setActualTotal(actualTotalCost);
+ if (actualQty.compareTo(BigDecimal.ZERO) > 0) {
+ detail.setActualPrice(actualTotalCost.divide(actualQty, 15, RoundingMode.HALF_UP));
+ } else {
+ detail.setActualPrice(BigDecimal.ZERO);
+ }
+
+ } else if ("鑳借�楁垚鏈�".equals(detail.getCategory())) {
+ Energy energy = energyMap.get(detail.getSubjectName());
+ if (energy != null) {
+ BigDecimal unitPrice = energy.getUnitPrice() != null ? energy.getUnitPrice() : BigDecimal.ZERO;
+ for (EnergyConsumptionDetail eDetail : energyDetails) {
+ if (Objects.equals(eDetail.getEnergyId(), energy.getId())) {
+ actualQty = actualQty.add(eDetail.getDosage() != null ? eDetail.getDosage() : BigDecimal.ZERO);
+ }
+ }
+ detail.setActualQty(actualQty);
+ detail.setActualPrice(unitPrice);
+ detail.setActualTotal(actualQty.multiply(unitPrice));
+ } else {
+ detail.setActualQty(BigDecimal.ZERO);
+ detail.setActualPrice(BigDecimal.ZERO);
+ detail.setActualTotal(BigDecimal.ZERO);
+ }
+ } else {
+ detail.setActualQty(BigDecimal.ZERO);
+ detail.setActualPrice(BigDecimal.ZERO);
+ detail.setActualTotal(BigDecimal.ZERO);
+ }
+
+ // 宸紓鍊肩櫨鍒嗘瘮璁$畻
+ detail.setDiffQty(calculatePercentage(detail.getActualQty(), detail.getBudgetQty()));
+ detail.setDiffPrice(calculatePercentage(detail.getActualPrice(), detail.getBudgetPrice()));
+ detail.setDiffTotal(calculatePercentage(detail.getActualTotal(), detail.getBudgetTotal()));
+ }
+
+ return dtoList.stream().collect(Collectors.groupingBy(ProductionSettlementDetailsDto::getCategory));
+ }
+
+ @Override
+ public List<String> getProductTypes(ProductionSettlementDto dto) {
+ String finalPeriodTime = parsePeriodTime(dto != null ? dto.getPeriodTime() : null);
+
+ ProductionSettlementBatches batch = this.lambdaQuery()
+ .eq(ProductionSettlementBatches::getPeriodTime, finalPeriodTime)
+ .one();
+ if (batch == null) {
+ return new ArrayList<>();
+ }
+
+ return productionSettlementDetailsService.lambdaQuery()
+ .eq(ProductionSettlementDetails::getBatchId, batch.getId())
+ .list()
+ .stream()
+ .map(ProductionSettlementDetails::getProductType)
+ .filter(StringUtils::isNotBlank)
+ .distinct()
+ .collect(Collectors.toList());
+ }
+
+
+ @Override
+ public List<String> getSubjectNames(ProductionSettlementDto dto) {
+ String finalPeriodTime = parsePeriodTime(dto != null ? dto.getPeriodTime() : null);
+
+ ProductionSettlementBatches batch = this.lambdaQuery()
+ .eq(ProductionSettlementBatches::getPeriodTime, finalPeriodTime)
+ .one();
+ if (batch == null) {
+ return new ArrayList<>();
+ }
+
+ return productionSettlementDetailsService.lambdaQuery()
+ .eq(ProductionSettlementDetails::getBatchId, batch.getId())
+ .list()
+ .stream()
+ .map(ProductionSettlementDetails::getSubjectName)
+ .filter(StringUtils::isNotBlank)
+ .distinct()
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * 鏍哥畻鏈堜唤锛屼负绌烘椂榛樿鍙栧綋鍓嶆湀锛屾牸寮忎负 yyyy-MM
+ */
+ private String parsePeriodTime(String periodTime) {
+ if (StringUtils.isBlank(periodTime)) {
+ return YearMonth.now().format(DateTimeFormatter.ofPattern("yyyy-MM"));
+ }
+ try {
+ return YearMonth.parse(periodTime, DateTimeFormatter.ofPattern("yyyy-MM"))
+ .format(DateTimeFormatter.ofPattern("yyyy-MM"));
+ } catch (Exception e) {
+ throw new ServiceException("鏃ユ湡鏍煎紡閿欒锛岃浣跨敤 yyyy-MM 鏍煎紡");
+ }
+ }
+
+ private String calculatePercentage(BigDecimal actual, BigDecimal budget) {
+ if (budget == null || budget.compareTo(BigDecimal.ZERO) == 0) {
+ return actual.compareTo(BigDecimal.ZERO) > 0 ? "100%" : "0%";
+ }
+ BigDecimal diff = actual.subtract(budget);
+ BigDecimal percentage = diff.divide(budget, 4, RoundingMode.HALF_UP).multiply(new BigDecimal("100"));
+ return percentage.stripTrailingZeros().toPlainString() + "%";
+ }
+
+ @Override
+ public ProductionSettlementTotalDto getTotalCosts(ProductionSettlementDto dto) {
+ // 澶嶇敤 getSettlement 鑾峰彇鍚疄闄呭�肩殑鏄庣粏鍒楄〃
+ Map<String, List<ProductionSettlementDetailsDto>> settlementMap = getSettlement(dto);
+
+ List<ProductionSettlementDetailsDto> allDetails = settlementMap.values().stream()
+ .flatMap(List::stream)
+ .collect(Collectors.toList());
+
+ // 鎸� dto 涓殑鍏朵綑鏉′欢鍦ㄥ唴瀛樹腑杩囨护
+ if (dto != null) {
+ if (StringUtils.isNotBlank(dto.getProductType())) {
+ String targetType = dto.getProductType().trim();
+ allDetails = allDetails.stream()
+ .filter(d -> targetType.equals(d.getProductType() != null ? d.getProductType().trim() : ""))
+ .collect(Collectors.toList());
+ }
+ if (StringUtils.isNotBlank(dto.getSubjectName())) {
+ String targetSubject = dto.getSubjectName().trim();
+ allDetails = allDetails.stream()
+ .filter(d -> targetSubject.equals(d.getSubjectName() != null ? d.getSubjectName().trim() : ""))
+ .collect(Collectors.toList());
+ }
+ if (StringUtils.isNotBlank(dto.getCostType())) {
+ String targetCostType = dto.getCostType().trim();
+ allDetails = allDetails.stream()
+ .filter(d -> targetCostType.equals(d.getCategory() != null ? d.getCategory().trim() : ""))
+ .collect(Collectors.toList());
+ }
+ }
+
+ // 姹囨�绘爣鍑嗘垚鏈笌瀹為檯鎴愭湰
+ BigDecimal budgetTotal = allDetails.stream()
+ .map(d -> d.getBudgetTotal() != null ? d.getBudgetTotal() : BigDecimal.ZERO)
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+ BigDecimal actualTotal = allDetails.stream()
+ .map(d -> d.getActualTotal() != null ? d.getActualTotal() : BigDecimal.ZERO)
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+ BigDecimal diffTotal = actualTotal.subtract(budgetTotal);
+
+ // 宸紓鐜囷細甯︽璐熷彿
+ String diffRate;
+ if (budgetTotal.compareTo(BigDecimal.ZERO) == 0) {
+ diffRate = actualTotal.compareTo(BigDecimal.ZERO) > 0 ? "+100%" : "0%";
+ } else {
+ BigDecimal rate = diffTotal.divide(budgetTotal, 4, RoundingMode.HALF_UP)
+ .multiply(new BigDecimal("100"));
+ String sign = rate.compareTo(BigDecimal.ZERO) >= 0 ? "+" : "";
+ diffRate = sign + rate.stripTrailingZeros().toPlainString() + "%";
+ }
+
+ ProductionSettlementTotalDto result = new ProductionSettlementTotalDto();
+ result.setBudgetTotal(budgetTotal);
+ result.setActualTotal(actualTotal);
+ result.setDiffTotal(diffTotal);
+ result.setDiffRate(diffRate);
+ return result;
+ }
}
\ No newline at end of file
--
Gitblit v1.9.3