WebSocket+Spring Boot/Vue/Android:智能柜跨端实时数据同步实践

38 阅读4分钟

引言

在智能柜仪器管理系统中,用户完成人脸识别后,需要让Android客户端和服务端前端页面实时同步显示该用户可领取的仪器列表,传统的轮询方式存在实时性差、资源消耗高的问题。本文将详细介绍如何基于WebSocket协议,结合Spring Boot、Vue.js和Android(Kotlin)技术栈,实现人脸识别后仪器领取信息的跨端实时同步方案。

一、需求与架构分析

1. 核心需求

用户人脸识别成功后:

  • Android客户端(SmartCabinet)在刷卡页面展示可领取仪器列表;
  • 服务端Vue前端(SmartCabinetManageVue)实时展示该用户可领取仪器信息。

2. 现有系统架构

端侧技术栈核心组件
客户端KotlinFaceRecognitionActivity(人脸识别)、CardScanActivity(刷卡验证)、ExternalApiService(服务端通信)
服务端Java + Spring BootRESTful API、UserQueryVO(用户信息)、UsersCardsAuthVO(卡片权限)
前端Vue.js智能柜管理界面(无实时监控能力)

二、核心方案设计

1. 技术选型

针对跨端实时通信需求,选择WebSocket作为核心通信协议,各端技术适配如下:

组件技术选型说明
服务端WebSocketSpring Boot WebSocket + STOMP提供标准化的WebSocket服务和消息代理能力
Android客户端OkHttp WebSocket轻量、稳定的Android端WebSocket实现
Vue前端StompJS + SockJS兼容STOMP协议,适配浏览器WebSocket兼容性问题

2. 核心流程

  1. 客户端人脸识别成功后,通过WebSocket向服务端发送用户信息;
  2. 服务端接收消息后,通过STOMP协议广播至所有连接的前端客户端;
  3. 前端接收消息,实时更新页面展示用户可领取仪器列表。

三、服务端实现(Spring Boot)

1. 引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

2. WebSocket配置

配置STOMP代理和端点,支持跨域和SockJS降级:

package cn.harry.config;
​
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
​
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
​
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic"); // 消息广播前缀
        config.setApplicationDestinationPrefixes("/app"); // 客户端发送消息前缀
    }
​
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws")
                .setAllowedOriginPatterns("*")
                .withSockJS(); // 启用SockJS
    }
}

3. 消息模型与控制器

定义通用WebSocket消息结构,实现消息接收与广播:

// 消息模型
package cn.harry.modular.smartcabinet.api.dto;
​
import lombok.Data;
​
@Data
public class WebSocketMessage<T> {
    private String eventType; // 事件类型
    private T data; // 业务数据
    private Long timestamp; // 时间戳
}
​
// WebSocket控制器
package cn.harry.modular.smartcabinet.controller;
​
import cn.harry.modular.smartcabinet.api.dto.WebSocketMessage;
import cn.harry.modular.smartcabinet.api.vo.UserQueryVO;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
​
@Controller
public class WebSocketController {
​
    @MessageMapping("/faceRecognitionSuccess") // 客户端消息接收地址
    @SendTo("/topic/faceRecognition") // 前端订阅地址
    public WebSocketMessage<UserQueryVO> handleFaceRecognitionSuccess(WebSocketMessage<UserQueryVO> message) {
        return message; // 广播接收到的用户信息
    }
}

四、Android客户端实现(Kotlin)

1. 引入依赖

// app/build.gradle.kts
implementation("com.squareup.okhttp3:okhttp:4.12.0")

2. WebSocket工具类

封装OkHttp WebSocket连接、消息发送与断开逻辑:

package cn.harry.cabinet.websocket
​
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import java.util.concurrent.TimeUnit
​
class WebSocketClient(private val url: String) {
​
    private val client = OkHttpClient.Builder()
        .connectTimeout(10, TimeUnit.SECONDS)
        .readTimeout(10, TimeUnit.SECONDS)
        .writeTimeout(10, TimeUnit.SECONDS)
        .build()
​
    private var webSocket: WebSocket? = null
​
    fun connect(listener: WebSocketListener) {
        val request = Request.Builder()
            .url(url)
            .build()
        webSocket = client.newWebSocket(request, listener)
    }
​
    fun send(message: String) {
        webSocket?.send(message)
    }
​
    fun disconnect() {
        webSocket?.close(1000, "Normal closure")
    }
}

3. 人脸识别成功后发送消息

在人脸识别成功回调中,构建并发送WebSocket消息:

