feat: 支持设备上下线统计

This commit is contained in:
ydajiang
2025-11-01 20:48:42 +08:00
parent 3ba048b9a5
commit 6825709704
9 changed files with 128 additions and 9 deletions

View File

@@ -302,6 +302,8 @@ func StartApiServer(addr string) {
apiServer.router.HandleFunc("/api/v1/log/list", withVerify(common.WithQueryStringParams(apiServer.OnLogList, QueryDeviceChannel{}))) // 操作日志
apiServer.router.HandleFunc("/api/v1/log/clear", withVerify(common.WithQueryStringParams(apiServer.OnLogClear, Empty{}))) // 操作日志
apiServer.router.HandleFunc("/api/v1/device/statuslog", withVerify(common.WithQueryStringParams(apiServer.OnStatusLogList, QueryDeviceChannel{}))) // 设备上下线统计
// 暂未开发
apiServer.router.HandleFunc("/api/v1/sms/list", withVerify(func(w http.ResponseWriter, req *http.Request) {})) // 流媒体服务器列表
apiServer.router.HandleFunc("/api/v1/cloudrecord/querychannels", withVerify(func(w http.ResponseWriter, req *http.Request) {})) // 云端录像
@@ -310,6 +312,9 @@ func StartApiServer(addr string) {
apiServer.router.HandleFunc("/api/v1/setbaseconfig", withVerify(common.WithFormDataParams(apiServer.OnSetBaseConfig, Empty{})))
apiServer.router.HandleFunc("/api/v1/gm/cert/list", withVerify(func(w http.ResponseWriter, req *http.Request) {}))
apiServer.router.HandleFunc("/api/v1/getrequestkey", withVerify(func(w http.ResponseWriter, req *http.Request) {}))
apiServer.router.HandleFunc("/api/v1/getrequestkey", withVerify(func(w http.ResponseWriter, req *http.Request) {}))
apiServer.router.HandleFunc("/api/v1/device/positionlog", withVerify(func(w http.ResponseWriter, req *http.Request) {}))
apiServer.router.HandleFunc("/api/v1/device/streamlog", withVerify(func(w http.ResponseWriter, req *http.Request) {}))
apiServer.registerStatisticsHandler("开始录制", "/api/v1/record/start", withVerify(apiServer.OnRecordStart)) // 开启录制
apiServer.registerStatisticsHandler("结束录制", "/api/v1/record/stop", withVerify(apiServer.OnRecordStop)) // 关闭录制

View File

@@ -444,3 +444,26 @@ func (api *ApiServer) OnPTZControl(v *QueryRecordParams, _ http.ResponseWriter,
return "OK", nil
}
func (api *ApiServer) OnStatusLogList(q *QueryDeviceChannel, _ http.ResponseWriter, _ *http.Request) (interface{}, error) {
if q.Limit < 1 {
q.Limit = 10
}
v := struct {
LogCount int
LogList interface{}
LogReserveDays int
}{
LogReserveDays: common.Config.LogReserveDays,
}
logList, count, err := dao.StatusLog.QueryBySerial(q.DeviceID, q.Limit)
if err != nil {
return nil, err
}
v.LogCount = count
v.LogList = logList
return &v, nil
}

View File

@@ -11,7 +11,7 @@ import (
// GBModel 解决`Model`变量名与gorm.Model冲突
type GBModel struct {
ID uint `gorm:"primarykey" xml:"-"`
CreatedAt time.Time `json:"created_at" xml:"-"`
CreatedAt time.Time `json:"-" xml:"-"`
UpdatedAt time.Time `json:"-" xml:"-"`
}

View File

@@ -164,7 +164,6 @@ func (l *daoLog) Clear() error {
func (l *daoLog) DeleteExpired(expireTime time.Time) error {
return DBTransaction(func(tx *gorm.DB) error {
tx.Delete(&LogModel{}, "created_at < ?", expireTime.Format("2006-01-02 15:04:05"))
return nil
return tx.Where("created_at < ?", expireTime).Delete(&LogModel{}).Unscoped().Error
})
}

View File

@@ -29,6 +29,7 @@ var (
Position = &daoPosition{}
Alarm = &daoAlarm{}
Log = &daoLog{}
StatusLog = &daoStatusLog{}
)
func init() {
@@ -90,6 +91,8 @@ func init() {
panic(err)
} else if err = db.AutoMigrate(&LogModel{}); err != nil {
panic(err)
} else if err = db.AutoMigrate(&StatusLogModel{}); err != nil {
panic(err)
}
StartSaveTask()

54
dao/status_log.go Normal file
View File

@@ -0,0 +1,54 @@
package dao
import (
"gorm.io/gorm"
"time"
)
// StatusLogModel 设备上下线记录
type StatusLogModel struct {
GBModel
Serial string `json:"Serial"`
Code string `json:"Code"`
Status string `json:"Status"`
Description string `json:"Description"`
CreatedAt_ string `json:"CreatedAt" gorm:"-"`
}
func (s *StatusLogModel) TableName() string {
return "lkm_status_log"
}
type daoStatusLog struct {
}
func (s *daoStatusLog) Save(model *StatusLogModel) error {
return db.Create(model).Error
}
func (s *daoStatusLog) QueryBySerial(serial string, limit int) ([]*StatusLogModel, int, error) {
// 统计总数
var count int64
err := db.Model(&StatusLogModel{}).Where("serial = ?", serial).Select("id").Count(&count).Error
if err != nil {
return nil, 0, err
} else if count < 1 {
return nil, 0, nil
}
var logs []*StatusLogModel
err = db.Where("serial = ?", serial).Order("created_at desc").Limit(limit).Find(&logs).Error
// 格式化时间
for _, log := range logs {
log.CreatedAt_ = log.CreatedAt.Format("2006-01-02 15:04:05")
}
return logs, int(count), err
}
func (s *daoStatusLog) DeleteExpired(time time.Time) error {
return DBTransaction(func(tx *gorm.DB) error {
return tx.Where("created_at < ?", time).Delete(&StatusLogModel{}).Unscoped().Error
})
}

View File

@@ -1,6 +1,7 @@
package stack
import (
"gb-cms/common"
"gb-cms/dao"
"gb-cms/log"
"sync"
@@ -91,15 +92,27 @@ func NewOnlineDeviceManager() *onlineDeviceManager {
// OnExpires Redis设备ID到期回调
func OnExpires(db int, id string) {
log.Sugar.Infof("设备心跳过期 device: %s", id)
CloseDevice(id)
CloseDevice(id, "设备超时离线 OFF")
}
func CloseDevice(id string) {
func CloseDevice(id string, reason string) {
device, _ := dao.Device.QueryDevice(id)
if device == nil {
log.Sugar.Errorf("设备不存在 device: %s", id)
return
}
// 保存设备状态日志
err := dao.StatusLog.Save(&dao.StatusLogModel{
Serial: id,
Code: "*",
Status: common.OFF.String(),
Description: reason,
})
if err != nil {
log.Sugar.Errorf("保存设备状态日志失败 device: %s err: %s", id, err.Error())
}
(&Device{DeviceModel: device}).Close()
}

View File

@@ -144,7 +144,7 @@ func updateDevicesStatus() {
}
for _, device := range offlineDevices {
CloseDevice(device)
CloseDevice(device, "服务重启时离线 OFF")
}
}
}
@@ -246,6 +246,12 @@ func Start() {
log.Sugar.Errorf("删除过期的操作记录失败 err: %s", err.Error())
}
// 删除过期的设备上下线记录
err = dao.StatusLog.DeleteExpired(logExpireTime)
if err != nil {
log.Sugar.Errorf("删除过期的设备上下线记录失败 err: %s", err.Error())
}
// 删除过期的报警记录
err = dao.Alarm.DeleteExpired(alarmExpireTime)
if err != nil {

View File

@@ -35,7 +35,7 @@ type EventHandler struct {
}
func (e *EventHandler) OnUnregister(id string) {
CloseDevice(id)
CloseDevice(id, "设备注销 OFF")
}
// OnRegister 处理设备注册请求
@@ -72,8 +72,24 @@ func (e *EventHandler) OnRegister(id, transport, addr, userAgent string) (int, G
// 级联通知通道上线
device := &Device{model}
if count > 0 && !alreadyOnline {
go device.PushCatalog()
// 新注册
if !alreadyOnline {
err := dao.StatusLog.Save(&dao.StatusLogModel{
Serial: id,
Code: "*",
Status: common.ON.String(),
Description: "设备注册上线 ON",
})
if err != nil {
log.Sugar.Errorf("保存设备状态日志失败 device: %s err: %s", id, err.Error())
}
// 通道不为空, 通知上级
if count > 0 {
go device.PushCatalog()
}
}
return 3600, device, count < 1 || dao.Device.QueryNeedRefreshCatalog(id, now)