初始
This commit is contained in:
98
api/saft_hat/device.go
Normal file
98
api/saft_hat/device.go
Normal file
@ -0,0 +1,98 @@
|
||||
package saft_hat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 新增安全帽设备
|
||||
type CreateDeviceReq struct {
|
||||
g.Meta `path:"/device/create" method:"post" tags:"设备管理" summary:"创建新设备"`
|
||||
DevNum string `json:"devNum" dc:"设备编号"`
|
||||
DevName string `json:"devName" dc:"设备名称"`
|
||||
ProjectID int64 `json:"projectId" dc:"项目ID"`
|
||||
}
|
||||
|
||||
type CreateDeviceRes struct {
|
||||
}
|
||||
|
||||
func (h Hat) CreateDevice(ctx context.Context, req *CreateDeviceReq) (res *CreateDeviceRes, err error) {
|
||||
res = new(CreateDeviceRes)
|
||||
device := g.Map{
|
||||
"dev_num": req.DevNum,
|
||||
"dev_name": req.DevName,
|
||||
"project_id": req.ProjectID,
|
||||
"create_time": time.Now(),
|
||||
}
|
||||
_, err = g.Model("device").Ctx(ctx).Insert(device)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 获取安全帽设备列表
|
||||
type DeviceListReq struct {
|
||||
g.Meta `path:"/device/list" method:"get" tags:"设备管理" summary:"获取设备信息"`
|
||||
ProjectId int64 `json:"projectId" dc:"项目ID"`
|
||||
Page int64 `json:"page" dc:"请求的页码" v:"required"`
|
||||
PageSize int64 `json:"pageSize" dc:"每页显示的条目数" v:"required"`
|
||||
}
|
||||
|
||||
type DeviceListRes struct {
|
||||
Devices []Device `json:"devices"` // 设备信息
|
||||
}
|
||||
|
||||
func (h Hat) GetDevice(ctx context.Context, req *DeviceListReq) (res *DeviceListRes, err error) {
|
||||
res = new(DeviceListRes)
|
||||
offset := (req.Page - 1) * req.PageSize
|
||||
var devices []Device
|
||||
err = g.Model("device").Ctx(ctx).Where("project_id = ?", req.ProjectId).Offset(int(offset)).Limit(int(req.PageSize)).Scan(&devices)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res.Devices = devices
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 删除设备信息
|
||||
type DeleteDeviceReq struct {
|
||||
g.Meta `path:"/device/delete" method:"delete" tags:"设备管理" summary:"删除设备"`
|
||||
DevNum string `json:"devNum" dc:"设备编号"` // 设备编号
|
||||
}
|
||||
|
||||
type DeleteDeviceRes struct {
|
||||
}
|
||||
|
||||
func (h Hat) DeleteDevice(ctx context.Context, req *DeleteDeviceReq) (res *DeleteDeviceRes, err error) {
|
||||
res = new(DeleteDeviceRes)
|
||||
// 执行删除操作
|
||||
_, err = g.Model("device").Ctx(ctx).Where("dev_num", req.DevNum).Delete()
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 修改安全帽的名称
|
||||
type UpdateDeviceNameReq struct {
|
||||
g.Meta `path:"/device/update/name" method:"post" tags:"设备管理" summary:"更新安全帽设备名称"`
|
||||
DevNum string `json:"devNum" dc:"设备编号"`
|
||||
DevName string `json:"devName" dc:"设备名称"`
|
||||
}
|
||||
|
||||
type UpdateDeviceNameRes struct {
|
||||
}
|
||||
|
||||
func (h Hat) UpdateDeviceName(ctx context.Context, req *UpdateDeviceNameReq) (res *UpdateDeviceNameRes, err error) {
|
||||
res = new(UpdateDeviceNameRes)
|
||||
_, err = g.Model("device").Data(g.Map{
|
||||
"dev_name": req.DevName,
|
||||
}).Where("dev_num", req.DevNum).Update()
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
39
api/saft_hat/entity.go
Normal file
39
api/saft_hat/entity.go
Normal file
@ -0,0 +1,39 @@
|
||||
package saft_hat
|
||||
|
||||
import "time"
|
||||
|
||||
type Device struct {
|
||||
DevNum string `json:"devNum" dc:"设备编号"`
|
||||
DevName string `json:"devName" dc:"设备名称"`
|
||||
Status int `json:"status" dc:"状态"`
|
||||
CreateTime time.Time `json:"createTime" dc:"添加时间"`
|
||||
UpdateTime time.Time `json:"updateTime" dc:"修改时间"`
|
||||
ProjectID int64 `json:"projectId" dc:"项目id"`
|
||||
Temperature float64 `json:"temperature" dc:"设备采集温度"`
|
||||
Humidity float64 `json:"humidity" dc:"设备采集湿度"`
|
||||
Posture int `json:"posture" dc:"姿势,1表示正常,-1表示脱帽,-2表示倒地"`
|
||||
BatteryTemp float64 `json:"batteryTemp" dc:"电池温度"`
|
||||
FixedBy string `json:"fixedBy" dc:"定位方"`
|
||||
BatteryLevel float64 `json:"batteryLevel" dc:"电量"`
|
||||
IsLowBattery int `json:"isLowBattery" dc:"是否低电量 1为低电量,0为正常"`
|
||||
}
|
||||
|
||||
type Location struct {
|
||||
DevNum string `json:"devNum" dc:"设备编号"`
|
||||
Time string `json:"time" dc:"时间"`
|
||||
Alarm *int `json:"alarm" dc:"告警信息"`
|
||||
Status *int `json:"status" dc:"状态"`
|
||||
Latitude float64 `json:"latitude" dc:"纬度"`
|
||||
Longitude float64 `json:"longitude" dc:"经度"`
|
||||
Elevation *int16 `json:"elevation" dc:"海拔"`
|
||||
Speed *int16 `json:"speed" dc:"速度"`
|
||||
Direction *int16 `json:"direction" dc:"方向"`
|
||||
Mileage *int64 `json:"mileage" dc:"里程数"`
|
||||
AccEnable *bool `json:"accEnable" dc:"ACC开关"`
|
||||
LocateEnable *bool `json:"locateEnable" dc:"定位开关"`
|
||||
LatitudeType *int8 `json:"latitudeType" dc:"纬度类型"` // 0:北纬, 1:南纬
|
||||
LongitudeType *int8 `json:"longitudeType" dc:"经度类型"` // 0:东经, 1:西经
|
||||
SpeedingWarn *bool `json:"speedingWarn" dc:"超速报警"`
|
||||
PowerVoltageWarn *bool `json:"powerVoltageWarn" dc:"电压告警"`
|
||||
PowerFailure *bool `json:"powerFailure" dc:"电源掉电"`
|
||||
}
|
8
api/saft_hat/hat.go
Normal file
8
api/saft_hat/hat.go
Normal file
@ -0,0 +1,8 @@
|
||||
package saft_hat
|
||||
|
||||
// 定义签名的 APPID 和密钥常量
|
||||
const secret = "5366474e589c4dcfadeef223a466ca0b"
|
||||
const appid = "0c9ab925c6684ab4a33350e15ee35062"
|
||||
|
||||
type Hat struct {
|
||||
}
|
54
api/saft_hat/hat_danger.go
Normal file
54
api/saft_hat/hat_danger.go
Normal file
@ -0,0 +1,54 @@
|
||||
package saft_hat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
)
|
||||
|
||||
// 危险源数据上传结构体
|
||||
type Danger struct {
|
||||
BT_MAC string `json:"BT_MAC"` // 危险源MAC
|
||||
IMEI string `json:"IMEI"` // 设备IMEI编号
|
||||
}
|
||||
|
||||
// 危险源数据上传请求
|
||||
type DangerReq struct {
|
||||
g.Meta `path:"/device/danger" method:"post" tags:"安全帽相关" summary:"接收危险源数据(不需要前端调用)"`
|
||||
}
|
||||
|
||||
type DangerRes struct {
|
||||
}
|
||||
|
||||
func (h Hat) Danger(ctx context.Context, req *DangerReq) (res *DangerRes, err error) {
|
||||
res = new(DangerRes)
|
||||
r := g.RequestFromCtx(ctx)
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
log.Printf("Failed to read request body: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
timestamp := r.GetHeader("timestamp")
|
||||
signature := r.GetHeader("signature")
|
||||
|
||||
if !VerifySignature(string(body)+timestamp, signature, secret) {
|
||||
glog.Errorf(ctx, "Signature verification failed")
|
||||
return nil, errors.New("signature verification failed")
|
||||
}
|
||||
|
||||
var danger Danger
|
||||
if err := gjson.DecodeTo(body, &danger); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Println("危险源数据", danger)
|
||||
|
||||
return res, nil
|
||||
}
|
259
api/saft_hat/hat_data.go
Normal file
259
api/saft_hat/hat_data.go
Normal file
@ -0,0 +1,259 @@
|
||||
package saft_hat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"github.com/gorilla/websocket"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func StartWs() {
|
||||
// 设置 WebSocket 路由
|
||||
http.HandleFunc("/ws", HandleConnections)
|
||||
|
||||
// 开启一个新的协程,处理消息广播
|
||||
go HandleMessages()
|
||||
|
||||
// 启动 WebSocket 服务器
|
||||
log.Println("http server started on :8222")
|
||||
err := http.ListenAndServe(":8222", nil)
|
||||
if err != nil {
|
||||
log.Fatal("ListenAndServe: ", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// WebSocket 部分
|
||||
var clients = make(map[*websocket.Conn]bool) // 连接的客户端
|
||||
var broadcast = make(chan []byte) // 广播通道
|
||||
var upgrader = websocket.Upgrader{
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
// 处理连接
|
||||
func HandleConnections(w http.ResponseWriter, r *http.Request) {
|
||||
ws, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer ws.Close()
|
||||
|
||||
// 注册新客户端
|
||||
clients[ws] = true
|
||||
|
||||
// 无限循环,保持连接活跃,但不处理任何消息
|
||||
for {
|
||||
if _, _, err := ws.ReadMessage(); err != nil {
|
||||
log.Printf("error: %v", err)
|
||||
delete(clients, ws)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理消息
|
||||
func HandleMessages() {
|
||||
for {
|
||||
// 从广播通道中获取消息
|
||||
msg := <-broadcast
|
||||
|
||||
// 发送消息到所有连接的客户端
|
||||
for client := range clients {
|
||||
err := client.WriteMessage(websocket.TextMessage, msg)
|
||||
if err != nil {
|
||||
log.Printf("websocket write error: %v", err)
|
||||
client.Close()
|
||||
delete(clients, client)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WS 推送的数据
|
||||
type BroadcastLocation struct {
|
||||
DevNum string `json:"devNum"`
|
||||
Time string `json:"time"`
|
||||
Latitude float64 `json:"latitude"`
|
||||
Longitude float64 `json:"longitude"`
|
||||
}
|
||||
|
||||
// Webhook 定位数据上传【远程发送给我们平台的数据】
|
||||
type WebhookRequest struct {
|
||||
Temperature float64 `json:"temperature"` // 设备采集温度数据
|
||||
Humidity float64 `json:"humidity"` // 设备采集湿度数据
|
||||
Posture int `json:"posture"` // 姿态:1正常 -1脱帽 -2倒地
|
||||
BatteryTemp float64 `json:"batteryTemp"` // 电池温度
|
||||
FixedBy string `json:"fixedBy"` // 定位方式:GPS/BD、WIFI、BT
|
||||
UtcDateTime int64 `json:"utcDateTime"` // 设备内部上传时间戳
|
||||
IMEI string `json:"IMEI"` // 设备IMEI
|
||||
BatteryLevel float64 `json:"batteryLevel"` // 电池电量
|
||||
Charging int `json:"charging"` // 是否正在充电 1充电 0没充电
|
||||
BluetoothMac string `json:"bluetoothMac"` // 蓝牙Mac地址
|
||||
Type string `json:"type"` // 设备上传类型和时间
|
||||
Latitude float64 `json:"latitude"` // WGS-84:是国际标准,GPS坐标(Google Earth使用、或者GPS模块)纬度坐标
|
||||
Longitude float64 `json:"longitude"` // WGS-84:是国际标准,GPS坐标(Google Earth使用、或者GPS模块)经度坐标
|
||||
Altitude int `json:"altitude"` // 海拔高度
|
||||
BT string `json:"bt"` // 蓝牙定位相关:{个数}:{地址,信号}
|
||||
LBS []string `json:"LBS"` // 多基站定位信息
|
||||
MAC []string `json:"MAC"` // WiFi - MAC地址信号
|
||||
}
|
||||
|
||||
// 定位数据上传请求
|
||||
type DataReq struct {
|
||||
g.Meta `path:"/device/data" method:"post" tags:"安全帽相关" summary:"接收安全帽数据(不需要前端调用)"`
|
||||
}
|
||||
|
||||
type DataRes struct {
|
||||
}
|
||||
|
||||
func (h Hat) Data(ctx context.Context, req *DataReq) (res *DataRes, err error) {
|
||||
res = new(DataRes)
|
||||
r := g.RequestFromCtx(ctx)
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
log.Printf("读取请求头失败: %v", err)
|
||||
return res, err
|
||||
}
|
||||
defer r.Body.Close() // 延迟关闭请求体
|
||||
|
||||
// 从请求头中获取时间戳和签名
|
||||
timestamp := r.GetHeader("timestamp")
|
||||
signature := r.GetHeader("signature")
|
||||
|
||||
// 验证签名的有效性
|
||||
if !VerifySignature(string(body)+timestamp, signature, secret) {
|
||||
glog.Errorf(ctx, "签名验证失败")
|
||||
return res, errors.New("验证签名失败")
|
||||
}
|
||||
|
||||
// 解析请求体中的JSON数据到预定义的结构体
|
||||
var webhookData WebhookRequest
|
||||
if err := gjson.DecodeTo(body, &webhookData); err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// 拿到数据之后,先取出 IMEI 设备号,根据 IMEI 查询,不存在则先插入数据到 device 表
|
||||
device := Device{
|
||||
DevNum: webhookData.IMEI,
|
||||
Temperature: webhookData.Temperature,
|
||||
Humidity: webhookData.Humidity,
|
||||
Posture: webhookData.Posture,
|
||||
FixedBy: webhookData.FixedBy,
|
||||
BatteryLevel: webhookData.BatteryLevel,
|
||||
}
|
||||
|
||||
// 查询设备是否存在,存在则更新安全帽的状态信息
|
||||
count, err := g.Model(&device).Where("dev_num", device.DevNum).Count()
|
||||
if err != nil {
|
||||
glog.Errorf(ctx, "查询设备是否存在出错:%v", err)
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
// 判断电池是否处于低电量状态
|
||||
var isLowBattery int
|
||||
|
||||
// 电量小于 0.2 则设置为低电量
|
||||
if device.BatteryLevel < 0.2 {
|
||||
isLowBattery = 1
|
||||
} else {
|
||||
isLowBattery = 0
|
||||
}
|
||||
|
||||
// 更新设备的数据
|
||||
_, err = g.Model("device").Data(g.Map{
|
||||
"temperature": device.Temperature,
|
||||
"humidity": device.Humidity,
|
||||
"posture": device.Posture,
|
||||
"battery_temp": device.BatteryTemp,
|
||||
"fixed_by": device.FixedBy,
|
||||
"battery_level": device.BatteryLevel * 100,
|
||||
"is_low_battery": isLowBattery,
|
||||
"update_time": time.Now(),
|
||||
}).Where("dev_num", device.DevNum).Update()
|
||||
|
||||
if err != nil {
|
||||
glog.Errorf(ctx, "更新设备数据出错:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 将接收到的数据转换为 Location 结构体
|
||||
location := Location{
|
||||
DevNum: webhookData.IMEI, // 设备编号为 IMEI
|
||||
Time: time.Now().Format("2006-01-02 15:04:05"), // 当前时间
|
||||
Latitude: webhookData.Latitude, // 将纬度转换为指针类型
|
||||
Longitude: webhookData.Longitude, // 将经度转换为指针类型
|
||||
}
|
||||
|
||||
// 构建插入数据的参数
|
||||
data := g.Map{
|
||||
"dev_num": location.DevNum,
|
||||
"time": location.Time,
|
||||
"latitude": location.Latitude,
|
||||
"longitude": location.Longitude,
|
||||
}
|
||||
|
||||
// 检查经纬度是否同时为 0,如果是则不执行插入操作,也不需要调用 Redis
|
||||
if location.Latitude != 0 || location.Longitude != 0 {
|
||||
// 执行插入操作
|
||||
_, err = g.Model("location").Data(data).Insert()
|
||||
// 获取 Redis 客户端
|
||||
redis := g.Redis("helmetRedis")
|
||||
|
||||
// 构造 Redis 的 key 和 value
|
||||
key := "safety_helmet:" + location.DevNum
|
||||
value := strconv.FormatFloat(location.Latitude, 'f', -1, 64) + "," + strconv.FormatFloat(location.Longitude, 'f', -1, 64)
|
||||
|
||||
// 插入数据之后,写入 Redis 的发布订阅
|
||||
_, err = redis.Publish(ctx, key, value)
|
||||
if err != nil {
|
||||
glog.Info(ctx, "发布订阅出错")
|
||||
return res, nil
|
||||
}
|
||||
// 插入数据之后,写入 Redis 的键值对
|
||||
_, err = redis.Set(ctx, key, value)
|
||||
if err != nil {
|
||||
glog.Info(ctx, "设置到Redis出错")
|
||||
return res, nil
|
||||
}
|
||||
if err != nil {
|
||||
// 处理可能的错误
|
||||
fmt.Println("Insert error:", err)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Latitude and Longitude are both zero, insertion skipped.")
|
||||
}
|
||||
|
||||
// 返回响应对象
|
||||
return &DataRes{}, nil
|
||||
}
|
||||
|
||||
// 发送数据到 WS 客户端
|
||||
func sendToWs(location Location, err error) {
|
||||
// 创建 WS 需要发送的数据对象
|
||||
broadcastLocation := BroadcastLocation{
|
||||
DevNum: location.DevNum,
|
||||
Time: location.Time,
|
||||
Latitude: location.Latitude,
|
||||
Longitude: location.Longitude,
|
||||
}
|
||||
|
||||
// 转换为 JSON 字符串
|
||||
locationJSON, err := json.Marshal(broadcastLocation)
|
||||
if err != nil {
|
||||
log.Printf("location json marshal error: %v", err)
|
||||
} else {
|
||||
// 发送转换后的JSON字符串到广播通道
|
||||
broadcast <- locationJSON
|
||||
}
|
||||
}
|
78
api/saft_hat/hat_location.go
Normal file
78
api/saft_hat/hat_location.go
Normal file
@ -0,0 +1,78 @@
|
||||
package saft_hat
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// 更新设备定位数据
|
||||
type LocationReq struct {
|
||||
g.Meta `path:"/device/location" method:"post" tags:"安全帽相关" summary:"刷新设备定位数据"`
|
||||
Data string `json:"data" dc:"设备号"` // 设备数据
|
||||
}
|
||||
|
||||
type LocationRes struct {
|
||||
Code int `json:"code"` // 响应代码
|
||||
Data bool `json:"data"` // 是否成功
|
||||
Msg string `json:"msg"` // 响应消息
|
||||
}
|
||||
|
||||
// 处理设备开关机时间的更新
|
||||
func (h Hat) Location(ctx context.Context, req *LocationReq) (res *LocationRes, err error) {
|
||||
res = new(LocationRes)
|
||||
data := req.Data
|
||||
timestamp := time.Now().Unix()
|
||||
|
||||
// 准备生成签名的参数
|
||||
params := map[string]string{
|
||||
"appid": appid,
|
||||
"data": data,
|
||||
"secret": secret,
|
||||
"timestamp": strconv.FormatInt(timestamp, 10),
|
||||
}
|
||||
|
||||
// 生成签名
|
||||
sign := GenerateSignature(params)
|
||||
|
||||
// 构造请求体
|
||||
payload := map[string]interface{}{
|
||||
"appid": appid,
|
||||
"data": data,
|
||||
"secret": secret,
|
||||
"timestamp": timestamp,
|
||||
"sign": sign,
|
||||
}
|
||||
payloadBytes, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// 发送POST请求
|
||||
resp, err := http.Post("https://www.loctp.com/api/crm/v1/refresh/location", "application/json", bytes.NewBuffer(payloadBytes))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 检查响应
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
fmt.Println(string(respBody))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(respBody, res)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
81
api/saft_hat/hat_rate.go
Normal file
81
api/saft_hat/hat_rate.go
Normal file
@ -0,0 +1,81 @@
|
||||
package saft_hat
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 定义设备上传速度频率请求【我们请求平台】
|
||||
type UploadRateReq struct {
|
||||
g.Meta `path:"/device/rate" method:"post" tags:"安全帽相关" summary:"更新设备数据发送频率"`
|
||||
Data string `json:"data" dc:"设备号"` // 设备数据
|
||||
RateTime int `json:"rateTime" dc:"上传频率单位是S"` // 上传频率
|
||||
}
|
||||
|
||||
type UploadRateRes struct {
|
||||
Code int `json:"code"` // 响应代码
|
||||
Data string `json:"data"` // 设备数据
|
||||
Msg string `json:"msg"` // 响应消息
|
||||
}
|
||||
|
||||
func (h Hat) UpdateRate(ctx context.Context, req *UploadRateReq) (res *UploadRateRes, err error) {
|
||||
res = new(UploadRateRes)
|
||||
// 获取请求参数
|
||||
rateTime := req.RateTime
|
||||
data := req.Data
|
||||
timestamp := time.Now().Unix()
|
||||
|
||||
// 准备生成签名的参数
|
||||
params := map[string]string{
|
||||
"appid": appid,
|
||||
"data": data,
|
||||
"rate_time": strconv.Itoa(rateTime),
|
||||
"secret": secret,
|
||||
"timestamp": strconv.FormatInt(timestamp, 10),
|
||||
}
|
||||
// 生成签名
|
||||
sign := GenerateSignature(params)
|
||||
|
||||
// 构造请求体
|
||||
payload := map[string]interface{}{
|
||||
"appid": appid,
|
||||
"data": data,
|
||||
"rate_time": rateTime,
|
||||
"timestamp": timestamp,
|
||||
"secret": secret,
|
||||
"sign": sign,
|
||||
}
|
||||
payloadBytes, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// 发送POST请求
|
||||
resp, err := http.Post("https://www.loctp.com/api/crm/v1/uploadRate", "application/json", bytes.NewBuffer(payloadBytes))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 检查响应
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
err = json.Unmarshal(respBody, &res)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// 把 respBody 转化为 UploadRateRes
|
||||
err = json.Unmarshal(respBody, &res)
|
||||
if err != nil {
|
||||
// 处理错误,例如打印或返回错误
|
||||
fmt.Println("Error unmarshalling res:", err)
|
||||
}
|
||||
return res, err
|
||||
}
|
62
api/saft_hat/hat_sos.go
Normal file
62
api/saft_hat/hat_sos.go
Normal file
@ -0,0 +1,62 @@
|
||||
package saft_hat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
)
|
||||
|
||||
// SOS报警数据结构体
|
||||
type SOS struct {
|
||||
Flag int `json:"flag"` // 两次数据上传标志:0:第二次 1:第一次
|
||||
UTCDateTime int64 `json:"utcDateTime"` // 设备内部上传时间戳
|
||||
Latitude float64 `json:"latitude"` // WGS-84 纬度坐标
|
||||
IMEI string `json:"IMEI"` // 设备IMEI编号
|
||||
Type string `json:"type"` // 设备上传类型和时间
|
||||
Longitude float64 `json:"longitude"` // WGS-84 经度坐标
|
||||
}
|
||||
|
||||
// SOS报警数据上传请求【其它平台发给我们】
|
||||
type SOSReq struct {
|
||||
g.Meta `path:"/device/alarm" method:"post" tags:"安全帽相关" summary:"接收SOS报警数据(不需要前端调用)"`
|
||||
}
|
||||
|
||||
type SOSRes struct {
|
||||
}
|
||||
|
||||
func (h Hat) SOS(ctx context.Context, req *SOSReq) (res *SOSRes, err error) {
|
||||
r := g.RequestFromCtx(ctx) // 从上下文中获取请求对象
|
||||
body, err := ioutil.ReadAll(r.Body) // 读取请求体
|
||||
if err != nil {
|
||||
log.Printf("读取请求头失败: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
defer r.Body.Close() // 延迟关闭请求体
|
||||
|
||||
// 从请求头中获取时间戳和签名
|
||||
timestamp := r.GetHeader("timestamp")
|
||||
signature := r.GetHeader("signature")
|
||||
|
||||
// 验证签名的有效性
|
||||
if !VerifySignature(string(body)+timestamp, signature, secret) {
|
||||
glog.Errorf(ctx, "签名验证失败")
|
||||
return nil, errors.New("验证签名失败")
|
||||
}
|
||||
|
||||
// 解析请求体中的 JSON 数据到预定义的结构体
|
||||
var sos SOS
|
||||
if err := gjson.DecodeTo(body, &sos); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 输出 SOS 的报警信息
|
||||
fmt.Println("报警信息:", sos)
|
||||
|
||||
// 返回响应对象
|
||||
return &SOSRes{}, nil
|
||||
}
|
133
api/saft_hat/hat_status.go
Normal file
133
api/saft_hat/hat_status.go
Normal file
@ -0,0 +1,133 @@
|
||||
package saft_hat
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 获取设备状态的请求结构
|
||||
type DeviceStatusReq struct {
|
||||
g.Meta `path:"/device/status" method:"post" tags:"安全帽相关" summary:"获取安全帽状态"`
|
||||
Data []string `json:"data" dc:"设备号"`
|
||||
}
|
||||
|
||||
// 获取设备状态
|
||||
func (h Hat) DeviceStatus(ctx context.Context, req *DeviceStatusReq) (res *DeviceStatusRes, err error) {
|
||||
return StatusCheck(ctx, req.Data)
|
||||
}
|
||||
|
||||
type DeviceStatusRes struct {
|
||||
Code int `json:"code"` // 响应代码
|
||||
Data []DeviceStatusInfo `json:"data"` // 设备数据
|
||||
Msg string `json:"msg"` // 响应消息
|
||||
}
|
||||
|
||||
// 定义数组中每个对象的结构
|
||||
type DeviceStatusInfo struct {
|
||||
IMEI string `json:"imei"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
// 检查设备的状态情况
|
||||
func StatusCheck(ctx context.Context, devNums []string) (res *DeviceStatusRes, err error) {
|
||||
res = new(DeviceStatusRes)
|
||||
data := strings.Join(devNums, ",")
|
||||
timestamp := time.Now().Unix()
|
||||
|
||||
// 准备生成签名的参数
|
||||
params := map[string]string{
|
||||
"appid": appid,
|
||||
"data": data,
|
||||
"secret": secret,
|
||||
"timestamp": strconv.FormatInt(timestamp, 10),
|
||||
}
|
||||
|
||||
// 生成签名
|
||||
sign := GenerateSignature(params)
|
||||
|
||||
// 构造请求体
|
||||
payload := map[string]interface{}{
|
||||
"appid": appid,
|
||||
"data": data,
|
||||
"secret": secret,
|
||||
"sign": sign,
|
||||
"timestamp": timestamp,
|
||||
}
|
||||
payloadBytes, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// 发送POST请求
|
||||
resp, err := http.Post("https://www.loctp.com/api/crm/v1/getStatus", "application/json", bytes.NewBuffer(payloadBytes))
|
||||
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 检查响应
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
fmt.Println(string(respBody))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(respBody, res)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 心跳检测
|
||||
func HeartCheck() {
|
||||
ticker := time.NewTicker(60 * time.Second)
|
||||
defer ticker.Stop() // 确保ticker被适当释放
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
var devices []Device
|
||||
err := g.Model("device").Fields("dev_num").Scan(&devices)
|
||||
if err != nil {
|
||||
fmt.Println("获取设备列表出错:", err)
|
||||
continue // 如果获取失败,则跳过本次循环
|
||||
}
|
||||
|
||||
if len(devices) == 0 {
|
||||
fmt.Println("没有找到任何设备")
|
||||
continue // 如果没有设备,则跳过本次循环
|
||||
}
|
||||
|
||||
// 构建devNums切片
|
||||
var devNums []string
|
||||
for _, device := range devices {
|
||||
devNums = append(devNums, device.DevNum)
|
||||
}
|
||||
res, err := StatusCheck(context.Background(), devNums)
|
||||
if err != nil {
|
||||
fmt.Println("检查设备状态出错:", err)
|
||||
continue // 如果检查失败,则跳过本次循环
|
||||
}
|
||||
|
||||
for _, deviceStatus := range res.Data {
|
||||
// 更新 device 表中的 status 字段
|
||||
_, err := g.Model("device").Data("status", deviceStatus.Status).Where("dev_num", deviceStatus.IMEI).Update()
|
||||
if err != nil {
|
||||
fmt.Printf("更新设备 %s 状态出错: %v\n", deviceStatus.IMEI, err)
|
||||
continue // 如果更新失败,则跳过本次设备的更新
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
78
api/saft_hat/hat_text_audio.go
Normal file
78
api/saft_hat/hat_text_audio.go
Normal file
@ -0,0 +1,78 @@
|
||||
package saft_hat
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TextToAudioReq 定义发送自定义语音的请求结构体
|
||||
type TextToAudioReq struct {
|
||||
g.Meta `path:"/device/text" method:"post" tags:"安全帽相关" summary:"发送自定义语言数据"`
|
||||
Data string `json:"data" dc:"设备号"`
|
||||
Text string `json:"text" dc:"发送语音的文字内容"`
|
||||
Time int `json:"time" dc:"播放次数 最少一次 最多三次"`
|
||||
}
|
||||
|
||||
type TextToAudioRes struct {
|
||||
Code int `json:"code"` // 响应代码
|
||||
Data string `json:"data"` // 设备数据
|
||||
Msg string `json:"msg"` // 响应消息
|
||||
}
|
||||
|
||||
// 发送自定义语音
|
||||
func (h Hat) SendTextToAudio(ctx context.Context, req *TextToAudioReq) (res *TextToAudioRes, err error) {
|
||||
res = new(TextToAudioRes)
|
||||
timestamp := time.Now().Unix()
|
||||
|
||||
// 准备生成签名的参数
|
||||
params := map[string]string{
|
||||
"appid": appid,
|
||||
"data": req.Data,
|
||||
"secret": secret,
|
||||
"text": req.Text,
|
||||
"time": strconv.Itoa(req.Time),
|
||||
"timestamp": strconv.FormatInt(timestamp, 10),
|
||||
}
|
||||
|
||||
// 生成签名
|
||||
sign := GenerateSignature(params)
|
||||
|
||||
// 构造请求体
|
||||
reqBody := map[string]interface{}{
|
||||
"data": req.Data,
|
||||
"appid": appid,
|
||||
"sign": sign,
|
||||
"secret": secret,
|
||||
"time": req.Time,
|
||||
"text": req.Text,
|
||||
"timestamp": strconv.FormatInt(timestamp, 10),
|
||||
}
|
||||
|
||||
payloadBytes, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// 发送POST请求
|
||||
resp, err := http.Post("https://www.loctp.com/api/crm/v1/textToAudio", "application/json", bytes.NewBuffer(payloadBytes))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 读取响应
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
fmt.Println("Response:", string(respBody))
|
||||
return res, nil
|
||||
}
|
114
api/saft_hat/hat_time.go
Normal file
114
api/saft_hat/hat_time.go
Normal file
@ -0,0 +1,114 @@
|
||||
package saft_hat
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 更新设备开关机时间
|
||||
type UpdateTimeReq struct {
|
||||
g.Meta `path:"/device/uploadTime" method:"post" tags:"安全帽相关" summary:"更新设备开关机时间"`
|
||||
Data string `json:"data" dc:"设备号"` // 设备数据
|
||||
OnTime string `json:"onTime" dc:"设备开机时间,例如 09:00"` // 设备开机时间
|
||||
OffTime string `json:"OffTime" dc:"设备关机时间,例如 23:00"` // 设备关机时间
|
||||
}
|
||||
|
||||
type UpdateTimeRes struct {
|
||||
Code int `json:"code"` // 响应代码
|
||||
Data string `json:"data"` // 设备数据
|
||||
Msg string `json:"msg"` // 响应消息
|
||||
}
|
||||
|
||||
// 处理设备开关机时间的更新
|
||||
func (h Hat) UpdateTime(ctx context.Context, req *UpdateTimeReq) (res *UpdateTimeRes, err error) {
|
||||
res = new(UpdateTimeRes)
|
||||
data := req.Data
|
||||
timestamp := time.Now().Unix()
|
||||
|
||||
// 准备生成签名的参数
|
||||
params := map[string]string{
|
||||
"appid": appid,
|
||||
"data": data,
|
||||
"off_time": req.OffTime,
|
||||
"on_time": req.OnTime,
|
||||
"secret": secret,
|
||||
"timestamp": strconv.FormatInt(timestamp, 10),
|
||||
}
|
||||
|
||||
// 生成签名
|
||||
sign := GenerateSignature(params)
|
||||
|
||||
// 构造请求体
|
||||
payload := map[string]interface{}{
|
||||
"appid": appid,
|
||||
"data": data,
|
||||
"off_time": req.OffTime,
|
||||
"on_time": req.OnTime,
|
||||
"secret": secret,
|
||||
"timestamp": timestamp,
|
||||
"sign": sign,
|
||||
}
|
||||
payloadBytes, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// 发送POST请求
|
||||
resp, err := http.Post("https://www.loctp.com/api/crm/v1/uploadTime", "application/json", bytes.NewBuffer(payloadBytes))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 检查响应
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// 解析包含多个设备号的字符串,设备号通过英文逗号分割
|
||||
devNums := strings.Split(req.Data, ",")
|
||||
|
||||
// 对每个设备号执行插入或更新操作
|
||||
for _, devNum := range devNums {
|
||||
// 首先去除可能的空格
|
||||
devNum = strings.TrimSpace(devNum)
|
||||
type StutusType struct {
|
||||
Status int
|
||||
}
|
||||
statusType := new(StutusType)
|
||||
// 先查看该设备的状态
|
||||
g.Model("device").Fields("status").Where("dev_num = ?", devNum).Scan(&statusType)
|
||||
// 如果设备在线(status=1)才允许更新它的开关机时间信息
|
||||
if statusType.Status == 1 {
|
||||
// 插入或更新电池开关机时间
|
||||
_, err := g.Model("device").Data(g.Map{
|
||||
"dev_num": devNum,
|
||||
"battery_on": req.OnTime,
|
||||
"battery_off": req.OffTime,
|
||||
}).Where("dev_num", devNum).Save()
|
||||
// 如果发生错误,立即返回
|
||||
if err != nil {
|
||||
fmt.Println("Error updating/inserting for device", devNum, ":", err)
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Device", devNum, "is not online; skipping update.")
|
||||
}
|
||||
}
|
||||
|
||||
err = json.Unmarshal(respBody, res)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
53
api/saft_hat/hat_tip.go
Normal file
53
api/saft_hat/hat_tip.go
Normal file
@ -0,0 +1,53 @@
|
||||
package saft_hat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
)
|
||||
|
||||
// 脱帽提示数据上传结构体
|
||||
type HatTip struct {
|
||||
UTCDateTime int64 `json:"utcDateTime"` // 设备内部上传时间戳
|
||||
IMEI string `json:"IMEI"` // 设备IMEI编号
|
||||
}
|
||||
|
||||
type HatTipReq struct {
|
||||
g.Meta `path:"/device/tip" method:"post" tags:"安全帽相关" summary:"脱帽提醒(不需要前端调用)"`
|
||||
}
|
||||
|
||||
type HatTipRes struct {
|
||||
}
|
||||
|
||||
func (h Hat) HatTip(ctx context.Context, req *HatTipReq) (res *HatTipRes, err error) {
|
||||
res = new(HatTipRes)
|
||||
r := g.RequestFromCtx(ctx)
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
log.Printf("Failed to read request body: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
timestamp := r.GetHeader("timestamp")
|
||||
signature := r.GetHeader("signature")
|
||||
|
||||
if !VerifySignature(string(body)+timestamp, signature, secret) {
|
||||
glog.Errorf(ctx, "Signature verification failed")
|
||||
return nil, errors.New("signature verification failed")
|
||||
}
|
||||
|
||||
var hatTip HatTip
|
||||
if err := gjson.DecodeTo(body, &hatTip); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Println("脱帽提示数据:", hatTip)
|
||||
|
||||
return res, nil
|
||||
}
|
81
api/saft_hat/hat_voice.go
Normal file
81
api/saft_hat/hat_voice.go
Normal file
@ -0,0 +1,81 @@
|
||||
package saft_hat
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 定义发送内置语音的请求结构
|
||||
type SendVoiceReq struct {
|
||||
g.Meta `path:"/device/voice" method:"post" tags:"安全帽相关" summary:"发送内置语音"`
|
||||
Data string `json:"data" dc:"设备号"`
|
||||
VoiceType int `json:"voice_type" dc:"内置语音种类,具体参照温度,1、2、3、4、5....."`
|
||||
}
|
||||
|
||||
type SendVoiceRes struct {
|
||||
Code int `json:"code"` // 响应代码
|
||||
Data string `json:"data"` // 设备数据
|
||||
Msg string `json:"msg"` // 响应消息
|
||||
}
|
||||
|
||||
// 处理设备开关机时间的更新
|
||||
func (h Hat) SendVoice(ctx context.Context, req *SendVoiceReq) (res *SendVoiceRes, err error) {
|
||||
res = new(SendVoiceRes)
|
||||
data := req.Data
|
||||
timestamp := time.Now().Unix()
|
||||
|
||||
// 准备生成签名的参数
|
||||
params := map[string]string{
|
||||
"appid": appid,
|
||||
"data": data,
|
||||
"secret": secret,
|
||||
"timestamp": strconv.FormatInt(timestamp, 10),
|
||||
"voice_type": strconv.Itoa(req.VoiceType),
|
||||
}
|
||||
|
||||
// 生成签名
|
||||
sign := GenerateSignature(params)
|
||||
|
||||
// 构造请求体
|
||||
payload := map[string]interface{}{
|
||||
"appid": appid,
|
||||
"data": data,
|
||||
"secret": secret,
|
||||
"sign": sign,
|
||||
"timestamp": timestamp,
|
||||
"voice_type": strconv.Itoa(req.VoiceType),
|
||||
}
|
||||
payloadBytes, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// 发送POST请求
|
||||
resp, err := http.Post("https://www.loctp.com/api/crm/v1/sendVoice", "application/json", bytes.NewBuffer(payloadBytes))
|
||||
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 检查响应
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
fmt.Println(string(respBody))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(respBody, res)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
14
api/saft_hat/router.go
Normal file
14
api/saft_hat/router.go
Normal file
@ -0,0 +1,14 @@
|
||||
package saft_hat
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
)
|
||||
|
||||
func InitHatAPI(group *ghttp.RouterGroup) {
|
||||
group.Middleware(ghttp.MiddlewareCORS)
|
||||
group.Group("/manage", func(group *ghttp.RouterGroup) {
|
||||
group.Group("/api/v1", func(group *ghttp.RouterGroup) {
|
||||
group.Bind(new(Hat))
|
||||
})
|
||||
})
|
||||
}
|
35
api/saft_hat/util.go
Normal file
35
api/saft_hat/util.go
Normal file
@ -0,0 +1,35 @@
|
||||
package saft_hat
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 使用参数名的ASCII升序排序后,将参数的值进行拼接并加密生成签名
|
||||
func GenerateSignature(params map[string]string) string {
|
||||
// 获取所有的键并按照ASCII码排序
|
||||
var keys []string
|
||||
for k := range params {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
// 按排序后的键,拼接它们的值
|
||||
var values []string
|
||||
for _, k := range keys {
|
||||
values = append(values, params[k])
|
||||
}
|
||||
signStr := strings.Join(values, "&")
|
||||
|
||||
// 将拼接后的字符串转换为小写
|
||||
signStr = strings.ToLower(signStr)
|
||||
|
||||
// 使用MD5加密
|
||||
hash := md5.New()
|
||||
hash.Write([]byte(signStr))
|
||||
md5String := hex.EncodeToString(hash.Sum(nil))
|
||||
|
||||
return md5String
|
||||
}
|
21
api/saft_hat/verify.go
Normal file
21
api/saft_hat/verify.go
Normal file
@ -0,0 +1,21 @@
|
||||
package saft_hat
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"log"
|
||||
)
|
||||
|
||||
// 函数用于验证请求的签名
|
||||
func VerifySignature(message, messageSignature, secret string) bool {
|
||||
mac := hmac.New(sha1.New, []byte(secret)) // 初始化HMAC-SHA1
|
||||
mac.Write([]byte(message)) // 写入消息体以计算摘要
|
||||
expectedMAC := mac.Sum(nil) // 计算消息的摘要
|
||||
signature, err := hex.DecodeString(messageSignature) // 将签名从十六进制字符串解码
|
||||
if err != nil {
|
||||
log.Printf("解码失败: %v", err)
|
||||
return false
|
||||
}
|
||||
return hmac.Equal(signature, expectedMAC) // 比较计算得到的摘要和传入的签名
|
||||
}
|
Reference in New Issue
Block a user