| 3 天以前 | gongchunyi | ![]() |
| 3 天以前 | gongchunyi | ![]() |
| 3 天以前 | gongchunyi | ![]() |
| 3 天以前 | huminmin | ![]() |
| 3 天以前 | huminmin | ![]() |
| 3 天以前 | huminmin | ![]() |
| 3 天以前 | huminmin | ![]() |
| 3 天以前 | huminmin | ![]() |
| 3 天以前 | gongchunyi | ![]() |
| 3 天以前 | huminmin | ![]() |
| 3 天以前 | gongchunyi | ![]() |
| 3 天以前 | huminmin | ![]() |
| 3 天以前 | gongchunyi | ![]() |
| 3 天以前 | gongchunyi | ![]() |
| 3 天以前 | huminmin | ![]() |
| 3 天以前 | zss | ![]() |
| 3 天以前 | zss | ![]() |
| 3 天以前 | huminmin | ![]() |
| 3 天以前 | huminmin | ![]() |
| 3 天以前 | huminmin | ![]() |
| 3 天以前 | huminmin | ![]() |
| 3 天以前 | gongchunyi | ![]() |
| 3 天以前 | gongchunyi | ![]() |
| 3 天以前 | gongchunyi | ![]() |
| 3 天以前 | huminmin | ![]() |
doc/ÄþÏÄ-ÖÐÊ¢½¨²Ä.sql
@@ -82,3 +82,71 @@ `update_user` int NULL DEFAULT NULL, PRIMARY KEY (`id`) ) COMMENT = 'è½æºç±»å-è½èæè¡¨æç»_éä»¶'; alter table product_order drop column sales_ledger_id, drop column sale_ledger_product_id, drop column product_model_id; alter table production_plan add assigned_quantity DECIMAL(10, 4) default 0 not null COMMENT 'ä¸åæ°é'; alter table product_order add plan_complete_time date NULL DEFAULT NULL COMMENT '计å宿æ¶é´'; # ç产订åä¸ç产计åå ³è表 drop table if exists product_order_plan; create table product_order_plan ( id bigint auto_increment primary key, product_order_id bigint not null default 0 comment 'ç产订åid', production_plan_id bigint not null default 0 comment 'ç产计åid', create_time datetime null comment 'å½å ¥æ¶é´', update_time datetime null comment 'æ´æ°æ¶é´', assigned_quantity DECIMAL(10, 4) default 0 not null comment 'ä¸åæ°é', index idx_product_order_id (product_order_id), index idx_production_plan_id (production_plan_id), unique idx_product_order_production_plan (product_order_id, production_plan_id) ); CREATE TABLE `product_material` ( `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '主é®ID', `tenant_id` BIGINT DEFAULT NULL COMMENT 'ç§æ·ID', `material_type_id` INT DEFAULT NULL COMMENT 'å ³èç©æç±»åID', `inventory_category_id` INT DEFAULT NULL COMMENT 'å ³èåè´§ç±»å«ID', `identifier_code` VARCHAR(100) DEFAULT NULL COMMENT 'æ è¯ç¼ç ', `material_code` VARCHAR(100) DEFAULT NULL COMMENT 'ç©æä»£ç ', `product_name` VARCHAR(255) DEFAULT NULL COMMENT '产ååç§°', `material_name` VARCHAR(255) DEFAULT NULL COMMENT 'ç©æåå', `specification` VARCHAR(255) DEFAULT NULL COMMENT 'è§æ ¼åå·', `base_unit` VARCHAR(50) DEFAULT NULL COMMENT 'åºæ¬åä½', `material_attribute` VARCHAR(100) DEFAULT NULL COMMENT 'ç©æå±æ§', `finished_product_name` VARCHAR(100) DEFAULT NULL COMMENT 'æååå', `originator_name` VARCHAR(100) DEFAULT NULL COMMENT 'æäº¤äººå§å', `originator_org` VARCHAR(255) DEFAULT 'å®å¤ä¸å绿è½å®ä¸é墿éå ¬å¸', `remark` TEXT COMMENT '夿³¨', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX `idx_type_id` (`material_type_id`), INDEX `idx_cat_id` (`inventory_category_id`) ) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COMMENT = 'ç©æä¿¡æ¯è¡¨'; ALTER TABLE product_material ADD COLUMN form_instance_id VARCHAR(100) DEFAULT NULL COMMENT 'å®æè¡¨åå®ä¾ID', ADD COLUMN form_modified_time DATETIME DEFAULT NULL COMMENT 'å®æä¿®æ¹æ¶é´'; CREATE TABLE `product_material_config` ( `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY, `config_type` varchar(50) NOT NULL COMMENT 'åºåç±»å: MATERIAL_TYPE æ INVENTORY_CAT', `config_name` varchar(100) NOT NULL COMMENT 'æ¾ç¤ºçåç§°' ) ENGINE = InnoDB COMMENT ='ç©æä¿¡æ¯è¡¨é 置表'; ALTER TABLE `production_plan` ADD COLUMN `product_material_id` int DEFAULT NULL COMMENT 'å ³èç©æä¿¡æ¯è¡¨ID' AFTER `material_code`; -- 建议顺便å ä¸ç´¢å¼ï¼æåå ³èæ¥è¯¢é度 ALTER TABLE `production_plan` ADD INDEX `idx_product_material_id` (`product_material_id`); src/main/java/com/ruoyi/energy/controller/EnergyConsumptionDetailController.java
@@ -3,11 +3,9 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.energy.dto.EnergyConsumptionDetailDto; import com.ruoyi.energy.dto.EnergyStatisticsDto; 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.energy.vo.EnergyStatisticsVo; import com.ruoyi.framework.aspectj.lang.annotation.Log; import com.ruoyi.framework.aspectj.lang.enums.BusinessType; import com.ruoyi.framework.web.domain.R; @@ -56,11 +54,13 @@ @Log(title = "è½èæè¡¨æç»-å¯¼å ¥", businessType = BusinessType.IMPORT) @PostMapping("/importData") @ApiOperation("è½èæè¡¨æç»-å¯¼å ¥") public R importData(MultipartFile file) throws Exception { return energyConsumptionDetailService.importData(file); } @PostMapping("/downloadTemplate") @ApiOperation("è½èæè¡¨-ä¸è½½æ¨¡æ¿") @Log(title = "è½èæè¡¨-ä¸è½½æ¨¡æ¿", businessType = BusinessType.EXPORT) public void downloadTemplate(HttpServletResponse response) { ExcelUtil<EnergyConsumptionDetailDto> util = new ExcelUtil<EnergyConsumptionDetailDto>(EnergyConsumptionDetailDto.class); @@ -78,8 +78,8 @@ @GetMapping("/statistics") @ApiOperation("ææ¥æå¹´æ±æ»ç»è®¡") public R statistics(EnergyStatisticsDto energyStatisticsDto) { return R.ok(energyConsumptionDetailService.statistics(energyStatisticsDto)); public R statistics(EnergyStatisticsVo energyStatisticsVo) { return R.ok(energyConsumptionDetailService.statistics(energyStatisticsVo)); } } src/main/java/com/ruoyi/energy/controller/EnergyController.java
@@ -57,11 +57,13 @@ @Log(title = "è½æºç±»å-å¯¼å ¥", businessType = BusinessType.IMPORT) @PostMapping("/importData") @ApiOperation("è½æºç±»å-å¯¼å ¥") public R importData(MultipartFile file) throws Exception { return energyService.importData(file); } @PostMapping("/downloadTemplate") @ApiOperation("è½æºç±»å-ä¸è½½æ¨¡æ¿") @Log(title = "è½æºç±»å-ä¸è½½æ¨¡æ¿", businessType = BusinessType.EXPORT) public void downloadTemplate(HttpServletResponse response) { ExcelUtil<Energy> util = new ExcelUtil<Energy>(Energy.class); src/main/java/com/ruoyi/energy/dto/EnergyConsumptionDetailDto.java
@@ -7,6 +7,8 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.math.BigDecimal; //è½èæè¡¨æç» @Data @ExcelIgnoreUnannotated @@ -26,4 +28,8 @@ //å建人 private String createUserName; //è´¹ç¨ private BigDecimal cost; } src/main/java/com/ruoyi/energy/dto/EnergyConsumptionTypeDto.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,22 @@ package com.ruoyi.energy.dto; import com.ruoyi.framework.aspectj.lang.annotation.Excel; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.math.BigDecimal; @Data @ApiModel("è½èç±»åå æ¯") public class EnergyConsumptionTypeDto { @ApiModelProperty("è½æºç±»å") private String energyTyep; @ApiModelProperty("è½èç¨é") private BigDecimal energyConsumption; @ApiModelProperty("è½èè´¹ç¨") private BigDecimal energyCost; } src/main/java/com/ruoyi/energy/dto/EnergyCostDto.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,43 @@ package com.ruoyi.energy.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.checkerframework.checker.units.qual.A; import java.math.BigDecimal; import java.time.LocalDate; //è½èè´¹ç¨æç» @Data @ApiModel("è½èè´¹ç¨æç»") public class EnergyCostDto { @ApiModelProperty("æ¥æ") private LocalDate meterReadingDate; @ApiModelProperty("ç¨æ°´é") private BigDecimal waterConsumption; @ApiModelProperty("æ°´è´¹") private BigDecimal waterCost; @ApiModelProperty("ç¨çµé") private BigDecimal electricityConsumption; @ApiModelProperty("çµè´¹") private BigDecimal electricityCost; @ApiModelProperty("ç¨æ°é") private BigDecimal gasConsumption; @ApiModelProperty("æ°è´¹") private BigDecimal gasCost; @ApiModelProperty("å计ç¨é") private BigDecimal totalConsumption; @ApiModelProperty("å计费ç¨") private BigDecimal totalCost; } src/main/java/com/ruoyi/energy/dto/EnergyStatisticsDto.java
@@ -1,20 +1,36 @@ package com.ruoyi.energy.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.time.LocalDate; import java.math.BigDecimal; import java.util.List; import java.util.Map; //ææ¥æå¹´æ±æ»ç»è®¡è½èçä¼ å @Data @ApiModel("ææ¥æå¹´æ±æ»ç»è®¡çè½èæ°æ®") public class EnergyStatisticsDto { //å¼å§æ¥æ private LocalDate startDate; @ApiModelProperty("æ»èç¨é") private BigDecimal totalEnergyConsumption; //ç»ææ¥æ private LocalDate endDate; @ApiModelProperty("æ»è½èè´¹ç¨") private BigDecimal totalEnergyCost; //è½èåºæ¯(åå ¬/ç产) private String type; @ApiModelProperty("å¹³åç¨é") private BigDecimal averageConsumption; @ApiModelProperty("ç¯æ¯åå") private BigDecimal changeVite; @ApiModelProperty("è½èç±»åå æ¯") private List<EnergyConsumptionTypeDto> energyConsumptionTypeProportion; @ApiModelProperty("è½èæç»") private List<EnergyCostDto> energyCostDtos; } src/main/java/com/ruoyi/energy/mapper/EnergyConsumptionDetailMapper.java
@@ -3,10 +3,17 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.energy.dto.EnergyConsumptionDetailDto; import com.ruoyi.energy.dto.EnergyConsumptionTypeDto; import com.ruoyi.energy.dto.EnergyCostDto; import com.ruoyi.energy.pojo.EnergyConsumptionDetail; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.energy.vo.EnergyStatisticsVo; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.math.BigDecimal; import java.util.List; import java.util.Map; /** * <p> @@ -20,4 +27,10 @@ public interface EnergyConsumptionDetailMapper extends BaseMapper<EnergyConsumptionDetail> { IPage<EnergyConsumptionDetailDto> pageEnergyConsumptionDetail(Page<EnergyConsumptionDetailDto> page, @Param("c") EnergyConsumptionDetailDto energyConsumptionDetailDto); Map<String, BigDecimal> calculateEnergy(@Param("c") EnergyStatisticsVo energyStatisticsVo); List<EnergyConsumptionTypeDto> energyConsumptionTypeProportion(@Param("c") EnergyStatisticsVo energyStatisticsVo); List<EnergyCostDto> energyCostDtos(@Param("c") EnergyStatisticsVo energyStatisticsVo); } src/main/java/com/ruoyi/energy/service/EnergyConsumptionDetailService.java
@@ -6,6 +6,7 @@ import com.ruoyi.energy.dto.EnergyStatisticsDto; import com.ruoyi.energy.pojo.EnergyConsumptionDetail; import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.energy.vo.EnergyStatisticsVo; import com.ruoyi.framework.web.domain.R; import org.springframework.web.multipart.MultipartFile; @@ -27,6 +28,6 @@ void export(HttpServletResponse response); Object statistics(EnergyStatisticsDto energyStatisticsDto); EnergyStatisticsDto statistics(EnergyStatisticsVo energyStatisticsVo); } src/main/java/com/ruoyi/energy/service/impl/EnergyConsumptionDetailServiceImpl.java
@@ -7,6 +7,8 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.energy.dto.EnergyConsumptionDetailDto; import com.ruoyi.energy.dto.EnergyConsumptionTypeDto; import com.ruoyi.energy.dto.EnergyCostDto; import com.ruoyi.energy.dto.EnergyStatisticsDto; import com.ruoyi.energy.mapper.EnergyMapper; import com.ruoyi.energy.pojo.Energy; @@ -14,6 +16,7 @@ import com.ruoyi.energy.mapper.EnergyConsumptionDetailMapper; import com.ruoyi.energy.service.EnergyConsumptionDetailService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.energy.vo.EnergyStatisticsVo; import com.ruoyi.framework.web.domain.R; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -21,7 +24,11 @@ import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** @@ -91,7 +98,32 @@ } @Override public Object statistics(EnergyStatisticsDto energyStatisticsDto) { return null; public EnergyStatisticsDto statistics(EnergyStatisticsVo energyStatisticsVo) { EnergyStatisticsDto energyStatisticsDto = new EnergyStatisticsDto(); //è®¡ç®æ»èç¨é+æ»è½èè´¹ç¨ Map<String, BigDecimal> map=energyConsumptionDetailMapper.calculateEnergy(energyStatisticsVo); energyStatisticsDto.setTotalEnergyConsumption(map.get("totalEnergyConsumption")); energyStatisticsDto.setTotalEnergyCost(map.get("totalEnergyCost")); //å¹³åç¨é energyStatisticsDto.setAverageConsumption(energyStatisticsDto.getTotalEnergyConsumption().divide(new BigDecimal(energyStatisticsVo.getDays()),2, RoundingMode.HALF_UP)); //ç¯æ¯åå=(æ¬æå¼ - 䏿å¼) / ä¸æå¼ * 100 LocalDate minDays = energyStatisticsVo.getStartDate().minusDays(energyStatisticsVo.getDays()); LocalDate masDays = energyStatisticsVo.getEndDate().minusDays(energyStatisticsVo.getDays()); EnergyStatisticsVo oldenergyStatisticsVo = new EnergyStatisticsVo(); BeanUtils.copyProperties(energyStatisticsVo,oldenergyStatisticsVo); oldenergyStatisticsVo.setStartDate(minDays); oldenergyStatisticsVo.setEndDate(masDays); Map<String, BigDecimal> oldmap=energyConsumptionDetailMapper.calculateEnergy(oldenergyStatisticsVo); if (ObjectUtils.isNotEmpty(oldmap)) { BigDecimal changeVite = (map.get("totalEnergyConsumption").subtract(oldmap.get("totalEnergyConsumption"))).divide(oldmap.get("totalEnergyConsumption"), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)); energyStatisticsDto.setChangeVite(changeVite); } //è½èç±»åå æ¯ List<EnergyConsumptionTypeDto> energyConsumptionTypeDtos=energyConsumptionDetailMapper.energyConsumptionTypeProportion(energyStatisticsVo); energyStatisticsDto.setEnergyConsumptionTypeProportion(energyConsumptionTypeDtos); //è½èæç» List<EnergyCostDto> energyCostDtos=energyConsumptionDetailMapper.energyCostDtos(energyStatisticsVo); energyStatisticsDto.setEnergyCostDtos(energyCostDtos); return energyStatisticsDto; } } src/main/java/com/ruoyi/energy/vo/EnergyStatisticsVo.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,23 @@ package com.ruoyi.energy.vo; import lombok.Data; import java.time.LocalDate; //ææ¥æå¹´æ±æ»ç»è®¡è½èçä¼ å @Data public class EnergyStatisticsVo { //å¼å§æ¥æ private LocalDate startDate; //ç»ææ¥æ private LocalDate endDate; //æ¥è¯¢é´éå¤©æ° private Long days; //è½èåºæ¯(åå ¬/ç产) private String type; } src/main/java/com/ruoyi/framework/config/AliDingConfig.java
@@ -42,9 +42,15 @@ private String appType; /** * å®æè¡¨åID * éå®çäº§éæ±-å®æè¡¨åID */ private String formUuid; private String producePlanFormUuid; /** * ç©æç¼ç -å®æè¡¨åID */ private String materialCodeFormUuid; /** * 宿åºç¨å¯é¥ src/main/java/com/ruoyi/production/controller/ProductMaterialController.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,37 @@ package com.ruoyi.production.controller; import com.ruoyi.framework.aspectj.lang.annotation.Log; import com.ruoyi.framework.aspectj.lang.enums.BusinessType; import com.ruoyi.framework.web.domain.AjaxResult; import com.ruoyi.production.service.ProductMaterialService; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * <br> * 产åç©æä¿¡æ¯æ§å¶å± * </br> * * @author deslrey * @version 1.0 * @since 2026/03/11 16:38 */ @RestController @RequestMapping("/productMaterial") public class ProductMaterialController { @Autowired private ProductMaterialService productMaterialService; @GetMapping("/loadData") @ApiOperation("æåç©æç¼ç æ°æ®") @Log(title = "æåç©æç¼ç æ°æ®", businessType = BusinessType.INSERT) public AjaxResult loadProductMaterialData() { productMaterialService.loadProductMaterialData(); return AjaxResult.success(); } } src/main/java/com/ruoyi/production/controller/ProductOrderController.java
@@ -46,6 +46,12 @@ return R.ok(productOrderService.listProcessRoute(productModelId)); } @PostMapping("/revoke") @ApiOperation("æ¤åç产计å") public R revoke(@RequestBody ProductOrder productOrder) { return R.ok(productOrderService.revoke(productOrder)); } /** * 导åºç产订å */ src/main/java/com/ruoyi/production/enums/MaterialConfigTypeEnum.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,25 @@ package com.ruoyi.production.enums; /** * <br> * ç©æé 置类åæä¸¾ * </br> * * @author deslrey * @version 1.0 * @since 2026/03/11 17:24 */ public enum MaterialConfigTypeEnum { /** * ç©æç±»å * å¯¹åºæ°æ®åº config_type = MATERIAL_TYPE */ MATERIAL_TYPE, /** * åè´§ç±»å« * å¯¹åºæ°æ®åº config_type = INVENTORY_CAT */ INVENTORY_CAT } src/main/java/com/ruoyi/production/mapper/ProductMaterialConfigMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,16 @@ package com.ruoyi.production.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.production.pojo.ProductMaterialConfig; /** * <br> * ç©æä¿¡æ¯è¡¨é ç½®Mapper * </br> * * @author deslrey * @version 1.0 * @since 2026/03/11 16:58 */ public interface ProductMaterialConfigMapper extends BaseMapper<ProductMaterialConfig> { } src/main/java/com/ruoyi/production/mapper/ProductMaterialMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,16 @@ package com.ruoyi.production.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.production.pojo.ProductMaterial; /** * <br> * 产åç©æä¿¡æ¯Mapper * </br> * * @author deslrey * @version 1.0 * @since 2026/03/11 16:35 */ public interface ProductMaterialMapper extends BaseMapper<ProductMaterial> { } src/main/java/com/ruoyi/production/pojo/ProductMaterial.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,84 @@ package com.ruoyi.production.pojo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.time.LocalDateTime; /** * <br> * 产åç©æä¿¡æ¯è¡¨ * </br> * * @author deslrey * @version 1.0 * @since 2026/03/11 16:31 */ @Data @TableName("product_material") @ApiModel(value = "ProductMaterial", description = "产åç©æä¿¡æ¯è¡¨") public class ProductMaterial { @TableId(type = IdType.AUTO) @ApiModelProperty("主é®ID") private Integer id; @ApiModelProperty("ç§æ·ID") private Long tenantId; @ApiModelProperty("ç©æç±»åID") private Integer materialTypeId; @ApiModelProperty("åè´§ç±»å«ID") private Integer inventoryCategoryId; @ApiModelProperty("æ è¯ç¼ç ") private String identifierCode; @ApiModelProperty("ç©æä»£ç ") private String materialCode; @ApiModelProperty("ç©æåå") private String materialName; @ApiModelProperty("è§æ ¼åå·") private String specification; @ApiModelProperty("åºæ¬åä½") private String baseUnit; @ApiModelProperty("ç©æå±æ§") private String materialAttribute; @ApiModelProperty("æååå") private String finishedProductName; @ApiModelProperty("æäº¤äººå§å") private String originatorName; @ApiModelProperty("æäº¤äººç»ç»") private String originatorOrg; @ApiModelProperty("夿³¨") private String remark; @ApiModelProperty("å建æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createTime; @ApiModelProperty("ä¿®æ¹æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime updateTime; @ApiModelProperty("å®æè¡¨åå®ä¾ID") private String formInstanceId; @ApiModelProperty("å®æä¿®æ¹æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime formModifiedTime; } src/main/java/com/ruoyi/production/pojo/ProductMaterialConfig.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,33 @@ package com.ruoyi.production.pojo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; /** * <br> * ç©æä¿¡æ¯è¡¨é 置表 * </br> * * @author deslrey * @version 1.0 * @since 2026/03/11 16:57 */ @Data @TableName("product_material_config") @ApiModel(value = "ProductMaterialConfig", description = "ç©æä¿¡æ¯é 置表") public class ProductMaterialConfig { @TableId(type = IdType.AUTO) @ApiModelProperty("主é®ID") private Integer id; @ApiModelProperty("åºåç±»å: MATERIAL_TYPE æ INVENTORY_CAT") private String configType; @ApiModelProperty("é ç½®åç§°") private String configName; } src/main/java/com/ruoyi/production/pojo/ProductOrder.java
@@ -10,7 +10,9 @@ import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; @Data @TableName("product_order") @@ -20,24 +22,6 @@ @TableId(value = "id", type = IdType.AUTO) private Long id; /** * éå®å°è´¦id */ @ApiModelProperty(value = "éå®å°è´¦id") private Long salesLedgerId; /** * éå®å°è´¦äº§åid(sales_ledger_product) */ @ApiModelProperty(value = "éå®å°è´¦äº§åid") private Long saleLedgerProductId; /** * 产åè§æ ¼id */ @ApiModelProperty(value = "产åè§æ ¼id") private Long productModelId; /** * 模ççå·¥èºè·¯çº¿id @@ -51,6 +35,14 @@ @ApiModelProperty(value = "ç产订åå·") @Excel(name = "ç产订åå·") private String npsNo; /** * 计å宿æ¶é´ */ @ApiModelProperty(value = "计å宿æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd") @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate planCompleteTime; /** * ç§æ·id @@ -73,7 +65,6 @@ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime updateTime; /** * éæ±æ°é @@ -100,7 +91,4 @@ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime endTime; } src/main/java/com/ruoyi/production/pojo/ProductionProductMain.java
@@ -32,6 +32,9 @@ @ApiModelProperty(value = "å·¥åid") private Long workOrderId; @ApiModelProperty(value = "ç产订åid") private Long productOrderId; @ApiModelProperty(value = "æ¥å·¥ç¶æ") private Integer status; src/main/java/com/ruoyi/production/service/ProductMaterialConfigService.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,16 @@ package com.ruoyi.production.service; import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.production.pojo.ProductMaterialConfig; /** * <br> * ç©æä¿¡æ¯è¡¨é ç½®æ¥å£ * </br> * * @author deslrey * @version 1.0 * @since 2026/03/11 16:58 */ public interface ProductMaterialConfigService extends IService<ProductMaterialConfig> { } src/main/java/com/ruoyi/production/service/ProductMaterialService.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,20 @@ package com.ruoyi.production.service; import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.production.pojo.ProductMaterial; /** * <br> * 产åç©æä¿¡æ¯æ¥å£ * </br> * * @author deslrey * @version 1.0 * @since 2026/03/11 16:35 */ public interface ProductMaterialService extends IService<ProductMaterial> { void loadProductMaterialData(); void syncProductMaterialJob(); } src/main/java/com/ruoyi/production/service/ProductOrderService.java
@@ -18,6 +18,11 @@ int bindingRoute(ProductOrder productOrder); /** * æ¤åç产计å */ Boolean revoke(ProductOrder productOrder); List<ProcessRoute> listProcessRoute(Long productModelId); List<ProductStructureDto> listProcessBom(Long orderId); src/main/java/com/ruoyi/production/service/impl/ProductMaterialConfigServiceImpl.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,24 @@ package com.ruoyi.production.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.production.mapper.ProductMaterialConfigMapper; import com.ruoyi.production.pojo.ProductMaterialConfig; import com.ruoyi.production.service.ProductMaterialConfigService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; /** * <br> * ç©æä¿¡æ¯è¡¨é ç½®æ¥å£å®ç°ç±» * </br> * * @author deslrey * @version 1.0 * @since 2026/03/11 16:59 */ @Slf4j @Service public class ProductMaterialConfigServiceImpl extends ServiceImpl<ProductMaterialConfigMapper, ProductMaterialConfig> implements ProductMaterialConfigService { } src/main/java/com/ruoyi/production/service/impl/ProductMaterialServiceImpl.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,306 @@ package com.ruoyi.production.service.impl; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.http.HttpUtils; import com.ruoyi.framework.config.AliDingConfig; import com.ruoyi.production.enums.MaterialConfigTypeEnum; import com.ruoyi.production.mapper.ProductMaterialMapper; import com.ruoyi.production.pojo.ProductMaterial; import com.ruoyi.production.pojo.ProductMaterialConfig; import com.ruoyi.production.service.ProductMaterialConfigService; import com.ruoyi.production.service.ProductMaterialService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.ReentrantLock; /** * <br> * 产åç©æä¿¡æ¯æ¥å£å®ç°ç±» * </br> * * @author deslrey * @version 1.0 * @since 2026/03/11 16:36 */ @Slf4j @Service public class ProductMaterialServiceImpl extends ServiceImpl<ProductMaterialMapper, ProductMaterial> implements ProductMaterialService { @Autowired private AliDingConfig aliDingConfig; @Autowired private ProductMaterialConfigService productMaterialConfigService; /** * 忥éï¼é²æ¢æå¨å宿¶ä»»å¡åæ¶æ§è¡ */ private final ReentrantLock syncLock = new ReentrantLock(); @Override public void loadProductMaterialData() { syncProductMaterialData(1); } @Override public void syncProductMaterialJob() { syncProductMaterialData(2); } /** * åæ¥æ°æ® */ @Transactional(rollbackFor = Exception.class) public void syncProductMaterialData(Integer dataSyncType) { if (!syncLock.tryLock()) { log.warn("忥æ£å¨è¿è¡ä¸ï¼æ¬æ¬¡ {} åæ¥è¯·æ±è¢«è·³è¿", dataSyncType == 1 ? "æå¨" : "宿¶ä»»å¡"); return; } try { // è·å AccessToken String accessToken = getAccessToken(); if (StringUtils.isEmpty(accessToken)) { return; } // è·åæ¬å°æå忥æ¶é´ LocalDateTime lastSyncTime = getLastSyncTime(); log.info("å¼å§ç©æç¼ç å¢éåæ¥ï¼æ¬å°æåä¿®æ¹æ¶é´: {}", lastSyncTime); int pageNumber = 1; int pageSize = 50; boolean hasMore = true; int totalSynced = 0; while (hasMore) { // æ¥è¯¢åæ° JSONObject searchParam = buildSearchParam(lastSyncTime, pageNumber, pageSize); // è°ç¨å®ææ¥å£æåæ°æ® String dataRes = HttpUtils.sendPostJson( aliDingConfig.getSearchFormDataUrl(), searchParam.toJSONString(), StandardCharsets.UTF_8.name(), null, accessToken ); if (StringUtils.isEmpty(dataRes)) { log.warn("第 {} 页æåæ°æ®ä¸ºç©º", pageNumber); break; } JSONObject resultObj = JSON.parseObject(dataRes); JSONArray dataArr = resultObj.getJSONArray("data"); Integer totalCount = resultObj.getInteger("totalCount"); if (dataArr == null || dataArr.isEmpty()) { log.info("æ²¡ææ´å¤æ°æ°æ®éè¦åæ¥"); break; } // è§£æå¹¶ä¿åæ°æ® List<ProductMaterial> list = parseProductMaterials(dataArr, totalCount); if (!list.isEmpty()) { // å¤çæ´æ°ææ°å¢ int affected = processSaveOrUpdate(list); totalSynced += affected; } // 夿æ¯å¦è¿æä¸ä¸é¡µ hasMore = (pageNumber * pageSize) < totalCount; pageNumber++; log.info("æ£å¨åæ¥ç¬¬ {} 页ï¼å½å已忥 {}/{}", pageNumber - 1, totalSynced, totalCount); } log.info("ç©ææ°æ®åæ¥å®æï¼å ±åæ¥ {} æ¡æ°æ®", totalSynced); } catch (Exception e) { log.error("åæ¥ç©æç¼ç å¼å¸¸", e); } finally { // éæ¾é syncLock.unlock(); } } private String getAccessToken() { String params = "appkey=" + aliDingConfig.getAppKey() + "&appsecret=" + aliDingConfig.getAppSecret(); String tokenRes = HttpUtils.sendGet(aliDingConfig.getAccessTokenUrl(), params); JSONObject tokenObj = JSON.parseObject(tokenRes); String accessToken = tokenObj.getString("access_token"); if (StringUtils.isEmpty(accessToken)) { log.error("è·åééAccessToken失败: {}", tokenRes); } return accessToken; } private LocalDateTime getLastSyncTime() { LambdaQueryWrapper<ProductMaterial> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.orderByDesc(ProductMaterial::getFormModifiedTime).last("LIMIT 1"); ProductMaterial lastRecord = this.getOne(queryWrapper); return lastRecord != null ? lastRecord.getFormModifiedTime() : null; } private JSONObject buildSearchParam(LocalDateTime lastSyncTime, int pageNumber, int pageSize) { JSONObject searchParam = new JSONObject(); searchParam.put("appType", aliDingConfig.getAppType()); searchParam.put("systemToken", aliDingConfig.getSystemToken()); searchParam.put("userId", aliDingConfig.getUserId()); searchParam.put("formUuid", aliDingConfig.getMaterialCodeFormUuid()); searchParam.put("currentPage", pageNumber); searchParam.put("pageSize", pageSize); JSONArray searchConditions = new JSONArray(); JSONObject statusCondition = new JSONObject(); statusCondition.put("key", "processInstanceStatus"); JSONArray statusValueArray = new JSONArray(); statusValueArray.add("COMPLETED"); statusCondition.put("value", statusValueArray); statusCondition.put("type", "ARRAY"); statusCondition.put("operator", "in"); statusCondition.put("componentName", "SelectField"); searchConditions.add(statusCondition); JSONObject resultCondition = new JSONObject(); resultCondition.put("key", "processApprovedResult"); JSONArray resultValueArray = new JSONArray(); resultValueArray.add("agree"); resultCondition.put("value", resultValueArray); resultCondition.put("type", "ARRAY"); resultCondition.put("operator", "in"); resultCondition.put("componentName", "SelectField"); searchConditions.add(resultCondition); searchParam.put("searchFieldJson", searchConditions.toJSONString()); searchParam.put("orderConfigJson", "{\"gmt_modified\":\"+\"}"); if (lastSyncTime != null) { String startTime = lastSyncTime.plusSeconds(1).atZone(ZoneId.systemDefault()) .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); searchParam.put("modifiedFromTimeGMT", startTime); } String endTime = LocalDateTime.now().atZone(ZoneId.systemDefault()) .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); searchParam.put("modifiedToTimeGMT", endTime); return searchParam; } private List<ProductMaterial> parseProductMaterials(JSONArray dataArr, Integer totalCount) { List<ProductMaterial> list = new ArrayList<>(); LocalDateTime now = LocalDateTime.now(); for (int i = 0; i < dataArr.size(); i++) { JSONObject item = dataArr.getJSONObject(i); String formInstanceId = item.getString("formInstanceId"); JSONObject originator = item.getJSONObject("originator"); String originatorName = originator != null && originator.containsKey("userName") ? originator.getJSONObject("userName").getString("nameInChinese") : "æªç¥"; JSONObject formData = item.getJSONObject("formData"); ProductMaterial material = new ProductMaterial(); material.setFormInstanceId(formInstanceId); material.setIdentifierCode(formData.getString("textField_l92h77ju")); material.setMaterialCode(formData.getString("textField_l92f36f2")); material.setMaterialName(formData.getString("textField_l92f36f5")); material.setSpecification(formData.getString("textField_l92f36f6")); material.setBaseUnit(formData.getString("textField_la147lnw")); material.setMaterialAttribute(formData.getString("selectField_la14k51j")); material.setFinishedProductName(formData.getString("radioField_lbkk2nn2")); material.setRemark(formData.getString("textareaField_l92f36f9")); // å¤çç©æç±»åååè´§ç±»å« String materialType = formData.getString("selectField_l92f36fb"); String inventoryCat = formData.getString("selectField_la154noy"); material.setMaterialTypeId(getOrCreateConfigId(materialType, MaterialConfigTypeEnum.MATERIAL_TYPE.name())); material.setInventoryCategoryId(getOrCreateConfigId(inventoryCat, MaterialConfigTypeEnum.INVENTORY_CAT.name())); material.setOriginatorName(originatorName); material.setOriginatorOrg("å®å¤ä¸å绿è½å®ä¸é墿éå ¬å¸"); material.setFormModifiedTime(parseUtcTime(item.getString("modifiedTimeGMT"))); material.setCreateTime(now); material.setUpdateTime(now); list.add(material); } return list; } private Integer getOrCreateConfigId(String name, String type) { if (StringUtils.isEmpty(name)) { return null; } ProductMaterialConfig config = productMaterialConfigService.getOne(new LambdaQueryWrapper<ProductMaterialConfig>() .eq(ProductMaterialConfig::getConfigName, name) .eq(ProductMaterialConfig::getConfigType, type)); if (config == null) { config = new ProductMaterialConfig(); config.setConfigName(name); config.setConfigType(type); productMaterialConfigService.save(config); } return config.getId(); } private int processSaveOrUpdate(List<ProductMaterial> list) { if (list == null || list.isEmpty()) { return 0; } int affected = 0; for (ProductMaterial material : list) { ProductMaterial exist = this.getOne(new LambdaQueryWrapper<ProductMaterial>() .eq(ProductMaterial::getFormInstanceId, material.getFormInstanceId())); if (exist == null) { this.save(material); affected++; log.info("æ°å¢ç©ææ°æ® formInstanceId={}", material.getFormInstanceId()); } else { if (exist.getFormModifiedTime() == null || !exist.getFormModifiedTime().equals(material.getFormModifiedTime())) { material.setId(exist.getId()); material.setCreateTime(exist.getCreateTime()); this.updateById(material); affected++; log.info("æ´æ°ç©ææ°æ® formInstanceId={}", material.getFormInstanceId()); } } } return affected; } private LocalDateTime parseUtcTime(String utcString) { if (StringUtils.isEmpty(utcString)) { return null; } try { OffsetDateTime odt = OffsetDateTime.parse(utcString); return odt.toLocalDateTime(); } catch (DateTimeParseException ex) { log.warn("è§£ææ¶é´ {} 失败: {}", utcString, ex.getMessage()); return null; } } } src/main/java/com/ruoyi/production/service/impl/ProductOrderServiceImpl.java
@@ -15,10 +15,15 @@ import com.ruoyi.production.mapper.*; import com.ruoyi.production.pojo.*; import com.ruoyi.production.service.ProductOrderService; import com.ruoyi.productionPlan.mapper.ProductOrderPlanMapper; import com.ruoyi.productionPlan.mapper.ProductionPlanMapper; import com.ruoyi.productionPlan.pojo.ProductOrderPlan; import com.ruoyi.productionPlan.pojo.ProductionPlan; import com.ruoyi.quality.mapper.QualityInspectMapper; import com.ruoyi.quality.pojo.QualityInspect; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.time.LocalDate; @@ -49,6 +54,12 @@ @Autowired private ProductionProductMainMapper productionProductMainMapper; @Autowired private ProductOrderPlanMapper productOrderPlanMapper; @Autowired private ProductionPlanMapper productionPlanMapper; @Autowired private ProductionProductOutputMapper productionProductOutputMapper; @@ -123,6 +134,29 @@ } @Override @Transactional(rollbackFor = Exception.class) public Boolean revoke(ProductOrder productOrder) { // todo 夿æ¯å¦äº§çæ¥å·¥ä¿¡æ¯ // æ¥è¯¢åå¹¶çç产计å List<ProductOrderPlan> productOrderPlans = productOrderPlanMapper.selectList(Wrappers.<ProductOrderPlan>lambdaQuery().in(ProductOrderPlan::getProductOrderId, productOrder.getId())); if (productOrderPlans.isEmpty()) { throw new RuntimeException("åå¹¶çç产计åä¸åå¨"); } for (ProductOrderPlan productOrderPlan : productOrderPlans) { ProductionPlan productionPlan = productionPlanMapper.selectById(productOrderPlan.getProductionPlanId()); productionPlan.setAssignedQuantity(productionPlan.getAssignedQuantity().subtract(productOrderPlan.getAssignedQuantity())); productionPlanMapper.updateById(productionPlan); } // å é¤å ³èå ³ç³» productOrderPlanMapper.delete(Wrappers.<ProductOrderPlan>lambdaQuery().in(ProductOrderPlan::getProductOrderId, productOrder.getId())); // å é¤è®¢å productOrderMapper.deleteById(productOrder.getId()); // todo å é¤è®¢åä¸çå·¥èºè·¯çº¿å表 return null; } @Override public List<ProcessRoute> listProcessRoute(Long productModelId) { return productOrderMapper.listProcessRoute(productModelId); } src/main/java/com/ruoyi/production/task/ProductMaterialTask.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,27 @@ package com.ruoyi.production.task; import com.ruoyi.production.service.ProductMaterialService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; /** * <br> * * </br> * * @author deslrey * @version 1.0 * @since 2026/03/11 16:39 */ @Component public class ProductMaterialTask { @Autowired private ProductMaterialService productMaterialService; @Scheduled(cron = "0 0 * * * ?") public void syncProdDataJob() { productMaterialService.syncProductMaterialJob(); } } src/main/java/com/ruoyi/productionPlan/controller/ProductOrderPlanController.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,18 @@ package com.ruoyi.productionPlan.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * <p> * å端æ§å¶å¨ * </p> * * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå ¬å¸ * @since 2026-03-11 03:02:58 */ @RestController @RequestMapping("/productOrderPlan") public class ProductOrderPlanController { } src/main/java/com/ruoyi/productionPlan/controller/ProductionPlanController.java
@@ -1,18 +1,22 @@ package com.ruoyi.productionPlan.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.framework.aspectj.lang.annotation.Log; import com.ruoyi.framework.aspectj.lang.enums.BusinessType; import com.ruoyi.framework.web.domain.AjaxResult; import com.ruoyi.productionPlan.dto.ProductionPlanDto; import com.ruoyi.productionPlan.dto.ProductionPlanImportDto; import com.ruoyi.productionPlan.dto.ProductionPlanSummaryDto; import com.ruoyi.productionPlan.service.ProductionPlanService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import java.util.List; /** * <br> @@ -38,9 +42,70 @@ } @GetMapping("/loadProdData") @ApiOperation("æåéå®ç产计å") @Log(title = "æåéå®ç产计å", businessType = BusinessType.INSERT) public AjaxResult loadProdData() { productionPlanService.loadProdData(); return AjaxResult.success(); } @PostMapping("/combine") @Log(title = "åå¹¶ç产计å", businessType = BusinessType.INSERT) @ApiOperation("åå¹¶ç产计å") public AjaxResult combine(@RequestBody ProductionPlanDto productionPlanDto) { return AjaxResult.success(productionPlanService.combine(productionPlanDto)); } @PostMapping("") @Log(title = "å建ç产计å", businessType = BusinessType.INSERT) @ApiOperation("å建ç产计å") public AjaxResult add(@RequestBody ProductionPlanDto productionPlanDto) { return AjaxResult.success(productionPlanService.add(productionPlanDto)); } @PutMapping("") @Log(title = "æ´æ°ç产计å", businessType = BusinessType.UPDATE) @ApiOperation("æ´æ°ç产计å") public AjaxResult update(@RequestBody ProductionPlanDto productionPlanDto) { return AjaxResult.success(productionPlanService.update(productionPlanDto)); } @DeleteMapping("") @Log(title = "å é¤ç产计å", businessType = BusinessType.DELETE) @ApiOperation("å é¤ç产计å") public AjaxResult delete(@RequestBody List<Long> ids) { return AjaxResult.success(productionPlanService.removeByIds(ids)); } @GetMapping("/summaryByProductType") @ApiOperation("æç §äº§åç±»å«æ±æ»ç»è®¡éæ±é") @Log(title = "æç §äº§åç±»å«æ±æ»ç»è®¡éæ±é", businessType = BusinessType.OTHER) public AjaxResult summaryByProductType(ProductionPlanSummaryDto query) { List<ProductionPlanSummaryDto> list = productionPlanService.summaryByProductType(query); return AjaxResult.success(list); } @PostMapping("/downloadTemplate") @Log(title = "ä¸è½½ä¸»ç产计åå¯¼å ¥æ¨¡æ¿", businessType = BusinessType.EXPORT) @ApiOperation("ä¸è½½ä¸»ç产计åå¯¼å ¥æ¨¡æ¿") public void importTemplate(HttpServletResponse response) { ExcelUtil<ProductionPlanImportDto> excelUtil = new ExcelUtil<>(ProductionPlanImportDto.class); excelUtil.importTemplateExcel(response, "主ç产计åå¯¼å ¥æ¨¡æ¿"); } @PostMapping("/import") @ApiOperation("主çäº§è®¡åæ°æ®å¯¼å ¥") @Log(title = "主çäº§è®¡åæ°æ®å¯¼å ¥", businessType = BusinessType.IMPORT) public AjaxResult importProdData(@RequestParam("file") MultipartFile file) { productionPlanService.importProdData(file); return AjaxResult.success("å¯¼å ¥æå"); } @PostMapping("/export") @ApiOperation("主çäº§è®¡åæ°æ®å¯¼åº") @Log(title = "主çäº§è®¡åæ°æ®å¯¼åº", businessType = BusinessType.EXPORT) public void exportProdData(HttpServletResponse response, @RequestBody(required = false) List<Long> ids) { productionPlanService.exportProdData(response, ids); } } src/main/java/com/ruoyi/productionPlan/dto/ProductionPlanDto.java
@@ -1,6 +1,26 @@ package com.ruoyi.productionPlan.dto; import com.fasterxml.jackson.annotation.JsonFormat; import com.ruoyi.productionPlan.pojo.ProductionPlan; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import java.math.BigDecimal; import java.time.LocalDate; import java.util.List; @Data public class ProductionPlanDto extends ProductionPlan { @ApiModelProperty(value = "ç产计åidéå") private List<Long> ids; @ApiModelProperty(value = "ä¸åæ°é") private BigDecimal totalAssignedQuantity; @ApiModelProperty(value = "计å宿æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd") @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate planCompleteTime; } src/main/java/com/ruoyi/productionPlan/dto/ProductionPlanImportDto.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,177 @@ package com.ruoyi.productionPlan.dto; import com.fasterxml.jackson.annotation.JsonFormat; import com.ruoyi.framework.aspectj.lang.annotation.Excel; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.math.BigDecimal; import java.util.Date; /** * <br> * éå®çäº§éæ± Excelå¯¼å ¥å¯¼åºDTO * </br> * * @author deslrey * @version 1.0 * @since 2026/03/11 11:28 */ @Data @ApiModel("éå®çäº§éæ± Excelå¯¼å ¥å¯¼åºDTO") public class ProductionPlanImportDto { /** * ç³è¯·åç¼å· */ @ApiModelProperty("ç³è¯·åç¼å·") @Excel(name = "ç³è¯·åç¼å·") private String applyNo; /** * 客æ·åç§° */ @ApiModelProperty("客æ·åç§°") @Excel(name = "客æ·åç§°") private String customerName; /** * ç©æç¼ç */ @ApiModelProperty("ç©æç¼ç ") @Excel(name = "ç©æç¼ç ") private String materialCode; /** * 产ååç§° */ @ApiModelProperty("产ååç§°") @Excel(name = "产ååç§°") private String productName; /** * 产åè§æ ¼ */ @ApiModelProperty("产åè§æ ¼") @Excel(name = "产åè§æ ¼") private String productSpec; /** * é¿ */ @ApiModelProperty("é¿") @Excel(name = "é¿") private Integer length; /** * 宽 */ @ApiModelProperty("宽") @Excel(name = "宽") private Integer width; /** * é« */ @ApiModelProperty("é«") @Excel(name = "é«") private Integer height; /** * åæ° */ @ApiModelProperty("åæ°") @Excel(name = "åæ°") private Integer quantity; /** * æ¹æ° */ @ApiModelProperty("æ¹æ°") @Excel(name = "æ¹æ°") private BigDecimal volume; /** * 强度 */ @ApiModelProperty("强度") @Excel(name = "强度") private String strength; /** * å¼å§æ¥æ */ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") @ApiModelProperty("å¼å§æ¥æ") @Excel(name = "å¼å§æ¥æ", width = 20, dateFormat = "yyyy-MM-dd") private Date startDate; /** * ç»ææ¥æ */ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") @ApiModelProperty("ç»ææ¥æ") @Excel(name = "ç»ææ¥æ", width = 20, dateFormat = "yyyy-MM-dd") private Date endDate; /** * æäº¤äºº */ @ApiModelProperty("æäº¤äºº") @Excel(name = "æäº¤äºº") private String submitter; /** * æäº¤äººç»ç» */ @ApiModelProperty("æäº¤äººç»ç»") @Excel(name = "æäº¤äººç»ç»") private String submitOrg; /** * 夿³¨1 */ @ApiModelProperty("夿³¨1") @Excel(name = "夿³¨1") private String remarkOne; /** * 夿³¨2 */ @ApiModelProperty("夿³¨2") @Excel(name = "夿³¨2") private String remarkTwo; /** * å建人 */ @ApiModelProperty("å建人") @Excel(name = "å建人", type = Excel.Type.EXPORT) private String creatorName; /** * ä¿®æ¹äºº */ @ApiModelProperty("ä¿®æ¹äºº") @Excel(name = "ä¿®æ¹äºº", type = Excel.Type.EXPORT) private String modifierName; /** * æ°æ®åæ¥ç±»åï¼1=æå¨ 2=宿¶ä»»å¡ */ @ApiModelProperty("æ°æ®åæ¥ç±»åï¼1=æå¨ 2=宿¶ä»»å¡") private Integer dataSyncType; /** * æ°æ®æ¥æºç±»åï¼1=忥 2=æ°å¢ */ @ApiModelProperty("æ°æ®æ¥æºç±»åï¼1=忥 2=æ°å¢") private Integer dataSourceType; /** * ä¸åæ°é */ @ApiModelProperty("ä¸åæ°é") @Excel(name = "ä¸åæ°é", type = Excel.Type.EXPORT) private BigDecimal assignedQuantity; } src/main/java/com/ruoyi/productionPlan/dto/ProductionPlanSummaryDto.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,70 @@ package com.ruoyi.productionPlan.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/03/11 10:24 */ @Data @ApiModel("çäº§éæ±äº§åæ±æ»") public class ProductionPlanSummaryDto { /** * ç©æç¼ç */ @ApiModelProperty("ç©æç¼ç ") private String materialCode; /** * 产ååç§° */ @ApiModelProperty("产ååç§°") private String productName; /** * 产åè§æ ¼ */ @ApiModelProperty("产åè§æ ¼") private String productSpec; /** * 产åé¿åº¦ */ @ApiModelProperty("产åé¿åº¦") private Integer length; /** * 产å宽度 */ @ApiModelProperty("产å宽度") private Integer width; /** * 产åé«åº¦ */ @ApiModelProperty("产åé«åº¦") private Integer height; /** * æ±æ»åæ° */ @ApiModelProperty("æ±æ»åæ°") private Integer quantity; /** * æ±æ»æ¹æ° */ @ApiModelProperty("æ±æ»æ¹æ°") private BigDecimal volume; } src/main/java/com/ruoyi/productionPlan/enums/DataSourceTypeEnum.java
@@ -14,8 +14,8 @@ @Getter public enum DataSourceTypeEnum { SALES_ORDER(1, "éå®è®¢å"), PRODUCTION_FORECAST(2, "çäº§é¢æµ"); SALES_ORDER(1, "忥"), PRODUCTION_FORECAST(2, "æ°å¢"); private final Integer code; private final String desc; src/main/java/com/ruoyi/productionPlan/mapper/ProductOrderPlanMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,18 @@ package com.ruoyi.productionPlan.mapper; import com.ruoyi.productionPlan.pojo.ProductOrderPlan; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; /** * <p> * Mapper æ¥å£ * </p> * * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå ¬å¸ * @since 2026-03-11 03:02:58 */ @Mapper public interface ProductOrderPlanMapper extends BaseMapper<ProductOrderPlan> { } src/main/java/com/ruoyi/productionPlan/mapper/ProductionPlanMapper.java
@@ -4,9 +4,11 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.productionPlan.dto.ProductionPlanDto; import com.ruoyi.productionPlan.dto.ProductionPlanSummaryDto; import com.ruoyi.productionPlan.pojo.ProductionPlan; import com.ruoyi.staff.dto.StaffLeaveDto; import org.apache.ibatis.annotations.Param; import java.util.List; /** * <br> @@ -19,4 +21,7 @@ */ public interface ProductionPlanMapper extends BaseMapper<ProductionPlan> { IPage<ProductionPlanDto> listPage(Page page, @Param("c") ProductionPlanDto productionPlanDto); List<ProductionPlanSummaryDto> selectSummaryByProductType(ProductionPlanSummaryDto query); } src/main/java/com/ruoyi/productionPlan/pojo/ProductOrderPlan.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,54 @@ package com.ruoyi.productionPlan.pojo; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDateTime; import com.ruoyi.framework.aspectj.lang.annotation.Excel; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; /** * <p> * * </p> * * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå ¬å¸ * @since 2026-03-11 03:02:58 */ @Getter @Setter @TableName("product_order_plan") @ApiModel(value = "ProductOrderPlan对象", description = "") public class ProductOrderPlan implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.AUTO) private Long id; @ApiModelProperty("ç产订åid") private Long productOrderId; @ApiModelProperty("ç产计åid") private Long productionPlanId; @ApiModelProperty(value = "ä¸åæ°é") @Excel(name = "ä¸åæ°é") private BigDecimal assignedQuantity; @ApiModelProperty("å½å ¥æ¶é´") @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; @ApiModelProperty("æ´æ°æ¶é´") @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; } src/main/java/com/ruoyi/productionPlan/pojo/ProductionPlan.java
@@ -4,10 +4,14 @@ import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import com.ruoyi.framework.aspectj.lang.annotation.Excel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.Date; /** * <br> @@ -31,146 +35,222 @@ /** * 表åå®ä¾ID */ @ApiModelProperty("表åå®ä¾ID") private String formInstanceId; /** * æµæ°´å· */ @ApiModelProperty("æµæ°´å·") private String serialNo; /** * ç³è¯·åç¼å· */ @ApiModelProperty("ç³è¯·åç¼å·") @Excel(name = "ç³è¯·åç¼å·") private String applyNo; /** * 客æ·åç§° */ @ApiModelProperty("客æ·åç§°") @Excel(name = "客æ·åç§°") private String customerName; /** * ç©æç¼ç */ @ApiModelProperty("ç©æç¼ç ") @Excel(name = "ç©æç¼ç ") private String materialCode; /** * å ³èç©æä¿¡æ¯è¡¨ID */ @ApiModelProperty("å ³èç©æä¿¡æ¯è¡¨ID") private Integer productMaterialId; /** * 产ååç§° */ @ApiModelProperty("产ååç§°") @Excel(name = "产ååç§°") private String productName; /** * 产åè§æ ¼ */ @ApiModelProperty("产åè§æ ¼") @Excel(name = "产åè§æ ¼") private String productSpec; /** * é¿ */ @ApiModelProperty("é¿") @Excel(name = "é¿") private Integer length; /** * 宽 */ @ApiModelProperty("宽") @Excel(name = "宽") private Integer width; /** * é« */ @ApiModelProperty("é«") @Excel(name = "é«") private Integer height; /** * åæ° */ @ApiModelProperty("åæ°") @Excel(name = "åæ°") private Integer quantity; /** * æ¹æ° */ @ApiModelProperty("æ¹æ°") @Excel(name = "æ¹æ°") private BigDecimal volume; /** * 强度 */ @ApiModelProperty("强度") @Excel(name = "强度") private String strength; /** * å¼å§æ¥æ */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime startDate; @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") @ApiModelProperty("å¼å§æ¥æ") @Excel(name = "å¼å§æ¥æ", width = 20, dateFormat = "yyyy-MM-dd") @DateTimeFormat(pattern = "yyyy-MM-dd") private Date startDate; /** * ç»ææ¥æ */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime endDate; @ApiModelProperty("ç»ææ¥æ") @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") @Excel(name = "ç»ææ¥æ", width = 20, dateFormat = "yyyy-MM-dd") @DateTimeFormat(pattern = "yyyy-MM-dd") private Date endDate; /** * æäº¤äºº */ @ApiModelProperty("æäº¤äºº") @Excel(name = "æäº¤äºº") private String submitter; /** * æäº¤äººç»ç» */ @ApiModelProperty("æäº¤äººç»ç»") @Excel(name = "æäº¤äººç»ç»") private String submitOrg; /** * 夿³¨1 */ @ApiModelProperty("夿³¨1") @Excel(name = "夿³¨1") private String remarkOne; /** * 夿³¨2 */ @ApiModelProperty("夿³¨2") @Excel(name = "夿³¨2") private String remarkTwo; /** * å建人 */ @ApiModelProperty("å建人") @Excel(name = "å建人") private String creatorName; /** * ä¿®æ¹äºº */ @ApiModelProperty("ä¿®æ¹äºº") @Excel(name = "ä¿®æ¹äºº") private String modifierName; /** * 表åå建æ¶é´ */ @ApiModelProperty("表åå建æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime formCreatedTime; /** * 表åä¿®æ¹æ¶é´ */ @ApiModelProperty("表åä¿®æ¹æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime formModifiedTime; /** * æ°æ®åæ¥ç±»åï¼1=æå¨ 2=宿¶ä»»å¡ */ @ApiModelProperty("æ°æ®åæ¥ç±»åï¼1=æå¨ 2=宿¶ä»»å¡") private Integer dataSyncType; /** * æ°æ®æ¥æºç±»åï¼1=éå®è®¢å 2=éå®é¢æµ * æ°æ®æ¥æºç±»åï¼1=忥 2=æ°å¢ */ @ApiModelProperty("æ°æ®æ¥æºç±»åï¼1=忥 2=æ°å¢") private Integer dataSourceType; /** * æ°æ®åºå建æ¶é´ */ @ApiModelProperty("æ°æ®åºå建æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime createTime; /** * æ°æ®åºæ´æ°æ¶é´ */ @ApiModelProperty("æ°æ®åºæ´æ°æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime updateTime; /** * å½åæ´æ°æ°é */ @ApiModelProperty("totalCount") private Integer totalCount; @ApiModelProperty(value = "ä¸åæ°é") @Excel(name = "ä¸åæ°é") private BigDecimal assignedQuantity; /** * 计ç®å©ä½æ¹æ° * * @return å©ä½æ¹æ° */ @ApiModelProperty(value = "å©ä½æ¹æ°") public BigDecimal getRemainingVolume() { if (volume == null) { return BigDecimal.ZERO; } if (assignedQuantity == null) { return volume; } return volume.subtract(assignedQuantity); } } src/main/java/com/ruoyi/productionPlan/service/ProductOrderPlanService.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,16 @@ package com.ruoyi.productionPlan.service; import com.ruoyi.productionPlan.pojo.ProductOrderPlan; import com.baomidou.mybatisplus.extension.service.IService; /** * <p> * æå¡ç±» * </p> * * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå ¬å¸ * @since 2026-03-11 03:02:58 */ public interface ProductOrderPlanService extends IService<ProductOrderPlan> { } src/main/java/com/ruoyi/productionPlan/service/ProductionPlanService.java
@@ -4,7 +4,12 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.productionPlan.dto.ProductionPlanDto; import com.ruoyi.productionPlan.dto.ProductionPlanSummaryDto; import com.ruoyi.productionPlan.pojo.ProductionPlan; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.util.List; /** * <br> @@ -27,4 +32,39 @@ * 宿¶åæ¥ */ void syncProdDataJob(); /** * åå¹¶ç产计å */ boolean combine(ProductionPlanDto productionPlanDto); /** * å建ç产计å */ boolean add(ProductionPlanDto productionPlanDto); /** * æ´æ°ç产计å */ boolean update(ProductionPlanDto productionPlanDto); /** * å é¤ç产计å */ boolean delete(List<Long> ids); /** * æç §äº§åç±»å«æ±æ»ç»è®¡éæ±é */ List<ProductionPlanSummaryDto> summaryByProductType(ProductionPlanSummaryDto query); /** * å¯¼å ¥æ°æ® */ void importProdData(MultipartFile file); /** * å¯¼åºæ°æ® */ void exportProdData(HttpServletResponse response, List<Long> ids); } src/main/java/com/ruoyi/productionPlan/service/impl/ProductOrderPlanServiceImpl.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,20 @@ package com.ruoyi.productionPlan.service.impl; import com.ruoyi.productionPlan.pojo.ProductOrderPlan; import com.ruoyi.productionPlan.mapper.ProductOrderPlanMapper; import com.ruoyi.productionPlan.service.ProductOrderPlanService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; /** * <p> * æå¡å®ç°ç±» * </p> * * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå ¬å¸ * @since 2026-03-11 03:02:58 */ @Service public class ProductOrderPlanServiceImpl extends ServiceImpl<ProductOrderPlanMapper, ProductOrderPlan> implements ProductOrderPlanService { } src/main/java/com/ruoyi/productionPlan/service/impl/ProductionPlanServiceImpl.java
@@ -5,30 +5,45 @@ import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.exception.base.BaseException; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.bean.BeanUtils; import com.ruoyi.common.utils.http.HttpUtils; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.framework.config.AliDingConfig; import com.ruoyi.production.pojo.ProductMaterial; import com.ruoyi.production.pojo.ProductOrder; import com.ruoyi.production.service.ProductMaterialService; import com.ruoyi.production.service.ProductOrderService; import com.ruoyi.productionPlan.dto.ProductionPlanDto; import com.ruoyi.productionPlan.dto.ProductionPlanImportDto; import com.ruoyi.productionPlan.dto.ProductionPlanSummaryDto; import com.ruoyi.productionPlan.mapper.ProductOrderPlanMapper; import com.ruoyi.productionPlan.mapper.ProductionPlanMapper; import com.ruoyi.productionPlan.pojo.ProductOrderPlan; import com.ruoyi.productionPlan.pojo.ProductionPlan; import com.ruoyi.productionPlan.service.ProductionPlanService; 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 javax.servlet.http.HttpServletResponse; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneId; import java.time.*; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.List; import java.util.*; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import static com.ruoyi.productionPlan.enums.DataSourceTypeEnum.PRODUCTION_FORECAST; /** * <br> @@ -49,6 +64,15 @@ @Autowired private ProductionPlanMapper productionPlanMapper; @Autowired private ProductOrderService productOrderService; @Autowired private ProductOrderPlanMapper productOrderPlanMapper; @Autowired private ProductMaterialService productMaterialService; /** * 忥éï¼ç¡®ä¿æå¨å宿¶ä»»å¡ä¸åæ¶æ§è¡ @@ -74,6 +98,123 @@ @Override public void syncProdDataJob() { syncProdData(2); } /** * åå¹¶ç产计å */ @Override @Transactional(rollbackFor = Exception.class) public boolean combine(ProductionPlanDto productionPlanDto) { if (productionPlanDto.getIds() == null || productionPlanDto.getIds().isEmpty()) { return false; } // æ¥è¯¢ä¸»ç产计å List<ProductionPlan> plans = productionPlanMapper.selectBatchIds(productionPlanDto.getIds()); plans.sort(Comparator.comparingLong(ProductionPlan::getId)); // æ ¡éªæ¯å¦åå¨ä¸åç产ååç§° String firstProductName = plans.get(0).getProductName(); if (plans.stream().anyMatch(p -> !p.getProductName().equals(firstProductName))) { throw new BaseException("å并失败ï¼åå¨ä¸åç产ååç§°"); } // æ ¡éªæ¯å¦åå¨ä¸åç产åè§æ ¼ String firstProductSpec = plans.get(0).getProductSpec(); if (plans.stream().anyMatch(p -> !p.getProductSpec().equals(firstProductSpec))) { throw new BaseException("å并失败ï¼åå¨ä¸åç产åè§æ ¼"); } // å å å©ä½æ¹æ° BigDecimal totalRemainingVolume = plans.stream() .map(ProductionPlan::getRemainingVolume) .filter(v -> v != null) .reduce(BigDecimal.ZERO, BigDecimal::add); // 夿ä¸åæ°éæ¯å¦å¤§äºçäºå©ä½æ¹æ° if (productionPlanDto.getTotalAssignedQuantity().compareTo(totalRemainingVolume) > 0) { throw new BaseException("æä½å¤±è´¥ï¼ä¸åæ°éä¸è½å¤§äºå©ä½æ¹æ°"); } // å建ç产订å ProductOrder productOrder = new ProductOrder(); productOrder.setQuantity(productionPlanDto.getTotalAssignedQuantity()); productOrder.setPlanCompleteTime(productionPlanDto.getPlanCompleteTime()); productOrderService.addProductOrder(productOrder); // æ ¹æ®ä¸åæ°éï¼ä»ç¬¬ä¸ä¸ªç产计åå¼å§åé æ¹æ° BigDecimal assignedVolume = BigDecimal.ZERO; for (ProductionPlan plan : plans) { BigDecimal volume = plan.getVolume(); if (volume == null) { continue; } // 计ç®å©ä½æ¹æ° BigDecimal remainingVolume = plan.getRemainingVolume(); if (remainingVolume.compareTo(BigDecimal.ZERO) <= 0) { continue; } ProductOrderPlan productOrderPlan = new ProductOrderPlan(); productOrderPlan.setProductOrderId(productOrder.getId()); productOrderPlan.setProductionPlanId(plan.getId()); if (assignedVolume.add(remainingVolume).compareTo(productionPlanDto.getTotalAssignedQuantity()) >= 0) { // æåä¸ä¸ªè®¡åï¼åé å©ä½æ¹æ° BigDecimal lastRemainingVolume = productionPlanDto.getTotalAssignedQuantity().subtract(assignedVolume); plan.setAssignedQuantity(plan.getAssignedQuantity().add(lastRemainingVolume)); productOrderPlan.setAssignedQuantity(lastRemainingVolume); productionPlanMapper.updateById(plan); productOrderPlanMapper.insert(productOrderPlan); break; } // åé å½åè®¡åæ¹æ° plan.setAssignedQuantity(plan.getAssignedQuantity().add(remainingVolume)); productOrderPlan.setAssignedQuantity(remainingVolume); // æ´æ°ç产计å productionPlanMapper.updateById(plan); // åå»ºå ³èå ³ç³» productOrderPlanMapper.insert(productOrderPlan); assignedVolume = assignedVolume.add(remainingVolume); } return true; } @Override @Transactional(rollbackFor = Exception.class) public boolean add(ProductionPlanDto productionPlanDto) { productionPlanDto.setDataSourceType(PRODUCTION_FORECAST.getCode()); productionPlanMapper.insert(productionPlanDto); return true; } @Override @Transactional(rollbackFor = Exception.class) public boolean update(ProductionPlanDto productionPlanDto) { // æ¥è¯¢æ¯å¦æå ³è订å boolean hasProductOrderPlan = productOrderPlanMapper.selectList(Wrappers.<ProductOrderPlan>lambdaQuery().eq(ProductOrderPlan::getProductionPlanId, productionPlanDto.getId())).stream().anyMatch(p -> p.getProductOrderId() != null); if (hasProductOrderPlan) { // å¦æå ³èï¼æ¹æ°åªè½éå¢ ProductionPlan currentPlan = productionPlanMapper.selectById(productionPlanDto.getId()); if (productionPlanDto.getVolume().compareTo(currentPlan.getVolume()) < 0) { throw new BaseException("æ¹æ°ä¸è½éå"); } } return productionPlanMapper.updateById(productionPlanDto) > 0; } @Override @Transactional(rollbackFor = Exception.class) public boolean delete(List<Long> ids) { // 妿æå ³è订åï¼åä¸è½å é¤ if (productOrderPlanMapper.selectList(Wrappers.<ProductOrderPlan>lambdaQuery().in(ProductOrderPlan::getProductionPlanId, ids)).stream().anyMatch(p -> p.getProductOrderId() != null)) { throw new BaseException("å é¤å¤±è´¥ï¼åå¨å ³è订å"); } return productionPlanMapper.deleteBatchIds(ids) > 0; } /** @@ -133,8 +274,8 @@ List<ProductionPlan> list = parseProductionPlans(dataArr, dataSyncType, totalCount); if (!list.isEmpty()) { // å¤çæ´æ°ææ°å¢ processSaveOrUpdate(list); totalSynced += list.size(); int affected = processSaveOrUpdate(list); totalSynced += affected; } // 夿æ¯å¦è¿æä¸ä¸é¡µ @@ -178,9 +319,23 @@ searchParam.put("appType", aliDingConfig.getAppType()); searchParam.put("systemToken", aliDingConfig.getSystemToken()); searchParam.put("userId", aliDingConfig.getUserId()); searchParam.put("formUuid", aliDingConfig.getFormUuid()); searchParam.put("formUuid", aliDingConfig.getProducePlanFormUuid()); searchParam.put("currentPage", pageNumber); searchParam.put("pageSize", pageSize); searchParam.put("pageNumber", pageNumber); JSONArray searchConditions = new JSONArray(); JSONObject condition = new JSONObject(); condition.put("key", "processApprovedResult"); JSONArray valueArray = new JSONArray(); valueArray.add("agree"); condition.put("value", valueArray); condition.put("type", "ARRAY"); condition.put("operator", "in"); condition.put("componentName", "SelectField"); searchConditions.add(condition); searchParam.put("searchFieldJson", searchConditions.toJSONString()); // é»è®¤æä¿®æ¹æ¶é´ååºæåºï¼ç¡®ä¿å页æåæ°æ®çè¿ç»æ§ // "+" 表示ååºï¼"gmt_modified" æ¯å®æ¹å ç½®åæ®µ @@ -230,7 +385,19 @@ plan.setApplyNo(formData.getString("textField_l7fytfco")); plan.setCustomerName(formData.getString("textField_lbkozohg")); plan.setMaterialCode(row.getString("textField_l9xo62q5")); String materialCode = row.getString("textField_l9xo62q5"); plan.setMaterialCode(materialCode); // æ ¹æ®ç©æç¼ç æ¥è¯¢ç©æä¿¡æ¯è¡¨ï¼å ³èç©æID if (StringUtils.isNotEmpty(materialCode)) { LambdaQueryWrapper<ProductMaterial> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(ProductMaterial::getMaterialCode, materialCode); ProductMaterial productMaterial = productMaterialService.getOne(queryWrapper); if (productMaterial != null) { plan.setProductMaterialId(productMaterial.getId()); } } plan.setProductName(row.getString("textField_l9xo62q7")); plan.setProductSpec(row.getString("textField_l9xo62q8")); plan.setLength(row.getInteger("numberField_lb7lgatg_value")); @@ -245,8 +412,20 @@ try { long start = Long.parseLong(dateArr.getString(0)); long end = Long.parseLong(dateArr.getString(1)); plan.setStartDate(Instant.ofEpochMilli(start).atZone(ZoneId.systemDefault()).toLocalDateTime()); plan.setEndDate(Instant.ofEpochMilli(end).atZone(ZoneId.systemDefault()).toLocalDateTime()); Date startDate = Date.from(Instant.ofEpochMilli(start) .atZone(ZoneId.systemDefault()) .toLocalDate() .atStartOfDay(ZoneId.systemDefault()) .toInstant()); Date endDate = Date.from(Instant.ofEpochMilli(end) .atZone(ZoneId.systemDefault()) .toLocalDate() .atStartOfDay(ZoneId.systemDefault()) .toInstant()); plan.setStartDate(startDate); plan.setEndDate(endDate); } catch (Exception e) { log.warn("è§£ææ¥æå¤±è´¥: {}", dateArr); } @@ -277,19 +456,48 @@ return list; } private void processSaveOrUpdate(List<ProductionPlan> list) { private int processSaveOrUpdate(List<ProductionPlan> list) { if (list == null || list.isEmpty()) { return 0; } int affected = 0; // å»é formInstanceId Set<String> formIds = list.stream() .map(ProductionPlan::getFormInstanceId) .collect(Collectors.toSet()); // æ¥è¯¢æ°æ®åºå·²ææ°æ® List<ProductionPlan> existList = this.list(new LambdaQueryWrapper<ProductionPlan>().in(ProductionPlan::getFormInstanceId, formIds)); // Map (formInstanceId + materialCode) Map<String, ProductionPlan> existMap = new HashMap<>(); for (ProductionPlan p : existList) { String key = p.getFormInstanceId() + "_" + p.getMaterialCode(); existMap.put(key, p); } // éååæ¥æ°æ® for (ProductionPlan plan : list) { LambdaQueryWrapper<ProductionPlan> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(ProductionPlan::getFormInstanceId, plan.getFormInstanceId()) .eq(ProductionPlan::getMaterialCode, plan.getMaterialCode()); ProductionPlan existing = this.getOne(queryWrapper); if (existing != null) { plan.setId(existing.getId()); this.updateById(plan); } else { String key = plan.getFormInstanceId() + "_" + plan.getMaterialCode(); ProductionPlan exist = existMap.get(key); if (exist == null) { // æ°å¢ this.save(plan); affected++; log.info("æ°å¢æ°æ® formInstanceId={}, materialCode={}", plan.getFormInstanceId(), plan.getMaterialCode()); } else { // 夿æ¯å¦éè¦æ´æ° if (exist.getFormModifiedTime() == null || !exist.getFormModifiedTime().equals(plan.getFormModifiedTime())) { plan.setId(exist.getId()); plan.setCreateTime(exist.getCreateTime()); this.updateById(plan); affected++; log.info("æ´æ°æ°æ® formInstanceId={}, materialCode={}", plan.getFormInstanceId(), plan.getMaterialCode()); } } } return affected; } private LocalDateTime parseUtcTime(String utcString) { @@ -304,4 +512,62 @@ return null; } } @Override public List<ProductionPlanSummaryDto> summaryByProductType(ProductionPlanSummaryDto query) { return baseMapper.selectSummaryByProductType(query); } @Override @Transactional(rollbackFor = Exception.class) public void importProdData(MultipartFile file) { if (file == null || file.isEmpty()) { throw new ServiceException("å¯¼å ¥æ°æ®ä¸è½ä¸ºç©º"); } ExcelUtil<ProductionPlanImportDto> excelUtil = new ExcelUtil<>(ProductionPlanImportDto.class); List<ProductionPlanImportDto> list; try { list = excelUtil.importExcel(file.getInputStream()); } catch (Exception e) { log.error("çäº§éæ±Excelå¯¼å ¥å¤±è´¥", e); throw new ServiceException("Excelè§£æå¤±è´¥"); } if (list == null || list.isEmpty()) { throw new ServiceException("Excelæ²¡ææ°æ®"); } List<ProductionPlan> entityList = new ArrayList<>(list.size()); ProductionPlan entity; for (ProductionPlanImportDto dto : list) { entity = new ProductionPlan(); BeanUtils.copyProperties(dto, entity); entity.setAssignedQuantity(BigDecimal.ZERO); entity.setCreateTime(LocalDateTime.now()); entity.setUpdateTime(LocalDateTime.now()); entity.setDataSourceType(2); entity.setDataSyncType(1); entityList.add(entity); } this.saveBatch(entityList); } @Override public void exportProdData(HttpServletResponse response, List<Long> ids) { List<ProductionPlan> list; if (ids != null && !ids.isEmpty()) { list = baseMapper.selectBatchIds(ids); } else { list = baseMapper.selectList(null); } List<ProductionPlanImportDto> exportList = new ArrayList<>(); for (ProductionPlan entity : list) { ProductionPlanImportDto dto = new ProductionPlanImportDto(); BeanUtils.copyProperties(entity, dto); exportList.add(dto); } ExcelUtil<ProductionPlanImportDto> util = new ExcelUtil<>(ProductionPlanImportDto.class); util.exportExcel(response, exportList, "éå®çäº§éæ±æ°æ®"); } } src/main/java/com/ruoyi/sales/service/impl/SalesLedgerProductServiceImpl.java
@@ -267,9 +267,9 @@ */ public void addProductionData(SalesLedgerProduct salesLedgerProduct) { ProductOrder productOrder = new ProductOrder(); productOrder.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId()); productOrder.setProductModelId(salesLedgerProduct.getProductModelId()); productOrder.setSaleLedgerProductId(salesLedgerProduct.getId()); // productOrder.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId()); // productOrder.setProductModelId(salesLedgerProduct.getProductModelId()); // productOrder.setSaleLedgerProductId(salesLedgerProduct.getId()); String string = productOrderServiceImpl.generateNextOrderNo(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))); productOrder.setNpsNo(string); productOrder.setQuantity(salesLedgerProduct.getQuantity());//éæ±æ°é @@ -340,7 +340,7 @@ //æ¹éæ¥è¯¢productOrder List<ProductOrder> productOrders = productOrderMapper.selectList( new LambdaQueryWrapper<ProductOrder>() .in(ProductOrder::getSaleLedgerProductId, productIds) // .in(ProductOrder::getSaleLedgerProductId, productIds) ); if (!org.springframework.util.CollectionUtils.isEmpty(productOrders)) { List<Long> orderIds = productOrders.stream() @@ -421,8 +421,8 @@ .in(ProductProcessRoute::getProductOrderId, orderIds)); // æ¹éå é¤productOrder productOrderMapper.delete(new LambdaQueryWrapper<ProductOrder>() .in(ProductOrder::getSaleLedgerProductId, productIds)); // productOrderMapper.delete(new LambdaQueryWrapper<ProductOrder>() // .in(ProductOrder::getSaleLedgerProductId, productIds)); } } src/main/resources/application-zsjc.yml
@@ -30,7 +30,8 @@ app-secret: "fO07qSZC5SMLw9It3Ydd3BuoFyVbRlsWXUnVr2kwPJXz0OpUntCAO5dqnr8G7zq5" user-id: "290166234126410562" app-type: "APP_UY8XMO7GNA8OF08EFNVQ" form-uuid: "FORM-4IA66891C5H3QWMDBSGO05C0OX9628GRPYF7L8" produce-plan-form-uuid: "FORM-4IA66891C5H3QWMDBSGO05C0OX9628GRPYF7L8" material-code-form-uuid: "FORM-8I666N718RW4ALO4ETYDDB0U6U8T3EIWTQ0ALL1" system-token: "4J766L91OFH3V612E7LG5B4DI8M13MQF9VF7LG4" # æ¥å£å°å access-token-url: "https://oapi.dingtalk.com/gettoken" src/main/resources/mapper/energy/EnergyConsumptionDetailMapper.xml
@@ -26,16 +26,92 @@ left join energy e on ecd.energy_id = e.id left join sys_user su on ecd.create_user = su.user_id) A <where> <if test="c.energyTyep != null and c.energyTyep != ''"> <if test="c.type != null and c.type != ''"> and type =#{c.type} </if> <if test="c.energyTyep != null and c.energyTyep != ''"> and energy_tyep like concat('%',#{c.energyTyep},'%') </if> </if> <if test="c.energyName != null and c.energyName != ''"> and energy_name like concat('%',#{c.energyName},'%') </if> </if> <if test="c.meterReadingDate != null and c.meterReadingDate != ''"> and meter_reading_date =#{c.meterReadingDate} </if> </where> </select> <select id="calculateEnergy" resultType="java.util.Map"> select SUM(ecd.dosage) totalEnergyConsumption, SUM(ecd.dosage * e.unit_price) totalEnergyCost from energy_consumption_detail ecd left join energy e on ecd.energy_id = e.id where ecd.meter_reading_date between #{c.startDate} and #{c.endDate} <if test="c.type != null and c.type != ''"> and ecd.type =#{c.type} </if> </where> </select> <select id="energyConsumptionTypeProportion" resultType="com.ruoyi.energy.dto.EnergyConsumptionTypeDto"> select e.energy_tyep, SUM(ecd.dosage) energyConsumption, SUM(ecd.dosage * e.unit_price) energyCost from energy_consumption_detail ecd left join energy e on ecd.energy_id = e.id where ecd.meter_reading_date between #{c.startDate} and #{c.endDate} <if test="c.type != null and c.type != ''"> and ecd.type =#{c.type} </if> group by e.energy_tyep </select> <select id="energyCostDtos" resultType="com.ruoyi.energy.dto.EnergyCostDto"> select A.meter_reading_date, A.waterConsumption, A.waterCost, B.electricityConsumption, B.electricityCost, C.gasConsumption, C.gasCost, sum(A.waterConsumption+B.electricityConsumption+C.gasConsumption) totalConsumption, sum(A.waterCost+B.electricityCost+C.gasCost) totalCost from (select ecd.meter_reading_date, sum(ecd.dosage) waterConsumption, sum(ecd.dosage * e1.unit_price) waterCost from energy_consumption_detail ecd left join energy e1 on ecd.energy_id = e1.id where ecd.meter_reading_date between #{c.startDate} and #{c.endDate} and e1.energy_tyep='æ°´' <if test="c.type != null and c.type != ''"> and ecd.type =#{c.type} </if> group by ecd.meter_reading_date)A join (select ecd.meter_reading_date, sum(ecd.dosage) electricityConsumption, sum(ecd.dosage * e2.unit_price) electricityCost from energy_consumption_detail ecd left join energy e2 on ecd.energy_id = e2.id where ecd.meter_reading_date between #{c.startDate} and #{c.endDate} and e2.energy_tyep='çµ' <if test="c.type != null and c.type != ''"> and ecd.type =#{c.type} </if> group by ecd.meter_reading_date)B on A.meter_reading_date=B.meter_reading_date join (select ecd.meter_reading_date, sum(ecd.dosage) gasConsumption, sum(ecd.dosage * e3.unit_price) gasCost from energy_consumption_detail ecd left join energy e3 on ecd.energy_id = e3.id where ecd.meter_reading_date between #{c.startDate} and #{c.endDate} and e3.energy_tyep='æ°' <if test="c.type != null and c.type != ''"> and ecd.type =#{c.type} </if> group by ecd.meter_reading_date)C on A.meter_reading_date=C.meter_reading_date </select> </mapper> src/main/resources/mapper/production/ProductMaterialMapper.xml
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,25 @@ <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.ruoyi.production.mapper.ProductMaterialMapper"> <resultMap id="ProductMaterialResultMap" type="com.ruoyi.production.pojo.ProductMaterial"> <id property="id" column="id"/> <result property="tenantId" column="tenant_id"/> <result property="materialTypeId" column="material_type_id"/> <result property="inventoryCategoryId" column="inventory_category_id"/> <result property="identifierCode" column="identifier_code"/> <result property="materialCode" column="material_code"/> <result property="materialName" column="material_name"/> <result property="specification" column="specification"/> <result property="baseUnit" column="base_unit"/> <result property="materialAttribute" column="material_attribute"/> <result property="finishedProductName" column="finished_product_name"/> <result property="originatorName" column="originator_name"/> <result property="originatorOrg" column="originator_org"/> <result property="remark" column="remark"/> <result property="createTime" column="create_time"/> <result property="updateTime" column="update_time"/> </resultMap> </mapper> src/main/resources/mapper/production/ProductOrderMapper.xml
@@ -15,50 +15,16 @@ </resultMap> <select id="pageProductOrder" resultType="com.ruoyi.production.dto.ProductOrderDto"> select po.*, sl.sales_contract_no, sl.customer_name, p.product_name as product_category, pm.model as specification_model, pm.unit, ppr.process_route_code, pb.bom_no, ROUND(po.complete_quantity / po.quantity * 100, 2) AS completionStatus, DATEDIFF(sl.delivery_date, CURDATE()) AS delivery_days_diff, sl.delivery_date, CASE WHEN shipping_status_counts.total_count = 0 THEN false WHEN shipping_status_counts.unshipped_count = 0 THEN true ELSE false END AS is_fh ROUND(po.complete_quantity / po.quantity * 100, 2) AS completionStatus from product_order po left join sales_ledger sl on po.sales_ledger_id = sl.id LEFT JOIN ( SELECT sales_ledger_id, COUNT(*) as total_count, SUM(CASE WHEN status != 'å·²åè´§' THEN 1 ELSE 0 END) as unshipped_count FROM shipping_info GROUP BY sales_ledger_id ) shipping_status_counts ON sl.id = shipping_status_counts.sales_ledger_id left join product_model pm on po.product_model_id = pm.id left join product p on pm.product_id = p.id left join sales_ledger_product slp on po.sale_ledger_product_id = slp.id and slp.type = 1 left join product_process_route ppr on po.id = ppr.product_order_id left join product_bom pb on pb.id = ppr.bom_id <where> <if test="c.npsNo != null and c.npsNo != ''"> and po.nps_no like concat('%',#{c.npsNo},'%') </if> <if test="c.salesContractNo != null and c.salesContractNo != ''"> and sl.sales_contract_no like concat('%',#{c.salesContractNo},'%') </if> <if test="c.customerName != null and c.customerName != ''"> and sl.customer_name like concat('%',#{c.customerName},'%') </if> <if test="c.productCategory != null and c.productCategory != ''"> and slp.product_category like concat('%',#{c.productCategory},'%') </if> <if test="c.specificationModel != null and c.specificationModel != ''"> and slp.specification_model like concat('%',#{c.specificationModel},'%') </if> <if test="c.startTime != null and c.endTime != null"> and po.create_time between #{c.startTime} and #{c.endTime} src/main/resources/mapper/productionPlan/ProductOrderPlanMapper.xml
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,14 @@ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.ruoyi.productionPlan.mapper.ProductOrderPlanMapper"> <!-- éç¨æ¥è¯¢æ å°ç»æ --> <resultMap id="BaseResultMap" type="com.ruoyi.productionPlan.pojo.ProductOrderPlan"> <id column="id" property="id" /> <result column="product_order_id" property="productOrderId" /> <result column="production_plan_id" property="productionPlanId" /> <result column="create_time" property="createTime" /> <result column="update_time" property="updateTime" /> </resultMap> </mapper> src/main/resources/mapper/productionPlan/ProductionPlanMapper.xml
@@ -12,6 +12,7 @@ <result property="applyNo" column="apply_no"/> <result property="customerName" column="customer_name"/> <result property="materialCode" column="material_code"/> <result property="productMaterialId" column="product_material_id"/> <result property="productName" column="product_name"/> <result property="productSpec" column="product_spec"/> <result property="length" column="length"/> @@ -38,10 +39,57 @@ </resultMap> <select id="listPage" resultMap="ProductionPlanResultMap"> <select id="listPage" resultType="com.ruoyi.productionPlan.dto.ProductionPlanDto"> SELECT * FROM production_plan FROM production_plan pp WHERE 1 = 1 <if test="c.customerName != null and c.customerName != '' "> AND pp.customer_name LIKE CONCAT('%',#{c.customerName},'%') </if> <if test="c.productName != null and c.productName != '' "> AND pp.product_name LIKE CONCAT('%',#{c.productName},'%') </if> <if test="c.productSpec != null and c.productSpec != '' "> AND pp.product_spec LIKE CONCAT('%',#{c.productSpec},'%') </if> <if test="c.materialCode != null and c.materialCode != '' "> AND pp.material_code LIKE CONCAT('%',#{c.materialCode},'%') </if> <if test="c.startDate != null"> AND pp.start_date >= DATE_FORMAT(#{c.startDate},'%Y-%m-%d') </if> <if test="c.endDate != null"> AND pp.end_date <= DATE_FORMAT(#{c.endDate},'%Y-%m-%d') </if> </select> <select id="selectSummaryByProductType" resultType="com.ruoyi.productionPlan.dto.ProductionPlanSummaryDto"> SELECT material_code, product_name, product_spec, length, width, height, COALESCE(SUM(quantity),0) AS quantity, COALESCE(SUM(volume),0) AS volume FROM production_plan <where> <if test="materialCode != null and materialCode != ''"> AND material_code LIKE CONCAT('%', #{materialCode}, '%') </if> <if test="productName != null and productName != ''"> AND product_name LIKE CONCAT('%', #{productName}, '%') </if> </where> GROUP BY material_code, product_name, product_spec, length, width, height </select> </mapper>