From 36d20514d2d3330e947977f74a2429738ad431fe Mon Sep 17 00:00:00 2001 From: "zangch@mesnac.com" Date: Mon, 2 Mar 2026 09:43:43 +0800 Subject: [PATCH] =?UTF-8?q?fix(inspect):=20=E8=A7=A3=E5=86=B3=E5=B7=A1?= =?UTF-8?q?=E6=A3=80=E6=A0=87=E5=87=86=E9=A1=B9=E7=9B=AEID=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E5=92=8C=E5=B7=A5=E5=8D=95=E7=94=9F=E6=88=90=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复巡检标准对应项目ID为空时的处理逻辑,避免插入空项目记录 - 添加resolveProjectIdByInspectStandard方法统一处理项目ID查询 - 在多个位置添加null检查和警告日志,提升系统健壮性 - 修复保养工单定时生成任务中的事务传播配置问题 - 调整工单生成服务的事务注解为REQUIRES_NEW传播方式 - 优化工单生成任务,对已存在的工单进行跳过处理并统计跳过数量 - 修复SQL映射文件中的JDBC类型定义问题 - 修复保养实例活动表的主键生成策略问题 --- aucma-dms/DMS_SEQUENCES.sql | 3 ++ .../dms/domain/DmsInspectInstanceDetail.java | 13 +++++ .../mapper/DmsBillsMaintInstanceMapper.java | 7 +++ .../aucma/dms/mapper/DmsPlanMaintMapper.java | 7 +++ .../DmsBillsMaintInstanceServiceImpl.java | 52 ++++++++++++++++++- .../service/impl/DmsPlanMaintServiceImpl.java | 11 ---- .../dms/DmsBillsMaintInstanceMapper.xml | 37 +++++++++++-- .../dms/DmsInspectInstanceDetailMapper.xml | 3 ++ .../mapper/dms/DmsPlanMaintMapper.xml | 9 ++++ 9 files changed, 125 insertions(+), 17 deletions(-) diff --git a/aucma-dms/DMS_SEQUENCES.sql b/aucma-dms/DMS_SEQUENCES.sql index 86f5620..ea7ec40 100644 --- a/aucma-dms/DMS_SEQUENCES.sql +++ b/aucma-dms/DMS_SEQUENCES.sql @@ -295,6 +295,7 @@ CREATE SEQUENCE "HAIWEI"."SEQ_DMS_BILLS_INSPECT_ACTIVITY" -- 14. SEQ_DMS_MAINT_INST (保养工单) -- 表: DMS_BILLS_MAINT_INSTANCE +-- 当前保养计划定时生成工单使用序列之一 -- ---------------------------- BEGIN 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 (保养工单明细) -- 表: DMS_BILLS_MAINT_DETAIL +-- 当前保养计划定时生成工单使用序列之一 -- ---------------------------- BEGIN 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 (保养明细项目) -- 表: DMS_BILLS_MAINT_DETAIL_PROJECT +-- 当前保养计划定时生成工单使用序列之一 -- ---------------------------- BEGIN EXECUTE IMMEDIATE 'DROP SEQUENCE "HAIWEI"."SEQ_DMS_MAINT_DTL_PROJ"'; diff --git a/aucma-dms/src/main/java/com/aucma/dms/domain/DmsInspectInstanceDetail.java b/aucma-dms/src/main/java/com/aucma/dms/domain/DmsInspectInstanceDetail.java index e5ce1d2..0d7bf69 100644 --- a/aucma-dms/src/main/java/com/aucma/dms/domain/DmsInspectInstanceDetail.java +++ b/aucma-dms/src/main/java/com/aucma/dms/domain/DmsInspectInstanceDetail.java @@ -47,6 +47,9 @@ public class DmsInspectInstanceDetail extends DmsBaseEntity @Excel(name = "巡检标准") private String inspectStandard; + /** 巡检标准名称(连表查询) */ + private String standardName; + /** 操作描述 */ @Excel(name = "操作描述") private String inspectDesc; @@ -132,6 +135,15 @@ public class DmsInspectInstanceDetail extends DmsBaseEntity { return inspectStandard; } + public void setStandardName(String standardName) + { + this.standardName = standardName; + } + + public String getStandardName() + { + return standardName; + } public void setInspectDesc(String inspectDesc) { this.inspectDesc = inspectDesc; @@ -210,6 +222,7 @@ public class DmsInspectInstanceDetail extends DmsBaseEntity .append("deviceId", getDeviceId()) .append("instanceDetailStatus", getInstanceDetailStatus()) .append("inspectStandard", getInspectStandard()) + .append("standardName", getStandardName()) .append("inspectDesc", getInspectDesc()) .append("inspectValue", getInspectValue()) .append("inspectStatus", getInspectStatus()) diff --git a/aucma-dms/src/main/java/com/aucma/dms/mapper/DmsBillsMaintInstanceMapper.java b/aucma-dms/src/main/java/com/aucma/dms/mapper/DmsBillsMaintInstanceMapper.java index 38c824e..ccf6c45 100644 --- a/aucma-dms/src/main/java/com/aucma/dms/mapper/DmsBillsMaintInstanceMapper.java +++ b/aucma-dms/src/main/java/com/aucma/dms/mapper/DmsBillsMaintInstanceMapper.java @@ -6,6 +6,7 @@ import com.aucma.dms.domain.DmsBillsMaintInstance; import com.aucma.dms.domain.DmsMaintInstanceActivity; import org.apache.ibatis.annotations.Param; +import java.util.Date; import java.util.List; /** @@ -92,4 +93,10 @@ public interface DmsBillsMaintInstanceMapper { * @return 工单数量 */ int countByPlanIdAndToday(@Param("planMaintId") Long planMaintId); + + /** + * 统一从 DB 获取当前时间,避免应用机与数据库时钟偏差 + * @return DB 当前时间 + */ + Date selectDbNow(); } diff --git a/aucma-dms/src/main/java/com/aucma/dms/mapper/DmsPlanMaintMapper.java b/aucma-dms/src/main/java/com/aucma/dms/mapper/DmsPlanMaintMapper.java index e73a742..11d5754 100644 --- a/aucma-dms/src/main/java/com/aucma/dms/mapper/DmsPlanMaintMapper.java +++ b/aucma-dms/src/main/java/com/aucma/dms/mapper/DmsPlanMaintMapper.java @@ -123,4 +123,11 @@ public interface DmsPlanMaintMapper int updatePlanNextTime(@Param("planMaintId") Long planMaintId, @Param("maintTime") Date maintTime); + /** + * 锁定保养计划行,避免并发重复生成工单 + * @param planMaintId 计划ID + * @return 锁定到的计划ID + */ + Long lockPlanById(@Param("planMaintId") Long planMaintId); + } diff --git a/aucma-dms/src/main/java/com/aucma/dms/service/impl/DmsBillsMaintInstanceServiceImpl.java b/aucma-dms/src/main/java/com/aucma/dms/service/impl/DmsBillsMaintInstanceServiceImpl.java index 49abb21..41114da 100644 --- a/aucma-dms/src/main/java/com/aucma/dms/service/impl/DmsBillsMaintInstanceServiceImpl.java +++ b/aucma-dms/src/main/java/com/aucma/dms/service/impl/DmsBillsMaintInstanceServiceImpl.java @@ -11,6 +11,8 @@ import com.aucma.dms.service.IDmsBillsMaintInstanceService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @@ -432,7 +434,27 @@ public class DmsBillsMaintInstanceServiceImpl implements IDmsBillsMaintInstanceS log.info("开始创建已完成保养工单 | planMaintCode={}, planMaintId={}, 明细数量={}", 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(); instance.setCreateTime(now); @@ -451,7 +473,21 @@ public class DmsBillsMaintInstanceServiceImpl implements IDmsBillsMaintInstanceS // 创建人优先使用计划的创建人,若无则使用-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(); @@ -502,4 +538,16 @@ public class DmsBillsMaintInstanceServiceImpl implements IDmsBillsMaintInstanceS 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; + } + } diff --git a/aucma-dms/src/main/java/com/aucma/dms/service/impl/DmsPlanMaintServiceImpl.java b/aucma-dms/src/main/java/com/aucma/dms/service/impl/DmsPlanMaintServiceImpl.java index b5e19f2..3b50774 100644 --- a/aucma-dms/src/main/java/com/aucma/dms/service/impl/DmsPlanMaintServiceImpl.java +++ b/aucma-dms/src/main/java/com/aucma/dms/service/impl/DmsPlanMaintServiceImpl.java @@ -8,7 +8,6 @@ import com.aucma.common.utils.StringUtils; import com.aucma.common.utils.uuid.Seq; import com.aucma.dms.domain.DmsPlanMaint; import com.aucma.dms.domain.DmsPlanMaintDetail; -import com.aucma.dms.mapper.DmsBillsMaintInstanceMapper; import com.aucma.dms.mapper.DmsPlanMaintMapper; import com.aucma.dms.service.IDmsBillsMaintInstanceService; import com.aucma.dms.service.IDmsPlanMaintService; @@ -38,9 +37,6 @@ public class DmsPlanMaintServiceImpl implements IDmsPlanMaintService { @Autowired private DmsPlanMaintMapper dmsPlanMaintMapper; - @Autowired - private DmsBillsMaintInstanceMapper dmsBillsMaintInstanceMapper; - @Autowired private IDmsBillsMaintInstanceService dmsBillsMaintInstanceService; @@ -342,13 +338,6 @@ public class DmsPlanMaintServiceImpl implements IDmsPlanMaintService { continue; } - // 通过Mapper检查今天是否已生成工单(避免重复) - int existCount = dmsBillsMaintInstanceMapper.countByPlanIdAndToday(planMaintId); - if (existCount > 0) { - log.debug("保养计划[{}]今天已生成工单,跳过", planMaintCode); - continue; - } - // 通过Service创建已完成状态的保养工单(含明细,工单中所有设备默认保养完成,异常可在网页端维护) int insertRows = dmsBillsMaintInstanceService.insertCompletedMaintInstance(planMaintCode); if (insertRows > 0) { diff --git a/aucma-dms/src/main/resources/mapper/dms/DmsBillsMaintInstanceMapper.xml b/aucma-dms/src/main/resources/mapper/dms/DmsBillsMaintInstanceMapper.xml index 4e4563f..218f360 100644 --- a/aucma-dms/src/main/resources/mapper/dms/DmsBillsMaintInstanceMapper.xml +++ b/aucma-dms/src/main/resources/mapper/dms/DmsBillsMaintInstanceMapper.xml @@ -167,13 +167,35 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 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 - ( #{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}) - 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 - ( #{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}) @@ -222,6 +244,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" SELECT COUNT(1) FROM dms_bills_maint_instance WHERE plan_maint_id = #{planMaintId} - AND TRUNC(create_time) = TRUNC(SYSDATE) + -- 使用 [当天00:00, 次日00:00) 范围,避免 TRUNC(create_time) 带来的索引失效风险 + AND create_time >= TRUNC(SYSDATE) + AND create_time < TRUNC(SYSDATE) + 1 + + + + diff --git a/aucma-dms/src/main/resources/mapper/dms/DmsInspectInstanceDetailMapper.xml b/aucma-dms/src/main/resources/mapper/dms/DmsInspectInstanceDetailMapper.xml index 4c38423..acdaff1 100644 --- a/aucma-dms/src/main/resources/mapper/dms/DmsInspectInstanceDetailMapper.xml +++ b/aucma-dms/src/main/resources/mapper/dms/DmsInspectInstanceDetailMapper.xml @@ -12,6 +12,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + @@ -122,6 +123,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" diid.machine_id, diid.instance_detail_status, diid.inspect_standard, + dbis.standard_name, a.inspect_project_desc inspect_desc, diid.inspect_status, dbdl.device_code, @@ -130,6 +132,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" dms_inspect_instance_detail diid left join HAIWEI.base_deviceledger dbdl on 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 diff --git a/aucma-dms/src/main/resources/mapper/dms/DmsPlanMaintMapper.xml b/aucma-dms/src/main/resources/mapper/dms/DmsPlanMaintMapper.xml index 0e9ee31..f67a0ac 100644 --- a/aucma-dms/src/main/resources/mapper/dms/DmsPlanMaintMapper.xml +++ b/aucma-dms/src/main/resources/mapper/dms/DmsPlanMaintMapper.xml @@ -229,4 +229,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" WHERE plan_maint_id = #{planMaintId} + + +