package cn.iocoder.yudao.module.mes.api.qc;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO;
import cn.iocoder.yudao.module.mes.api.qc.dto.MesQcReportItemRespDTO;
import cn.iocoder.yudao.module.mes.api.qc.dto.MesQcReportRespDTO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
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.iqc.MesQcIqcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcLineDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ipqc.MesQcIpqcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ipqc.MesQcIpqcLineDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcLineDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcLineDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcCheckResultEnum;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcIndicatorItemTypeEnum;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcResultValueTypeEnum;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcStatusEnum;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import cn.iocoder.yudao.module.mes.service.qc.indicator.MesQcIndicatorService;
import cn.iocoder.yudao.module.mes.service.qc.indicatorresult.MesQcIndicatorResultService;
import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcLineService;
import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcService;
import cn.iocoder.yudao.module.mes.service.qc.ipqc.MesQcIpqcLineService;
import cn.iocoder.yudao.module.mes.service.qc.ipqc.MesQcIpqcService;
import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcLineService;
import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcService;
import cn.iocoder.yudao.module.mes.service.qc.rqc.MesQcRqcLineService;
import cn.iocoder.yudao.module.mes.service.qc.rqc.MesQcRqcService;
import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi;
import cn.iocoder.yudao.module.srm.api.supplier.dto.SrmSupplierRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
/**
* MES 质检单数据 API 实现类
*
* 四类质检单的行表结构完全一致(仅父键名不同:iqc_id / ipqc_id / oqc_id / rqc_id),
* 所以「取行」只写四个薄适配方法,映射逻辑一份共用。
*/
@Service
@Validated
public class MesQcReportApiImpl implements MesQcReportApi {
/**
* 组名合并格里「这一格让位给上面那一行」的标记值。
*
* 报告侧把它原样写成 HTML 的 {@code hidden} 属性,所以值本身是什么不重要,
* 重要的是它非空 —— 空串在两侧渲染器里都表示「不输出这个属性」,也就是这一格照常可见。
*/
private static final String GROUP_CELL_HIDDEN = "hidden";
@Resource
private MesQcIqcService iqcService;
@Resource
private MesQcIpqcService ipqcService;
@Resource
private MesQcOqcService oqcService;
@Resource
private MesQcRqcService rqcService;
@Resource
private MesQcIqcLineService iqcLineService;
@Resource
private MesQcIpqcLineService ipqcLineService;
@Resource
private MesQcOqcLineService oqcLineService;
@Resource
private MesQcRqcLineService rqcLineService;
@Resource
private MesQcIndicatorResultService indicatorResultService;
@Resource
private MesQcIndicatorService indicatorService;
@Resource
private MesMdItemService itemService;
@Resource
private MesMdUnitMeasureService unitMeasureService;
@Resource
private MesProWorkOrderService workOrderService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private SrmSupplierApi srmSupplierApi;
@Resource
private CrmCustomerApi customerApi;
@Override
public MesQcReportRespDTO getQcReportData(Integer qcType, Long qcId) {
if (qcType == null || qcId == null) {
return null;
}
MesQcReportRespDTO resp;
List lines;
Long inspectorUserId;
if (Objects.equals(qcType, MesQcTypeEnum.IQC.getType())) {
MesQcIqcDO order = iqcService.getIqc(qcId);
if (order == null) {
return null;
}
resp = common(qcType, order.getId(), order.getCode(), order.getName(), order.getTemplateId(),
order.getSourceDocType(), order.getSourceDocCode(), order.getItemId(), order.getCheckQuantity(),
order.getQualifiedQuantity(), order.getUnqualifiedQuantity(), order.getCheckResult(),
order.getInspectDate(), order.getStatus(), order.getRemark());
resp.setVendorBatch(defaultStr(order.getVendorBatch()));
resp.setVendorName(supplierName(order.getVendorId()));
lines = loadIqcLines(qcId);
inspectorUserId = order.getInspectorUserId();
} else if (Objects.equals(qcType, MesQcTypeEnum.IPQC.getType())) {
MesQcIpqcDO order = ipqcService.getIpqc(qcId);
if (order == null) {
return null;
}
resp = common(qcType, order.getId(), order.getCode(), order.getName(), order.getTemplateId(),
order.getSourceDocType(), order.getSourceDocCode(), order.getItemId(), order.getCheckQuantity(),
order.getQualifiedQuantity(), order.getUnqualifiedQuantity(), order.getCheckResult(),
order.getInspectDate(), order.getStatus(), order.getRemark());
resp.setWorkOrderCode(workOrderCode(order.getWorkOrderId()));
lines = loadIpqcLines(qcId);
inspectorUserId = order.getInspectorUserId();
} else if (Objects.equals(qcType, MesQcTypeEnum.OQC.getType())) {
MesQcOqcDO order = oqcService.getOqc(qcId);
if (order == null) {
return null;
}
resp = common(qcType, order.getId(), order.getCode(), order.getName(), order.getTemplateId(),
order.getSourceDocType(), order.getSourceDocCode(), order.getItemId(), order.getCheckQuantity(),
order.getQualifiedQuantity(), order.getUnqualifiedQuantity(), order.getCheckResult(),
order.getInspectDate(), order.getStatus(), order.getRemark());
resp.setBatchCode(defaultStr(order.getBatchCode()));
resp.setClientName(clientName(order.getClientId()));
lines = loadOqcLines(qcId);
inspectorUserId = order.getInspectorUserId();
} else if (Objects.equals(qcType, MesQcTypeEnum.RQC.getType())) {
MesQcRqcDO order = rqcService.getRqc(qcId);
if (order == null) {
return null;
}
resp = common(qcType, order.getId(), order.getCode(), order.getName(), order.getTemplateId(),
order.getSourceDocType(), order.getSourceDocCode(), order.getItemId(), order.getCheckQuantity(),
order.getQualifiedQuantity(), order.getUnqualifiedQuantity(), order.getCheckResult(),
order.getInspectDate(), order.getStatus(), order.getRemark());
resp.setBatchCode(defaultStr(order.getBatchCode()));
lines = loadRqcLines(qcId);
inspectorUserId = order.getInspectorUserId();
} else {
throw new IllegalArgumentException("暂不支持 qcType=" + qcType);
}
// 物料与单位:单位优先取行上的(检验时实际用的单位),缺失才回退物料的默认单位
MesMdItemDO item = resp.getItemId() == null ? null
: itemService.getItemMap(Collections.singletonList(resp.getItemId())).get(resp.getItemId());
if (item != null) {
resp.setItemCode(defaultStr(item.getCode()));
resp.setItemName(defaultStr(item.getName()));
resp.setItemSpecification(defaultStr(item.getSpecification()));
}
Map unitMap = unitMapOf(lines, item);
if (item != null) {
MesMdUnitMeasureDO itemUnit = unitMap.get(item.getUnitMeasureId());
resp.setUnitName(itemUnit == null ? "" : defaultStr(itemUnit.getName()));
}
resp.setInspectorName(userName(inspectorUserId));
BuiltItems built = buildItems(resp.getQcId(), qcType, lines, unitMap, item);
resp.setItems(built.items());
resp.setChildlessGroupNames(built.childlessGroupNames());
resp.setSynthesizedGroupNames(built.synthesizedGroupNames());
resp.setMissingIndicatorCount(built.missingIndicatorCount());
return resp;
}
/** 四类单据共有的单头字段 */
private MesQcReportRespDTO common(Integer qcType, Long qcId, String code, String name, Long templateId,
Integer sourceDocType, String sourceDocCode, Long itemId, BigDecimal checkQuantity,
BigDecimal qualifiedQuantity, BigDecimal unqualifiedQuantity,
Integer checkResult, LocalDateTime inspectDate, Integer status, String remark) {
return new MesQcReportRespDTO()
.setQcType(qcType)
.setQcTypeName(qcTypeName(qcType))
.setQcId(qcId)
.setQcCode(defaultStr(code))
.setQcName(defaultStr(name))
.setTemplateId(templateId)
.setSourceDocType(sourceDocType)
.setSourceDocCode(defaultStr(sourceDocCode))
.setItemId(itemId)
.setCheckQuantity(checkQuantity)
.setQualifiedQuantity(qualifiedQuantity)
.setUnqualifiedQuantity(unqualifiedQuantity)
.setCheckResult(checkResult)
.setCheckResultText(checkResultName(checkResult))
.setInspectDate(inspectDate)
.setStatus(status)
.setStatusName(statusName(status))
.setFinished(Objects.equals(status, MesQcStatusEnum.FINISHED.getStatus()))
.setRemark(defaultStr(remark));
}
/**
* 检验项 = 「样品 × 指标」一行,按分组结构排好序。
*
* 归属只看指标的 {@code parent_id}:被本单里别的行用 parent_id 指认的分组项就是组头,
* 它自己那一行连同所有子项行合成一组,组名在报告里纵向合并。三处刻意不这么做:
* 不用 {@code result_type IS NULL} 判分组(方向是反的,丢掉的恰好是带公式的组名、
* 留下的恰好是过程参数),不靠行序(行表里组头与它的子项并不相邻),也不把组头丢掉
* (组名与公式是分组检验项最不该丢的两样东西)。
*/
private BuiltItems buildItems(Long qcId, Integer qcType, List lines,
Map unitMap, MesMdItemDO item) {
if (CollUtil.isEmpty(lines)) {
return new BuiltItems(Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), 0);
}
Map indicatorMap = indicatorService.getIndicatorMap(
convertSet(lines, LineRow::indicatorId));
// 样品 → 该样品的实测值明细
Map> detailMap = Collections.emptyMap();
List samples = indicatorResultService.getIndicatorResultListByQcIdAndType(qcId, qcType);
if (CollUtil.isNotEmpty(samples)) {
List details = indicatorResultService.getIndicatorResultDetailListByResultIds(
new ArrayList<>(convertSet(samples, MesQcIndicatorResultDO::getId)));
detailMap = new LinkedHashMap<>();
for (MesQcIndicatorResultDetailDO detail : details) {
detailMap.computeIfAbsent(detail.getResultId(), k -> new ArrayList<>()).add(detail);
}
}
// ① 行 → 指标;指标被删掉的行还原不出任何东西,只计数
List liveLines = new ArrayList<>(lines.size());
List liveIndicators = new ArrayList<>(lines.size());
int missingIndicatorCount = 0;
for (LineRow line : lines) {
MesQcIndicatorDO indicator = indicatorMap.get(line.indicatorId());
if (indicator == null) {
missingIndicatorCount++;
continue;
}
liveLines.add(line);
liveIndicators.add(indicator);
}
// ② 组头指标可能压根没排进本单(模板只选了子项),补取出来,否则整组的组名与公式会丢
Map allIndicators = new LinkedHashMap<>(indicatorMap);
Set extraIds = new LinkedHashSet<>();
for (MesQcIndicatorDO indicator : liveIndicators) {
Long parentId = indicator.getParentId();
if (parentId != null && parentId != 0 && !allIndicators.containsKey(parentId)) {
extraIds.add(parentId);
}
}
if (CollUtil.isNotEmpty(extraIds)) {
allIndicators.putAll(indicatorService.getIndicatorMap(extraIds));
}
// ③ 每一行归到「组头自己的行」或「某个组的子项行」
Map> ownLinesOf = new LinkedHashMap<>();
Map> childLinesOf = new LinkedHashMap<>();
for (int i = 0; i < liveLines.size(); i++) {
Long headId = groupHeadId(liveIndicators.get(i), allIndicators);
if (headId == null) {
ownLinesOf.computeIfAbsent(liveLines.get(i).indicatorId(), k -> new ArrayList<>()).add(i);
} else {
childLinesOf.computeIfAbsent(headId, k -> new ArrayList<>()).add(i);
}
}
// ④ 顶层条目 = 组 + 独立项;组头那一行从独立项里摘出来当作组的锚点行
List childlessGroupNames = new ArrayList<>();
List synthesizedGroupNames = new ArrayList<>();
List entries = new ArrayList<>();
for (Map.Entry> each : childLinesOf.entrySet()) {
Long headId = each.getKey();
MesQcIndicatorDO head = allIndicators.get(headId);
List children = sortByIndicatorOrder(each.getValue(), liveIndicators);
List own = ownLinesOf.remove(headId);
if (CollUtil.isEmpty(own)) {
synthesizedGroupNames.add(defaultStr(head.getName()));
}
entries.add(new Entry(head, own == null ? Collections.emptyList() : own, children,
children.get(0)));
}
for (Map.Entry> each : ownLinesOf.entrySet()) {
MesQcIndicatorDO indicator = allIndicators.get(each.getKey());
if (MesQcIndicatorItemTypeEnum.isGroup(indicator.getItemType())) {
// 指标主数据里是分组项,本单却没有属于它的子项:照常出成独立检验项,只是组不起来
childlessGroupNames.add(defaultStr(indicator.getName()));
}
entries.add(new Entry(indicator, each.getValue(), Collections.emptyList(), each.getValue().get(0)));
}
// 排序号缺失的排最后;同值保持行序(List.sort 稳定,比较键里再带上首行下标兜底)
entries.sort(Comparator.comparingInt((Entry entry) -> sortKey(entry.indicator()))
.thenComparingInt(Entry::firstLine));
// ⑤ 逐条目产出行,并把分组信息下发到每一行
List items = new ArrayList<>();
int index = 0;
for (Entry entry : entries) {
List groupRows = new ArrayList<>();
for (Integer lineIndex : entry.ownLines()) {
groupRows.addAll(expandRows(liveIndicators.get(lineIndex), liveLines.get(lineIndex),
samples, detailMap, unitMap, item));
}
boolean grouped = CollUtil.isNotEmpty(entry.childLines());
if (grouped && groupRows.isEmpty()) {
// 组头没有自己的行:补一行,否则组名与公式无处可放
groupRows.add(toItem(entry.indicator(), emptyLine(entry.indicator()), "", "", "", unitMap, item));
}
int anchorCount = groupRows.size();
List entryRows = new ArrayList<>(groupRows);
for (Integer lineIndex : entry.childLines()) {
for (MesQcReportItemRespDTO row : expandRows(liveIndicators.get(lineIndex), liveLines.get(lineIndex),
samples, detailMap, unitMap, item)) {
row.setGroupChildName(defaultStr(liveIndicators.get(lineIndex).getName()));
entryRows.add(row);
}
}
int span = grouped ? entryRows.size() : 1;
for (int i = 0; i < entryRows.size(); i++) {
MesQcReportItemRespDTO row = entryRows.get(i);
row.setIndex(++index);
// 组名的合并格只挂在组的第一行,组内其余行整格隐藏、让位给它
row.setGroupSpan(i == 0 ? span : 1);
row.setGroupHidden(i == 0 ? "" : GROUP_CELL_HIDDEN);
row.setRequirement(i < anchorCount
? anchorRequirement(entry.indicator(), row)
: defaultStr(row.getStandardValue()));
items.add(row);
}
}
return new BuiltItems(items, childlessGroupNames, synthesizedGroupNames, missingIndicatorCount);
}
/** 装配结果:检验项本身,以及没能成组、被补了行而必须让调用方知道的那些 */
private record BuiltItems(List items, List childlessGroupNames,
List synthesizedGroupNames, int missingIndicatorCount) {
}
/**
* 一个顶层条目:一个分组(组头指标 + 它的子项行),或一个独立检验项。
*
* @param indicator 组头的指标 / 独立项的指标
* @param ownLines 组头自己那些行(独立项时就是它全部的行)在行表里的下标
* @param childLines 组内子项行的下标,独立项恒为空
* @param firstLine 条目第一行在行表里的下标,排序号相同时靠它保持行序
*/
private record Entry(MesQcIndicatorDO indicator, List ownLines, List childLines,
int firstLine) {
}
/**
* 这一行的组头指标编号:{@code parent_id} 指向一个「分组项」才算数。
*
* 指向录入项属于指标主数据配置有问题,不能拿它去合并单元格;
* 指向的指标已被删除同样不成组。
*/
private static Long groupHeadId(MesQcIndicatorDO indicator, Map allIndicators) {
Long parentId = indicator.getParentId();
if (parentId == null || parentId == 0) {
return null;
}
MesQcIndicatorDO head = allIndicators.get(parentId);
return head != null && MesQcIndicatorItemTypeEnum.isGroup(head.getItemType()) ? parentId : null;
}
/** 组头的「检测要求」放公式(分组项只有公式、没有规格),没配公式就照常放标准要求 */
private static String anchorRequirement(MesQcIndicatorDO head, MesQcReportItemRespDTO row) {
String formula = defaultStr(head.getFormulaText());
return formula.isEmpty() ? defaultStr(row.getStandardValue()) : formula;
}
/** 组头没排进本单时补出来的那一行:只有指标本身,没有单据行 */
private static LineRow emptyLine(MesQcIndicatorDO head) {
return new LineRow(head.getId(), head.getTool(), null, null, null, null, null, null);
}
/**
* 一个检验项摊平成若干行:多样品、一个指标被录了多次实测值都会摊平。
* 一行都没有(尚未录入实测值)时仍出一行,避免报告漏项。
*/
private List expandRows(MesQcIndicatorDO indicator, LineRow line,
List samples,
Map> detailMap,
Map unitMap, MesMdItemDO item) {
List rows = new ArrayList<>();
for (MesQcIndicatorResultDO sample : samples) {
for (MesQcIndicatorResultDetailDO detail : detailMap.getOrDefault(sample.getId(),
Collections.emptyList())) {
if (Objects.equals(detail.getIndicatorId(), line.indicatorId())) {
rows.add(toItem(indicator, line, sample.getCode(), detail.getValue(), detail.getRemark(),
unitMap, item));
}
}
}
if (rows.isEmpty()) {
rows.add(toItem(indicator, line, "", "", "", unitMap, item));
}
return rows;
}
/** 排序键:指标没设排序号的一律排最后 */
private static int sortKey(MesQcIndicatorDO indicator) {
Integer sortOrder = indicator.getSortOrder();
return sortOrder == null ? Integer.MAX_VALUE : sortOrder;
}
/** 按各行的指标排序号排,同值保持行序 */
private static List sortByIndicatorOrder(List lineIndexes,
List indicators) {
List sorted = new ArrayList<>(lineIndexes);
sorted.sort(Comparator.comparingInt((Integer i) -> sortKey(indicators.get(i)))
.thenComparingInt(Integer::intValue));
return sorted;
}
private MesQcReportItemRespDTO toItem(MesQcIndicatorDO indicator, LineRow line, String sampleCode, String value,
String detailRemark, Map unitMap,
MesMdItemDO item) {
MesMdUnitMeasureDO unit = unitMap.get(line.unitMeasureId());
if (unit == null && item != null) {
unit = unitMap.get(item.getUnitMeasureId());
}
return new MesQcReportItemRespDTO()
.setIndicatorId(indicator.getId())
.setIndicatorCode(defaultStr(indicator.getCode()))
.setIndicatorName(defaultStr(indicator.getName()))
.setCheckMethod(defaultStr(line.checkMethod()))
.setTool(defaultStr(line.tool()))
.setStandardValue(plain(line.standardValue()))
.setActualValue(defaultStr(value))
.setUnit(unit == null ? "" : defaultStr(unit.getName()))
.setUpperLimit(line.maxThreshold())
.setLowerLimit(line.minThreshold())
.setValueType(indicator.getResultType())
.setValueTypeName(valueTypeName(indicator.getResultType()))
.setSampleNo(defaultStr(sampleCode))
// 默认「不是子项、不参与合并」;真子项由装配那一步改写
.setGroupChildName("")
.setGroupSpan(1)
.setGroupHidden("")
.setRemark(mergeRemark(line.remark(), detailRemark));
}
// ==================== 取行(四类同构) ====================
private List loadIqcLines(Long qcId) {
List lines = iqcLineService.getIqcLineListByIqcId(qcId);
if (CollUtil.isEmpty(lines)) {
return Collections.emptyList();
}
List rows = new ArrayList<>(lines.size());
for (MesQcIqcLineDO l : lines) {
rows.add(new LineRow(l.getIndicatorId(), l.getTool(), l.getCheckMethod(), l.getStandardValue(),
l.getUnitMeasureId(), l.getMaxThreshold(), l.getMinThreshold(), l.getRemark()));
}
return rows;
}
private List loadIpqcLines(Long qcId) {
List lines = ipqcLineService.getIpqcLineListByIpqcId(qcId);
if (CollUtil.isEmpty(lines)) {
return Collections.emptyList();
}
List rows = new ArrayList<>(lines.size());
for (MesQcIpqcLineDO l : lines) {
rows.add(new LineRow(l.getIndicatorId(), l.getTool(), l.getCheckMethod(), l.getStandardValue(),
l.getUnitMeasureId(), l.getMaxThreshold(), l.getMinThreshold(), l.getRemark()));
}
return rows;
}
private List loadOqcLines(Long qcId) {
List lines = oqcLineService.getOqcLineListByOqcId(qcId);
if (CollUtil.isEmpty(lines)) {
return Collections.emptyList();
}
List rows = new ArrayList<>(lines.size());
for (MesQcOqcLineDO l : lines) {
rows.add(new LineRow(l.getIndicatorId(), l.getTool(), l.getCheckMethod(), l.getStandardValue(),
l.getUnitMeasureId(), l.getMaxThreshold(), l.getMinThreshold(), l.getRemark()));
}
return rows;
}
private List loadRqcLines(Long qcId) {
List lines = rqcLineService.getRqcLineListByRqcId(qcId);
if (CollUtil.isEmpty(lines)) {
return Collections.emptyList();
}
List rows = new ArrayList<>(lines.size());
for (MesQcRqcLineDO l : lines) {
rows.add(new LineRow(l.getIndicatorId(), l.getTool(), l.getCheckMethod(), l.getStandardValue(),
l.getUnitMeasureId(), l.getMaxThreshold(), l.getMinThreshold(), l.getRemark()));
}
return rows;
}
// ==================== 关联名称 ====================
private Map unitMapOf(List lines, MesMdItemDO item) {
List unitIds = new ArrayList<>();
for (LineRow line : lines) {
if (line.unitMeasureId() != null) {
unitIds.add(line.unitMeasureId());
}
}
if (item != null && item.getUnitMeasureId() != null) {
unitIds.add(item.getUnitMeasureId());
}
return unitIds.isEmpty() ? Collections.emptyMap() : unitMeasureService.getUnitMeasureMap(unitIds);
}
private String supplierName(Long vendorId) {
if (vendorId == null) {
return "";
}
List suppliers = srmSupplierApi.getSupplierList(Collections.singleton(vendorId))
.getCheckedData();
return CollUtil.isEmpty(suppliers) ? "" : defaultStr(suppliers.get(0).getName());
}
private String clientName(Long clientId) {
if (clientId == null) {
return "";
}
CrmCustomerRespDTO client = customerApi.getCustomerMap(Collections.singleton(clientId)).get(clientId);
return client == null ? "" : defaultStr(client.getName());
}
private String workOrderCode(Long workOrderId) {
if (workOrderId == null) {
return "";
}
MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(workOrderId);
return workOrder == null ? "" : defaultStr(workOrder.getCode());
}
private String userName(Long userId) {
if (userId == null) {
return "";
}
AdminUserRespDTO user = adminUserApi.getUserMap(Collections.singleton(userId)).get(userId);
return user == null ? "" : defaultStr(user.getNickname());
}
// ==================== 小工具 ====================
/** 去掉无意义的小数尾零,避免报告里出现 5.0000 这种由数据库精度带出来的尾巴 */
private static String plain(BigDecimal value) {
return value == null ? "" : value.stripTrailingZeros().toPlainString();
}
private static String mergeRemark(String lineRemark, String detailRemark) {
if (StrUtil.isBlank(lineRemark)) {
return defaultStr(detailRemark);
}
return StrUtil.isBlank(detailRemark) ? lineRemark : lineRemark + ";" + detailRemark;
}
private static String defaultStr(String value) {
return value == null ? "" : value;
}
private static String qcTypeName(Integer qcType) {
for (MesQcTypeEnum e : MesQcTypeEnum.values()) {
if (Objects.equals(e.getType(), qcType)) {
return e.getName();
}
}
return "";
}
private static String statusName(Integer status) {
for (MesQcStatusEnum e : MesQcStatusEnum.values()) {
if (Objects.equals(e.getStatus(), status)) {
return e.getName();
}
}
return "";
}
private static String checkResultName(Integer checkResult) {
for (MesQcCheckResultEnum e : MesQcCheckResultEnum.values()) {
if (Objects.equals(e.getType(), checkResult)) {
return e.getName();
}
}
return "";
}
private static String valueTypeName(Integer resultType) {
for (MesQcResultValueTypeEnum e : MesQcResultValueTypeEnum.values()) {
if (Objects.equals(e.getType(), resultType)) {
return e.getName();
}
}
return "";
}
/** 四类行表字段完全一致,抽成统一形态后共用映射 */
private record LineRow(Long indicatorId, String tool, String checkMethod, BigDecimal standardValue,
Long unitMeasureId, BigDecimal maxThreshold, BigDecimal minThreshold, String remark) {
}
}