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.
129 lines
3.7 KiB
129 lines
3.7 KiB
package service
|
|
|
|
import (
|
|
"errors"
|
|
"strconv"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"laic-backend/common"
|
|
"laic-backend/logger"
|
|
"laic-backend/model"
|
|
"laic-backend/tool"
|
|
)
|
|
|
|
type WorkflowService struct{}
|
|
|
|
var DefaultWorkflowService = &WorkflowService{}
|
|
|
|
// WorkflowStateIn 工作流状态上报载荷
|
|
type WorkflowStateIn struct {
|
|
CommandID string `json:"commandId"`
|
|
Type string `json:"type"`
|
|
TaskID string `json:"taskId"`
|
|
MissionID string `json:"missionId"`
|
|
State string `json:"state"`
|
|
Step string `json:"step"`
|
|
ResultCode string `json:"resultCode"`
|
|
}
|
|
|
|
// Upsert 持久化工作流状态(按 command_id 去重更新),并在终态时回写任务执行记录
|
|
func (s *WorkflowService) Upsert(dockID, requestID string, in *WorkflowStateIn) {
|
|
now := time.Now()
|
|
if requestID != "" {
|
|
var cmd model.DeviceCommandLog
|
|
if err := common.DB.Where("dock_id = ? AND request_id = ? AND status IN ?", dockID, requestID, []string{"sent", "acked"}).First(&cmd).Error; err != nil {
|
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
logger.ERROR("按 requestId 查询工作流指令失败", err)
|
|
}
|
|
return
|
|
}
|
|
expected := strconv.FormatInt(cmd.ID, 10)
|
|
if in.CommandID != "" && in.CommandID != expected {
|
|
logger.WARN("工作流状态 commandId 与 requestId 不匹配", dockID, requestID)
|
|
return
|
|
}
|
|
in.CommandID = expected
|
|
}
|
|
|
|
if in.CommandID != "" {
|
|
var existing model.WorkflowState
|
|
err := common.DB.Where("dock_id = ? AND command_id = ?", dockID, in.CommandID).First(&existing).Error
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
id, e := tool.NextID()
|
|
if e == nil {
|
|
common.DB.Create(&model.WorkflowState{
|
|
ID: id,
|
|
DockID: dockID,
|
|
CommandID: in.CommandID,
|
|
Type: in.Type,
|
|
TaskID: in.TaskID,
|
|
MissionID: in.MissionID,
|
|
State: in.State,
|
|
Step: in.Step,
|
|
ResultCode: in.ResultCode,
|
|
UpdatedAt: now,
|
|
})
|
|
}
|
|
} else if err == nil {
|
|
common.DB.Model(&existing).Updates(map[string]any{
|
|
"dock_id": dockID,
|
|
"type": in.Type,
|
|
"task_id": in.TaskID,
|
|
"mission_id": in.MissionID,
|
|
"state": in.State,
|
|
"step": in.Step,
|
|
"result_code": in.ResultCode,
|
|
"updated_at": now,
|
|
})
|
|
}
|
|
}
|
|
|
|
// 回写 task_execution,优先按 command_id 精确关联,兼容旧设备时只回退到最新未终态记录。
|
|
if (in.CommandID != "" || in.TaskID != "") && (in.State == "running" || isTerminalWorkflowState(in.State)) {
|
|
updates := map[string]any{"status": in.State}
|
|
if in.State == "running" {
|
|
updates["start_time"] = now
|
|
} else {
|
|
updates["end_time"] = now
|
|
updates["start_time"] = gorm.Expr("COALESCE(start_time, ?)", now)
|
|
}
|
|
|
|
query := common.DB.Model(&model.TaskExecution{}).
|
|
Where("dock_id = ? AND status NOT IN ('succeeded','failed','cancelled')", dockID)
|
|
if in.CommandID != "" {
|
|
query = query.Where("command_id = ?", in.CommandID)
|
|
} else {
|
|
query = query.Where("task_id = ?", in.TaskID)
|
|
var execution model.TaskExecution
|
|
if err := query.Order("created_at DESC, id DESC").First(&execution).Error; err != nil {
|
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
logger.ERROR("查找任务执行记录失败", err)
|
|
}
|
|
query = nil
|
|
} else {
|
|
query = common.DB.Model(&execution)
|
|
}
|
|
}
|
|
if query != nil {
|
|
if err := query.Updates(updates).Error; err != nil {
|
|
logger.ERROR("回写任务执行状态失败", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 终态回写轨迹(MySQL trajectory_json + TDengine task_trajectory)
|
|
if isTerminalWorkflowState(in.State) {
|
|
DefaultTrajectoryStore.Finalize(dockID, DefaultTelemetryStore.DroneSN(dockID))
|
|
}
|
|
}
|
|
|
|
func isTerminalWorkflowState(state string) bool {
|
|
switch state {
|
|
case "succeeded", "failed", "cancelled":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|