实现 StopUpgrade
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/app/dto"
|
"git.huangwc.com/pig/pig-farm-controller/internal/app/dto"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/domain/device"
|
"git.huangwc.com/pig/pig-farm-controller/internal/domain/device"
|
||||||
@@ -12,7 +13,6 @@ import (
|
|||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/utils/file"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/utils/file"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ func (s *areaControllerService) StartUpgrade(ctx context.Context, areaController
|
|||||||
return nil, fmt.Errorf("读取固件文件内容失败: %w", err)
|
return nil, fmt.Errorf("读取固件文件内容失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
subDir := uuid.New().String()
|
subDir := filepath.Join(models.OTADir, uuid.New().String())
|
||||||
var filePath string
|
var filePath string
|
||||||
var actionErr error
|
var actionErr error
|
||||||
|
|
||||||
|
|||||||
@@ -2,20 +2,27 @@ package device
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
|
||||||
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/utils/file"
|
||||||
)
|
)
|
||||||
|
|
||||||
// otaServiceImpl 是 OtaService 接口的实现。
|
// otaServiceImpl 是 OtaService 接口的实现。
|
||||||
type otaServiceImpl struct {
|
type otaServiceImpl struct {
|
||||||
|
ctx context.Context
|
||||||
otaRepo repository.OtaRepository
|
otaRepo repository.OtaRepository
|
||||||
deviceRepo repository.DeviceRepository
|
deviceRepo repository.DeviceRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOtaService 创建一个新的 OtaService 实例。
|
// NewOtaService 创建一个新的 OtaService 实例。
|
||||||
func NewOtaService(otaRepo repository.OtaRepository, deviceRepo repository.DeviceRepository) OtaService {
|
func NewOtaService(ctx context.Context, otaRepo repository.OtaRepository, deviceRepo repository.DeviceRepository) OtaService {
|
||||||
return &otaServiceImpl{
|
return &otaServiceImpl{
|
||||||
|
ctx: ctx,
|
||||||
otaRepo: otaRepo,
|
otaRepo: otaRepo,
|
||||||
deviceRepo: deviceRepo,
|
deviceRepo: deviceRepo,
|
||||||
}
|
}
|
||||||
@@ -32,6 +39,37 @@ func (o *otaServiceImpl) GetUpgradeProgress(ctx context.Context, taskID uint32)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *otaServiceImpl) StopUpgrade(ctx context.Context, taskID uint32) error {
|
func (o *otaServiceImpl) StopUpgrade(ctx context.Context, taskID uint32) error {
|
||||||
//TODO implement me
|
serviceCtx, logger := logs.Trace(ctx, o.ctx, "StopUpgrade")
|
||||||
panic("implement me")
|
|
||||||
|
task, err := o.otaRepo.FindByID(serviceCtx, taskID)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("查找 OTA 任务失败: %v, 任务ID: %d", err, taskID)
|
||||||
|
return fmt.Errorf("查找 OTA 任务失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 幂等性检查:如果任务已处于终态,则直接返回成功
|
||||||
|
if task.IsOver() {
|
||||||
|
logger.Infof("OTA 任务 %d 已处于终态 %s,无需停止", taskID, task.Status)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
task.Status = models.OTATaskStatusStopped
|
||||||
|
task.CompletedAt = &now
|
||||||
|
task.ErrorMessage = "任务被用户手动停止"
|
||||||
|
|
||||||
|
if err := o.otaRepo.Update(serviceCtx, task); err != nil {
|
||||||
|
logger.Errorf("更新 OTA 任务状态失败: %v, 任务ID: %d", err, taskID)
|
||||||
|
return fmt.Errorf("更新 OTA 任务状态失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理相关文件目录
|
||||||
|
dirToRemove := filepath.Join(models.OTADir, fmt.Sprintf("%d", taskID))
|
||||||
|
if err := file.RemoveTempDir(dirToRemove); err != nil {
|
||||||
|
// 文件清理失败不应阻塞主流程,但需要记录日志
|
||||||
|
logger.Warnf("清理 OTA 任务 %d 的文件目录 %s 失败: %v", taskID, dirToRemove, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Infof("OTA 任务 %d 已被成功标记为手动停止", taskID)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package task
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -10,6 +11,7 @@ import (
|
|||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
|
||||||
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/utils/file"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OtaCheckTaskParams 定义了 OTA 检查任务所需的参数。
|
// OtaCheckTaskParams 定义了 OTA 检查任务所需的参数。
|
||||||
@@ -91,6 +93,13 @@ func (t *otaCheckTask) Execute(ctx context.Context) error {
|
|||||||
if err := t.otaRepo.Update(taskCtx, task); err != nil {
|
if err := t.otaRepo.Update(taskCtx, task); err != nil {
|
||||||
// 仅记录错误,不中断整个检查任务,以确保其他超时任务能被处理
|
// 仅记录错误,不中断整个检查任务,以确保其他超时任务能被处理
|
||||||
logger.Errorf("更新超时的OTA任务 #%d 状态失败: %v", task.ID, err)
|
logger.Errorf("更新超时的OTA任务 #%d 状态失败: %v", task.ID, err)
|
||||||
|
} else {
|
||||||
|
// 数据库更新成功后,清理文件
|
||||||
|
logger.Infof("OTA任务 #%d 状态已更新为超时,现在开始清理文件。", task.ID)
|
||||||
|
dirToRemove := filepath.Join(models.OTADir, fmt.Sprintf("%d", task.ID))
|
||||||
|
if removeErr := file.RemoveTempDir(dirToRemove); removeErr != nil {
|
||||||
|
logger.Warnf("清理超时的OTA任务 #%d 的文件目录 %s 失败: %v", task.ID, dirToRemove, removeErr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -160,16 +160,23 @@ type OTATaskStatus string
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
OTATaskStatusPending OTATaskStatus = "待开始" // 任务已创建,等待下发
|
OTATaskStatusPending OTATaskStatus = "待开始" // 任务已创建,等待下发
|
||||||
|
|
||||||
OTATaskStatusInProgress OTATaskStatus = "进行中" // 任务已下发,设备正在处理
|
OTATaskStatusInProgress OTATaskStatus = "进行中" // 任务已下发,设备正在处理
|
||||||
|
|
||||||
OTATaskStatusSuccess OTATaskStatus = "成功" // 设备报告升级成功,新固件已运行
|
OTATaskStatusSuccess OTATaskStatus = "成功" // 设备报告升级成功,新固件已运行
|
||||||
OTATaskStatusAlreadyUpToDate OTATaskStatus = "版本已是最新" // 设备报告版本已是最新,未执行升级
|
OTATaskStatusAlreadyUpToDate OTATaskStatus = "版本已是最新" // 设备报告版本已是最新,未执行升级
|
||||||
|
|
||||||
OTATaskStatusFailedPreCheck OTATaskStatus = "预检失败" // 设备报告升级前检查失败 (如拒绝降级、准备分区失败)
|
OTATaskStatusFailedPreCheck OTATaskStatus = "预检失败" // 设备报告升级前检查失败 (如拒绝降级、准备分区失败)
|
||||||
OTATaskStatusFailedDownload OTATaskStatus = "下载或校验失败" // 设备报告文件下载或校验失败 (包括清单文件和固件文件)
|
OTATaskStatusFailedDownload OTATaskStatus = "下载或校验失败" // 设备报告文件下载或校验失败 (包括清单文件和固件文件)
|
||||||
OTATaskStatusFailedRollback OTATaskStatus = "固件回滚" // 新固件启动失败,设备自动回滚
|
OTATaskStatusFailedRollback OTATaskStatus = "固件回滚" // 新固件启动失败,设备自动回滚
|
||||||
OTATaskStatusTimedOut OTATaskStatus = "超时" // 平台在超时后仍未收到最终报告
|
OTATaskStatusTimedOut OTATaskStatus = "超时" // 平台在超时后仍未收到最终报告
|
||||||
OTATaskStatusPlatformError OTATaskStatus = "平台内部错误" // 平台处理过程中发生的非设备报告错误
|
OTATaskStatusPlatformError OTATaskStatus = "平台内部错误" // 平台处理过程中发生的非设备报告错误
|
||||||
|
OTATaskStatusStopped OTATaskStatus = "手动停止" // 手动停止
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// OTADir 是 OTA 升级相关的临时文件存储目录
|
||||||
|
const OTADir = "ota"
|
||||||
|
|
||||||
// OTATask 记录一次 OTA 升级任务的详细信息
|
// OTATask 记录一次 OTA 升级任务的详细信息
|
||||||
type OTATask struct {
|
type OTATask struct {
|
||||||
// ID 是数据库自增主键,将作为 task_id 在平台与设备间通信
|
// ID 是数据库自增主键,将作为 task_id 在平台与设备间通信
|
||||||
@@ -186,6 +193,15 @@ type OTATask struct {
|
|||||||
FinalReportedVersion string `gorm:"type:varchar(32);comment:任务结束后,设备上报的最终固件版本"`
|
FinalReportedVersion string `gorm:"type:varchar(32);comment:任务结束后,设备上报的最终固件版本"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o OTATask) IsOver() bool {
|
||||||
|
switch o.Status {
|
||||||
|
case OTATaskStatusPending, OTATaskStatusInProgress:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TableName 自定义 GORM 使用的数据库表名
|
// TableName 自定义 GORM 使用的数据库表名
|
||||||
func (OTATask) TableName() string {
|
func (OTATask) TableName() string {
|
||||||
return "ota_tasks"
|
return "ota_tasks"
|
||||||
|
|||||||
Reference in New Issue
Block a user