doc/ÄþÏÄ-ÖÐÊ¢½¨²Ä.sql
@@ -82,3 +82,15 @@ `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 datetime(0) NULL DEFAULT NULL COMMENT '计å宿æ¶é´', add combine_production_plan_ids varchar(500) default '' not null; src/main/java/com/ruoyi/production/pojo/ProductOrder.java
@@ -11,6 +11,7 @@ import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; @Data @TableName("product_order") @@ -20,24 +21,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 +34,20 @@ @ApiModelProperty(value = "ç产订åå·") @Excel(name = "ç产订åå·") private String npsNo; /** * åå¹¶ç产计åids */ @ApiModelProperty(value = "åå¹¶ç产计åids") private String combineProductionPlanIds; /** * 计å宿æ¶é´ */ @ApiModelProperty(value = "计å宿æ¶é´") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime planCompleteTime; /** * ç§æ·id @@ -73,7 +70,6 @@ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime updateTime; /** * éæ±æ°é @@ -100,7 +96,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/productionPlan/controller/ProductionPlanController.java
@@ -5,14 +5,15 @@ 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.ProductionPlanSummaryDto; import com.ruoyi.productionPlan.service.ProductionPlanService; import com.ruoyi.staff.dto.StaffLeaveDto; 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 javax.annotation.Resource; import java.util.List; /** * <br> @@ -38,9 +39,32 @@ } @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)); } @GetMapping("/summaryByProductType") @ApiOperation("æç §äº§åç±»å«æ±æ»ç»è®¡éæ±é") @Log(title = "æç §äº§åç±»å«æ±æ»ç»è®¡éæ±é", businessType = BusinessType.OTHER) public AjaxResult summaryByProductType(ProductionPlanSummaryDto query) { List<ProductionPlanSummaryDto> list = productionPlanService.summaryByProductType(query); return AjaxResult.success(list); } } 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.LocalDateTime; 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 HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime planCompleteTime; } 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/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/ProductionPlan.java
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.math.BigDecimal; @@ -31,146 +32,177 @@ /** * 表åå®ä¾ID */ @ApiModelProperty("表åå®ä¾ID") private String formInstanceId; /** * æµæ°´å· */ @ApiModelProperty("æµæ°´å·") private String serialNo; /** * ç³è¯·åç¼å· */ @ApiModelProperty("ç³è¯·åç¼å·") private String applyNo; /** * 客æ·åç§° */ @ApiModelProperty("客æ·åç§°") private String customerName; /** * ç©æç¼ç */ @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; /** * 强度 */ @ApiModelProperty("强度") private String strength; /** * å¼å§æ¥æ */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") @ApiModelProperty("å¼å§æ¥æ") private LocalDateTime startDate; /** * ç»ææ¥æ */ @ApiModelProperty("ç»ææ¥æ") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime endDate; /** * æäº¤äºº */ @ApiModelProperty("æäº¤äºº") private String submitter; /** * æäº¤äººç»ç» */ @ApiModelProperty("æäº¤äººç»ç»") private String submitOrg; /** * 夿³¨1 */ @ApiModelProperty("夿³¨1") private String remarkOne; /** * 夿³¨2 */ @ApiModelProperty("夿³¨2") private String remarkTwo; /** * å建人 */ @ApiModelProperty("å建人") private String creatorName; /** * ä¿®æ¹äºº */ @ApiModelProperty("ä¿®æ¹äºº") 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 = "ä¸åæ°é") private BigDecimal assignedQuantity; } src/main/java/com/ruoyi/productionPlan/service/ProductionPlanService.java
@@ -4,7 +4,10 @@ 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 java.util.List; /** * <br> @@ -27,4 +30,19 @@ * 宿¶åæ¥ */ void syncProdDataJob(); /** * åå¹¶ç产计å */ boolean combine(ProductionPlanDto productionPlanDto); /** * å建ç产计å */ boolean add(ProductionPlanDto productionPlanDto); /** * æç §äº§åç±»å«æ±æ»ç»è®¡éæ±é */ List<ProductionPlanSummaryDto> summaryByProductType(ProductionPlanSummaryDto query); } src/main/java/com/ruoyi/productionPlan/service/impl/ProductionPlanServiceImpl.java
@@ -7,10 +7,14 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.basic.pojo.Product; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.http.HttpUtils; import com.ruoyi.framework.config.AliDingConfig; import com.ruoyi.production.pojo.ProductOrder; import com.ruoyi.production.service.ProductOrderService; import com.ruoyi.productionPlan.dto.ProductionPlanDto; import com.ruoyi.productionPlan.dto.ProductionPlanSummaryDto; import com.ruoyi.productionPlan.mapper.ProductionPlanMapper; import com.ruoyi.productionPlan.pojo.ProductionPlan; import com.ruoyi.productionPlan.service.ProductionPlanService; @@ -19,6 +23,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.time.LocalDateTime; @@ -26,9 +31,11 @@ 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.*; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import static com.ruoyi.productionPlan.enums.DataSourceTypeEnum.PRODUCTION_FORECAST; /** * <br> @@ -49,6 +56,9 @@ @Autowired private ProductionPlanMapper productionPlanMapper; @Autowired private ProductOrderService productOrderService; /** * 忥éï¼ç¡®ä¿æå¨å宿¶ä»»å¡ä¸åæ¶æ§è¡ @@ -74,6 +84,83 @@ @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()); // æ ¡éªæ¯å¦åå¨ä¸åç产ååç§° String firstProductName = plans.get(0).getProductName(); if (plans.stream().anyMatch(p -> !p.getProductName().equals(firstProductName))) { log.warn("å并失败ï¼åå¨ä¸åç产ååç§°"); return false; } // æ ¡éªæ¯å¦åå¨ä¸åç产åè§æ ¼ String firstProductSpec = plans.get(0).getProductSpec(); if (plans.stream().anyMatch(p -> !p.getProductSpec().equals(firstProductSpec))) { log.warn("å并失败ï¼åå¨ä¸åç产åè§æ ¼"); return false; } // å å æ¹æ° BigDecimal totalVolume = plans.stream() .map(ProductionPlan::getVolume) .filter(v -> v != null) .reduce(BigDecimal.ZERO, BigDecimal::add); // 夿ä¸åæ°éæ¯å¦å¤§äºçäºæ¹æ° if (productionPlanDto.getTotalAssignedQuantity().compareTo(totalVolume) > 0) { log.warn("å并失败ï¼ä¸åæ°éä¸è½å¤§äºæ¹æ°"); return false; } // æ ¹æ®ä¸åæ°éï¼ä»ç¬¬ä¸ä¸ªç产计åå¼å§åé æ¹æ° BigDecimal assignedVolume = BigDecimal.ZERO; for (ProductionPlan plan : plans) { BigDecimal volume = plan.getVolume(); if (volume == null) { continue; } if (assignedVolume.add(volume).compareTo(productionPlanDto.getTotalAssignedQuantity()) >= 0) { // æåä¸ä¸ªè®¡åï¼åé å©ä½æ¹æ° plan.setAssignedQuantity(productionPlanDto.getTotalAssignedQuantity().subtract(assignedVolume)); break; } // åé å½åè®¡åæ¹æ° plan.setAssignedQuantity(volume); productionPlanMapper.updateById(plan); assignedVolume = assignedVolume.add(volume); } // å建ç产订å ProductOrder productOrder = new ProductOrder(); String combineIds = StringUtils.join(productionPlanDto.getIds(), ","); productOrder.setCombineProductionPlanIds(combineIds); productOrder.setQuantity(productionPlanDto.getTotalAssignedQuantity()); productOrder.setPlanCompleteTime(productionPlanDto.getPlanCompleteTime()); productOrderService.addProductOrder(productOrder); return true; } @Override @Transactional(rollbackFor = Exception.class) public boolean add(ProductionPlanDto productionPlanDto) { productionPlanDto.setDataSourceType(PRODUCTION_FORECAST.getCode()); productionPlanMapper.insert(productionPlanDto); return true; } /** @@ -133,8 +220,8 @@ List<ProductionPlan> list = parseProductionPlans(dataArr, dataSyncType, totalCount); if (!list.isEmpty()) { // å¤çæ´æ°ææ°å¢ processSaveOrUpdate(list); totalSynced += list.size(); int affected = processSaveOrUpdate(list); totalSynced += affected; } // 夿æ¯å¦è¿æä¸ä¸é¡µ @@ -277,19 +364,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 +420,9 @@ return null; } } @Override public List<ProductionPlanSummaryDto> summaryByProductType(ProductionPlanSummaryDto query) { return baseMapper.selectSummaryByProductType(query); } } 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/mapper/productionPlan/ProductionPlanMapper.xml
@@ -44,4 +44,33 @@ WHERE 1 = 1 </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>