diff --git a/Dockerfile b/Dockerfile index 9ecf300b945a8cfd82427ebf932c3c7f64c5c1c4..60a4e0cc629ed5e9c46c3053abbccb9fe95fc02a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,5 @@ -# FROM nginx:1.27.2-bookworm -FROM registry.hncy.info/nginx:1.27.2-bookworm-arm -# FROM registry.hncy.info/nginx:1.25.4 -# FROM registry.hncy.info/nginx:1.27.2-bookworm-arm +FROM registry.hncy.info/nginx:1.25.4 +#FROM registry.hncy.info/nginx:1.27.2-bookworm-arm WORKDIR /usr/src/app/ @@ -19,4 +17,4 @@ RUN chmod +x /usr/bin/docker_start.sh && \ chmod +x /opt/regist-to-eureka/*.sh && \ cp /opt/regist-to-eureka/*.sh /usr/bin -CMD ["docker_start.sh"] \ No newline at end of file +CMD ["docker_start.sh"] diff --git a/builder.sh b/builder.sh index 8a6028f2f7052737a186c8523463b5542e41c192..1b70eb823fbbf469c51dce30960825b409b770eb 100755 --- a/builder.sh +++ b/builder.sh @@ -4,12 +4,48 @@ git checkout $gitTag repo=registry.hncy.info/hncy/cd-comprehensive-web tag=${1:-$gitTag} echo $tag -git submodule update --init --recursive + +# 尝试更新子模块,但不因子模块失败而中断构建 +echo "正在初始化并更新子模块..." +git submodule init + +# 先更新 regist-to-eureka 子模块 +echo "更新 regist-to-eureka 子模块..." +git submodule update --init regist-to-eureka + +# 检查 regist-to-eureka 子模块是否成功初始化 if [ ! -f regist-to-eureka/reg2eureka.sh ]; then + echo "错误: regist-to-eureka 子模块初始化失败" exit 1 fi + +# 尝试更新 CooglMap 子模块,但即使失败也继续构建 +echo "尝试更新 CooglMap 子模块..." +git submodule update --init src/components/CooglMap || { + echo "警告: CooglMap 子模块更新失败,尝试强制重置..." + + # 如果子模块目录已存在,尝试强制重置 + if [ -d "src/components/CooglMap" ]; then + echo "CooglMap 目录存在,尝试重置..." + cd src/components/CooglMap + git fetch origin main + git reset --hard origin/main + cd ../../../ + else + # 如果子模块目录不存在,尝试直接克隆 + echo "CooglMap 目录不存在,尝试直接克隆..." + mkdir -p src/components/CooglMap + git clone ssh://git@171.217.92.33:2224/cityLevel/cooglMap.git src/components/CooglMap + fi +} + +echo "清理旧构建..." rm -rf dist -# yarn + +echo "开始构建..." yarn build -docker buildx use desktop-linux -docker buildx build --platform linux/arm64 -t $repo:$tag . --push \ No newline at end of file + +echo "构建Docker镜像并推送..." +docker buildx use desktop-linux +docker buildx build --platform linux/amd64 -t $repo:$tag . --push +#docker buildx build --platform linux/arm64 -t $repo:$tag . --push diff --git a/src/pages/NewHome/NewLeftSidebar/OperatingSituation/index.less b/src/pages/NewHome/NewLeftSidebar/OperatingSituation/index.less index 537356531c2a9dfeda57a3dadcf7ccadc6993e64..b78ac4855419c5525381e6f34733a7909fda8fde 100644 --- a/src/pages/NewHome/NewLeftSidebar/OperatingSituation/index.less +++ b/src/pages/NewHome/NewLeftSidebar/OperatingSituation/index.less @@ -239,7 +239,8 @@ } .cityIssue { - margin-top: 40px; + margin-top: 12px; + // margin-top: 40px; padding-left: 21px; background-color: rgba(71, 101, 165, 0.307); height: 72px; diff --git a/src/pages/NewHome/NewLeftSidebar/OperatingSituation/index.tsx b/src/pages/NewHome/NewLeftSidebar/OperatingSituation/index.tsx index fa9e2975863407a51698c9a83d908e2d89937b23..7d1ada4556f7f7cb60ff7b2226dd83db8ba1452a 100644 --- a/src/pages/NewHome/NewLeftSidebar/OperatingSituation/index.tsx +++ b/src/pages/NewHome/NewLeftSidebar/OperatingSituation/index.tsx @@ -10,6 +10,8 @@ import CountUp from 'react-countup'; import DateSelect from './DateSelect'; import styles from './index.less'; import LineCharts from './LineCharts'; +import webSocketService from '@/utils/websocket'; + const formatter: StatisticProps['formatter'] = (value: any) => { return ( { }, ); + // 添加 AI 实时巡查数据状态 + const [aiPatrolCount, setAiPatrolCount] = useState(99); + const getDifficultData = () => { services.Physicalsign.getDifficultEventsInfo().then((res) => { if (res.code === 200) { @@ -60,6 +65,22 @@ const OperatingSituation = () => { }); }; + // 连接WebSocket并订阅'total'类型消息 + useEffect(() => { + // 订阅WebSocket消息 + const unsubscribe = webSocketService.subscribe('total', (data) => { + if (data !== undefined && data !== null) { + console.log('==> WEBSocket 实时巡查数据 AiPatrolCount', data); + setAiPatrolCount(Number(data)); + } + }); + + // 组件卸载时取消订阅 + return () => { + unsubscribe(); + }; + }, []); + useEffect(() => { getDifficultData(); }, []); @@ -355,8 +376,8 @@ const OperatingSituation = () => { -
-
+
+
{ }), ) || [] } - style={{ width: '100%', height: 104 }} + style={{ width: '100%', height: 76 }} />
@@ -535,6 +556,29 @@ const OperatingSituation = () => {
+ +
{ + // dispatch.push('DifficultEvents', { + // title: '疑难问题', + // }); + // }} + // style={{ cursor: 'pointer' }} + > +
市级监督AI实时巡查
+
+ {commonStatistic( + // convertUnits(DEDataSource?.cityManageEventTotal)?.num, + aiPatrolCount, + 30, + )} + + {/* {convertUnits(DEDataSource?.cityManageEventTotal)?.unit} */} + 件 + +
+
diff --git a/src/utils/envConfig.ts b/src/utils/envConfig.ts new file mode 100644 index 0000000000000000000000000000000000000000..7ebd4b18ea98a5ad3581566db1262b3a22e9e3bb --- /dev/null +++ b/src/utils/envConfig.ts @@ -0,0 +1,33 @@ +/** + * 环境配置工具 + * 根据不同环境返回不同的配置 + */ + +interface EnvConfig { + wsUrl: string; + apiBaseUrl: string; + // 可扩展其他环境配置 +} + +// 开发/测试环境配置 +const devConfig: EnvConfig = { + wsUrl: 'ws://171.217.92.33:21601/city-operation/movement-sensor-event/websocket', + apiBaseUrl: '/api', +}; + +// 生产环境配置 +const prodConfig: EnvConfig = { + wsUrl: 'wss://cdcgzgf.cn:13200/city-operation/movement-sensor-event/websocket', + apiBaseUrl: '/api', +}; + +/** + * 获取当前环境的配置 + */ +export const getEnvConfig = (): EnvConfig => { + const env = process.env.NODE_ENV; + if (env === 'production') { + return prodConfig; + } + return devConfig; +}; \ No newline at end of file diff --git a/src/utils/websocket.ts b/src/utils/websocket.ts new file mode 100644 index 0000000000000000000000000000000000000000..cd0e374c63037ee6c28b83c05a5a08949eb0789f --- /dev/null +++ b/src/utils/websocket.ts @@ -0,0 +1,221 @@ +/** + * WebSocket通信管理类 + * 支持自动重连、消息订阅机制 + */ +import { message } from '@/layouts/messageStore'; +import { getEnvConfig } from './envConfig'; + +// 环境配置获取 +const getWebSocketUrl = () => { + const env = process.env.NODE_ENV; + if (env === 'development' || env === 'test') { + return 'ws://171.217.92.33:21601/city-operation/movement-sensor-event/websocket'; + } + return 'wss://cdcgzgf.cn:13200/city-operation/movement-sensor-event/websocket'; +}; + +// 消息类型定义 +type MessageType = 'total' | 'other'; // 可扩展其他消息类型 +export interface WSMessage { + code: number; + data: any; + msg: string; + success: boolean; + type: MessageType; +} + +// 消息处理函数 +type MessageHandler = (data: any) => void; + +class WebSocketService { + private ws: WebSocket | null = null; + private url: string; + private reconnectAttempts = 0; + private maxReconnectAttempts = 5; + private reconnectInterval = 3000; // 重连间隔3秒 + private reconnectTimeoutId: number | null = null; + private messageSubscribers: Map> = new Map(); + private isConnecting = false; + private isManualClosed = false; + + constructor(url?: string) { + this.url = url || getWebSocketUrl(); + } + + // 连接WebSocket + connect(): Promise { + if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) { + return Promise.resolve(true); + } + + if (this.isConnecting) { + return new Promise((resolve) => { + // 等待连接完成 + const checkInterval = setInterval(() => { + if (this.ws && this.ws.readyState === WebSocket.OPEN) { + clearInterval(checkInterval); + resolve(true); + } + }, 100); + }); + } + + this.isConnecting = true; + this.isManualClosed = false; + + return new Promise((resolve) => { + try { + this.ws = new WebSocket(this.url); + + this.ws.onopen = () => { + console.log('WebSocket connected'); + this.reconnectAttempts = 0; + this.isConnecting = false; + resolve(true); + }; + + this.ws.onmessage = (event) => { + try { + const data: WSMessage = JSON.parse(event.data); + this.handleMessage(data); + } catch (err) { + console.error('Failed to parse WebSocket message', err); + } + }; + + this.ws.onclose = (event) => { + this.isConnecting = false; + console.log('WebSocket disconnected', event.code, event.reason); + + if (!this.isManualClosed) { + this.attemptReconnect(); + } + + if (this.ws) { + this.ws = null; + } + }; + + this.ws.onerror = (error) => { + this.isConnecting = false; + console.error('WebSocket error:', error); + resolve(false); + }; + } catch (error) { + this.isConnecting = false; + console.error('Failed to create WebSocket instance', error); + resolve(false); + this.attemptReconnect(); + } + }); + } + + // 关闭WebSocket连接 + disconnect() { + this.isManualClosed = true; + + if (this.reconnectTimeoutId) { + window.clearTimeout(this.reconnectTimeoutId); + this.reconnectTimeoutId = null; + } + + if (this.ws) { + this.ws.close(); + this.ws = null; + } + + // 清空所有订阅 + this.messageSubscribers.clear(); + } + + // 尝试重新连接 + private attemptReconnect() { + if (this.reconnectAttempts >= this.maxReconnectAttempts || this.isManualClosed) { + console.log('Max reconnect attempts reached or manually closed. Giving up.'); + return; + } + + this.reconnectAttempts++; + console.log(`Attempting to reconnect (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`); + + this.reconnectTimeoutId = window.setTimeout(() => { + this.connect().catch(console.error); + }, this.reconnectInterval); + } + + // 处理接收到的消息 + private handleMessage(message: WSMessage) { + if (!message || !message.type) return; + + // 获取该类型的所有订阅者 + const subscribers = this.messageSubscribers.get(message.type); + if (subscribers) { + subscribers.forEach(handler => { + try { + handler(message.data); + } catch (err) { + console.error('Error in message handler', err); + } + }); + } + } + + // 订阅消息 + subscribe(type: MessageType, handler: MessageHandler): () => void { + if (!this.messageSubscribers.has(type)) { + this.messageSubscribers.set(type, new Set()); + } + + const subscribers = this.messageSubscribers.get(type)!; + subscribers.add(handler); + + // 如果WebSocket未连接,尝试连接 + if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { + this.connect().catch(console.error); + } + + // 返回取消订阅的函数 + return () => { + const subscribers = this.messageSubscribers.get(type); + if (subscribers) { + subscribers.delete(handler); + + // 如果该类型没有订阅者了,删除该类型 + if (subscribers.size === 0) { + this.messageSubscribers.delete(type); + } + } + + // 如果没有任何订阅了,断开连接 + if (this.messageSubscribers.size === 0) { + this.disconnect(); + } + }; + } + + // 发送消息 + sendMessage(data: any) { + if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { + console.error('WebSocket is not connected'); + return false; + } + + try { + this.ws.send(JSON.stringify(data)); + return true; + } catch (err) { + console.error('Failed to send message', err); + return false; + } + } + + // 检查是否已连接 + isConnected(): boolean { + return !!this.ws && this.ws.readyState === WebSocket.OPEN; + } +} + +// 创建单例 +const webSocketService = new WebSocketService(); + +export default webSocketService; \ No newline at end of file