From f32315f790e61f6bb20c0a4934e1cf0ac2859b3c Mon Sep 17 00:00:00 2001 From: lzh Date: Sun, 15 Mar 2026 10:31:50 +0800 Subject: [PATCH] =?UTF-8?q?feat(ops):=20=E5=AE=89=E4=BF=9D=E5=B7=A5?= =?UTF-8?q?=E5=8D=95=E6=96=B0=E5=A2=9E=E8=AF=AF=E6=8A=A5=E6=A0=87=E8=AE=B0?= =?UTF-8?q?=E3=80=81=E5=AE=8C=E5=96=84=E7=A1=AE=E8=AE=A4/=E5=AE=8C?= =?UTF-8?q?=E5=8D=95=E6=8E=A5=E5=8F=A3=E6=94=AF=E6=8C=81=20open-api=20?= =?UTF-8?q?=E5=9C=BA=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 falseAlarmOrder 方法,标记误报并完成工单 - confirmOrder/manualCompleteOrder 支持 operatorId 为 null(open-api 自动取已分配人员) - 新增 resolveOperatorId 辅助方法,null 时记录 warn 日志 - createSecurityOrder 移除 location 透传,改用 AreaPathBuilder 自动拼接 - 消除 createSecurityOrder 中 area 重复查询(校验 + buildPath 共用同一 DO) - OpsOrderSecurityExtDO 新增 falseAlarm 字段 - SecurityOrderCompleteReqDTO.operatorId 移除 @NotNull 约束 Co-Authored-By: Claude Opus 4.6 --- .../workorder/OpsOrderSecurityExtDO.java | 4 + .../SecurityOrderCompleteReqDTO.java | 3 +- .../SecurityOrderCreateReqDTO.java | 2 - .../securityorder/SecurityOrderService.java | 8 ++ .../SecurityOrderServiceImpl.java | 73 ++++++++++++++++--- 5 files changed, 74 insertions(+), 16 deletions(-) diff --git a/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/dal/dataobject/workorder/OpsOrderSecurityExtDO.java b/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/dal/dataobject/workorder/OpsOrderSecurityExtDO.java index 5073164..426eeef 100644 --- a/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/dal/dataobject/workorder/OpsOrderSecurityExtDO.java +++ b/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/dal/dataobject/workorder/OpsOrderSecurityExtDO.java @@ -81,6 +81,10 @@ public class OpsOrderSecurityExtDO extends BaseDO { * 处理结果图片URL,JSON数组 */ private String resultImgUrls; + /** + * 是否误报(true=误报) + */ + private Boolean falseAlarm; // ==================== 关键时间点 ==================== diff --git a/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/service/securityorder/SecurityOrderCompleteReqDTO.java b/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/service/securityorder/SecurityOrderCompleteReqDTO.java index ba1a094..3fce009 100644 --- a/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/service/securityorder/SecurityOrderCompleteReqDTO.java +++ b/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/service/securityorder/SecurityOrderCompleteReqDTO.java @@ -29,9 +29,8 @@ public class SecurityOrderCompleteReqDTO { private List resultImgUrls; /** - * 操作人ID(由 Controller 层填充) + * 操作人ID(由 Controller 层填充,open-api 场景可为 null,Service 层会自动取已分配人员) */ - @NotNull(message = "操作人ID不能为空") private Long operatorId; } diff --git a/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/service/securityorder/SecurityOrderCreateReqDTO.java b/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/service/securityorder/SecurityOrderCreateReqDTO.java index 56d90ed..fae0fd5 100644 --- a/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/service/securityorder/SecurityOrderCreateReqDTO.java +++ b/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/service/securityorder/SecurityOrderCreateReqDTO.java @@ -28,8 +28,6 @@ public class SecurityOrderCreateReqDTO { @NotNull(message = "区域ID不能为空") private Long areaId; - private String location; - // ==================== 告警来源 ==================== private String alarmId; diff --git a/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/service/securityorder/SecurityOrderService.java b/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/service/securityorder/SecurityOrderService.java index 245d602..b77cb47 100644 --- a/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/service/securityorder/SecurityOrderService.java +++ b/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/service/securityorder/SecurityOrderService.java @@ -44,6 +44,14 @@ public interface SecurityOrderService { */ void manualCompleteOrder(SecurityOrderCompleteReqDTO req); + /** + * 误报标记(将工单标记为误报并完成) + * + * @param orderId 工单ID + * @param operatorId 操作人ID(可为 null,为 null 时取已分配人员) + */ + void falseAlarmOrder(Long orderId, Long operatorId); + /** * 根据工单ID查询安保扩展信息 * diff --git a/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/service/securityorder/SecurityOrderServiceImpl.java b/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/service/securityorder/SecurityOrderServiceImpl.java index 206a44c..8f453c9 100644 --- a/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/service/securityorder/SecurityOrderServiceImpl.java +++ b/viewsh-module-ops/viewsh-module-security-biz/src/main/java/com/viewsh/module/ops/security/service/securityorder/SecurityOrderServiceImpl.java @@ -1,8 +1,9 @@ package com.viewsh.module.ops.security.service.securityorder; import cn.hutool.core.util.StrUtil; -import com.viewsh.module.ops.core.event.OrderEventPublisher; import com.viewsh.module.ops.core.event.OrderCreatedEvent; +import com.viewsh.module.ops.core.event.OrderEventPublisher; +import com.viewsh.module.ops.dal.dataobject.area.OpsBusAreaDO; import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO; import com.viewsh.module.ops.dal.mysql.area.OpsBusAreaMapper; import com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper; @@ -11,17 +12,14 @@ import com.viewsh.module.ops.enums.PriorityEnum; import com.viewsh.module.ops.enums.SourceTypeEnum; import com.viewsh.module.ops.enums.WorkOrderStatusEnum; import com.viewsh.module.ops.enums.WorkOrderTypeEnum; +import com.viewsh.module.ops.infrastructure.area.AreaPathBuilder; import com.viewsh.module.ops.infrastructure.code.OrderCodeGenerator; +import com.viewsh.module.ops.infrastructure.id.OrderIdGenerator; import com.viewsh.module.ops.infrastructure.log.enumeration.EventDomain; import com.viewsh.module.ops.infrastructure.log.enumeration.LogModule; import com.viewsh.module.ops.infrastructure.log.enumeration.LogType; import com.viewsh.module.ops.infrastructure.log.recorder.EventLogRecord; import com.viewsh.module.ops.infrastructure.log.recorder.EventLogRecorder; -// 注意:confirm/complete 的业务日志由 SecurityOrderEventListener 统一记录 -// 本类仅记录 CREATE 日志(创建不经过状态变更事件) -import static com.viewsh.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.viewsh.module.ops.enums.ErrorCodeConstants.*; -import com.viewsh.module.ops.infrastructure.id.OrderIdGenerator; import com.viewsh.module.ops.security.dal.dataobject.workorder.OpsOrderSecurityExtDO; import com.viewsh.module.ops.security.dal.mysql.workorder.OpsOrderSecurityExtMapper; import com.viewsh.module.ops.service.fsm.OrderStateMachine; @@ -30,6 +28,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import static com.viewsh.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.viewsh.module.ops.enums.ErrorCodeConstants.*; + /** * 安保工单服务实现 * @@ -57,6 +58,9 @@ public class SecurityOrderServiceImpl implements SecurityOrderService { @Resource private OpsBusAreaMapper opsBusAreaMapper; + @Resource + private AreaPathBuilder areaPathBuilder; + @Resource private OrderStateMachine orderStateMachine; @@ -69,7 +73,8 @@ public class SecurityOrderServiceImpl implements SecurityOrderService { @Transactional(rollbackFor = Exception.class) public Long createSecurityOrder(SecurityOrderCreateReqDTO createReq) { // 0. 校验区域是否存在 - if (opsBusAreaMapper.selectById(createReq.getAreaId()) == null) { + OpsBusAreaDO area = opsBusAreaMapper.selectById(createReq.getAreaId()); + if (area == null) { throw exception(AREA_NOT_FOUND); } @@ -82,7 +87,7 @@ public class SecurityOrderServiceImpl implements SecurityOrderService { ? createReq.getSourceType() : (StrUtil.isNotBlank(createReq.getAlarmId()) ? SourceTypeEnum.ALARM.getType() : SourceTypeEnum.MANUAL.getType()); - // 3. 构建主表记录 + // 3. 构建主表记录(location 由 areaId 自动拼接) OpsOrderDO order = OpsOrderDO.builder() .id(orderId) .orderCode(orderCode) @@ -93,7 +98,7 @@ public class SecurityOrderServiceImpl implements SecurityOrderService { .priority(createReq.getPriority() != null ? createReq.getPriority() : PriorityEnum.P2.getPriority()) .status(WorkOrderStatusEnum.PENDING.getStatus()) .areaId(createReq.getAreaId()) - .location(createReq.getLocation()) + .location(areaPathBuilder.buildPath(area)) .build(); opsOrderMapper.insert(order); @@ -142,11 +147,14 @@ public class SecurityOrderServiceImpl implements SecurityOrderService { OpsOrderDO order = getOrderOrThrow(orderId); validateOrderType(order); + // 如果 userId 为 null(open-api 调用),取已分配人员 + Long effectiveUserId = resolveOperatorId(orderId, userId); + // 状态转换:DISPATCHED → CONFIRMED(扩展表时间 + 业务日志由 EventListener 统一记录) orderStateMachine.transition(order, WorkOrderStatusEnum.CONFIRMED, - OperatorTypeEnum.SECURITY_GUARD, userId, "安保人员确认接单"); + OperatorTypeEnum.SECURITY_GUARD, effectiveUserId, "安保人员确认接单"); - log.info("安保工单确认: orderId={}, userId={}", orderId, userId); + log.info("安保工单确认: orderId={}, userId={}", orderId, effectiveUserId); } @Override @@ -169,9 +177,12 @@ public class SecurityOrderServiceImpl implements SecurityOrderService { OpsOrderDO order = getOrderOrThrow(req.getOrderId()); validateOrderType(order); + // 如果 operatorId 为 null(open-api 调用),取已分配人员 + Long effectiveOperatorId = resolveOperatorId(req.getOrderId(), req.getOperatorId()); + // 状态转换 → COMPLETED(扩展表 completedTime + 业务日志由 EventListener 统一记录,主表 endTime 由状态机统一设置) orderStateMachine.transition(order, WorkOrderStatusEnum.COMPLETED, - OperatorTypeEnum.SECURITY_GUARD, req.getOperatorId(), "安保人员提交处理结果"); + OperatorTypeEnum.SECURITY_GUARD, effectiveOperatorId, "安保人员提交处理结果"); // 更新扩展表:结果 + 图片 OpsOrderSecurityExtDO extUpdate = new OpsOrderSecurityExtDO(); @@ -185,6 +196,29 @@ public class SecurityOrderServiceImpl implements SecurityOrderService { log.info("安保工单人工完单: orderId={}", req.getOrderId()); } + @Override + @Transactional(rollbackFor = Exception.class) + public void falseAlarmOrder(Long orderId, Long operatorId) { + OpsOrderDO order = getOrderOrThrow(orderId); + validateOrderType(order); + + // 如果 operatorId 为 null(open-api 调用),取已分配人员 + Long effectiveOperatorId = resolveOperatorId(orderId, operatorId); + + // 状态转换 → COMPLETED + orderStateMachine.transition(order, WorkOrderStatusEnum.COMPLETED, + OperatorTypeEnum.SECURITY_GUARD, effectiveOperatorId, "误报标记"); + + // 更新扩展表:标记误报 + 结果 + OpsOrderSecurityExtDO extUpdate = new OpsOrderSecurityExtDO(); + extUpdate.setOpsOrderId(orderId); + extUpdate.setFalseAlarm(true); + extUpdate.setResult("误报"); + securityExtMapper.insertOrUpdateSelective(extUpdate); + + log.info("安保工单误报标记: orderId={}", orderId); + } + @Override public OpsOrderSecurityExtDO getSecurityExt(Long opsOrderId) { return securityExtMapper.selectByOpsOrderId(opsOrderId); @@ -206,4 +240,19 @@ public class SecurityOrderServiceImpl implements SecurityOrderService { } } + /** + * 解析操作人ID:如果传入为 null,则取扩展表中已分配的安保人员 + */ + private Long resolveOperatorId(Long orderId, Long operatorId) { + if (operatorId != null) { + return operatorId; + } + OpsOrderSecurityExtDO ext = securityExtMapper.selectByOpsOrderId(orderId); + Long assignedUserId = ext != null ? ext.getAssignedUserId() : null; + if (assignedUserId == null) { + log.warn("工单未分配安保人员,操作人为空: orderId={}", orderId); + } + return assignedUserId; + } + }