Files
zmkgC/third/ys7/ys72.go
2025-07-07 20:11:59 +08:00

339 lines
8.8 KiB
Go

package ys7
import (
"context"
"fmt"
"log"
"os"
"time"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/frame/g"
"github.com/tidwall/gjson"
"github.com/tiger1103/gfast/v3/internal/app/system/dao"
"github.com/tiger1103/gfast/v3/internal/app/system/model/do"
"github.com/tiger1103/gfast/v3/internal/app/system/model/entity"
)
type Ys72 struct {
FnLogin func() (string, error) // 登录回调
Devices map[string]Ys7Device // 设备列表
Total int // 总页数 和 当前页数
}
var Ys72Instance = NewYs72()
// NewYs72 初始化
func NewYs72() *Ys72 {
ys7 := &Ys72{
FnLogin: DefaultLogin,
Devices: make(map[string]Ys7Device),
Total: 999, // 第一次获取设备列表时,会刷新其实际值
}
// 刷新 ys7.go 中的 AccessToken
_ = ys7.GetAccessToken()
// 初始化设备列表
//if err := ys7.Init(); err != nil {
// panic(err)
//}
// 定时任务更新设备列表
go func() {
for range time.Tick(3 * time.Minute) {
if err := ys7.Init(); err != nil {
panic(err)
}
if err := ys7.GetAllDeviceList(); err != nil {
log.Println("更新设备列表失败:", err)
}
// 获取设备截图并存入 Redis
ys7.CaptureAllDevice()
}
}()
return ys7
}
// Init 第一次初始化时从数据库获取设备列表存入 Devices
func (ys *Ys72) Init() error {
device := []entity.Ys7Devices{}
// 获取设备列表
err := dao.Ys7Devices.Ctx(context.Background()).Scan(&device)
if err != nil {
return err
}
// 将设备列表存入 Devices
for _, v := range device {
ys.Devices[v.DeviceSerial] = Ys7Device{
DeviceSerial: v.DeviceSerial,
DeviceName: v.DeviceName,
DeviceType: v.DeviceType,
Status: v.Status,
Defence: int(v.Defence),
DeviceVersion: v.DeviceVersion,
}
}
return nil
}
// 遍历 ys.Devices 调用 Capture 获取设备截图并存入Redis
func (ys *Ys72) CaptureAllDevice() {
for _, device := range ys.Devices {
// 获取设备截图
resp, err := device.Capture()
if err != nil {
log.Println("获取设备截图失败:", err)
return
}
// 存入 Redis
ctx := context.Background()
g.Redis().Set(ctx, "ys7:capture:"+device.DeviceSerial, resp.Data.PicUrl)
// 两小时后过期
g.Redis().Expire(ctx, "ys7:capture:"+device.DeviceSerial, 2*60*60)
}
}
// 由 Redis 管理 AccessToken 的生命周期,如果获取失败则重新刷新
func (ys *Ys72) GetAccessToken() string {
ctx := context.Background()
// 从 Redis 获取 AccessToken
accessToken, err := g.Redis().Get(ctx, "ys7:accessToken")
// 兼容原本获取 AccessToken 的逻辑
SetAccessToken(accessToken.String())
if err == nil && accessToken.String() != "" {
return accessToken.String()
}
// 未获取到 AccessToken 则重新登录
if accessToken.String() == "" {
// 如果登录失败则递归重试
if err := ys.login(); err != nil {
return ys.GetAccessToken()
}
accessToken, err = g.Redis().Get(ctx, "ys7:accessToken")
if err != nil {
return accessToken.String()
}
}
return accessToken.String()
}
// 传入 deviceSerial 获取设备信息
func (ys *Ys72) GetDevice(deviceSerial string) (Ys7Device, error) {
if device, ok := ys.Devices[deviceSerial]; ok {
return device, nil
}
return Ys7Device{}, fmt.Errorf("设备不存在: %s", deviceSerial)
}
// 获取单页设备列表并存入 Devices
func (ys *Ys72) getDeviceList(page int, deviceMapping map[string]Ys7Device) error {
accessToken := ys.GetAccessToken()
req := struct {
AccessToken string `json:"accessToken"`
PageStart int `json:"pageStart"`
PageSize int `json:"pageSize"`
}{AccessToken: accessToken, PageSize: 50, PageStart: page}
resp, err := Post("https://open.ys7.com/api/lapp/device/list", req)
if err != nil {
return err
}
body := string(resp)
// 解析返回数据
if gjson.Get(body, "code").Int() != 200 {
return fmt.Errorf("获取设备列表失败: %s", gjson.Get(body, "msg").String())
}
// 解析设备列表
gjson.Get(body, "data").ForEach(func(_, value gjson.Result) bool {
deviceSerial := value.Get("deviceSerial").String() // 设备序列号
deviceName := value.Get("deviceName").String() // 设备名称
deviceType := value.Get("deviceType").String() // 设备类型
deviceStatus := int(value.Get("status").Int()) // 设备状态 1:在线 2:离线
defence := int(value.Get("defence").Int()) // 是否布撤防 0:未布防 1:布防 不用管
deviceVersion := value.Get("deviceVersion").String() // 设备版本
deviceMapping[deviceSerial] = Ys7Device{
DeviceSerial: deviceSerial,
DeviceName: deviceName,
DeviceType: deviceType,
Status: deviceStatus,
Defence: defence,
DeviceVersion: deviceVersion,
}
return true
})
ys.Total = int(gjson.Get(body, "page.total").Int())
return nil
}
// 依次调用 getDeviceList 获取所有设备列表
func (ys *Ys72) GetAllDeviceList() error {
// 创建一个设备map
deviceMapping := make(map[string]Ys7Device)
// 遍历页码获取所有设备列表
for page := 0; page <= ys.Total; page++ {
if err := ys.getDeviceList(page, deviceMapping); err != nil {
return err
}
}
newDevices := []do.Ys7Devices{} // 新设备列表
updateDevices := []do.Ys7Devices{} // 更新设备列表
// 遍历 deviceMapping 对比 ys.Devices 筛选出增量或更新设备列表
for deviceSerial, device := range deviceMapping {
// 如果设备不存在则为新增设备
if _, ok := ys.Devices[deviceSerial]; !ok {
newDevices = append(newDevices, do.Ys7Devices{
DeviceSerial: device.DeviceSerial,
DeviceName: device.DeviceName,
DeviceType: device.DeviceType,
Status: device.Status,
Defence: device.Defence,
DeviceVersion: device.DeviceVersion,
})
} else {
// 如果设备存在则对比设备状态
if ok, newDevice := ys.CompareDevice(ys.Devices[deviceSerial], device); !ok {
// 如果设备状态不同则更新设备
updateDevices = append(updateDevices, do.Ys7Devices{
DeviceSerial: newDevice.DeviceSerial,
DeviceName: newDevice.DeviceName,
DeviceType: newDevice.DeviceType,
Status: newDevice.Status,
Defence: newDevice.Defence,
DeviceVersion: newDevice.DeviceVersion,
})
}
}
}
// 新旧状态替换
ys.Devices = deviceMapping
// 新设备列表入库
if len(newDevices) > 0 {
_, err := dao.Ys7Devices.Ctx(context.Background()).Data(newDevices).Batch(20).Insert()
if err != nil {
return err
}
}
// 更新设备列表
if len(updateDevices) > 0 {
for _, device := range updateDevices {
_, err := dao.Ys7Devices.Ctx(context.Background()).Data(device).
Where(dao.Ys7Devices.Columns().DeviceSerial, device.DeviceSerial).Update()
if err != nil {
return err
}
}
// _, err := dao.Ys7Devices.Ctx(context.Background()).Data(updateDevices).Batch(20).Update()
// if err != nil {
// log.Println("更新错误:", err)
// return err
// }
}
return nil
}
// 登录并将 AccessToken 存入 Redis 由 Redis 管理其有效期
// 当获取失败则重新登录后刷新周期
func (ys *Ys72) login() error {
accessToken, err := ys.FnLogin()
if err != nil {
return err
}
ctx := context.Background()
g.Redis().Set(ctx, "ys7:accessToken", accessToken)
// 设置 key 将在 5 天后过期
g.Redis().Expire(ctx, "ys7:accessToken", 5*24*60*60)
return nil
}
// CompareDevice 对比传递的两个设备结构体是否相同
// 如果不同则返回 false 并构造出新的设备结构体
func (ys *Ys72) CompareDevice(old, new Ys7Device) (bool, Ys7Device) {
// 对比old 和 new 结构体的所有字段,是否相同
if old == new {
return true, Ys7Device{}
}
// 构造出新的ys7Device
return false, Ys7Device{
DeviceSerial: new.DeviceSerial,
DeviceName: new.DeviceName,
DeviceType: new.DeviceType,
Status: new.Status,
Defence: new.Defence,
DeviceVersion: new.DeviceVersion,
}
}
type defalutFn func() (string, error)
var DefaultLogin = defalutFn(func() (string, error) {
appKey := "3acf9f1a43dc4209841e0893003db0a2"
appSecret := "4bbf3e9394f55d3af6e3af27b2d3db36"
req := struct {
AppKey string `json:"appKey"`
AppSecret string `json:"appSecret"`
}{AppKey: appKey, AppSecret: appSecret}
// 登录
resp, err := Post("https://open.ys7.com/api/lapp/token/get", req)
if err != nil {
return "", err
}
body := string(resp)
// 解析返回数据
if gjson.Get(body, "code").Int() != 200 {
return "", fmt.Errorf("登录失败: %s", gjson.Get(body, "msg").String())
}
// 返回 AccessToken
return gjson.Get(body, "data.accessToken").String(), nil
})
func StartAI(device Ys7Device) {
capture, err := device.Capture()
if err != nil {
fmt.Println("定时快照错误!", err)
} else {
get, err := g.Client().Get(gctx.New(), capture.Data.PicUrl)
if err != nil {
} else {
os.WriteFile("", get.ReadAll(), 0o666)
}
}
}