fix(inspect): 解决巡检标准项目ID查询和工单生成问题

- 修复巡检标准对应项目ID为空时的处理逻辑,避免插入空项目记录
- 添加resolveProjectIdByInspectStandard方法统一处理项目ID查询
- 在多个位置添加null检查和警告日志,提升系统健壮性
- 修复保养工单定时生成任务中的事务传播配置问题
- 调整工单生成服务的事务注解为REQUIRES_NEW传播方式
- 优化工单生成任务,对已存在的工单进行跳过处理并统计跳过数量
- 修复SQL映射文件中的JDBC类型定义问题
- 修复保养实例活动表的主键生成策略问题
master
zangch@mesnac.com 2 months ago
parent c938c84792
commit 36d20514d2

@ -295,6 +295,7 @@ CREATE SEQUENCE "HAIWEI"."SEQ_DMS_BILLS_INSPECT_ACTIVITY"
-- 14. SEQ_DMS_MAINT_INST (保养工单) -- 14. SEQ_DMS_MAINT_INST (保养工单)
-- 表: DMS_BILLS_MAINT_INSTANCE -- 表: DMS_BILLS_MAINT_INSTANCE
-- 当前保养计划定时生成工单使用序列之一
-- ---------------------------- -- ----------------------------
BEGIN BEGIN
EXECUTE IMMEDIATE 'DROP SEQUENCE "HAIWEI"."SEQ_DMS_MAINT_INST"'; EXECUTE IMMEDIATE 'DROP SEQUENCE "HAIWEI"."SEQ_DMS_MAINT_INST"';
@ -329,6 +330,7 @@ CREATE SEQUENCE "HAIWEI"."SEQ_DMS_MAINT_INST_ACTIVITY"
-- ---------------------------- -- ----------------------------
-- 15. SEQ_DMS_MAINT_DETAIL (保养工单明细) -- 15. SEQ_DMS_MAINT_DETAIL (保养工单明细)
-- 表: DMS_BILLS_MAINT_DETAIL -- 表: DMS_BILLS_MAINT_DETAIL
-- 当前保养计划定时生成工单使用序列之一
-- ---------------------------- -- ----------------------------
BEGIN BEGIN
EXECUTE IMMEDIATE 'DROP SEQUENCE "HAIWEI"."SEQ_DMS_MAINT_DETAIL"'; EXECUTE IMMEDIATE 'DROP SEQUENCE "HAIWEI"."SEQ_DMS_MAINT_DETAIL"';
@ -346,6 +348,7 @@ CREATE SEQUENCE "HAIWEI"."SEQ_DMS_MAINT_DETAIL"
-- ---------------------------- -- ----------------------------
-- 16. SEQ_DMS_MAINT_DTL_PROJ (保养明细项目) -- 16. SEQ_DMS_MAINT_DTL_PROJ (保养明细项目)
-- 表: DMS_BILLS_MAINT_DETAIL_PROJECT -- 表: DMS_BILLS_MAINT_DETAIL_PROJECT
-- 当前保养计划定时生成工单使用序列之一
-- ---------------------------- -- ----------------------------
BEGIN BEGIN
EXECUTE IMMEDIATE 'DROP SEQUENCE "HAIWEI"."SEQ_DMS_MAINT_DTL_PROJ"'; EXECUTE IMMEDIATE 'DROP SEQUENCE "HAIWEI"."SEQ_DMS_MAINT_DTL_PROJ"';

@ -47,6 +47,9 @@ public class DmsInspectInstanceDetail extends DmsBaseEntity
@Excel(name = "巡检标准") @Excel(name = "巡检标准")
private String inspectStandard; private String inspectStandard;
/** 巡检标准名称(连表查询) */
private String standardName;
/** 操作描述 */ /** 操作描述 */
@Excel(name = "操作描述") @Excel(name = "操作描述")
private String inspectDesc; private String inspectDesc;
@ -132,6 +135,15 @@ public class DmsInspectInstanceDetail extends DmsBaseEntity
{ {
return inspectStandard; return inspectStandard;
} }
public void setStandardName(String standardName)
{
this.standardName = standardName;
}
public String getStandardName()
{
return standardName;
}
public void setInspectDesc(String inspectDesc) public void setInspectDesc(String inspectDesc)
{ {
this.inspectDesc = inspectDesc; this.inspectDesc = inspectDesc;
@ -210,6 +222,7 @@ public class DmsInspectInstanceDetail extends DmsBaseEntity
.append("deviceId", getDeviceId()) .append("deviceId", getDeviceId())
.append("instanceDetailStatus", getInstanceDetailStatus()) .append("instanceDetailStatus", getInstanceDetailStatus())
.append("inspectStandard", getInspectStandard()) .append("inspectStandard", getInspectStandard())
.append("standardName", getStandardName())
.append("inspectDesc", getInspectDesc()) .append("inspectDesc", getInspectDesc())
.append("inspectValue", getInspectValue()) .append("inspectValue", getInspectValue())
.append("inspectStatus", getInspectStatus()) .append("inspectStatus", getInspectStatus())

@ -6,6 +6,7 @@ import com.aucma.dms.domain.DmsBillsMaintInstance;
import com.aucma.dms.domain.DmsMaintInstanceActivity; import com.aucma.dms.domain.DmsMaintInstanceActivity;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List; import java.util.List;
/** /**
@ -92,4 +93,10 @@ public interface DmsBillsMaintInstanceMapper {
* @return * @return
*/ */
int countByPlanIdAndToday(@Param("planMaintId") Long planMaintId); int countByPlanIdAndToday(@Param("planMaintId") Long planMaintId);
/**
* DB
* @return DB
*/
Date selectDbNow();
} }

