二次元绘画创作
56.21M · 2026-02-04
在智能柜仪器管理系统中,用户完成人脸识别后,需要让Android客户端和服务端前端页面实时同步显示该用户可领取的仪器列表,传统的轮询方式存在实时性差、资源消耗高的问题。本文将详细介绍如何基于WebSocket协议,结合Spring Boot、Vue.js和Android(Kotlin)技术栈,实现人脸识别后仪器领取信息的跨端实时同步方案。
用户人脸识别成功后:
| 端侧 | 技术栈 | 核心组件 |
|---|---|---|
| 客户端 | Kotlin | FaceRecognitionActivity(人脸识别)、CardScanActivity(刷卡验证)、ExternalApiService(服务端通信) |
| 服务端 | Java + Spring Boot | RESTful API、UserQueryVO(用户信息)、UsersCardsAuthVO(卡片权限) |
| 前端 | Vue.js | 智能柜管理界面(无实时监控能力) |
针对跨端实时通信需求,选择WebSocket作为核心通信协议,各端技术适配如下:
| 组件 | 技术选型 | 说明 |
|---|---|---|
| 服务端WebSocket | Spring Boot WebSocket + STOMP | 提供标准化的WebSocket服务和消息代理能力 |
| Android客户端 | OkHttp WebSocket | 轻量、稳定的Android端WebSocket实现 |
| Vue前端 | StompJS + SockJS | 兼容STOMP协议,适配浏览器WebSocket兼容性问题 |
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
配置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
}
}
定义通用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; // 广播接收到的用户信息
}
}
// app/build.gradle.kts
implementation("com.squareup.okhttp3:okhttp:4.12.0")
封装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")
}
}
在人脸识别成功回调中,构建并发送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)
}
}
}
pnpm add stompjs sockjs-client
// 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()
// src/main.ts
import WebSocketService from './utils/websocket'
WebSocketService.connect()
window.addEventListener('beforeunload', () => {
WebSocketService.disconnect()
})
<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>
本文基于WebSocket协议,结合Spring Boot、Vue和Android技术栈,实现了智能柜人脸识别后仪器领取信息的跨端实时同步。该方案不仅满足了核心业务需求,还具备良好的可扩展性和维护性,可为类似的跨端实时通信场景提供参考。