初始
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