feat(qms): 优化检验标准管理及相关功能

- QmsIncomingInspectionTaskNonconformanceVO新增ticketId、version和associationTaskNo字段,完善查询结果字段
- QmsInspectionStandard实体新增发布状态2=已废弃,增强状态管理
- QmsInspectionStandardController中的saveDraft接口调整,返回新建草稿ID
- QmsInspectionStandardControllerService中saveDraft方法重构
  - 已发布标准不允许直接修改,改为创建新的草稿记录
  - 新增抽样检测方式相关必填字段校验
  - 实现基于已发布检验标准创建全新草稿及其检测项和内容的逻辑
- QmsInspectionStandardServiceImpl发布方法增强
  - 校验不得包含已发布的标准
  - 发布后将相同物料ID的其他检验标准状态设置为已废弃
- QmsIssueTicket实体新增关联检测任务单号字段
- QmsIssueTicketControllerService中优化图片ID拼接和图片列表查询逻辑,避免空字段NullPointer异常
This commit is contained in:
曹鹏飞 2026-05-09 10:39:00 +08:00
parent a5d04d067d
commit a8e6c1570a
8 changed files with 175 additions and 40 deletions

View File

@ -78,9 +78,8 @@ public class QmsInspectionStandardController extends BaseController {
* 暂存检验标准 * 暂存检验标准
*/ */
@PostMapping("saveDraft") @PostMapping("saveDraft")
public ApiResult<Void> saveDraft(@Valid @RequestBody QmsInspectionStandardSaveQO request) { public ApiResult<Long> saveDraft(@Valid @RequestBody QmsInspectionStandardSaveQO request) {
inspectionStandardControllerService.saveDraft(request); return ApiResult.success(inspectionStandardControllerService.saveDraft(request));
return ApiResult.success();
} }
/** /**

View File

@ -513,22 +513,23 @@ public class QmsInspectionStandardControllerService {
* @param qo 暂存请求参数 * @param qo 暂存请求参数
*/ */
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void saveDraft(QmsInspectionStandardSaveQO qo) { public Long saveDraft(QmsInspectionStandardSaveQO qo) {
// 1. 校验检验标准是否存在 // 1. 校验检验标准是否存在
QmsInspectionStandard standard = inspectionStandardService.getById(qo.getInspectionStandardId()); QmsInspectionStandard standard = inspectionStandardService.getById(qo.getInspectionStandardId());
if (standard == null) { if (standard == null) {
throw new NflgException(com.nflg.wms.common.constant.STATE.BusinessError, "检验标准不存在"); throw new NflgException(com.nflg.wms.common.constant.STATE.BusinessError, "检验标准不存在");
} }
// 已发布的标准不允许暂存修改
if (standard.getPublishStatus() != null && standard.getPublishStatus() == 1) {
throw new NflgException(com.nflg.wms.common.constant.STATE.BusinessError, "已发布的检验标准不允许修改");
}
Long userId = UserUtil.getUserId(); Long userId = UserUtil.getUserId();
String userName = UserUtil.getUserName(); String userName = UserUtil.getUserName();
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();
// 已发布的标准不允许直接修改改为创建新的草稿记录
if (standard.getPublishStatus() != null && standard.getPublishStatus() == 1) {
validateSamplingFields(qo);
return createNewDraftFromPublished(qo, userId, userName, now);
}
// 2. 更新检验标准信息 // 2. 更新检验标准信息
VUtil.trueThrowBusinessError( VUtil.trueThrowBusinessError(
inspectionStandardService.lambdaQuery() inspectionStandardService.lambdaQuery()
@ -539,22 +540,7 @@ public class QmsInspectionStandardControllerService {
).throwMessage("存在相同版本的检验标准"); ).throwMessage("存在相同版本的检验标准");
// 检测方式为抽样时校验相关字段 // 检测方式为抽样时校验相关字段
if (qo.getTestingMethodDictItemId() != null) { validateSamplingFields(qo);
List<DictionaryItem> testingMethodItems = dictionaryItemService.lambdaQuery()
.eq(DictionaryItem::getId, qo.getTestingMethodDictItemId())
.list();
DictionaryItem testingMethod = testingMethodItems.isEmpty() ? null : testingMethodItems.get(0);
if (testingMethod != null && StrUtil.equals(testingMethod.getCode(), "抽样")) {
VUtil.trueThrowBusinessError(Objects.isNull(qo.getSamplingPlanId()))
.throwMessage("检测方式为【抽样】时,抽样方案不能为空");
VUtil.trueThrowBusinessError(Objects.isNull(qo.getInspectionLevelDictItemId()))
.throwMessage("检测方式为【抽样】时,检验水平不能为空");
VUtil.trueThrowBusinessError(Objects.isNull(qo.getAqlPriorityValueId()))
.throwMessage("检测方式为【抽样】时AQL值不能为空");
VUtil.trueThrowBusinessError(Objects.isNull(qo.getAqlTypeDictItemId()))
.throwMessage("检测方式为【抽样】时AQL类型不能为空");
}
}
// 3. 更新字段不修改版本号 // 3. 更新字段不修改版本号
standard.setMaterialId(qo.getMaterialId()); standard.setMaterialId(qo.getMaterialId());
@ -578,6 +564,96 @@ public class QmsInspectionStandardControllerService {
// 3. 处理检测项列表 // 3. 处理检测项列表
processItems(dictionaryItemService.getListByDictionaryCode("InspectionStandardDetectionType"), processItems(dictionaryItemService.getListByDictionaryCode("InspectionStandardDetectionType"),
qo.getInspectionStandardId(), qo.getItems(), userId, userName, now); qo.getInspectionStandardId(), qo.getItems(), userId, userName, now);
return standard.getId();
}
/**
* 校验检测方式为抽样时的必填字段
*/
private void validateSamplingFields(QmsInspectionStandardSaveQO qo) {
if (qo.getTestingMethodDictItemId() != null) {
List<DictionaryItem> testingMethodItems = dictionaryItemService.lambdaQuery()
.eq(DictionaryItem::getId, qo.getTestingMethodDictItemId())
.list();
DictionaryItem testingMethod = testingMethodItems.isEmpty() ? null : testingMethodItems.get(0);
if (testingMethod != null && StrUtil.equals(testingMethod.getCode(), "抽样")) {
VUtil.trueThrowBusinessError(Objects.isNull(qo.getSamplingPlanId()))
.throwMessage("检测方式为【抽样】时,抽样方案不能为空");
VUtil.trueThrowBusinessError(Objects.isNull(qo.getInspectionLevelDictItemId()))
.throwMessage("检测方式为【抽样】时,检验水平不能为空");
VUtil.trueThrowBusinessError(Objects.isNull(qo.getAqlPriorityValueId()))
.throwMessage("检测方式为【抽样】时AQL值不能为空");
VUtil.trueThrowBusinessError(Objects.isNull(qo.getAqlTypeDictItemId()))
.throwMessage("检测方式为【抽样】时AQL类型不能为空");
}
}
}
/**
* 基于已发布的检验标准创建新的草稿记录全量新增不复用旧记录 ID
*/
private Long createNewDraftFromPublished(QmsInspectionStandardSaveQO qo,
Long userId, String userName, LocalDateTime now) {
// 1. 版本唯一性校验排除原发布记录本身
VUtil.trueThrowBusinessError(
inspectionStandardService.lambdaQuery()
.ne(QmsInspectionStandard::getId, qo.getInspectionStandardId())
.eq(QmsInspectionStandard::getMaterialId, qo.getMaterialId())
.eq(QmsInspectionStandard::getVersion, qo.getVersion())
.exists()
).throwMessage("存在相同版本的检验标准");
// 2. 创建新的检验标准草稿状态
QmsInspectionStandard newStandard = new QmsInspectionStandard();
newStandard.setMaterialId(qo.getMaterialId());
newStandard.setDrawingUrl(qo.getDrawingUrl());
newStandard.setPackagingMethodId(qo.getPackagingMethodId());
newStandard.setInspectionCycle(qo.getInspectionCycle());
newStandard.setVersion(qo.getVersion());
newStandard.setIsEnabled(qo.getIsEnabled());
newStandard.setTestingMethodDictItemId(qo.getTestingMethodDictItemId());
newStandard.setSamplingPlanId(qo.getSamplingPlanId());
newStandard.setInspectionLevelDictItemId(qo.getInspectionLevelDictItemId());
newStandard.setAqlPriorityValueId(qo.getAqlPriorityValueId());
newStandard.setAqlTypeDictItemId(qo.getAqlTypeDictItemId());
newStandard.setPublishStatus((short) 0);
newStandard.setCreateUserId(userId);
newStandard.setCreateUserName(userName);
newStandard.setCreateTime(now);
newStandard.setUpdateUserId(userId);
newStandard.setUpdateUserName(userName);
newStandard.setUpdateTime(now);
inspectionStandardService.save(newStandard);
// 3. 全量新增检测项及内容不引用旧记录的 ID
if (qo.getItems() == null) {
return newStandard.getId();
}
List<DictionaryItem> dictionaryItems = dictionaryItemService.getListByDictionaryCode("InspectionStandardDetectionType");
for (QmsInspectionStandardSaveQO.InspectionStandardItemQO itemQO : qo.getItems()) {
DictionaryItem dictionaryItem = dictionaryItems.stream()
.filter(d -> d.getId().equals(itemQO.getDetectionTypeDictItemId()))
.findFirst()
.orElse(null);
VUtil.trueThrowBusinessError(Objects.isNull(dictionaryItem))
.throwMessage("检测项【" + itemQO.getName() + "】设置的检测类型不存在");
QmsInspectionStandardItem newItem = new QmsInspectionStandardItem()
.setInspectionStandardId(newStandard.getId());
updateItemFields(newItem, itemQO, userId, userName, now);
inspectionStandardItemService.save(newItem);
if (itemQO.getContents() != null) {
for (QmsInspectionStandardSaveQO.InspectionStandardItemContentQO contentQO : itemQO.getContents()) {
QmsInspectionStandardItemContent newContent = new QmsInspectionStandardItemContent()
.setInspectionStandardItemId(newItem.getId());
updateContentFields(newContent, contentQO, userId, userName, now);
inspectionStandardItemContentService.save(newContent);
}
}
}
return newStandard.getId();
} }
/** /**

View File

@ -458,22 +458,26 @@ public class QmsIssueTicketControllerService {
return r1; return r1;
}); });
r.setUnqualifiedQty(r.getUnqualifiedQty() + record.getUnqualifiedQty()); r.setUnqualifiedQty(r.getUnqualifiedQty() + record.getUnqualifiedQty());
r.setImageIds(StrUtil.join(",", r.getImageIds(), record.getImageIds())); if (StrUtil.isNotBlank(r.getImageIds())) {
r.setImageIds(StrUtil.join(",", r.getImageIds(), record.getImageIds()));
}
}); });
records.stream() records.stream()
.filter(record -> StrUtil.isNotBlank(record.getImageIds())) .filter(record -> StrUtil.isNotBlank(record.getImageIds()))
.forEach(record -> { .forEach(record -> {
record.setImages(fileUploadRecordService.lambdaQuery() if(StrUtil.isNotBlank(record.getImageIds())) {
.in(FileUploadRecord::getId, StrUtil.split(record.getImageIds(), ",")) record.setImages(fileUploadRecordService.lambdaQuery()
.list() .in(FileUploadRecord::getId, Arrays.stream(StrUtil.splitToLong(record.getImageIds(), ",")).boxed().toList())
.stream() .list()
.map(file -> new FileUploadVO() .stream()
.setId(file.getId()) .map(file -> new FileUploadVO()
.setFileName(file.getFileName()) .setId(file.getId())
.setUrl(file.getUrl()) .setFileName(file.getFileName())
) .setUrl(file.getUrl())
.collect(Collectors.toList()) )
); .collect(Collectors.toList())
);
}
}); });
vo.setRecords(records); vo.setRecords(records);
} }

View File

@ -15,6 +15,11 @@ public class QmsIncomingInspectionTaskNonconformanceVO {
*/ */
private Long taskId; private Long taskId;
/**
* 工单ID
*/
private Long ticketId;
/** /**
* 检测单号 * 检测单号
*/ */
@ -50,6 +55,11 @@ public class QmsIncomingInspectionTaskNonconformanceVO {
*/ */
private String drawingNoVer; private String drawingNoVer;
/**
* 检测标准版本号
*/
private String version;
/** /**
* 供应商编号 * 供应商编号
*/ */
@ -154,4 +164,9 @@ public class QmsIncomingInspectionTaskNonconformanceVO {
* 流程状态0=未发起1=处理中2=已完成 * 流程状态0=未发起1=处理中2=已完成
*/ */
private Short status; private Short status;
/**
* 关联检测任务单号
*/
private String associationTaskNo;
} }

View File

@ -82,7 +82,7 @@ public class QmsInspectionStandard implements Serializable {
private Long aqlTypeDictItemId; private Long aqlTypeDictItemId;
/** /**
* 发布状态0-未发布1-已发布 * 发布状态0-未发布1-已发布2=已废弃
*/ */
private Short publishStatus; private Short publishStatus;

View File

@ -164,4 +164,9 @@ public class QmsIssueTicket implements Serializable {
*/ */
@TableLogic @TableLogic
private Integer deleted; private Integer deleted;
/**
* 关联检测任务单号
*/
private String associationTaskNo;
} }

View File

@ -4,13 +4,16 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nflg.wms.common.pojo.qo.QmsIncomingInspectionTaskTodoCheckItemsQO; import com.nflg.wms.common.pojo.qo.QmsIncomingInspectionTaskTodoCheckItemsQO;
import com.nflg.wms.common.pojo.vo.QmsIncomingInspectionTaskCheckItemVO; import com.nflg.wms.common.pojo.vo.QmsIncomingInspectionTaskCheckItemVO;
import com.nflg.wms.common.util.UserUtil; import com.nflg.wms.common.util.UserUtil;
import com.nflg.wms.common.util.VUtil;
import com.nflg.wms.repository.entity.QmsInspectionStandard; import com.nflg.wms.repository.entity.QmsInspectionStandard;
import com.nflg.wms.repository.mapper.QmsInspectionStandardMapper; import com.nflg.wms.repository.mapper.QmsInspectionStandardMapper;
import com.nflg.wms.repository.service.IQmsInspectionStandardService; import com.nflg.wms.repository.service.IQmsInspectionStandardService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
/** /**
* 检验标准 服务实现类 * 检验标准 服务实现类
@ -19,15 +22,26 @@ import java.util.List;
public class QmsInspectionStandardServiceImpl extends ServiceImpl<QmsInspectionStandardMapper, QmsInspectionStandard> public class QmsInspectionStandardServiceImpl extends ServiceImpl<QmsInspectionStandardMapper, QmsInspectionStandard>
implements IQmsInspectionStandardService { implements IQmsInspectionStandardService {
@Transactional
@Override @Override
public void publish(List<Long> ids) { public void publish(List<Long> ids) {
if (ids == null || ids.isEmpty()) { if (ids == null || ids.isEmpty()) {
return; return;
} }
// 查询待发布的记录
List<QmsInspectionStandard> standards = listByIds(ids);
// 校验是否包含已发布的记录
boolean hasPublished = standards.stream()
.anyMatch(s -> s.getPublishStatus() == 1);
VUtil.trueThrowBusinessError(hasPublished).throwMessage("包含已发布的检验标准,请重新选择");
Long userId = UserUtil.getUserId(); Long userId = UserUtil.getUserId();
String userName = UserUtil.getUserName(); String userName = UserUtil.getUserName();
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();
// 发布选中的记录
lambdaUpdate() lambdaUpdate()
.eq(QmsInspectionStandard::getPublishStatus, (short) 0) .eq(QmsInspectionStandard::getPublishStatus, (short) 0)
.in(QmsInspectionStandard::getId, ids) .in(QmsInspectionStandard::getId, ids)
@ -39,6 +53,24 @@ public class QmsInspectionStandardServiceImpl extends ServiceImpl<QmsInspectionS
.set(QmsInspectionStandard::getUpdateUserName, userName) .set(QmsInspectionStandard::getUpdateUserName, userName)
.set(QmsInspectionStandard::getUpdateTime, now) .set(QmsInspectionStandard::getUpdateTime, now)
.update(); .update();
// 将相同物料ID的其他记录设置为已废弃
List<Long> materialIds = standards.stream()
.map(QmsInspectionStandard::getMaterialId)
.distinct()
.collect(Collectors.toList());
if (!materialIds.isEmpty()) {
lambdaUpdate()
.in(QmsInspectionStandard::getMaterialId, materialIds)
.notIn(QmsInspectionStandard::getId, ids)
.ne(QmsInspectionStandard::getPublishStatus, (short) 2)
.set(QmsInspectionStandard::getPublishStatus, (short) 2)
.set(QmsInspectionStandard::getUpdateUserId, userId)
.set(QmsInspectionStandard::getUpdateUserName, userName)
.set(QmsInspectionStandard::getUpdateTime, now)
.update();
}
} }
@Override @Override

View File

@ -5,6 +5,7 @@
<select id="search" resultType="com.nflg.wms.common.pojo.vo.QmsIncomingInspectionTaskNonconformanceVO"> <select id="search" resultType="com.nflg.wms.common.pojo.vo.QmsIncomingInspectionTaskNonconformanceVO">
SELECT SELECT
t.id as task_id, t.id as task_id,
it.id as ticket_id,
t.task_no, t.task_no,
it.ticket_no, it.ticket_no,
it.ticket_title, it.ticket_title,
@ -12,6 +13,7 @@
m.material_no, m.material_no,
m.material_desc, m.material_desc,
m.drawing_no_ver, m.drawing_no_ver,
is.version,
t.supplier_code, t.supplier_code,
t.supplier_name, t.supplier_name,
t.delivery_order_no, t.delivery_order_no,
@ -32,10 +34,12 @@
t.inspection_finish_time, t.inspection_finish_time,
t.required_finish_time, t.required_finish_time,
t.is_overdue, t.is_overdue,
CASE WHEN it.status IS NULL THEN 0 ELSE it.status END AS status CASE WHEN it.status IS NULL THEN 0 ELSE it.status END AS status,
it.association_task_no
FROM qms_incoming_inspection_task t FROM qms_incoming_inspection_task t
LEFT JOIN qms_issue_ticket it ON it.source_id = t.id LEFT JOIN qms_issue_ticket it ON it.source_id = t.id
INNER JOIN qms_qc_material m ON t.material_id = m.id INNER JOIN qms_qc_material m ON t.material_id = m.id
LEFT JOIN qms_inspection_standard is ON t.inspection_standard_id = is.id
WHERE t.inspection_status=2 and t.inspection_result=false WHERE t.inspection_status=2 and t.inspection_result=false
AND ((t.inspection_type=0 and t.purchase_group=#{request.purchaseGroup}) or (t.inspection_type=1 and #{request.isWarehouseManager}=true)) AND ((t.inspection_type=0 and t.purchase_group=#{request.purchaseGroup}) or (t.inspection_type=1 and #{request.isWarehouseManager}=true))
<if test="request.dataType != null"> <if test="request.dataType != null">