初始
This commit is contained in:
4
api/video_hat/alarm/alarm.go
Normal file
4
api/video_hat/alarm/alarm.go
Normal file
@ -0,0 +1,4 @@
|
||||
package alarm
|
||||
|
||||
type Alarm struct {
|
||||
}
|
17
api/video_hat/alarm/model.go
Normal file
17
api/video_hat/alarm/model.go
Normal file
@ -0,0 +1,17 @@
|
||||
package alarm
|
||||
|
||||
import "time"
|
||||
|
||||
type HatAlarm struct {
|
||||
ID int `json:"id" dc:"ID"`
|
||||
DevNum string `json:"devNum" dc:"视频安全帽设备号"`
|
||||
DevName string `json:"devName" dc:"设备名称"`
|
||||
ProjectID int `json:"projectId" dc:"项目ID"`
|
||||
BatteryLevel string `json:"batteryLevel" dc:"电池电量"`
|
||||
IsLowBattery bool `json:"isLowBattery" dc:"是否低电量"`
|
||||
RoomID string `json:"roomId" dc:"房间ID"`
|
||||
SipId string `json:"sipId" dc:"sipId"`
|
||||
IsHandle string `json:"isHandle" dc:"是否处理(1处理了、0未处理)"`
|
||||
CreatedAt time.Time `json:"createdAt" dc:"创建时间"`
|
||||
HandleAt time.Time `json:"handleAt" dc:"处理时间"`
|
||||
}
|
15
api/video_hat/alarm/req.go
Normal file
15
api/video_hat/alarm/req.go
Normal file
@ -0,0 +1,15 @@
|
||||
package alarm
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
type HatAlarmListReq struct {
|
||||
g.Meta `path:"/alarm/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 HandleAlarmReq struct {
|
||||
g.Meta `path:"/alarm/handle" method:"post" tags:"视频安全帽相关" summary:"处理SOS报警消息"`
|
||||
Id string `json:"id" dc:"报警ID"`
|
||||
}
|
9
api/video_hat/alarm/res.go
Normal file
9
api/video_hat/alarm/res.go
Normal file
@ -0,0 +1,9 @@
|
||||
package alarm
|
||||
|
||||
type HatAlarmListRes struct {
|
||||
HatAlarms []HatAlarm `json:"hatAlarms" dc:"报警列表"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
type HandleAlarmRes struct {
|
||||
}
|
38
api/video_hat/alarm/service.go
Normal file
38
api/video_hat/alarm/service.go
Normal file
@ -0,0 +1,38 @@
|
||||
package alarm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 报警列表
|
||||
func (h Alarm) HatAlarmList(ctx context.Context, req *HatAlarmListReq) (res *HatAlarmListRes, err error) {
|
||||
res = new(HatAlarmListRes)
|
||||
offset := (req.Page - 1) * req.PageSize
|
||||
var hatAlarms []HatAlarm
|
||||
model := g.Model("hat_alarm")
|
||||
count, err := model.Ctx(ctx).Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
model.Where("project_id = ?", req.ProjectId)
|
||||
err = model.Offset(int(offset)).Limit(int(req.PageSize)).Scan(&hatAlarms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.HatAlarms = hatAlarms
|
||||
res.Total = int64(count)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 处理报警
|
||||
func (h Alarm) HandleAlarm(ctx context.Context, req *HandleAlarmReq) (res *HandleAlarmRes, err error) {
|
||||
res = new(HandleAlarmRes)
|
||||
data := g.Map{
|
||||
"is_handle": 1,
|
||||
"handle_at": time.Now(),
|
||||
}
|
||||
g.Model("hat_alarm").Ctx(ctx).Data(data).Where("id", req.Id).Update()
|
||||
return res, nil
|
||||
}
|
4
api/video_hat/guiji_back5/guiji_back.go
Normal file
4
api/video_hat/guiji_back5/guiji_back.go
Normal file
@ -0,0 +1,4 @@
|
||||
package guiji_back5
|
||||
|
||||
type GuijiBack struct {
|
||||
}
|
117
api/video_hat/guiji_back5/guiji_back5.1.go
Normal file
117
api/video_hat/guiji_back5/guiji_back5.1.go
Normal file
@ -0,0 +1,117 @@
|
||||
package guiji_back5
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/tiger1103/gfast/v3/api/constant"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// 请求参数结构体【这里传时间】
|
||||
type GetDevicesOnlineTimeDaysReq struct {
|
||||
g.Meta `path:"/guijiDate" method:"post" tags:"视频安全帽相关" summary:"获取指定时间戳范围内帽子的在线时间"`
|
||||
UID string `json:"uid" dc:"平台的用户ID"`
|
||||
Stime int64 `json:"stime" dc:"Unix开始时间戳"`
|
||||
Etime int64 `json:"etime" dc:"Unix结束时间戳"`
|
||||
}
|
||||
|
||||
type DevicesOnlineTimeDaysReq struct {
|
||||
AdminID string `json:"admin_id"`
|
||||
UserID string `json:"user_id"`
|
||||
Stime int64 `json:"stime"`
|
||||
Etime int64 `json:"etime"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type DataListItem struct {
|
||||
Ctime string `json:"ctime"`
|
||||
Ltime int `json:"ltime"`
|
||||
Sec int `json:"sec"`
|
||||
}
|
||||
|
||||
// 完整响应结构体
|
||||
type GetDevicesOnlineTimeDaysRes struct {
|
||||
Status bool `json:"status"`
|
||||
Msg string `json:"msg"`
|
||||
Data DataResponse `json:"data"`
|
||||
MsgCode string `json:"msg_code"`
|
||||
}
|
||||
|
||||
// 响应中的数据部分结构体
|
||||
type DataResponse struct {
|
||||
List []DataListItem `json:"list"`
|
||||
Total int `json:"total"`
|
||||
TotalText string `json:"total_text"`
|
||||
}
|
||||
|
||||
// GetDevicesOnlineTimeDays是用于获取设备在线时间的方法
|
||||
func (gui GuijiBack) GetDevicesOnlineTimeDays(ctx context.Context, req *GetDevicesOnlineTimeDaysReq) (res *GetDevicesOnlineTimeDaysRes, err error) {
|
||||
res = new(GetDevicesOnlineTimeDaysRes)
|
||||
dataReq := DevicesOnlineTimeDaysReq{
|
||||
AdminID: constant.AdminId,
|
||||
UserID: req.UID,
|
||||
Stime: req.Stime,
|
||||
Etime: req.Etime,
|
||||
Token: constant.Token,
|
||||
}
|
||||
|
||||
reqBody, err := json.Marshal(dataReq)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// 发送POST请求
|
||||
resp, err := http.Post("https://caps.runde.pro/api/index.php?ctl=device&act=get_devices_online_time_days", "application/json", bytes.NewBuffer(reqBody))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 读取响应体
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// 反序列化响应体到结构体
|
||||
err = json.Unmarshal(respBody, &res)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 自定义UnmarshalJSON方法来处理sec字段的不同数据类型【小杨源码改写】
|
||||
func (d *DataListItem) UnmarshalJSON(data []byte) error {
|
||||
// 创建一个只在此方法内使用的类型,以避免无限递归调用UnmarshalJSON
|
||||
type Alias DataListItem
|
||||
aux := &struct {
|
||||
Sec json.RawMessage `json:"sec"`
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(d),
|
||||
}
|
||||
if err := json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
// 尝试将sec解析为int
|
||||
if err := json.Unmarshal(aux.Sec, &d.Sec); err != nil {
|
||||
// 如果解析为int失败,尝试解析为string,然后转换为int
|
||||
var secStr string
|
||||
if err := json.Unmarshal(aux.Sec, &secStr); err != nil {
|
||||
return err // 不能解析sec为int或string
|
||||
}
|
||||
secInt, err := strconv.Atoi(secStr)
|
||||
if err != nil {
|
||||
return err // 不能将sec的字符串值转换为int
|
||||
}
|
||||
d.Sec = secInt
|
||||
}
|
||||
return nil
|
||||
}
|
77
api/video_hat/guiji_back5/guiji_back5.2.go
Normal file
77
api/video_hat/guiji_back5/guiji_back5.2.go
Normal file
@ -0,0 +1,77 @@
|
||||
package guiji_back5
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/tiger1103/gfast/v3/api/constant"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type GetUserPathWebReq struct {
|
||||
g.Meta `path:"/guijiData" method:"post" tags:"视频安全帽相关" summary:"获取指定时间段内轨迹记录"`
|
||||
UID string `json:"uid" dc:"平台的用户ID"`
|
||||
Start int64 `json:"start" dc:"Unix开始时间戳"`
|
||||
End int64 `json:"end" dc:"Unix结束时间戳"`
|
||||
}
|
||||
|
||||
type UserPathWebRequest struct {
|
||||
AdminID string `json:"admin_id"`
|
||||
UserID string `json:"user_id"`
|
||||
Start int64 `json:"start"`
|
||||
End int64 `json:"end"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type GetUserPathWebRes struct {
|
||||
Status bool `json:"status"`
|
||||
Msg string `json:"msg"`
|
||||
Data []PathDataItem `json:"data"`
|
||||
MsgCode string `json:"msg_code"`
|
||||
}
|
||||
|
||||
type PathDataItem struct {
|
||||
XPoint string `json:"x_point"`
|
||||
YPoint string `json:"y_point"`
|
||||
Time string `json:"time"`
|
||||
CTime string `json:"c_time"`
|
||||
CAngle string `json:"c_angle"`
|
||||
CSpeed string `json:"c_speed"`
|
||||
CTrust string `json:"c_trust"`
|
||||
GPS string `json:"gps"`
|
||||
}
|
||||
|
||||
func (gj GuijiBack) GetUserPathWeb(ctx context.Context, req *GetUserPathWebReq) (res *GetUserPathWebRes, err error) {
|
||||
res = new(GetUserPathWebRes)
|
||||
dataReq := UserPathWebRequest{
|
||||
AdminID: constant.AdminId,
|
||||
UserID: req.UID,
|
||||
Start: req.Start,
|
||||
End: req.End,
|
||||
Token: constant.Token,
|
||||
}
|
||||
|
||||
reqBody, err := json.Marshal(dataReq)
|
||||
|
||||
// 替换为实际的API URL
|
||||
url := "https://caps.runde.pro/api/index.php?ctl=location&act=get_user_path_web"
|
||||
resp, err := http.Post(url, "application/json", bytes.NewBuffer(reqBody))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(respBody, &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
207
api/video_hat/index.go
Normal file
207
api/video_hat/index.go
Normal file
@ -0,0 +1,207 @@
|
||||
package video_hat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"time"
|
||||
)
|
||||
|
||||
type VideoDeviceHat struct {
|
||||
DevNum string `json:"dev_num"` // 设备编号
|
||||
DevName string `json:"dev_name"` // 设备名
|
||||
Status int `json:"status"` // 安全帽状态(0:离线,1:在线,2:监控中,3:通话中,4:隐私模式)
|
||||
ProjectID int64 `json:"project_id"` // 项目id
|
||||
UserID string `json:"user_id"` // 用户id
|
||||
BatteryLevel string `json:"battery_level"` // 电量
|
||||
IsLowBattery bool `json:"is_low_battery"` // 是否处于低电量,true代表低电量,false则不是低电量
|
||||
CreateTime time.Time `json:"create_time"` // 创建时间
|
||||
UpdatedAt time.Time `json:"updated_at"` // 更新时间
|
||||
Longitude string `json:"longitude"` // 最新的经度
|
||||
Latitude string `json:"latitude"` // 最新的维度
|
||||
SipId int `json:"sip_id"` // SIPID
|
||||
Uid int `json:"uid"` // 安全帽厂商返回的用户ID
|
||||
PushStatus string `json:"push_status"` // 推流状态
|
||||
Nickname string `json:"nick_name"` // 用户名称
|
||||
UserName string `json:"user_name"` // 真实名称
|
||||
HeadIcon string `json:"head_icon"` // 头像
|
||||
Phone string `json:"phone"` // 电话
|
||||
}
|
||||
|
||||
// 视频安全帽设备列表
|
||||
type DeviceListReq struct {
|
||||
g.Meta `path:"/video/device/list" method:"post" tags:"视频安全帽相关" summary:"视频安全帽设备列表"`
|
||||
ProjectId int64 `json:"projectId" dc:"项目ID"`
|
||||
DevNum string `json:"devNum" dc:"设备编号模糊查询"`
|
||||
DevName string `json:"devName" dc:"设备名称模糊查询"`
|
||||
Status *int `json:"status" dc:"安全帽状态等值查询"` // 使用指针允许状态为nil,表示不过滤状态
|
||||
Page int64 `json:"page" dc:"请求的页码" v:"required"`
|
||||
PageSize int64 `json:"pageSize" dc:"每页显示的条目数" v:"required"`
|
||||
}
|
||||
|
||||
// 视频安全帽设备列表响应结构体
|
||||
type DeviceListRes struct {
|
||||
Total int64 `json:"total"` // 总条目数
|
||||
VideoDeviceHats []VideoDeviceHat `json:"videoDeviceHats"`
|
||||
}
|
||||
|
||||
func (v VideoHat) DeviceList(ctx context.Context, req *DeviceListReq) (res *DeviceListRes, err error) {
|
||||
res = new(DeviceListRes)
|
||||
offset := (req.Page - 1) * req.PageSize
|
||||
var devices []VideoDeviceHat
|
||||
|
||||
// 准备查询模型,设置连表查询
|
||||
model := g.Model("device_video_hat AS dvh").LeftJoin("bus_construction_user AS user", "user.id=dvh.user_id")
|
||||
|
||||
// 根据项目ID过滤
|
||||
if req.ProjectId != 0 {
|
||||
model = model.Where("dvh.project_id = ?", req.ProjectId)
|
||||
}
|
||||
|
||||
// 设备编号模糊查询
|
||||
if req.DevNum != "" {
|
||||
model = model.Where("dvh.dev_num LIKE ?", "%"+req.DevNum+"%")
|
||||
}
|
||||
|
||||
// 设备名称模糊查询
|
||||
if req.DevName != "" {
|
||||
model = model.Where("dvh.dev_name LIKE ?", "%"+req.DevName+"%")
|
||||
}
|
||||
|
||||
// 安全帽状态等值查询
|
||||
if req.Status != nil {
|
||||
model = model.Where("dvh.status = ?", *req.Status)
|
||||
}
|
||||
|
||||
// 应用查询条件但不选取字段,用于计数
|
||||
count, err := model.Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 选取具体字段,包括连表查询字段
|
||||
model = model.Fields("dvh.*, user.nick_name,user.user_name,user.head_icon,user.phone")
|
||||
|
||||
// 获取当前页的设备列表
|
||||
err = model.Offset(int(offset)).Limit(int(req.PageSize)).Scan(&devices)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res.Total = int64(count)
|
||||
res.VideoDeviceHats = devices
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 为设备绑定项目ID
|
||||
type BindProjectIdReq struct {
|
||||
g.Meta `path:"/video/project/bind" method:"post" tags:"视频安全帽相关" summary:"为设备绑定项目"`
|
||||
ProjectId int64 `json:"projectId" dc:"项目ID"`
|
||||
DevNum string `json:"devNum" dc:"设备编号"`
|
||||
}
|
||||
|
||||
type BindProjectIdRes struct {
|
||||
}
|
||||
|
||||
func (v VideoHat) BindProjectId(ctx context.Context, req *BindProjectIdReq) (res *BindProjectIdRes, err error) {
|
||||
res = new(BindProjectIdRes)
|
||||
param := g.Map{
|
||||
"project_id": req.ProjectId,
|
||||
}
|
||||
g.Model("device_video_hat").Data(param).Where("dev_num = ?", req.DevNum).Update()
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 为设备解绑项目ID
|
||||
type UnBindProjectIdReq struct {
|
||||
g.Meta `path:"/video/project/unbind" method:"post" tags:"视频安全帽相关" summary:"为设备解绑项目"`
|
||||
DevNum string `json:"devNum" dc:"设备编号"`
|
||||
}
|
||||
|
||||
// 视频安全帽设备列表响应结构体
|
||||
type UnBindProjectIdRes struct {
|
||||
}
|
||||
|
||||
func (v VideoHat) UnBindProjectId(ctx context.Context, req *UnBindProjectIdReq) (res *UnBindProjectIdRes, err error) {
|
||||
res = new(UnBindProjectIdRes)
|
||||
param := g.Map{
|
||||
"project_id": nil,
|
||||
}
|
||||
g.Model("device_video_hat").Data(param).Where("dev_num = ?", req.DevNum).Update()
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 为设备绑定用户ID
|
||||
type BindUserIdReq struct {
|
||||
g.Meta `path:"/video/user/bind" method:"post" tags:"视频安全帽相关" summary:"为设备绑定用户"`
|
||||
UserId int64 `json:"userId" dc:"用户ID"`
|
||||
DevNum string `json:"devNum" dc:"设备编号"`
|
||||
}
|
||||
|
||||
type BindUserIdRes struct {
|
||||
}
|
||||
|
||||
func (v VideoHat) BindUserId(ctx context.Context, req *BindUserIdReq) (res *BindUserIdRes, err error) {
|
||||
param := g.Map{
|
||||
"user_id": req.UserId,
|
||||
}
|
||||
g.Model("device_video_hat").Data(param).Where("dev_num = ?", req.DevNum).Update()
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 为设备绑定用户ID
|
||||
type UnBindUserIdReq struct {
|
||||
g.Meta `path:"/video/user/unbind" method:"post" tags:"视频安全帽相关" summary:"为设备解绑用户"`
|
||||
DevNum string `json:"devNum" dc:"设备编号"`
|
||||
}
|
||||
|
||||
type UnBindUserIdRes struct {
|
||||
}
|
||||
|
||||
func (v VideoHat) UnBindUserId(ctx context.Context, req *UnBindUserIdReq) (res *UnBindUserIdRes, err error) {
|
||||
param := g.Map{
|
||||
"user_id": nil,
|
||||
}
|
||||
g.Model("device_video_hat").Data(param).Where("dev_num = ?", req.DevNum).Update()
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 查询某个安全帽有轨迹的时间
|
||||
type DeviceListDateTimeReq struct {
|
||||
g.Meta `path:"/video/device/dateList" method:"post" tags:"视频安全帽相关" summary:"视频安全帽轨迹时间列表"`
|
||||
DevNum string `json:"devNum" dc:"设备编号"`
|
||||
StartTime string `json:"startTime" dc:"开始时间,格式为 YYYY-MM"`
|
||||
}
|
||||
|
||||
type DeviceDataTime struct {
|
||||
DataTime string `json:"dataTime"`
|
||||
}
|
||||
|
||||
// 视频安全帽设备列表响应结构体
|
||||
type DeviceListDateTimeRes struct {
|
||||
DateTimeList []DeviceDataTime `json:"dateTimeList" dc:"设备含有轨迹的数据列表"`
|
||||
}
|
||||
|
||||
func (v VideoHat) DeviceListDateTime(ctx context.Context, req *DeviceListDateTimeReq) (res *DeviceListDateTimeRes, err error) {
|
||||
res = new(DeviceListDateTimeRes)
|
||||
|
||||
// 解析开始时间
|
||||
startTime, err := time.Parse("2006-01", req.StartTime)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid start time format: %v", err)
|
||||
}
|
||||
|
||||
// 计算该月的第一天和最后一天
|
||||
firstDayOfMonth := startTime
|
||||
lastDayOfMonth := firstDayOfMonth.AddDate(0, 1, -1) // 增加一个月,减去一天
|
||||
|
||||
// 查询设备的数据时间在指定月份内
|
||||
g.Model("device_data_time").
|
||||
Fields("data_time AS dataTime").
|
||||
Where("dev_num = ?", req.DevNum).
|
||||
Where("data_time >= ?", firstDayOfMonth.Format("2006-01-02")).
|
||||
Where("data_time <= ?", lastDayOfMonth.Format("2006-01-02")).
|
||||
Scan(&res.DateTimeList)
|
||||
|
||||
return res, nil
|
||||
}
|
24
api/video_hat/location.go
Normal file
24
api/video_hat/location.go
Normal file
@ -0,0 +1,24 @@
|
||||
package video_hat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// 查询某个设备的当前定位
|
||||
type GetNowLocationReq struct {
|
||||
g.Meta `path:"/video/location" method:"post" tags:"视频安全帽相关" summary:"获取某个设备的当前定位"`
|
||||
DevNum string `json:"devNum" dc:"设备号"`
|
||||
}
|
||||
|
||||
type GetNowLocationRes struct {
|
||||
DevNum string `json:"devNum"`
|
||||
Longitude string `json:"longitude"`
|
||||
Latitude string `json:"latitude"`
|
||||
}
|
||||
|
||||
func (v VideoHat) GetAllVideodata(ctx context.Context, req *GetNowLocationReq) (res *GetNowLocationRes, err error) {
|
||||
res = new(GetNowLocationRes)
|
||||
g.Model("device_video_hat").Fields("dev_num AS devNum,longitude,latitude").Where("dev_num", req.DevNum).Scan(&res)
|
||||
return res, nil
|
||||
}
|
57
api/video_hat/photomange10/get_pictures.go
Normal file
57
api/video_hat/photomange10/get_pictures.go
Normal file
@ -0,0 +1,57 @@
|
||||
package photomange10
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/tiger1103/gfast/v3/api/constant"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// 获取照片
|
||||
type GetPicturesReq struct {
|
||||
g.Meta `path:"/getpictures" method:"post" tags:"视频安全帽相关" summary:"获取照片"`
|
||||
DevNum string `json:"devNum" dc:"设备号"`
|
||||
Date string `json:"date" dc:"格式 yyyy-mm-dd(不是必填参数)"`
|
||||
PageNum string `json:"pageNum" dc:"当前页数"`
|
||||
}
|
||||
|
||||
// 获取照片相应
|
||||
type GetPicturesRes struct {
|
||||
ResponseData ResponseData `json:"responseData"`
|
||||
}
|
||||
|
||||
func (v Pictures) GetPictures(ctx context.Context, req *GetPicturesReq) (res *GetPicturesRes, err error) {
|
||||
var userId string
|
||||
id, _ := g.Model("device_video_hat").Fields("uid AS userId").Where("dev_num", req.DevNum).Value()
|
||||
userId = gconv.String(id)
|
||||
res = new(GetPicturesRes)
|
||||
data := url.Values{}
|
||||
data.Set("user_id", userId)
|
||||
if req.Date != "" {
|
||||
data.Set("date", req.Date)
|
||||
}
|
||||
data.Set("p", req.PageNum)
|
||||
data.Set("token", constant.Token)
|
||||
|
||||
resp, err := http.PostForm("https://caps.runde.pro/api/index.php?ctl=report&act=get_user_image", data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(respBody, &res.ResponseData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
4
api/video_hat/photomange10/pictures.go
Normal file
4
api/video_hat/photomange10/pictures.go
Normal file
@ -0,0 +1,4 @@
|
||||
package photomange10
|
||||
|
||||
type Pictures struct {
|
||||
}
|
23
api/video_hat/photomange10/pictures_model.go
Normal file
23
api/video_hat/photomange10/pictures_model.go
Normal file
@ -0,0 +1,23 @@
|
||||
package photomange10
|
||||
|
||||
// 获取照片
|
||||
type ResponseData struct {
|
||||
Status bool `json:"status"`
|
||||
Message string `json:"msg"`
|
||||
Data []ImageInfo `json:"data"`
|
||||
MsgCode string `json:"msg_code"`
|
||||
}
|
||||
|
||||
type ImageInfo struct {
|
||||
ID string `json:"i_id"`
|
||||
UserID string `json:"user_id" dc:""`
|
||||
ImageURL string `json:"image_url"`
|
||||
XCoordinate string `json:"x_point"` // 将经纬度转换为浮点数类型
|
||||
YCoordinate string `json:"y_point"`
|
||||
Timestamp string `json:"time" ` // 需要使用time.Unix()反序列化时间戳
|
||||
Extra string `json:"extra"`
|
||||
TaskID string `json:"task_id"`
|
||||
Content string `json:"content"`
|
||||
FaceName string `json:"face_name"`
|
||||
Temperature string `json:"temperature"` // 温度可能是字符串类型,如果需要转为数值类型请相应调整
|
||||
}
|
25
api/video_hat/router.go
Normal file
25
api/video_hat/router.go
Normal file
@ -0,0 +1,25 @@
|
||||
package video_hat
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/tiger1103/gfast/v3/api/video_hat/alarm"
|
||||
"github.com/tiger1103/gfast/v3/api/video_hat/guiji_back5"
|
||||
"github.com/tiger1103/gfast/v3/api/video_hat/photomange10"
|
||||
"github.com/tiger1103/gfast/v3/api/video_hat/video_playback11"
|
||||
"github.com/tiger1103/gfast/v3/api/video_hat/ws2"
|
||||
)
|
||||
|
||||
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(VideoHat), // 视频安全帽获取Token
|
||||
new(photomange10.Pictures), // 照片管理10
|
||||
new(video_playback11.VideoPlayback), // 视频回放11
|
||||
new(ws2.WsRouter), // ws2相关
|
||||
new(guiji_back5.GuijiBack), // 轨迹相关
|
||||
new(alarm.Alarm))
|
||||
})
|
||||
})
|
||||
}
|
4
api/video_hat/video.go
Normal file
4
api/video_hat/video.go
Normal file
@ -0,0 +1,4 @@
|
||||
package video_hat
|
||||
|
||||
type VideoHat struct {
|
||||
}
|
49
api/video_hat/video_playback11/all_video_data11.1.go
Normal file
49
api/video_hat/video_playback11/all_video_data11.1.go
Normal file
@ -0,0 +1,49 @@
|
||||
package video_playback11
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/tiger1103/gfast/v3/api/constant"
|
||||
)
|
||||
|
||||
// 回放路由
|
||||
type VideoPlayback struct{}
|
||||
|
||||
// 管理员下所有视频
|
||||
type GetAllVideodataReq struct {
|
||||
g.Meta `path:"/video/data" method:"post" tags:"视频安全帽相关" summary:"获取管理员账号下所有设备的视频记录日期"`
|
||||
}
|
||||
|
||||
type GetAllVideodataRes struct {
|
||||
List UserData `json:"list"`
|
||||
}
|
||||
|
||||
func (v VideoPlayback) GetAllVideodata(ctx context.Context, req *GetAllVideodataReq) (res *GetAllVideodataRes, err error) {
|
||||
res = new(GetAllVideodataRes)
|
||||
data := url.Values{}
|
||||
data.Set("admin_id", constant.AdminId)
|
||||
data.Set("token", constant.Token)
|
||||
|
||||
resp, err := http.PostForm("https://caps.runde.pro/api/index.php?ctl=video&act=get_path_list", data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(respBody, &res.List)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
76
api/video_hat/video_playback11/data_videoplayback11.2.go
Normal file
76
api/video_hat/video_playback11/data_videoplayback11.2.go
Normal file
@ -0,0 +1,76 @@
|
||||
package video_playback11
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/tiger1103/gfast/v3/api/constant"
|
||||
"golang.org/x/net/context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// 管理员下所有视频
|
||||
type GetDataVideoPlaybackReq struct {
|
||||
g.Meta `path:"/video/playback" method:"post" tags:"视频安全帽相关" summary:"根据日期获取设备视频列表"`
|
||||
Year string `json:"year" dc:"年"`
|
||||
Month string `json:"month" dc:"月"`
|
||||
Day string `json:"day" dc:"日"`
|
||||
UID string `json:"uid" dc:"平台的用户ID"`
|
||||
}
|
||||
|
||||
type GetDataVideoPlaybackRes struct {
|
||||
List DataVideoPlayRes `json:"list"`
|
||||
}
|
||||
|
||||
type AnyString string
|
||||
|
||||
func (a *AnyString) UnmarshalJSON(data []byte) error {
|
||||
if string(data) == "null" {
|
||||
return nil
|
||||
}
|
||||
// 尝试解析为字符串
|
||||
var str string
|
||||
if err := json.Unmarshal(data, &str); err == nil {
|
||||
*a = AnyString(str)
|
||||
return nil
|
||||
}
|
||||
// 尝试解析为数字并转换为字符串
|
||||
var num int
|
||||
if err := json.Unmarshal(data, &num); err == nil {
|
||||
*a = AnyString(fmt.Sprintf("%d", num))
|
||||
return nil
|
||||
}
|
||||
return errors.New("msg_code is neither a string nor a number")
|
||||
}
|
||||
|
||||
func (v VideoPlayback) GetDataVideoPlayback(ctx context.Context, req *GetDataVideoPlaybackReq) (res *GetDataVideoPlaybackRes, err error) {
|
||||
res = new(GetDataVideoPlaybackRes)
|
||||
data := url.Values{}
|
||||
data.Set("token", constant.Token)
|
||||
data.Set("admin_id", constant.AdminId)
|
||||
data.Set("uid", req.UID)
|
||||
data.Set("year", req.Year)
|
||||
data.Set("month", req.Month)
|
||||
data.Set("day", req.Day)
|
||||
|
||||
resp, err := http.PostForm("https://caps.runde.pro/api/index.php?ctl=video&act=get_video_list_v1", data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(respBody, &res.List)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
24
api/video_hat/video_playback11/videopalyback_model.go
Normal file
24
api/video_hat/video_playback11/videopalyback_model.go
Normal file
@ -0,0 +1,24 @@
|
||||
package video_playback11
|
||||
|
||||
// 11.1 获取管理员账号下所有设备包含视频记录日期
|
||||
type UserData map[string]UserYears
|
||||
type UserYears map[string]UserMonths
|
||||
type UserMonths map[string][]string
|
||||
|
||||
// 11.2 根据日期获取设备视频列表 定义外层结构体
|
||||
type DataVideoPlayRes struct {
|
||||
Status bool `json:"status"`
|
||||
Msg string `json:"msg"`
|
||||
Data []Video `json:"data"`
|
||||
MsgCode interface{} `json:"msg_code"`
|
||||
}
|
||||
|
||||
// 定义Video结构体,表示data字段中的单个元素
|
||||
type Video struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
URL string `json:"url"`
|
||||
CTime string `json:"ctime"`
|
||||
Content *string `json:"content"` // 使用指针处理可能为null的字段
|
||||
PlayTime string `json:"play_time"`
|
||||
}
|
84
api/video_hat/video_token.go
Normal file
84
api/video_hat/video_token.go
Normal file
@ -0,0 +1,84 @@
|
||||
package video_hat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/tiger1103/gfast/v3/api/constant"
|
||||
)
|
||||
|
||||
type RefreshTokenReq struct {
|
||||
g.Meta `path:"/video/refreshToken" method:"post" tags:"视频安全帽相关" summary:"刷新Token(不需要前端调用)"`
|
||||
}
|
||||
|
||||
type RefreshTokenRes struct{}
|
||||
|
||||
func (v VideoHat) RefreshToken(ctx context.Context, req *RefreshTokenReq) (res *RefreshTokenRes, err error) {
|
||||
res = new(RefreshTokenRes)
|
||||
InitToken()
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 用于接收获取 Token 的响应
|
||||
type TokenRes struct {
|
||||
Status bool `json:"status"`
|
||||
Token string `json:"token"`
|
||||
SessionId string `json:"session_id"`
|
||||
MsgCode string `json:"msg_code"`
|
||||
}
|
||||
|
||||
type PKeyResponse struct {
|
||||
Status bool `json:"status"`
|
||||
Data string `json:"data"`
|
||||
Msg string `json:"msg"`
|
||||
MsgCode string `json:"msg_code"`
|
||||
}
|
||||
|
||||
type TokenResponse struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
// 发送 POST 请求
|
||||
func fetchPostForm(url string, data url.Values) ([]byte, error) {
|
||||
resp, err := http.PostForm(url, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return io.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
// InitToken 项目初始化启动获取 Token【以及刷新 Token】
|
||||
func InitToken() error {
|
||||
data := url.Values{}
|
||||
data.Set("user_name", constant.Username)
|
||||
data.Set("pwd", constant.Password)
|
||||
|
||||
respBody, err := fetchPostForm("https://caps.runde.pro/api/index.php?ctl=tool&act=get_pkey", data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var pkeyRes PKeyResponse
|
||||
if err := json.Unmarshal(respBody, &pkeyRes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data.Set("pkey", pkeyRes.Data)
|
||||
respBody, err = fetchPostForm("https://caps.runde.pro/api/index.php?ctl=tool&act=get_token", data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var tokenRes TokenResponse
|
||||
if err := json.Unmarshal(respBody, &tokenRes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
constant.Token = tokenRes.Token
|
||||
return nil
|
||||
}
|
4
api/video_hat/ws2/ws.go
Normal file
4
api/video_hat/ws2/ws.go
Normal file
@ -0,0 +1,4 @@
|
||||
package ws2
|
||||
|
||||
type WsRouter struct {
|
||||
}
|
112
api/video_hat/ws2/ws2.1.go
Normal file
112
api/video_hat/ws2/ws2.1.go
Normal file
@ -0,0 +1,112 @@
|
||||
package ws2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/tiger1103/gfast/v3/api/constant"
|
||||
"golang.org/x/net/context"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 根据 Token 去登录 WS 请求
|
||||
func Login(conn *websocket.Conn) {
|
||||
loginMessage := map[string]string{
|
||||
"act": "ma_login",
|
||||
"user_name": constant.Username,
|
||||
"access_token": constant.Token,
|
||||
}
|
||||
conn.WriteJSON(loginMessage)
|
||||
// 休眠两秒钟,以便第三方服务端记录连接的信息
|
||||
time.Sleep(time.Second * 3)
|
||||
}
|
||||
|
||||
// 定义管理员登录响应结构体
|
||||
type AdminLoginResponse struct {
|
||||
Cmd string `json:"cmd"`
|
||||
Status bool `json:"status"`
|
||||
Msg string `json:"msg"`
|
||||
AdminInfo AdminInfo `json:"admin_info"`
|
||||
}
|
||||
|
||||
// 定义管理员信息结构体
|
||||
type AdminInfo struct {
|
||||
AdminID string `json:"admin_id"`
|
||||
UserName string `json:"user_name"`
|
||||
Mobile string `json:"mobile"`
|
||||
CTime string `json:"c_time"`
|
||||
Pwd string `json:"pwd"`
|
||||
PID string `json:"p_id"`
|
||||
Department string `json:"department"`
|
||||
Role string `json:"role"`
|
||||
SIPInfo SIPInfo `json:"sip_info"`
|
||||
}
|
||||
|
||||
// 定义 SIP 信息结构体
|
||||
type SIPInfo struct {
|
||||
SIPID string `json:"sip_id" dc:"SIPID"`
|
||||
SIPPwd string `json:"sip_pwd" dc:"SIP密码"`
|
||||
SIPHost string `json:"sip_host" dc:"SIP服务器URL"`
|
||||
WSSURL string `json:"wss_url" dc:"WSS地址"`
|
||||
STUNHost string `json:"stun_host" dc:"stun服务器"`
|
||||
TurnHost string `json:"turn_host" dc:"turnserver地址"`
|
||||
TurnUser string `json:"turn_user" dc:"turnserver账号"`
|
||||
TurnPwd string `json:"turn_pwd" dc:"turnserver密码"`
|
||||
}
|
||||
|
||||
// 处理登录
|
||||
func HandleLogin(jsonString string) {
|
||||
var response AdminLoginResponse
|
||||
err := json.Unmarshal([]byte(jsonString), &response)
|
||||
if err != nil {
|
||||
log.Fatal("Error decoding JSON to struct:", err)
|
||||
}
|
||||
sipInfo := response.AdminInfo.SIPInfo
|
||||
// 查询是否存在该数据
|
||||
IfExists, _ := g.Model("sip_info").Where("id = ?", 1).Count()
|
||||
// 如果不存在,则插入数据
|
||||
if IfExists == 0 {
|
||||
sipinfo := g.Map{
|
||||
"sip_id": sipInfo.SIPID,
|
||||
"sip_pwd": sipInfo.SIPPwd,
|
||||
"sip_host": sipInfo.SIPHost,
|
||||
"wss_url": sipInfo.WSSURL,
|
||||
"stun_host": sipInfo.STUNHost,
|
||||
"turn_host": sipInfo.TurnHost,
|
||||
"turn_user": sipInfo.TurnUser,
|
||||
"turn_pwd": sipInfo.TurnPwd,
|
||||
}
|
||||
g.Model("sip_info").Data(sipinfo).Insert()
|
||||
} else {
|
||||
// 存在则更新数据
|
||||
sipinfo := g.Map{
|
||||
"sip_id": sipInfo.SIPID,
|
||||
"sip_pwd": sipInfo.SIPPwd,
|
||||
"sip_host": sipInfo.SIPHost,
|
||||
"wss_url": sipInfo.WSSURL,
|
||||
"stun_host": sipInfo.STUNHost,
|
||||
"turn_host": sipInfo.TurnHost,
|
||||
"turn_user": sipInfo.TurnUser,
|
||||
"turn_pwd": sipInfo.TurnPwd,
|
||||
}
|
||||
g.Model("sip_info").Data(sipinfo).Where("id =?", 1).Update()
|
||||
}
|
||||
}
|
||||
|
||||
// 管理员详情sip
|
||||
type GetAdminLoginInfoReq struct {
|
||||
g.Meta `path:"/device/admininfo" method:"get" tags:"视频安全帽相关" summary:"管理员详情sip"`
|
||||
}
|
||||
|
||||
type GetAdminLoginInfoRes struct {
|
||||
List []SIPInfo `json:"list"`
|
||||
}
|
||||
|
||||
func (w WsRouter) GetAdminLoginInfo(ctx context.Context, req *GetAdminLoginInfoReq) (res *GetAdminLoginInfoRes, err error) {
|
||||
res = new(GetAdminLoginInfoRes)
|
||||
var list []SIPInfo
|
||||
g.Model("sip_info").WithAll().Scan(&list)
|
||||
res.List = list
|
||||
return res, nil
|
||||
}
|
194
api/video_hat/ws2/ws2.2.go
Normal file
194
api/video_hat/ws2/ws2.2.go
Normal file
@ -0,0 +1,194 @@
|
||||
package ws2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/tiger1103/gfast/v3/api/constant"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 获取实时、状态等心跳包的请求
|
||||
func SendHeartbeat(conn *websocket.Conn) {
|
||||
activeDevicesMessage := map[string]string{
|
||||
"act": "ma_get_active_devices",
|
||||
}
|
||||
conn.WriteJSON(activeDevicesMessage)
|
||||
}
|
||||
|
||||
// 定时任务
|
||||
func SendHeartbeatTime(Conn *websocket.Conn) {
|
||||
// 使用匿名函数创建一个定时器,每隔60秒执行一次
|
||||
ticker := time.NewTicker(60 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
SendHeartbeat(Conn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Response 包含从服务器获取的设备数据
|
||||
type LocationAndHeart struct {
|
||||
Cmd string `json:"cmd"`
|
||||
Status bool `json:"status"`
|
||||
Message string `json:"msg"`
|
||||
Data []DeviceData `json:"data"`
|
||||
MsgCode string `json:"msg_code"`
|
||||
}
|
||||
|
||||
// UserInfo 包含有关用户的信息
|
||||
type UserInfo struct {
|
||||
UserID string `json:"user_id" dc:"用户id"`
|
||||
CAVersion string `json:"ca_ver"`
|
||||
DeviceID string `json:"device_id" dc:"设备id"`
|
||||
Mobile string `json:"mobile"`
|
||||
SIM string `json:"sim"`
|
||||
CTime string `json:"c_time" dc:"创建时间"`
|
||||
CALastLoginTime string `json:"ca_last_login_time"`
|
||||
UserName string `json:"user_name" dc:"用户名字"`
|
||||
UserImg string `json:"user_img"`
|
||||
Department string `json:"department"`
|
||||
Role string `json:"role"`
|
||||
FID string `json:"f_id"`
|
||||
CapType string `json:"cap_type"`
|
||||
B1 string `json:"b1"`
|
||||
B2 string `json:"b2"`
|
||||
SOSHeight string `json:"sos_height"`
|
||||
CUserID string `json:"c_user_id"`
|
||||
VoicePkg string `json:"voice_pkg"`
|
||||
AppVersion string `json:"app_version"`
|
||||
Headset string `json:"headset"`
|
||||
UploadVideoNum string `json:"upload_video_num"`
|
||||
NotUploadedVideos string `json:"notuploaded_video_count"`
|
||||
GID string `json:"g_id"`
|
||||
SIPID int `json:"sip_id"`
|
||||
}
|
||||
|
||||
// LocationInfo 包含有关位置信息的数据
|
||||
type LocationInfo struct {
|
||||
Act string `json:"act"`
|
||||
BatteryLevel string `json:"bat_l" dc:"电量百分比"`
|
||||
BatteryVoltage string `json:"bat_v" dc:"电池电压"`
|
||||
Charging string `json:"charging"`
|
||||
GPS string `json:"gps"`
|
||||
GPSLevel string `json:"gps_level"`
|
||||
InUse string `json:"in_use"`
|
||||
IsWorn string `json:"is_weared"`
|
||||
NetStrength string `json:"net_strenth"`
|
||||
NetType string `json:"net_type"`
|
||||
NotUploadedVideos string `json:"notuploaded_video_count"`
|
||||
OnlineType string `json:"online_type"`
|
||||
PhoneNumber string `json:"phoneNumber"`
|
||||
RailStatus string `json:"rail_status"`
|
||||
SIMDataNum string `json:"sim_data_num"`
|
||||
SIMStatus string `json:"sim_status"`
|
||||
TCardStatus string `json:"tcard_status"`
|
||||
TempData string `json:"tempdata"`
|
||||
UserID string `json:"user_id"`
|
||||
XPoint string `json:"x_point"`
|
||||
YPoint string `json:"y_point"`
|
||||
CTime int64 `json:"ctime"`
|
||||
CapType string `json:"cap_type"`
|
||||
}
|
||||
|
||||
// DeviceData 包含用户信息和位置信息
|
||||
type DeviceData struct {
|
||||
UserInfo UserInfo `json:"user_info"`
|
||||
LocationInfo LocationInfo `json:"location_info"`
|
||||
}
|
||||
|
||||
// 处理收到的心跳和实时数据信息
|
||||
func HandleLocation(jsonString string) {
|
||||
var response LocationAndHeart
|
||||
// 解析 JSON 字符串到结构体
|
||||
err := json.Unmarshal([]byte(jsonString), &response)
|
||||
if err != nil {
|
||||
log.Fatal("Error decoding JSON to struct:", err)
|
||||
}
|
||||
|
||||
var IsLowBattery int
|
||||
// 用来跟踪当前活跃的设备编号
|
||||
activeDevices := make(map[string]bool)
|
||||
|
||||
// 获取当前时间,格式化为 YYYY-MM-DD
|
||||
currentTime := time.Now()
|
||||
todayDate := currentTime.Format("2006-01-02")
|
||||
|
||||
for _, v := range response.Data {
|
||||
// 标记此设备ID为活跃
|
||||
activeDevices[v.UserInfo.DeviceID] = true
|
||||
|
||||
// 将 Unix 时间戳(秒)转换为 time.Time 类型
|
||||
createTime := time.Unix(gconv.Int64(v.UserInfo.CTime), 0)
|
||||
|
||||
// 判断电量是否低于20%,如果是,则标记为低电量
|
||||
if gconv.Int(v.LocationInfo.BatteryLevel) <= constant.BatteryLevel {
|
||||
IsLowBattery = 1
|
||||
}
|
||||
|
||||
// 检查数据库中是否存在该设备
|
||||
IfExists, _ := g.Model("device_video_hat").Where("dev_num", v.UserInfo.DeviceID).Count()
|
||||
|
||||
if IfExists == 0 {
|
||||
// 如果设备不存在,则插入新设备数据(不包括设备名称)
|
||||
devHat := g.Map{
|
||||
"dev_num": v.UserInfo.DeviceID,
|
||||
"status": v.LocationInfo.InUse,
|
||||
"uid": v.UserInfo.UserID,
|
||||
"battery_level": v.LocationInfo.BatteryLevel,
|
||||
"is_low_battery": IsLowBattery,
|
||||
"create_time": createTime,
|
||||
"longitude": v.LocationInfo.XPoint,
|
||||
"latitude": v.LocationInfo.YPoint,
|
||||
"sip_id": v.UserInfo.SIPID,
|
||||
}
|
||||
g.Model("device_video_hat").Data(devHat).Save()
|
||||
} else {
|
||||
// 更新已存在的设备数据
|
||||
updateData := g.Map{
|
||||
"status": v.LocationInfo.InUse,
|
||||
"uid": v.UserInfo.UserID,
|
||||
"battery_level": v.LocationInfo.BatteryLevel,
|
||||
"is_low_battery": IsLowBattery,
|
||||
"create_time": createTime,
|
||||
"longitude": v.LocationInfo.XPoint,
|
||||
"latitude": v.LocationInfo.YPoint,
|
||||
"sip_id": v.UserInfo.SIPID,
|
||||
}
|
||||
g.Model("device_video_hat").Data(updateData).Where("dev_num", v.UserInfo.DeviceID).Update()
|
||||
}
|
||||
|
||||
// 检查 device_data_time 表是否已有当天的记录
|
||||
count, err := g.Model("device_data_time").Where("dev_num = ? AND data_time = ?", v.UserInfo.DeviceID, todayDate).Count()
|
||||
if err != nil {
|
||||
log.Println("Error checking device_data_time:", err)
|
||||
continue
|
||||
}
|
||||
if count == 0 {
|
||||
// 如果没有记录,则插入新数据
|
||||
g.Model("device_data_time").Data(g.Map{
|
||||
"dev_num": v.UserInfo.DeviceID,
|
||||
"data_time": todayDate,
|
||||
}).Insert()
|
||||
}
|
||||
}
|
||||
|
||||
// 从数据库中检索所有设备
|
||||
var allDevices []struct{ DevNum string }
|
||||
err = g.Model("device_video_hat").Fields("dev_num AS DevNum").Scan(&allDevices)
|
||||
if err != nil {
|
||||
log.Fatal("Error retrieving all devices:", err)
|
||||
}
|
||||
|
||||
// 为不活跃的设备设置状态为0
|
||||
for _, device := range allDevices {
|
||||
if _, isActive := activeDevices[device.DevNum]; !isActive {
|
||||
g.Model("device_video_hat").Data(g.Map{"status": 0}).Where("dev_num", device.DevNum).Update()
|
||||
}
|
||||
}
|
||||
}
|
153
api/video_hat/ws2/ws2.4.go
Normal file
153
api/video_hat/ws2/ws2.4.go
Normal file
@ -0,0 +1,153 @@
|
||||
package ws2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/tiger1103/gfast/v3/api/constant"
|
||||
"github.com/tiger1103/gfast/v3/api/video_hat/alarm"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CaInfo struct {
|
||||
UserID string `json:"user_id"`
|
||||
DeviceID string `json:"device_id"`
|
||||
CaVer string `json:"ca_ver"`
|
||||
Mobile string `json:"mobile"`
|
||||
SIM string `json:"sim"`
|
||||
Password string `json:"pwd"`
|
||||
CTime string `json:"c_time"`
|
||||
AppLastLoginTime string `json:"app_last_login_time"`
|
||||
CaLastLoginTime string `json:"ca_last_login_time"`
|
||||
UserName string `json:"user_name"`
|
||||
RealName string `json:"real_name"`
|
||||
UserImg string `json:"user_img"`
|
||||
Department string `json:"department"`
|
||||
Role string `json:"role"`
|
||||
FID string `json:"f_id"`
|
||||
CapType string `json:"cap_type"`
|
||||
}
|
||||
|
||||
type ServerPushCaSipSos struct {
|
||||
Cmd string `json:"cmd"`
|
||||
Type string `json:"type"`
|
||||
RoomID string `json:"room_id"`
|
||||
SosRoomID string `json:"sos_room_id"`
|
||||
PictureQuality string `json:"picture_quality"`
|
||||
CaInfo CaInfo `json:"ca_info"`
|
||||
XPoint string `json:"x_point"`
|
||||
YPoint string `json:"y_point"`
|
||||
Time int64 `json:"time"`
|
||||
}
|
||||
|
||||
// 处理SOS信息
|
||||
func HandlePushCaSipSos(jsonString string) {
|
||||
var response ServerPushCaSipSos
|
||||
err := json.Unmarshal([]byte(jsonString), &response)
|
||||
deviceID := response.CaInfo.DeviceID
|
||||
var videoHat VideoDeviceHat
|
||||
g.Model("device_video_hat").Where("dev_num = ?", deviceID).Scan(&videoHat)
|
||||
|
||||
// 发送请求去获取设备的 sip_id 信息 https://caps.runde.pro/api/index.php?ctl=device&act=get_user_sip_id
|
||||
res, _ := getSipId(constant.Token, videoHat.Uid)
|
||||
sipid := res.Data.SIPID
|
||||
|
||||
// 收到了报警 SOS 信息,把数据写入数据库,处理标记为未处理
|
||||
data := alarm.HatAlarm{
|
||||
DevNum: deviceID,
|
||||
DevName: videoHat.DevName,
|
||||
ProjectID: int(videoHat.ProjectID),
|
||||
BatteryLevel: videoHat.BatteryLevel,
|
||||
IsLowBattery: videoHat.IsLowBattery,
|
||||
RoomID: response.RoomID,
|
||||
SipId: sipid,
|
||||
IsHandle: strconv.Itoa(0),
|
||||
HandleAt: time.Time{},
|
||||
}
|
||||
g.Model("hat_alarm").Data(data).Insert()
|
||||
if err != nil {
|
||||
log.Fatal("Error decoding JSON to struct:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func getSipId(token string, userId int) (res *Response, err error) {
|
||||
res = new(Response)
|
||||
|
||||
// 创建请求
|
||||
req, err := http.NewRequest("GET", "https://caps.runde.pro/api/index.php?ctl=device&act=get_user_sip_id", nil)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// 添加请求头
|
||||
req.Header.Set("Authentication", token)
|
||||
|
||||
// 添加查询参数
|
||||
query := req.URL.Query()
|
||||
query.Add("user_id", strconv.Itoa(userId))
|
||||
req.URL.RawQuery = query.Encode()
|
||||
|
||||
// 发送请求
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 读取响应体
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// 检查响应状态码
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return res, errors.New(fmt.Sprintf("unexpected status code: %d", resp.StatusCode))
|
||||
}
|
||||
|
||||
// 反序列化响应体到结构体
|
||||
err = json.Unmarshal(respBody, &res)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Status bool `json:"status"`
|
||||
Msg string `json:"msg"`
|
||||
Data DataObject `json:"data"`
|
||||
MsgCode string `json:"msg_code"`
|
||||
}
|
||||
|
||||
type DataObject struct {
|
||||
SIPID string `json:"sip_id"`
|
||||
}
|
||||
|
||||
type VideoDeviceHat struct {
|
||||
DevNum string `json:"dev_num"` // 设备编号
|
||||
DevName string `json:"dev_name"` // 设备名
|
||||
Status int `json:"status"` // 安全帽状态(0:离线,1:在线,2:监控中,3:通话中,4:隐私模式)
|
||||
ProjectID int64 `json:"project_id"` // 项目id
|
||||
UserID string `json:"user_id"` // 用户id
|
||||
BatteryLevel string `json:"battery_level"` // 电量
|
||||
IsLowBattery bool `json:"is_low_battery"` // 是否处于低电量,true代表低电量,false则不是低电量
|
||||
CreateTime time.Time `json:"create_time"` // 创建时间
|
||||
UpdatedAt time.Time `json:"updated_at"` // 更新时间
|
||||
Longitude string `json:"longitude"` // 最新的经度
|
||||
Latitude string `json:"latitude"` // 最新的维度
|
||||
SipId int `json:"sip_id"` // SIPID
|
||||
Uid int `json:"uid"` // 安全帽厂商返回的用户ID
|
||||
PushStatus string `json:"push_status"` // 推流状态
|
||||
Nickname string `json:"nick_name"` // 用户名称
|
||||
UserName string `json:"user_name"` // 真实名称
|
||||
HeadIcon string `json:"head_icon"` // 头像
|
||||
Phone string `json:"phone"` // 电话
|
||||
}
|
84
api/video_hat/ws2/ws2.5.1.go
Normal file
84
api/video_hat/ws2/ws2.5.1.go
Normal file
@ -0,0 +1,84 @@
|
||||
package ws2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/tiger1103/gfast/v3/api/constant"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type OpenPushFlowReq struct {
|
||||
g.Meta `path:"/device/open" method:"post" tags:"视频安全帽相关" summary:"开启推流"`
|
||||
DevNum string `json:"devNum" dc:"设备id"`
|
||||
}
|
||||
|
||||
type OpenPushFlowRes struct {
|
||||
Url string `json:"Url" dc:"返回地址"`
|
||||
}
|
||||
|
||||
// 设备地址的 Map 映射通道
|
||||
var deviceChannels = make(map[string]chan string)
|
||||
|
||||
func (w WsRouter) OpenPushFlow(ctx context.Context, req *OpenPushFlowReq) (res *OpenPushFlowRes, err error) {
|
||||
// 创建设备ID对应的通道
|
||||
deviceChan := make(chan string, 1) // 缓冲为1
|
||||
deviceChannels[req.DevNum] = deviceChan
|
||||
|
||||
// 触发WS的推流
|
||||
SendDeviceEnablesPushFlow(constant.Conn, req.DevNum)
|
||||
|
||||
// 等待接收 URL 或超时
|
||||
select {
|
||||
case url := <-deviceChan:
|
||||
res = &OpenPushFlowRes{Url: url}
|
||||
case <-time.After(time.Second * 10): // 设置超时时间为10秒
|
||||
err = fmt.Errorf("timeout waiting for URL")
|
||||
}
|
||||
// 清理工作
|
||||
delete(deviceChannels, req.DevNum)
|
||||
close(deviceChan)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// 发送报文指定设备开启推流
|
||||
func SendDeviceEnablesPushFlow(conn *websocket.Conn, DeviceId string) {
|
||||
activeDevicesMessage := map[string]string{
|
||||
"act": "ma_open_rtsp",
|
||||
"device_id": DeviceId,
|
||||
}
|
||||
|
||||
conn.WriteJSON(activeDevicesMessage)
|
||||
}
|
||||
|
||||
type MaOpenRtspResponse struct {
|
||||
Cmd string `json:"cmd"`
|
||||
DeviceId string `json:"device_id"`
|
||||
Status bool `json:"status"`
|
||||
Msg string `json:"msg"`
|
||||
ApiUrl string `json:"api_url"`
|
||||
PlayUrl []string `json:"play_url"`
|
||||
MsgCode string `json:"msg_code"`
|
||||
WebrtcUrl string `json:"webrtc_url"`
|
||||
}
|
||||
|
||||
// 处理函数
|
||||
func HandleDeviceEnablesPushFlow(jsonString string) {
|
||||
var response MaOpenRtspResponse
|
||||
err := json.Unmarshal([]byte(jsonString), &response)
|
||||
if err != nil {
|
||||
log.Fatal("Error decoding JSON to struct:", err)
|
||||
}
|
||||
for _, url := range response.PlayUrl {
|
||||
// 截取 FLV 结尾的视频并发送到 HTTP 响应的通道中
|
||||
if strings.HasSuffix(url, ".flv") {
|
||||
if deviceChan, ok := deviceChannels[response.DeviceId]; ok {
|
||||
deviceChan <- url // 将URL发送到对应的通道
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
48
api/video_hat/ws2/ws2.5.2.go
Normal file
48
api/video_hat/ws2/ws2.5.2.go
Normal file
@ -0,0 +1,48 @@
|
||||
package ws2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gorilla/websocket"
|
||||
|
||||
"github.com/tiger1103/gfast/v3/api/constant"
|
||||
)
|
||||
|
||||
type OffPushFlowReq struct {
|
||||
g.Meta `path:"/device/off" method:"post" tags:"视频安全帽相关" summary:"关闭推流"`
|
||||
DevNum string `json:"devNum" dc:"设备id"`
|
||||
}
|
||||
|
||||
type OffPushFlowRes struct {
|
||||
}
|
||||
|
||||
func (w WsRouter) OffPushFlow(ctx context.Context, req *OffPushFlowReq) (res *OffPushFlowRes, err error) {
|
||||
SendDeviceOffPushFlow(constant.Conn, req.DevNum)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// 设备关闭推流
|
||||
func SendDeviceOffPushFlow(conn *websocket.Conn, DeviceId string) {
|
||||
activeDevicesMessage := map[string]string{
|
||||
"act": "ma_stop_rtsp",
|
||||
"device_id": DeviceId,
|
||||
}
|
||||
|
||||
conn.WriteJSON(activeDevicesMessage)
|
||||
}
|
||||
|
||||
// MaStopRtspCommand 定义了停止RTSP流的命令结构体
|
||||
type MaStopRtspCommand struct {
|
||||
Cmd string `json:"cmd"` // cmd 指令标识
|
||||
DeviceID string `json:"device_id"` // device_id 设备ID
|
||||
Status bool `json:"status"` // status 返回状态
|
||||
Msg string `json:"msg"` // msg 返回消息
|
||||
MsgCode string `json:"msg_code"` // msg_code 返回消息码
|
||||
}
|
||||
|
||||
func HandStopPush(jsonString string) {
|
||||
var response MaStopRtspCommand
|
||||
json.Unmarshal([]byte(jsonString), &response)
|
||||
|
||||
}
|
89
api/video_hat/ws_connect.go
Normal file
89
api/video_hat/ws_connect.go
Normal file
@ -0,0 +1,89 @@
|
||||
package video_hat
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/tiger1103/gfast/v3/api/constant"
|
||||
"github.com/tiger1103/gfast/v3/api/video_hat/ws2"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 项目启动就登录视频安全帽的 WS
|
||||
func WsConnection() {
|
||||
// 首先加载 WS 路径映射
|
||||
InitWsMap()
|
||||
|
||||
// 无限循环尝试连接,直到成功
|
||||
for {
|
||||
if connectWs() {
|
||||
break // 如果连接成功,则退出循环
|
||||
}
|
||||
log.Println("Reconnecting in 5 seconds...")
|
||||
time.Sleep(5 * time.Second) // 等待5秒后重新连接WS
|
||||
}
|
||||
}
|
||||
|
||||
func connectWs() bool {
|
||||
// 创建 WebSocket 连接
|
||||
dialer := websocket.Dialer{
|
||||
HandshakeTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
// 尝试连接 WebSocket
|
||||
var err error
|
||||
constant.Conn, _, err = dialer.Dial(constant.Url, nil)
|
||||
if err != nil {
|
||||
log.Println("Dial:", err)
|
||||
return false
|
||||
}
|
||||
defer constant.Conn.Close()
|
||||
|
||||
// 连接成功之后发送登录信息
|
||||
ws2.Login(constant.Conn)
|
||||
|
||||
// 设置定时任务,每间隔60秒发送参数获取实时、状态等心跳包
|
||||
go ws2.SendHeartbeatTime(constant.Conn)
|
||||
|
||||
// 处理从 WebSocket 接收的消息
|
||||
if handlerFun(constant.Conn) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func handlerFun(Conn *websocket.Conn) bool {
|
||||
// 读取 Conn 的数据成 JSON 字符串
|
||||
for {
|
||||
_, message, err := Conn.ReadMessage()
|
||||
|
||||
if err != nil {
|
||||
log.Println("ReadMessage:", err)
|
||||
return false // 如果读取过程中出现错误,则返回 false
|
||||
}
|
||||
|
||||
// 创建一个 map 来存储解析后的 JSON
|
||||
var msgMap map[string]interface{}
|
||||
|
||||
// 将读取到的字节切片解析为 JSON
|
||||
err = json.Unmarshal(message, &msgMap)
|
||||
if err != nil {
|
||||
log.Println("Unmarshal:", err)
|
||||
continue // 如果解析失败,则跳过此次循环
|
||||
}
|
||||
|
||||
if cmd, ok := msgMap["cmd"].(string); ok {
|
||||
if handler, exists := commandHandlers[cmd]; exists {
|
||||
jsonString, err := json.Marshal(msgMap)
|
||||
if err != nil {
|
||||
log.Println("Marshal:", err)
|
||||
continue // 如果序列化失败,则跳过此次循环
|
||||
}
|
||||
handler(string(jsonString)) // 调用处理函数
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true // 正常退出循环,返回 true
|
||||
}
|
26
api/video_hat/ws_map.go
Normal file
26
api/video_hat/ws_map.go
Normal file
@ -0,0 +1,26 @@
|
||||
package video_hat
|
||||
|
||||
import (
|
||||
"github.com/tiger1103/gfast/v3/api/video_hat/ws2"
|
||||
)
|
||||
|
||||
type CommandHandler func(json string)
|
||||
|
||||
type SpecialCommandHandler func(json string) (interface{}, error)
|
||||
|
||||
var commandHandlers = map[string]CommandHandler{}
|
||||
|
||||
// 初始化对应函数处理的方法
|
||||
func InitWsMap() {
|
||||
commandHandlers[MALOGIN] = ws2.HandleLogin // 注册处理登录的函数【需要定义一个请求一个响应】
|
||||
commandHandlers[MAGETACTIVEDEVICES] = ws2.HandleLocation // 注册实时数据心跳的函数【需要定义一个请求一个响应】
|
||||
commandHandlers[MAOPENRTSP] = ws2.HandleDeviceEnablesPushFlow // 注册长链接发送报文指定设备开启推流
|
||||
commandHandlers[SERVERPUSHCASIPSOS] = ws2.HandlePushCaSipSos // 接收设备主动sos报警
|
||||
commandHandlers[MASTOPRTSP] = ws2.HandStopPush // 停止推流
|
||||
}
|
||||
|
||||
const MALOGIN = "ma_login" // 登录
|
||||
const MAGETACTIVEDEVICES = "ma_get_active_devices" // 获取实时、状态等心跳包
|
||||
const MAOPENRTSP = "ma_open_rtsp" // 长链接发送报文指定设备开启推流
|
||||
const SERVERPUSHCASIPSOS = "server_push_ca_sip_sos" // 接收设备主动sos报警
|
||||
const MASTOPRTSP = "ma_stop_rtsp" // 停止推流
|
Reference in New Issue
Block a user