gongchunyi
12 小时以前 5d314130e6f21bd265388b5210808baeba5f2d0c
feat: 生产报工的工序参数/BOM消耗产品查询返回、新增报工、分页查询
已添加29个文件
已修改6个文件
1367 ■■■■■ 文件已修改
src/main/java/com/ruoyi/other/controller/TempFileController.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/other/service/impl/TempFileServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionProductRouteItemController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionProductRouteItemFileController.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionProductRouteItemParamController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionRecordController.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/ProductionProductMainDto.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/ProductionProductRouteItemDto.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/ProductionProductRouteItemFileDto.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/ProductionProductRouteItemParamDto.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/dto/ProductionRecordDto.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/mapper/ProductionProductRouteItemFileMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/mapper/ProductionProductRouteItemMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/mapper/ProductionProductRouteItemParamMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductionProductMain.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductionProductOutput.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductionProductRouteItem.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductionProductRouteItemFile.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductionProductRouteItemParam.java 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/IProductionProductRouteItemFileService.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/IProductionProductRouteItemParamService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/IProductionProductRouteItemService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductionRecordService.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionProductRouteItemFileServiceImpl.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionProductRouteItemParamServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionProductRouteItemServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionRecordServiceImpl.java 348 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/vo/ProductionOrderRouteItemParamVo.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/vo/ProductionOrderRouteItemVo.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/vo/ProductionOrderStructureVo.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/vo/ProductionRecordVo.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionProductMainMapper.xml 65 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionProductRouteItemFileMapper.xml 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionProductRouteItemMapper.xml 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionProductRouteItemParamMapper.xml 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/other/controller/TempFileController.java
@@ -8,10 +8,7 @@
import com.ruoyi.purchase.service.ITicketRegistrationService;
import com.ruoyi.purchase.service.impl.TicketRegistrationServiceImpl;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
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;
@@ -25,7 +22,7 @@
    private TicketRegistrationServiceImpl ticketRegistrationServiceImpl;
    @PostMapping("/upload")
    public AjaxResult uploadFile(MultipartFile file, Integer type) {
    public AjaxResult uploadFile(MultipartFile file, @RequestParam(required = false) Integer type) {
        try {
            return AjaxResult.success(tempFileService.uploadFile(file, type));
        } catch (Exception e) {
src/main/java/com/ruoyi/other/service/impl/TempFileServiceImpl.java
@@ -165,7 +165,7 @@
        }
    }
//    @Scheduled(cron = "0 0 3 * * ?") // æ¯å¤©å‡Œæ™¨3点执行
    @Scheduled(cron = "0 0 3 * * ?") // æ¯å¤©å‡Œæ™¨3点执行
    public void cleanupExpiredTempFiles() {
        LambdaQueryWrapper<TempFile> wrapper = new LambdaQueryWrapper<>();
        wrapper.lt(TempFile::getExpireTime, LocalDateTime.now()); // expireTime < å½“前时间
src/main/java/com/ruoyi/production/controller/ProductionProductRouteItemController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package com.ruoyi.production.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•的工序表(包含岗位人员+设备异常处置+工艺人员交待) å‰ç«¯æŽ§åˆ¶å™¨
 * </p>
 *
 * @author deslrey
 * @since 2026-03-23
 */
@RestController
@RequestMapping("/production-product-route-item")
public class ProductionProductRouteItemController {
}
src/main/java/com/ruoyi/production/controller/ProductionProductRouteItemFileController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package com.ruoyi.production.controller;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.production.service.IProductionProductRouteItemFileService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•的工序附件表 å‰ç«¯æŽ§åˆ¶å™¨
 * </p>
 *
 * @author deslrey
 * @since 2026-03-23
 */
@RestController
@RequestMapping("/productionProductRouteItemFile")
public class ProductionProductRouteItemFileController {
    @Autowired
    private IProductionProductRouteItemFileService productionProductRouteItemFileService;
    @DeleteMapping("/{fileId}")
    @ApiOperation("生产报工-删除附件")
    public AjaxResult deleteFile(@PathVariable Long fileId) {
        productionProductRouteItemFileService.deleteFile(fileId);
        return AjaxResult.success();
    }
}
src/main/java/com/ruoyi/production/controller/ProductionProductRouteItemParamController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package com.ruoyi.production.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•工序的参数明细表 å‰ç«¯æŽ§åˆ¶å™¨
 * </p>
 *
 * @author deslrey
 * @since 2026-03-23
 */
@RestController
@RequestMapping("/production-product-route-item-param")
public class ProductionProductRouteItemParamController {
}
src/main/java/com/ruoyi/production/controller/ProductionRecordController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package com.ruoyi.production.controller;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.production.dto.ProductionRecordDto;
import com.ruoyi.production.service.ProductionRecordService;
import com.ruoyi.production.vo.ProductionRecordVo;
import io.swagger.annotations.ApiModel;
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>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•控制层
 * </br>
 *
 * @author deslrey
 * @since 2026/03/23
 */
@RestController
@RequestMapping("/productionRecord")
@ApiModel(value = "ProductionRecordController", description = "生产报工记录控制层")
public class ProductionRecordController {
    @Autowired
    private ProductionRecordService productionRecordService;
    @GetMapping("/add/{productOrderId}")
    @ApiOperation("生产报工-根据生产订单ID获取工艺路线的工序、BOM")
    public AjaxResult productRouteItem(@PathVariable Long productOrderId) {
        ProductionRecordVo vo = productionRecordService.productRouteItem(productOrderId);
        return AjaxResult.success(vo);
    }
    @PostMapping("/add")
    @ApiOperation("生产报工-新增")
    public AjaxResult addProductionRecordService(@RequestBody ProductionRecordDto dto) {
        productionRecordService.addProductionRecordService(dto);
        return AjaxResult.success();
    }
}
src/main/java/com/ruoyi/production/dto/ProductionProductMainDto.java
@@ -6,13 +6,14 @@
import com.ruoyi.production.pojo.ProductionProductMain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@EqualsAndHashCode(callSuper = true)
@ExcelIgnoreUnannotated
public class ProductionProductMainDto extends ProductionProductMain {
    @ApiModelProperty(value = "工单编号")
@@ -61,5 +62,15 @@
    private BigDecimal workHours;
    private BigDecimal wages;
    @ApiModelProperty(value = "生产订单号")
    @Excel(name = "生产订单号")
    private String npsNo;
    @ApiModelProperty(value = "产品编码")
    @Excel(name = "产品编码")
    private String materialCode;
    @ApiModelProperty(value = "产出方量")
    @Excel(name = "产出方量")
    private BigDecimal totalQuantity;
}
src/main/java/com/ruoyi/production/dto/ProductionProductRouteItemDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package com.ruoyi.production.dto;
import com.ruoyi.production.pojo.ProductionProductRouteItem;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
 * <br>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•的工序表(包含岗位人员+设备异常处置+工艺人员交待)Dto
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/23 11:01
 */
@Data
@EqualsAndHashCode(callSuper = false)
public class ProductionProductRouteItemDto extends ProductionProductRouteItem {
    @ApiModelProperty("工序绑定的参数信息")
    private List<ProductionProductRouteItemParamDto> productionProductRouteItemParamDtoList;
    @ApiModelProperty("工序临时附件ID")
    private List<String> files;
}
src/main/java/com/ruoyi/production/dto/ProductionProductRouteItemFileDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package com.ruoyi.production.dto;
import com.ruoyi.production.pojo.ProductionProductRouteItemFile;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * <br>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•的工序附件表Dto
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/23 11:01
 */
@Data
@EqualsAndHashCode(callSuper = false)
public class ProductionProductRouteItemFileDto extends ProductionProductRouteItemFile {
}
src/main/java/com/ruoyi/production/dto/ProductionProductRouteItemParamDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package com.ruoyi.production.dto;
import com.ruoyi.production.pojo.ProductionProductRouteItemParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * <br>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•工序的参数明细表Dto
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/23 11:01
 */
@Data
@EqualsAndHashCode(callSuper = false)
public class ProductionProductRouteItemParamDto extends ProductionProductRouteItemParam {
}
src/main/java/com/ruoyi/production/dto/ProductionRecordDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
package com.ruoyi.production.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/**
 * <br>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•Dto
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/23 10:30
 */
@Data
@ApiModel(value = "ProductionRecordDto", description = "生产报工记录Dto")
public class ProductionRecordDto {
    @ApiModelProperty("生产订单ID")
    private Long productOrderId;
    @ApiModelProperty("产品ID")
    private Long productId;
    @ApiModelProperty(value = "岗位人员")
    private String postName;
    @ApiModelProperty(value = "班次")
    private String schedule;
    @ApiModelProperty(value = "班次日期")
    private LocalDateTime reportingTime;
    @ApiModelProperty("合格数量")
    private BigDecimal qualifiedQuantity;
    @ApiModelProperty("不合格数量")
    private BigDecimal unqualifiedQuantity;
    @ApiModelProperty("产出数量")
    private BigDecimal quantity;
    @ApiModelProperty("工艺路线绑定的工序")
    private List<ProductionProductRouteItemDto> productionProductRouteItemDtoList;
}
src/main/java/com/ruoyi/production/mapper/ProductionProductRouteItemFileMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.ruoyi.production.mapper;
import com.ruoyi.production.pojo.ProductionProductRouteItemFile;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
 * <p>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•的工序附件表 Mapper æŽ¥å£
 * </p>
 *
 * @author deslrey
 * @since 2026-03-23
 */
public interface ProductionProductRouteItemFileMapper extends BaseMapper<ProductionProductRouteItemFile> {
}
src/main/java/com/ruoyi/production/mapper/ProductionProductRouteItemMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.ruoyi.production.mapper;
import com.ruoyi.production.pojo.ProductionProductRouteItem;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
 * <p>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•的工序表(包含岗位人员+设备异常处置+工艺人员交待) Mapper æŽ¥å£
 * </p>
 *
 * @author deslrey
 * @since 2026-03-23
 */
public interface ProductionProductRouteItemMapper extends BaseMapper<ProductionProductRouteItem> {
}
src/main/java/com/ruoyi/production/mapper/ProductionProductRouteItemParamMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.ruoyi.production.mapper;
import com.ruoyi.production.pojo.ProductionProductRouteItemParam;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
 * <p>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•工序的参数明细表 Mapper æŽ¥å£
 * </p>
 *
 * @author deslrey
 * @since 2026-03-23
 */
public interface ProductionProductRouteItemParamMapper extends BaseMapper<ProductionProductRouteItemParam> {
}
src/main/java/com/ruoyi/production/pojo/ProductionProductMain.java
@@ -33,7 +33,6 @@
    private Long workOrderId;
    @ApiModelProperty(value = "生产订单id")
    @TableField(exist = false)
    private Long productOrderId;
    @ApiModelProperty(value = "报工状态")
@@ -55,4 +54,16 @@
    @ApiModelProperty(value = "租户ID")
    @TableField(fill = FieldFill.INSERT)
    private Long tenantId;
    @ApiModelProperty(value = "岗位人员")
    private String postName;
    @ApiModelProperty(value = "班次")
    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/ProductionProductOutput.java
@@ -20,7 +20,7 @@
    @ApiModelProperty(value = "产品id")
    private Long productModelId;
    @ApiModelProperty(value = "报工数量(总数量)")
    @ApiModelProperty(value = "合格数量")
    private BigDecimal quantity;
    @ApiModelProperty(value = "创建时间")
@@ -31,6 +31,6 @@
    @TableField(fill = FieldFill.INSERT)
    private Long tenantId;
    @ApiModelProperty(value = "报废数量")
    @ApiModelProperty(value = "不合格数量")
    private BigDecimal scrapQty;
}
src/main/java/com/ruoyi/production/pojo/ProductionProductRouteItem.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package com.ruoyi.production.pojo;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
 * <p>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•的工序表(包含岗位人员+设备异常处置+工艺人员交待)
 * </p>
 *
 * @author deslrey
 * @since 2026-03-23
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("production_product_route_item")
@ApiModel(value = "ProductionProductRouteItem对象", description = "生产报工记录的工序表(包含岗位人员+设备异常处置+工艺人员交待)")
public class ProductionProductRouteItem implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "主键ID")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @ApiModelProperty(value = "报工主表ID")
    private Long productMainId;
    @ApiModelProperty(value = "岗位人员")
    private String postName;
    @ApiModelProperty(value = "设备异常")
    private String equipmentMalfunction;
    @ApiModelProperty(value = "设备处置")
    private String equipmentDisposal;
    @ApiModelProperty(value = "工艺人员交代")
    private String processExplained;
    @ApiModelProperty(value = "工序ID")
    private Long processId;
    @ApiModelProperty(value = "创建日期")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @ApiModelProperty(value = "更新日期")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @ApiModelProperty(value = "租户ID")
    @TableField(fill = FieldFill.INSERT)
    private Long tenantId;
}
src/main/java/com/ruoyi/production/pojo/ProductionProductRouteItemFile.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,68 @@
package com.ruoyi.production.pojo;
import com.baomidou.mybatisplus.annotation.*;
import java.time.LocalDateTime;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
 * <p>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•的工序附件表
 * </p>
 *
 * @author deslrey
 * @since 2026-03-23
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("production_product_route_item_file")
@ApiModel(value="ProductionProductRouteItemFile对象", description="生产报工记录的工序附件表")
public class ProductionProductRouteItemFile implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "主键ID")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @ApiModelProperty(value = "生产报工记录的工序表ID")
    private Long productionProductRouteItemId;
    @ApiModelProperty(value = "文件名称")
    private String fileName;
    @ApiModelProperty(value = "文件访问地址")
    private String fileUrl;
    @ApiModelProperty(value = "文件大小")
    private Long fileSize;
    @ApiModelProperty(value = "文件后缀")
    private String fileSuffix;
    @ApiModelProperty(value = "上传者")
    private String createUser;
    @ApiModelProperty(value = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @ApiModelProperty(value = "修改者")
    private LocalDateTime updateUser;
    @ApiModelProperty(value = "修改时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @ApiModelProperty(value = "租户ID")
    @TableField(fill = FieldFill.INSERT)
    private Long tenantId;
}
src/main/java/com/ruoyi/production/pojo/ProductionProductRouteItemParam.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,100 @@
package com.ruoyi.production.pojo;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.*;
import java.time.LocalDateTime;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
 * <p>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•工序的参数明细表
 * </p>
 *
 * @author deslrey
 * @since 2026-03-23
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("production_product_route_item_param")
@ApiModel(value = "ProductionProductRouteItemParam对象", description = "生产报工记录工序的参数明细表")
public class ProductionProductRouteItemParam implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "主键ID")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @ApiModelProperty(value = "生产报工记录的工序表ID")
    private Long productionProductRouteItemId;
    @ApiModelProperty(value = "参数名称")
    private String paramName;
    @ApiModelProperty(value = "参数类型(1数字 2文本 3下拉选择 4时间)")
    private Integer paramType;
    @ApiModelProperty(value = "参数格式")
    private String paramFormat;
    @ApiModelProperty(value = "值模式(1单值 2区间)")
    private Integer valueMode;
    @ApiModelProperty(value = "标准值")
    private String standardValue;
    @ApiModelProperty(value = "最小值")
    private BigDecimal minValue;
    @ApiModelProperty(value = "最大值")
    private BigDecimal maxValue;
    @ApiModelProperty(value = "产品ID")
    private Long productId;
    @ApiModelProperty(value = "产品投入值")
    private BigDecimal productValue;
    @ApiModelProperty(value = "BOM ID")
    private BigDecimal bomId;
    @ApiModelProperty(value = "单位")
    private String unit;
    @ApiModelProperty(value = "是否必填(0否 1是)")
    private Boolean isRequired;
    @ApiModelProperty(value = "参数排序")
    private Integer sourceSort;
    @ApiModelProperty(value = "产品类型")
    private Long dictCode;
    @ApiModelProperty(value = "上传者")
    private Long createUser;
    @ApiModelProperty(value = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @ApiModelProperty(value = "修改者")
    private Long updateUser;
    @ApiModelProperty(value = "修改时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @ApiModelProperty(value = "租户ID")
    @TableField(fill = FieldFill.INSERT)
    private Long tenantId;
}
src/main/java/com/ruoyi/production/service/IProductionProductRouteItemFileService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
package com.ruoyi.production.service;
import com.ruoyi.production.pojo.ProductionProductRouteItemFile;
import com.baomidou.mybatisplus.extension.service.IService;
/**
 * <p>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•的工序附件表 æœåŠ¡ç±»
 * </p>
 *
 * @author deslrey
 * @since 2026-03-23
 */
public interface IProductionProductRouteItemFileService extends IService<ProductionProductRouteItemFile> {
    void deleteFile(Long fileId);
}
src/main/java/com/ruoyi/production/service/IProductionProductRouteItemParamService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.ruoyi.production.service;
import com.ruoyi.production.pojo.ProductionProductRouteItemParam;
import com.baomidou.mybatisplus.extension.service.IService;
/**
 * <p>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•工序的参数明细表 æœåŠ¡ç±»
 * </p>
 *
 * @author deslrey
 * @since 2026-03-23
 */
public interface IProductionProductRouteItemParamService extends IService<ProductionProductRouteItemParam> {
}
src/main/java/com/ruoyi/production/service/IProductionProductRouteItemService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.ruoyi.production.service;
import com.ruoyi.production.pojo.ProductionProductRouteItem;
import com.baomidou.mybatisplus.extension.service.IService;
/**
 * <p>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•的工序表(包含岗位人员+设备异常处置+工艺人员交待) æœåŠ¡ç±»
 * </p>
 *
 * @author deslrey
 * @since 2026-03-23
 */
public interface IProductionProductRouteItemService extends IService<ProductionProductRouteItem> {
}
src/main/java/com/ruoyi/production/service/ProductionRecordService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package com.ruoyi.production.service;
import com.ruoyi.production.dto.ProductionRecordDto;
import com.ruoyi.production.vo.ProductionRecordVo;
/**
 * <br>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•Service
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/23 10:22
 */
public interface ProductionRecordService {
    ProductionRecordVo productRouteItem(Long productOrderId);
    void addProductionRecordService(ProductionRecordDto dto);
}
src/main/java/com/ruoyi/production/service/impl/ProductionProductRouteItemFileServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package com.ruoyi.production.service.impl;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.production.pojo.ProductionProductRouteItemFile;
import com.ruoyi.production.mapper.ProductionProductRouteItemFileMapper;
import com.ruoyi.production.service.IProductionProductRouteItemFileService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.nio.file.Files;
/**
 * <p>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•的工序附件表 æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author deslrey
 * @since 2026-03-23
 */
@Slf4j
@Service
public class ProductionProductRouteItemFileServiceImpl extends ServiceImpl<ProductionProductRouteItemFileMapper, ProductionProductRouteItemFile> implements IProductionProductRouteItemFileService {
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteFile(Long fileId) {
        if (fileId == null) {
            throw new ServiceException("附件删除失败,数据不能为空");
        }
        ProductionProductRouteItemFile productionProductRouteItemFile = baseMapper.selectById(fileId);
        if (productionProductRouteItemFile == null) {
            throw new ServiceException("附件删除失败,附件不存在");
        }
        String fileUrl = productionProductRouteItemFile.getFileUrl();
        if (fileUrl != null) {
            try {
                java.nio.file.Path path = java.nio.file.Paths.get(fileUrl);
                if (Files.exists(path)) {
                    Files.delete(path);
                } else {
                    log.warn("文件不存在,无需删除: {}", fileUrl);
                }
            } catch (Exception e) {
                log.error("删除文件失败: {}", fileUrl, e);
                throw new ServiceException("附件删除失败,物理文件删除异常");
            }
        }
        int result = baseMapper.deleteById(fileId);
        if (result == 0) {
            throw new ServiceException("附件删除失败,数据库删除失败");
        }
    }
}
src/main/java/com/ruoyi/production/service/impl/ProductionProductRouteItemParamServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package com.ruoyi.production.service.impl;
import com.ruoyi.production.pojo.ProductionProductRouteItemParam;
import com.ruoyi.production.mapper.ProductionProductRouteItemParamMapper;
import com.ruoyi.production.service.IProductionProductRouteItemParamService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
 * <p>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•工序的参数明细表 æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author deslrey
 * @since 2026-03-23
 */
@Service
public class ProductionProductRouteItemParamServiceImpl extends ServiceImpl<ProductionProductRouteItemParamMapper, ProductionProductRouteItemParam> implements IProductionProductRouteItemParamService {
}
src/main/java/com/ruoyi/production/service/impl/ProductionProductRouteItemServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package com.ruoyi.production.service.impl;
import com.ruoyi.production.pojo.ProductionProductRouteItem;
import com.ruoyi.production.mapper.ProductionProductRouteItemMapper;
import com.ruoyi.production.service.IProductionProductRouteItemService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
 * <p>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•的工序表(包含岗位人员+设备异常处置+工艺人员交待) æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author deslrey
 * @since 2026-03-23
 */
@Service
public class ProductionProductRouteItemServiceImpl extends ServiceImpl<ProductionProductRouteItemMapper, ProductionProductRouteItem> implements IProductionProductRouteItemService {
}
src/main/java/com/ruoyi/production/service/impl/ProductionRecordServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,348 @@
package com.ruoyi.production.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
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.enums.ProductOrderStatusEnum;
import com.ruoyi.production.pojo.*;
import com.ruoyi.production.service.*;
import com.ruoyi.production.vo.ProductionOrderRouteItemParamVo;
import com.ruoyi.production.vo.ProductionOrderRouteItemVo;
import com.ruoyi.production.vo.ProductionOrderStructureVo;
import com.ruoyi.production.vo.ProductionRecordVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
/**
 * <br>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•Service实现类
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/23 10:23
 */
@Slf4j
@Service
public class ProductionRecordServiceImpl implements ProductionRecordService {
    @Value("${file.upload-dir}")
    private String uploadDir;
    @Autowired
    private ProductionProductMainService productionProductMainService;
    @Autowired
    private ProductionProductInputService productionProductInputService;
    @Autowired
    private ProductionProductOutputService productionProductOutputService;
    @Autowired
    private IProductionProductRouteItemService productionProductRouteItemService;
    @Autowired
    private IProductionProductRouteItemParamService productionProductRouteItemParamService;
    @Autowired
    private IProductionProductRouteItemFileService productionProductRouteItemFileService;
    @Autowired
    private ProductOrderService productOrderService;
    @Autowired
    private IProductionOrderRouteItemService productionOrderRouteItemService;
    @Autowired
    private IProductionOrderRouteItemParamService productionOrderRouteItemParamService;
    @Autowired
    private IProductionOrderStructureService productionOrderStructureService;
    @Autowired
    private ProductProcessService productProcessService;
    @Autowired
    private ProductMaterialSkuService productMaterialSkuService;
    @Autowired
    private ProductMaterialService productMaterialService;
    @Autowired
    private TempFileMapper tempFileMapper;
    @Override
    public ProductionRecordVo productRouteItem(Long productOrderId) {
        if (productOrderId == null) {
            throw new ServiceException("请选择生产订单再新增报工");
        }
        //  æŸ¥è¯¢å‡ºç”Ÿäº§è®¢å•绑定的工艺路线
        ProductOrder productOrder = productOrderService.getById(productOrderId);
        if (productOrder == null) {
            throw new ServiceException("未查询到该生产订单的信息");
        }
        //  é€šè¿‡å·¥è‰ºè·¯çº¿æŸ¥è¯¢å‡ºç»‘定的工序及参数
        List<ProductionOrderRouteItem> productionOrderRouteItemList = productionOrderRouteItemService.list(new LambdaQueryWrapper<ProductionOrderRouteItem>()
                .eq(ProductionOrderRouteItem::getOrderId, productOrderId)
                .eq(ProductionOrderRouteItem::getRouteId, productOrder.getRouteId()));
        if (productionOrderRouteItemList == null || productionOrderRouteItemList.isEmpty()) {
            throw new ServiceException("该生产订单绑定的工艺路线无工序");
        }
        ProductionRecordVo productionRecordVo = new ProductionRecordVo();
        productionRecordVo.setProductOrderId(productOrderId);
        productionRecordVo.setProductRouteId(productOrder.getRouteId());
        List<ProductionOrderRouteItemVo> productionOrderRouteItemVoList = new ArrayList<>();
        //  æ ¹æ®ç»‘定的工序查询出工序参数及BOM需要消耗的产品
        for (ProductionOrderRouteItem orderRouteItem : productionOrderRouteItemList) {
            ProductionOrderRouteItemVo productionOrderRouteItemVo = new ProductionOrderRouteItemVo();
            productionOrderRouteItemVo.setProcessId(orderRouteItem.getProcessId());
            if (orderRouteItem.getProcessId() != null) {
                ProductProcess process = productProcessService.getById(orderRouteItem.getProcessId());
                if (process != null) {
                    productionOrderRouteItemVo.setProcessName(process.getName());
                }
            }
            List<ProductionOrderRouteItemParam> orderRouteItemParamList = productionOrderRouteItemParamService.list(new LambdaQueryWrapper<ProductionOrderRouteItemParam>()
                    .eq(ProductionOrderRouteItemParam::getOrderId, productOrderId)
                    .eq(ProductionOrderRouteItemParam::getRouteItemId, orderRouteItem.getId()));
            if (orderRouteItemParamList != null && !orderRouteItemParamList.isEmpty()) {
                List<ProductionOrderRouteItemParamVo> paraVoList = orderRouteItemParamList.stream().map(param -> {
                    ProductionOrderRouteItemParamVo paraVo = new ProductionOrderRouteItemParamVo();
                    BeanUtils.copyProperties(param, paraVo);
                    paraVo.setParamFormat(DateUtils.toUpperCasePattern(paraVo.getParamFormat()));
                    return paraVo;
                }).collect(Collectors.toList());
                productionOrderRouteItemVo.setOrderRouteItemParaVos(paraVoList);
            }
            List<ProductionOrderStructure> orderStructureList = productionOrderStructureService.list(new LambdaQueryWrapper<ProductionOrderStructure>()
                    .eq(ProductionOrderStructure::getOrderId, productOrderId)
                    .eq(ProductionOrderStructure::getProcessId, orderRouteItem.getProcessId()));
            if (orderStructureList != null && !orderStructureList.isEmpty()) {
                List<ProductionOrderStructureVo> structureVoList = orderStructureList.stream().map(struct -> {
                    ProductionOrderStructureVo structureVo = new ProductionOrderStructureVo();
                    BeanUtils.copyProperties(struct, structureVo);
                    //  æŸ¥è¯¢å‡ºäº§å“çš„名称/模型
                    ProductMaterialSku productMaterialSku = productMaterialSkuService.getById(structureVo.getProductModelId());
                    ProductMaterial productMaterial = productMaterialService.getById(productMaterialSku.getProductId());
                    structureVo.setProductName(productMaterial.getProductName());
                    structureVo.setModel(productMaterialSku.getModel());
                    return structureVo;
                }).collect(Collectors.toList());
                productionOrderRouteItemVo.setOrderStructureVos(structureVoList);
            }
            //  æ¯ä¸€ä¸ªå·¥åºçš„参数、BOM消耗产品
            productionOrderRouteItemVoList.add(productionOrderRouteItemVo);
        }
        productionRecordVo.setProductionOrderRouteItemVos(productionOrderRouteItemVoList);
        return productionRecordVo;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addProductionRecordService(ProductionRecordDto dto) {
        if (dto == null) {
            throw new ServiceException("报工失败,数据不能为空");
        }
        if (dto.getProductOrderId() == null) {
            throw new ServiceException("报工失败,生产订单ID不能为空");
        }
        if (dto.getProductId() == null) {
            throw new ServiceException("报工失败,产品信息不能为空");
        }
        if (StringUtils.isEmpty(dto.getPostName()) || StringUtils.isEmpty(dto.getSchedule())) {
            throw new ServiceException("报工失败,岗位人员/班次信息不能为空");
        }
        //  æ›´æ–°ç”Ÿäº§è®¢å•数据
        ProductOrder productOrder = productOrderService.getById(dto.getProductOrderId());
        if (productOrder == null) {
            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("报工失败,本次报工数量不能大于剩余可报工数量");
        }
        //  è®¾ç½®å¼€å§‹æ—¶é—´ï¼ˆç¬¬ä¸€æ¬¡æŠ¥å·¥ï¼‰
        if (productOrder.getStartTime() == null) {
            productOrder.setStartTime(LocalDateTime.now());
        }
        //  çŠ¶æ€åˆ¤æ–­
        if (newCompleteQty.compareTo(totalQty) >= 0 && totalQty.compareTo(BigDecimal.ZERO) > 0) {
            productOrder.setStatus(ProductOrderStatusEnum.FINISHED.getCode());
            productOrder.setEndTime(LocalDateTime.now());
        } else {
            productOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode());
        }
        boolean update = productOrderService.updateById(productOrder);
        if (!update) {
            throw new ServiceException("报工失败,生产订单更新失败");
        }
        //  å®ŒæˆæŠ¥å·¥ä¸»è¡¨-投入表-产出表数据
        ProductionProductMain productionProductMain = new ProductionProductMain();
        productionProductMain.setProductNo("BG" + UUID.randomUUID());
        productionProductMain.setProductOrderId(dto.getProductOrderId());
        productionProductMain.setSchedule(dto.getSchedule());
        boolean result = productionProductMainService.save(productionProductMain);
        if (!result) {
            throw new ServiceException("报工失败,数据存储失败");
        }
        ProductionProductInput productionProductInput = new ProductionProductInput();
        productionProductInput.setProductMainId(productionProductMain.getId());
        productionProductInput.setProductModelId(dto.getProductId());
        productionProductInput.setQuantity(dto.getQuantity());
        result = productionProductInputService.save(productionProductInput);
        if (!result) {
            throw new ServiceException("报工失败,生产投入存储失败");
        }
        ProductionProductOutput productionProductOutput = new ProductionProductOutput();
        productionProductOutput.setProductMainId(productionProductMain.getId());
        productionProductOutput.setProductModelId(dto.getProductId());
        productionProductOutput.setQuantity(dto.getQualifiedQuantity());
        productionProductOutput.setScrapQty(dto.getUnqualifiedQuantity());
        result = productionProductOutputService.save(productionProductOutput);
        if (!result) {
            throw new ServiceException("报工失败,生产产出存储失败");
        }
        //  å¤„理工序
        List<ProductionProductRouteItemDto> productionProductRouteItemDtoList = dto.getProductionProductRouteItemDtoList();
        if (productionProductRouteItemDtoList == null || productionProductRouteItemDtoList.isEmpty()) {
            throw new ServiceException("报工失败,工序参数不能为空");
        }
        for (ProductionProductRouteItemDto productRouteItemDto : productionProductRouteItemDtoList) {
            //  å¤„理工序主表
            ProductionProductRouteItem productRouteItemEntity = new ProductionProductRouteItem();
            BeanUtils.copyProperties(productRouteItemDto, productRouteItemEntity, "id");
            productRouteItemEntity.setProductMainId(productionProductMain.getId());
            productionProductRouteItemService.save(productRouteItemEntity);
            //  å¤„理参数及消耗产品
            List<ProductionProductRouteItemParamDto> productionProductRouteItemParamDtoList = productRouteItemDto.getProductionProductRouteItemParamDtoList();
            if (productionProductRouteItemParamDtoList != null && !productionProductRouteItemParamDtoList.isEmpty()) {
                for (ProductionProductRouteItemParamDto productRouteItemParamDto : productionProductRouteItemParamDtoList) {
                    ProductionProductRouteItemParam paramEntity = new ProductionProductRouteItemParam();
                    BeanUtils.copyProperties(productRouteItemParamDto, paramEntity, "id");
                    paramEntity.setProductionProductRouteItemId(productRouteItemEntity.getId());
                    if (paramEntity.getProductId() != null) {
                    } else {
                        ProductionOrderRouteItemParam productionOrderRouteItemParam = productionOrderRouteItemParamService.getById(productRouteItemParamDto.getId());
                        paramEntity.setParamName(productionOrderRouteItemParam.getParamName());
                        paramEntity.setParamType(productionOrderRouteItemParam.getParamType());
                        paramEntity.setParamFormat(productionOrderRouteItemParam.getParamFormat());
                        paramEntity.setValueMode(productionOrderRouteItemParam.getValueMode());
                    }
                    productionProductRouteItemParamService.save(paramEntity);
                }
            }
            //  å¤„理工序上传的附件
            List<String> files = productRouteItemDto.getFiles();
            if (files != null && !files.isEmpty()) {
                for (String tempId : files) {
                    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 = productRouteItemEntity.getId() + "_" +
                                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(productRouteItemEntity.getId());
                        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());
                    }
                }
            }
        }
    }
}
src/main/java/com/ruoyi/production/vo/ProductionOrderRouteItemParamVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
package com.ruoyi.production.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * <br>
 *
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/23 13:05
 */
