From f78657473e85487c09cd3c10112db48a0c4a7c4e Mon Sep 17 00:00:00 2001 From: xiaoQQya <xiaoQQya@126.com> Date: 星期一, 06 十一月 2023 20:34:58 +0800 Subject: [PATCH] fix(报警推送): 修复报警推送功能无效的问题 --- web_src/src/main.js | 13 +-- /dev/null | 37 --------- src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java | 11 +- src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/sse/SseController.java | 55 +++++++++++++ web_src/src/layout/UiHeader.vue | 16 ++- src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java | 77 +++++++++++-------- 6 files changed, 121 insertions(+), 88 deletions(-) diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java index 6a24735..9cb3a1f 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java @@ -1,12 +1,12 @@ package com.genersoft.iot.vmp.conf.security; import com.genersoft.iot.vmp.conf.UserSetting; -import org.springframework.core.annotation.Order; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; @@ -28,6 +28,7 @@ /** * 閰嶇疆Spring Security + * * @author lin */ @Configuration @@ -75,6 +76,7 @@ matchers.add("/js/**"); matchers.add("/api/device/query/snap/**"); matchers.add("/record_proxy/*/**"); + matchers.add("/api/emit"); matchers.addAll(userSetting.getInterfaceAuthenticationExcludes()); // 鍙互鐩存帴璁块棶鐨勯潤鎬佹暟鎹� web.ignoring().antMatchers(matchers.toArray(new String[0])); @@ -83,6 +85,7 @@ /** * 閰嶇疆璁よ瘉鏂瑰紡 + * * @param auth * @throws Exception */ @@ -111,7 +114,7 @@ .authorizeRequests() .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() .antMatchers(userSetting.getInterfaceAuthenticationExcludes().toArray(new String[0])).permitAll() - .antMatchers("/api/user/login","/index/hook/**","/zlm_Proxy/FhTuMYqB2HeCuNOb/record/t/1/2023-03-25/16:35:07-16:35:16-9353.mp4").permitAll() + .antMatchers("/api/user/login", "/index/hook/**").permitAll() .anyRequest().authenticated() // 寮傚父澶勭悊鍣� .and() @@ -124,7 +127,7 @@ } - CorsConfigurationSource configurationSource(){ + CorsConfigurationSource configurationSource() { // 閰嶇疆璺ㄥ煙 CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowedHeaders(Arrays.asList("*")); @@ -135,7 +138,7 @@ corsConfiguration.setExposedHeaders(Arrays.asList(JwtUtils.getHeader())); UrlBasedCorsConfigurationSource url = new UrlBasedCorsConfigurationSource(); - url.registerCorsConfiguration("/**",corsConfiguration); + url.registerCorsConfiguration("/**", corsConfiguration); return url; } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java index 9ee6477..aef5907 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java @@ -1,55 +1,68 @@ package com.genersoft.iot.vmp.gb28181.event.alarm; -import org.springframework.context.ApplicationListener; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; -import java.io.IOException; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.Map; - +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +import java.io.PrintWriter; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** - * @description: 鎶ヨ浜嬩欢鐩戝惉 - * @author: lawrencehj - * @data: 2021-01-20 + * 鎶ヨ浜嬩欢鐩戝惉鍣�. + * + * @author lawrencehj + * @author <a href="mailto:xiaoQQya@126.com">xiaoQQya</a> + * @since 2021/01/20 */ - @Component public class AlarmEventListener implements ApplicationListener<AlarmEvent> { - private final static Logger logger = LoggerFactory.getLogger(AlarmEventListener.class); + private static final Logger logger = LoggerFactory.getLogger(AlarmEventListener.class); - private static Map<String, SseEmitter> sseEmitters = new Hashtable<>(); + private static final Map<String, PrintWriter> SSE_CACHE = new ConcurrentHashMap<>(); - public void addSseEmitters(String browserId, SseEmitter sseEmitter) { - sseEmitters.put(browserId, sseEmitter); + public void addSseEmitter(String browserId, PrintWriter writer) { + SSE_CACHE.put(browserId, writer); + logger.info("SSE 鍦ㄧ嚎鏁伴噺: {}", SSE_CACHE.size()); + } + + public void removeSseEmitter(String browserId, PrintWriter writer) { + SSE_CACHE.remove(browserId, writer); + logger.info("SSE 鍦ㄧ嚎鏁伴噺: {}", SSE_CACHE.size()); } @Override - public void onApplicationEvent(AlarmEvent event) { + public void onApplicationEvent(@NotNull AlarmEvent event) { if (logger.isDebugEnabled()) { - logger.debug("璁惧鎶ヨ浜嬩欢瑙﹀彂锛宒eviceId锛�" + event.getAlarmInfo().getDeviceId() + ", " - + event.getAlarmInfo().getAlarmDescription()); + logger.debug("璁惧鎶ヨ浜嬩欢瑙﹀彂, deviceId: {}, {}", event.getAlarmInfo().getDeviceId(), event.getAlarmInfo().getAlarmDescription()); } - String msg = "<strong>璁惧缂栫爜锛�</strong> <i>" + event.getAlarmInfo().getDeviceId() + "</i>" - + "<br><strong>鎶ヨ鎻忚堪锛�</strong> <i>" + event.getAlarmInfo().getAlarmDescription() + "</i>" - + "<br><strong>鎶ヨ鏃堕棿锛�</strong> <i>" + event.getAlarmInfo().getAlarmTime() + "</i>" - + "<br><strong>鎶ヨ浣嶇疆锛�</strong> <i>" + event.getAlarmInfo().getLongitude() + "</i>" - + ", <i>" + event.getAlarmInfo().getLatitude() + "</i>"; - for (Iterator<Map.Entry<String, SseEmitter>> it = sseEmitters.entrySet().iterator(); it.hasNext();) { - Map.Entry<String, SseEmitter> emitter = it.next(); - logger.info("鎺ㄩ�佸埌SSE杩炴帴锛屾祻瑙堝櫒ID: " + emitter.getKey()); + String msg = "<strong>璁惧缂栧彿锛�</strong> <i>" + event.getAlarmInfo().getDeviceId() + "</i>" + + "<br><strong>閫氶亾缂栧彿锛�</strong> <i>" + event.getAlarmInfo().getChannelId() + "</i>" + + "<br><strong>鎶ヨ鎻忚堪锛�</strong> <i>" + event.getAlarmInfo().getAlarmDescription() + "</i>" + + "<br><strong>鎶ヨ鏃堕棿锛�</strong> <i>" + event.getAlarmInfo().getAlarmTime() + "</i>"; + + for (Iterator<Map.Entry<String, PrintWriter>> it = SSE_CACHE.entrySet().iterator(); it.hasNext(); ) { + Map.Entry<String, PrintWriter> response = it.next(); + logger.info("鎺ㄩ�佸埌 SSE 杩炴帴, 娴忚鍣� ID: {}", response.getKey()); try { - emitter.getValue().send(msg); - } catch (IOException | IllegalStateException e) { - if (logger.isDebugEnabled()) { - logger.debug("SSE杩炴帴宸插叧闂�"); + PrintWriter writer = response.getValue(); + + if (writer.checkError()) { + it.remove(); + continue; } - // 绉婚櫎宸插叧闂殑杩炴帴 + + String sseMsg = "event:message\n" + + "data:" + msg + "\n" + + "\n"; + writer.write(sseMsg); + writer.flush(); + } catch (Exception e) { it.remove(); } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/SseController/SseController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/SseController/SseController.java deleted file mode 100755 index b1ad3b9..0000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/SseController/SseController.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.SseController; - -import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEventListener; - -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; - -/** - * @description: SSE鎺ㄩ�� - * @author: lawrencehj - * @data: 2021-01-20 - */ -@Tag(name = "SSE鎺ㄩ��") - -@Controller -@RequestMapping("/api") -public class SseController { - @Autowired - AlarmEventListener alarmEventListener; - - @GetMapping("/emit") - public SseEmitter emit(@RequestParam String browserId) { - final SseEmitter sseEmitter = new SseEmitter(0L); - try { - alarmEventListener.addSseEmitters(browserId, sseEmitter); - }catch (Exception e){ - sseEmitter.completeWithError(e); - } - return sseEmitter; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/sse/SseController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/sse/SseController.java new file mode 100644 index 0000000..575f22b --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/sse/SseController.java @@ -0,0 +1,55 @@ +package com.genersoft.iot.vmp.vmanager.gb28181.sse; + +import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEventListener; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + + +/** + * SSE 鎺ㄩ��. + * + * @author lawrencehj + * @author <a href="mailto:xiaoQQya@126.com">xiaoQQya</a> + * @since 2021/01/20 + */ +@Tag(name = "SSE 鎺ㄩ��") +@RestController +@RequestMapping("/api") +public class SseController { + + @Resource + private AlarmEventListener alarmEventListener; + + /** + * SSE 鎺ㄩ��. + * + * @param response 鍝嶅簲 + * @param browserId 娴忚鍣↖D + * @throws IOException IOEXCEPTION + * @author <a href="mailto:xiaoQQya@126.com">xiaoQQya</a> + * @since 2023/11/06 + */ + @GetMapping("/emit") + public void emit(HttpServletResponse response, @RequestParam String browserId) throws IOException, InterruptedException { + response.setContentType("text/event-stream"); + response.setCharacterEncoding("utf-8"); + + PrintWriter writer = response.getWriter(); + alarmEventListener.addSseEmitter(browserId, writer); + + while (!writer.checkError()) { + Thread.sleep(1000); + writer.write(":keep alive\n\n"); + writer.flush(); + } + alarmEventListener.removeSseEmitter(browserId, writer); + } +} diff --git a/web_src/src/layout/UiHeader.vue b/web_src/src/layout/UiHeader.vue index 2cdca02..fdfcb9f 100755 --- a/web_src/src/layout/UiHeader.vue +++ b/web_src/src/layout/UiHeader.vue @@ -37,7 +37,6 @@ </template> <script> - import changePasswordDialog from '../components/dialog/changePassword.vue' import userService from '../components/service/UserService' import {Notification} from 'element-ui'; @@ -55,18 +54,19 @@ }; }, created() { - console.log(4444) console.log(JSON.stringify(userService.getUser())) if (this.$route.path.startsWith("/channelList")) { this.activeIndex = "/deviceList" - } }, mounted() { window.addEventListener('beforeunload', e => this.beforeunloadHandler(e)) - // window.addEventListener('unload', e => this.unloadHandler(e)) this.alarmNotify = this.getAlarmSwitchStatus() === "true"; - this.sseControl(); + + // TODO: 姝ゅ寤惰繜杩炴帴 sse, 閬垮厤 sse 杩炴帴鏃� browserId 杩樻湭鐢熸垚, 鍚庣画寰呬紭鍖� + setTimeout(() => { + this.sseControl() + }, 3000); }, methods: { loginout() { @@ -107,10 +107,12 @@ this.sseSource = new EventSource('/api/emit?browserId=' + this.$browserId); this.sseSource.addEventListener('message', function (evt) { that.$notify({ - title: '鏀跺埌鎶ヨ淇℃伅', + title: '鎶ヨ淇℃伅', dangerouslyUseHTMLString: true, message: evt.data, - type: 'warning' + type: 'warning', + position: 'bottom-right', + duration: 3000 }); console.log("鏀跺埌淇℃伅锛�" + evt.data); }); diff --git a/web_src/src/main.js b/web_src/src/main.js index a6c6e1b..328bb25 100755 --- a/web_src/src/main.js +++ b/web_src/src/main.js @@ -1,21 +1,19 @@ import Vue from 'vue'; import App from './App.vue'; - -Vue.config.productionTip = false; -import ElementUI from 'element-ui'; +import ElementUI, {Notification} from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; import router from './router/index.js'; import axios from 'axios'; import VueCookies from 'vue-cookies'; -import echarts from 'echarts'; import VCharts from 'v-charts'; import VueClipboard from 'vue-clipboard2'; -import {Notification} from 'element-ui'; import Fingerprint2 from 'fingerprintjs2'; import VueClipboards from 'vue-clipboards'; import Contextmenu from "vue-contextmenujs" import userService from "./components/service/UserService" + +Vue.config.productionTip = false; // 鐢熸垚鍞竴ID @@ -29,10 +27,9 @@ //console.log(values) //浣跨敤鐨勬祻瑙堝櫒淇℃伅npm // 鐢熸垚鏈�缁坕d let port = window.location.port; - console.log(port); const fingerPrint = Fingerprint2.x64hash128(values.join(port), 31) Vue.prototype.$browserId = fingerPrint; - console.log("鍞竴鏍囪瘑鐮侊細" + fingerPrint); + console.log("娴忚鍣� ID: " + fingerPrint); }); Vue.use(VueClipboard); @@ -75,7 +72,7 @@ ); Vue.prototype.$axios = axios; -Vue.prototype.$cookies.config(60*30); +Vue.prototype.$cookies.config(60 * 30); new Vue({ router: router, -- Gitblit v1.8.0