diff --git a/hzims-service-api/alarm-api/src/main/java/com/hnac/hzims/alarm/entity/SystemAlarmEntity.java b/hzims-service-api/alarm-api/src/main/java/com/hnac/hzims/alarm/entity/SystemAlarmEntity.java new file mode 100644 index 0000000..a10b125 --- /dev/null +++ b/hzims-service-api/alarm-api/src/main/java/com/hnac/hzims/alarm/entity/SystemAlarmEntity.java @@ -0,0 +1,47 @@ +package com.hnac.hzims.alarm.entity; + +import io.swagger.annotations.ApiModel; +import lombok.Data; +import org.springblade.core.tenant.mp.TenantEntity; + + +/** + * @author ysj + */ +@Data +@ApiModel(value = "华自3000告警对象", description = "华自3000告警对象") +public class SystemAlarmEntity extends TenantEntity { + + private static final long serialVersionUID = 1L; + + /** + * 告警时间 + */ + private String ts; + /** + * 告警ID,由平台生成,具有唯一性 + */ + private String id; + /** + * 告警内容 + */ + private String soeExplain; + /** + * 告警类型,对应数据字典 soe_alarm_type + */ + private Integer soeType; + private Integer soeStatus; + /** + * 告警动作,对应HZ3000的告警,如 {"分", "合"}, {"复归", "动作"} + */ + private Integer soeAlarmType; + /** + * 动作值 + */ + private String optionvals; + /** + * 站点id + */ + private String station; + +} \ No newline at end of file diff --git a/hzims-service-api/hzims-operational-api/src/main/java/com/hnac/hzims/operational/alert/constants/AbnormalAlarmConstant.java b/hzims-service-api/hzims-operational-api/src/main/java/com/hnac/hzims/operational/alert/constants/AbnormalAlarmConstant.java index 9ece78b..094df95 100644 --- a/hzims-service-api/hzims-operational-api/src/main/java/com/hnac/hzims/operational/alert/constants/AbnormalAlarmConstant.java +++ b/hzims-service-api/hzims-operational-api/src/main/java/com/hnac/hzims/operational/alert/constants/AbnormalAlarmConstant.java @@ -17,6 +17,7 @@ public interface AbnormalAlarmConstant { List SEND_MESSSAGE_TYPE_LIST = Arrays.asList("3","13","14"); String LEVEL_TYPE_LIST = "3,2,5,10,13,14"; + String SYSTEM_TYPE_LIST = "3,2,5,13,14"; /**通讯恢复*/ String ABNORMAL_STATUS = "1"; diff --git a/hzims-service/hzims-alarm/pom.xml b/hzims-service/hzims-alarm/pom.xml index a9472ef..1143588 100644 --- a/hzims-service/hzims-alarm/pom.xml +++ b/hzims-service/hzims-alarm/pom.xml @@ -80,8 +80,22 @@ blade-core-auto provided - - + + + org.springframework.boot + spring-boot-starter-websocket + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + + org.java-websocket + Java-WebSocket + com.baomidou @@ -108,7 +122,18 @@ 4.0.0-SNAPSHOT compile - + + com.hnac.hzims + equipment-api + + + com.hnac.hzims + hzims-operational-api + + + com.hnac.hzims + message-api + diff --git a/hzims-service/hzims-alarm/src/main/java/com/hnac/hzims/alarm/config/ws/SystemAlarmRegular.java b/hzims-service/hzims-alarm/src/main/java/com/hnac/hzims/alarm/config/ws/SystemAlarmRegular.java new file mode 100644 index 0000000..d249749 --- /dev/null +++ b/hzims-service/hzims-alarm/src/main/java/com/hnac/hzims/alarm/config/ws/SystemAlarmRegular.java @@ -0,0 +1,51 @@ +package com.hnac.hzims.alarm.config.ws; + +import com.hnac.hzims.alarm.show.service.SystemAlarmService; +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.tool.utils.ObjectUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.net.URI; + +/** + * 等级告警获取数据长链接 + * @author ysj + */ +@Slf4j +@Component +@EnableScheduling +public class SystemAlarmRegular { + + @Value("${hzims.system.alarm-url}") + private String system_alarm_url; + + private SystemAlarmWebSocket client; + + @Autowired + private SystemAlarmService systemAlarmService; + + // 定时发送消息 + @Scheduled(cron = "0 0/30 * * * ?") + private void regular(){ + // 检查链接存活状态 + if(ObjectUtil.isNotEmpty(client) && client.isOpen()){ + client.send(systemAlarmService.sendMessage()); + }else { + this.createClient(); + } + } + + // 创建websocket链接 + private void createClient() { + try{ + client = new SystemAlarmWebSocket(new URI(system_alarm_url)); + client.connectBlocking(); + }catch (Exception e){ + log.error("level create error : {}",e.getMessage()); + } + } +} \ No newline at end of file diff --git a/hzims-service/hzims-alarm/src/main/java/com/hnac/hzims/alarm/config/ws/SystemAlarmWebSocket.java b/hzims-service/hzims-alarm/src/main/java/com/hnac/hzims/alarm/config/ws/SystemAlarmWebSocket.java new file mode 100644 index 0000000..03b4e96 --- /dev/null +++ b/hzims-service/hzims-alarm/src/main/java/com/hnac/hzims/alarm/config/ws/SystemAlarmWebSocket.java @@ -0,0 +1,125 @@ +package com.hnac.hzims.alarm.config.ws; + +import com.hnac.hzims.alarm.show.service.SystemAlarmService; +import lombok.extern.slf4j.Slf4j; +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.handshake.ServerHandshake; +import org.springblade.core.tool.utils.SpringUtil; +import org.springblade.core.tool.utils.StringUtil; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.net.Socket; +import java.net.URI; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.Optional; + +/** + * 等级告警获取数据长链接 + * @author ysj + */ +@Slf4j +public class SystemAlarmWebSocket extends WebSocketClient { + + private final SystemAlarmService systemAlarmService; + + /** + * 构造等级告警websocket + * @param uri + */ + public SystemAlarmWebSocket(URI uri) { + super(uri); + systemAlarmService = SpringUtil.getBean(SystemAlarmService.class); + connection(this); + } + + // 链接到服务器回调接口 + @Override + public void onOpen(ServerHandshake handshakedata) { + log.error("systemAlarm websocket open"); + } + + // 接收到服务器消息回调接口 + @Override + public void onMessage(String message) { + if(StringUtil.isEmpty(message)){ + log.error("systemAlarm on message is null"); + }else{ + // 等级告警数据处理 + systemAlarmService.receiveMessage(message); + } + } + + // 与服务器链接中断回调接口 + @Override + public void onClose(int code, String reason, boolean remote) { + log.error("systemAlarm websocket close"); + } + + // 与服务器通讯异常触发 + @Override + public void onError(Exception e) { + log.error("systemAlarm websocket error : {}",e.getMessage()); + } + + /** + * 建立链接 + * @param webSocket + */ + private void connection(SystemAlarmWebSocket webSocket) { + SSLContext context = init(); + if(Optional.ofNullable(context).isPresent()){ + Socket socket = create(context); + if(Optional.ofNullable(socket).isPresent()){ + webSocket.setSocket(socket); + } + } + } + + /** + * 创建Socket + * @param context + * @return + */ + private Socket create(SSLContext context) { + Socket socket = null; + try{ + socket = context.getSocketFactory().createSocket(); + }catch (Exception e){ + log.error("systemAlarm socket create error : {}",e.getMessage()); + } + return socket; + } + + /** + * 协议初始化 + * @return SSLContext + */ + private SSLContext init() { + SSLContext SSL = null; + try{ + SSL = SSLContext.getInstance("TLS"); + SSL.init(null, new TrustManager[]{new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, + String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + }}, new SecureRandom()); + }catch (Exception e){ + log.error("systemAlarm SSL init error : {}",e.getMessage()); + } + return SSL; + } + +} \ No newline at end of file diff --git a/hzims-service/hzims-alarm/src/main/java/com/hnac/hzims/alarm/show/controller/ShowAlarmController.java b/hzims-service/hzims-alarm/src/main/java/com/hnac/hzims/alarm/show/controller/ShowAlarmController.java new file mode 100644 index 0000000..fb4dd8e --- /dev/null +++ b/hzims-service/hzims-alarm/src/main/java/com/hnac/hzims/alarm/show/controller/ShowAlarmController.java @@ -0,0 +1,82 @@ +package com.hnac.hzims.alarm.show.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import com.hnac.hzims.alarm.entity.AlarmEntity; +import com.hnac.hzims.alarm.show.service.AlarmService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.AllArgsConstructor; +import org.springblade.core.boot.ctrl.BladeController; +import org.springblade.core.mp.support.Condition; +import org.springblade.core.mp.support.Query; +import org.springblade.core.tool.api.R; +import org.springblade.core.tool.utils.Func; +import org.springframework.web.bind.annotation.*; +import springfox.documentation.annotations.ApiIgnore; + +import javax.validation.Valid; +import java.util.Map; + +/** + * @author ysj + */ +@RestController +@AllArgsConstructor +@RequestMapping("/show/alarm") +@Api(value = "告警配置", tags = "告警配置") +public class ShowAlarmController extends BladeController { + + private final AlarmService alarmService; + + @PostMapping("/save") + @ApiOperationSupport(order = 1) + @ApiOperation(value = "新增", notes = "传入AlarmEntity对象") + public R save(@Valid @RequestBody AlarmEntity entity) { + return R.status(alarmService.save(entity)); + } + + @PostMapping("/update") + @ApiOperationSupport(order = 2) + @ApiOperation(value = "修改", notes = "传入AlarmEntity对象") + public R update(@Valid @RequestBody AlarmEntity entity) { + return R.status(alarmService.updateById(entity)); + } + + + @PostMapping("/remove") + @ApiOperationSupport(order = 3) + @ApiOperation(value = "逻辑删除", notes = "传入ids") + public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) { + return R.status(alarmService.deleteLogic(Func.toLongList(ids))); + } + @GetMapping("/count") + @ApiOperationSupport(order = 4) + @ApiOperation(value = "告警数量", notes = "传入em_model_param") + public R count(@ApiIgnore @RequestParam AlarmEntity entity) { + QueryWrapper queryWrapper = Condition.getQueryWrapper(entity); + int count = alarmService.count(queryWrapper); + return R.data(count); + } + + @GetMapping("/list") + @ApiOperationSupport(order = 4) + @ApiOperation(value = "分页", notes = "传入em_model_param") + public R> list(@ApiIgnore @RequestParam Map params, Query query) { + QueryWrapper queryWrapper = Condition.getQueryWrapper( params, AlarmEntity.class); + IPage pages = alarmService.page(Condition.getPage(query), queryWrapper); + return R.data(pages); + } + + @GetMapping("/detail") + @ApiOperationSupport(order = 5) + @ApiOperation(value = "详情", notes = "传入em_model_param") + public R detail(AlarmEntity entity) { + AlarmEntity detail = alarmService.getOne(Condition.getQueryWrapper(entity)); + return R.data(detail); + } + + +} diff --git a/hzims-service/hzims-alarm/src/main/java/com/hnac/hzims/alarm/show/service/SystemAlarmService.java b/hzims-service/hzims-alarm/src/main/java/com/hnac/hzims/alarm/show/service/SystemAlarmService.java new file mode 100644 index 0000000..7f0cff4 --- /dev/null +++ b/hzims-service/hzims-alarm/src/main/java/com/hnac/hzims/alarm/show/service/SystemAlarmService.java @@ -0,0 +1,12 @@ +package com.hnac.hzims.alarm.show.service; + +/** + * 告警处理接口 + * @author ysj + */ +public interface SystemAlarmService { + + String sendMessage(); + + void receiveMessage(String message); +} diff --git a/hzims-service/hzims-alarm/src/main/java/com/hnac/hzims/alarm/show/service/impl/SystemAlarmServiceImpl.java b/hzims-service/hzims-alarm/src/main/java/com/hnac/hzims/alarm/show/service/impl/SystemAlarmServiceImpl.java new file mode 100644 index 0000000..3cb379c --- /dev/null +++ b/hzims-service/hzims-alarm/src/main/java/com/hnac/hzims/alarm/show/service/impl/SystemAlarmServiceImpl.java @@ -0,0 +1,221 @@ +package com.hnac.hzims.alarm.show.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.hnac.hzims.alarm.entity.AlarmEntity; +import com.hnac.hzims.alarm.entity.SystemAlarmEntity; +import com.hnac.hzims.alarm.show.service.AlarmService; +import com.hnac.hzims.alarm.show.service.SystemAlarmService; +import com.hnac.hzims.equipment.feign.IEmInfoClient; +import com.hnac.hzims.message.MessageConstants; +import com.hnac.hzims.message.dto.MessagePushRecordDto; +import com.hnac.hzims.message.fegin.IMessageClient; +import com.hnac.hzims.operational.alert.constants.AbnormalAlarmConstant; +import com.hnac.hzims.operational.station.entity.StationEntity; +import com.hnac.hzims.operational.station.feign.IStationClient; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.log.exception.ServiceException; +import org.springblade.core.tool.api.R; +import org.springblade.core.tool.utils.CollectionUtil; +import org.springblade.core.tool.utils.DateUtil; +import org.springblade.core.tool.utils.StringUtil; +import org.springblade.system.feign.ISysClient; +import org.springblade.system.user.entity.User; +import org.springblade.system.user.feign.IUserClient; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * 等级告警实现类 + * @author ysj + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class SystemAlarmServiceImpl implements SystemAlarmService { + + + + private final IEmInfoClient deviceClient; + private final IUserClient userClient; + + private final ISysClient sysClient; + + private final IMessageClient messageClient; + private final AlarmService alarmService; + private final IStationClient stationClient; + /** + * 定时发送消息内容 + * @return String + */ + @Override + public String sendMessage() { + R> listAll = stationClient.getListAll(); + if (!listAll.isSuccess()||CollectionUtil.isEmpty(listAll.getData())){ + throw new ServiceException("level send message is null"); + } + List stations = listAll.getData(); + Map map = new ConcurrentHashMap<>(); + map.put("stations",stations.stream().map(StationEntity::getCode).collect(Collectors.joining(","))); + map.put("soe_type", AbnormalAlarmConstant.SYSTEM_TYPE_LIST); + return JSONObject.toJSONString(map); + } + + + /** + * 接收服务推送消息 + * @param message + */ + @Override + public void receiveMessage(String message) { + // 对象转换 + List alarms = JSONObject.parseArray(message, SystemAlarmEntity.class); + if(CollectionUtil.isEmpty(alarms)){ + return; + } + + R> listAll = stationClient.getListAll(); + if (!listAll.isSuccess()||CollectionUtil.isEmpty(listAll.getData())){ + throw new ServiceException("level send message is null"); + } + List stations = listAll.getData(); + // 查询当天已经记录的告警 + List historys = alarmService.list(Wrappers.lambdaQuery(). + ge(AlarmEntity::getCreateTime, DateUtil.format(new Date(), DateUtil.PATTERN_DATE) + " 00:00:00")); + + // 数据过滤 + List entitys = alarms.stream() + .filter(alarm -> CollectionUtil.isNotEmpty(historys) || !historys.stream().map(AlarmEntity::getAlarmId).collect(Collectors.toList()).contains(alarm.getId())) + .map(item->{ + AlarmEntity entity = getAlarmEntity(item); + return entity; + }).collect(Collectors.toList()); + if(CollectionUtil.isEmpty(entitys)){ + return; + } + // 批量保存 + alarmService.saveBatch(entitys); + + // 消息推送 + CompletableFuture.runAsync(()->this.sendAlarmMessage(entitys,stations.stream().filter(station -> entitys.stream().filter(stationId -> !StringUtil.isEmpty(stationId)).map(AlarmEntity::getStationId).collect(Collectors.toList()).contains(station.getCode())).collect(Collectors.toList()))); + } + + private AlarmEntity getAlarmEntity(SystemAlarmEntity item) { + AlarmEntity entity = new AlarmEntity(); + entity.setAlarmId(item.getId()); + entity.setAlarmTime(DateUtil.parse(item.getTs(), "yyyy-MM-dd HH:mm:ss.s")); + entity.setAlarmContext(item.getSoeExplain()); + entity.setAlarmType(item.getSoeAlarmType()); + entity.setAlarmStatus(item.getSoeStatus()); + entity.setAlarmValue(item.getOptionvals()); + entity.setStationId(item.getStation()); + R stationByCode = stationClient.getStationByCode(item.getStation()); + if (stationByCode.isSuccess()&& ObjectUtils.isNotEmpty(stationByCode.getData())){ + entity.setCreateDept(stationByCode.getData().getCreateDept()); + } + return entity; + } + + + /** + * 告警消息推送 + * @param entitys + */ + private void sendAlarmMessage(List entitys,List stations) { + if(CollectionUtil.isEmpty(entitys)){ + return; + } +// // 告警等级 :一级、二级告警发送通知 +// List alarms = entitys.stream().filter(entity -> AbnormalAlarmConstant.LEVEL_LIST.contains(entity.getAlarmLevel())).collect(Collectors.toList()); +// if(CollectionUtil.isEmpty(alarms)){ +// return; +// } + // 查询站点用户 + entitys.forEach(entity->{ + if(StringUtil.isEmpty(entity.getStationId())){ + return; + } + List depts = stations.stream().filter(station -> station.getCode().equals(entity.getStationId())).map(StationEntity::getRefDept).collect(Collectors.toList()); + if(CollectionUtil.isEmpty(depts)){ + return; + } + // app,web消息推送 + this.sendMessageByWebApp(depts.get(0),entity); + // 短信推送 + //this.message(depts.get(0),entity); + }); + } + + private void sendMessageByWebApp(Long dept,AlarmEntity entity) { + // 获取站点用户 + R> result = userClient.userListByDeptId(dept); + if(!result.isSuccess() || CollectionUtil.isEmpty(result.getData())){ + return; + } + MessagePushRecordDto message = new MessagePushRecordDto(); + message.setBusinessClassify("warning"); + message.setBusinessKey(MessageConstants.BusinessClassifyEnum.WARNING.getKey()); + message.setSubject(MessageConstants.BusinessClassifyEnum.WARNING.getDescription()); + message.setTaskId(entity.getId()); + message.setTenantId("200000"); + message.setContent(entity.getAlarmContext()); + message.setTypes(Arrays.asList(MessageConstants.APP_PUSH, MessageConstants.WS_PUSH)); + message.setPushType(MessageConstants.IMMEDIATELY); + message.setDeptId(dept); + message.setCreateDept(dept); + R deptName = sysClient.getDeptName(dept); + if (deptName.isSuccess()) { + message.setDeptName(deptName.getData()); + } + message.setCreateDept(dept); + result.getData().forEach(user->{ + message.setPusher(String.valueOf(user.getId())); + message.setPusherName(user.getName()); + message.setAccount(String.valueOf(user.getId())); + message.setCreateUser(user.getId()); + messageClient.sendMessage(message); + }); + } + + /** + * 发送短信 + * @param dept + * @param entity + */ + private void message(Long dept,AlarmEntity entity) { + // 获取站点用户 + R> result = userClient.relationUserListByRoleAlias("200000",dept,"projectManager"); + if(!result.isSuccess() || CollectionUtil.isEmpty(result.getData())){ + return; + } + MessagePushRecordDto message = new MessagePushRecordDto(); + message.setBusinessClassify("warning"); + message.setBusinessKey(MessageConstants.BusinessClassifyEnum.WARNING.getKey()); + message.setSubject(MessageConstants.BusinessClassifyEnum.WARNING.getDescription()); + message.setTaskId(entity.getId()); + message.setTenantId("200000"); + message.setContent(entity.getAlarmContext()); + message.setTypes(Collections.singletonList(MessageConstants.SMS_PUSH)); + message.setPushType(MessageConstants.IMMEDIATELY); + message.setDeptId(dept); + R deptName = sysClient.getDeptName(dept); + if (deptName.isSuccess()) { + message.setDeptName(deptName.getData()); + } + User admin = userClient.userByAccount("200000", "admin").getData(); + message.setCreateDept(admin.getCreateDept()); + message.setCreateUser(admin.getId()); + result.getData().forEach(user->{ + message.setPusher(String.valueOf(user.getId())); + message.setPusherName(user.getName()); + message.setAccount(String.valueOf(user.getId())); + messageClient.sendMessage(message); + }); + } +}