@Data
public class ProductionOrderRouteItemParamVo {
    @ApiModelProperty(value = "主键ID")
    private Long id;
    @ApiModelProperty(value = "参数唯一标识")
    private String paramKey;
    @ApiModelProperty(value = "参数名称")
    private String paramName;
    @ApiModelProperty(value = "参数类型(1数字 2文本 3下拉选择 4时间)")
    private Integer paramType;
    @ApiModelProperty(value = "参数格式")
    private String paramFormat;
    @ApiModelProperty(value = "值模式(1单值 2区间)")
    private Integer valueMode;
    @ApiModelProperty(value = "单位")
    private String unit;
    @ApiModelProperty(value = "标准值")
    private String standardValue;
    @ApiModelProperty(value = "此路线节点设定的标准最小值")
    private java.math.BigDecimal minValue;
    @ApiModelProperty(value = "此路线节点设定的标准最大值")
    private java.math.BigDecimal maxValue;
    @ApiModelProperty(value = "是否必填")
    private Boolean isRequired;
}
src/main/java/com/ruoyi/production/vo/ProductionOrderRouteItemVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package com.ruoyi.production.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
 * <br>
 * ç”Ÿäº§è®¢å•绑定的工艺路线--工序表Vo
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/23 12:00
 */
@Data
public class ProductionOrderRouteItemVo {
    @ApiModelProperty(value = "工序id")
    private Long processId;
    @ApiModelProperty(value = "工序名称")
    private String processName;
    @ApiModelProperty("工序绑定的参数列表")
    private List<ProductionOrderRouteItemParamVo> orderRouteItemParaVos;
    @ApiModelProperty("BOM内工序需要消耗的产品列表")
    private List<ProductionOrderStructureVo> orderStructureVos;
}
src/main/java/com/ruoyi/production/vo/ProductionOrderStructureVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
package com.ruoyi.production.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
/**
 * <br>
 *
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/23 13:05
 */
