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

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

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

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

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

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

@ -7,9 +7,9 @@
<sql id="routerMasterDataCte"> <sql id="routerMasterDataCte">
WITH router_master_data AS ( WITH router_master_data AS (
SELECT SELECT
MAX(CASE WHEN REPLACE(sm.query_param, ' ', '') LIKE '%"router":"act"%' THEN sm.master_data_id END) AS act_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,
MAX(CASE WHEN REPLACE(sm.query_param, ' ', '') LIKE '%"router":"term"%' THEN sm.master_data_id END) AS term_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,
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":"jar"%' THEN sm.master_data_id END), 10011) AS jar_master_data_id
FROM sys_master_data sm FROM sys_master_data sm
WHERE sm.del_flag = '0' WHERE sm.del_flag = '0'
AND sm.active_flag = '1' AND sm.active_flag = '1'
@ -84,6 +84,12 @@
OR LTRIM(RTRIM(COALESCE(ppd.material_barcode, ''))) = LTRIM(RTRIM(#{map.productionBarcode})) OR LTRIM(RTRIM(COALESCE(ppd.material_barcode, ''))) = LTRIM(RTRIM(#{map.productionBarcode}))
) )
</if> </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 ORDER BY
CASE CASE
WHEN #{map.planDetailId} IS NOT NULL WHEN #{map.planDetailId} IS NOT NULL
@ -140,17 +146,31 @@
tp.classTeamName AS classTeamName, tp.classTeamName AS classTeamName,
tp.planAmount AS planAmount, tp.planAmount AS planAmount,
COALESCE(tp.detailCompleteAmount, tp.planCompleteAmount) AS completeAmount, 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, tp.trainNumber AS trainNumber,
train_stat.totalTrainNo AS totalTrainNo,
tp.planDetailStatus AS planDetailStatus,
COALESCE(tp.detailRealBeginTime, tp.planRealBeginTime) AS realBeginTime, COALESCE(tp.detailRealBeginTime, tp.planRealBeginTime) AS realBeginTime,
COALESCE(tp.detailRealEndTime, tp.planRealEndTime) AS realEndTime COALESCE(tp.detailRealEndTime, tp.planRealEndTime) AS realEndTime
FROM prod_recipe_info ri FROM prod_recipe_info ri
LEFT JOIN prod_base_machine_info bm ON bm.machine_id = ri.machine_id 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 LEFT JOIN base_material_info bmi ON bmi.material_id = ri.material_id
<include refid="tracePickApply"/> <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' WHERE ri.del_flag = '0'
<if test="map.recipeCode != null and map.recipeCode != ''"> <if test="map.recipeCode != null and map.recipeCode != ''">
AND ri.recipe_code LIKE CONCAT('%', #{map.recipeCode}, '%') AND ri.recipe_code LIKE CONCAT('%', #{map.recipeCode}, '%')
</if> </if>
<if test="map.recipeId != null and map.recipeId != ''">
AND ri.recipe_id = #{map.recipeId}
</if>
<if test="map.machineId != null and map.machineId != ''"> <if test="map.machineId != null and map.machineId != ''">
AND ri.machine_id = #{map.machineId} AND ri.machine_id = #{map.machineId}
</if> </if>
@ -190,6 +210,8 @@
or (map.planDetailId != null and map.planDetailId != '') or (map.planDetailId != null and map.planDetailId != '')
or (map.planDetailCode != null and map.planDetailCode != '') or (map.planDetailCode != null and map.planDetailCode != '')
or (map.productionBarcode != null and map.productionBarcode != '') 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.shiftId != null and map.shiftId != '')
or (map.classTeamId != null and map.classTeamId != '') or (map.classTeamId != null and map.classTeamId != '')
or (map.shiftName != null and map.shiftName != '') or (map.shiftName != null and map.shiftName != '')
@ -296,6 +318,8 @@
mix_last.dischargePower AS dischargePower, mix_last.dischargePower AS dischargePower,
mix_last.dischargeEnergy AS dischargeEnergy, mix_last.dischargeEnergy AS dischargeEnergy,
tp.planDetailStatus AS mixingStatus, tp.planDetailStatus AS mixingStatus,
CAST(NULL AS VARCHAR(64)) AS testResult,
ri.done_time AS recipeTime,
mix_sum.totalMixingTime AS mixingTime, mix_sum.totalMixingTime AS mixingTime,
CASE CASE
WHEN tp.detailRealBeginTime IS NOT NULL AND tp.detailRealEndTime IS NOT NULL WHEN tp.detailRealBeginTime IS NOT NULL AND tp.detailRealEndTime IS NOT NULL
@ -388,9 +412,12 @@
w.father_code AS fatherCode, w.father_code AS fatherCode,
w.unit_id AS unitId, w.unit_id AS unitId,
w.child_code AS childCode, w.child_code AS childCode,
bmi.material_name AS materialName,
w.if_use_bat AS ifUseBat, w.if_use_bat AS ifUseBat,
w.max_rate AS maxRate w.max_rate AS maxRate
FROM prod_recipe_weight w 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 CROSS JOIN router_master_data rmd
OUTER APPLY ( OUTER APPLY (
SELECT TOP 1 d.data_detail_name SELECT TOP 1 d.data_detail_name
@ -411,8 +438,23 @@
<include refid="routerMasterDataCte"/> <include refid="routerMasterDataCte"/>
SELECT SELECT
w.weight_id AS usageId, 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, 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, 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, w.set_weight AS setWeight,
trace_used.actualWeight AS actualWeight, trace_used.actualWeight AS actualWeight,
@ -431,11 +473,58 @@
CASE CASE
WHEN act_detail.data_detail_name IS NULL OR LTRIM(RTRIM(act_detail.data_detail_name)) = '' THEN RTRIM(LTRIM(COALESCE(w.act_code, ''))) 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)) 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 FROM prod_recipe_weight w
CROSS JOIN router_master_data rmd 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 cm ON cm.material_id = w.child_code
LEFT JOIN base_material_info fm ON fm.material_id = w.father_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 ( OUTER APPLY (
SELECT TOP 1 d.data_detail_name SELECT TOP 1 d.data_detail_name
FROM sys_master_data_detail d FROM sys_master_data_detail d
@ -447,40 +536,150 @@
) act_detail ) act_detail
<if test="map.productionBarcode != null and map.productionBarcode != ''"> <if test="map.productionBarcode != null and map.productionBarcode != ''">
OUTER APPLY ( OUTER APPLY (
SELECT SELECT TOP 1
CASE matched.actualWeight,
WHEN ISNULL(SUM(CASE WHEN t.materiel_id = w.child_code THEN 1 ELSE 0 END), 0) = 0 matched.tracePlanDetailId,
AND ISNULL(SUM(CASE matched.traceRecipeId,
WHEN w.father_code IS NOT NULL matched.traceWeightSeq,
AND w.father_code &lt;&gt; w.child_code matched.traceWeightId,
AND t.materiel_id = w.father_code THEN 1 matched.traceActCode,
ELSE 0 matched.traceRowCount
END), 0) = 0 FROM (
THEN NULL SELECT
ELSE CAST(1 AS INT) AS matchPriority,
ISNULL(SUM(CASE WHEN t.materiel_id = w.child_code THEN ISNULL(t.used_amount, 0) ELSE 0 END), 0) CAST(SUM(ISNULL(t.used_amount, 0)) AS DECIMAL(18, 4)) AS actualWeight,
+ ISNULL(SUM(CASE MAX(t.plan_detail_id) AS tracePlanDetailId,
WHEN w.father_code IS NOT NULL MAX(t.recipe_id) AS traceRecipeId,
AND w.father_code &lt;&gt; w.child_code MAX(t.weight_seq) AS traceWeightSeq,
AND t.materiel_id = w.father_code MAX(t.weight_id) AS traceWeightId,
THEN ISNULL(t.used_amount, 0) MAX(RTRIM(LTRIM(COALESCE(t.act_code, '')))) AS traceActCode,
ELSE 0 CAST(COUNT(1) AS BIGINT) AS traceRowCount
END), 0) FROM prod_trace_cur_info t
END AS actualWeight WHERE t.production_barcode = #{map.productionBarcode}
FROM prod_trace_cur_info t <if test="map.tenantId != null and map.tenantId != ''">
WHERE t.production_barcode = #{map.productionBarcode} AND t.tenant_id = #{map.tenantId}
AND ( </if>
t.materiel_id = w.child_code <if test="map.planDetailId != null and map.planDetailId != ''">
OR ( AND t.plan_detail_id = #{map.planDetailId}
w.father_code IS NOT NULL </if>
AND w.father_code &lt;&gt; w.child_code <if test="map.recipeId != null and map.recipeId != ''">
AND t.materiel_id = w.father_code 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 ) trace_used
</if> </if>
<if test="map.productionBarcode == null or map.productionBarcode == ''"> <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> </if>
WHERE w.del_flag = '0' WHERE w.del_flag = '0'
AND w.recipe_id = #{map.recipeId} AND w.recipe_id = #{map.recipeId}

Loading…
Cancel
Save