gongchunyi
8 小时以前 ec7d3b867e7b7f5073dda1684a8720424b9da5ad
feat: 生产报工的详情、修改、删除接口
已修改15个文件
534 ■■■■■ 文件已修改
doc/宁夏-中盛建材.sql 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionRecordController.java 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/ProductionProductRouteItemDto.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/ProductionProductRouteItemParamDto.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/ProductionRecordDto.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/mapper/ProductMaterialMapper.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductionProductMain.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductionProductRouteItemParam.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductMaterialService.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductionRecordService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductMaterialServiceImpl.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionRecordServiceImpl.java 402 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductMaterialMapper.xml 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionProductMainMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionProductRouteItemParamMapper.xml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/ÄþÏÄ-ÖÐÊ¢½¨²Ä.sql
@@ -414,4 +414,7 @@
DROP TABLE IF EXISTS process_route_item_instance;
DROP TABLE IF EXISTS process_route_item_param_instance;
DROP TABLE IF EXISTS product_structure_instance;
DROP TABLE IF EXISTS product_structure_instance;
ALTER TABLE `product-inventory-management-zsjc`.`production_product_route_item_param`
    MODIFY COLUMN `order_item_param_id` bigint NULL DEFAULT NULL COMMENT '生产订单绑定的工艺路线工序--参数表ID' AFTER `production_product_route_item_id`;
src/main/java/com/ruoyi/production/controller/ProductionRecordController.java
@@ -8,10 +8,6 @@
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.List;
/**
 * <br>
@@ -37,7 +33,6 @@
        return AjaxResult.success(vo);
    }
    @PostMapping("/add")
    @ApiOperation("生产报工-新增")
    public AjaxResult addProductionRecordService(@RequestBody ProductionRecordDto dto) {
@@ -45,4 +40,25 @@
        return AjaxResult.success();
    }
    @DeleteMapping("/{productMainId}")
    @ApiOperation("生产报工-删除")
    public AjaxResult deleteProductMain(@PathVariable Long productMainId) {
        productionRecordService.deleteProductMain(productMainId);
        return AjaxResult.success();
    }
    @GetMapping("/detail/{productMainId}")
    @ApiOperation("生产报工-详情")
    public AjaxResult detailProductMain(@PathVariable Long productMainId) {
        ProductionRecordDto dto = productionRecordService.detailProductMain(productMainId);
        return AjaxResult.success(dto);
    }
    @PostMapping("/edit")
    @ApiOperation("生产报工-编辑")
    public AjaxResult editProductMain(@RequestBody ProductionRecordDto dto) {
        productionRecordService.editProductMain(dto);
        return AjaxResult.success();
    }
}
src/main/java/com/ruoyi/production/dto/ProductionProductRouteItemDto.java
@@ -26,4 +26,10 @@
    @ApiModelProperty("工序临时附件ID")
    private List<String> files;
    @ApiModelProperty("工序附件详情列表")
    private List<ProductionProductRouteItemFileDto> fileList;
    @ApiModelProperty("删除工序附件ID集合")
    private List<Long> delFileIds;
}
src/main/java/com/ruoyi/production/dto/ProductionProductRouteItemParamDto.java
@@ -1,6 +1,7 @@
package com.ruoyi.production.dto;
import com.ruoyi.production.pojo.ProductionProductRouteItemParam;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -16,4 +17,8 @@
@Data
@EqualsAndHashCode(callSuper = false)
public class ProductionProductRouteItemParamDto extends ProductionProductRouteItemParam {
    @ApiModelProperty("产品名称")
    private String productName;
}
src/main/java/com/ruoyi/production/dto/ProductionRecordDto.java
@@ -21,11 +21,26 @@
@ApiModel(value = "ProductionRecordDto", description = "生产报工记录Dto")
public class ProductionRecordDto {
    @ApiModelProperty("报工主表ID")
    private Long productMainId;
    @ApiModelProperty("生产订单ID")
    private Long productOrderId;
    @ApiModelProperty("生产订单号")
    private String npsNo;
    @ApiModelProperty("产品ID")
    private Long productId;
    @ApiModelProperty("产品编码")
    private String materialCode;
    @ApiModelProperty("产品名称")
    private String productName;
    @ApiModelProperty("规格")
    private String model;
    @ApiModelProperty(value = "岗位人员")
    private String postName;
@@ -36,6 +51,12 @@
    @ApiModelProperty(value = "班次日期")
    private LocalDateTime reportingTime;
    @ApiModelProperty(value = "创建时间")
    private LocalDateTime createTime;
    @ApiModelProperty(value = "更新时间")
    private LocalDateTime updateTime;
    @ApiModelProperty("合格数量")
    private BigDecimal qualifiedQuantity;
src/main/java/com/ruoyi/production/mapper/ProductMaterialMapper.java
@@ -1,6 +1,7 @@
package com.ruoyi.production.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.production.dto.ProductMaterialSkuDto;
import com.ruoyi.production.pojo.ProductMaterial;
/**
@@ -14,6 +15,7 @@
 */
