feat(ops): 新增业务日志枚举类
- 新增 EventDomain:事件领域枚举 - 新增 EventLevel:事件级别枚举 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
package com.viewsh.module.ops.dal.dataobject.log;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import com.viewsh.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 通用业务事件日志 DO
|
||||
* <p>
|
||||
* 用于记录所有模块的业务事件日志,支持多模块(clean/repair/patrol等)
|
||||
* 采用方案A:在记录时<E5BD95><E697B6>步填充设备/人员名称冗余字段,便于展示时直接查询
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@TableName(value = "ops_business_event_log", autoResultMap = true)
|
||||
@KeySequence("ops_business_event_log_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class OpsBusinessEventLogDO extends BaseDO {
|
||||
|
||||
// ==================== 主键 ====================
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
// ==================== 时间与级别 ====================
|
||||
|
||||
/**
|
||||
* 事件时间
|
||||
*/
|
||||
private LocalDateTime eventTime;
|
||||
|
||||
/**
|
||||
* 日志级别(INFO=信息/WARN=警告/ERROR=错误)
|
||||
*/
|
||||
private String eventLevel;
|
||||
|
||||
// ==================== 业务分类 ====================
|
||||
|
||||
/**
|
||||
* 模块标识(clean=保洁/repair=维修/patrol=巡检)
|
||||
*/
|
||||
private String module;
|
||||
|
||||
/**
|
||||
* 事件域(DISPATCH=调度/BEACON=信标/TRAFFIC=客流/DEVICE=设备/SYSTEM=系统/AUDIT=审计)
|
||||
*/
|
||||
private String eventDomain;
|
||||
|
||||
/**
|
||||
* 事件类型(如:ARRIVE_CONFIRMED/TTS_SENT/AUTO_DISPATCH)
|
||||
*/
|
||||
private String eventType;
|
||||
|
||||
// ==================== 设备关联 ====================
|
||||
|
||||
/**
|
||||
* 设备ID(关联 iot_device.id)
|
||||
*/
|
||||
private Long deviceId;
|
||||
|
||||
/**
|
||||
* 设备名称(冗余字段,异步填充)
|
||||
*/
|
||||
private String deviceName;
|
||||
|
||||
/**
|
||||
* 设备编码(冗余字段,如设备序列号)
|
||||
*/
|
||||
private String deviceCode;
|
||||
|
||||
/**
|
||||
* 设备类型(冗余字段,如:BADGE/TRAFFIC_COUNTER/BEACON)
|
||||
*/
|
||||
private String deviceType;
|
||||
|
||||
// ==================== 人员关联 ====================
|
||||
|
||||
/**
|
||||
* 人员ID(保洁员/巡检员等)
|
||||
*/
|
||||
private Long personId;
|
||||
|
||||
/**
|
||||
* 人员姓名(冗余字段,异步填充)
|
||||
*/
|
||||
private String personName;
|
||||
|
||||
/**
|
||||
* 人员类型(冗余字段,如:CLEANER/INSPECTOR)
|
||||
*/
|
||||
private String personType;
|
||||
|
||||
// ==================== 业务实体关联 ====================
|
||||
|
||||
/**
|
||||
* 业务实体ID(如工单ID、区域ID)
|
||||
*/
|
||||
private Long targetId;
|
||||
|
||||
/**
|
||||
* 业务实体类型(order=工单/area=区域/task=任务)
|
||||
*/
|
||||
private String targetType;
|
||||
|
||||
// ==================== 日志内容 ====================
|
||||
|
||||
/**
|
||||
* 事件描述(完整描述)
|
||||
*/
|
||||
private String eventMessage;
|
||||
|
||||
/**
|
||||
* 简要摘要(用于列表展示,可选)
|
||||
*/
|
||||
private String eventSummary;
|
||||
|
||||
/**
|
||||
* 扩展数据(JSON格式,存储额外结构化信息)
|
||||
*/
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private Map<String, Object> eventPayload;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.viewsh.module.ops.dal.mysql.log;
|
||||
|
||||
import com.viewsh.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.viewsh.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.viewsh.module.ops.dal.dataobject.log.OpsBusinessEventLogDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 通用业务事件日志 Mapper
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@Mapper
|
||||
public interface OpsBusinessEventLogMapper extends BaseMapperX<OpsBusinessEventLogDO> {
|
||||
|
||||
/**
|
||||
* 根据模块查询日志
|
||||
*
|
||||
* @param module 模块标识(clean/repair/patrol)
|
||||
* @return 日志列表
|
||||
*/
|
||||
default List<OpsBusinessEventLogDO> selectListByModule(String module) {
|
||||
return selectList(new LambdaQueryWrapperX<OpsBusinessEventLogDO>()
|
||||
.eq(OpsBusinessEventLogDO::getModule, module)
|
||||
.orderByDesc(OpsBusinessEventLogDO::getEventTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模块和事件域查询日志
|
||||
*
|
||||
* @param module 模块标识
|
||||
* @param eventDomain 事件域
|
||||
* @return 日志列表
|
||||
*/
|
||||
default List<OpsBusinessEventLogDO> selectListByModuleAndDomain(String module, String eventDomain) {
|
||||
return selectList(new LambdaQueryWrapperX<OpsBusinessEventLogDO>()
|
||||
.eq(OpsBusinessEventLogDO::getModule, module)
|
||||
.eq(OpsBusinessEventLogDO::getEventDomain, eventDomain)
|
||||
.orderByDesc(OpsBusinessEventLogDO::getEventTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据设备ID查询日志
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @return 日志列表
|
||||
*/
|
||||
default List<OpsBusinessEventLogDO> selectListByDeviceId(Long deviceId) {
|
||||
return selectList(new LambdaQueryWrapperX<OpsBusinessEventLogDO>()
|
||||
.eq(OpsBusinessEventLogDO::getDeviceId, deviceId)
|
||||
.orderByDesc(OpsBusinessEventLogDO::getEventTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据人员ID查询日志
|
||||
*
|
||||
* @param personId 人员ID
|
||||
* @return 日志列表
|
||||
*/
|
||||
default List<OpsBusinessEventLogDO> selectListByPersonId(Long personId) {
|
||||
return selectList(new LambdaQueryWrapperX<OpsBusinessEventLogDO>()
|
||||
.eq(OpsBusinessEventLogDO::getPersonId, personId)
|
||||
.orderByDesc(OpsBusinessEventLogDO::getEventTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据目标实体查询日志
|
||||
*
|
||||
* @param targetType 目标类型(order/area/task)
|
||||
* @param targetId 目标ID
|
||||
* @return 日志列表
|
||||
*/
|
||||
default List<OpsBusinessEventLogDO> selectListByTarget(String targetType, Long targetId) {
|
||||
return selectList(new LambdaQueryWrapperX<OpsBusinessEventLogDO>()
|
||||
.eq(OpsBusinessEventLogDO::getTargetType, targetType)
|
||||
.eq(OpsBusinessEventLogDO::getTargetId, targetId)
|
||||
.orderByDesc(OpsBusinessEventLogDO::getEventTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据工单ID查询日志
|
||||
*
|
||||
* @param orderId 工单ID
|
||||
* @return 日志列表
|
||||
*/
|
||||
default List<OpsBusinessEventLogDO> selectListByOrderId(Long orderId) {
|
||||
return selectList(new LambdaQueryWrapperX<OpsBusinessEventLogDO>()
|
||||
.eq(OpsBusinessEventLogDO::getTargetType, "order")
|
||||
.eq(OpsBusinessEventLogDO::getTargetId, orderId)
|
||||
.orderByDesc(OpsBusinessEventLogDO::getEventTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据事件域和类型查询日志
|
||||
*
|
||||
* @param eventDomain 事件域
|
||||
* @param eventType 事件类型
|
||||
* @return 日志列表
|
||||
*/
|
||||
default List<OpsBusinessEventLogDO> selectListByDomainAndType(String eventDomain, String eventType) {
|
||||
return selectList(new LambdaQueryWrapperX<OpsBusinessEventLogDO>()
|
||||
.eq(OpsBusinessEventLogDO::getEventDomain, eventDomain)
|
||||
.eq(OpsBusinessEventLogDO::getEventType, eventType)
|
||||
.orderByDesc(OpsBusinessEventLogDO::getEventTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模块、事件域和类型查询日志
|
||||
*
|
||||
* @param module 模块标识
|
||||
* @param eventDomain 事件域
|
||||
* @param eventType 事件类型
|
||||
* @return 日志列表
|
||||
*/
|
||||
default List<OpsBusinessEventLogDO> selectListByModuleDomainAndType(String module, String eventDomain, String eventType) {
|
||||
return selectList(new LambdaQueryWrapperX<OpsBusinessEventLogDO>()
|
||||
.eq(OpsBusinessEventLogDO::getModule, module)
|
||||
.eq(OpsBusinessEventLogDO::getEventDomain, eventDomain)
|
||||
.eq(OpsBusinessEventLogDO::getEventType, eventType)
|
||||
.orderByDesc(OpsBusinessEventLogDO::getEventTime));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.viewsh.module.ops.infrastructure.log.enumeration;
|
||||
|
||||
/**
|
||||
* 事件域枚举
|
||||
* <p>
|
||||
* 用于标识业务事件发生的领域/模块
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
public enum EventDomain {
|
||||
|
||||
/**
|
||||
* 调度域 - 工单派发、分配等
|
||||
*/
|
||||
DISPATCH("dispatch", "调度"),
|
||||
|
||||
/**
|
||||
* 信标域 - 信标检测、到岗确认等
|
||||
*/
|
||||
BEACON("beacon", "信标"),
|
||||
|
||||
/**
|
||||
* 客流域 - 客流统计、阈值触发等
|
||||
*/
|
||||
TRAFFIC("traffic", "客流"),
|
||||
|
||||
/**
|
||||
* 设备域 - 设备控制、TTS<54><53>震动等
|
||||
*/
|
||||
DEVICE("device", "设备"),
|
||||
|
||||
/**
|
||||
* 系统域 - 系统级事件
|
||||
*/
|
||||
SYSTEM("system", "系统"),
|
||||
|
||||
/**
|
||||
* 审计域 - 审计日志
|
||||
*/
|
||||
AUDIT("audit", "审计");
|
||||
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
EventDomain(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.viewsh.module.ops.infrastructure.log.enumeration;
|
||||
|
||||
/**
|
||||
* 事件日志级别枚举
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
public enum EventLevel {
|
||||
|
||||
/**
|
||||
* 信息级别 - 正常业务流程
|
||||
*/
|
||||
INFO("INFO", "信息"),
|
||||
|
||||
/**
|
||||
* 警告级别 - 需要关注但不影响流程
|
||||
*/
|
||||
WARN("WARN", "警告"),
|
||||
|
||||
/**
|
||||
* 错误级别 - 业务异常或失败
|
||||
*/
|
||||
ERROR("ERROR", "错误");
|
||||
|
||||
private final String code;
|
||||
private final String description;
|
||||
|
||||
EventLevel(String code, String description) {
|
||||
this.code = code;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.viewsh.module.ops.infrastructure.log.recorder;
|
||||
|
||||
import com.viewsh.module.ops.dal.dataobject.log.OpsBusinessEventLogDO;
|
||||
|
||||
/**
|
||||
* 事件日志持久化接口
|
||||
* <p>
|
||||
* 负责将日志记录持久化到数据库
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
public interface EventLogPersister {
|
||||
|
||||
/**
|
||||
* 持久化日志记录(同步)
|
||||
*
|
||||
* @param recordDO 日志DO
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean persist(OpsBusinessEventLogDO recordDO);
|
||||
|
||||
/**
|
||||
* 持久化日志记录(异步)
|
||||
*
|
||||
* @param recordDO 日志DO
|
||||
*/
|
||||
void persistAsync(OpsBusinessEventLogDO recordDO);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.viewsh.module.ops.infrastructure.log.recorder;
|
||||
|
||||
import com.viewsh.module.ops.dal.dataobject.log.OpsBusinessEventLogDO;
|
||||
import com.viewsh.module.ops.dal.mysql.log.OpsBusinessEventLogMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 事件日志持久化实现
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class EventLogPersisterImpl implements EventLogPersister {
|
||||
|
||||
@Resource
|
||||
private OpsBusinessEventLogMapper eventLogMapper;
|
||||
|
||||
@Override
|
||||
public boolean persist(OpsBusinessEventLogDO recordDO) {
|
||||
try {
|
||||
int rows = eventLogMapper.insert(recordDO);
|
||||
return rows > 0;
|
||||
} catch (Exception e) {
|
||||
log.error("[EventLogPersister] 持久化失败: module={}, domain={}, type={}",
|
||||
recordDO.getModule(), recordDO.getEventDomain(), recordDO.getEventType(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Async("ops-task-executor")
|
||||
public void persistAsync(OpsBusinessEventLogDO recordDO) {
|
||||
persist(recordDO);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
package com.viewsh.module.ops.infrastructure.log.recorder;
|
||||
|
||||
import com.viewsh.module.ops.infrastructure.log.enumeration.EventDomain;
|
||||
import com.viewsh.module.ops.infrastructure.log.enumeration.EventLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 事件日志记录实体
|
||||
* <p>
|
||||
* 用于记录业务事件日志,包含设备、人员、业务实体的关联信息
|
||||
* 采用方案A:在记录时异步填充名称冗余字段,便于展示时直接查询
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class EventLogRecord {
|
||||
|
||||
// ==================== 基础分类字段 ====================
|
||||
|
||||
/**
|
||||
* 模块标识(如:clean, repair, patrol)
|
||||
*/
|
||||
private String module;
|
||||
|
||||
/**
|
||||
* 事件域
|
||||
*/
|
||||
private EventDomain domain;
|
||||
|
||||
/**
|
||||
* 事件类型(如:ARRIVE_CONFIRMED, TTS_SENT, AUTO_DISPATCH)
|
||||
*/
|
||||
private String eventType;
|
||||
|
||||
/**
|
||||
* 日志级别
|
||||
*/
|
||||
@Builder.Default
|
||||
private EventLevel level = EventLevel.INFO;
|
||||
|
||||
// ==================== 内容字段 ====================
|
||||
|
||||
/**
|
||||
* 事件描述(完整描述)
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 简要摘要(用于列表展示,可选)
|
||||
*/
|
||||
private String summary;
|
||||
|
||||
// ==================== 设备关联字段 ====================
|
||||
|
||||
/**
|
||||
* 设备ID
|
||||
*/
|
||||
private Long deviceId;
|
||||
|
||||
/**
|
||||
* 设备名称(冗余字段,异步填充)
|
||||
*/
|
||||
private String deviceName;
|
||||
|
||||
/**
|
||||
* 设备编码(冗余字段,如设备序列号)
|
||||
*/
|
||||
private String deviceCode;
|
||||
|
||||
/**
|
||||
* 设备类型(冗余字段,如:BADGE, TRAFFIC_COUNTER)
|
||||
*/
|
||||
private String deviceType;
|
||||
|
||||
// ==================== 人员关联字段 ====================
|
||||
|
||||
/**
|
||||
* 人员ID(保洁员、巡检员等)
|
||||
*/
|
||||
private Long personId;
|
||||
|
||||
/**
|
||||
* 人员姓名(冗余字段,异步填充)
|
||||
*/
|
||||
private String personName;
|
||||
|
||||
/**
|
||||
* 人员类型(冗余字段,如:CLEANER, INSPECTOR)
|
||||
*/
|
||||
private String personType;
|
||||
|
||||
// ==================== 业务实体关联字段 ====================
|
||||
|
||||
/**
|
||||
* 业务实体ID(如工单ID、区域ID)
|
||||
*/
|
||||
private Long targetId;
|
||||
|
||||
/**
|
||||
* 业务实体类型(如:order, area, task)
|
||||
*/
|
||||
private String targetType;
|
||||
|
||||
// ==================== 扩展字段 ====================
|
||||
|
||||
/**
|
||||
* 扩展数据(JSON格式)
|
||||
*/
|
||||
@Builder.Default
|
||||
private Map<String, Object> payload = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 事件时间
|
||||
*/
|
||||
@Builder.Default
|
||||
private LocalDateTime eventTime = LocalDateTime.now();
|
||||
|
||||
// ==================== 便捷方法 ====================
|
||||
|
||||
/**
|
||||
* 添加扩展数据
|
||||
*/
|
||||
public void putPayload(String key, Object value) {
|
||||
if (this.payload == null) {
|
||||
this.payload = new HashMap<>();
|
||||
}
|
||||
this.payload.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取扩展数据
|
||||
*/
|
||||
public Object getPayload(String key) {
|
||||
return payload != null ? payload.get(key) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取扩展数据(String类型)
|
||||
*/
|
||||
public String getPayloadString(String key) {
|
||||
Object value = getPayload(key);
|
||||
return value != null ? String.valueOf(value) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取扩展数据(Long类型)
|
||||
*/
|
||||
public Long getPayloadLong(String key) {
|
||||
Object value = getPayload(key);
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取扩展数据(Integer类型)
|
||||
*/
|
||||
public Integer getPayloadInt(String key) {
|
||||
Object value = getPayload(key);
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).intValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ==================== 静态构建方法 ====================
|
||||
|
||||
/**
|
||||
* 创建设备事件日志
|
||||
*/
|
||||
public static EventLogRecord forDevice(String module, EventDomain domain, String eventType,
|
||||
String message, Long deviceId) {
|
||||
return EventLogRecord.builder()
|
||||
.module(module)
|
||||
.domain(domain)
|
||||
.eventType(eventType)
|
||||
.message(message)
|
||||
.deviceId(deviceId)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建工单事件日志
|
||||
*/
|
||||
public static EventLogRecord forOrder(String module, EventDomain domain, String eventType,
|
||||
String message, Long orderId, Long deviceId, Long personId) {
|
||||
return EventLogRecord.builder()
|
||||
.module(module)
|
||||
.domain(domain)
|
||||
.eventType(eventType)
|
||||
.message(message)
|
||||
.targetId(orderId)
|
||||
.targetType("order")
|
||||
.deviceId(deviceId)
|
||||
.personId(personId)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建系统事件日志
|
||||
*/
|
||||
public static EventLogRecord forSystem(String module, EventDomain domain, String eventType,
|
||||
String message) {
|
||||
return EventLogRecord.builder()
|
||||
.module(module)
|
||||
.domain(domain)
|
||||
.eventType(eventType)
|
||||
.message(message)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.viewsh.module.ops.infrastructure.log.recorder;
|
||||
|
||||
import com.viewsh.module.ops.infrastructure.log.enumeration.EventDomain;
|
||||
|
||||
/**
|
||||
* 事件日志记录器接口
|
||||
* <p>
|
||||
* 提供业务事件日志记录的统一入口
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
public interface EventLogRecorder {
|
||||
|
||||
/**
|
||||
* 记录事件日志(同步)
|
||||
*
|
||||
* @param record 日志记录
|
||||
*/
|
||||
void record(EventLogRecord record);
|
||||
|
||||
/**
|
||||
* 记录事件日志(异步)
|
||||
*
|
||||
* @param record 日志记录
|
||||
*/
|
||||
void recordAsync(EventLogRecord record);
|
||||
|
||||
// ==================== 便捷方法:按级别记录 ====================
|
||||
|
||||
/**
|
||||
* 记录信息级别日志
|
||||
*
|
||||
* @param module 模块标识
|
||||
* @param domain 事件域
|
||||
* @param eventType 事件类型
|
||||
* @param message 事件消息
|
||||
*/
|
||||
void info(String module, EventDomain domain, String eventType, String message);
|
||||
|
||||
/**
|
||||
* 记录信息级别日志(带设备关联)
|
||||
*
|
||||
* @param module 模块标识
|
||||
* @param domain 事件域
|
||||
* @param eventType 事件类型
|
||||
* @param message 事件消息
|
||||
* @param deviceId 设备ID
|
||||
*/
|
||||
void info(String module, EventDomain domain, String eventType, String message, Long deviceId);
|
||||
|
||||
/**
|
||||
* 记录信息级别日志(带工单关联)
|
||||
*
|
||||
* @param module 模块标识
|
||||
* @param domain 事件域
|
||||
* @param eventType 事件类型
|
||||
* @param message 事件消息
|
||||
* @param orderId 工单ID
|
||||
* @param deviceId 设备ID
|
||||
* @param personId 人员ID
|
||||
*/
|
||||
void info(String module, EventDomain domain, String eventType, String message,
|
||||
Long orderId, Long deviceId, Long personId);
|
||||
|
||||
/**
|
||||
* 记录警告级别日志
|
||||
*
|
||||
* @param module 模块标识
|
||||
* @param domain 事件域
|
||||
* @param eventType 事件类型
|
||||
* @param message 事件消息
|
||||
*/
|
||||
void warn(String module, EventDomain domain, String eventType, String message);
|
||||
|
||||
/**
|
||||
* 记录警告级别日志(带设备关联)
|
||||
*
|
||||
* @param module 模块标识
|
||||
* @param domain 事件域
|
||||
* @param eventType 事件类型
|
||||
* @param message 事件消息
|
||||
* @param deviceId 设备ID
|
||||
*/
|
||||
void warn(String module, EventDomain domain, String eventType, String message, Long deviceId);
|
||||
|
||||
/**
|
||||
* 记录错误级别日志
|
||||
*
|
||||
* @param module 模块标识
|
||||
* @param domain 事件域
|
||||
* @param eventType 事件类型
|
||||
* @param message 事件消息
|
||||
* @param throwable 异常信息
|
||||
*/
|
||||
void error(String module, EventDomain domain, String eventType, String message, Throwable throwable);
|
||||
|
||||
/**
|
||||
* 记录错误级别日志(带设备关联)
|
||||
*
|
||||
* @param module 模块标识
|
||||
* @param domain 事件域
|
||||
* @param eventType 事件类型
|
||||
* @param message 事件消息
|
||||
* @param deviceId 设备ID
|
||||
* @param throwable 异常信息
|
||||
*/
|
||||
void error(String module, EventDomain domain, String eventType, String message,
|
||||
Long deviceId, Throwable throwable);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
package com.viewsh.module.ops.infrastructure.log.recorder;
|
||||
|
||||
import com.viewsh.module.ops.dal.dataobject.log.OpsBusinessEventLogDO;
|
||||
import com.viewsh.module.ops.infrastructure.log.enumeration.EventDomain;
|
||||
import com.viewsh.module.ops.infrastructure.log.enumeration.EventLevel;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 事件日志记录器实现
|
||||
* <p>
|
||||
* 负责记录业务事件日志,包含:
|
||||
* 1. 将 EventLogRecord 转换为 OpsBusinessEventLogDO
|
||||
* 2. 异步填充设备/人员名称(方案A:冗余存储)
|
||||
* 3. 持久化到数据库
|
||||
* 4. 输出到控制台(用于调试)
|
||||
* <p>
|
||||
* 设计说明:
|
||||
* - record() 方法默认异步执行,不阻塞主业务流程
|
||||
* - recordSync() 方法同步执行,用于需要确认日志写入成功的场景
|
||||
* - recordAsync() 方法保留,语义更明确
|
||||
*
|
||||
* @author lzh
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class EventLogRecorderImpl implements EventLogRecorder {
|
||||
|
||||
@Resource
|
||||
private EventLogPersister persister;
|
||||
|
||||
/**
|
||||
* 异步记录日志(默认方式)
|
||||
* <p>
|
||||
* 使用 @Async 确保日志记录不阻塞主业务流程
|
||||
*/
|
||||
@Override
|
||||
@Async("ops-task-executor")
|
||||
public void record(EventLogRecord record) {
|
||||
doRecord(record);
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步记录日志(语义明确的方法名)
|
||||
*/
|
||||
@Override
|
||||
@Async("ops-task-executor")
|
||||
public void recordAsync(EventLogRecord record) {
|
||||
doRecord(record);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步记录日志
|
||||
* <p>
|
||||
* 用于需要确认日志写入成功的场景(如测试、关键业务)
|
||||
*/
|
||||
public void recordSync(EventLogRecord record) {
|
||||
doRecord(record);
|
||||
}
|
||||
|
||||
/**
|
||||
* 实际执行记录逻辑
|
||||
*/
|
||||
private void doRecord(EventLogRecord record) {
|
||||
try {
|
||||
// 转换为 DO
|
||||
OpsBusinessEventLogDO recordDO = convertToDO(record);
|
||||
|
||||
// TODO: 方案A - 异步填充设备/人员名称
|
||||
// 这里预留接口,后续通过 Feign 调用 IoT/Ops 模块获取名称
|
||||
enrichRecord(recordDO);
|
||||
|
||||
// 持久化(同步写入,但在异步线程中执行)
|
||||
persister.persist(recordDO);
|
||||
|
||||
// 控制台输出(便于调试)
|
||||
logConsole(record);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("[EventLogRecorder] 记录失败: module={}, domain={}, type={}",
|
||||
record.getModule(), record.getDomain(), record.getEventType(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String module, EventDomain domain, String eventType, String message) {
|
||||
record(EventLogRecord.builder()
|
||||
.module(module)
|
||||
.domain(domain)
|
||||
.eventType(eventType)
|
||||
.message(message)
|
||||
.level(EventLevel.INFO)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String module, EventDomain domain, String eventType, String message, Long deviceId) {
|
||||
record(EventLogRecord.builder()
|
||||
.module(module)
|
||||
.domain(domain)
|
||||
.eventType(eventType)
|
||||
.message(message)
|
||||
.deviceId(deviceId)
|
||||
.level(EventLevel.INFO)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String module, EventDomain domain, String eventType, String message,
|
||||
Long orderId, Long deviceId, Long personId) {
|
||||
record(EventLogRecord.builder()
|
||||
.module(module)
|
||||
.domain(domain)
|
||||
.eventType(eventType)
|
||||
.message(message)
|
||||
.targetId(orderId)
|
||||
.targetType("order")
|
||||
.deviceId(deviceId)
|
||||
.personId(personId)
|
||||
.level(EventLevel.INFO)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String module, EventDomain domain, String eventType, String message) {
|
||||
record(EventLogRecord.builder()
|
||||
.module(module)
|
||||
.domain(domain)
|
||||
.eventType(eventType)
|
||||
.message(message)
|
||||
.level(EventLevel.WARN)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String module, EventDomain domain, String eventType, String message, Long deviceId) {
|
||||
record(EventLogRecord.builder()
|
||||
.module(module)
|
||||
.domain(domain)
|
||||
.eventType(eventType)
|
||||
.message(message)
|
||||
.deviceId(deviceId)
|
||||
.level(EventLevel.WARN)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String module, EventDomain domain, String eventType, String message, Throwable throwable) {
|
||||
EventLogRecord record = EventLogRecord.builder()
|
||||
.module(module)
|
||||
.domain(domain)
|
||||
.eventType(eventType)
|
||||
.message(message)
|
||||
.level(EventLevel.ERROR)
|
||||
.build();
|
||||
if (throwable != null) {
|
||||
record.putPayload("errorMessage", throwable.getMessage());
|
||||
record.putPayload("errorClass", throwable.getClass().getSimpleName());
|
||||
}
|
||||
record(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String module, EventDomain domain, String eventType, String message,
|
||||
Long deviceId, Throwable throwable) {
|
||||
EventLogRecord record = EventLogRecord.builder()
|
||||
.module(module)
|
||||
.domain(domain)
|
||||
.eventType(eventType)
|
||||
.message(message)
|
||||
.deviceId(deviceId)
|
||||
.level(EventLevel.ERROR)
|
||||
.build();
|
||||
if (throwable != null) {
|
||||
record.putPayload("errorMessage", throwable.getMessage());
|
||||
record.putPayload("errorClass", throwable.getClass().getSimpleName());
|
||||
}
|
||||
record(record);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为 DO
|
||||
*/
|
||||
private OpsBusinessEventLogDO convertToDO(EventLogRecord record) {
|
||||
return OpsBusinessEventLogDO.builder()
|
||||
.eventTime(record.getEventTime())
|
||||
.eventLevel(record.getLevel() != null ? record.getLevel().getCode() : EventLevel.INFO.getCode())
|
||||
.module(record.getModule())
|
||||
.eventDomain(record.getDomain() != null ? record.getDomain().getCode() : null)
|
||||
.eventType(record.getEventType())
|
||||
.deviceId(record.getDeviceId())
|
||||
.deviceName(record.getDeviceName())
|
||||
.deviceCode(record.getDeviceCode())
|
||||
.deviceType(record.getDeviceType())
|
||||
.personId(record.getPersonId())
|
||||
.personName(record.getPersonName())
|
||||
.personType(record.getPersonType())
|
||||
.targetId(record.getTargetId())
|
||||
.targetType(record.getTargetType())
|
||||
.eventMessage(record.getMessage())
|
||||
.eventSummary(record.getSummary())
|
||||
.eventPayload(record.getPayload())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步填充设备/人员名称(方案A)
|
||||
* <p>
|
||||
* TODO: 后续通过 Feign 调用以下接口异步填充:
|
||||
* - IotDeviceApi.getBasicInfo(deviceId) -> 获取设备名称、编码、类型
|
||||
* - CleanerService.getBasicInfo(personId) -> 获取人员姓名、类型
|
||||
* <p>
|
||||
* 当前先留空,待 Feign 接口创建后再补充
|
||||
*/
|
||||
private void enrichRecord(OpsBusinessEventLogDO recordDO) {
|
||||
// 暂不实现异步填充,待后续完成 Feign 接口后再补充
|
||||
// 设计时考虑:
|
||||
// 1. 如果 recordDO.getDeviceId() 不为空,调用 IoT 模块获取设备信息
|
||||
// 2. 如果 recordDO.getPersonId() 不为空,调用 Ops 模块获取人员信息
|
||||
// 3. 填充冗余字段:deviceName, deviceCode, deviceType, personName, personType
|
||||
}
|
||||
|
||||
/**
|
||||
* 控制台输出(便于调试)
|
||||
*/
|
||||
private void logConsole(EventLogRecord record) {
|
||||
String deviceInfo = record.getDeviceId() != null
|
||||
? " [设备:" + record.getDeviceId() + (record.getDeviceName() != null ? "(" + record.getDeviceName() + ")" : "") + "]"
|
||||
: "";
|
||||
String personInfo = record.getPersonId() != null
|
||||
? " [人员:" + record.getPersonId() + (record.getPersonName() != null ? "(" + record.getPersonName() + ")" : "") + "]"
|
||||
: "";
|
||||
String targetInfo = record.getTargetId() != null
|
||||
? " [" + record.getTargetType() + ":" + record.getTargetId() + "]"
|
||||
: "";
|
||||
|
||||
log.info("[EventLog] [{}] {} {} {} - {}{}{}",
|
||||
record.getLevel() != null ? record.getLevel().getCode() : "INFO",
|
||||
record.getModule(),
|
||||
record.getDomain() != null ? record.getDomain().getCode() : "UNKNOWN",
|
||||
record.getEventType(),
|
||||
record.getMessage(),
|
||||
deviceInfo,
|
||||
personInfo,
|
||||
targetInfo);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user