package cn.iocoder.yudao.module.mes.service.qc.template;
|
|
import cn.hutool.crypto.digest.DigestUtil;
|
import cn.iocoder.yudao.module.mes.controller.admin.qc.indicator.vo.MesQcIndicatorSaveReqVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.qc.template.vo.MesQcTemplateSaveReqVO;
|
import cn.iocoder.yudao.module.mes.controller.admin.qc.template.vo.indicator.MesQcTemplateIndicatorSaveReqVO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicator.MesQcIndicatorDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateDO;
|
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateIndicatorDO;
|
import cn.iocoder.yudao.module.mes.dal.mysql.qc.indicator.MesQcIndicatorMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.qc.template.MesQcTemplateIndicatorMapper;
|
import cn.iocoder.yudao.module.mes.dal.mysql.qc.template.MesQcTemplateMapper;
|
import cn.iocoder.yudao.module.mes.service.qc.indicator.MesQcIndicatorService;
|
import cn.iocoder.yudao.module.system.api.agent.imports.AgentImportCommitReqVO;
|
import cn.iocoder.yudao.module.system.api.agent.imports.AgentImportModuleAdapter;
|
import cn.iocoder.yudao.module.system.api.agent.imports.AgentImportPreflightReqVO;
|
import cn.iocoder.yudao.module.system.api.agent.imports.AgentImportPreflightRespVO;
|
import cn.iocoder.yudao.module.system.api.agent.imports.AgentImportRowReceipt;
|
import cn.iocoder.yudao.module.system.framework.agent.AgentPrincipal;
|
import cn.iocoder.yudao.module.system.service.agent.imports.SystemAgentGenericImportService;
|
import jakarta.annotation.Resource;
|
import org.springframework.stereotype.Component;
|
|
import java.math.BigDecimal;
|
import java.util.*;
|
|
/**
|
* 质检方案导入:按检测项名称复用现有主数据;缺失项作为副作用提案,用户确认预检后才创建。
|
*/
|
@Component
|
public class MesQcTemplateImportAdapter implements AgentImportModuleAdapter {
|
|
public static final String MODULE_ID = "mes.qc-template";
|
|
@Resource private MesQcTemplateMapper templateMapper;
|
@Resource private MesQcTemplateService templateService;
|
@Resource private MesQcIndicatorMapper indicatorMapper;
|
@Resource private MesQcIndicatorService indicatorService;
|
@Resource private MesQcTemplateIndicatorMapper templateIndicatorMapper;
|
@Resource private MesQcTemplateIndicatorService templateIndicatorService;
|
|
@Override
|
public String moduleId() {
|
return MODULE_ID;
|
}
|
|
@Override
|
public String permission() {
|
return "mes:qc-template:create";
|
}
|
|
@Override
|
public List<String> permissions() {
|
return List.of("mes:qc-template:create", "mes:qc-indicator:create");
|
}
|
|
@Override
|
public AgentImportPreflightRespVO preflight(AgentPrincipal principal, AgentImportPreflightReqVO request) {
|
List<AgentImportPreflightRespVO.Row> plannedRows = new ArrayList<>();
|
List<Map<String, Object>> resolvedPayload = new ArrayList<>();
|
Set<String> fileKeys = new HashSet<>();
|
int create = 0;
|
int skip = 0;
|
int blocked = 0;
|
|
for (AgentImportPreflightReqVO.Row row : request.getRows()) {
|
List<AgentImportPreflightRespVO.Issue> blockers = new ArrayList<>();
|
List<AgentImportPreflightRespVO.Issue> warnings = new ArrayList<>();
|
String templateCode = value(row, "code");
|
String templateName = value(row, "name");
|
String indicatorName = value(row, "indicatorName");
|
if (templateCode.isEmpty()) blockers.add(issue("TEMPLATE_CODE_REQUIRED", "code", "方案编号不能为空"));
|
if (templateName.isEmpty()) blockers.add(issue("TEMPLATE_NAME_REQUIRED", "name", "方案名称不能为空"));
|
if (indicatorName.isEmpty()) blockers.add(issue("INDICATOR_NAME_REQUIRED", "indicatorName", "检测项不能为空"));
|
|
List<Integer> types = parseTypes(value(row, "types"), blockers);
|
Integer status = parseInteger(value(row, "status"), 0, "status", blockers);
|
Integer indicatorType = parseInteger(value(row, "indicatorType"), 5, "indicatorType", blockers);
|
Integer resultType = parseInteger(value(row, "resultType"), 1, "resultType", blockers);
|
BigDecimal thresholdMax = decimal(value(row, "thresholdMax"), "thresholdMax", blockers);
|
BigDecimal thresholdMin = decimal(value(row, "thresholdMin"), "thresholdMin", blockers);
|
|
MesQcIndicatorDO indicator = findIndicator(value(row, "indicatorCode"), indicatorName);
|
String indicatorCode = indicator == null ? proposedCode(indicatorName) : indicator.getCode();
|
String indicatorAction = indicator == null ? "CREATE_AFTER_REVIEW" : "REUSE";
|
if (indicator == null && !indicatorName.isEmpty()) {
|
warnings.add(issue("INDICATOR_WILL_CREATE", "indicatorName",
|
"系统不存在检测项“" + indicatorName + "”;确认导入后将先新增检测项 " + indicatorCode));
|
}
|
if (!value(row, "unitCode").isEmpty()) {
|
warnings.add(issue("UNIT_REQUIRES_REVIEW", "unitCode",
|
"计量单位“" + value(row, "unitCode") + "”暂以原文保留,请复核系统单位关联"));
|
}
|
|
MesQcTemplateDO template = templateCode.isEmpty() ? null : templateMapper.selectByCode(templateCode);
|
MesQcTemplateIndicatorDO relation = template == null || indicator == null ? null
|
: templateIndicatorMapper.selectListByTemplateId(template.getId()).stream()
|
.filter(item -> Objects.equals(item.getIndicatorId(), indicator.getId())).findFirst().orElse(null);
|
String fileKey = templateCode + "\u0000" + indicatorName;
|
String action;
|
Long targetId = null;
|
if (!blockers.isEmpty()) {
|
action = "BLOCKED";
|
blocked++;
|
} else if (!fileKeys.add(fileKey)) {
|
blockers.add(issue("DUPLICATE_IN_FILE", "indicatorName", "文件内检测项重复,请保留一条"));
|
action = "BLOCKED";
|
blocked++;
|
} else if (relation != null) {
|
action = "SKIP";
|
targetId = relation.getId();
|
skip++;
|
warnings.add(issue("TEMPLATE_INDICATOR_EXISTS", "indicatorName", "方案已关联该检测项,已跳过"));
|
} else {
|
action = "CREATE";
|
create++;
|
}
|
|
Map<String, Object> resolved = new LinkedHashMap<>();
|
resolved.put("code", templateCode);
|
resolved.put("name", templateName);
|
resolved.put("types", types);
|
resolved.put("status", status);
|
resolved.put("indicatorCode", indicatorCode);
|
resolved.put("indicatorName", indicatorName);
|
resolved.put("indicatorType", indicatorType);
|
resolved.put("resultType", resultType);
|
resolved.put("indicatorAction", indicatorAction);
|
resolved.put("thresholdMax", thresholdMax);
|
resolved.put("thresholdMin", thresholdMin);
|
resolved.put("standardValue", value(row, "standardValue"));
|
resolved.put("unitCode", value(row, "unitCode"));
|
resolved.put("action", action);
|
resolvedPayload.add(resolved);
|
|
plannedRows.add(AgentImportPreflightRespVO.Row.builder()
|
.candidateId(row.getCandidateId()).rowNo(row.getRowNo()).action(action)
|
.resolved(resolved).blockers(blockers).warnings(warnings).targetId(targetId).build());
|
}
|
return AgentImportPreflightRespVO.builder()
|
.moduleId(MODULE_ID)
|
.preflightHash(SystemAgentGenericImportService.preflightHash(MODULE_ID, request, resolvedPayload))
|
.source(AgentImportPreflightRespVO.Source.builder()
|
.attachmentId(request.getAttachmentId()).sessionId(request.getSessionId())
|
.sourceSha256(request.getSourceSha256()).proposalRevision(request.getProposalRevision()).build())
|
.summary(AgentImportPreflightRespVO.Summary.builder()
|
.total(plannedRows.size()).create(create).skip(skip).blocked(blocked).build())
|
.canCommit(blocked == 0)
|
.sideEffects(List.of(
|
"按方案编号创建质检方案(检测种类使用系统 mes_qc_type)",
|
"复用同名检测项;缺失检测项经本次人工确认后新增",
|
"创建方案与检测项关联,不覆盖现有检测项主数据"))
|
.rows(plannedRows).blockingItems(List.of()).build();
|
}
|
|
@Override
|
public AgentImportRowReceipt commitRow(AgentPrincipal principal, AgentImportCommitReqVO request,
|
AgentImportPreflightRespVO.Row planned,
|
AgentImportPreflightReqVO.Row row) {
|
MesQcTemplateDO template = templateMapper.selectByCode(value(row, "code"));
|
if (template == null) {
|
MesQcTemplateSaveReqVO create = new MesQcTemplateSaveReqVO();
|
create.setCode(value(row, "code"));
|
create.setName(value(row, "name"));
|
create.setTypes(parseTypes(value(row, "types"), new ArrayList<>()));
|
create.setStatus(parseInteger(value(row, "status"), 0, "status", new ArrayList<>()));
|
create.setRemark("来源:工业智能体文档补录;已由用户确认");
|
Long id = templateService.createTemplate(create);
|
template = templateService.getTemplate(id);
|
}
|
|
String indicatorName = value(row, "indicatorName");
|
MesQcIndicatorDO indicator = findIndicator(value(row, "indicatorCode"), indicatorName);
|
if (indicator == null) {
|
MesQcIndicatorSaveReqVO create = new MesQcIndicatorSaveReqVO();
|
create.setCode(proposedCode(indicatorName));
|
create.setName(indicatorName);
|
create.setType(parseInteger(value(row, "indicatorType"), 5, "indicatorType", new ArrayList<>()));
|
create.setItemType(1);
|
create.setParentId(0L);
|
create.setResultType(parseInteger(value(row, "resultType"), 1, "resultType", new ArrayList<>()));
|
create.setRemark("来源:工业智能体从已授权 SOP 提取;经用户审核新增");
|
Long id = indicatorService.createIndicator(create);
|
indicator = indicatorService.getIndicator(id);
|
}
|
|
Long indicatorId = indicator.getId();
|
MesQcTemplateIndicatorDO existing = templateIndicatorMapper.selectListByTemplateId(template.getId()).stream()
|
.filter(item -> Objects.equals(item.getIndicatorId(), indicatorId)).findFirst().orElse(null);
|
if (existing != null) {
|
return AgentImportRowReceipt.created(existing.getId(), value(row, "code"), indicatorName);
|
}
|
MesQcTemplateIndicatorSaveReqVO create = new MesQcTemplateIndicatorSaveReqVO();
|
create.setTemplateId(template.getId());
|
create.setIndicatorId(indicatorId);
|
create.setThresholdMax(decimal(value(row, "thresholdMax"), "thresholdMax", new ArrayList<>()));
|
create.setThresholdMin(decimal(value(row, "thresholdMin"), "thresholdMin", new ArrayList<>()));
|
String standard = value(row, "standardValue");
|
String unit = value(row, "unitCode");
|
create.setRemark("原文标准:" + standard + (unit.isEmpty() ? "" : ";单位:" + unit));
|
Long relationId = templateIndicatorService.createTemplateIndicator(create);
|
return AgentImportRowReceipt.created(relationId, value(row, "code"), indicatorName);
|
}
|
|
private MesQcIndicatorDO findIndicator(String code, String name) {
|
if (!code.isEmpty()) {
|
MesQcIndicatorDO found = indicatorMapper.selectByCode(code);
|
if (found != null) return found;
|
}
|
if (name.isEmpty()) return null;
|
return indicatorService.getIndicatorList().stream()
|
.filter(item -> name.equalsIgnoreCase(item.getName().trim())).findFirst().orElse(null);
|
}
|
|
private static String proposedCode(String name) {
|
return "AI-QI-" + DigestUtil.sha256Hex(name == null ? "" : name.trim()).substring(0, 12).toUpperCase(Locale.ROOT);
|
}
|
|
private static List<Integer> parseTypes(String raw, List<AgentImportPreflightRespVO.Issue> blockers) {
|
List<Integer> values = new ArrayList<>();
|
for (String part : raw.replace("[", "").replace("]", "").split("[,,]")) {
|
if (part.isBlank()) continue;
|
try {
|
int value = Integer.parseInt(part.trim());
|
if (value < 1 || value > 4) throw new NumberFormatException();
|
values.add(value);
|
} catch (NumberFormatException ex) {
|
blockers.add(issue("INVALID_QC_TYPE", "types", "检测种类必须使用系统字典值 1/2/3/4"));
|
}
|
}
|
if (values.isEmpty()) blockers.add(issue("QC_TYPE_REQUIRED", "types", "检测种类不能为空"));
|
return values;
|
}
|
|
private static Integer parseInteger(String raw, int defaultValue, String field,
|
List<AgentImportPreflightRespVO.Issue> blockers) {
|
if (raw.isEmpty()) return defaultValue;
|
try {
|
return Integer.valueOf(raw);
|
} catch (NumberFormatException ex) {
|
blockers.add(issue("INVALID_INTEGER", field, field + " 必须是整数"));
|
return defaultValue;
|
}
|
}
|
|
private static BigDecimal decimal(String raw, String field,
|
List<AgentImportPreflightRespVO.Issue> blockers) {
|
if (raw.isEmpty()) return null;
|
try {
|
return new BigDecimal(raw);
|
} catch (NumberFormatException ex) {
|
blockers.add(issue("INVALID_DECIMAL", field, field + " 必须是数字"));
|
return null;
|
}
|
}
|
|
private static String value(AgentImportPreflightReqVO.Row row, String key) {
|
String raw = row.getValues() == null ? null : row.getValues().get(key);
|
return raw == null ? "" : raw.trim();
|
}
|
|
private static AgentImportPreflightRespVO.Issue issue(String code, String field, String message) {
|
return AgentImportPreflightRespVO.Issue.builder()
|
.code(code).field(field).message(message).retryable(false).build();
|
}
|
}
|