From 8676963bac7f2a71233e6c3babc1adfac63f6a35 Mon Sep 17 00:00:00 2001 From: mac Date: Thu, 23 Jul 2026 12:03:47 +0800 Subject: [PATCH] feat(delivery): add delivery data models and auto-migration Add 8 GORM models for MySQL one-click delivery: DeploymentTarget, ResourceQuota, DeliveryTask, ResourceReservation, ResourceUsage, MySQLInstance, ExecutionJob, TaskEvent. Register them in AutoMigrate. Relates-to: #97 --- server/internal/database/database.go | 8 ++ server/internal/model/delivery.go | 132 +++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 server/internal/model/delivery.go diff --git a/server/internal/database/database.go b/server/internal/database/database.go index 29829cf..0a296c5 100644 --- a/server/internal/database/database.go +++ b/server/internal/database/database.go @@ -23,5 +23,13 @@ func AutoMigrate(db *gorm.DB) error { &model.Deployment{}, &model.DeploymentEvent{}, &model.AuditLog{}, + &model.DeploymentTarget{}, + &model.ResourceQuota{}, + &model.DeliveryTask{}, + &model.ResourceReservation{}, + &model.MySQLInstance{}, + &model.ResourceUsage{}, + &model.ExecutionJob{}, + &model.TaskEvent{}, ) } diff --git a/server/internal/model/delivery.go b/server/internal/model/delivery.go new file mode 100644 index 0000000..6c7262d --- /dev/null +++ b/server/internal/model/delivery.go @@ -0,0 +1,132 @@ +package model + +import "time" + +const ( + TaskPending = "pending" + TaskValidating = "validating" + TaskDispatching = "dispatching" + TaskRunning = "running" + TaskRegistering = "registering" + TaskFinished = "finished" + TaskRegisterFailed = "register_failed" + TaskExecutionFailed = "execution_failed" + TaskValidationFailed = "validation_failed" + TaskCanceling = "canceling" + TaskCanceled = "canceled" +) + +type DeploymentTarget struct { + ID uint64 `gorm:"primaryKey" json:"id"` + Name string `gorm:"size:128;not null;uniqueIndex" json:"name"` + TargetType string `gorm:"size:32;not null;index" json:"target_type"` + AWXInventoryID uint64 `gorm:"not null" json:"awx_inventory_id"` + AWXTemplateID uint64 `gorm:"not null" json:"awx_template_id"` + Enabled bool `gorm:"not null;default:true;index" json:"enabled"` + Metadata string `gorm:"type:json" json:"metadata"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type ResourceQuota struct { + ID uint64 `gorm:"primaryKey" json:"id"` + BusinessLineID uint64 `gorm:"not null;uniqueIndex:idx_resource_quota_scope,priority:1" json:"business_line_id"` + TargetID uint64 `gorm:"not null;uniqueIndex:idx_resource_quota_scope,priority:2" json:"target_id"` + CPUMilli int64 `gorm:"not null" json:"cpu_milli"` + MemoryMi int64 `gorm:"not null" json:"memory_mi"` + StorageGi int64 `gorm:"not null" json:"storage_gi"` + InstanceLimit int64 `gorm:"not null" json:"instance_limit"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type DeliveryTask struct { + ID string `gorm:"size:36;primaryKey" json:"id"` + BusinessLineID uint64 `gorm:"not null;index" json:"business_line_id"` + RequestedBy uint64 `gorm:"not null;index" json:"requested_by"` + TargetType string `gorm:"size:32;not null" json:"target_type"` + TargetID uint64 `gorm:"not null;index" json:"target_id"` + Namespace string `gorm:"size:63;not null;index" json:"namespace"` + InstanceName string `gorm:"size:63;not null" json:"instance_name"` + TargetHost string `gorm:"size:128" json:"target_host,omitempty"` + TargetHostIP string `gorm:"size:64" json:"target_host_ip,omitempty"` + MySQLPort int `gorm:"column:mysql_port;not null;default:3307" json:"mysql_port"` + Status string `gorm:"size:32;not null;index" json:"status"` + ImmutablePayload string `gorm:"type:json;not null" json:"immutable_payload"` + PayloadHash string `gorm:"size:64;not null" json:"payload_hash"` + IdempotencyKey string `gorm:"size:128;not null;uniqueIndex" json:"idempotency_key"` + ErrorMessage string `gorm:"type:text" json:"error_message,omitempty"` + CreatedAt time.Time `gorm:"index" json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + StartedAt *time.Time `json:"started_at,omitempty"` + FinishedAt *time.Time `json:"finished_at,omitempty"` +} + +type ResourceReservation struct { + ID uint64 `gorm:"primaryKey" json:"id"` + TaskID string `gorm:"size:36;not null;uniqueIndex" json:"task_id"` + BusinessLineID uint64 `gorm:"not null;index" json:"business_line_id"` + TargetID uint64 `gorm:"not null;index" json:"target_id"` + CPUMilli int64 `gorm:"not null" json:"cpu_milli"` + MemoryMi int64 `gorm:"not null" json:"memory_mi"` + StorageGi int64 `gorm:"not null" json:"storage_gi"` + InstanceCount int64 `gorm:"not null;default:1" json:"instance_count"` + Status string `gorm:"size:32;not null;index" json:"status"` + ExpiresAt time.Time `gorm:"not null;index" json:"expires_at"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type ResourceUsage struct { + ID uint64 `gorm:"primaryKey" json:"id"` + TaskID string `gorm:"size:36;not null;uniqueIndex" json:"task_id"` + InstanceID uint64 `gorm:"not null;uniqueIndex" json:"instance_id"` + BusinessLineID uint64 `gorm:"not null;index" json:"business_line_id"` + TargetID uint64 `gorm:"not null;index" json:"target_id"` + CPUMilli int64 `gorm:"not null" json:"cpu_milli"` + MemoryMi int64 `gorm:"not null" json:"memory_mi"` + StorageGi int64 `gorm:"not null" json:"storage_gi"` + InstanceCount int64 `gorm:"not null;default:1" json:"instance_count"` + Status string `gorm:"size:32;not null;index" json:"status"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + ReleasedAt *time.Time `json:"released_at,omitempty"` +} + +type MySQLInstance struct { + ID uint64 `gorm:"primaryKey" json:"id"` + TaskID string `gorm:"size:36;not null;uniqueIndex" json:"task_id"` + BusinessLineID uint64 `gorm:"not null;index" json:"business_line_id"` + TargetID uint64 `gorm:"not null;index;uniqueIndex:idx_mysql_target_name,priority:1" json:"target_id"` + Namespace string `gorm:"size:63;not null" json:"namespace"` + Name string `gorm:"size:63;not null;uniqueIndex:idx_mysql_target_name,priority:2" json:"name"` + NodeName string `gorm:"size:128" json:"node_name"` + Host string `gorm:"size:255;not null" json:"host"` + Port int `gorm:"not null;default:3306" json:"port"` + Version string `gorm:"size:32;not null" json:"version"` + Status string `gorm:"size:32;not null;index" json:"status"` + CloudDMID string `gorm:"size:128" json:"clouddm_id,omitempty"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type ExecutionJob struct { + ID uint64 `gorm:"primaryKey" json:"id"` + TaskID string `gorm:"size:36;not null;uniqueIndex" json:"task_id"` + IdempotencyKey string `gorm:"size:128;not null;uniqueIndex" json:"idempotency_key"` + ExecutorJobID string `gorm:"size:128;not null;uniqueIndex" json:"executor_job_id"` + Status string `gorm:"size:32;not null;index" json:"status"` + StartedAt *time.Time `json:"started_at,omitempty"` + FinishedAt *time.Time `json:"finished_at,omitempty"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type TaskEvent struct { + ID uint64 `gorm:"primaryKey" json:"id"` + TaskID string `gorm:"size:36;not null;index" json:"task_id"` + FromState string `gorm:"size:32;not null" json:"from_state"` + ToState string `gorm:"size:32;not null" json:"to_state"` + Message string `gorm:"type:text" json:"message"` + CreatedAt time.Time `gorm:"index" json:"created_at"` +}