初始
This commit is contained in:
94
third/ws/business.go
Normal file
94
third/ws/business.go
Normal file
@ -0,0 +1,94 @@
|
||||
// Package ws
|
||||
// @Author 铁憨憨[cory] 2025/2/12 15:45:00
|
||||
package ws
|
||||
|
||||
/*
|
||||
TheSenderInformationOfTheAssemblyPersonnel 组装下发人员信息
|
||||
*/
|
||||
func TheSenderInformationOfTheAssemblyPersonnel(sn string, userId string, name string, face string) (err error) {
|
||||
sUuid := GenerateUUIDWithSixRandomDigits()
|
||||
people := PeopleInformation{
|
||||
Cmd: "to_device",
|
||||
From: sUuid,
|
||||
To: sn,
|
||||
Data: PeopleInData{
|
||||
Cmd: "addUser",
|
||||
UserID: userId,
|
||||
Name: name,
|
||||
FaceTemplate: face,
|
||||
IDValid: "",
|
||||
},
|
||||
}
|
||||
_, err = SendRequestAndWaitResponse(sn, sUuid, people)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
SelectUserAll 获取打卡设备所有人员
|
||||
*/
|
||||
func SelectUserAll(sn string) (CommonResponse, error) {
|
||||
sUuid := GenerateUUIDWithSixRandomDigits()
|
||||
people := PersonnelInformationAcquisition{
|
||||
Cmd: "to_device",
|
||||
From: sUuid,
|
||||
To: sn,
|
||||
Data: PersonnelInformationAcquisitionTwo{
|
||||
Cmd: "getUserInfo",
|
||||
Value: 1,
|
||||
},
|
||||
}
|
||||
return SendRequestAndWaitResponse(sn, sUuid, people)
|
||||
}
|
||||
|
||||
/*
|
||||
DelByUserId 删除指定人员
|
||||
*/
|
||||
func DelByUserId(sn string, userId string) (CommonResponse, error) {
|
||||
sUuid := GenerateUUIDWithSixRandomDigits()
|
||||
people := DeletionOfPersonnel{
|
||||
Cmd: "to_device",
|
||||
From: sUuid,
|
||||
To: sn,
|
||||
Data: DeletionOfPersonnelData{
|
||||
Cmd: "delUser",
|
||||
UserID: userId,
|
||||
UserType: 0,
|
||||
},
|
||||
}
|
||||
return SendRequestAndWaitResponse(sn, sUuid, people)
|
||||
}
|
||||
|
||||
/*
|
||||
BatchDelete 批量删除指定人员
|
||||
*/
|
||||
func BatchDelete(sn string, userIds []string) (CommonResponse, error) {
|
||||
sUuid := GenerateUUIDWithSixRandomDigits()
|
||||
people := BatchDeletion{
|
||||
Cmd: "to_device",
|
||||
From: sUuid,
|
||||
To: sn,
|
||||
Data: BatchDeletionData{
|
||||
Cmd: "delMultiUser",
|
||||
UserIds: userIds,
|
||||
UserType: 0,
|
||||
},
|
||||
}
|
||||
return SendRequestAndWaitResponse(sn, sUuid, people)
|
||||
}
|
||||
|
||||
/*
|
||||
DelAll 删除指定考勤机全部人员
|
||||
*/
|
||||
func DelAll(sn string) (CommonResponse, error) {
|
||||
sUuid := GenerateUUIDWithSixRandomDigits()
|
||||
people := DeletionALlOfPersonnel{
|
||||
Cmd: "to_device",
|
||||
From: sUuid,
|
||||
To: sn,
|
||||
Data: DeletionALlOfPersonnelData{
|
||||
Cmd: "delAllUser",
|
||||
UserType: 0,
|
||||
},
|
||||
}
|
||||
return SendRequestAndWaitResponse(sn, sUuid, people)
|
||||
}
|
167
third/ws/construction.go
Normal file
167
third/ws/construction.go
Normal file
@ -0,0 +1,167 @@
|
||||
// Package ws
|
||||
// @Author 铁憨憨[cory] 2025/2/12 15:20:00
|
||||
package ws
|
||||
|
||||
import "github.com/gorilla/websocket"
|
||||
|
||||
/*
|
||||
============================================常量============================================
|
||||
============================================常量============================================
|
||||
============================================常量============================================
|
||||
*/
|
||||
const (
|
||||
DECLARE = "declare" //设备初上线
|
||||
PING = "ping" //心跳
|
||||
ToClient = "to_client" // 服务器消息下发到客户端的响应
|
||||
)
|
||||
|
||||
/*
|
||||
============================================公共============================================
|
||||
============================================公共============================================
|
||||
============================================公共============================================
|
||||
*/
|
||||
|
||||
// DeviceInfo 存储连接设备信息
|
||||
type DeviceInfo struct {
|
||||
IP string `json:"ip"`
|
||||
Port string `json:"port"`
|
||||
Conn *websocket.Conn `json:"conn"`
|
||||
}
|
||||
|
||||
// GenericMessage 通用消息结构,用于解析初始的 cmd 字段
|
||||
type GenericMessage struct {
|
||||
CMD string `json:"cmd"`
|
||||
}
|
||||
|
||||
// CommonResponse 公共响应结构
|
||||
type CommonResponse struct {
|
||||
Cmd string `json:"cmd"`
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
//Extra string `json:"extra"`
|
||||
Data CommonResponseData `json:"data"`
|
||||
}
|
||||
type CommonResponseData struct {
|
||||
Cmd string `json:"cmd"`
|
||||
UserIds []string `json:"userIds" dc:"用户IDS"`
|
||||
UserID string `json:"user_id"`
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
DelFailed []DelMultiUserData `json:"delFailed"`
|
||||
}
|
||||
|
||||
type DelMultiUserData struct {
|
||||
UserID string `json:"user_id"`
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
/*
|
||||
============================================基础============================================
|
||||
============================================基础============================================
|
||||
============================================基础============================================
|
||||
*/
|
||||
|
||||
// DeclareMessage 设备上线消息
|
||||
type DeclareMessage struct {
|
||||
Cmd string `json:"cmd"`
|
||||
Type string `json:"type"`
|
||||
SN string `json:"sn"`
|
||||
VersionCode string `json:"version_code"`
|
||||
VersionName string `json:"version_name"`
|
||||
Token string `json:"token"`
|
||||
Ip string `json:"ip"`
|
||||
Timestamp float64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
type PongMessage struct {
|
||||
Cmd string `json:"cmd"`
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
Data PongMessageData `json:"data"`
|
||||
}
|
||||
|
||||
// PongMessageData 心跳回复消息结构
|
||||
type PongMessageData struct {
|
||||
Cmd string `json:"cmd"`
|
||||
}
|
||||
|
||||
/*
|
||||
============================================ws请求============================================
|
||||
============================================ws请求============================================
|
||||
============================================ws请求============================================
|
||||
*/
|
||||
|
||||
// PeopleInformation 人员信息下发
|
||||
type PeopleInformation struct {
|
||||
Cmd string `json:"cmd"` //该接口固定为to_device
|
||||
From string `json:"from"` //可不填写,填写uuid来做为发送请求或响应的标识
|
||||
To string `json:"to"` //设备号(请查看公共设置中的设备号)
|
||||
//Extra string `json:"extra"` //给服务端预留的补充字段,设备端不处理这个字段内容。设备在响应这条指令时原样返回
|
||||
Data PeopleInData `json:"data"`
|
||||
}
|
||||
type PeopleInData struct {
|
||||
Cmd string `json:"cmd"`
|
||||
UserID string `json:"user_id"`
|
||||
Name string `json:"name"`
|
||||
FaceTemplate string `json:"face_template"` // http 链接图
|
||||
IDValid string `json:"id_valid"` // 人员有效期(人员在这个时间点后,无法通行)格式:yyyy-MM-dd 或者 yyyy-MM-dd HH:mm,为 “” 则为永久
|
||||
}
|
||||
|
||||
// DeletionOfPersonnel 删除人员
|
||||
type DeletionOfPersonnel struct {
|
||||
Cmd string `json:"cmd"`
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
Data DeletionOfPersonnelData `json:"data"`
|
||||
}
|
||||
type DeletionOfPersonnelData struct {
|
||||
Cmd string `json:"cmd"`
|
||||
UserID string `json:"user_id"`
|
||||
UserType int `json:"user_type"` // 删除的用户类型:0-人脸接口下发的数据 1-人证比对接口下发的数据
|
||||
}
|
||||
|
||||
// BatchDeletion 批量删除
|
||||
type BatchDeletion struct {
|
||||
Cmd string `json:"cmd"`
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
Data BatchDeletionData `json:"data"`
|
||||
}
|
||||
type BatchDeletionData struct {
|
||||
Cmd string `json:"cmd"`
|
||||
UserIds []string `json:"user_ids"`
|
||||
UserType int `json:"user_type"` // 删除的用户类型:0-人脸接口下发的数据 1-人证比对接口下发的数据
|
||||
}
|
||||
|
||||
// DeletionALlOfPersonnel 删除全部人员
|
||||
type DeletionALlOfPersonnel struct {
|
||||
Cmd string `json:"cmd"`
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
Data DeletionALlOfPersonnelData `json:"data"`
|
||||
}
|
||||
type DeletionALlOfPersonnelData struct {
|
||||
Cmd string `json:"cmd"`
|
||||
UserType int `json:"user_type"` // 删除的用户类型:0-人脸接口下发的数据 1-人证比对接口下发的数据
|
||||
}
|
||||
|
||||
// PersonnelInformationAcquisition 人员信息获取
|
||||
type PersonnelInformationAcquisition struct {
|
||||
Cmd string `json:"cmd"` //该接口固定为to_device
|
||||
From string `json:"from"` //可不填写,填写uuid来做为发送请求或响应的标识
|
||||
To string `json:"to"` //设备号(请查看公共设置中的设备号)
|
||||
Data PersonnelInformationAcquisitionTwo `json:"data"`
|
||||
}
|
||||
type PersonnelInformationAcquisitionTwo struct {
|
||||
Cmd string `json:"cmd"`
|
||||
Value int `json:"value"`
|
||||
}
|
||||
|
||||
/*
|
||||
============================================http请求============================================
|
||||
============================================http请求============================================
|
||||
============================================http请求============================================
|
||||
*/
|
||||
|
||||
//loggerMiddlewareloggerMiddlewareloggerMiddleware
|
265
third/ws/ws.go
Normal file
265
third/ws/ws.go
Normal file
@ -0,0 +1,265 @@
|
||||
// Package ws
|
||||
// @Author 铁憨憨[cory] 2025/2/12 15:18:00
|
||||
package ws
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/tiger1103/gfast/v3/api/v1/system"
|
||||
"github.com/tiger1103/gfast/v3/internal/app/system/service"
|
||||
"golang.org/x/net/context"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 存储所有连接的设备信息
|
||||
var connectedDevices = make(map[string]*DeviceInfo)
|
||||
|
||||
// 存储每个 uuid 对应的通道
|
||||
var responseChannels = make(map[string]chan CommonResponse)
|
||||
var responseChannelsMutex sync.Mutex
|
||||
|
||||
// HandleWebSocket 处理WebSocket连接的函数
|
||||
func HandleWebSocket(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := gctx.New()
|
||||
// 将HTTP连接升级为WebSocket连接
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Println("WebSocket升级失败:", err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// 读取设备发送的第一条消息,从中提取 SN然后添加到设备列表中
|
||||
declare, err := addDevice(ctx, conn, r)
|
||||
if err != nil {
|
||||
log.Println("添加设备信息时出错:", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 持续读取从客户端(其他服务器)发送过来的数据
|
||||
for {
|
||||
// 读取消息类型和消息内容
|
||||
messageType, message, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
log.Println("读取消息时出错:", err)
|
||||
// 移除设备信息
|
||||
delete(connectedDevices, declare.SN)
|
||||
log.Printf("设备断开连接,设备信息: %+v", declare)
|
||||
err = service.BusAttendanceMachine().Change(ctx, &system.BusAttendanceMachineChangeReq{
|
||||
Sn: declare.SN,
|
||||
Status: "0",
|
||||
})
|
||||
log.Println("修改考勤设备状态时出错:", err)
|
||||
break
|
||||
}
|
||||
|
||||
// 先解析出 cmd 字段
|
||||
var genericMsg GenericMessage
|
||||
err = json.Unmarshal(message, &genericMsg)
|
||||
if err != nil {
|
||||
log.Println("解析 cmd 字段时出错:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// 根据 cmd 字段进行不同处理
|
||||
switch genericMsg.CMD {
|
||||
case DECLARE:
|
||||
log.Println("设备在线:", declare)
|
||||
case PING:
|
||||
|
||||
declareMessage := DeclareMessage{}
|
||||
if json.Unmarshal(message, &declareMessage) != nil {
|
||||
return
|
||||
}
|
||||
if err = handlePing(ctx, conn, r, declareMessage.SN); err != nil {
|
||||
log.Println("处理心跳回复:", err)
|
||||
}
|
||||
case ToClient:
|
||||
if err := requestResponse(message); err != nil {
|
||||
log.Println("处理响应:", err)
|
||||
}
|
||||
default:
|
||||
log.Printf("收到未知消息---> 类型: %d, 消息内容: %s", messageType, string(message))
|
||||
}
|
||||
}
|
||||
// 清理通道资源
|
||||
responseChannelsMutex.Lock()
|
||||
for key, ch := range responseChannels {
|
||||
if connectedDevices[declare.SN] != nil {
|
||||
delete(responseChannels, key)
|
||||
close(ch)
|
||||
}
|
||||
}
|
||||
responseChannelsMutex.Unlock()
|
||||
}
|
||||
|
||||
// addDevice 将设备信息添加到设备列表中
|
||||
func addDevice(ctx context.Context, conn *websocket.Conn, r *http.Request) (declareMessage DeclareMessage, err error) {
|
||||
// 读取设备发送的第一条消息,从中提取 SN
|
||||
_, message, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
declareMessage = DeclareMessage{}
|
||||
if json.Unmarshal(message, &declareMessage) != nil {
|
||||
return
|
||||
}
|
||||
// 假设设备信息可以从请求中获取,这里简单从 RemoteAddr 解析
|
||||
ip, port := parseRemoteAddr(r.RemoteAddr)
|
||||
deviceInfo := &DeviceInfo{
|
||||
IP: ip,
|
||||
Port: port,
|
||||
Conn: conn,
|
||||
}
|
||||
if declareMessage.SN != "" {
|
||||
// 存储设备信息
|
||||
connectedDevices[declareMessage.SN] = deviceInfo
|
||||
log.Printf("新设备连接,设备信息: %+v", declareMessage)
|
||||
// 变更状态
|
||||
err = service.BusAttendanceMachine().Register(ctx, &system.BusAttendanceMachineRegisterReq{
|
||||
Sn: declareMessage.SN,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return declareMessage, nil
|
||||
}
|
||||
|
||||
// handlePing 处理 ping 消息
|
||||
func handlePing(ctx context.Context, conn *websocket.Conn, r *http.Request, sn string) (err error) {
|
||||
pongMsg := PongMessageData{
|
||||
Cmd: "pong",
|
||||
}
|
||||
pongJSON, _ := json.Marshal(pongMsg)
|
||||
err = conn.WriteMessage(websocket.TextMessage, pongJSON)
|
||||
if err != nil {
|
||||
log.Println("发送 Pong 消息时出错:", err)
|
||||
return err
|
||||
}
|
||||
// 存储设备信息、变更状态
|
||||
connectedDevices[sn] = &DeviceInfo{
|
||||
Conn: conn,
|
||||
}
|
||||
err = service.BusAttendanceMachine().Register(ctx, &system.BusAttendanceMachineRegisterReq{
|
||||
Sn: sn,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// requestResponse 处理请求得到的响应
|
||||
func requestResponse(message []byte) (err error) {
|
||||
log.Println("请求响应:", string(message))
|
||||
var common CommonResponse
|
||||
err = json.Unmarshal(message, &common)
|
||||
if err != nil {
|
||||
log.Println("解析 cmd 字段时出错:", err)
|
||||
return err
|
||||
}
|
||||
// 根据UUID查找对应的通道
|
||||
responseChannelsMutex.Lock()
|
||||
if ch, ok := responseChannels[common.To]; ok {
|
||||
ch <- common
|
||||
delete(responseChannels, common.To)
|
||||
}
|
||||
responseChannelsMutex.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 根据 SN 发送消息给对应的设备
|
||||
func sendMessageToDevice(sn string, uuid string, message interface{}) error {
|
||||
conn, exists := connectedDevices[sn]
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
msgJSON, err := json.Marshal(message)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = conn.Conn.WriteMessage(websocket.TextMessage, msgJSON)
|
||||
if err != nil {
|
||||
responseChannelsMutex.Lock()
|
||||
delete(responseChannels, uuid)
|
||||
responseChannelsMutex.Unlock()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SendRequestAndWaitResponse(sn string, sUuid string, payload interface{}) (CommonResponse, error) {
|
||||
responseChan := make(chan CommonResponse, 1)
|
||||
// 存入全局映射
|
||||
responseChannelsMutex.Lock()
|
||||
responseChannels[sUuid] = responseChan
|
||||
responseChannelsMutex.Unlock()
|
||||
|
||||
// 发送请求
|
||||
err := sendMessageToDevice(sn, sUuid, payload)
|
||||
if err != nil {
|
||||
return CommonResponse{}, err
|
||||
}
|
||||
|
||||
// 等待响应
|
||||
select {
|
||||
case resp := <-responseChan:
|
||||
fmt.Printf("收到响应: %+v\n", resp)
|
||||
return resp, nil
|
||||
case <-time.After(10 * time.Second):
|
||||
responseChannelsMutex.Lock()
|
||||
delete(responseChannels, sUuid)
|
||||
responseChannelsMutex.Unlock()
|
||||
return CommonResponse{}, fmt.Errorf("等待响应超时")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=========================================================业务无关=========================================================
|
||||
=========================================================业务无关=========================================================
|
||||
=========================================================业务无关=========================================================
|
||||
*/
|
||||
|
||||
// 定义一个升级器,用于将HTTP连接升级为WebSocket连接
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
// 允许跨域访问,这里设置为允许所有来源
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
// 解析远程地址,获取 IP 和端口
|
||||
func parseRemoteAddr(addr string) (string, string) {
|
||||
// 简单处理,假设地址格式为 IP:Port
|
||||
for i := len(addr) - 1; i >= 0; i-- {
|
||||
if addr[i] == ':' {
|
||||
return addr[:i], addr[i+1:]
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// GenerateUUIDWithSixRandomDigits 生成一个 UUID 并拼接 6 位随机数
|
||||
func GenerateUUIDWithSixRandomDigits() string {
|
||||
// 生成 UUID
|
||||
uuidStr := uuid.New().String()
|
||||
// 初始化随机数种子
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
// 生成 6 位随机数
|
||||
randomNum := rand.Intn(900000) + 100000
|
||||
// 将随机数转换为字符串
|
||||
randomNumStr := strconv.Itoa(randomNum)
|
||||
// 拼接 UUID 和 6 位随机数
|
||||
result := uuidStr + "-" + randomNumStr
|
||||
return result
|
||||
}
|
Reference in New Issue
Block a user