【验收】6-保护方案告警同步云端&& 【验收】7-下发操作日志同步云端
This commit is contained in:
@ -0,0 +1,104 @@
|
|||||||
|
package com.xzzn.framework.aspectj;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.xzzn.common.utils.StringUtils;
|
||||||
|
import com.xzzn.ems.domain.EmsAlarmRecords;
|
||||||
|
import com.xzzn.ems.domain.MqttSyncLog;
|
||||||
|
import com.xzzn.ems.mapper.EmsMqttTopicConfigMapper;
|
||||||
|
import com.xzzn.ems.mapper.MqttSyncLogMapper;
|
||||||
|
import com.xzzn.framework.web.service.MqttPublisher;
|
||||||
|
import org.apache.juli.logging.Log;
|
||||||
|
import org.apache.juli.logging.LogFactory;
|
||||||
|
import org.aspectj.lang.JoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.AfterReturning;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.annotation.Pointcut;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本地保护告警信息同步云端
|
||||||
|
* 本地 - 云端
|
||||||
|
* @author xzzn
|
||||||
|
*/
|
||||||
|
@Aspect
|
||||||
|
@Component
|
||||||
|
public class FaultPlanAlarmAspect
|
||||||
|
{
|
||||||
|
|
||||||
|
private static final Log logger = LogFactory.getLog(FaultPlanAlarmAspect.class);
|
||||||
|
|
||||||
|
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
@Autowired
|
||||||
|
private MqttSyncLogMapper mqttSyncLogMapper;
|
||||||
|
@Autowired
|
||||||
|
private MqttPublisher mqttPublisher;
|
||||||
|
@Autowired
|
||||||
|
private EmsMqttTopicConfigMapper emsMqttTopicConfigMapper;
|
||||||
|
|
||||||
|
private static final String MQTT_TOPIC = "FAULT_ALARM_RECORD_UP";
|
||||||
|
private static final String TABLE_NAME = "ems_alarm_records";
|
||||||
|
// 切入点:拦截所有添加了@SyncAfterInsert注解的方法
|
||||||
|
@Pointcut("@annotation(com.xzzn.common.annotation.SyncAfterInsert)")
|
||||||
|
public void syncInsertPointcut() {}
|
||||||
|
|
||||||
|
// 方法执行成功后同步数据
|
||||||
|
@AfterReturning(pointcut = "syncInsertPointcut()", returning = "result")
|
||||||
|
public void afterInsert(JoinPoint joinPoint, Integer result) {
|
||||||
|
logger.info("【新增保护策略告警数据切面进入成功】");
|
||||||
|
if (result == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 校验是否配置监听topic-监听则不发布
|
||||||
|
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
|
||||||
|
if (!StringUtils.isEmpty(topic)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取参数中的EmsAlarmRecords对象
|
||||||
|
Object[] args = joinPoint.getArgs();
|
||||||
|
EmsAlarmRecords alarmRecords = null;
|
||||||
|
for (Object arg : args) {
|
||||||
|
if (arg instanceof EmsAlarmRecords) {
|
||||||
|
alarmRecords = (EmsAlarmRecords) arg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (alarmRecords == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建日志同步消息
|
||||||
|
MqttSyncLog message = createMessageObject("INSERT", alarmRecords.getSiteId());
|
||||||
|
try {
|
||||||
|
// 同步内容
|
||||||
|
String content = objectMapper.writeValueAsString(alarmRecords);
|
||||||
|
message.setContent(content);
|
||||||
|
|
||||||
|
// 发布到MQTT主题-同步到云端
|
||||||
|
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
|
||||||
|
} catch (Exception e) {
|
||||||
|
message.setStatus("FAIL");
|
||||||
|
message.setErrorMsg(e.getMessage());
|
||||||
|
}
|
||||||
|
// 存储同步信息
|
||||||
|
mqttSyncLogMapper.insertMqttSyncLog(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建同步信息
|
||||||
|
private MqttSyncLog createMessageObject(String operateType, String siteId) {
|
||||||
|
MqttSyncLog message = new MqttSyncLog();
|
||||||
|
message.setSyncId(UUID.randomUUID().toString());
|
||||||
|
message.setOperateType(operateType);
|
||||||
|
message.setTableName(TABLE_NAME);
|
||||||
|
message.setCreateTime(new Date());
|
||||||
|
message.setTopic(MQTT_TOPIC);
|
||||||
|
message.setStatus("SUCCESS");
|
||||||
|
message.setSyncObject(siteId);
|
||||||
|
message.setTarget("CLOUD");
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,98 @@
|
|||||||
|
package com.xzzn.framework.aspectj;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.xzzn.common.utils.StringUtils;
|
||||||
|
import com.xzzn.ems.domain.EmsFaultIssueLog;
|
||||||
|
import com.xzzn.ems.domain.MqttSyncLog;
|
||||||
|
import com.xzzn.ems.mapper.EmsMqttTopicConfigMapper;
|
||||||
|
import com.xzzn.ems.mapper.MqttSyncLogMapper;
|
||||||
|
import com.xzzn.framework.web.service.MqttPublisher;
|
||||||
|
import org.apache.juli.logging.Log;
|
||||||
|
import org.apache.juli.logging.LogFactory;
|
||||||
|
import org.aspectj.lang.JoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.AfterReturning;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.annotation.Pointcut;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保护告警方案下发日志同步
|
||||||
|
* 本地 - 云端
|
||||||
|
*/
|
||||||
|
@Aspect
|
||||||
|
@Component
|
||||||
|
public class FaultPlanIssueAspect {
|
||||||
|
|
||||||
|
private static final Log logger = LogFactory.getLog(FaultPlanIssueAspect.class);
|
||||||
|
@Autowired
|
||||||
|
private MqttPublisher mqttPublisher;
|
||||||
|
|
||||||
|
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
private static final String MQTT_TOPIC = "FAULT_PLAN_ISSUE_UP";
|
||||||
|
private static final String TABLE_NAME = "ems_fault_issue_log";
|
||||||
|
@Autowired
|
||||||
|
private EmsMqttTopicConfigMapper emsMqttTopicConfigMapper;
|
||||||
|
@Autowired
|
||||||
|
private MqttSyncLogMapper mqttSyncLogMapper;
|
||||||
|
|
||||||
|
// 定义切点:拦截策略相关表的Mapper方法
|
||||||
|
@Pointcut("(execution(* com.xzzn.ems.mapper.EmsFaultIssueLogMapper.insertEmsFaultIssueLog(..)) && args(insertEntity)) ")
|
||||||
|
public void insertPointCut(EmsFaultIssueLog insertEntity) {
|
||||||
|
logger.info("【新增保护告警策略下发数据】FaultPlanIssueAspect 实例化");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方法执行成功后发布同步消息
|
||||||
|
@AfterReturning(pointcut = "insertPointCut(insertEntity)", returning = "result")
|
||||||
|
public void afterInsert(JoinPoint joinPoint, EmsFaultIssueLog insertEntity, Integer result) {
|
||||||
|
logger.info("【新增保护告警策略下发数据切面进入成功】");
|
||||||
|
if (result == 0 || insertEntity == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 校验是否配置监听topic-监听则不发布
|
||||||
|
String topic = emsMqttTopicConfigMapper.checkTopicIsExist(MQTT_TOPIC);
|
||||||
|
if (!StringUtils.isEmpty(topic)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析方法名,获取操作类型(INSERT/UPDATE/DELETE)和表名
|
||||||
|
String operateType = "INSERT";
|
||||||
|
String siteId = insertEntity.getSiteId();
|
||||||
|
|
||||||
|
// 构建日志同步消息
|
||||||
|
MqttSyncLog message = createMessageObject(operateType,siteId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 数据转换
|
||||||
|
String content = objectMapper.writeValueAsString(insertEntity);
|
||||||
|
message.setContent(content);
|
||||||
|
|
||||||
|
// mqtt同步到云端
|
||||||
|
mqttPublisher.publish(MQTT_TOPIC, objectMapper.writeValueAsString(message), 1);
|
||||||
|
} catch (Exception e) {
|
||||||
|
message.setStatus("FAIL");
|
||||||
|
message.setErrorMsg(e.getMessage());
|
||||||
|
}
|
||||||
|
// 存储同步信息
|
||||||
|
mqttSyncLogMapper.insertMqttSyncLog(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建同步信息
|
||||||
|
private MqttSyncLog createMessageObject(String operateType, String siteId) {
|
||||||
|
MqttSyncLog message = new MqttSyncLog();
|
||||||
|
message.setSyncId(UUID.randomUUID().toString());
|
||||||
|
message.setOperateType(operateType);
|
||||||
|
message.setTableName(TABLE_NAME);
|
||||||
|
message.setCreateTime(new Date());
|
||||||
|
message.setTopic(MQTT_TOPIC);
|
||||||
|
message.setStatus("SUCCESS");
|
||||||
|
message.setSyncObject(siteId);
|
||||||
|
message.setTarget("CLOUD");
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSON;
|
|||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.xzzn.common.annotation.SyncAfterInsert;
|
||||||
import com.xzzn.common.constant.RedisKeyConstants;
|
import com.xzzn.common.constant.RedisKeyConstants;
|
||||||
import com.xzzn.common.core.redis.RedisCache;
|
import com.xzzn.common.core.redis.RedisCache;
|
||||||
import com.xzzn.common.enums.AlarmLevelStatus;
|
import com.xzzn.common.enums.AlarmLevelStatus;
|
||||||
@ -11,16 +12,10 @@ import com.xzzn.common.enums.AlarmStatus;
|
|||||||
import com.xzzn.common.enums.ProtPlanStatus;
|
import com.xzzn.common.enums.ProtPlanStatus;
|
||||||
import com.xzzn.common.enums.StrategyStatus;
|
import com.xzzn.common.enums.StrategyStatus;
|
||||||
import com.xzzn.common.utils.StringUtils;
|
import com.xzzn.common.utils.StringUtils;
|
||||||
import com.xzzn.ems.domain.EmsAlarmRecords;
|
import com.xzzn.ems.domain.*;
|
||||||
import com.xzzn.ems.domain.EmsDevicesSetting;
|
|
||||||
import com.xzzn.ems.domain.EmsFaultProtectionPlan;
|
|
||||||
import com.xzzn.ems.domain.EmsStrategyRunning;
|
|
||||||
import com.xzzn.ems.domain.vo.ProtectionPlanVo;
|
import com.xzzn.ems.domain.vo.ProtectionPlanVo;
|
||||||
import com.xzzn.ems.domain.vo.ProtectionSettingVo;
|
import com.xzzn.ems.domain.vo.ProtectionSettingVo;
|
||||||
import com.xzzn.ems.mapper.EmsAlarmRecordsMapper;
|
import com.xzzn.ems.mapper.*;
|
||||||
import com.xzzn.ems.mapper.EmsDevicesSettingMapper;
|
|
||||||
import com.xzzn.ems.mapper.EmsFaultProtectionPlanMapper;
|
|
||||||
import com.xzzn.ems.mapper.EmsStrategyRunningMapper;
|
|
||||||
import com.xzzn.ems.service.IEmsFaultProtectionPlanService;
|
import com.xzzn.ems.service.IEmsFaultProtectionPlanService;
|
||||||
import com.xzzn.framework.manager.ModbusConnectionManager;
|
import com.xzzn.framework.manager.ModbusConnectionManager;
|
||||||
import com.xzzn.framework.manager.ModbusConnectionWrapper;
|
import com.xzzn.framework.manager.ModbusConnectionWrapper;
|
||||||
@ -33,10 +28,7 @@ import org.springframework.util.ObjectUtils;
|
|||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -70,6 +62,8 @@ public class ProtectionPlanTask {
|
|||||||
private ModbusConnectionManager connectionManager;
|
private ModbusConnectionManager connectionManager;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ModbusService modbusService;
|
private ModbusService modbusService;
|
||||||
|
@Autowired
|
||||||
|
private EmsFaultIssueLogMapper emsFaultIssueLogMapper;
|
||||||
|
|
||||||
public ProtectionPlanTask(IEmsFaultProtectionPlanService iEmsFaultProtectionPlanService) {
|
public ProtectionPlanTask(IEmsFaultProtectionPlanService iEmsFaultProtectionPlanService) {
|
||||||
this.iEmsFaultProtectionPlanService = iEmsFaultProtectionPlanService;
|
this.iEmsFaultProtectionPlanService = iEmsFaultProtectionPlanService;
|
||||||
@ -110,6 +104,8 @@ public class ProtectionPlanTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 处理告警保护方案-返回触发下发方案时是否最高等级
|
// 处理告警保护方案-返回触发下发方案时是否最高等级
|
||||||
|
// 需要同步云端
|
||||||
|
@SyncAfterInsert
|
||||||
private boolean dealWithProtectionPlan(EmsFaultProtectionPlan plan, List<ProtectionSettingVo> protSettings) {
|
private boolean dealWithProtectionPlan(EmsFaultProtectionPlan plan, List<ProtectionSettingVo> protSettings) {
|
||||||
boolean isHighLevel = false;
|
boolean isHighLevel = false;
|
||||||
|
|
||||||
@ -140,7 +136,7 @@ public class ProtectionPlanTask {
|
|||||||
String protPlanJson = plan.getProtectionPlan();
|
String protPlanJson = plan.getProtectionPlan();
|
||||||
if (protPlanJson != null && !protPlanJson.isEmpty()) {
|
if (protPlanJson != null && !protPlanJson.isEmpty()) {
|
||||||
logger.info("<下发保护方案> 方案内容:{}", protPlanJson);
|
logger.info("<下发保护方案> 方案内容:{}", protPlanJson);
|
||||||
//executeProtectionActions(protPlanJson,siteId,plan.getId()); // 执行Modbus指令
|
executeProtectionActions(protPlanJson,siteId,plan.getId(),plan.getFaultLevel()); // 执行Modbus指令
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新方案状态为“已启用”
|
// 更新方案状态为“已启用”
|
||||||
@ -181,7 +177,7 @@ public class ProtectionPlanTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 下发保护方案
|
// 下发保护方案
|
||||||
private void executeProtectionActions(String protPlanJson, String siteId, Long planId){
|
private void executeProtectionActions(String protPlanJson, String siteId, Long planId, Integer faultLevel){
|
||||||
final List<ProtectionPlanVo> protPlanList;
|
final List<ProtectionPlanVo> protPlanList;
|
||||||
try {
|
try {
|
||||||
protPlanList = objectMapper.readValue(
|
protPlanList = objectMapper.readValue(
|
||||||
@ -197,6 +193,10 @@ public class ProtectionPlanTask {
|
|||||||
if (StringUtils.isEmpty(plan.getDeviceId()) || StringUtils.isEmpty(plan.getPoint())) {
|
if (StringUtils.isEmpty(plan.getDeviceId()) || StringUtils.isEmpty(plan.getPoint())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 给设备发送指令记录日志,并同步云端
|
||||||
|
EmsFaultIssueLog faultIssueLog = createLogEntity(plan,siteId);
|
||||||
|
faultIssueLog.setLogLevel(faultLevel);
|
||||||
|
emsFaultIssueLogMapper.insertEmsFaultIssueLog(faultIssueLog);
|
||||||
|
|
||||||
// 通过modbus连接设备,发送数据
|
// 通过modbus连接设备,发送数据
|
||||||
executeSinglePlan(plan,siteId);
|
executeSinglePlan(plan,siteId);
|
||||||
@ -208,6 +208,19 @@ public class ProtectionPlanTask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private EmsFaultIssueLog createLogEntity(ProtectionPlanVo plan,String siteId) {
|
||||||
|
EmsFaultIssueLog faultIssueLog = new EmsFaultIssueLog();
|
||||||
|
faultIssueLog.setLogId(UUID.randomUUID().toString());
|
||||||
|
faultIssueLog.setLogTime(new Date());
|
||||||
|
faultIssueLog.setSiteId(siteId);
|
||||||
|
faultIssueLog.setDeviceId(plan.getDeviceId());
|
||||||
|
faultIssueLog.setPoint(plan.getPoint());
|
||||||
|
faultIssueLog.setValue(plan.getValue());
|
||||||
|
faultIssueLog.setCreateBy("sys");
|
||||||
|
faultIssueLog.setCreateTime(new Date());
|
||||||
|
return faultIssueLog;
|
||||||
|
}
|
||||||
|
|
||||||
private void executeSinglePlan(ProtectionPlanVo plan, String siteId) throws Exception {
|
private void executeSinglePlan(ProtectionPlanVo plan, String siteId) throws Exception {
|
||||||
String deviceId = plan.getDeviceId();
|
String deviceId = plan.getDeviceId();
|
||||||
// 获取设备地址信息
|
// 获取设备地址信息
|
||||||
|
|||||||
@ -0,0 +1,150 @@
|
|||||||
|
package com.xzzn.ems.domain;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import com.xzzn.common.core.domain.BaseEntity;
|
||||||
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
|
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||||
|
import com.xzzn.common.annotation.Excel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 告警保护方案下发日志对象 ems_fault_issue_log
|
||||||
|
*
|
||||||
|
* @author xzzn
|
||||||
|
* @date 2025-11-15
|
||||||
|
*/
|
||||||
|
public class EmsFaultIssueLog extends BaseEntity
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 主键 */
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/** 日志ID(UUID) */
|
||||||
|
@Excel(name = "日志ID", readConverterExp = "U=UID")
|
||||||
|
private String logId;
|
||||||
|
|
||||||
|
/** 日志时间(精确到秒) */
|
||||||
|
@Excel(name = "日志时间", readConverterExp = "精=确到秒")
|
||||||
|
private Date logTime;
|
||||||
|
|
||||||
|
/** 日志等级(同方案等级1/2/3等) */
|
||||||
|
@Excel(name = "日志等级", readConverterExp = "同=方案等级1/2/3等")
|
||||||
|
private Integer logLevel;
|
||||||
|
|
||||||
|
/** 站点名称/编号 */
|
||||||
|
@Excel(name = "站点名称/编号")
|
||||||
|
private String siteId;
|
||||||
|
|
||||||
|
/** 设备ID */
|
||||||
|
@Excel(name = "设备ID")
|
||||||
|
private String deviceId;
|
||||||
|
|
||||||
|
/** 操作点位 */
|
||||||
|
@Excel(name = "操作点位")
|
||||||
|
private String point;
|
||||||
|
|
||||||
|
/** 设置数据 */
|
||||||
|
@Excel(name = "设置数据")
|
||||||
|
private BigDecimal value;
|
||||||
|
|
||||||
|
public void setId(Long id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogId(String logId)
|
||||||
|
{
|
||||||
|
this.logId = logId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLogId()
|
||||||
|
{
|
||||||
|
return logId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogTime(Date logTime)
|
||||||
|
{
|
||||||
|
this.logTime = logTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLogTime()
|
||||||
|
{
|
||||||
|
return logTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogLevel(Integer logLevel)
|
||||||
|
{
|
||||||
|
this.logLevel = logLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getLogLevel()
|
||||||
|
{
|
||||||
|
return logLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSiteId(String siteId)
|
||||||
|
{
|
||||||
|
this.siteId = siteId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSiteId()
|
||||||
|
{
|
||||||
|
return siteId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeviceId(String deviceId)
|
||||||
|
{
|
||||||
|
this.deviceId = deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDeviceId()
|
||||||
|
{
|
||||||
|
return deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPoint(String point)
|
||||||
|
{
|
||||||
|
this.point = point;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPoint()
|
||||||
|
{
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(BigDecimal value)
|
||||||
|
{
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getValue()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||||
|
.append("id", getId())
|
||||||
|
.append("logId", getLogId())
|
||||||
|
.append("logTime", getLogTime())
|
||||||
|
.append("logLevel", getLogLevel())
|
||||||
|
.append("site", getSiteId())
|
||||||
|
.append("deviceId", getDeviceId())
|
||||||
|
.append("point", getPoint())
|
||||||
|
.append("value", getValue())
|
||||||
|
.append("createBy", getCreateBy())
|
||||||
|
.append("createTime", getCreateTime())
|
||||||
|
.append("updateBy", getUpdateBy())
|
||||||
|
.append("updateTime", getUpdateTime())
|
||||||
|
.append("remark", getRemark())
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
package com.xzzn.ems.mapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import com.xzzn.ems.domain.EmsFaultIssueLog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 告警保护方案下发日志Mapper接口
|
||||||
|
*
|
||||||
|
* @author xzzn
|
||||||
|
* @date 2025-11-15
|
||||||
|
*/
|
||||||
|
public interface EmsFaultIssueLogMapper
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 查询告警保护方案下发日志
|
||||||
|
*
|
||||||
|
* @param id 告警保护方案下发日志主键
|
||||||
|
* @return 告警保护方案下发日志
|
||||||
|
*/
|
||||||
|
public EmsFaultIssueLog selectEmsFaultIssueLogById(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询告警保护方案下发日志列表
|
||||||
|
*
|
||||||
|
* @param emsFaultIssueLog 告警保护方案下发日志
|
||||||
|
* @return 告警保护方案下发日志集合
|
||||||
|
*/
|
||||||
|
public List<EmsFaultIssueLog> selectEmsFaultIssueLogList(EmsFaultIssueLog emsFaultIssueLog);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增告警保护方案下发日志
|
||||||
|
*
|
||||||
|
* @param emsFaultIssueLog 告警保护方案下发日志
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public int insertEmsFaultIssueLog(EmsFaultIssueLog emsFaultIssueLog);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改告警保护方案下发日志
|
||||||
|
*
|
||||||
|
* @param emsFaultIssueLog 告警保护方案下发日志
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public int updateEmsFaultIssueLog(EmsFaultIssueLog emsFaultIssueLog);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除告警保护方案下发日志
|
||||||
|
*
|
||||||
|
* @param id 告警保护方案下发日志主键
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public int deleteEmsFaultIssueLogById(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除告警保护方案下发日志
|
||||||
|
*
|
||||||
|
* @param ids 需要删除的数据主键集合
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public int deleteEmsFaultIssueLogByIds(Long[] ids);
|
||||||
|
|
||||||
|
// 根据logId获取日志
|
||||||
|
public EmsFaultIssueLog selectEmsFaultIssueLogByLogId(String logId);
|
||||||
|
}
|
||||||
@ -95,4 +95,7 @@ public interface IEmsAlarmRecordsService
|
|||||||
|
|
||||||
// 根据site_id和deviceId更新对应的redis
|
// 根据site_id和deviceId更新对应的redis
|
||||||
public void updateRedisAlarmRecords(String siteId, String deviceId);
|
public void updateRedisAlarmRecords(String siteId, String deviceId);
|
||||||
|
|
||||||
|
// 处理本地端同步的保护策略告警信息
|
||||||
|
public void dealSyncData(String content, String operateType);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,64 @@
|
|||||||
|
package com.xzzn.ems.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import com.xzzn.ems.domain.EmsFaultIssueLog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 告警保护方案下发日志Service接口
|
||||||
|
*
|
||||||
|
* @author xzzn
|
||||||
|
* @date 2025-11-15
|
||||||
|
*/
|
||||||
|
public interface IEmsFaultIssueLogService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 查询告警保护方案下发日志
|
||||||
|
*
|
||||||
|
* @param id 告警保护方案下发日志主键
|
||||||
|
* @return 告警保护方案下发日志
|
||||||
|
*/
|
||||||
|
public EmsFaultIssueLog selectEmsFaultIssueLogById(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询告警保护方案下发日志列表
|
||||||
|
*
|
||||||
|
* @param emsFaultIssueLog 告警保护方案下发日志
|
||||||
|
* @return 告警保护方案下发日志集合
|
||||||
|
*/
|
||||||
|
public List<EmsFaultIssueLog> selectEmsFaultIssueLogList(EmsFaultIssueLog emsFaultIssueLog);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增告警保护方案下发日志
|
||||||
|
*
|
||||||
|
* @param emsFaultIssueLog 告警保护方案下发日志
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public int insertEmsFaultIssueLog(EmsFaultIssueLog emsFaultIssueLog);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改告警保护方案下发日志
|
||||||
|
*
|
||||||
|
* @param emsFaultIssueLog 告警保护方案下发日志
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public int updateEmsFaultIssueLog(EmsFaultIssueLog emsFaultIssueLog);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除告警保护方案下发日志
|
||||||
|
*
|
||||||
|
* @param ids 需要删除的告警保护方案下发日志主键集合
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public int deleteEmsFaultIssueLogByIds(Long[] ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除告警保护方案下发日志信息
|
||||||
|
*
|
||||||
|
* @param id 告警保护方案下发日志主键
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public int deleteEmsFaultIssueLogById(Long id);
|
||||||
|
|
||||||
|
// 处理本地端同步的告警保护下发日志信息
|
||||||
|
public void dealSyncData(String content, String operateType);
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ import java.util.*;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
import com.xzzn.common.constant.RedisKeyConstants;
|
import com.xzzn.common.constant.RedisKeyConstants;
|
||||||
import com.xzzn.common.core.domain.entity.SysUser;
|
import com.xzzn.common.core.domain.entity.SysUser;
|
||||||
import com.xzzn.common.core.redis.RedisCache;
|
import com.xzzn.common.core.redis.RedisCache;
|
||||||
@ -18,7 +19,6 @@ import com.xzzn.ems.domain.vo.AlarmRecordListRequestVo;
|
|||||||
import com.xzzn.ems.domain.vo.AlarmRecordListResponseVo;
|
import com.xzzn.ems.domain.vo.AlarmRecordListResponseVo;
|
||||||
import com.xzzn.ems.mapper.EmsAlarmMatchDataMapper;
|
import com.xzzn.ems.mapper.EmsAlarmMatchDataMapper;
|
||||||
import com.xzzn.ems.mapper.EmsTicketMapper;
|
import com.xzzn.ems.mapper.EmsTicketMapper;
|
||||||
import com.xzzn.ems.service.IEmsTicketService;
|
|
||||||
import com.xzzn.system.mapper.SysUserMapper;
|
import com.xzzn.system.mapper.SysUserMapper;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -292,5 +292,21 @@ public class EmsAlarmRecordsServiceImpl implements IEmsAlarmRecordsService
|
|||||||
redisCache.setAllCacheMapValue(redisKey, newAlarms);
|
redisCache.setAllCacheMapValue(redisKey, newAlarms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dealSyncData(String content, String operateType) {
|
||||||
|
if (StringUtils.isEmpty(content)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EmsAlarmRecords alarmRecords = JSON.parseObject(content, EmsAlarmRecords.class);
|
||||||
|
switch(operateType) {
|
||||||
|
case "INSERT":
|
||||||
|
insertEmsAlarmRecords(alarmRecords);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,129 @@
|
|||||||
|
package com.xzzn.ems.service.impl;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.xzzn.common.utils.DateUtils;
|
||||||
|
import com.xzzn.common.utils.StringUtils;
|
||||||
|
import com.xzzn.ems.domain.EmsAlarmRecords;
|
||||||
|
import com.xzzn.ems.service.IFXXAlarmDataProcessService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import com.xzzn.ems.mapper.EmsFaultIssueLogMapper;
|
||||||
|
import com.xzzn.ems.domain.EmsFaultIssueLog;
|
||||||
|
import com.xzzn.ems.service.IEmsFaultIssueLogService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 告警保护方案下发日志Service业务层处理
|
||||||
|
*
|
||||||
|
* @author xzzn
|
||||||
|
* @date 2025-11-15
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class EmsFaultIssueLogServiceImpl implements IEmsFaultIssueLogService
|
||||||
|
{
|
||||||
|
@Autowired
|
||||||
|
private EmsFaultIssueLogMapper emsFaultIssueLogMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询告警保护方案下发日志
|
||||||
|
*
|
||||||
|
* @param id 告警保护方案下发日志主键
|
||||||
|
* @return 告警保护方案下发日志
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public EmsFaultIssueLog selectEmsFaultIssueLogById(Long id)
|
||||||
|
{
|
||||||
|
return emsFaultIssueLogMapper.selectEmsFaultIssueLogById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增告警保护方案下发日志
|
||||||
|
*
|
||||||
|
* @param emsFaultIssueLog 告警保护方案下发日志
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int insertEmsFaultIssueLog(EmsFaultIssueLog emsFaultIssueLog)
|
||||||
|
{
|
||||||
|
emsFaultIssueLog.setCreateTime(DateUtils.getNowDate());
|
||||||
|
return emsFaultIssueLogMapper.insertEmsFaultIssueLog(emsFaultIssueLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询告警保护方案下发日志列表
|
||||||
|
*
|
||||||
|
* @param emsFaultIssueLog 告警保护方案下发日志
|
||||||
|
* @return 告警保护方案下发日志
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<EmsFaultIssueLog> selectEmsFaultIssueLogList(EmsFaultIssueLog emsFaultIssueLog)
|
||||||
|
{
|
||||||
|
return emsFaultIssueLogMapper.selectEmsFaultIssueLogList(emsFaultIssueLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改告警保护方案下发日志
|
||||||
|
*
|
||||||
|
* @param emsFaultIssueLog 告警保护方案下发日志
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int updateEmsFaultIssueLog(EmsFaultIssueLog emsFaultIssueLog)
|
||||||
|
{
|
||||||
|
emsFaultIssueLog.setUpdateTime(DateUtils.getNowDate());
|
||||||
|
return emsFaultIssueLogMapper.updateEmsFaultIssueLog(emsFaultIssueLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除告警保护方案下发日志
|
||||||
|
*
|
||||||
|
* @param ids 需要删除的告警保护方案下发日志主键
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int deleteEmsFaultIssueLogByIds(Long[] ids)
|
||||||
|
{
|
||||||
|
return emsFaultIssueLogMapper.deleteEmsFaultIssueLogByIds(ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除告警保护方案下发日志信息
|
||||||
|
*
|
||||||
|
* @param id 告警保护方案下发日志主键
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int deleteEmsFaultIssueLogById(Long id)
|
||||||
|
{
|
||||||
|
return emsFaultIssueLogMapper.deleteEmsFaultIssueLogById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dealSyncData(String content, String operateType) {
|
||||||
|
if (StringUtils.isEmpty(content)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EmsFaultIssueLog issueLog = JSON.parseObject(content, EmsFaultIssueLog.class);
|
||||||
|
String logId = issueLog.getLogId();
|
||||||
|
if (checkLogIsExist(logId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch(operateType) {
|
||||||
|
case "INSERT":
|
||||||
|
insertEmsFaultIssueLog(issueLog);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkLogIsExist(String logId) {
|
||||||
|
EmsFaultIssueLog issueLog = emsFaultIssueLogMapper.selectEmsFaultIssueLogByLogId(logId);
|
||||||
|
if (issueLog != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,111 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper
|
||||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.xzzn.ems.mapper.EmsFaultIssueLogMapper">
|
||||||
|
|
||||||
|
<resultMap type="EmsFaultIssueLog" id="EmsFaultIssueLogResult">
|
||||||
|
<result property="id" column="id" />
|
||||||
|
<result property="logId" column="log_id" />
|
||||||
|
<result property="logTime" column="log_time" />
|
||||||
|
<result property="logLevel" column="log_level" />
|
||||||
|
<result property="siteId" column="site_id" />
|
||||||
|
<result property="deviceId" column="device_id" />
|
||||||
|
<result property="point" column="point" />
|
||||||
|
<result property="value" column="value" />
|
||||||
|
<result property="createBy" column="create_by" />
|
||||||
|
<result property="createTime" column="create_time" />
|
||||||
|
<result property="updateBy" column="update_by" />
|
||||||
|
<result property="updateTime" column="update_time" />
|
||||||
|
<result property="remark" column="remark" />
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="selectEmsFaultIssueLogVo">
|
||||||
|
select id, log_id, log_time, log_level, site_id, device_id, point, value, create_by, create_time, update_by, update_time, remark from ems_fault_issue_log
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<select id="selectEmsFaultIssueLogList" parameterType="EmsFaultIssueLog" resultMap="EmsFaultIssueLogResult">
|
||||||
|
<include refid="selectEmsFaultIssueLogVo"/>
|
||||||
|
<where>
|
||||||
|
<if test="logId != null and logId != ''"> and log_id = #{logId}</if>
|
||||||
|
<if test="logTime != null "> and log_time = #{logTime}</if>
|
||||||
|
<if test="logLevel != null "> and log_level = #{logLevel}</if>
|
||||||
|
<if test="siteId != null and siteId != ''"> and site_id = #{siteId}</if>
|
||||||
|
<if test="deviceId != null and deviceId != ''"> and device_id = #{deviceId}</if>
|
||||||
|
<if test="point != null and point != ''"> and point = #{point}</if>
|
||||||
|
<if test="value != null "> and value = #{value}</if>
|
||||||
|
</where>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectEmsFaultIssueLogById" parameterType="Long" resultMap="EmsFaultIssueLogResult">
|
||||||
|
<include refid="selectEmsFaultIssueLogVo"/>
|
||||||
|
where id = #{id}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<insert id="insertEmsFaultIssueLog" parameterType="EmsFaultIssueLog" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
insert into ems_fault_issue_log
|
||||||
|
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||||
|
<if test="logId != null and logId != ''">log_id,</if>
|
||||||
|
<if test="logTime != null">log_time,</if>
|
||||||
|
<if test="logLevel != null">log_level,</if>
|
||||||
|
<if test="siteId != null">site_id,</if>
|
||||||
|
<if test="deviceId != null">device_id,</if>
|
||||||
|
<if test="point != null and point != ''">point,</if>
|
||||||
|
<if test="value != null">value,</if>
|
||||||
|
<if test="createBy != null">create_by,</if>
|
||||||
|
<if test="createTime != null">create_time,</if>
|
||||||
|
<if test="updateBy != null">update_by,</if>
|
||||||
|
<if test="updateTime != null">update_time,</if>
|
||||||
|
<if test="remark != null">remark,</if>
|
||||||
|
</trim>
|
||||||
|
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||||
|
<if test="logId != null and logId != ''">#{logId},</if>
|
||||||
|
<if test="logTime != null">#{logTime},</if>
|
||||||
|
<if test="logLevel != null">#{logLevel},</if>
|
||||||
|
<if test="siteId != null">#{siteId},</if>
|
||||||
|
<if test="deviceId != null">#{deviceId},</if>
|
||||||
|
<if test="point != null and point != ''">#{point},</if>
|
||||||
|
<if test="value != null">#{value},</if>
|
||||||
|
<if test="createBy != null">#{createBy},</if>
|
||||||
|
<if test="createTime != null">#{createTime},</if>
|
||||||
|
<if test="updateBy != null">#{updateBy},</if>
|
||||||
|
<if test="updateTime != null">#{updateTime},</if>
|
||||||
|
<if test="remark != null">#{remark},</if>
|
||||||
|
</trim>
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<update id="updateEmsFaultIssueLog" parameterType="EmsFaultIssueLog">
|
||||||
|
update ems_fault_issue_log
|
||||||
|
<trim prefix="SET" suffixOverrides=",">
|
||||||
|
<if test="logId != null and logId != ''">log_id = #{logId},</if>
|
||||||
|
<if test="logTime != null">log_time = #{logTime},</if>
|
||||||
|
<if test="logLevel != null">log_level = #{logLevel},</if>
|
||||||
|
<if test="siteId != null">site_id = #{siteId},</if>
|
||||||
|
<if test="deviceId != null">device_id = #{deviceId},</if>
|
||||||
|
<if test="point != null and point != ''">point = #{point},</if>
|
||||||
|
<if test="value != null">value = #{value},</if>
|
||||||
|
<if test="createBy != null">create_by = #{createBy},</if>
|
||||||
|
<if test="createTime != null">create_time = #{createTime},</if>
|
||||||
|
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||||
|
<if test="updateTime != null">update_time = #{updateTime},</if>
|
||||||
|
<if test="remark != null">remark = #{remark},</if>
|
||||||
|
</trim>
|
||||||
|
where id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
|
<delete id="deleteEmsFaultIssueLogById" parameterType="Long">
|
||||||
|
delete from ems_fault_issue_log where id = #{id}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<delete id="deleteEmsFaultIssueLogByIds" parameterType="String">
|
||||||
|
delete from ems_fault_issue_log where id in
|
||||||
|
<foreach item="id" collection="array" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<select id="selectEmsFaultIssueLogByLogId" parameterType="String" resultMap="EmsFaultIssueLogResult">
|
||||||
|
<include refid="selectEmsFaultIssueLogVo"/>
|
||||||
|
where log_id = #{logId}
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
Reference in New Issue
Block a user