package cn.iocoder.yudao.module.qcreport.engine.context;
|
|
import cn.iocoder.yudao.module.qcreport.engine.JsValues;
|
|
import java.util.ArrayList;
|
import java.util.List;
|
import java.util.Map;
|
|
/**
|
* 报告上下文与「数据快照」之间的编解码。
|
* <p>
|
* 报告实例落库时要冻结一份 {@code data_snapshot},保证历史报告不因业务数据变化而改变。
|
* 快照就是 {@link ReportContext#toScope()} 那份结构({@code {report, inspectionItems}}),
|
* 能直接交给 JSON 列的类型处理器;反过来要重新生成时,得把它读回 {@link ReportContext}。
|
* <p>
|
* <b>为什么手写 fromMap 而不用 Jackson 反序列化:</b>一是与引擎其余部分风格一致
|
* ({@link ReportContext#reportMap()}、{@link ReportContext#itemMap} 都是显式字段清单,
|
* 加字段时改一处就能看出来);二是这个模块里 Jackson 3(JSON 列类型处理器)与 Jackson 2
|
* (Spring 请求体)并存,靠反射映射等于把「取哪套配置」变成一件碰运气的事。
|
* 这里是历史报告能否复现的唯一路径,值得写死。
|
*/
|
public final class ReportContextCodec {
|
|
private static final String REPORT = "report";
|
private static final String INSPECTION_ITEMS = "inspectionItems";
|
|
private ReportContextCodec() {
|
}
|
|
/**
|
* 上下文 → 快照。
|
* <p>
|
* 传进来的通常已经是判定后的上下文({@code RenderOutcome.context()}),
|
* 冻结的应当是「产出这份 HTML 的那份数据」,包含算好的 PASS/FAIL 与合格率。
|
*/
|
public static Map<String, Object> toMap(ReportContext context) {
|
return context == null ? ReportContext.empty().toScope() : context.toScope();
|
}
|
|
/** 快照 → 上下文。缺字段、类型不对都按「这个字段没有值」处理,不抛异常 */
|
public static ReportContext fromMap(Map<String, Object> snapshot) {
|
ReportContext context = ReportContext.empty();
|
if (snapshot == null) {
|
return context;
|
}
|
if (snapshot.get(REPORT) instanceof Map<?, ?> report) {
|
context.setReport(reportOf(report));
|
}
|
if (snapshot.get(INSPECTION_ITEMS) instanceof List<?> items) {
|
context.setInspectionItems(itemsOf(items));
|
}
|
return context;
|
}
|
|
private static ReportFields reportOf(Map<?, ?> report) {
|
return new ReportFields()
|
.setReportNo(text(report.get("reportNo")))
|
.setReportName(text(report.get("reportName")))
|
.setSampleNo(text(report.get("sampleNo")))
|
.setProductCode(text(report.get("productCode")))
|
.setProductName(text(report.get("productName")))
|
.setSpec(text(report.get("spec")))
|
.setBatchNo(text(report.get("batchNo")))
|
.setWorkOrderNo(text(report.get("workOrderNo")))
|
.setInspectType(text(report.get("inspectType")))
|
.setInspector(text(report.get("inspector")))
|
.setInspectDate(text(report.get("inspectDate")))
|
.setDepartment(text(report.get("department")))
|
.setCustomerName(text(report.get("customerName")))
|
.setSupplierName(text(report.get("supplierName")))
|
.setResult(text(report.get("result")))
|
.setResultText(text(report.get("resultText")))
|
.setConclusion(text(report.get("conclusion")))
|
.setPassRate(text(report.get("passRate")))
|
.setQcResult(text(report.get("qcResult")))
|
.setQcResultText(text(report.get("qcResultText")))
|
.setTotal(integer(report.get("total")))
|
.setPassCount(integer(report.get("passCount")))
|
.setFailCount(integer(report.get("failCount")));
|
}
|
|
private static List<InspectionItem> itemsOf(List<?> items) {
|
List<InspectionItem> result = new ArrayList<>(items.size());
|
for (Object element : items) {
|
if (element instanceof Map<?, ?> item) {
|
result.add(itemOf(item));
|
}
|
}
|
return result;
|
}
|
|
private static InspectionItem itemOf(Map<?, ?> item) {
|
return new InspectionItem()
|
.setIndex(integer(item.get("index")))
|
.setItemCode(text(item.get("itemCode")))
|
.setItemName(text(item.get("itemName")))
|
.setStandardValue(text(item.get("standardValue")))
|
.setActualValue(text(item.get("actualValue")))
|
.setUnit(text(item.get("unit")))
|
.setRequirement(text(item.get("requirement")))
|
.setCheckMethod(text(item.get("checkMethod")))
|
.setGroup(item.get("group") instanceof Map<?, ?> group ? groupOf(group) : null)
|
.setUpperLimit(decimal(item.get("upperLimit")))
|
.setLowerLimit(decimal(item.get("lowerLimit")))
|
.setResult(text(item.get("result")))
|
.setResultText(text(item.get("resultText")))
|
.setRemark(text(item.get("remark")));
|
}
|
|
/** 分组信息回读:丢了它,历史报告重出时组名就跨不住整组了 */
|
private static InspectionItem.Group groupOf(Map<?, ?> group) {
|
return new InspectionItem.Group()
|
.setChildName(text(group.get("childName")))
|
.setSpan(span(group.get("span")))
|
.setHidden(text(group.get("hidden")));
|
}
|
|
/** 文本字段:null 得空串。与 {@link ReportFields} 的默认值语义一致——空串是「没值」,不是「缺字段」 */
|
private static String text(Object value) {
|
return JsValues.asText(value);
|
}
|
|
private static int integer(Object value) {
|
return value instanceof Number number ? number.intValue() : 0;
|
}
|
|
/** 合并行数取不到值时按 1 算:0 会渲染成 rowspan="0",把整行的列对齐搞坏 */
|
private static int span(Object value) {
|
return value instanceof Number number ? number.intValue() : 1;
|
}
|
|
/** 上下限可以为空(没有规格区间),空值要原样保留成 null,不能变成 0 */
|
private static Double decimal(Object value) {
|
return value instanceof Number number ? number.doubleValue() : null;
|
}
|
|
}
|