|
|
|
|
@ -0,0 +1,277 @@
|
|
|
|
|
package org.dromara.qms.service.impl;
|
|
|
|
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
|
|
import java.util.Date;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
import cn.dev33.satoken.stp.StpUtil;
|
|
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
import org.apache.dubbo.config.annotation.DubboReference;
|
|
|
|
|
import org.apache.dubbo.rpc.RpcContext;
|
|
|
|
|
import org.dromara.common.core.exception.ServiceException;
|
|
|
|
|
import org.dromara.common.core.utils.StringUtils;
|
|
|
|
|
import org.dromara.common.tenant.helper.TenantHelper;
|
|
|
|
|
import org.dromara.system.api.RemoteCodeRuleService;
|
|
|
|
|
import org.dromara.qms.domain.bo.QcInspectionMainBo;
|
|
|
|
|
import org.dromara.qms.domain.bo.QcInspectionResultBo;
|
|
|
|
|
import org.dromara.qms.domain.bo.ProdBaseStationInfoBo;
|
|
|
|
|
import org.dromara.qms.domain.bo.ProdBaseProcessInfoBo;
|
|
|
|
|
import org.dromara.qms.domain.bo.QcTemplateItemBo;
|
|
|
|
|
import org.dromara.qms.domain.dto.QcInspectionMainTask;
|
|
|
|
|
import org.dromara.qms.domain.vo.ProdBaseStationInfoVo;
|
|
|
|
|
import org.dromara.qms.domain.vo.ProdBaseProcessInfoVo;
|
|
|
|
|
import org.dromara.qms.domain.vo.QcInspectionTemplateVo;
|
|
|
|
|
import org.dromara.qms.domain.vo.QcTemplateItemVo;
|
|
|
|
|
import org.dromara.qms.service.IQcHMIService;
|
|
|
|
|
import org.dromara.qms.service.IQcInspectionMainService;
|
|
|
|
|
import org.dromara.qms.service.IQcInspectionResultService;
|
|
|
|
|
import org.dromara.qms.service.IQcInspectionTemplateService;
|
|
|
|
|
import org.dromara.qms.service.IQcTemplateItemService;
|
|
|
|
|
import org.dromara.qms.service.IProdBaseStationInfoService;
|
|
|
|
|
import org.dromara.qms.service.IProdBaseProcessInfoService;
|
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 上位机接口Service业务层处理
|
|
|
|
|
*
|
|
|
|
|
* @author zch
|
|
|
|
|
* @date 2026-01-14
|
|
|
|
|
*/
|
|
|
|
|
@Slf4j
|
|
|
|
|
@RequiredArgsConstructor
|
|
|
|
|
@Service
|
|
|
|
|
public class QcHMIServiceImpl implements IQcHMIService {
|
|
|
|
|
|
|
|
|
|
private final IQcInspectionMainService qcInspectionMainService;
|
|
|
|
|
private final IQcInspectionResultService qcInspectionResultService;
|
|
|
|
|
private final IQcInspectionTemplateService qcInspectionTemplateService;
|
|
|
|
|
private final IQcTemplateItemService qcTemplateItemService;
|
|
|
|
|
private final IProdBaseStationInfoService prodBaseStationInfoService;
|
|
|
|
|
private final IProdBaseProcessInfoService prodBaseProcessInfoService;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 编码规则服务(Dubbo 引用)
|
|
|
|
|
* 用于生成质检单号
|
|
|
|
|
*/
|
|
|
|
|
@DubboReference(timeout = 300000)
|
|
|
|
|
private final RemoteCodeRuleService remoteCodeRuleService;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 上位机默认租户ID
|
|
|
|
|
*/
|
|
|
|
|
private static final String HMI_DEFAULT_TENANT_ID = "000000";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 上位机默认创建人ID(系统管理员)
|
|
|
|
|
*/
|
|
|
|
|
private static final Long HMI_DEFAULT_CREATE_BY = 1L;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 上位机调用生成质检任务
|
|
|
|
|
* <p>
|
|
|
|
|
* 业务流程:
|
|
|
|
|
* 1. 参数校验(物料编码、检验类型必填)
|
|
|
|
|
* 2. 根据工位名称查询工位编码
|
|
|
|
|
* 3. 匹配质检模板(8级降级匹配策略)
|
|
|
|
|
* 4. 生成质检单号
|
|
|
|
|
* 5. 构建并保存质检主表
|
|
|
|
|
* 6. 根据模板生成质检结果子表
|
|
|
|
|
* 7. 返回质检单号
|
|
|
|
|
*
|
|
|
|
|
* @param taskDto 上位机传入的任务DTO
|
|
|
|
|
* @return 质检单号
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
|
|
public String generateInspectionTask(QcInspectionMainTask taskDto) {
|
|
|
|
|
// 【重要】上位机不登录,模拟登录系统管理员,确保认证上下文和租户信息正确传递
|
|
|
|
|
StpUtil.login(HMI_DEFAULT_CREATE_BY, "login");
|
|
|
|
|
// 设置默认租户上下文
|
|
|
|
|
TenantHelper.setDynamic(HMI_DEFAULT_TENANT_ID);
|
|
|
|
|
// 【关键】通过Dubbo隐式参数传递租户ID,确保远程服务能获取租户上下文
|
|
|
|
|
RpcContext.getContext().setAttachment("tenantId", HMI_DEFAULT_TENANT_ID);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
return doGenerateInspectionTask(taskDto);
|
|
|
|
|
} finally {
|
|
|
|
|
// 【清理】清理登录状态和租户上下文,避免线程池复用时的状态污染
|
|
|
|
|
try {
|
|
|
|
|
StpUtil.logout();
|
|
|
|
|
} catch (Exception ignored) {
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
TenantHelper.clearDynamic();
|
|
|
|
|
} catch (Exception ignored) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 实际执行生成质检任务的逻辑
|
|
|
|
|
*/
|
|
|
|
|
private String doGenerateInspectionTask(QcInspectionMainTask taskDto) {
|
|
|
|
|
// 1. 参数校验
|
|
|
|
|
if (StringUtils.isBlank(taskDto.getMaterialCode())) {
|
|
|
|
|
throw new ServiceException("物料编码不能为空");
|
|
|
|
|
}
|
|
|
|
|
if (StringUtils.isBlank(taskDto.getInspectionType())) {
|
|
|
|
|
throw new ServiceException("检验类型不能为空");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.info("上位机调用生成质检任务,物料编码: {}, 工位名称: {}, 检验类型: {}",
|
|
|
|
|
taskDto.getMaterialCode(), taskDto.getStationName(), taskDto.getInspectionType());
|
|
|
|
|
|
|
|
|
|
// 2. 匹配质检模板(调用已实现的8级降级匹配策略)
|
|
|
|
|
// FIXME: QcInspectionMainTask.stationName 是工位名称,需要查询工位编码
|
|
|
|
|
// FIXME: 上位机不传工序信息,工序编码传 null
|
|
|
|
|
// TODO: 后期建议上位机直接传递编码,避免跨模块查询
|
|
|
|
|
String materialCode = taskDto.getMaterialCode();
|
|
|
|
|
String stationCode = getStationCodeByName(taskDto.getStationName()); // 根据工位名称查询工位编码
|
|
|
|
|
QcInspectionTemplateVo templateVo = qcInspectionTemplateService.getMatchedTemplate(
|
|
|
|
|
materialCode,
|
|
|
|
|
stationCode, // 工位编码(通过名称查询)
|
|
|
|
|
null, // 工序编码(上位机不传)
|
|
|
|
|
taskDto.getInspectionType()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (templateVo == null) {
|
|
|
|
|
throw new ServiceException("未找到匹配的质检模板,物料编码: " + taskDto.getMaterialCode());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.info("质检模板匹配成功,模板ID: {}, 模板名称: {}", templateVo.getTemplateId(), templateVo.getTemplateName());
|
|
|
|
|
|
|
|
|
|
// 3. 生成质检单号(调用编码规则服务,使用支持租户传递的方法)
|
|
|
|
|
String inspectionNo = remoteCodeRuleService.selectCodeRuleCodeWithTenant("3");
|
|
|
|
|
if (StringUtils.isBlank(inspectionNo)) {
|
|
|
|
|
throw new ServiceException("获取质检单号失败");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. 构建质检主表BO
|
|
|
|
|
QcInspectionMainBo mainBo = buildInspectionMainBo(taskDto, templateVo, inspectionNo);
|
|
|
|
|
|
|
|
|
|
// 5. 插入质检主表
|
|
|
|
|
Boolean insertResult = qcInspectionMainService.insertByBo(mainBo);
|
|
|
|
|
if (!insertResult) {
|
|
|
|
|
throw new ServiceException("质检主表保存失败");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.info("质检主表创建成功,质检单号: {}, 质检ID: {}", inspectionNo, mainBo.getInspectionId());
|
|
|
|
|
|
|
|
|
|
// 6. 根据模板生成质检结果子表
|
|
|
|
|
generateInspectionResults(mainBo.getInspectionId(), templateVo.getTemplateId());
|
|
|
|
|
|
|
|
|
|
log.info("质检结果子表生成成功,共{}个检测项", getTemplateItemCount(templateVo.getTemplateId()));
|
|
|
|
|
|
|
|
|
|
// 7. 返回质检单号
|
|
|
|
|
return inspectionNo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 构建质检主表 BO
|
|
|
|
|
*/
|
|
|
|
|
private QcInspectionMainBo buildInspectionMainBo(QcInspectionMainTask taskDto,
|
|
|
|
|
QcInspectionTemplateVo templateVo,
|
|
|
|
|
String inspectionNo) {
|
|
|
|
|
QcInspectionMainBo mainBo = new QcInspectionMainBo();
|
|
|
|
|
mainBo.setInspectionNo(inspectionNo);
|
|
|
|
|
mainBo.setTemplateId(templateVo.getTemplateId());
|
|
|
|
|
mainBo.setTemplateName(templateVo.getTemplateName());
|
|
|
|
|
mainBo.setInspectionType(templateVo.getTypeId());
|
|
|
|
|
mainBo.setMaterialCode(taskDto.getMaterialCode());
|
|
|
|
|
mainBo.setStationName(taskDto.getStationName());
|
|
|
|
|
mainBo.setInspectionQty(new BigDecimal(taskDto.getInspectionQty()));
|
|
|
|
|
mainBo.setProductionOrder(taskDto.getProductionOrder());
|
|
|
|
|
mainBo.setBatchNo(taskDto.getBatchNo());
|
|
|
|
|
mainBo.setBarcode(taskDto.getBarcode());
|
|
|
|
|
mainBo.setStatus("0"); // 未处理
|
|
|
|
|
mainBo.setResult("0"); // 待判定
|
|
|
|
|
|
|
|
|
|
// 上位机不登录,手动设置审计字段
|
|
|
|
|
mainBo.setCreateBy(HMI_DEFAULT_CREATE_BY);
|
|
|
|
|
mainBo.setCreateTime(new Date());
|
|
|
|
|
return mainBo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 生成质检结果子表
|
|
|
|
|
* <p>
|
|
|
|
|
* 根据模板中的检测项生成对应的质检结果记录
|
|
|
|
|
*/
|
|
|
|
|
private void generateInspectionResults(Long inspectionId, Long templateId) {
|
|
|
|
|
QcTemplateItemBo itemBo = new QcTemplateItemBo();
|
|
|
|
|
itemBo.setTemplateId(templateId);
|
|
|
|
|
List<QcTemplateItemVo> itemList = qcTemplateItemService.queryList(itemBo);
|
|
|
|
|
|
|
|
|
|
for (QcTemplateItemVo item : itemList) {
|
|
|
|
|
QcInspectionResultBo resultBo = new QcInspectionResultBo();
|
|
|
|
|
resultBo.setInspectionId(inspectionId);
|
|
|
|
|
resultBo.setItemId(item.getItemId());
|
|
|
|
|
resultBo.setDetectResult("2"); // 未判定
|
|
|
|
|
resultBo.setItemCode(item.getItemCode());
|
|
|
|
|
resultBo.setItemName(item.getItemName());
|
|
|
|
|
resultBo.setInspectionPosition(item.getInspectionPosition());
|
|
|
|
|
resultBo.setCategoryName(item.getCategoryName());
|
|
|
|
|
resultBo.setDetectType(item.getDetectType());
|
|
|
|
|
resultBo.setControlType(item.getControlType());
|
|
|
|
|
resultBo.setStandardValue(item.getStandardValue());
|
|
|
|
|
resultBo.setUpperLimit(item.getUpperLimit());
|
|
|
|
|
resultBo.setLowerLimit(item.getLowerLimit());
|
|
|
|
|
resultBo.setSpecName(item.getSpecName());
|
|
|
|
|
resultBo.setSpecUpper(item.getSpecUpper());
|
|
|
|
|
resultBo.setSpecLower(item.getSpecLower());
|
|
|
|
|
resultBo.setDescription(item.getDescription());
|
|
|
|
|
resultBo.setTypeId(item.getInspectionType());
|
|
|
|
|
// 上位机不登录,手动设置审计字段
|
|
|
|
|
resultBo.setCreateBy(HMI_DEFAULT_CREATE_BY);
|
|
|
|
|
resultBo.setCreateTime(new Date());
|
|
|
|
|
qcInspectionResultService.insertByBo(resultBo);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取模板检测项数量(用于日志)
|
|
|
|
|
*/
|
|
|
|
|
private int getTemplateItemCount(Long templateId) {
|
|
|
|
|
QcTemplateItemBo itemBo = new QcTemplateItemBo();
|
|
|
|
|
itemBo.setTemplateId(templateId);
|
|
|
|
|
return qcTemplateItemService.queryList(itemBo).size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据工位名称(机台名称)查询工位编码
|
|
|
|
|
* FIXME: 后期需要优化缓存策略,避免每次查询都访问数据库
|
|
|
|
|
* TODO: 考虑在应用启动时缓存工位名称→编码的映射关系
|
|
|
|
|
*
|
|
|
|
|
* @param stationName 工位名称(来自上位机 QcInspectionMainTask.stationName)
|
|
|
|
|
* @return 工位编码,如果查询不到返回 null
|
|
|
|
|
*/
|
|
|
|
|
private String getStationCodeByName(String stationName) {
|
|
|
|
|
if (StringUtils.isBlank(stationName)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
ProdBaseStationInfoBo bo = new ProdBaseStationInfoBo();
|
|
|
|
|
bo.setStationName(stationName);
|
|
|
|
|
List<ProdBaseStationInfoVo> list = prodBaseStationInfoService.queryList(bo);
|
|
|
|
|
return (list != null && !list.isEmpty()) ? list.get(0).getStationCode() : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据工序名称查询工序编码
|
|
|
|
|
* FIXME: 后期需要优化缓存策略,避免每次查询都访问数据库
|
|
|
|
|
* TODO: 考虑在应用启动时缓存工序名称→编码的映射关系
|
|
|
|
|
*
|
|
|
|
|
* @param processName 工序名称
|
|
|
|
|
* @return 工序编码,如果查询不到返回 null
|
|
|
|
|
*/
|
|
|
|
|
private String getProcessCodeByName(String processName) {
|
|
|
|
|
if (StringUtils.isBlank(processName)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
ProdBaseProcessInfoBo bo = new ProdBaseProcessInfoBo();
|
|
|
|
|
bo.setProcessName(processName);
|
|
|
|
|
List<ProdBaseProcessInfoVo> list = prodBaseProcessInfoService.queryList(bo);
|
|
|
|
|
return (list != null && !list.isEmpty()) ? list.get(0).getProcessCode() : null;
|
|
|
|
|
}
|
|
|
|
|
}
|