public interface ProductMaterialMapper extends BaseMapper<ProductMaterial> {
    String selectProductModelIdByName(Long productModelId);
    ProductMaterialSkuDto selectProductByModelId(Long productModelId);
    ProductMaterialSkuDto selectProductByProductMainId(Long productOrderId);
}
src/main/java/com/ruoyi/production/pojo/ProductionProductMain.java
@@ -62,7 +62,6 @@
    private String schedule;
    @ApiModelProperty(value = "班次日期")
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime reportingTime;
src/main/java/com/ruoyi/production/pojo/ProductionProductRouteItemParam.java
@@ -37,6 +37,9 @@
    @ApiModelProperty(value = "生产报工记录的工序表ID")
    private Long productionProductRouteItemId;
    @ApiModelProperty(value = "生产订单绑定的工艺路线工序--参数表ID")
    private Long orderItemParamId;
    @ApiModelProperty(value = "参数名称")
    private String paramName;
@@ -58,6 +61,9 @@
    @ApiModelProperty(value = "最大值")
    private BigDecimal maxValue;
    @ApiModelProperty(value = "参数值")
    private String paramValue;
    @ApiModelProperty(value = "产品ID")
    private Long productId;
src/main/java/com/ruoyi/production/service/ProductMaterialService.java
@@ -2,6 +2,7 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.production.dto.ProductMaterialGroupDto;
import com.ruoyi.production.dto.ProductMaterialSkuDto;
import com.ruoyi.production.pojo.ProductMaterial;
import java.util.List;
@@ -31,6 +32,7 @@
    List<ProductMaterialGroupDto> productMaterialListByQuery(String materialName, Integer materialTypeId);
    String selectProductModelIdByName(Long productId);
    ProductMaterialSkuDto selectProductByModelId(Long productModelId);
    ProductMaterialSkuDto selectProductByProductMainId(Long productOrderId);
}
src/main/java/com/ruoyi/production/service/ProductionRecordService.java
@@ -18,4 +18,10 @@
    void addProductionRecordService(ProductionRecordDto dto);
    void deleteProductMain(Long productMainId);
    ProductionRecordDto detailProductMain(Long productMainId);
    void editProductMain(ProductionRecordDto dto);
}
src/main/java/com/ruoyi/production/service/impl/ProductMaterialServiceImpl.java
@@ -11,6 +11,7 @@
import com.ruoyi.framework.util.AliDingUtils;
import com.ruoyi.production.dto.ProductMaterialDto;
import com.ruoyi.production.dto.ProductMaterialGroupDto;
import com.ruoyi.production.dto.ProductMaterialSkuDto;
import com.ruoyi.production.enums.MaterialConfigTypeEnum;
import com.ruoyi.production.mapper.ProductMaterialMapper;
import com.ruoyi.production.pojo.ProductMaterial;
@@ -410,8 +411,13 @@
    }
    @Override
    public String selectProductModelIdByName(Long productId) {
        return baseMapper.selectProductModelIdByName(productId);
    public ProductMaterialSkuDto selectProductByModelId(Long productModelId) {
        return baseMapper.selectProductByModelId(productModelId);
    }
    @Override
    public ProductMaterialSkuDto selectProductByProductMainId(Long productOrderId) {
        return baseMapper.selectProductByProductMainId(productOrderId);
    }
    private void validateProductMaterial(ProductMaterial productMaterial, boolean requireId) {
src/main/java/com/ruoyi/production/service/impl/ProductionRecordServiceImpl.java
@@ -7,9 +7,7 @@
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.other.mapper.TempFileMapper;
import com.ruoyi.other.pojo.TempFile;
import com.ruoyi.production.dto.ProductionProductRouteItemDto;
import com.ruoyi.production.dto.ProductionProductRouteItemParamDto;
import com.ruoyi.production.dto.ProductionRecordDto;
import com.ruoyi.production.dto.*;
import com.ruoyi.production.enums.ProductOrderStatusEnum;
import com.ruoyi.production.pojo.*;
import com.ruoyi.production.service.*;
@@ -197,21 +195,21 @@
            throw new ServiceException("报工失败,生产订单不存在");
        }
        //  å½“前报工合格数量
        //  å½“前报工合格数量(前端已保证 = æŠ•入数量 - ä¸åˆæ ¼æ•°é‡ï¼Œç›´æŽ¥ä½¿ç”¨ï¼‰
        BigDecimal qualifiedQty = dto.getQualifiedQuantity() == null ? BigDecimal.ZERO : dto.getQualifiedQuantity();
        //  å·²å®Œæˆæ•°é‡
        //  è®¢å•已完成数量(历史累计)
        BigDecimal completeQty = productOrder.getCompleteQuantity() == null ? BigDecimal.ZERO : productOrder.getCompleteQuantity();
        //  ç´¯åŠ (只算合格)
        BigDecimal newCompleteQty = completeQty.add(qualifiedQty);
        productOrder.setCompleteQuantity(newCompleteQty);
        //  éœ€æ±‚数量
        BigDecimal totalQty = productOrder.getQuantity() == null ? BigDecimal.ZERO : productOrder.getQuantity();
        // å‰©ä½™å¯æŠ¥å·¥æ•°é‡
        //  å‰©ä½™å¯æŠ¥å·¥æ•°é‡
        BigDecimal remainQty = totalQty.subtract(completeQty);
        //  æœ¬æ¬¡æŠ¥å·¥ä¸èƒ½è¶…过剩余数量
        //  æœ¬æ¬¡æŠ¥å·¥åˆæ ¼æ•°é‡ä¸èƒ½è¶…过剩余数量(校验必须在累加前执行)
        if (qualifiedQty.compareTo(remainQty) > 0) {
            throw new ServiceException("报工失败,本次报工数量不能大于剩余可报工数量");
            throw new ServiceException("报工失败,本次报工数量不能大于剩余可报工数量,剩余报工数量:[" + remainQty + "]");
        }
        //  åŽ†å²å·²å®Œæˆ + æœ¬æ¬¡åˆæ ¼æ•°é‡
        BigDecimal newCompleteQty = completeQty.add(qualifiedQty);
        productOrder.setCompleteQuantity(newCompleteQty);
        //  è®¾ç½®å¼€å§‹æ—¶é—´ï¼ˆç¬¬ä¸€æ¬¡æŠ¥å·¥ï¼‰
        if (productOrder.getStartTime() == null) {
            productOrder.setStartTime(LocalDateTime.now());
@@ -230,9 +228,11 @@
        //  å®ŒæˆæŠ¥å·¥ä¸»è¡¨-投入表-产出表数据
        ProductionProductMain productionProductMain = new ProductionProductMain();
        productionProductMain.setProductNo("BG" + UUID.randomUUID());
        productionProductMain.setProductNo(productionProductMainService.generateProductNo());
        productionProductMain.setProductOrderId(dto.getProductOrderId());
        productionProductMain.setSchedule(dto.getSchedule());
        productionProductMain.setPostName(dto.getPostName());
        productionProductMain.setReportingTime(LocalDateTime.now());
        boolean result = productionProductMainService.save(productionProductMain);
        if (!result) {
            throw new ServiceException("报工失败,数据存储失败");
@@ -276,6 +276,7 @@
                    ProductionProductRouteItemParam paramEntity = new ProductionProductRouteItemParam();
                    BeanUtils.copyProperties(productRouteItemParamDto, paramEntity, "id");
                    paramEntity.setProductionProductRouteItemId(productRouteItemEntity.getId());
                    paramEntity.setOrderItemParamId(productRouteItemParamDto.getId());
                    if (paramEntity.getProductId() == null) {
                        ProductionOrderRouteItemParam productionOrderRouteItemParam = productionOrderRouteItemParamService.getById(productRouteItemParamDto.getId());
                        paramEntity.setParamName(productionOrderRouteItemParam.getParamName());
@@ -344,4 +345,381 @@
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteProductMain(Long productMainId) {
        if (productMainId == null) {
            throw new ServiceException("删除失败,报工ID不能为空");
        }
        //  æ ¡éªŒæŠ¥å·¥ä¸»è¡¨æ˜¯å¦å­˜åœ¨
        ProductionProductMain productMain = productionProductMainService.getById(productMainId);
        if (productMain == null) {
            throw new ServiceException("删除失败,报工记录不存在");
        }
        //  æŸ¥è¯¢è¯¥æŠ¥å·¥ä¸‹æ‰€æœ‰å·¥åºè®°å½•
        List<ProductionProductRouteItem> routeItemList = productionProductRouteItemService.list(new LambdaQueryWrapper<ProductionProductRouteItem>()
                .eq(ProductionProductRouteItem::getProductMainId, productMainId));
        if (routeItemList != null && !routeItemList.isEmpty()) {
            List<Long> routeItemIds = routeItemList.stream()
                    .map(ProductionProductRouteItem::getId)
                    .collect(Collectors.toList());
            //  æŸ¥è¯¢å¹¶åˆ é™¤æ¯ä¸ªå·¥åºä¸‹çš„附件
            List<ProductionProductRouteItemFile> fileList = productionProductRouteItemFileService.list(new LambdaQueryWrapper<ProductionProductRouteItemFile>()
                    .in(ProductionProductRouteItemFile::getProductionProductRouteItemId, routeItemIds));
            if (fileList != null && !fileList.isEmpty()) {
                for (ProductionProductRouteItemFile file : fileList) {
                    if (StringUtils.hasText(file.getFileUrl())) {
                        try {
                            Files.deleteIfExists(Paths.get(file.getFileUrl()));
                        } catch (IOException e) {
                            log.warn("删除附件文件失败: {}", file.getFileUrl(), e);
                        }
                    }
                }
                productionProductRouteItemFileService.remove(new LambdaQueryWrapper<ProductionProductRouteItemFile>()
                        .in(ProductionProductRouteItemFile::getProductionProductRouteItemId, routeItemIds));
            }
            //  åˆ é™¤å·¥åºå‚æ•°
            productionProductRouteItemParamService.remove(new LambdaQueryWrapper<ProductionProductRouteItemParam>()
                    .in(ProductionProductRouteItemParam::getProductionProductRouteItemId, routeItemIds));
            //  åˆ é™¤å·¥åºè®°å½•
            productionProductRouteItemService.remove(new LambdaQueryWrapper<ProductionProductRouteItem>()
                    .eq(ProductionProductRouteItem::getProductMainId, productMainId));
        }
        //  åˆ é™¤æŠ•入表
        productionProductInputService.remove(new LambdaQueryWrapper<ProductionProductInput>()
                .eq(ProductionProductInput::getProductMainId, productMainId));
        //  å›žæ»šç”Ÿäº§è®¢å•完成数量
        ProductionProductOutput output = productionProductOutputService.getOne(new LambdaQueryWrapper<ProductionProductOutput>()
                .eq(ProductionProductOutput::getProductMainId, productMainId)
                .last("LIMIT 1"));
        ProductOrder productOrder = productOrderService.getById(productMain.getProductOrderId());
        if (productOrder == null) {
            throw new ServiceException("删除失败,关联的生产订单不存在");
        }
        BigDecimal qualifiedQty = (output != null && output.getQuantity() != null)
                ? output.getQuantity() : BigDecimal.ZERO;
        BigDecimal currentCompleteQty = productOrder.getCompleteQuantity() == null
                ? BigDecimal.ZERO : productOrder.getCompleteQuantity();
        BigDecimal totalQty = productOrder.getQuantity() == null ? BigDecimal.ZERO : productOrder.getQuantity();
        //  æ‰£å‡æœ¬æ¡æŠ¥å·¥è´¡çŒ®çš„合格数量,不允许出现负数
        BigDecimal updatedCompleteQty = currentCompleteQty.subtract(qualifiedQty).max(BigDecimal.ZERO);
        productOrder.setCompleteQuantity(updatedCompleteQty);
        //  é‡æ–°åˆ¤æ–­è®¢å•状态
        if (updatedCompleteQty.compareTo(totalQty) >= 0 && totalQty.compareTo(BigDecimal.ZERO) > 0) {
            //  å®Œæˆæ•° >= éœ€æ±‚æ•° â†’ å·²å®Œæˆ
            productOrder.setStatus(ProductOrderStatusEnum.FINISHED.getCode());
        } else if (updatedCompleteQty.compareTo(BigDecimal.ZERO) == 0) {
            //  å®Œæˆæ•°å½’é›¶ â†’ å›žåˆ°å¾…开始,清空开始/结束时间
            productOrder.setStatus(ProductOrderStatusEnum.WAIT.getCode());
            productOrder.setStartTime(null);
            productOrder.setEndTime(null);
        } else {
            //  å®Œæˆæ•°ä»‹äºŽ 0 ~ éœ€æ±‚数之间 â†’ è¿›è¡Œä¸­
            productOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode());
            productOrder.setEndTime(null);
        }
        productOrderService.updateById(productOrder);
        //  åˆ é™¤äº§å‡ºè¡¨
        productionProductOutputService.remove(new LambdaQueryWrapper<ProductionProductOutput>()
                .eq(ProductionProductOutput::getProductMainId, productMainId));
        //  åˆ é™¤æŠ¥å·¥ä¸»è¡¨
        productionProductMainService.removeById(productMainId);
        log.info("报工记录删除成功,productMainId={}", productMainId);
    }
    @Override
    public ProductionRecordDto detailProductMain(Long productMainId) {
        if (productMainId == null) {
            throw new ServiceException("查询失败,报工ID不能为空");
        }
        //  æŸ¥è¯¢æŠ¥å·¥ä¸»è¡¨
        ProductionProductMain productMain = productionProductMainService.getById(productMainId);
        if (productMain == null) {
            throw new ServiceException("查询失败,报工记录不存在");
        }
        ProductionRecordDto dto = new ProductionRecordDto();
        dto.setProductMainId(productMainId);
        dto.setProductOrderId(productMain.getProductOrderId());
        dto.setPostName(productMain.getPostName());
        dto.setSchedule(productMain.getSchedule());
        dto.setReportingTime(productMain.getReportingTime());
        dto.setCreateTime(productMain.getCreateTime());
        dto.setUpdateTime(productMain.getUpdateTime());
        //  ç”Ÿäº§è®¢å•信息
        ProductOrder productOrder = productOrderService.getById(productMain.getProductOrderId());
        if (productOrder == null) {
            throw new ServiceException("查询失败,未查询到生产订单信息");
        }
        dto.setNpsNo(productOrder.getNpsNo());
        /// äº§å“ä¿¡æ¯
        ProductMaterialSkuDto productMaterialSkuDto = productMaterialService.selectProductByProductMainId(productOrder.getId());
        dto.setProductName(productMaterialSkuDto.getProductName());
        dto.setMaterialCode(productMaterialSkuDto.getMaterialCode());
        dto.setModel(productMaterialSkuDto.getModel());
        //  æŸ¥è¯¢æŠ•入表(获取产品ID和投入数量)
        ProductionProductInput input = productionProductInputService.getOne(
                new LambdaQueryWrapper<ProductionProductInput>()
                        .eq(ProductionProductInput::getProductMainId, productMainId)
                        .last("LIMIT 1"));
        if (input != null) {
            dto.setProductId(input.getProductModelId());
            dto.setQuantity(input.getQuantity());
        }
        //  æŸ¥è¯¢äº§å‡ºè¡¨ï¼ˆèŽ·å–åˆæ ¼/不合格数量)
        ProductionProductOutput output = productionProductOutputService.getOne(
                new LambdaQueryWrapper<ProductionProductOutput>()
                        .eq(ProductionProductOutput::getProductMainId, productMainId)
                        .last("LIMIT 1"));
        if (output != null) {
            dto.setQualifiedQuantity(output.getQuantity());
            dto.setUnqualifiedQuantity(output.getScrapQty());
        }
        //  æŸ¥è¯¢å·¥åºåˆ—表
        List<ProductionProductRouteItem> routeItemList = productionProductRouteItemService.list(
                new LambdaQueryWrapper<ProductionProductRouteItem>()
                        .eq(ProductionProductRouteItem::getProductMainId, productMainId));
        if (routeItemList != null && !routeItemList.isEmpty()) {
            List<ProductionProductRouteItemDto> routeItemDtoList = routeItemList.stream().map(routeItem -> {
                ProductionProductRouteItemDto routeItemDto = new ProductionProductRouteItemDto();
                BeanUtils.copyProperties(routeItem, routeItemDto);
                //  æŸ¥è¯¢å·¥åºå‚æ•°
                List<ProductionProductRouteItemParam> paramList = productionProductRouteItemParamService.list(
                        new LambdaQueryWrapper<ProductionProductRouteItemParam>()
                                .eq(ProductionProductRouteItemParam::getProductionProductRouteItemId, routeItem.getId()));
                if (paramList != null && !paramList.isEmpty()) {
                    List<ProductionProductRouteItemParamDto> paramDtoList = paramList.stream().map(param -> {
                        ProductionProductRouteItemParamDto paramDto = new ProductionProductRouteItemParamDto();
                        BeanUtils.copyProperties(param, paramDto);
                        if (paramDto.getProductId() != null) {
                            ProductMaterialSkuDto materialSkuDto = productMaterialService.selectProductByModelId(paramDto.getProductId());
                            productMaterialService.selectProductByModelId(paramDto.getProductId());
                            paramDto.setParamName(materialSkuDto.getProductName());
                        }
                        return paramDto;
                    }).collect(Collectors.toList());
                    routeItemDto.setProductionProductRouteItemParamDtoList(paramDtoList);
                }
                //  æŸ¥è¯¢å·¥åºé™„ä»¶
                List<ProductionProductRouteItemFile> fileRecordList = productionProductRouteItemFileService.list(
                        new LambdaQueryWrapper<ProductionProductRouteItemFile>()
                                .eq(ProductionProductRouteItemFile::getProductionProductRouteItemId, routeItem.getId()));
                if (fileRecordList != null && !fileRecordList.isEmpty()) {
                    List<ProductionProductRouteItemFileDto> fileDtoList = fileRecordList.stream().map(file -> {
                        ProductionProductRouteItemFileDto fileDto = new ProductionProductRouteItemFileDto();
                        BeanUtils.copyProperties(file, fileDto);
                        return fileDto;
                    }).collect(Collectors.toList());
                    routeItemDto.setFileList(fileDtoList);
                }
                return routeItemDto;
            }).collect(Collectors.toList());
            dto.setProductionProductRouteItemDtoList(routeItemDtoList);
        }
        return dto;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editProductMain(ProductionRecordDto dto) {
        if (dto == null || dto.getProductMainId() == null) {
            throw new ServiceException("编辑失败,报工ID不能为空");
        }
        if (dto.getProductId() == null) {
            throw new ServiceException("编辑失败,产品信息不能为空");
        }
        if (StringUtils.isEmpty(dto.getPostName()) || StringUtils.isEmpty(dto.getSchedule())) {
            throw new ServiceException("编辑失败,岗位人员/班次信息不能为空");
        }
        Long productMainId = dto.getProductMainId();
        //  æŸ¥è¯¢æŠ¥å·¥ä¸»è¡¨æ˜¯å¦å­˜åœ¨
        ProductionProductMain productMain = productionProductMainService.getById(productMainId);
        if (productMain == null) {
            throw new ServiceException("编辑失败,报工记录不存在");
        }
        //  å›žæ»šç”Ÿäº§è®¢å•的合格数量
        ProductionProductOutput oldOutput = productionProductOutputService.getOne(new LambdaQueryWrapper<ProductionProductOutput>()
                .eq(ProductionProductOutput::getProductMainId, productMainId)
                .last("LIMIT 1"));
        ProductOrder productOrder = productOrderService.getById(productMain.getProductOrderId());
        if (productOrder == null) {
            throw new ServiceException("编辑失败,关联的生产订单不存在");
        }
        BigDecimal oldQualifiedQty = (oldOutput != null && oldOutput.getQuantity() != null)
                ? oldOutput.getQuantity() : BigDecimal.ZERO;
        BigDecimal newQualifiedQty = dto.getQualifiedQuantity() == null ? BigDecimal.ZERO : dto.getQualifiedQuantity();
        BigDecimal currentCompleteQty = productOrder.getCompleteQuantity() == null
                ? BigDecimal.ZERO : productOrder.getCompleteQuantity();
        BigDecimal totalQty = productOrder.getQuantity() == null ? BigDecimal.ZERO : productOrder.getQuantity();
        //  å›žæ»šæ—§å€¼ï¼Œè®¡ç®—本次编辑后的新完成数量
        BigDecimal baseQty = currentCompleteQty.subtract(oldQualifiedQty);
        BigDecimal updatedCompleteQty = baseQty.add(newQualifiedQty);
        //  æ–°çš„合格数量不能超过(需求数量 - æŠ¥å·¥æ€»å®Œæˆæ•°é‡ï¼‰
        if (newQualifiedQty.compareTo(totalQty.subtract(baseQty)) > 0) {
            throw new ServiceException("编辑失败,本次报工合格数量不能大于剩余可报工数量");
        }
        //  é‡æ–°åˆ¤æ–­ç”Ÿäº§è®¢å•状态
        productOrder.setCompleteQuantity(updatedCompleteQty);
        if (updatedCompleteQty.compareTo(totalQty) >= 0 && totalQty.compareTo(BigDecimal.ZERO) > 0) {
            productOrder.setStatus(ProductOrderStatusEnum.FINISHED.getCode());
            productOrder.setEndTime(LocalDateTime.now());
        } else {
            productOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode());
            productOrder.setEndTime(null);
        }
        productOrderService.updateById(productOrder);
        //  æ›´æ–°æŠ¥å·¥ä¸»è¡¨
        productMain.setPostName(dto.getPostName());
        productMain.setSchedule(dto.getSchedule());
        productMain.setReportingTime(dto.getReportingTime());
        productionProductMainService.updateById(productMain);
        //  æ›´æ–°æŠ•入表
        ProductionProductInput input = productionProductInputService.getOne(new LambdaQueryWrapper<ProductionProductInput>()
                .eq(ProductionProductInput::getProductMainId, productMainId)
                .last("LIMIT 1"));
        if (input != null) {
            input.setProductModelId(dto.getProductId());
            input.setQuantity(dto.getQuantity());
            productionProductInputService.updateById(input);
        }
        //  æ›´æ–°äº§å‡ºè¡¨
        if (oldOutput != null) {
            oldOutput.setProductModelId(dto.getProductId());
            oldOutput.setQuantity(dto.getQualifiedQuantity());
            oldOutput.setScrapQty(dto.getUnqualifiedQuantity());
            productionProductOutputService.updateById(oldOutput);
        }
        //  å¤„理工序列表
        List<ProductionProductRouteItemDto> routeItemDtoList = dto.getProductionProductRouteItemDtoList();
        if (routeItemDtoList == null || routeItemDtoList.isEmpty()) {
            throw new ServiceException("编辑失败,工序参数不能为空");
        }
        for (ProductionProductRouteItemDto routeItemDto : routeItemDtoList) {
            //  æ›´æ–°å·²æœ‰å·¥åº
            ProductionProductRouteItem routeItemEntity = productionProductRouteItemService.getById(routeItemDto.getId());
            if (routeItemEntity == null) {
                log.error("编辑失败,工序记录不存在,ID={}", routeItemDto.getId());
                throw new ServiceException("编辑失败,工序记录不存在");
            }
            BeanUtils.copyProperties(routeItemDto, routeItemEntity, "id", "productMainId", "createTime", "tenantId");
            productionProductRouteItemService.updateById(routeItemEntity);
            final Long routeItemId = routeItemEntity.getId();
            //  å¤„理工序参数: å…ˆåˆ é™¤è¯¥å·¥åºä¸‹æ‰€æœ‰æ—§å‚数,再重新插入传入的参数
            productionProductRouteItemParamService.remove(new LambdaQueryWrapper<ProductionProductRouteItemParam>()
                    .eq(ProductionProductRouteItemParam::getProductionProductRouteItemId, routeItemId));
            List<ProductionProductRouteItemParamDto> paramDtoList = routeItemDto.getProductionProductRouteItemParamDtoList();
                if (paramDtoList != null && !paramDtoList.isEmpty()) {
                for (ProductionProductRouteItemParamDto paramDto : paramDtoList) {
                    ProductionProductRouteItemParam paramEntity = new ProductionProductRouteItemParam();
                    BeanUtils.copyProperties(paramDto, paramEntity, "id");
                    paramEntity.setProductionProductRouteItemId(routeItemId);
                    if (paramEntity.getProductId() == null && paramDto.getId() != null) {
                        ProductionOrderRouteItemParam orderParam = productionOrderRouteItemParamService.getById(paramDto.getId());
                        if (orderParam != null) {
                            paramEntity.setOrderItemParamId(orderParam.getId());
                            paramEntity.setParamName(orderParam.getParamName());
                            paramEntity.setParamType(orderParam.getParamType());
                            paramEntity.setParamFormat(orderParam.getParamFormat());
                            paramEntity.setValueMode(orderParam.getValueMode());
                        }
                    }
                    productionProductRouteItemParamService.save(paramEntity);
                }
            }
            //  å¤„理新上传的临时附件
            List<String> newFiles = routeItemDto.getFiles();
            if (newFiles != null && !newFiles.isEmpty()) {
                for (String tempId : newFiles) {
                    TempFile tempFile = tempFileMapper.selectById(tempId);
                    if (tempFile == null) {
                        log.warn("未找到临时文件记录: {}", tempId);
                        continue;
                    }
                    try {
                        String formalDir = uploadDir + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
                        Path formalDirPath = Paths.get(formalDir);
                        if (!Files.exists(formalDirPath)) {
                            Files.createDirectories(formalDirPath);
                        }
                        String originalFilename = tempFile.getOriginalName();
                        String fileExtension = FilenameUtils.getExtension(originalFilename);
                        String formalFilename = routeItemId + "_"
                                + System.currentTimeMillis() + "_"
                                + UUID.randomUUID().toString().substring(0, 8)
                                + (StringUtils.hasText(fileExtension) ? "." + fileExtension : "");
                        Path formalFilePath = formalDirPath.resolve(formalFilename);
                        Files.copy(Paths.get(tempFile.getTempPath()), formalFilePath, StandardCopyOption.REPLACE_EXISTING);
                        Files.deleteIfExists(Paths.get(tempFile.getTempPath()));
                        ProductionProductRouteItemFile fileEntity = new ProductionProductRouteItemFile();
                        fileEntity.setProductionProductRouteItemId(routeItemId);
                        fileEntity.setFileName(originalFilename);
                        fileEntity.setFileUrl(formalFilePath.toString());
                        fileEntity.setFileSuffix(fileExtension);
                        fileEntity.setFileSize(Files.size(formalFilePath));
                        fileEntity.setCreateTime(LocalDateTime.now());
                        fileEntity.setTenantId(SecurityUtils.getLoginUser().getTenantId());
                        productionProductRouteItemFileService.save(fileEntity);
                        tempFileMapper.deleteById(tempId);
                        log.info("编辑-工序附件迁移成功: {} -> {}", tempFile.getTempPath(), formalFilePath);
                    } catch (IOException e) {
                        log.error("编辑-工序附件迁移失败: {}", tempFile.getTempPath(), e);
                        throw new ServiceException("工序附件处理异常: " + e.getMessage());
                    }
                }
            }
            List<Long> delFileIds = routeItemDto.getDelFileIds();
            if (delFileIds != null && !delFileIds.isEmpty()) {
                delFileIds.forEach(productionProductRouteItemFileService::deleteFile);
            }
        }
        log.info("报工记录编辑成功,productMainId={}", productMainId);
    }
}
src/main/resources/mapper/production/ProductMaterialMapper.xml
@@ -16,8 +16,12 @@
        <result property="updateTime" column="update_time"/>
    </resultMap>
    <select id="selectProductModelIdByName" resultType="java.lang.String" parameterType="java.lang.Long">
        select pm.product_name
    <select id="selectProductByModelId" resultType="com.ruoyi.production.dto.ProductMaterialSkuDto"
            parameterType="java.lang.Long">
        select pm.product_name as productName,
        pm.unit,
        pms.material_code as materialCode,
        pms.model
        from product_material pm
        left join product_material_sku pms on pms.product_id = pm.id
        <where>
@@ -32,4 +36,29 @@
        </where>
    </select>
    <select id="selectProductByProductMainId" resultType="com.ruoyi.production.dto.ProductMaterialSkuDto"
            parameterType="java.lang.Long">
        select
        pm.product_name as productName,
        pm.unit,
        pms.material_code as materialCode,
        pms.model
        from product_order po
        left join product_order_plan pop on po.id = pop.product_order_id
        left join production_plan pp on pp.id = pop.production_plan_id
        left join product_material_sku pms on pms.id = pop.production_plan_id
        left join product_material pm on pm.id = pms.product_id
        <where>
            <choose>
                <when test="productOrderId != null">
                    po.id = #{productOrderId}
                </when>
                <otherwise>
                    1 = 0
                </otherwise>
            </choose>
        </where>
    </select>
</mapper>
src/main/resources/mapper/production/ProductionProductMainMapper.xml
@@ -17,11 +17,12 @@
    <select id="listPageProductionProductMainDto" resultType="com.ruoyi.production.dto.ProductionProductMainDto">
        select ppm.*,
        ppm.post_name as postName,
        po.nps_no as npsNo,
        pms.material_code as materialCode,
        pm.product_name as productName,
        pms.model as productModelName,
        IFNULL(ppo.quantity, 0) as totalQuantity,
        IFNULL(ppi.quantity, 0) as totalQuantity,
        IFNULL(ppo.scrap_qty, 0) as scrapQty,
        IFNULL(ppo.quantity, 0) as quantity
        from
src/main/resources/mapper/production/ProductionProductRouteItemParamMapper.xml
@@ -5,6 +5,7 @@
    <resultMap id="BaseResultMap" type="com.ruoyi.production.pojo.ProductionProductRouteItemParam">
        <id column="id" property="id"/>
        <result column="production_product_route_item_id" property="productionProductRouteItemId"/>
        <result column="order_item_param_id" property="orderItemParamId"/>
        <result column="param_name" property="paramName"/>
        <result column="param_type" property="paramType"/>
        <result column="param_format" property="paramFormat"/>
@@ -12,6 +13,7 @@
        <result column="standard_value" property="standardValue"/>
        <result column="min_value" property="minValue"/>
        <result column="max_value" property="maxValue"/>
        <result column="param_value" property="paramValue"/>
        <result column="product_id" property="productId"/>
        <result column="bom_id" property="bomId"/>
        <result column="product_value" property="productValue"/>