feat(mes): 新增混炼追溯报表功能模块

- 在 MixTraceListVo 中新增实际重量、检验结果、累计车次、计划明细状态字段
- 在 MixTraceSummaryVo 中新增检验结果和配方时间字段
- 在 MixTraceUsageVo 中新增配方ID、计划明细ID、重量ID等相关字段
- 修改数据库查询逻辑,添加默认值处理和条件筛选功能
- 新增车次范围查询条件支持
- 完善物料追溯匹配逻辑,增加多层级匹配优先级
- 优化料罐类型显示逻辑,支持字典翻译和回退机制
- 添加租户ID过滤支持,增强数据隔离能力
- 完善动作类型和状态的分类逻辑,支持自动识别卸料和称量操作
master
zangch@mesnac.com 2 days ago
parent df3b293a9e
commit 534cc43806

@ -144,10 +144,26 @@ public class MixTraceListVo implements Serializable {
@ExcelProperty(value = "完成数")
private BigDecimal completeAmount;
/** 实际重量/实量 */
@ExcelProperty(value = "实量")
private BigDecimal realWeight;
/** 检验结果 */
@ExcelProperty(value = "检验结果")
private String testResult;
/** 车次 */
@ExcelProperty(value = "密炼车次")
private Long trainNumber;
/** 累计车次 */
@ExcelProperty(value = "累计车次")
private Long totalTrainNo;
/** 计划明细状态 */
@ExcelProperty(value = "生产状态")
private String planDetailStatus;
/** 实际开始时间 */
@ExcelProperty(value = "开始生产时间")
private Date realBeginTime;

@ -49,6 +49,9 @@ public class MixTraceSummaryVo implements Serializable {
private BigDecimal dischargeEnergy;
private String mixingStatus;
private String testResult;
private Long recipeTime;
private Long mixingTime;
private Long consumeTime;
private Long intervalTime;

@ -16,8 +16,18 @@ public class MixTraceUsageVo implements Serializable {
private static final long serialVersionUID = 1L;
private Long usageId;
private Long recipeId;
private Long planDetailId;
private Long weightId;
private Long weightSeq;
private Integer traceWeightSeq;
private Long traceWeightId;
private String traceActCode;
private Long traceRowCount;
private String jarTypeName;
private String categoryName;
private Long wareNum;
private String weighNum;
private String materialName;
private BigDecimal setWeight;
@ -30,5 +40,6 @@ public class MixTraceUsageVo implements Serializable {
private String actCode;
private String actName;
private String actionType;
private String actionStatus;
}

@ -67,6 +67,7 @@ public class ProdMixTraceReportServiceImpl implements IProdMixTraceReportService
// 入口参数统一规范后,再注入 recipeId避免前端同名空值覆盖
Map<String, Object> detailParams = safeParams(params);
detailParams.put("recipeId", recipeId);
detailParams.put("tenantId", TenantHelper.getTenantId());
// 先查主信息,主信息不存在时不继续查询子表,避免无意义 IO
MixTraceListVo recipeInfo = mixTraceReportMapper.selectTraceRecipeInfo(detailParams);

@ -7,9 +7,9 @@
<sql id="routerMasterDataCte">
WITH router_master_data AS (
SELECT
MAX(CASE WHEN REPLACE(sm.query_param, ' ', '') LIKE '%"router":"act"%' THEN sm.master_data_id END) AS act_master_data_id,
MAX(CASE WHEN REPLACE(sm.query_param, ' ', '') LIKE '%"router":"term"%' THEN sm.master_data_id END) AS term_master_data_id,
MAX(CASE WHEN REPLACE(sm.query_param, ' ', '') LIKE '%"router":"jar"%' THEN sm.master_data_id END) AS jar_master_data_id
COALESCE(MAX(CASE WHEN REPLACE(sm.query_param, ' ', '') LIKE '%"router":"act"%' THEN sm.master_data_id END), 10009) AS act_master_data_id,
COALESCE(MAX(CASE WHEN REPLACE(sm.query_param, ' ', '') LIKE '%"router":"term"%' THEN sm.master_data_id END), 10010) AS term_master_data_id,
COALESCE(MAX(CASE WHEN REPLACE(sm.query_param, ' ', '') LIKE '%"router":"jar"%' THEN sm.master_data_id END), 10011) AS jar_master_data_id
FROM sys_master_data sm
WHERE sm.del_flag = '0'
AND sm.active_flag = '1'
@ -84,6 +84,12 @@
OR LTRIM(RTRIM(COALESCE(ppd.material_barcode, ''))) = LTRIM(RTRIM(#{map.productionBarcode}))
)
</if>
<if test="map.trainNumberStart != null and map.trainNumberStart != ''">
AND ppd.train_number &gt;= #{map.trainNumberStart}
</if>
<if test="map.trainNumberEnd != null and map.trainNumberEnd != ''">
AND ppd.train_number &lt;= #{map.trainNumberEnd}
</if>
ORDER BY
CASE
WHEN #{map.planDetailId} IS NOT NULL
@ -140,17 +146,31 @@
tp.classTeamName AS classTeamName,
tp.planAmount AS planAmount,
COALESCE(tp.detailCompleteAmount, tp.planCompleteAmount) AS completeAmount,
COALESCE(tp.detailCompleteAmount, tp.planCompleteAmount) AS realWeight,
CAST(NULL AS VARCHAR(64)) AS testResult,
tp.trainNumber AS trainNumber,
train_stat.totalTrainNo AS totalTrainNo,
tp.planDetailStatus AS planDetailStatus,
COALESCE(tp.detailRealBeginTime, tp.planRealBeginTime) AS realBeginTime,
COALESCE(tp.detailRealEndTime, tp.planRealEndTime) AS realEndTime
FROM prod_recipe_info ri
LEFT JOIN prod_base_machine_info bm ON bm.machine_id = ri.machine_id
LEFT JOIN base_material_info bmi ON bmi.material_id = ri.material_id
<include refid="tracePickApply"/>
OUTER APPLY (
SELECT CAST(COUNT(1) AS BIGINT) AS totalTrainNo
FROM prod_product_plan_detail_1 d
WHERE tp.planId IS NOT NULL
AND d.plan_id = tp.planId
AND ISNULL(d.del_flag, '0') = '0'
) train_stat
WHERE ri.del_flag = '0'
<if test="map.recipeCode != null and map.recipeCode != ''">
AND ri.recipe_code LIKE CONCAT('%', #{map.recipeCode}, '%')
</if>
<if test="map.recipeId != null and map.recipeId != ''">
AND ri.recipe_id = #{map.recipeId}
</if>
<if test="map.machineId != null and map.machineId != ''">
AND ri.machine_id = #{map.machineId}
</if>
@ -190,6 +210,8 @@
or (map.planDetailId != null and map.planDetailId != '')
or (map.planDetailCode != null and map.planDetailCode != '')
or (map.productionBarcode != null and map.productionBarcode != '')
or (map.trainNumberStart != null and map.trainNumberStart != '')
or (map.trainNumberEnd != null and map.trainNumberEnd != '')
or (map.shiftId != null and map.shiftId != '')
or (map.classTeamId != null and map.classTeamId != '')
or (map.shiftName != null and map.shiftName != '')
@ -296,6 +318,8 @@
mix_last.dischargePower AS dischargePower,
mix_last.dischargeEnergy AS dischargeEnergy,
tp.planDetailStatus AS mixingStatus,
CAST(NULL AS VARCHAR(64)) AS testResult,
ri.done_time AS recipeTime,
mix_sum.totalMixingTime AS mixingTime,
CASE
WHEN tp.detailRealBeginTime IS NOT NULL AND tp.detailRealEndTime IS NOT NULL
@ -388,9 +412,12 @@
w.father_code AS fatherCode,
w.unit_id AS unitId,
w.child_code AS childCode,
bmi.material_name AS materialName,
w.if_use_bat AS ifUseBat,
w.max_rate AS maxRate
FROM prod_recipe_weight w
LEFT JOIN base_material_info bmi ON bmi.material_id = w.child_code
AND bmi.tenant_id = w.tenant_id
CROSS JOIN router_master_data rmd
OUTER APPLY (
SELECT TOP 1 d.data_detail_name
@ -411,8 +438,23 @@
<include refid="routerMasterDataCte"/>
SELECT
w.weight_id AS usageId,
w.recipe_id AS recipeId,
trace_used.tracePlanDetailId AS planDetailId,
w.weight_id AS weightId,
w.weight_seq AS weightSeq,
RTRIM(LTRIM(COALESCE(w.weight_type, ''))) AS categoryName,
trace_used.traceWeightSeq AS traceWeightSeq,
trace_used.traceWeightId AS traceWeightId,
trace_used.traceActCode AS traceActCode,
trace_used.traceRowCount AS traceRowCount,
-- 类别展示三层兜底jar主数据名称 -> 机台罐型编码 -> 配方weight_type
jar_detail.jarTypeName AS jarTypeName,
COALESCE(
NULLIF(RTRIM(LTRIM(COALESCE(jar_detail.jarTypeName, ''))), ''),
NULLIF(RTRIM(LTRIM(COALESCE(CONVERT(VARCHAR(64), jar_ref.tankTypeCode), ''))), ''),
RTRIM(LTRIM(COALESCE(w.weight_type, '')))
) AS categoryName,
jar_ref.wareNum AS wareNum,
jar_ref.weighNum AS weighNum,
COALESCE(cm.material_name, fm.material_name, act_detail.data_detail_name, RTRIM(LTRIM(COALESCE(w.act_code, '')))) AS materialName,
w.set_weight AS setWeight,
trace_used.actualWeight AS actualWeight,
@ -431,11 +473,58 @@
CASE
WHEN act_detail.data_detail_name IS NULL OR LTRIM(RTRIM(act_detail.data_detail_name)) = '' THEN RTRIM(LTRIM(COALESCE(w.act_code, '')))
ELSE RTRIM(LTRIM(act_detail.data_detail_name))
END AS actName
END AS actName,
-- 动作类型严格二值:卸料/称量ELSE 必须回落称量)
CASE
WHEN CHARINDEX(N'卸', COALESCE(act_detail.data_detail_name, '')) > 0 THEN N'卸料'
WHEN RTRIM(LTRIM(COALESCE(w.act_code, ''))) IN ('11', '12') THEN N'卸料'
ELSE N'称量'
END AS actionType,
-- 称量状态优先级:卸料强制覆盖 -> 自动/手动 -> '-'
CASE
WHEN CHARINDEX(N'卸', COALESCE(act_detail.data_detail_name, '')) > 0
OR RTRIM(LTRIM(COALESCE(w.act_code, ''))) IN ('11', '12') THEN N'卸料'
WHEN RTRIM(LTRIM(COALESCE(w.if_use_bat, ''))) = '1' THEN N'自动'
WHEN RTRIM(LTRIM(COALESCE(w.if_use_bat, ''))) = '0' THEN N'手动'
ELSE '-'
END AS actionStatus
FROM prod_recipe_weight w
CROSS JOIN router_master_data rmd
LEFT JOIN prod_recipe_info ri ON ri.recipe_id = w.recipe_id AND ISNULL(ri.del_flag, '0') = '0'
LEFT JOIN base_material_info cm ON cm.material_id = w.child_code
LEFT JOIN base_material_info fm ON fm.material_id = w.father_code
-- 机台+物料映射料罐,子料优先,再按料仓优先级与仓位号稳定选择
OUTER APPLY (
SELECT TOP 1
td.ware_num AS wareNum,
td.weigh_num AS weighNum,
tt.tank_type AS tankTypeCode
FROM prod_machine_tank_detail td
LEFT JOIN prod_machine_tank_type tt
ON tt.tank_type_id = td.tank_type_id
AND ISNULL(tt.del_flag, '0') = '0'
WHERE ISNULL(td.del_flag, '0') = '0'
AND RTRIM(LTRIM(CONVERT(VARCHAR(64), td.machine_id))) = RTRIM(LTRIM(CONVERT(VARCHAR(64), ri.machine_id)))
AND td.material_id IN (w.child_code, w.father_code)
<if test="map.tenantId != null and map.tenantId != ''">
AND td.tenant_id = #{map.tenantId}
</if>
ORDER BY
CASE WHEN td.material_id = w.child_code THEN 0 ELSE 1 END,
ISNULL(td.mater_priority, 999999),
td.ware_num
) jar_ref
-- jar 主数据字典翻译:优先用罐型编码,缺失时回退 weight_type
OUTER APPLY (
SELECT TOP 1 d.data_detail_name AS jarTypeName
FROM sys_master_data_detail d
WHERE d.del_flag = '0'
AND d.active_flag = '1'
AND d.master_data_id = rmd.jar_master_data_id
AND RTRIM(LTRIM(COALESCE(d.data_detail_code, ''))) =
RTRIM(LTRIM(COALESCE(CONVERT(VARCHAR(64), jar_ref.tankTypeCode), CONVERT(VARCHAR(64), w.weight_type), '')))
ORDER BY d.master_data_detail_id DESC
) jar_detail
OUTER APPLY (
SELECT TOP 1 d.data_detail_name
FROM sys_master_data_detail d
@ -447,40 +536,150 @@
) act_detail
<if test="map.productionBarcode != null and map.productionBarcode != ''">
OUTER APPLY (
SELECT
CASE
WHEN ISNULL(SUM(CASE WHEN t.materiel_id = w.child_code THEN 1 ELSE 0 END), 0) = 0
AND ISNULL(SUM(CASE
WHEN w.father_code IS NOT NULL
AND w.father_code &lt;&gt; w.child_code
AND t.materiel_id = w.father_code THEN 1
ELSE 0
END), 0) = 0
THEN NULL
ELSE
ISNULL(SUM(CASE WHEN t.materiel_id = w.child_code THEN ISNULL(t.used_amount, 0) ELSE 0 END), 0)
+ ISNULL(SUM(CASE
WHEN w.father_code IS NOT NULL
AND w.father_code &lt;&gt; w.child_code
AND t.materiel_id = w.father_code
THEN ISNULL(t.used_amount, 0)
ELSE 0
END), 0)
END AS actualWeight
FROM prod_trace_cur_info t
WHERE t.production_barcode = #{map.productionBarcode}
AND (
t.materiel_id = w.child_code
OR (
w.father_code IS NOT NULL
AND w.father_code &lt;&gt; w.child_code
AND t.materiel_id = w.father_code
SELECT TOP 1
matched.actualWeight,
matched.tracePlanDetailId,
matched.traceRecipeId,
matched.traceWeightSeq,
matched.traceWeightId,
matched.traceActCode,
matched.traceRowCount
FROM (
SELECT
CAST(1 AS INT) AS matchPriority,
CAST(SUM(ISNULL(t.used_amount, 0)) AS DECIMAL(18, 4)) AS actualWeight,
MAX(t.plan_detail_id) AS tracePlanDetailId,
MAX(t.recipe_id) AS traceRecipeId,
MAX(t.weight_seq) AS traceWeightSeq,
MAX(t.weight_id) AS traceWeightId,
MAX(RTRIM(LTRIM(COALESCE(t.act_code, '')))) AS traceActCode,
CAST(COUNT(1) AS BIGINT) AS traceRowCount
FROM prod_trace_cur_info t
WHERE t.production_barcode = #{map.productionBarcode}
<if test="map.tenantId != null and map.tenantId != ''">
AND t.tenant_id = #{map.tenantId}
</if>
<if test="map.planDetailId != null and map.planDetailId != ''">
AND t.plan_detail_id = #{map.planDetailId}
</if>
<if test="map.recipeId != null and map.recipeId != ''">
AND t.recipe_id = #{map.recipeId}
</if>
AND t.weight_id = w.weight_id
AND (
t.materiel_id = w.child_code
OR (
w.father_code IS NOT NULL
AND w.father_code &lt;&gt; w.child_code
AND t.materiel_id = w.father_code
)
)
)
HAVING COUNT(1) &gt; 0
UNION ALL
SELECT
CAST(2 AS INT) AS matchPriority,
CAST(SUM(ISNULL(t.used_amount, 0)) AS DECIMAL(18, 4)) AS actualWeight,
MAX(t.plan_detail_id) AS tracePlanDetailId,
MAX(t.recipe_id) AS traceRecipeId,
MAX(t.weight_seq) AS traceWeightSeq,
MAX(t.weight_id) AS traceWeightId,
MAX(RTRIM(LTRIM(COALESCE(t.act_code, '')))) AS traceActCode,
CAST(COUNT(1) AS BIGINT) AS traceRowCount
FROM prod_trace_cur_info t
WHERE t.production_barcode = #{map.productionBarcode}
<if test="map.tenantId != null and map.tenantId != ''">
AND t.tenant_id = #{map.tenantId}
</if>
<if test="map.planDetailId != null and map.planDetailId != ''">
AND t.plan_detail_id = #{map.planDetailId}
</if>
<if test="map.recipeId != null and map.recipeId != ''">
AND t.recipe_id = #{map.recipeId}
</if>
AND t.weight_id IS NULL
AND t.weight_seq = w.weight_seq
AND (
RTRIM(LTRIM(COALESCE(t.act_code, ''))) = RTRIM(LTRIM(COALESCE(w.act_code, '')))
OR RTRIM(LTRIM(COALESCE(t.act_code, ''))) = ''
)
AND (
t.materiel_id = w.child_code
OR (
w.father_code IS NOT NULL
AND w.father_code &lt;&gt; w.child_code
AND t.materiel_id = w.father_code
)
)
HAVING COUNT(1) &gt; 0
UNION ALL
SELECT
CAST(9 AS INT) AS matchPriority,
CAST(
CASE
WHEN ISNULL(SUM(CASE WHEN t.materiel_id = w.child_code THEN 1 ELSE 0 END), 0) = 0
AND ISNULL(SUM(CASE
WHEN w.father_code IS NOT NULL
AND w.father_code &lt;&gt; w.child_code
AND t.materiel_id = w.father_code THEN 1
ELSE 0
END), 0) = 0
THEN NULL
ELSE
ISNULL(SUM(CASE WHEN t.materiel_id = w.child_code THEN ISNULL(t.used_amount, 0) ELSE 0 END), 0)
+ ISNULL(SUM(CASE
WHEN w.father_code IS NOT NULL
AND w.father_code &lt;&gt; w.child_code
AND t.materiel_id = w.father_code
THEN ISNULL(t.used_amount, 0)
ELSE 0
END), 0)
END
AS DECIMAL(18, 4)) AS actualWeight,
MAX(t.plan_detail_id) AS tracePlanDetailId,
MAX(t.recipe_id) AS traceRecipeId,
MAX(t.weight_seq) AS traceWeightSeq,
MAX(t.weight_id) AS traceWeightId,
MAX(RTRIM(LTRIM(COALESCE(t.act_code, '')))) AS traceActCode,
CAST(COUNT(1) AS BIGINT) AS traceRowCount
FROM prod_trace_cur_info t
WHERE t.production_barcode = #{map.productionBarcode}
<if test="map.tenantId != null and map.tenantId != ''">
AND t.tenant_id = #{map.tenantId}
</if>
<if test="map.planDetailId != null and map.planDetailId != ''">
AND (t.plan_detail_id = #{map.planDetailId} OR t.plan_detail_id IS NULL)
</if>
<if test="map.recipeId != null and map.recipeId != ''">
AND (t.recipe_id = #{map.recipeId} OR t.recipe_id IS NULL)
</if>
AND (
t.materiel_id = w.child_code
OR (
w.father_code IS NOT NULL
AND w.father_code &lt;&gt; w.child_code
AND t.materiel_id = w.father_code
)
)
HAVING COUNT(1) &gt; 0
) matched
ORDER BY matched.matchPriority ASC
) trace_used
</if>
<if test="map.productionBarcode == null or map.productionBarcode == ''">
OUTER APPLY (SELECT CAST(NULL AS DECIMAL(18, 4)) AS actualWeight) trace_used
OUTER APPLY (
SELECT
CAST(NULL AS DECIMAL(18, 4)) AS actualWeight,
CAST(NULL AS BIGINT) AS tracePlanDetailId,
CAST(NULL AS BIGINT) AS traceRecipeId,
CAST(NULL AS INT) AS traceWeightSeq,
CAST(NULL AS BIGINT) AS traceWeightId,
CAST(NULL AS VARCHAR(32)) AS traceActCode,
CAST(NULL AS BIGINT) AS traceRowCount
) trace_used
</if>
WHERE w.del_flag = '0'
AND w.recipe_id = #{map.recipeId}

Loading…
Cancel
Save