diff --git a/nflg-qms-admin/pom.xml b/nflg-qms-admin/pom.xml index 67cbca07..eaddcf46 100644 --- a/nflg-qms-admin/pom.xml +++ b/nflg-qms-admin/pom.xml @@ -248,7 +248,7 @@ org.springframework.boot spring-boot-maven-plugin - true + com.nflg.qms.admin.QmsApplication diff --git a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/controller/QmsPqcInspectionRuleController.java b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/controller/QmsPqcInspectionRuleController.java index 47a26436..43250160 100644 --- a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/controller/QmsPqcInspectionRuleController.java +++ b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/controller/QmsPqcInspectionRuleController.java @@ -4,25 +4,29 @@ import com.nflg.qms.admin.pojo.qo.PqcInspectionRuleAddQO; import com.nflg.qms.admin.pojo.qo.PqcInspectionRuleAuditQO; import com.nflg.qms.admin.pojo.qo.PqcInspectionRuleEditQO; import com.nflg.qms.admin.pojo.qo.PqcInspectionRuleSearchQO; -import com.nflg.qms.admin.pojo.vo.PqcInspectionPointListVO; import com.nflg.qms.admin.pojo.vo.PqcInspectionRuleDetailVO; import com.nflg.qms.admin.pojo.vo.PqcInspectionRuleVO; import com.nflg.qms.admin.service.QmsPqcInspectionRuleControllerService; import com.nflg.wms.common.constant.Constant; import com.nflg.wms.common.pojo.ApiResult; import com.nflg.wms.common.pojo.PageData; -import com.nflg.wms.common.pojo.vo.QmsPqcInspectionPointItemsGroupedVO; +import com.nflg.wms.common.pojo.qo.PqcInspectionRuleExportQO; import com.nflg.wms.common.util.MultilingualUtil; import com.nflg.wms.repository.entity.DictionaryItem; import com.nflg.wms.repository.service.IDictionaryItemService; import com.nflg.wms.starter.BaseController; import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * PQC检验规则管理 @@ -53,31 +57,16 @@ public class QmsPqcInspectionRuleController extends BaseController { return ApiResult.success(ruleControllerService.getDetail(id)); } - /** - * 根据机型编号查询该机型最新启用PQC规则下的检查点列表 - */ - @GetMapping("/points") - public ApiResult> listPointsByModelNo(@NotNull String modelNo) { - return ApiResult.success(ruleControllerService.listPointsByModelNo(modelNo)); - } - - /** - * 根据检查点ID查询对应的检测项列表(分组返回) - * 返回三类:关键物料拍照类、工序检查-自检复核类、工序检查-QC检测类 - */ - @GetMapping("/items") - public ApiResult listItemsByInspectionPointId(@NotNull Long inspectionPointId) { - return ApiResult.success(ruleControllerService.listItemsByInspectionPointIdGrouped(inspectionPointId)); - } - /** * 新增规则 */ @PostMapping("/add") @Transactional - public ApiResult add(@Valid @RequestBody PqcInspectionRuleAddQO qo) { - ruleControllerService.add(qo); - return ApiResult.success(); + public ApiResult> add(@Valid @RequestBody PqcInspectionRuleAddQO qo) { + Long id = ruleControllerService.add(qo); + Map result = new HashMap<>(); + result.put("id", id); + return ApiResult.success(result); } /** @@ -85,9 +74,11 @@ public class QmsPqcInspectionRuleController extends BaseController { */ @PostMapping("/edit") @Transactional - public ApiResult edit(@Valid @RequestBody PqcInspectionRuleEditQO qo) { - ruleControllerService.edit(qo); - return ApiResult.success(); + public ApiResult> edit(@Valid @RequestBody PqcInspectionRuleEditQO qo) { + Long id = ruleControllerService.edit(qo); + Map result = new HashMap<>(); + result.put("id", id); + return ApiResult.success(result); } /** @@ -124,11 +115,23 @@ public class QmsPqcInspectionRuleController extends BaseController { */ @PostMapping("/toggleDisabled") @Transactional - public ApiResult toggleDisabled(@NotNull Long id) { + public ApiResult toggleDisabled(@NotNull @RequestParam Long id) { ruleControllerService.toggleDisabled(id); return ApiResult.success(); } + /** + * 删除检查点(含其下检查项) + */ + @PostMapping("/deletePoint") + @Transactional + public ApiResult> deletePoint(@NotNull @RequestParam Long pointId) { + Long ruleId = ruleControllerService.deletePoint(pointId); + Map result = new HashMap<>(); + result.put("id", ruleId); + return ApiResult.success(result); + } + /** * 获取步装字典 */ @@ -137,4 +140,34 @@ public class QmsPqcInspectionRuleController extends BaseController { return ApiResult.success(dictionaryItemService.getListByDictionaryCode( Constant.DICTIONARY_STEP_POSITION, MultilingualUtil.getLanguage())); } + + /** + * 导出PQC检测规则 + * 如果ids不为空则导出指定ID数据,否则导出查询条件匹配的全部数据 + */ + @PostMapping("/export") + public void export(HttpServletResponse response, @Valid @RequestBody PqcInspectionRuleExportQO qo) throws IOException { + ruleControllerService.export(response, qo); + } + + /** + * 下载导入模板 + */ + @GetMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) throws IOException { + ruleControllerService.downloadTemplate(response); + } + + /** + * 导入PQC检测规则 + * @param file Excel文件 + * @param importMode 导入方式:overwrite=覆盖导入,append=追加导入 + */ + @PostMapping("/import") + @Transactional + public ApiResult importData(@RequestParam("file") MultipartFile file, + @NotNull(message = "导入方式不能为空") @RequestParam String importMode) throws Exception { + ruleControllerService.importFromExcel(file, importMode); + return ApiResult.success(); + } } diff --git a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/qo/PqcInspectionRuleAddQO.java b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/qo/PqcInspectionRuleAddQO.java index 94f533ab..ac0b6cc8 100644 --- a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/qo/PqcInspectionRuleAddQO.java +++ b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/qo/PqcInspectionRuleAddQO.java @@ -47,15 +47,13 @@ public class PqcInspectionRuleAddQO { private Long stepDicItemId; /** - * 检查点编号(必填) + * 检查点编号 */ - @NotBlank(message = "检查点编号不能为空") private String inspectionPointCode; /** - * 检查点名称(必填) + * 检查点名称 */ - @NotBlank(message = "检查点名称不能为空") private String inspectionPointName; /** @@ -78,9 +76,9 @@ public class PqcInspectionRuleAddQO { private String inspectionContent; /** - * 检查类型:0=工序检查,1=关键物料拍照,2=全部(必填) + * 检查类型:0=工序检查,1=关键物料拍照,2=关键物料采集(多选) */ - private Integer inspectionType; + private List inspectionType; /** * 判定类型:0=目视,1=量具(必填) diff --git a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/qo/PqcInspectionRuleEditQO.java b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/qo/PqcInspectionRuleEditQO.java index 210a3ce5..c3b27293 100644 --- a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/qo/PqcInspectionRuleEditQO.java +++ b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/qo/PqcInspectionRuleEditQO.java @@ -86,9 +86,9 @@ public class PqcInspectionRuleEditQO { private String inspectionContent; /** - * 检查类型 + * 检查类型:多选 */ - private Integer inspectionType; + private List inspectionType; /** * 判定类型 diff --git a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/vo/PqcInspectionPointItemVO.java b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/vo/PqcInspectionPointItemVO.java index ce9410b3..9d880cf2 100644 --- a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/vo/PqcInspectionPointItemVO.java +++ b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/vo/PqcInspectionPointItemVO.java @@ -4,6 +4,7 @@ import lombok.Data; import lombok.experimental.Accessors; import java.time.LocalDateTime; +import java.util.List; /** * 检查项VO @@ -20,70 +21,45 @@ public class PqcInspectionPointItemVO { /** * 检查点ID */ - private Long pointId; + private Long inspectionCodeId; /** - * 检验规则ID - */ - private Long ruleId; - - /** - * 检查项名称 - */ - private String itemName; - - /** - * 检查项编码 - */ - private String itemCode; - - /** - * 检验标准 - */ - private String standard; - - /** - * 检验方法 - */ - private String inspectionMethod; - - /** - * 上限值 - */ - private String upperLimit; - - /** - * 下限值 - */ - private String lowerLimit; - - /** - * 单位 - */ - private String unit; - - /** - * 检查顺序 + * 排序 */ private Integer sort; /** - * 是否必填:0-否,1-是 + * 检查内容 */ - private Short isRequired; + private String inspectionContent; /** - * 备注 + * 检查类型:0=工序检查,1=关键物料拍照,2=关键物料采集(多选,返回数组) */ - private String remark; + private List inspectionType; /** - * 创建人ID + * 判定类型:0=目视,1=量具 */ - private Long createId; + private Integer inspectionMethods; /** - * 创建人名称 + * 样例图地址 + */ + private String inspectionImgUrl; + + /** + * 星级(1-3级) + */ + private Integer inspectionLevel; + + /** + * 创建人编号 + */ + private Long createBy; + + /** + * 创建人姓名 */ private String createName; @@ -93,17 +69,17 @@ public class PqcInspectionPointItemVO { private LocalDateTime createTime; /** - * 修改人ID + * 修改人编号 */ - private Long modifyId; + private Long updateBy; /** - * 修改人名称 + * 修改人姓名 */ - private String modifyName; + private String updateName; /** * 修改时间 */ - private LocalDateTime modifyTime; + private LocalDateTime updateTime; } diff --git a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/vo/PqcInspectionPointVO.java b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/vo/PqcInspectionPointVO.java index 9b420db4..bc4c4703 100644 --- a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/vo/PqcInspectionPointVO.java +++ b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/vo/PqcInspectionPointVO.java @@ -19,37 +19,37 @@ public class PqcInspectionPointVO { private Long id; /** - * 检验规则ID + * 规则主表ID */ - private Long ruleId; + private Long pqcRuleId; + + /** + * 步装名称 + */ + private String stepName; + + /** + * 步装字典ID + */ + private Long stepDicItemId; + + /** + * 检查点编号 + */ + private String inspectionPointCode; /** * 检查点名称 */ - private String pointName; + private String inspectionPointName; /** - * 检查点编码 + * 创建人编号 */ - private String pointCode; + private Long createBy; /** - * 检查顺序 - */ - private Integer sort; - - /** - * 备注 - */ - private String remark; - - /** - * 创建人ID - */ - private Long createId; - - /** - * 创建人名称 + * 创建人姓名 */ private String createName; @@ -59,19 +59,19 @@ public class PqcInspectionPointVO { private LocalDateTime createTime; /** - * 修改人ID + * 修改人编号 */ - private Long modifyId; + private Long updateBy; /** - * 修改人名称 + * 修改人姓名 */ - private String modifyName; + private String updateName; /** * 修改时间 */ - private LocalDateTime modifyTime; + private LocalDateTime updateTime; /** * 检查项列表 diff --git a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/vo/PqcInspectionRuleDetailVO.java b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/vo/PqcInspectionRuleDetailVO.java index d3824117..3cd84bf1 100644 --- a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/vo/PqcInspectionRuleDetailVO.java +++ b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/pojo/vo/PqcInspectionRuleDetailVO.java @@ -19,72 +19,52 @@ public class PqcInspectionRuleDetailVO { private Long id; /** - * 规则编码 + * 机型编号 */ - private String ruleCode; + private String modelNo; /** - * 规则名称 + * 规则编号 */ - private String ruleName; + private String pqcRuleCode; /** - * 物料编码 + * 版本号 */ - private String materialCode; + private Integer ruleVersion; /** - * 物料描述 + * 是否禁用 */ - private String materialName; + private Boolean isDisabled; /** - * 规则类型 + * 审核状态:0=未审核,1=已审核,2=已驳回 */ - private Short ruleType; + private Integer auditStatus; /** - * 规则类型名称 + * 驳回原因 */ - private String ruleTypeName; + private String auditRemark; /** - * 适用工序 + * 审核人姓名 */ - private String processCode; + private String auditUserName; /** - * 适用工序名称 + * 审核时间 */ - private String processName; + private LocalDateTime auditDateTime; /** - * 审核状态:0-待审核,1-审核通过,2-审核不通过 + * 创建人编号 */ - private Short auditStatus; + private Long createBy; /** - * 审核状态名称 - */ - private String auditStatusName; - - /** - * 启用状态:0-停用,1-启用 - */ - private Short enableStatus; - - /** - * 备注 - */ - private String remark; - - /** - * 创建人ID - */ - private Long createId; - - /** - * 创建人名称 + * 创建人姓名 */ private String createName; @@ -94,34 +74,19 @@ public class PqcInspectionRuleDetailVO { private LocalDateTime createTime; /** - * 修改人ID + * 修改人编号 */ - private Long modifyId; + private Long updateBy; /** - * 修改人名称 + * 修改人姓名 */ - private String modifyName; + private String updateName; /** * 修改时间 */ - private LocalDateTime modifyTime; - - /** - * 审核人 - */ - private String auditBy; - - /** - * 审核时间 - */ - private LocalDateTime auditTime; - - /** - * 审核说明 - */ - private String auditMsg; + private LocalDateTime updateTime; /** * 检查点列表 diff --git a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/service/QmsPqcInspectionRuleControllerService.java b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/service/QmsPqcInspectionRuleControllerService.java index eed86a0f..f83baff0 100644 --- a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/service/QmsPqcInspectionRuleControllerService.java +++ b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/service/QmsPqcInspectionRuleControllerService.java @@ -5,12 +5,15 @@ import com.nflg.qms.admin.pojo.qo.PqcInspectionRuleAddQO; import com.nflg.qms.admin.pojo.qo.PqcInspectionRuleAuditQO; import com.nflg.qms.admin.pojo.qo.PqcInspectionRuleEditQO; import com.nflg.qms.admin.pojo.qo.PqcInspectionRuleSearchQO; -import com.nflg.qms.admin.pojo.vo.PqcInspectionPointListVO; import com.nflg.qms.admin.pojo.vo.PqcInspectionRuleDetailVO; import com.nflg.qms.admin.pojo.vo.PqcInspectionRuleVO; import com.nflg.wms.common.pojo.PageData; -import com.nflg.wms.common.pojo.vo.QmsPqcInspectionPointItemsGroupedVO; +import com.nflg.wms.common.pojo.qo.PqcInspectionRuleExportQO; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; import java.util.List; /** @@ -30,13 +33,15 @@ public interface QmsPqcInspectionRuleControllerService { /** * 新增规则 + * @return 新创建的规则ID */ - void add(PqcInspectionRuleAddQO qo); + Long add(PqcInspectionRuleAddQO qo); /** * 编辑规则(含版本管理) + * 已审核规则有变化时会 fork 出新版本,返回实际保存的规则ID */ - void edit(PqcInspectionRuleEditQO qo); + Long edit(PqcInspectionRuleEditQO qo); /** * 批量删除规则(只能删除未审核的) @@ -58,10 +63,27 @@ public interface QmsPqcInspectionRuleControllerService { */ void toggleDisabled(Long id); - List listPointsByModelNo(String modelNo); + /** + * 删除检查点(含其下检查项) + * 已审核规则会 fork 出新版本后执行删除,返回新版本规则ID;未审核规则返回原规则ID + */ + Long deletePoint(Long pointId); /** - * 根据检查点ID查询检测项列表(分组返回) + * 导入PQC检测规则 + * @param file Excel文件 + * @param importMode 导入方式:overwrite=覆盖导入,append=追加导入 */ - QmsPqcInspectionPointItemsGroupedVO listItemsByInspectionPointIdGrouped(Long inspectionPointId); + void importFromExcel(MultipartFile file, String importMode) throws Exception; + + /** + * 导出PQC检测规则 + * 如果ids不为空则导出指定ID数据,否则导出查询条件匹配的全部数据 + */ + void export(HttpServletResponse response, PqcInspectionRuleExportQO qo) throws IOException; + + /** + * 下载导入模板 + */ + void downloadTemplate(HttpServletResponse response) throws IOException; } diff --git a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/service/impl/QmsPqcInspectionRuleControllerServiceImpl.java b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/service/impl/QmsPqcInspectionRuleControllerServiceImpl.java index 78240453..1aa4b432 100644 --- a/nflg-qms-admin/src/main/java/com/nflg/qms/admin/service/impl/QmsPqcInspectionRuleControllerServiceImpl.java +++ b/nflg-qms-admin/src/main/java/com/nflg/qms/admin/service/impl/QmsPqcInspectionRuleControllerServiceImpl.java @@ -1,6 +1,8 @@ package com.nflg.qms.admin.service.impl; import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -8,34 +10,53 @@ import com.nflg.qms.admin.pojo.qo.PqcInspectionRuleAddQO; import com.nflg.qms.admin.pojo.qo.PqcInspectionRuleAuditQO; import com.nflg.qms.admin.pojo.qo.PqcInspectionRuleEditQO; import com.nflg.qms.admin.pojo.qo.PqcInspectionRuleSearchQO; -import com.nflg.qms.admin.pojo.vo.PqcInspectionPointItemListVO; import com.nflg.qms.admin.pojo.vo.PqcInspectionPointItemVO; -import com.nflg.qms.admin.pojo.vo.PqcInspectionPointListVO; import com.nflg.qms.admin.pojo.vo.PqcInspectionPointVO; import com.nflg.qms.admin.pojo.vo.PqcInspectionRuleDetailVO; import com.nflg.qms.admin.pojo.vo.PqcInspectionRuleVO; +import com.nflg.qms.admin.service.BasdeSerialNumberControllerService; import com.nflg.qms.admin.service.QmsPqcInspectionRuleControllerService; import com.nflg.wms.common.constant.STATE; import com.nflg.wms.common.exception.NflgException; import com.nflg.wms.common.pojo.PageData; -import com.nflg.wms.common.pojo.vo.QmsPqcInspectionPointItemListVO; -import com.nflg.wms.common.pojo.vo.QmsPqcInspectionPointItemsGroupedVO; +import com.nflg.wms.common.pojo.dto.PqcInspectionRuleExportDTO; +import com.nflg.wms.common.pojo.dto.PqcInspectionRuleImportDTO; +import com.nflg.wms.common.pojo.qo.PqcInspectionRuleExportQO; +import com.nflg.wms.common.util.DateTimeUtil; +import com.nflg.wms.common.util.EecExcelUtil; import com.nflg.wms.common.util.PageUtil; import com.nflg.wms.common.util.UserUtil; +import com.nflg.wms.repository.entity.DictionaryItem; +import com.nflg.wms.repository.entity.FileUploadRecord; import com.nflg.wms.repository.entity.QmsPqcInspectionPoint; import com.nflg.wms.repository.entity.QmsPqcInspectionPointItems; import com.nflg.wms.repository.entity.QmsPqcInspectionRule; +import com.nflg.wms.repository.service.IDictionaryItemService; +import com.nflg.wms.repository.service.IFileUploadRecordService; import com.nflg.wms.repository.service.IQmsPqcInspectionPointItemsService; import com.nflg.wms.repository.service.IQmsPqcInspectionPointService; import com.nflg.wms.repository.service.IQmsPqcInspectionRuleService; +import com.nflg.wms.starter.service.FileUploadService; import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import org.ttzero.excel.entity.ListSheet; +import org.ttzero.excel.entity.Workbook; +import org.ttzero.excel.reader.Drawings; +import org.ttzero.excel.reader.ExcelReader; +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -57,10 +78,22 @@ public class QmsPqcInspectionRuleControllerServiceImpl implements QmsPqcInspecti @Resource private IQmsPqcInspectionPointItemsService itemsService; + @Resource + private BasdeSerialNumberControllerService serialNumberControllerService; + + @Resource + private IDictionaryItemService dictionaryItemService; + + @Resource + private FileUploadService fileUploadService; + + @Resource + private IFileUploadRecordService fileUploadRecordService; + @Override public PageData search(PqcInspectionRuleSearchQO qo) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - + // 查询条件 queryWrapper.like(StrUtil.isNotBlank(qo.getModelNo()), QmsPqcInspectionRule::getModelNo, qo.getModelNo()) .like(StrUtil.isNotBlank(qo.getPqcRuleCode()), QmsPqcInspectionRule::getPqcRuleCode, qo.getPqcRuleCode()) @@ -104,7 +137,12 @@ public class QmsPqcInspectionRuleControllerServiceImpl implements QmsPqcInspecti .list(); List itemVOs = items.stream() - .map(item -> BeanUtil.copyProperties(item, PqcInspectionPointItemVO.class)) + .map(item -> { + PqcInspectionPointItemVO itemVO = BeanUtil.copyProperties(item, PqcInspectionPointItemVO.class); + // 将位标志转换回List + itemVO.setInspectionType(bitmaskToList(item.getInspectionType())); + return itemVO; + }) .collect(Collectors.toList()); pointVO.setInspectionItems(itemVOs); @@ -117,7 +155,7 @@ public class QmsPqcInspectionRuleControllerServiceImpl implements QmsPqcInspecti @Override @Transactional - public void add(PqcInspectionRuleAddQO qo) { + public Long add(PqcInspectionRuleAddQO qo) { String operator = UserUtil.getUserName(); Long operatorId = UserUtil.getUserId(); LocalDateTime now = LocalDateTime.now(); @@ -125,7 +163,11 @@ public class QmsPqcInspectionRuleControllerServiceImpl implements QmsPqcInspecti // 创建规则主表 QmsPqcInspectionRule rule = new QmsPqcInspectionRule(); rule.setModelNo(qo.getModelNo()); - rule.setPqcRuleCode(qo.getPqcRuleCode()); + + // 规则编号为空时自动生成 + rule.setPqcRuleCode(StrUtil.isNotBlank(qo.getPqcRuleCode()) + ? qo.getPqcRuleCode() + : serialNumberControllerService.generateSerialNumber(40)); rule.setRuleVersion(1); rule.setIsDisabled(true); // 默认启用 rule.setAuditStatus(0); // 默认未审核 @@ -137,11 +179,13 @@ public class QmsPqcInspectionRuleControllerServiceImpl implements QmsPqcInspecti // 保存检查点和检查项 savePointsAndItems(rule.getId(), qo.getInspectionPoints(), operator, operatorId, now); + + return rule.getId(); } @Override @Transactional - public void edit(PqcInspectionRuleEditQO qo) { + public Long edit(PqcInspectionRuleEditQO qo) { QmsPqcInspectionRule existRule = ruleService.getById(qo.getId()); if (Objects.isNull(existRule)) { throw new NflgException(STATE.BusinessError, "规则不存在"); @@ -151,53 +195,24 @@ public class QmsPqcInspectionRuleControllerServiceImpl implements QmsPqcInspecti Long operatorId = UserUtil.getUserId(); LocalDateTime now = LocalDateTime.now(); - // 如果已审核,需要比对数据是否变化,决定是更新还是创建新版本 - if (existRule.getAuditStatus() == 1) { - // 比对数据是否变化 - boolean hasChanged = checkDataChanged(existRule.getId(), qo); - - if (hasChanged) { - // 有变化,创建新版本 - QmsPqcInspectionRule newRule = new QmsPqcInspectionRule(); - BeanUtil.copyProperties(existRule, newRule); - newRule.setId(null); - newRule.setRuleVersion(existRule.getRuleVersion() + 1); - newRule.setAuditStatus(0); // 新版本默认未审核 - newRule.setAuditUserId(null); - newRule.setAuditUserName(null); - newRule.setAuditDateTime(null); - newRule.setAuditRemark(null); - newRule.setUpdateBy(operatorId); - newRule.setUpdateName(operator); - newRule.setUpdateTime(now); - newRule.setModelNo(qo.getModelNo()); - - ruleService.save(newRule); - - // 保存新版本的检查点和检查项 - savePointsAndItemsFromEditQO(newRule.getId(), qo.getInspectionPoints(), operator, operatorId, now); - } else { - // 无变化,直接更新 - existRule.setModelNo(qo.getModelNo()); - existRule.setUpdateBy(operatorId); - existRule.setUpdateName(operator); - existRule.setUpdateTime(now); - ruleService.updateById(existRule); - - // 更新检查点和检查项 - updatePointsAndItems(existRule.getId(), qo.getInspectionPoints(), operator, operatorId, now); + // 已审核规则:fork 出新版本,在新版本上操作,原记录不动 + if (existRule.getAuditStatus() != null && existRule.getAuditStatus() == 1) { + if (checkDraftExists(existRule.getModelNo(), null)) { + throw new NflgException(STATE.BusinessError, "该机型已存在未审核的版本,请先完成审核后再编辑"); } - } else { - // 未审核,直接更新 - existRule.setModelNo(qo.getModelNo()); - existRule.setUpdateBy(operatorId); - existRule.setUpdateName(operator); - existRule.setUpdateTime(now); - ruleService.updateById(existRule); - - // 更新检查点和检查项 - updatePointsAndItems(existRule.getId(), qo.getInspectionPoints(), operator, operatorId, now); + Long newRuleId = forkNewVersion(existRule); + existRule = ruleService.getById(newRuleId); } + + existRule.setModelNo(qo.getModelNo()); + existRule.setUpdateBy(operatorId); + existRule.setUpdateName(operator); + existRule.setUpdateTime(now); + ruleService.updateById(existRule); + + // 更新检查点和检查项 + updatePointsAndItems(existRule.getId(), qo.getInspectionPoints(), operator, operatorId, now); + return existRule.getId(); } @Override @@ -339,89 +354,117 @@ public class QmsPqcInspectionRuleControllerServiceImpl implements QmsPqcInspecti ruleService.updateById(rule); } + @Override + @Transactional + public Long deletePoint(Long pointId) { + QmsPqcInspectionPoint point = pointService.getById(pointId); + if (Objects.isNull(point)) { + throw new NflgException(STATE.BusinessError, "检查点不存在"); + } + + QmsPqcInspectionRule rule = ruleService.getById(point.getPqcRuleId()); + + // 已审核规则:fork 出新版本,在新版本上删除对应检查点,原记录不动 + if (Objects.nonNull(rule) && rule.getAuditStatus() != null && rule.getAuditStatus() == 1) { + if (checkDraftExists(rule.getModelNo(), null)) { + throw new NflgException(STATE.BusinessError, "该机型已存在未审核的版本,请先完成审核后再编辑"); + } + Long newRuleId = forkNewVersion(rule); + // 在新版本中找到对应的检查点(通过 stepDicItemId + inspectionPointCode 匹配)并删除 + QmsPqcInspectionPoint newPoint = pointService.lambdaQuery() + .eq(QmsPqcInspectionPoint::getPqcRuleId, newRuleId) + .eq(QmsPqcInspectionPoint::getStepDicItemId, point.getStepDicItemId()) + .eq(QmsPqcInspectionPoint::getInspectionPointCode, point.getInspectionPointCode()) + .one(); + if (Objects.nonNull(newPoint)) { + itemsService.lambdaUpdate() + .eq(QmsPqcInspectionPointItems::getInspectionCodeId, newPoint.getId()) + .remove(); + pointService.removeById(newPoint.getId()); + } + return newRuleId; + } + + // 未审核规则:直接删除 + itemsService.lambdaUpdate() + .eq(QmsPqcInspectionPointItems::getInspectionCodeId, pointId) + .remove(); + pointService.removeById(pointId); + return rule != null ? rule.getId() : null; + } + // ========================= 私有方法 ========================= + /** + * 基于已审核规则 fork 出新版本规则(新记录,不修改原记录) + * 新规则:新 pqcRuleCode、ruleVersion+1、auditStatus=0 + * @return 新规则 ID + */ + private Long forkNewVersion(QmsPqcInspectionRule existRule) { + String operator = UserUtil.getUserName(); + Long operatorId = UserUtil.getUserId(); + LocalDateTime now = LocalDateTime.now(); + + QmsPqcInspectionRule newRule = new QmsPqcInspectionRule(); + newRule.setModelNo(existRule.getModelNo()); + newRule.setPqcRuleCode(serialNumberControllerService.generateSerialNumber(40)); + newRule.setRuleVersion(existRule.getRuleVersion() == null ? 1 : existRule.getRuleVersion() + 1); + newRule.setIsDisabled(existRule.getIsDisabled()); + newRule.setAuditStatus(0); + newRule.setCreateBy(operatorId); + newRule.setCreateName(operator); + newRule.setCreateTime(now); + ruleService.save(newRule); + + // 复制所有检查点和检查项 + List points = pointService.lambdaQuery() + .eq(QmsPqcInspectionPoint::getPqcRuleId, existRule.getId()) + .list(); + for (QmsPqcInspectionPoint point : points) { + QmsPqcInspectionPoint newPoint = new QmsPqcInspectionPoint(); + BeanUtil.copyProperties(point, newPoint); + newPoint.setId(null); + newPoint.setPqcRuleId(newRule.getId()); + newPoint.setCreateBy(operatorId); + newPoint.setCreateName(operator); + newPoint.setCreateTime(now); + pointService.save(newPoint); + + List items = itemsService.lambdaQuery() + .eq(QmsPqcInspectionPointItems::getInspectionCodeId, point.getId()) + .list(); + for (QmsPqcInspectionPointItems item : items) { + QmsPqcInspectionPointItems newItem = new QmsPqcInspectionPointItems(); + BeanUtil.copyProperties(item, newItem); + newItem.setId(null); + newItem.setInspectionCodeId(newPoint.getId()); + newItem.setCreateBy(operatorId); + newItem.setCreateName(operator); + newItem.setCreateTime(now); + itemsService.save(newItem); + } + } + + return newRule.getId(); + } + + /** + * 检查同机型是否已存在未审核(auditStatus=0)的规则 + */ + private boolean checkDraftExists(String modelNo, Long excludeRuleId) { + if (StrUtil.isBlank(modelNo)) { + return false; + } + return ruleService.lambdaQuery() + .eq(QmsPqcInspectionRule::getModelNo, modelNo) + .eq(QmsPqcInspectionRule::getAuditStatus, 0) + .ne(Objects.nonNull(excludeRuleId), QmsPqcInspectionRule::getId, excludeRuleId) + .exists(); + } + /** * 保存检查点和检查项 */ - @Override - public List listPointsByModelNo(String modelNo) { - QmsPqcInspectionRule latestRule = ruleService.lambdaQuery() - .eq(QmsPqcInspectionRule::getModelNo, modelNo) - .eq(QmsPqcInspectionRule::getIsDisabled, true) - .orderByDesc(QmsPqcInspectionRule::getRuleVersion) - .last("LIMIT 1") - .one(); - if (latestRule == null) { - return Collections.emptyList(); - } - return pointService.lambdaQuery() - .eq(QmsPqcInspectionPoint::getPqcRuleId, latestRule.getId()) - .list() - .stream() - .map(point -> new PqcInspectionPointListVO() - .setId(point.getId()) - .setInspectionPointCode(point.getInspectionPointCode()) - .setInspectionPointName(point.getInspectionPointName())) - .collect(Collectors.toList()); - } - - @Override - public QmsPqcInspectionPointItemsGroupedVO listItemsByInspectionPointIdGrouped(Long inspectionPointId) { - // 查询所有检测项 - List allItems = itemsService.lambdaQuery() - .eq(QmsPqcInspectionPointItems::getInspectionCodeId, inspectionPointId) - .orderByAsc(QmsPqcInspectionPointItems::getSort) - .list(); - - // 初始化三个分类列表 - List materialItems = new ArrayList<>(); // 第1类:关键物料拍照 - List selfReviewItems = new ArrayList<>(); // 第2类:工序检查-自检复核 - List qcItems = new ArrayList<>(); // 第3类:工序检查-QC检测 - - // 分类逻辑 - for (QmsPqcInspectionPointItems item : allItems) { - QmsPqcInspectionPointItemListVO vo = new QmsPqcInspectionPointItemListVO() - .setId(item.getId()) - .setInspectionCodeId(item.getInspectionCodeId()) - .setSort(item.getSort()) - .setInspectionContent(item.getInspectionContent()) - .setInspectionType(item.getInspectionType()) - .setInspectionMethods(item.getInspectionMethods()) - .setInspectionImgUrl(item.getInspectionImgUrl()) - .setInspectionLevel(item.getInspectionLevel()); - - Integer type = item.getInspectionType(); - Integer level = item.getInspectionLevel(); - - // 第1类:关键物料拍照类 - // 包含:inspectionType = 0(所有)+ inspectionType = 2(所有) - if (type == 0 || type == 2) { - materialItems.add(vo); - } - - // 第2类和第3类:工序检查(按类型和星级分) - // 包含:inspectionType = 1 或 2 - if (type == 1 || type == 2) { - if (level == 1 || level == 2) { - // 星级1或2 → 自检复核类 - selfReviewItems.add(vo); - } else if (level == 3) { - // 星级3 → QC检测类 - qcItems.add(vo); - } - } - } - - // 组装返回结果 - QmsPqcInspectionPointItemsGroupedVO groupedVO = new QmsPqcInspectionPointItemsGroupedVO(); - groupedVO.setMaterialItems(materialItems); - groupedVO.setSelfReviewItems(selfReviewItems); - groupedVO.setQcItems(qcItems); - - return groupedVO; - } - private void savePointsAndItems(Long ruleId, List points, String operator, Long operatorId, LocalDateTime now) { if (points == null || points.isEmpty()) { @@ -448,49 +491,7 @@ public class QmsPqcInspectionRuleControllerServiceImpl implements QmsPqcInspecti item.setInspectionCodeId(point.getId()); item.setSort(sort++); item.setInspectionContent(itemQO.getInspectionContent()); - item.setInspectionType(itemQO.getInspectionType()); - item.setInspectionMethods(itemQO.getInspectionMethods()); - item.setInspectionImgUrl(itemQO.getInspectionImgUrl()); - item.setInspectionLevel(itemQO.getInspectionLevel()); - item.setCreateBy(operatorId); - item.setCreateName(operator); - item.setCreateTime(now); - itemsService.save(item); - } - } - } - } - - /** - * 保存检查点和检查项(从EditQO) - */ - private void savePointsAndItemsFromEditQO(Long ruleId, List points, - String operator, Long operatorId, LocalDateTime now) { - if (points == null || points.isEmpty()) { - return; - } - - for (PqcInspectionRuleEditQO.PqcInspectionPointEditQO pointQO : points) { - QmsPqcInspectionPoint point = new QmsPqcInspectionPoint(); - point.setPqcRuleId(ruleId); - point.setStepName(pointQO.getStepName()); - point.setStepDicItemId(pointQO.getStepDicItemId()); - point.setInspectionPointCode(pointQO.getInspectionPointCode()); - point.setInspectionPointName(pointQO.getInspectionPointName()); - point.setCreateBy(operatorId); - point.setCreateName(operator); - point.setCreateTime(now); - pointService.save(point); - - // 保存检查项 - if (pointQO.getInspectionItems() != null && !pointQO.getInspectionItems().isEmpty()) { - int sort = 1; - for (PqcInspectionRuleEditQO.PqcInspectionPointItemEditQO itemQO : pointQO.getInspectionItems()) { - QmsPqcInspectionPointItems item = new QmsPqcInspectionPointItems(); - item.setInspectionCodeId(point.getId()); - item.setSort(itemQO.getSort() != null ? itemQO.getSort() : sort++); - item.setInspectionContent(itemQO.getInspectionContent()); - item.setInspectionType(itemQO.getInspectionType()); + item.setInspectionType(listToBitmask(itemQO.getInspectionType())); item.setInspectionMethods(itemQO.getInspectionMethods()); item.setInspectionImgUrl(itemQO.getInspectionImgUrl()); item.setInspectionLevel(itemQO.getInspectionLevel()); @@ -547,7 +548,7 @@ public class QmsPqcInspectionRuleControllerServiceImpl implements QmsPqcInspecti item.setInspectionCodeId(point.getId()); item.setSort(itemQO.getSort() != null ? itemQO.getSort() : sort++); item.setInspectionContent(itemQO.getInspectionContent()); - item.setInspectionType(itemQO.getInspectionType()); + item.setInspectionType(listToBitmask(itemQO.getInspectionType())); item.setInspectionMethods(itemQO.getInspectionMethods()); item.setInspectionImgUrl(itemQO.getInspectionImgUrl()); item.setInspectionLevel(itemQO.getInspectionLevel()); @@ -560,61 +561,533 @@ public class QmsPqcInspectionRuleControllerServiceImpl implements QmsPqcInspecti } } - /** - * 比对数据是否发生变化 - */ - private boolean checkDataChanged(Long ruleId, PqcInspectionRuleEditQO qo) { - // 获取现有的检查点 - List existPoints = pointService.lambdaQuery() - .eq(QmsPqcInspectionPoint::getPqcRuleId, ruleId) - .list(); + // ========================= inspectionType 位标志转换 ========================= - // 如果检查点数量不同,说明有变化 - if (qo.getInspectionPoints() == null || qo.getInspectionPoints().size() != existPoints.size()) { - return true; + /** + * 将List转换为位标志Integer + * 0=工序检查 → bit 0 → 1 + * 1=关键物料拍照 → bit 1 → 2 + * 2=关键物料采集 → bit 2 → 4 + */ + private Integer listToBitmask(List types) { + if (types == null || types.isEmpty()) { + return 0; + } + int result = 0; + for (Integer type : types) { + result |= (1 << type); + } + return result; + } + + /** + * 将位标志Integer转换为List + */ + private List bitmaskToList(Integer bitmask) { + if (bitmask == null || bitmask == 0) { + return Collections.emptyList(); + } + List result = new ArrayList<>(); + if ((bitmask & 1) != 0) result.add(0); + if ((bitmask & 2) != 0) result.add(1); + if ((bitmask & 4) != 0) result.add(2); + return result; + } + + // ========================= 导入 ========================= + + /** + * 判断导入的行是否为空 + */ + private boolean isRowEmpty(PqcInspectionRuleImportDTO dto) { + return Objects.isNull(dto) || + (StrUtil.isBlank(dto.getModelNo()) && + StrUtil.isBlank(dto.getStepName()) && + StrUtil.isBlank(dto.getInspectionPointCode()) && + StrUtil.isBlank(dto.getInspectionPointName()) && + StrUtil.isBlank(dto.getInspectionContent()) && + StrUtil.isBlank(dto.getInspectionTypeText()) && + StrUtil.isBlank(dto.getInspectionMethodsText()) && + StrUtil.isBlank(dto.getInspectionLevelText()) && + StrUtil.isBlank(dto.getInspectionImage()) && + Objects.isNull(dto.getSort())); + } + + @Override + @Transactional + public void importFromExcel(MultipartFile file, String importMode) throws Exception { + // 校验导入方式 + if (!"overwrite".equals(importMode) && !"append".equals(importMode)) { + throw new NflgException(STATE.BusinessError, "导入方式只能是overwrite(覆盖)或append(追加)"); } - // 比对每个检查点 - Map existPointMap = existPoints.stream() - .collect(Collectors.toMap(QmsPqcInspectionPoint::getId, p -> p)); + // 读取Excel全量数据(不预先过滤空行,保留原始行号对应关系用于图片匹配) + byte[] fileBytes = file.getBytes(); + List allData = EecExcelUtil.readTo(new ByteArrayInputStream(fileBytes), PqcInspectionRuleImportDTO.class); - for (PqcInspectionRuleEditQO.PqcInspectionPointEditQO pointQO : qo.getInspectionPoints()) { - if (pointQO.getId() == null || !existPointMap.containsKey(pointQO.getId())) { - return true; // 新增的检查点或ID不存在 + // 读取Excel中的图片,按行号建立索引 + // firstRow 是1-based,第1行为表头,数据从第2行开始,故 dataIndex = firstRow - 2 + List pictures = new ExcelReader(new ByteArrayInputStream(fileBytes)).listPictures(); + Map pictureByRowIndex = new LinkedHashMap<>(); + for (Drawings.Picture pic : pictures) { + if (pic.getDimension() != null) { + int dataIndex = pic.getDimension().getFirstRow() - 2; + pictureByRowIndex.put(dataIndex, pic); } + } - QmsPqcInspectionPoint existPoint = existPointMap.get(pointQO.getId()); - // 比对关键字段 - if (!Objects.equals(existPoint.getStepName(), pointQO.getStepName()) || - !Objects.equals(existPoint.getInspectionPointCode(), pointQO.getInspectionPointCode()) || - !Objects.equals(existPoint.getInspectionPointName(), pointQO.getInspectionPointName())) { - return true; - } - - // 比对检查项 - List existItems = itemsService.lambdaQuery() - .eq(QmsPqcInspectionPointItems::getInspectionCodeId, pointQO.getId()) - .list(); - - if (pointQO.getInspectionItems() == null || - pointQO.getInspectionItems().size() != existItems.size()) { - return true; - } - - // 简单比对检查项数量和内容 - for (int i = 0; i < existItems.size(); i++) { - QmsPqcInspectionPointItems existItem = existItems.get(i); - PqcInspectionRuleEditQO.PqcInspectionPointItemEditQO itemQO = pointQO.getInspectionItems().get(i); - - if (!Objects.equals(existItem.getInspectionContent(), itemQO.getInspectionContent()) || - !Objects.equals(existItem.getInspectionType(), itemQO.getInspectionType()) || - !Objects.equals(existItem.getInspectionMethods(), itemQO.getInspectionMethods()) || - !Objects.equals(existItem.getInspectionLevel(), itemQO.getInspectionLevel())) { - return true; + // 在过滤前按原始行索引处理图片,保证行号准确对应 + for (int i = 0; i < allData.size(); i++) { + Drawings.Picture pic = pictureByRowIndex.get(i); + if (pic != null && pic.getLocalPath() != null) { + try (FileInputStream fis = new FileInputStream(pic.getLocalPath().toFile())) { + String imageUrl = fileUploadService.upload( + "image/" + DateTimeUtil.format(LocalDate.now(), "yyyyMMdd") + "/" + IdUtil.fastUUID() + ".png", + fis, "image/png"); + FileUploadRecord record = new FileUploadRecord(); + record.setId(IdUtil.getSnowflakeNextId()); + record.setFileName(pic.getLocalPath().getFileName().toString()); + record.setFileType("image/png"); + record.setUrl(imageUrl); + record.setSource((short) 0); + record.setSourceId(0L); + record.setFrom("PQC检测规则导入"); + record.setCreateBy(UserUtil.getUserName()); + record.setCreateTime(LocalDateTime.now()); + fileUploadRecordService.save(record); + allData.get(i).setInspectionImage(imageUrl); } } } - return false; + // 过滤空行 + List data = allData.stream() + .filter(dto -> !isRowEmpty(dto)) + .collect(Collectors.toList()); + + if (CollectionUtil.isEmpty(data)) { + throw new NflgException(STATE.BusinessError, "导入文件无数据"); + } + + // 过滤掉必填字段为空的数据 + List validData = data.stream() + .filter(dto -> StrUtil.isNotBlank(dto.getModelNo()) + && StrUtil.isNotBlank(dto.getStepName()) + && StrUtil.isNotBlank(dto.getInspectionPointCode()) + && StrUtil.isNotBlank(dto.getInspectionContent())) + .collect(Collectors.toList()); + if (CollectionUtil.isEmpty(validData)) { + throw new NflgException(STATE.BusinessError, "导入数据中必填字段为空,请检查文件"); + } + + // 预加载步装字典,用于stepName → stepDicItemId匹配 + List stepDicItems = dictionaryItemService.getListByDictionaryCode("step"); + + // 按机型编号分组 + Map> modelNoGroups = validData.stream() + .collect(Collectors.groupingBy(PqcInspectionRuleImportDTO::getModelNo, LinkedHashMap::new, Collectors.toList())); + + String operator = UserUtil.getUserName(); + Long operatorId = UserUtil.getUserId(); + LocalDateTime now = LocalDateTime.now(); + + for (Map.Entry> entry : modelNoGroups.entrySet()) { + String modelNo = entry.getKey(); + List modelRows = entry.getValue(); + + // 查找该机型编号的最高版本规则 + QmsPqcInspectionRule highestRule = ruleService.lambdaQuery() + .eq(QmsPqcInspectionRule::getModelNo, modelNo) + .orderByDesc(QmsPqcInspectionRule::getRuleVersion) + .last("LIMIT 1") + .one(); + + Long targetRuleId; + boolean isNewRule = false; + + if (Objects.isNull(highestRule)) { + // 该机型无规则,新建 + QmsPqcInspectionRule newRule = new QmsPqcInspectionRule(); + newRule.setModelNo(modelNo); + newRule.setPqcRuleCode(serialNumberControllerService.generateSerialNumber(40)); + newRule.setRuleVersion(1); + newRule.setIsDisabled(true); + newRule.setAuditStatus(0); + newRule.setCreateBy(operatorId); + newRule.setCreateName(operator); + newRule.setCreateTime(now); + ruleService.save(newRule); + targetRuleId = newRule.getId(); + isNewRule = true; + } else if (highestRule.getAuditStatus() != null && highestRule.getAuditStatus() == 1) { + // 最高版本已审核,fork新版本 + if (checkDraftExists(modelNo, null)) { + throw new NflgException(STATE.BusinessError, "机型" + modelNo + "已存在未审核的版本,请先完成审核后再导入"); + } + targetRuleId = forkNewVersion(highestRule); + } else { + // 最高版本未审核,直接在其上修改 + targetRuleId = highestRule.getId(); + } + + // 按 (步装名称 + 检查点编号) 分组,构建检查点和检查项的层级结构 + // 使用 "stepName|inspectionPointCode" 作为分组key + Map> pointGroups = modelRows.stream() + .collect(Collectors.groupingBy( + dto -> dto.getStepName() + "|" + dto.getInspectionPointCode(), + LinkedHashMap::new, Collectors.toList())); + + if ("overwrite".equals(importMode)) { + // 覆盖导入:删除目标规则的所有检查点和检查项,用导入数据替换 + List oldPoints = pointService.lambdaQuery() + .eq(QmsPqcInspectionPoint::getPqcRuleId, targetRuleId) + .list(); + for (QmsPqcInspectionPoint oldPoint : oldPoints) { + itemsService.lambdaUpdate() + .eq(QmsPqcInspectionPointItems::getInspectionCodeId, oldPoint.getId()) + .remove(); + } + pointService.lambdaUpdate() + .eq(QmsPqcInspectionPoint::getPqcRuleId, targetRuleId) + .remove(); + + // 保存导入数据 + saveImportPointsAndItems(targetRuleId, pointGroups, stepDicItems, operator, operatorId, now); + } else { + // 追加导入:保留已有数据,追加导入数据(去重) + appendImportPointsAndItems(targetRuleId, pointGroups, stepDicItems, operator, operatorId, now); + } + } + } + + /** + * 保存导入的检查点和检查项(覆盖模式) + */ + private void saveImportPointsAndItems(Long ruleId, + Map> pointGroups, + List stepDicItems, + String operator, Long operatorId, LocalDateTime now) { + for (Map.Entry> pointEntry : pointGroups.entrySet()) { + String[] keys = pointEntry.getKey().split("\\|"); + String stepName = keys[0]; + String inspectionPointCode = keys[1]; + + // 匹配步装字典获取stepDicItemId + Long stepDicItemId = matchStepDicItemId(stepName, stepDicItems); + + PqcInspectionRuleImportDTO firstRow = pointEntry.getValue().get(0); + QmsPqcInspectionPoint point = new QmsPqcInspectionPoint(); + point.setPqcRuleId(ruleId); + point.setStepName(stepName); + point.setStepDicItemId(stepDicItemId); + point.setInspectionPointCode(inspectionPointCode); + point.setInspectionPointName(StrUtil.isNotBlank(firstRow.getInspectionPointName()) ? firstRow.getInspectionPointName() : ""); + point.setCreateBy(operatorId); + point.setCreateName(operator); + point.setCreateTime(now); + pointService.save(point); + + // 保存检查项 + int sort = 1; + for (PqcInspectionRuleImportDTO dto : pointEntry.getValue()) { + QmsPqcInspectionPointItems item = new QmsPqcInspectionPointItems(); + item.setInspectionCodeId(point.getId()); + item.setSort(dto.getSort() != null ? dto.getSort() : sort++); + item.setInspectionContent(dto.getInspectionContent()); + item.setInspectionType(parseInspectionTypeText(dto.getInspectionTypeText())); + item.setInspectionMethods(parseInspectionMethodsText(dto.getInspectionMethodsText())); + item.setInspectionImgUrl(dto.getInspectionImage()); + item.setInspectionLevel(parseInspectionLevelText(dto.getInspectionLevelText())); + item.setCreateBy(operatorId); + item.setCreateName(operator); + item.setCreateTime(now); + itemsService.save(item); + } + } + } + + /** + * 追加导入检查点和检查项(追加模式,去重) + */ + private void appendImportPointsAndItems(Long ruleId, + Map> pointGroups, + List stepDicItems, + String operator, Long operatorId, LocalDateTime now) { + // 加载已有检查点 + List existPoints = pointService.lambdaQuery() + .eq(QmsPqcInspectionPoint::getPqcRuleId, ruleId) + .list(); + + // 构建已有检查点Map: stepDicItemId|inspectionPointCode → Point + Map existPointMap = existPoints.stream() + .collect(Collectors.toMap( + p -> (p.getStepDicItemId() != null ? p.getStepDicItemId() : 0L) + "|" + p.getInspectionPointCode(), + p -> p, (a, b) -> a)); + + for (Map.Entry> pointEntry : pointGroups.entrySet()) { + String[] keys = pointEntry.getKey().split("\\|"); + String stepName = keys[0]; + String inspectionPointCode = keys[1]; + + Long stepDicItemId = matchStepDicItemId(stepName, stepDicItems); + String pointKey = (stepDicItemId != null ? stepDicItemId : 0L) + "|" + inspectionPointCode; + + PqcInspectionRuleImportDTO firstRow = pointEntry.getValue().get(0); + + QmsPqcInspectionPoint targetPoint; + if (existPointMap.containsKey(pointKey)) { + // 已存在同key检查点,在其上追加检查项 + targetPoint = existPointMap.get(pointKey); + } else { + // 不存在,新建检查点 + targetPoint = new QmsPqcInspectionPoint(); + targetPoint.setPqcRuleId(ruleId); + targetPoint.setStepName(stepName); + targetPoint.setStepDicItemId(stepDicItemId); + targetPoint.setInspectionPointCode(inspectionPointCode); + targetPoint.setInspectionPointName(StrUtil.isNotBlank(firstRow.getInspectionPointName()) ? firstRow.getInspectionPointName() : ""); + targetPoint.setCreateBy(operatorId); + targetPoint.setCreateName(operator); + targetPoint.setCreateTime(now); + pointService.save(targetPoint); + existPointMap.put(pointKey, targetPoint); + } + + // 加载该检查点的已有检查项,用于去重 + List existItems = itemsService.lambdaQuery() + .eq(QmsPqcInspectionPointItems::getInspectionCodeId, targetPoint.getId()) + .list(); + // 去重key: inspectionContent(相同检查点下,相同内容的检查项视为重复) + Map existItemMap = existItems.stream() + .collect(Collectors.toMap( + QmsPqcInspectionPointItems::getInspectionContent, + i -> i, (a, b) -> a)); + + // 查询当前最大sort值 + int maxSort = existItems.stream() + .map(QmsPqcInspectionPointItems::getSort) + .max(Comparator.naturalOrder()) + .orElse(0); + + for (PqcInspectionRuleImportDTO dto : pointEntry.getValue()) { + if (existItemMap.containsKey(dto.getInspectionContent())) { + // 已存在相同内容的检查项,更新 + QmsPqcInspectionPointItems existItem = existItemMap.get(dto.getInspectionContent()); + existItem.setInspectionType(parseInspectionTypeText(dto.getInspectionTypeText())); + existItem.setInspectionMethods(parseInspectionMethodsText(dto.getInspectionMethodsText())); + if (StrUtil.isNotBlank(dto.getInspectionImage())) { + existItem.setInspectionImgUrl(dto.getInspectionImage()); + } + if (dto.getInspectionLevelText() != null) { + existItem.setInspectionLevel(parseInspectionLevelText(dto.getInspectionLevelText())); + } + if (dto.getSort() != null) { + existItem.setSort(dto.getSort()); + } + existItem.setUpdateBy(operatorId); + existItem.setUpdateName(operator); + existItem.setUpdateTime(now); + itemsService.updateById(existItem); + } else { + // 不存在,新增检查项 + maxSort++; + QmsPqcInspectionPointItems newItem = new QmsPqcInspectionPointItems(); + newItem.setInspectionCodeId(targetPoint.getId()); + newItem.setSort(dto.getSort() != null ? dto.getSort() : maxSort); + newItem.setInspectionContent(dto.getInspectionContent()); + newItem.setInspectionType(parseInspectionTypeText(dto.getInspectionTypeText())); + newItem.setInspectionMethods(parseInspectionMethodsText(dto.getInspectionMethodsText())); + newItem.setInspectionImgUrl(dto.getInspectionImage()); + newItem.setInspectionLevel(parseInspectionLevelText(dto.getInspectionLevelText())); + newItem.setCreateBy(operatorId); + newItem.setCreateName(operator); + newItem.setCreateTime(now); + itemsService.save(newItem); + } + } + } + } + + /** + * 匹配步装字典:通过stepName查找stepDicItemId + */ + private Long matchStepDicItemId(String stepName, List stepDicItems) { + return stepDicItems.stream() + .filter(item -> item.getName().equals(stepName)) + .map(DictionaryItem::getId) + .findFirst() + .orElseThrow(() -> new NflgException(STATE.BusinessError, "步装名称\"" + stepName + "\"在字典中未找到,请检查")); + } + + /** + * 解析检查项类别文本 → 位标志Integer + * 工序检查→1, 关键物料采集拍照→2, 全部→3 + */ + private Integer parseInspectionTypeText(String text) { + if (StrUtil.isBlank(text)) return 0; + switch (text.trim()) { + case "工序检查": return 1; + case "关键物料采集拍照": return 2; + case "全部": return 3; + default: + // 尝试数字解析 + try { return Integer.parseInt(text.trim()); } catch (NumberFormatException e) { + throw new NflgException(STATE.BusinessError, "检查项类别\"" + text + "\"无法识别,请输入:工序检查/关键物料采集拍照/全部"); + } + } + } + + /** + * 位标志Integer → 检查项类别文本 + */ + private String inspectionTypeToText(Integer bitmask) { + if (bitmask == null || bitmask == 0) return ""; + if (bitmask == 1) return "工序检查"; + if (bitmask == 2) return "关键物料采集拍照"; + if (bitmask == 3) return "全部"; + // 其他位标志组合(如4=关键物料采集)也做处理 + if (bitmask == 4) return "关键物料采集"; + List types = bitmaskToList(bitmask); + return types.stream().map(t -> { + switch (t) { case 0: return "工序检查"; case 1: return "关键物料采集拍照"; case 2: return "关键物料采集"; default: return ""; } + }).collect(Collectors.joining("+")); + } + + /** + * 解析检测方法文本 → Integer + * 目视→0, 量具→1 + */ + private Integer parseInspectionMethodsText(String text) { + if (StrUtil.isBlank(text)) return null; + switch (text.trim()) { + case "目视": return 0; + case "量具": return 1; + default: + try { return Integer.parseInt(text.trim()); } catch (NumberFormatException e) { + throw new NflgException(STATE.BusinessError, "检测方法\"" + text + "\"无法识别,请输入:目视/量具"); + } + } + } + + /** + * Integer → 检测方法文本 + */ + private String inspectionMethodsToText(Integer methods) { + if (methods == null) return ""; + switch (methods) { case 0: return "目视"; case 1: return "量具"; default: return String.valueOf(methods); } + } + + /** + * 解析等级文本 → Integer(1-3) + */ + private Integer parseInspectionLevelText(String text) { + if (StrUtil.isBlank(text)) return null; + try { return Integer.parseInt(text.trim()); } catch (NumberFormatException e) { + // 支持 "一级"/"二级"/"三级" 格式 + switch (text.trim()) { + case "一级": return 1; case "二级": return 2; case "三级": return 3; + default: throw new NflgException(STATE.BusinessError, "等级\"" + text + "\"无法识别,请输入1/2/3"); + } + } + } + + // ========================= 导出 ========================= + + @Override + public void export(HttpServletResponse response, PqcInspectionRuleExportQO qo) throws IOException { + // 查询要导出的规则 + List rules; + if (CollectionUtil.isNotEmpty(qo.getIds())) { + // 有选中ID,导出指定规则 + rules = ruleService.listByIds(qo.getIds()); + } else { + // 无选中ID,按查询条件导出全部 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.like(StrUtil.isNotBlank(qo.getModelNo()), QmsPqcInspectionRule::getModelNo, qo.getModelNo()) + .like(StrUtil.isNotBlank(qo.getPqcRuleCode()), QmsPqcInspectionRule::getPqcRuleCode, qo.getPqcRuleCode()) + .eq(Objects.nonNull(qo.getIsDisabled()), QmsPqcInspectionRule::getIsDisabled, qo.getIsDisabled()) + .eq(Objects.nonNull(qo.getAuditStatus()), QmsPqcInspectionRule::getAuditStatus, qo.getAuditStatus()) + .ge(StrUtil.isNotBlank(qo.getCreateTimeStart()), QmsPqcInspectionRule::getCreateTime, qo.getCreateTimeStart()) + .le(StrUtil.isNotBlank(qo.getCreateTimeEnd()), QmsPqcInspectionRule::getCreateTime, qo.getCreateTimeEnd()) + .orderByDesc(QmsPqcInspectionRule::getCreateTime); + rules = ruleService.list(queryWrapper); + } + + if (CollectionUtil.isEmpty(rules)) { + throw new NflgException(STATE.BusinessError, "无数据可导出"); + } + + // 将三层结构展平为导出DTO列表 + List exportData = new ArrayList<>(); + for (QmsPqcInspectionRule rule : rules) { + List points = pointService.lambdaQuery() + .eq(QmsPqcInspectionPoint::getPqcRuleId, rule.getId()) + .list(); + for (QmsPqcInspectionPoint point : points) { + List items = itemsService.lambdaQuery() + .eq(QmsPqcInspectionPointItems::getInspectionCodeId, point.getId()) + .orderByAsc(QmsPqcInspectionPointItems::getSort) + .list(); + for (QmsPqcInspectionPointItems item : items) { + PqcInspectionRuleExportDTO dto = new PqcInspectionRuleExportDTO(); + dto.setModelNo(rule.getModelNo()); + dto.setStepName(point.getStepName()); + dto.setInspectionPointCode(point.getInspectionPointCode()); + dto.setInspectionPointName(point.getInspectionPointName()); + dto.setInspectionContent(item.getInspectionContent()); + dto.setInspectionTypeText(inspectionTypeToText(item.getInspectionType())); + dto.setInspectionMethodsText(inspectionMethodsToText(item.getInspectionMethods())); + dto.setInspectionLevelText(item.getInspectionLevel() != null ? String.valueOf(item.getInspectionLevel()) : ""); + dto.setSort(item.getSort()); + + // 实例图:从文件ID转为URL + if (StrUtil.isNotBlank(item.getInspectionImgUrl())) { + try { + Long fileId = Long.valueOf(item.getInspectionImgUrl().trim()); + FileUploadRecord fileRecord = fileUploadRecordService.getById(fileId); + if (fileRecord != null && StrUtil.isNotBlank(fileRecord.getUrl())) { + dto.setInspectionImage(fileRecord.getUrl()); + } else { + dto.setInspectionImage(""); + } + } catch (NumberFormatException e) { + dto.setInspectionImage(item.getInspectionImgUrl()); + } + } else { + dto.setInspectionImage(""); + } + + dto.setPqcRuleCode(rule.getPqcRuleCode()); + dto.setRuleVersion(rule.getRuleVersion()); + dto.setAuditStatusText(rule.getAuditStatus() != null ? + (rule.getAuditStatus() == 0 ? "未审核" : rule.getAuditStatus() == 1 ? "已审核" : rule.getAuditStatus() == 2 ? "已驳回" : "") : ""); + + exportData.add(dto); + } + } + } + + EecExcelUtil.setResponseExcelHeader(response, "PQC检测规则"); + new Workbook() + .addSheet(new ListSheet<>("PQC检测规则", exportData).setRowHeight(60)) + .writeTo(response.getOutputStream()); + } + + // ========================= 下载导入模板 ========================= + + @Override + public void downloadTemplate(HttpServletResponse response) throws IOException { + PqcInspectionRuleImportDTO example = new PqcInspectionRuleImportDTO() + .setModelNo("示例机型编号") + .setStepName("示例步装名称") + .setInspectionPointCode("示例检查点编号") + .setInspectionPointName("示例检查点名称") + .setInspectionContent("示例检查项内容") + .setInspectionTypeText("工序检查") + .setInspectionMethodsText("目视") + .setInspectionLevelText("1") + .setInspectionImage("123456") + .setSort(1); + EecExcelUtil.export("PQC检测规则导入模板", "PQC检测规则导入模板", List.of(example), response); } } diff --git a/nflg-wms-common/src/main/java/com/nflg/wms/common/pojo/dto/PqcInspectionRuleExportDTO.java b/nflg-wms-common/src/main/java/com/nflg/wms/common/pojo/dto/PqcInspectionRuleExportDTO.java new file mode 100644 index 00000000..012cae69 --- /dev/null +++ b/nflg-wms-common/src/main/java/com/nflg/wms/common/pojo/dto/PqcInspectionRuleExportDTO.java @@ -0,0 +1,94 @@ +package com.nflg.wms.common.pojo.dto; + +import lombok.Data; +import lombok.experimental.Accessors; +import org.ttzero.excel.annotation.ExcelColumn; +import org.ttzero.excel.annotation.MediaColumn; + +/** + * PQC检测规则 导出DTO + * 将规则、检查点、检查项三层结构展平为单行导出 + */ +@Data +@Accessors(chain = true) +public class PqcInspectionRuleExportDTO { + + /** + * 机型编号 + */ + @ExcelColumn("机型编号") + private String modelNo; + + /** + * 步装名称 + */ + @ExcelColumn("步装名称") + private String stepName; + + /** + * 检查点编号 + */ + @ExcelColumn("检查点编号") + private String inspectionPointCode; + + /** + * 检查点名称 + */ + @ExcelColumn("检查点名称") + private String inspectionPointName; + + /** + * 检查项内容 + */ + @ExcelColumn("检查项内容") + private String inspectionContent; + + /** + * 检查项类别(工序检查/关键物料采集拍照/全部) + */ + @ExcelColumn("检查项类别") + private String inspectionTypeText; + + /** + * 检测方法(目视/量具) + */ + @ExcelColumn("检测方法") + private String inspectionMethodsText; + + /** + * 等级(1-3) + */ + @ExcelColumn("等级") + private String inspectionLevelText; + + /** + * 实例图 + */ + @ExcelColumn("实例图") + @MediaColumn + private String inspectionImage; + + /** + * 排序 + */ + @ExcelColumn("排序") + private Integer sort; + + /** + * 规则编号 + */ + @ExcelColumn("规则编号") + private String pqcRuleCode; + + /** + * 版本号 + */ + @ExcelColumn("版本号") + private Integer ruleVersion; + + /** + * 审核状态 + */ + @ExcelColumn("审核状态") + private String auditStatusText; +} \ No newline at end of file diff --git a/nflg-wms-common/src/main/java/com/nflg/wms/common/pojo/dto/PqcInspectionRuleImportDTO.java b/nflg-wms-common/src/main/java/com/nflg/wms/common/pojo/dto/PqcInspectionRuleImportDTO.java new file mode 100644 index 00000000..db9eefa3 --- /dev/null +++ b/nflg-wms-common/src/main/java/com/nflg/wms/common/pojo/dto/PqcInspectionRuleImportDTO.java @@ -0,0 +1,80 @@ +package com.nflg.wms.common.pojo.dto; + +import lombok.Data; +import lombok.experimental.Accessors; +import org.ttzero.excel.annotation.ExcelColumn; + +/** + * PQC检测规则 导入DTO + * Excel模板列:机型编号*, 步装名称*, 检查点编号*, 检查点名称, 检查项内容*, 检查项类别*, 检测方法*, 等级*, 实例图, 排序 + */ +@Data +@Accessors(chain = true) +public class PqcInspectionRuleImportDTO { + + /** + * 机型编号(必填) + */ + @ExcelColumn("机型编号*") + private String modelNo; + + /** + * 步装名称(必填) + */ + @ExcelColumn("步装名称*") + private String stepName; + + /** + * 检查点编号(必填) + */ + @ExcelColumn("检查点编号*") + private String inspectionPointCode; + + /** + * 检查点名称(可选) + */ + @ExcelColumn("检查点名称") + private String inspectionPointName; + + /** + * 检查项内容(必填) + */ + @ExcelColumn("检查项内容*") + private String inspectionContent; + + /** + * 检查项类别(必填):工序检查/关键物料采集拍照/全部 + */ + @ExcelColumn("检查项类别*") + private String inspectionTypeText; + + /** + * 检测方法(必填):目视/量具 + */ + @ExcelColumn("检测方法*") + private String inspectionMethodsText; + + /** + * 等级(必填):1-3 + */ + @ExcelColumn("等级*") + private String inspectionLevelText; + + /** + * 实例图(可选,支持DISPIMG公式) + */ + @ExcelColumn("实例图") + private String inspectionImage; + + /** + * 排序(可选) + */ + @ExcelColumn("排序") + private Integer sort; + + /** + * 错误信息(导入时填写) + */ + @ExcelColumn("错误信息") + private String error; +} \ No newline at end of file diff --git a/nflg-wms-common/src/main/java/com/nflg/wms/common/pojo/qo/PqcInspectionRuleExportQO.java b/nflg-wms-common/src/main/java/com/nflg/wms/common/pojo/qo/PqcInspectionRuleExportQO.java new file mode 100644 index 00000000..f7027c76 --- /dev/null +++ b/nflg-wms-common/src/main/java/com/nflg/wms/common/pojo/qo/PqcInspectionRuleExportQO.java @@ -0,0 +1,32 @@ +package com.nflg.wms.common.pojo.qo; + +import lombok.Data; + +import java.util.List; + +/** + * PQC检测规则 导出请求参数 + */ +@Data +public class PqcInspectionRuleExportQO { + + /** + * 指定导出的规则ID列表(可选,为空则导出查询条件匹配的全部数据) + */ + private List ids; + + /** + * 查询条件(可选,当ids为空时用于导出查询条件匹配的数据) + */ + private String modelNo; + + private String pqcRuleCode; + + private Boolean isDisabled; + + private Integer auditStatus; + + private String createTimeStart; + + private String createTimeEnd; +} \ No newline at end of file diff --git a/nflg-wms-repository/src/main/java/com/nflg/wms/repository/entity/QmsPqcInspectionPointItems.java b/nflg-wms-repository/src/main/java/com/nflg/wms/repository/entity/QmsPqcInspectionPointItems.java index b55abb0c..a282b9f3 100644 --- a/nflg-wms-repository/src/main/java/com/nflg/wms/repository/entity/QmsPqcInspectionPointItems.java +++ b/nflg-wms-repository/src/main/java/com/nflg/wms/repository/entity/QmsPqcInspectionPointItems.java @@ -47,7 +47,7 @@ public class QmsPqcInspectionPointItems implements Serializable { private String inspectionContent; /** - * 检查类型:0=工序检查,1=关键物料拍照,2=全部 + * 检查类型(位标志,支持多选):bit0=工序检查(1), bit1=关键物料拍照(2), bit2=关键物料采集(4) */ private Integer inspectionType; diff --git a/nflg-wms-starter/src/main/java/com/nflg/wms/starter/service/impl/RustFSServiceImpl.java b/nflg-wms-starter/src/main/java/com/nflg/wms/starter/service/impl/RustFSServiceImpl.java index 8a01a789..2995bf8a 100644 --- a/nflg-wms-starter/src/main/java/com/nflg/wms/starter/service/impl/RustFSServiceImpl.java +++ b/nflg-wms-starter/src/main/java/com/nflg/wms/starter/service/impl/RustFSServiceImpl.java @@ -41,13 +41,14 @@ public class RustFSServiceImpl implements FileUploadService { @Override public String upload(String filePath, InputStream stream, String contentType) throws Exception { ensureBucketExists(); + byte[] bytes = stream.readAllBytes(); s3Client.putObject( PutObjectRequest.builder() .bucket(bucketName) .key(filePath) .contentType(contentType) .build(), - RequestBody.fromInputStream(stream, stream.available()) + RequestBody.fromBytes(bytes) ); return StrUtil.format("{}/{}/{}", domain, bucketName, filePath); }