@Data
public class ProductionOrderStructureVo {
    @ApiModelProperty(value = "主键ID")
    private Long id;
    @ApiModelProperty(value = "产品id")
    private Long productModelId;
    @ApiModelProperty(value = "产品名称")
    private String productName;
    @ApiModelProperty(value = "产品模型")
    private String model;
    @ApiModelProperty(value = "单位产出需要数量")
    private BigDecimal unitQuantity;
//    @ApiModelProperty(value = "需求数量")
//    private java.math.BigDecimal demandedQuantity;
    @ApiModelProperty(value = "单位")
    private String unit;
    @ApiModelProperty(value = "bom的id")
    private Long bomId;
}
src/main/java/com/ruoyi/production/vo/ProductionRecordVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package com.ruoyi.production.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
 * <br>
 * ç”Ÿäº§æŠ¥å·¥è®°å½•Vo
 * </br>
 *
 * @author deslrey
 * @version 1.0
 * @since 2026/03/23 11:36
 */
@Data
@ApiModel(value = "ProductionRecordVo", description = "生产报工记录Vo")
public class ProductionRecordVo {
    @ApiModelProperty("生产订单ID")
    private Long productOrderId;
    @ApiModelProperty("工艺路线ID")
    private Long productRouteId;
    @ApiModelProperty("工艺路线绑定的工序")
    private List<ProductionOrderRouteItemVo> productionOrderRouteItemVos;
}
src/main/resources/mapper/production/ProductionProductMainMapper.xml
@@ -10,48 +10,43 @@
        <result property="tenantId" column="tenant_id"/>
        <result property="createTime" column="create_time"/>
        <result property="status" column="status"/>
        <result property="postName" column="post_name"/>
        <result property="schedule" column="schedule"/>
        <result property="reportingTime" column="reporting_time"/>
    </resultMap>
    <select id="listPageProductionProductMainDto" resultType="com.ruoyi.production.dto.ProductionProductMainDto">
        select ppm.*,
        pwo.work_order_no as workOrderNo,
        pwo.status as workOrderStatus,
        u.nick_name as nickName,
        p.product_name as productName,
        pp.name as process,
        pm.model as productModelName,
        ppo.quantity,
        ppo.scrap_qty,
        pm.unit,
        sl.sales_contract_no salesContractNo
        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(ppo.scrap_qty, 0) as scrapQty,
        IFNULL(ppo.quantity, 0) as quantity
        from
        production_product_main ppm
        left join product_work_order pwo on pwo.id = ppm.work_order_id
        left join product_process_route_item ppri on ppri.id = pwo.product_process_route_item_id
        left join product_process pp on pp.id = ppri.process_id
        left join product_order po on po.id = pwo.product_order_id
        left join production_product_output ppo on ppm.id = ppo.product_main_id
        left join product_model pm on pm.id = ppo.product_model_id
        left join product p on p.id = pm.product_id
        left join sales_ledger sl on sl.id = po.sales_ledger_id
        left join sys_user u on u.user_id = ppm.user_id
        left join production_product_output ppo on ppo.product_main_id = ppm.id
        left join production_product_input ppi on ppi.product_main_id = ppm.id
        left join product_order po on po.id = ppm.product_order_id
        left join product_order_plan pop on po.id = pop.product_order_id
        left join production_plan pp on pop.production_plan_id = pp.id
        left join product_material_sku pms on pp.product_material_sku_id = pms.id
        left join product_material pm on pm.id = pms.product_id
        <where>
            <if test="c.nickName != null and c.nickName != ''">
                and u.nick_name like concat('%',#{c.nickName},'%')
            <if test="c.npsNo != null and c.npsNo != ''">
                AND po.nps_no like concat('%', #{c.npsNo}, '%')
            </if>
            <if test="c.workOrderNo != null and c.workOrderNo != ''">
                and pwo.work_order_no like concat('%',#{c.workOrderNo},'%')
            <if test="c.schedule != null and c.schedule != ''">
                AND ppm.schedule like concat('%', #{c.schedule}, '%')
            </if>
            <if test="c.workOrderStatus != null and c.workOrderStatus != ''">
                and pwo.status = #{c.workOrderStatus}
            </if>
            <if test="c.status != null and c.status != ''">
                and ppm.status = #{c.status}
            <if test="c.productName != null and c.productName != ''">
                AND pm.product_name like concat('%', #{c.productName}, '%')
            </if>
        </where>
        order by ppm.id
        order by ppm.id desc
    </select>
    <select id="getOrderByMainId" resultType="com.ruoyi.production.pojo.ProductOrder">
        select po.*
        from product_order po
@@ -86,11 +81,11 @@
            <if test="ew.schedulingUserName != null and ew.schedulingUserName !=''">
                and slpa.scheduling_user_name = #{ew.schedulingUserName}
            </if>
            <if test="ew.entryDate != null " >
            <if test="ew.entryDate != null ">
                and slpa.scheduling_date >= #{ew.entryDate}
                and slpa.scheduling_date &lt; DATE_ADD(#{ew.entryDate}, INTERVAL 1 DAY)
            </if>
            <if test="ew.entryDateStart != null and ew.entryDateEnd != null" >
            <if test="ew.entryDateStart != null and ew.entryDateEnd != null">
                and slpa.scheduling_date >= #{ew.entryDateStart}
                and slpa.scheduling_date &lt; date_add(#{ew.entryDateEnd}, INTERVAL 1 DAY)
            </if>
@@ -98,9 +93,9 @@
    </select>
    <select id="listMain" resultType="java.lang.Long">
        SELECT ppm.id FROM production_product_main ppm
            left join product_work_order pwo on pwo.id = ppm.work_order_id
                  left join product_order po on po.id = pwo.product_order_id
                  left join sales_ledger sl on sl.id = po.sales_ledger_id
        left join product_work_order pwo on pwo.id = ppm.work_order_id
        left join product_order po on po.id = pwo.product_order_id
        left join sales_ledger sl on sl.id = po.sales_ledger_id
        <where>
            <if test="idList != null and idList.size() > 0">
                and sl.id in
src/main/resources/mapper/production/ProductionProductRouteItemFileMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
<?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.ProductionProductRouteItemFileMapper">
    <resultMap id="BaseResultMap" type="com.ruoyi.production.pojo.ProductionProductRouteItemFile">
        <id column="id" property="id" />
        <result column="production_product_route_item_id" property="productionProductRouteItemId" />
        <result column="file_name" property="fileName" />
        <result column="file_url" property="fileUrl" />
        <result column="file_size" property="fileSize" />
        <result column="file_suffix" property="fileSuffix" />
        <result column="create_user" property="createUser" />
        <result column="create_time" property="createTime" />
        <result column="update_user" property="updateUser" />
        <result column="update_time" property="updateTime" />
        <result column="tenant_id" property="tenantId" />
    </resultMap>
</mapper>
src/main/resources/mapper/production/ProductionProductRouteItemMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
<?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.ProductionProductRouteItemMapper">
    <resultMap id="BaseResultMap" type="com.ruoyi.production.pojo.ProductionProductRouteItem">
        <id column="id" property="id"/>
        <result column="product_main_id" property="productMainId"/>
        <result column="post_name" property="postName"/>
        <result column="equipment_malfunction" property="equipmentMalfunction"/>
        <result column="equipment_disposal" property="equipmentDisposal"/>
        <result column="process_explained" property="processExplained"/>
        <result column="process_id" property="processId"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
        <result column="tenant_id" property="tenantId"/>
    </resultMap>
</mapper>
src/main/resources/mapper/production/ProductionProductRouteItemParamMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
<?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.ProductionProductRouteItemParamMapper">
    <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="param_name" property="paramName"/>
        <result column="param_type" property="paramType"/>
        <result column="param_format" property="paramFormat"/>
        <result column="value_mode" property="valueMode"/>
        <result column="standard_value" property="standardValue"/>
        <result column="min_value" property="minValue"/>
        <result column="max_value" property="maxValue"/>
        <result column="product_id" property="productId"/>
        <result column="bom_id" property="bomId"/>
        <result column="product_value" property="productValue"/>
        <result column="unit" property="unit"/>
        <result column="is_required" property="isRequired"/>
        <result column="source_sort" property="sourceSort"/>
        <result column="dict_code" property="dictCode"/>
        <result column="create_user" property="createUser"/>
        <result column="create_time" property="createTime"/>
        <result column="update_user" property="updateUser"/>
        <result column="update_time" property="updateTime"/>
        <result column="tenant_id" property="tenantId"/>
    </resultMap>
</mapper>