@ -123,4 +123,11 @@ public interface DmsPlanMaintMapper
int updatePlanNextTime(@Param("planMaintId") Long planMaintId, int updatePlanNextTime(@Param("planMaintId") Long planMaintId,
@Param("maintTime") Date maintTime); @Param("maintTime") Date maintTime);
/**
*
* @param planMaintId ID
* @return ID
*/
Long lockPlanById(@Param("planMaintId") Long planMaintId);
} }

@ -11,6 +11,8 @@ import com.aucma.dms.service.IDmsBillsMaintInstanceService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -432,7 +434,27 @@ public class DmsBillsMaintInstanceServiceImpl implements IDmsBillsMaintInstanceS
log.info("开始创建已完成保养工单 | planMaintCode={}, planMaintId={}, 明细数量={}", log.info("开始创建已完成保养工单 | planMaintCode={}, planMaintId={}, 明细数量={}",
planMaintCodeS, dmsPlanMaint.getPlanMaintId(), planMaintDetails.size()); planMaintCodeS, dmsPlanMaint.getPlanMaintId(), planMaintDetails.size());
Date now = new Date(); Long planMaintId = dmsPlanMaint.getPlanMaintId();
if (planMaintId != null) {
// 1) 先锁计划行,串行化同一计划的并发生成请求
Long lockedId = dmsPlanMaintMapper.lockPlanById(planMaintId);
if (lockedId == null) {
log.warn("保养计划不存在,跳过工单生成 | planMaintId={}, planMaintCode={}", planMaintId, planMaintCodeS);
return 0;
}
// 2) 再做当天查重(应用层幂等)
int existCount = dmsBillsMaintInstanceMapper.countByPlanIdAndToday(planMaintId);
if (existCount > 0) {
log.debug("保养计划今天已生成工单,跳过 | planMaintId={}, planMaintCode={}", planMaintId, planMaintCodeS);
return 0;
}
}
// 3) 统一使用 DB 时间,减少应用机/DB 机时钟差异
Date now = dmsBillsMaintInstanceMapper.selectDbNow();
if (now == null) {
now = new Date();
}
DmsBillsMaintInstance instance = new DmsBillsMaintInstance(); DmsBillsMaintInstance instance = new DmsBillsMaintInstance();
instance.setCreateTime(now); instance.setCreateTime(now);
@ -451,7 +473,21 @@ public class DmsBillsMaintInstanceServiceImpl implements IDmsBillsMaintInstanceS
// 创建人优先使用计划的创建人,若无则使用-1L表示系统自动创建 // 创建人优先使用计划的创建人,若无则使用-1L表示系统自动创建
instance.setCreateBy(dmsPlanMaint.getCreateBy() != null ? dmsPlanMaint.getCreateBy() : -1L); instance.setCreateBy(dmsPlanMaint.getCreateBy() != null ? dmsPlanMaint.getCreateBy() : -1L);
int i = dmsBillsMaintInstanceMapper.insertDmsBillsMaintInstance(instance); int i;
try {
i = dmsBillsMaintInstanceMapper.insertDmsBillsMaintInstance(instance);
} catch (DuplicateKeyException e) {
log.debug("保养工单触发唯一约束(重复生成),跳过 | planMaintId={}, billsMaintCode={}",
instance.getPlanMaintId(), instance.getBillsMaintCode());
return 0;
} catch (DataIntegrityViolationException e) {
if (isOracleUniqueViolation(e)) {
log.debug("保养工单触发 ORA-00001(重复生成),跳过 | planMaintId={}, billsMaintCode={}",
instance.getPlanMaintId(), instance.getBillsMaintCode());
return 0;
}
throw e;
}
// 创建工单实例节点(第一步) // 创建工单实例节点(第一步)
DmsMaintInstanceActivity activity = new DmsMaintInstanceActivity(); DmsMaintInstanceActivity activity = new DmsMaintInstanceActivity();
@ -502,4 +538,16 @@ public class DmsBillsMaintInstanceServiceImpl implements IDmsBillsMaintInstanceS
return i; return i;
} }
private boolean isOracleUniqueViolation(Throwable e) {
Throwable cursor = e;
while (cursor != null) {
String msg = cursor.getMessage();
if (msg != null && msg.contains("ORA-00001")) {
return true;
}
cursor = cursor.getCause();
}
return false;
}
} }

