低空智控平台 后端go
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

197 lines
5.7 KiB

package service
import (
"errors"
"time"
"gorm.io/gorm"
"laic-backend/cache"
"laic-backend/common"
"laic-backend/logger"
"laic-backend/model"
"laic-backend/tool"
"laic-backend/vo"
)
type DroneService struct{}
var DefaultDroneService = &DroneService{}
// GetPage 无人机分页列表
func (s *DroneService) GetPage(userID int64, isAdmin bool, req *vo.DronePageReq) (*common.PageResponse[model.Drone], *common.BusiError) {
db := common.DB.Model(&model.Drone{}).Scopes(withUserFilter(userID, isAdmin))
if req.Keyword != "" {
kw := "%" + req.Keyword + "%"
db = db.Where("drone_sn LIKE ? OR name LIKE ? OR code LIKE ? OR model LIKE ?", kw, kw, kw, kw)
}
var total int64
if err := db.Count(&total).Error; err != nil {
logger.ERROR("统计无人机失败", err)
return nil, common.ErrInternal
}
var list []model.Drone
if err := db.Scopes(req.Paginate).Order("id DESC").Find(&list).Error; err != nil {
logger.ERROR("查询无人机列表失败", err)
return nil, common.ErrInternal
}
return common.Page(req.Pagination, total, list), nil
}
// GetDetail 无人机详情(含实时遥测)
func (s *DroneService) GetDetail(userID int64, isAdmin bool, id int64) (*vo.DroneVO, *common.BusiError) {
var drone model.Drone
if err := common.DB.Scopes(withUserFilter(userID, isAdmin)).First(&drone, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, common.ErrDroneNotFound
}
return nil, common.ErrInternal
}
online, realtime := s.realtime(drone.DockID, drone.DroneSN)
return &vo.DroneVO{Drone: &drone, Online: online, Realtime: realtime}, nil
}
// GetTelemetry 无人机实时遥测(当前值,历史曲线走 TDengine,见后续阶段)
func (s *DroneService) GetTelemetry(userID int64, isAdmin bool, id int64) (map[string]any, *common.BusiError) {
var drone model.Drone
if err := common.DB.Scopes(withUserFilter(userID, isAdmin)).First(&drone, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, common.ErrDroneNotFound
}
return nil, common.ErrInternal
}
online, realtime := s.realtime(drone.DockID, drone.DroneSN)
return map[string]any{
"droneSn": drone.DroneSN,
"online": online,
"realtime": realtime,
}, nil
}
// Update 更新无人机基础信息
func (s *DroneService) Update(userID int64, isAdmin bool, id int64, req *vo.DroneUpdateReq) (*model.Drone, *common.BusiError) {
var drone model.Drone
if err := common.DB.Scopes(withUserFilter(userID, isAdmin)).First(&drone, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, common.ErrDroneNotFound
}
return nil, common.ErrInternal
}
updates := map[string]any{"updated_at": time.Now()}
if req.Name != "" {
updates["name"] = req.Name
}
if req.Code != "" {
updates["code"] = req.Code
}
if req.Model != "" {
updates["model"] = req.Model
}
if err := common.DB.Model(&drone).Updates(updates).Error; err != nil {
logger.ERROR("更新无人机失败", err)
return nil, common.ErrInternal
}
common.DB.First(&drone, id)
return &drone, nil
}
// Delete 删除无人机
func (s *DroneService) Delete(userID int64, isAdmin bool, id int64) *common.BusiError {
res := common.DB.Scopes(withUserFilter(userID, isAdmin)).Delete(&model.Drone{}, id)
if res.Error != nil {
logger.ERROR("删除无人机失败", res.Error)
return common.ErrInternal
}
if res.RowsAffected == 0 {
return common.ErrDroneNotFound
}
return nil
}
// realtime 读取无人机实时遥测(Redis Hash,按 dock_id 存储)+ 在线集合
func (s *DroneService) realtime(dockID, droneSN string) (bool, map[string]string) {
realtime, err := common.HashGetAll(cache.DroneTelemetryKeyOf(dockID))
if err != nil {
realtime = map[string]string{}
}
online, _ := common.SetMemberExists(cache.OnlineDroneSetKey, droneSN)
return online, realtime
}
// EnsureFromState 无人机自动关联:按 droneSn 或 dockId 创建/更新(1:1 绑定)
func (s *DroneService) EnsureFromState(dockID, droneSN, name string, online bool, battery int, firmwareVer string) {
if droneSN == "" {
return
}
var dock model.Dock
if err := common.DB.Select("user_id").Where("dock_id = ?", dockID).First(&dock).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
logger.ERROR("查询无人机关联机巢失败", err)
}
return
}
var drone model.Drone
err := common.DB.Where("drone_sn = ? OR dock_id = ?", droneSN, dockID).First(&drone).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
id, e := tool.NextID()
if e != nil {
logger.ERROR("生成无人机 ID 失败", e)
return
}
now := time.Now()
drone = model.Drone{
ID: id,
UserID: dock.UserID,
DroneSN: droneSN,
DockID: dockID,
Name: name,
Battery: battery,
FirmwareVer: firmwareVer,
CreatedAt: now,
UpdatedAt: now,
}
if err := common.DB.Create(&drone).Error; err != nil {
logger.ERROR("创建无人机失败", err)
return
}
} else if err == nil {
updates := map[string]any{
"user_id": dock.UserID,
"drone_sn": droneSN,
"dock_id": dockID,
"battery": battery,
"updated_at": time.Now(),
}
if firmwareVer != "" {
updates["firmware_ver"] = firmwareVer
}
if name != "" {
updates["name"] = name
}
if err := common.DB.Model(&drone).Updates(updates).Error; err != nil {
logger.ERROR("更新无人机失败", err)
}
}
// 在线集合
if online {
_ = common.SetAdd(cache.OnlineDroneSetKey, droneSN)
refreshDeviceHeartbeat(cache.DroneHeartbeatKeyOf(droneSN))
} else {
_ = common.SetRemove(cache.OnlineDroneSetKey, droneSN)
_ = common.Delete(cache.DroneHeartbeatKeyOf(droneSN))
}
broadcast("drone.status", map[string]any{
"dockId": dockID,
"droneSn": droneSN,
"online": online,
"battery": battery,
"firmwareVer": firmwareVer,
})
}