package cn.iocoder.yudao.module.mes.service.qc.indicatorresult; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.mes.controller.admin.qc.indicatorresult.vo.MesQcIndicatorResultPageReqVO; import cn.iocoder.yudao.module.mes.controller.admin.qc.indicatorresult.vo.MesQcIndicatorResultSaveReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicator.MesQcIndicatorDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicatorresult.MesQcIndicatorResultDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicatorresult.MesQcIndicatorResultDetailDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ipqc.MesQcIpqcDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcDO; import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcDO; import cn.iocoder.yudao.module.mes.dal.mysql.qc.indicatorresult.MesQcIndicatorResultMapper; import cn.iocoder.yudao.module.mes.enums.qc.MesQcResultValueTypeEnum; import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum; import cn.iocoder.yudao.module.mes.service.qc.indicator.MesQcIndicatorService; import cn.iocoder.yudao.module.mes.service.qc.ipqc.MesQcIpqcService; import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcService; import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcService; import cn.iocoder.yudao.module.mes.service.qc.rqc.MesQcRqcService; import cn.iocoder.yudao.module.system.api.dict.DictDataApi; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; import java.net.URI; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; /** * MES 检验结果 Service 实现类 * * @author 芋道源码 */ @Service @Validated public class MesQcIndicatorResultServiceImpl implements MesQcIndicatorResultService { @Resource private MesQcIndicatorResultMapper resultMapper; @Resource private MesQcIndicatorResultDetailService resultDetailService; @Resource private MesQcIndicatorService indicatorService; @Resource private MesQcIqcService iqcService; @Resource @Lazy private MesQcIpqcService ipqcService; @Resource @Lazy private MesQcOqcService oqcService; @Resource @Lazy private MesQcRqcService rqcService; @Resource private DictDataApi dictDataApi; @Override @Transactional(rollbackFor = Exception.class) public Long createIndicatorResult(MesQcIndicatorResultSaveReqVO createReqVO) { // 1.1 根据 qcType 查询源质检单,获取 itemId Long itemId = getItemIdFromQcDoc(createReqVO.getQcId(), createReqVO.getQcType()); // 1.2 校验明细数据(indicatorId 存在性 + 值格式) validateItemsSaveData(createReqVO.getItems()); // 2.1 插入主表 MesQcIndicatorResultDO result = BeanUtils.toBean(createReqVO, MesQcIndicatorResultDO.class); result.setItemId(itemId); resultMapper.insert(result); // 2.2 批量插入明细 List details = BeanUtils.toBean(createReqVO.getItems(), MesQcIndicatorResultDetailDO.class); details.forEach(detail -> detail.setResultId(result.getId())); resultDetailService.createDetailList(details); return result.getId(); } @Override @Transactional(rollbackFor = Exception.class) public void updateIndicatorResult(MesQcIndicatorResultSaveReqVO updateReqVO) { // 1.1 校验存在 validateIndicatorResultExists(updateReqVO.getId()); // 1.2 校验明细数据(indicatorId 存在性 + 值格式) validateItemsSaveData(updateReqVO.getItems()); // 2.1 更新主表(锁定 qcId/qcType/itemId,不允许改挂到其他质检单) MesQcIndicatorResultDO updateObj = BeanUtils.toBean(updateReqVO, MesQcIndicatorResultDO.class); updateObj.setQcId(null).setQcType(null).setItemId(null); resultMapper.updateById(updateObj); // 2.2 批量更新明细 List details = BeanUtils.toBean(updateReqVO.getItems(), MesQcIndicatorResultDetailDO.class); resultDetailService.createOrUpdateDetailList(details); } @Override @Transactional(rollbackFor = Exception.class) public void deleteIndicatorResult(Long id) { // 1. 校验存在 validateIndicatorResultExists(id); // 2.1 级联删除明细 resultDetailService.deleteDetailByResultId(id); // 2.2 删除主表 resultMapper.deleteById(id); } @Override public MesQcIndicatorResultDO getIndicatorResult(Long id) { return resultMapper.selectById(id); } @Override public PageResult getIndicatorResultPage(MesQcIndicatorResultPageReqVO pageReqVO) { return resultMapper.selectPage(pageReqVO); } @Override public List getIndicatorResultDetailListByResultId(Long resultId) { return resultDetailService.getDetailListByResultId(resultId); } @Override public Long getIndicatorResultCountByQcIdAndType(Long qcId, Integer qcType) { return resultMapper.selectCountByQcIdAndType(qcId, qcType); } @Override public void validateIndicatorResultExistsByQcIdAndType(Long qcId, Integer qcType) { Long resultCount = getIndicatorResultCountByQcIdAndType(qcId, qcType); if (resultCount == 0) { throw exception(QC_FINISH_INDICATOR_RESULT_REQUIRED); } } // ==================== 私有方法 ==================== private MesQcIndicatorResultDO validateIndicatorResultExists(Long id) { MesQcIndicatorResultDO result = resultMapper.selectById(id); if (result == null) { throw exception(QC_RESULT_NOT_EXISTS); } return result; } /** * 校验明细保存数据(indicatorId 存在性 + 值格式) */ private void validateItemsSaveData(List items) { // 校验所有明细的 indicatorId 是否存在 Map indicatorMap = validateIndicatorIds(items); // 校验明细值格式是否符合指标的 resultType validateDetailValues(items, indicatorMap); } /** * 校验所有明细的 indicatorId 是否存在 * * @return indicatorId -> MesQcIndicatorDO 映射,供后续值格式校验使用 */ private Map validateIndicatorIds(List items) { if (CollUtil.isEmpty(items)) { return Collections.emptyMap(); } Set indicatorIds = convertSet(items, MesQcIndicatorResultSaveReqVO.Item::getIndicatorId); return indicatorService.validateIndicatorListExists(indicatorIds); } /** * 按检测项的 resultType 校验明细值格式 * *

FLOAT → 必须可解析为 BigDecimal;INTEGER → 必须可解析为整数; * DICT → 必须属于字典值域;FILE → 必须为 http/https URL;TEXT → 放行 */ private void validateDetailValues(List items, Map indicatorMap) { if (CollUtil.isEmpty(items)) { return; } for (MesQcIndicatorResultSaveReqVO.Item item : items) { if (item.getIndicatorId() == null || StrUtil.isBlank(item.getValue())) { continue; } MesQcIndicatorDO indicator = indicatorMap.get(item.getIndicatorId()); if (indicator == null || indicator.getResultType() == null) { continue; } Integer resultType = indicator.getResultType(); if (Objects.equals(resultType, MesQcResultValueTypeEnum.FLOAT.getType())) { try { new BigDecimal(item.getValue()); } catch (NumberFormatException e) { throw exception(QC_RESULT_VALUE_FORMAT_INVALID, "检测项[" + indicator.getName() + "]要求浮点数,实际值=" + item.getValue()); } } else if (Objects.equals(resultType, MesQcResultValueTypeEnum.INTEGER.getType())) { try { Long.parseLong(item.getValue()); } catch (NumberFormatException e) { throw exception(QC_RESULT_VALUE_FORMAT_INVALID, "检测项[" + indicator.getName() + "]要求整数,实际值=" + item.getValue()); } } // DICT:校验值属于对应字典类型 if (Objects.equals(resultType, MesQcResultValueTypeEnum.DICT.getType())) { String dictType = indicator.getResultSpecification(); if (StrUtil.isNotBlank(dictType)) { dictDataApi.validateDictDataList(dictType, Collections.singleton(item.getValue())); } } if (Objects.equals(resultType, MesQcResultValueTypeEnum.FILE.getType()) && !isHttpUrl(item.getValue())) { throw exception(QC_RESULT_VALUE_FORMAT_INVALID, "检测项[" + indicator.getName() + "]要求文件 URL,实际值=" + item.getValue()); } // TEXT 不做格式校验 } } private boolean isHttpUrl(String value) { try { URI uri = URI.create(value); String scheme = uri.getScheme(); return StrUtil.isNotBlank(uri.getHost()) && ("http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme)); } catch (IllegalArgumentException e) { return false; } } /** * 根据 qcType 查询源质检单,获取 itemId */ private Long getItemIdFromQcDoc(Long qcId, Integer qcType) { if (Objects.equals(qcType, MesQcTypeEnum.IQC.getType())) { MesQcIqcDO iqc = iqcService.validateIqcExists(qcId); return iqc.getItemId(); } else if (Objects.equals(qcType, MesQcTypeEnum.IPQC.getType())) { MesQcIpqcDO ipqc = ipqcService.validateIpqcExists(qcId); return ipqc.getItemId(); } else if (Objects.equals(qcType, MesQcTypeEnum.OQC.getType())) { MesQcOqcDO oqc = oqcService.validateOqcExists(qcId); return oqc.getItemId(); } else if (Objects.equals(qcType, MesQcTypeEnum.RQC.getType())) { MesQcRqcDO rqc = rqcService.validateRqcExists(qcId); return rqc.getItemId(); } throw new IllegalArgumentException("暂不支持 qcType=" + qcType); } }