@ -94,7 +94,28 @@ func (s *DockService) Create(userID int64, isAdmin bool, req *vo.DockCreateReq)
}
var existing model . Dock
if err := common . DB . Where ( "dock_id = ?" , req . DockID ) . First ( & existing ) . Error ; err == nil {
if err := common . DB . Unscoped ( ) . Where ( "dock_id = ?" , req . DockID ) . First ( & existing ) . Error ; err == nil {
if existing . DeletedAt . Valid {
updates := map [ string ] any {
"user_id" : owner , "deleted_at" : nil , "register_status" : "registered" , "updated_at" : time . Now ( ) ,
"name" : req . Name , "code" : req . Code , "sn" : req . SN , "location" : req . Location ,
"longitude" : req . Longitude , "latitude" : req . Latitude , "altitude" : req . Altitude ,
}
if err := common . DB . Transaction ( func ( tx * gorm . DB ) error {
if err := tx . Unscoped ( ) . Model ( & existing ) . Updates ( updates ) . Error ; err != nil {
return err
}
return tx . Model ( & model . Drone { } ) . Where ( "dock_id = ?" , existing . DockID ) . Update ( "user_id" , owner ) . Error
} ) ; err != nil {
logger . ERROR ( "恢复已删除机巢失败" , err )
return nil , common . ErrInternal
}
common . DB . First ( & existing , existing . ID )
if req . Iccid != "" {
DefaultSimRechargeService . EnsureCardFromICCID ( owner , existing . DockID , req . Iccid )
}
return & existing , nil
}
if existing . UserID != 0 {
return nil , common . ErrDockExists
}
@ -112,9 +133,6 @@ func (s *DockService) Create(userID int64, isAdmin bool, req *vo.DockCreateReq)
if req . SN != "" {
updates [ "sn" ] = req . SN
}
if req . Iccid != "" {
updates [ "iccid" ] = req . Iccid
}
if req . Location != "" {
updates [ "location" ] = req . Location
}
@ -131,6 +149,9 @@ func (s *DockService) Create(userID int64, isAdmin bool, req *vo.DockCreateReq)
return nil , common . ErrInternal
}
common . DB . First ( & existing , existing . ID )
if req . Iccid != "" {
DefaultSimRechargeService . EnsureCardFromICCID ( owner , existing . DockID , req . Iccid )
}
return & existing , nil
} else if ! errors . Is ( err , gorm . ErrRecordNotFound ) {
logger . ERROR ( "查询机巢失败" , err )
@ -149,7 +170,6 @@ func (s *DockService) Create(userID int64, isAdmin bool, req *vo.DockCreateReq)
Name : req . Name ,
Code : req . Code ,
SN : req . SN ,
Iccid : req . Iccid ,
Location : req . Location ,
Longitude : req . Longitude ,
Latitude : req . Latitude ,
@ -167,6 +187,9 @@ func (s *DockService) Create(userID int64, isAdmin bool, req *vo.DockCreateReq)
logger . ERROR ( "登记机巢失败" , err )
return nil , common . ErrInternal
}
if req . Iccid != "" {
DefaultSimRechargeService . EnsureCardFromICCID ( owner , dock . DockID , req . Iccid )
}
return dock , nil
}
@ -227,10 +250,13 @@ func (s *DockService) Update(userID int64, isAdmin bool, id int64, req *vo.DockU
return nil , common . ErrInternal
}
common . DB . First ( & dock , id )
if req . RegisterAs == "registered" && req . Iccid != "" {
DefaultSimRechargeService . EnsureCardFromICCID ( dock . UserID , dock . DockID , req . Iccid )
}
return & dock , nil
}
// Delete 硬删除机巢及其关联设备数据
// Delete 软删除机巢:保留任务、直播、告警、指令、视频等历史数据用于审计。
func ( s * DockService ) Delete ( userID int64 , isAdmin bool , id int64 ) * common . BusiError {
var dock model . Dock
if err := common . DB . Scopes ( withUserFilter ( userID , isAdmin ) ) . First ( & dock , id ) . Error ; err != nil {
@ -241,62 +267,20 @@ func (s *DockService) Delete(userID int64, isAdmin bool, id int64) *common.BusiE
return common . ErrInternal
}
var taskIDs [ ] string
var droneSNs [ ] string
var simCardIDs [ ] int64
if err := common . DB . Transaction ( func ( tx * gorm . DB ) error {
if err := tx . Model ( & model . TaskPlan { } ) . Where ( "dock_id = ?" , dock . DockID ) . Pluck ( "id" , & taskIDs ) . Error ; err != nil {
return err
}
if err := tx . Model ( & model . Drone { } ) . Where ( "dock_id = ?" , dock . DockID ) . Pluck ( "drone_sn" , & droneSNs ) . Error ; err != nil {
return err
}
if err := tx . Model ( & model . SimCard { } ) . Where ( "dock_id = ?" , dock . DockID ) . Pluck ( "id" , & simCardIDs ) . Error ; err != nil {
return err
}
if len ( droneSNs ) > 0 {
var videoIDs [ ] int64
if err := tx . Model ( & model . Video { } ) . Where ( "drone_sn IN ?" , droneSNs ) . Pluck ( "id" , & videoIDs ) . Error ; err != nil {
return err
}
if len ( videoIDs ) > 0 {
if err := tx . Where ( "video_id IN ?" , videoIDs ) . Delete ( & model . DownloadLog { } ) . Error ; err != nil {
// 防止删除后 cron 任务仍继续向已移除的设备下发指令;任务历史保留。
if err := tx . Model ( & model . TaskPlan { } ) .
Where ( "dock_id = ? AND schedule_type = ? AND status = ?" , dock . DockID , "cron" , "enabled" ) .
Updates ( map [ string ] any { "status" : "disabled" , "updated_at" : time . Now ( ) } ) . Error ; err != nil {
return err
}
if err := tx . Where ( "id IN ?" , videoIDs ) . Delete ( & model . Video { } ) . Error ; err != nil {
return err
}
}
}
if len ( simCardIDs ) > 0 {
if err := tx . Where ( "sim_card_id IN ?" , simCardIDs ) . Delete ( & model . SimUsageRecord { } ) . Error ; err != nil {
return err
}
if err := tx . Where ( "sim_card_id IN ?" , simCardIDs ) . Delete ( & model . SimRechargeLog { } ) . Error ; err != nil {
return err
}
if err := tx . Where ( "id IN ?" , simCardIDs ) . Delete ( & model . SimCard { } ) . Error ; err != nil {
return err
}
}
for _ , item := range [ ] any {
& model . Alarm { } , & model . DeviceCommandLog { } , & model . WorkflowState { } ,
& model . TaskExecution { } , & model . TaskPlan { } , & model . LiveSession { } , & model . Drone { } ,
} {
if err := tx . Where ( "dock_id = ?" , dock . DockID ) . Delete ( item ) . Error ; err != nil {
return err
}
}
return tx . Delete ( & dock ) . Error
} ) ; err != nil {
logger . ERROR ( "硬 删除机巢失败" , err )
logger . ERROR ( "软删除机巢失败" , err )
return common . ErrInternal
}
for _ , taskID := range taskIDs {
DefaultScheduler . RemoveTask ( taskID )
}
DefaultScheduler . RemoveTaskForDock ( dock . DockID )
_ = common . SetRemove ( cache . OnlineDockSetKey , dock . DockID )
_ = common . Delete ( cache . DockHeartbeatKeyOf ( dock . DockID ) )
_ = common . Delete ( cache . DockStatusKeyOf ( dock . DockID ) )
@ -316,9 +300,14 @@ func (s *DockService) realtime(dockID string) (bool, map[string]string) {
// EnsureFromOnline 设备自发现:不存在则登记为 pending,存在则更新在线状态与软件版本
func ( s * DockService ) EnsureFromOnline ( dockID string , st statusOnline ) {
online := st . Status == "online"
var dock model . Dock
err := common . DB . Where ( "dock_id = ?" , dockID ) . First ( & dock ) . Error
err := common . DB . Unscoped ( ) . Where ( "dock_id = ?" , dockID ) . First ( & dock ) . Error
if err == nil && dock . DeletedAt . Valid {
// 已删除设备不会因 MQTT 自动发现而复活,必须通过“添加/认领机巢”显式恢复。
_ = common . SetRemove ( cache . OnlineDockSetKey , dockID )
_ = common . Delete ( cache . DockHeartbeatKeyOf ( dockID ) )
return
}
if errors . Is ( err , gorm . ErrRecordNotFound ) {
id , e := tool . NextID ( )
if e != nil {
@ -326,29 +315,16 @@ func (s *DockService) EnsureFromOnline(dockID string, st statusOnline) {
return
}
now := time . Now ( )
dock = model . Dock {
ID : id ,
UserID : 0 , // 待认领
DockID : dockID ,
Name : st . Name ,
Code : dockID ,
SN : dockID ,
Location : st . Location ,
Latitude : st . Latitude ,
Longitude : st . Longitude ,
RegisterStatus : "pending" ,
DockIDSource : "auto" ,
SoftwareVer : st . SoftwareVersion ,
ProtocolVer : st . ProtocolVersion ,
CreatedAt : now ,
UpdatedAt : now ,
}
dock = model . Dock { ID : id , UserID : 0 , DockID : dockID , Name : st . Name , Code : dockID , SN : dockID , Location : st . Location , Latitude : st . Latitude , Longitude : st . Longitude , RegisterStatus : "pending" , DockIDSource : "auto" , SoftwareVer : st . SoftwareVersion , ProtocolVer : st . ProtocolVersion , CreatedAt : now , UpdatedAt : now }
if err := common . DB . Create ( & dock ) . Error ; err != nil {
logger . ERROR ( "登记机巢失败" , err )
return
}
} else if err == nil {
updates := map [ string ] any { "updated_at" : time . Now ( ) }
if dock . DeletedAt . Valid {
updates [ "deleted_at" ] = nil
}
if st . SoftwareVersion != "" {
updates [ "software_ver" ] = st . SoftwareVersion
}
@ -367,12 +343,13 @@ func (s *DockService) EnsureFromOnline(dockID string, st statusOnline) {
if dock . Longitude == 0 && st . Longitude != 0 {
updates [ "longitude" ] = st . Longitude
}
if err := common . DB . Model ( & dock ) . Updates ( updates ) . Error ; err != nil {
if err := common . DB . Unscoped ( ) . Model ( & dock ) . Updates ( updates ) . Error ; err != nil {
logger . ERROR ( "更新机巢在线状态失败" , err )
}
} else {
logger . ERROR ( "查询机巢在线状态失败" , err )
return
}
// 在线集合
if online {
_ = common . SetAdd ( cache . OnlineDockSetKey , dockID )
refreshDeviceHeartbeat ( cache . DockHeartbeatKeyOf ( dockID ) )
@ -380,11 +357,5 @@ func (s *DockService) EnsureFromOnline(dockID string, st statusOnline) {
_ = common . SetRemove ( cache . OnlineDockSetKey , dockID )
_ = common . Delete ( cache . DockHeartbeatKeyOf ( dockID ) )
}
broadcast ( "dock.status" , map [ string ] any {
"dockId" : dockID ,
"online" : online ,
"softwareVer" : st . SoftwareVersion ,
"protocolVer" : st . ProtocolVersion ,
} )
broadcast ( "dock.status" , map [ string ] any { "dockId" : dockID , "online" : online , "softwareVer" : st . SoftwareVersion , "protocolVer" : st . ProtocolVersion } )
}