@ -8,7 +8,6 @@ import com.aucma.common.utils.StringUtils;
import com.aucma.common.utils.uuid.Seq; import com.aucma.common.utils.uuid.Seq;
import com.aucma.dms.domain.DmsPlanMaint; import com.aucma.dms.domain.DmsPlanMaint;
import com.aucma.dms.domain.DmsPlanMaintDetail; import com.aucma.dms.domain.DmsPlanMaintDetail;
import com.aucma.dms.mapper.DmsBillsMaintInstanceMapper;
import com.aucma.dms.mapper.DmsPlanMaintMapper; import com.aucma.dms.mapper.DmsPlanMaintMapper;
import com.aucma.dms.service.IDmsBillsMaintInstanceService; import com.aucma.dms.service.IDmsBillsMaintInstanceService;
import com.aucma.dms.service.IDmsPlanMaintService; import com.aucma.dms.service.IDmsPlanMaintService;
@ -38,9 +37,6 @@ public class DmsPlanMaintServiceImpl implements IDmsPlanMaintService {
@Autowired @Autowired
private DmsPlanMaintMapper dmsPlanMaintMapper; private DmsPlanMaintMapper dmsPlanMaintMapper;
@Autowired
private DmsBillsMaintInstanceMapper dmsBillsMaintInstanceMapper;
@Autowired @Autowired
private IDmsBillsMaintInstanceService dmsBillsMaintInstanceService; private IDmsBillsMaintInstanceService dmsBillsMaintInstanceService;
@ -342,13 +338,6 @@ public class DmsPlanMaintServiceImpl implements IDmsPlanMaintService {
continue; continue;
} }
// 通过Mapper检查今天是否已生成工单避免重复
int existCount = dmsBillsMaintInstanceMapper.countByPlanIdAndToday(planMaintId);
if (existCount > 0) {
log.debug("保养计划[{}]今天已生成工单,跳过", planMaintCode);
continue;
}
// 通过Service创建已完成状态的保养工单含明细工单中所有设备默认保养完成异常可在网页端维护 // 通过Service创建已完成状态的保养工单含明细工单中所有设备默认保养完成异常可在网页端维护
int insertRows = dmsBillsMaintInstanceService.insertCompletedMaintInstance(planMaintCode); int insertRows = dmsBillsMaintInstanceService.insertCompletedMaintInstance(planMaintCode);
if (insertRows > 0) { if (insertRows > 0) {

@ -167,13 +167,35 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<insert id="batchDmsBillsMaintDetail"> <insert id="batchDmsBillsMaintDetail">
insert into dms_bills_maint_detail( bills_maint_detail_id, maint_instance_id, device_id, station_id, maint_standard_id, operation_description, maint_status, is_flag, remark, create_by, create_time, update_by, update_time) values insert into dms_bills_maint_detail( bills_maint_detail_id, maint_instance_id, device_id, station_id, maint_standard_id, operation_description, maint_status, is_flag, remark, create_by, create_time, update_by, update_time) values
<foreach item="item" index="index" collection="list" separator=","> <foreach item="item" index="index" collection="list" separator=",">
( #{item.billsMaintDetailId}, #{item.maintInstanceId}, #{item.deviceId}, #{item.stationId}, #{item.maintStandardId}, #{item.operationDescription}, #{item.maintStatus}, #{item.isFlag}, #{item.remark}, #{item.createBy}, #{item.createTime}, #{item.updateBy}, #{item.updateTime}) ( HAIWEI.SEQ_DMS_MAINT_DETAIL.NEXTVAL,
#{item.maintInstanceId,jdbcType=NUMERIC},
#{item.deviceId,jdbcType=NUMERIC},
#{item.stationId,jdbcType=NUMERIC},
#{item.maintStandardId,jdbcType=NUMERIC},
#{item.operationDescription,jdbcType=VARCHAR},
#{item.maintStatus,jdbcType=NUMERIC},
#{item.isFlag,jdbcType=NUMERIC},
#{item.remark,jdbcType=VARCHAR},
#{item.createBy,jdbcType=NUMERIC},
#{item.createTime,jdbcType=TIMESTAMP},
#{item.updateBy,jdbcType=NUMERIC},
#{item.updateTime,jdbcType=TIMESTAMP})
</foreach> </foreach>
</insert> </insert>
<insert id="batchDmsProject"> <insert id="batchDmsProject">
insert into dms_bills_maint_detail_project( bills_maint_detail_id, maint_project_id, maint_project_name, maint_project_desc, maint_project_status, remark, create_by, create_time, update_by, update_time) values insert into dms_bills_maint_detail_project( maint_detail_project_id, bills_maint_detail_id, maint_project_id, maint_project_name, maint_project_desc, maint_project_status, remark, create_by, create_time, update_by, update_time) values
<foreach item="item" index="index" collection="list" separator=","> <foreach item="item" index="index" collection="list" separator=",">
( #{item.billsMaintDetailId}, #{item.maintProjectId}, #{item.maintProjectName}, #{item.maintProjectDesc}, #{item.maintProjectStatus}, #{item.remark}, #{item.createBy}, #{item.createTime}, #{item.updateBy}, #{item.updateTime}) ( HAIWEI.SEQ_DMS_MAINT_DTL_PROJ.NEXTVAL,
#{item.billsMaintDetailId,jdbcType=NUMERIC},
#{item.maintProjectId,jdbcType=NUMERIC},
#{item.maintProjectName,jdbcType=VARCHAR},
#{item.maintProjectDesc,jdbcType=VARCHAR},
#{item.maintProjectStatus,jdbcType=VARCHAR},
#{item.remark,jdbcType=VARCHAR},
#{item.createBy,jdbcType=NUMERIC},
#{item.createTime,jdbcType=TIMESTAMP},
#{item.updateBy,jdbcType=NUMERIC},
#{item.updateTime,jdbcType=TIMESTAMP})
</foreach> </foreach>
</insert> </insert>
@ -222,6 +244,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
SELECT COUNT(1) SELECT COUNT(1)
FROM dms_bills_maint_instance FROM dms_bills_maint_instance
WHERE plan_maint_id = #{planMaintId} WHERE plan_maint_id = #{planMaintId}
AND TRUNC(create_time) = TRUNC(SYSDATE) -- 使用 [当天00:00, 次日00:00) 范围,避免 TRUNC(create_time) 带来的索引失效风险
AND create_time &gt;= TRUNC(SYSDATE)
AND create_time &lt; TRUNC(SYSDATE) + 1
</select>
<!-- 统一从 DB 读取当前时间,避免应用与数据库时钟差异 -->
<select id="selectDbNow" resultType="java.util.Date">
SELECT SYSDATE FROM DUAL
</select> </select>
</mapper> </mapper>

@ -12,6 +12,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="deviceId" column="machine_id" /> <result property="deviceId" column="machine_id" />
<result property="instanceDetailStatus" column="instance_detail_status" /> <result property="instanceDetailStatus" column="instance_detail_status" />
<result property="inspectStandard" column="inspect_standard" /> <result property="inspectStandard" column="inspect_standard" />
<result property="standardName" column="standard_name" />
<result property="inspectDesc" column="inspect_desc" /> <result property="inspectDesc" column="inspect_desc" />
<result property="inspectStatus" column="inspect_status" /> <result property="inspectStatus" column="inspect_status" />
<result property="createBy" column="create_by" /> <result property="createBy" column="create_by" />
@ -122,6 +123,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
diid.machine_id, diid.machine_id,
diid.instance_detail_status, diid.instance_detail_status,
diid.inspect_standard, diid.inspect_standard,
dbis.standard_name,
a.inspect_project_desc inspect_desc, a.inspect_project_desc inspect_desc,
diid.inspect_status, diid.inspect_status,
dbdl.device_code, dbdl.device_code,
@ -130,6 +132,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
dms_inspect_instance_detail diid dms_inspect_instance_detail diid
left join HAIWEI.base_deviceledger dbdl on left join HAIWEI.base_deviceledger dbdl on
diid.machine_id = dbdl.OBJ_ID diid.machine_id = dbdl.OBJ_ID
left join dms_base_inspect_standard dbis on to_char(dbis.inspect_standard_id) = diid.inspect_standard
left join dms_inspect_inst_det_proj a on diid.instance_detail_id = a.instance_detail_id left join dms_inspect_inst_det_proj a on diid.instance_detail_id = a.instance_detail_id
</sql> </sql>

@ -229,4 +229,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
WHERE plan_maint_id = #{planMaintId} WHERE plan_maint_id = #{planMaintId}
</update> </update>
<!-- 锁定计划行,避免并发重复生成工单 -->
<select id="lockPlanById" resultType="java.lang.Long">
<!-- FOR UPDATE: 对同一 plan_maint_id 的并发任务形成串行执行 -->
SELECT plan_maint_id
FROM dms_plan_maint
WHERE plan_maint_id = #{planMaintId}
FOR UPDATE
</select>
</mapper> </mapper>

Loading…
Cancel
Save