​
private fun onAuthSuccess(user: GetUserResponse) {
    // 原有业务逻辑...
    
    // 发送WebSocket消息
    sendFaceRecognitionSuccessMessage(user)
}
​
private fun sendFaceRecognitionSuccessMessage(user: GetUserResponse) {
    lifecycleScope.launch {
        try {
            val wsUrl = withContext(Dispatchers.IO) {
                configRepository.getByTypeAndKey(
                    AppConfigKeys.TYPE_SERVER,
                    AppConfigKeys.KEY_WEBSOCKET_URL
                )?.value ?: "ws://localhost:8080/ws"
            }
            
            val wsClient = WebSocketClient(wsUrl)
            wsClient.connect(object : WebSocketListener() {
                override fun onOpen(webSocket: WebSocket, response: Response) {
                    val message = mapOf(
                        "eventType" to "FACE_RECOGNITION_SUCCESS",
                        "data" to user,
                        "timestamp" to System.currentTimeMillis()
                    )
                    webSocket.send(Json.encodeToString(message))
                    webSocket.close(1000, "Message sent")
                }
            })
        } catch (e: Exception) {
            Log.e(TAG, "发送WebSocket消息失败", e)
        }
    }
}

五、Vue前端实现

1. 安装依赖

​
pnpm add stompjs sockjs-client

2. WebSocket工具封装

// src/utils/websocket.js
import SockJS from 'sockjs-client'
import Stomp from 'stompjs'class WebSocketService {
  constructor() {
    this.stompClient = null
    this.callbacks = {}
  }
​
  connect(url = '/ws') {
    return new Promise((resolve, reject) => {
      try {
        const socket = new SockJS(url)
        this.stompClient = Stomp.over(socket)
        
        this.stompClient.connect({}, () => {
          this.stompClient.subscribe('/topic/faceRecognition', (message) => {
            const data = JSON.parse(message.body)
            this.handleMessage(data)
          })
          resolve()
        }, (error) => {
          reject(error)
        })
      } catch (error) {
        reject(error)
      }
    })
  }
​
  on(eventType, callback) {
    if (!this.callbacks[eventType]) {
      this.callbacks[eventType] = []
    }
    this.callbacks[eventType].push(callback)
  }
​
  handleMessage(message) {
    const { eventType } = message
    if (this.callbacks[eventType]) {
      this.callbacks[eventType].forEach(callback => callback(message))
    }
  }
}
​
export default new WebSocketService()

3. 全局初始化连接

// src/main.ts
import WebSocketService from './utils/websocket'WebSocketService.connect()
​
window.addEventListener('beforeunload', () => {
  WebSocketService.disconnect()
})

4. 实时监控页面开发

<template>
  <div class="real-time-monitor">
    <h3>实时监控</h3>
    <div v-if="currentUser" class="user-info">
      <h4>当前识别用户</h4>
      <p>姓名:{{ currentUser.name }}</p>
      <p>工号:{{ currentUser.employeeId }}</p>
      <p>部门:{{ currentUser.dept }}</p>
    </div>
    <div v-if="currentUser?.cardsAuth && currentUser.cardsAuth.length > 0" class="cards-auth">
      <h4>可领取仪器</h4>
      <el-table :data="currentUser.cardsAuth" style="width: 100%">
        <el-table-column prop="typeName" label="仪器类型" />
        <el-table-column prop="maxNum" label="最大领取数量" />
        <el-table-column prop="receivedNum" label="已领取数量" />
        <el-table-column label="可领取数量" :formatter="(row) => row.maxNum - row.receivedNum" />
      </el-table>
    </div>
  </div>
</template><script setup>
import { ref, onMounted } from 'vue'
import WebSocketService from '@/utils/websocket'const currentUser = ref(null)
​
const handleFaceRecognitionSuccess = (message) => {
  currentUser.value = message.data
  setTimeout(() => {
    currentUser.value = null
  }, 5000) // 5秒后清除展示
}
​
onMounted(() => {
  WebSocketService.on('FACE_RECOGNITION_SUCCESS', handleFaceRecognitionSuccess)
})
</script>

六、方案评估与扩展

1. 方案优势

  • 实时性:基于WebSocket长连接,消息秒级同步,优于轮询;
  • 低耦合:客户端、前端通过服务端广播通信,无直接依赖;
  • 易扩展:可快速添加刷卡成功、仪器归还等实时事件。

2. 优化方向

  • 连接可靠性:添加WebSocket断线重连、心跳检测机制;
  • 消息可靠性:增加消息确认机制,避免消息丢失;
  • 权限控制:限制前端实时监控页面的访问权限,仅对管理员开放;
  • 性能优化:服务端添加连接数限制,避免高并发下的性能问题。

七、总结

本文基于WebSocket协议,结合Spring Boot、Vue和Android技术栈,实现了智能柜人脸识别后仪器领取信息的跨端实时同步。该方案不仅满足了核心业务需求,还具备良好的可扩展性和维护性,可为类似的跨端实时通信场景提供参考。