From db06e9996769fd7288dbdcf987d2327ecc0aec88 Mon Sep 17 00:00:00 2001 From: dailz Date: Mon, 20 Apr 2026 10:37:58 +0800 Subject: [PATCH] feat(model): add task defaults, job queries, and refine file/task DTOs Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- internal/model/application.go | 81 +++++++------- internal/model/cluster.go | 4 +- internal/model/file.go | 200 +++++++++++++++++----------------- internal/model/job.go | 35 +++++- internal/model/task.go | 178 +++++++++++++++++++++--------- 5 files changed, 299 insertions(+), 199 deletions(-) diff --git a/internal/model/application.go b/internal/model/application.go index 4de2d42..c6a52bd 100644 --- a/internal/model/application.go +++ b/internal/model/application.go @@ -5,7 +5,7 @@ import ( "time" ) -// Parameter type constants for ParameterSchema.Type. +// 参数类型常量 const ( ParamTypeString = "string" ParamTypeInteger = "integer" @@ -15,62 +15,59 @@ const ( ParamTypeBoolean = "boolean" ) -// Application represents a parameterized application definition for HPC job submission. +// Application 表示一个参数化的 HPC 应用定义。 type Application struct { - ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` - Name string `gorm:"uniqueIndex;size:255;not null" json:"name"` - Description string `gorm:"type:text" json:"description,omitempty"` - Icon string `gorm:"size:255" json:"icon,omitempty"` - Category string `gorm:"size:255" json:"category,omitempty"` - ScriptTemplate string `gorm:"type:text;not null" json:"script_template"` - Parameters json.RawMessage `gorm:"type:json" json:"parameters,omitempty"` - Scope string `gorm:"size:50;default:'system'" json:"scope,omitempty"` - CreatedBy int64 `json:"created_by,omitempty"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键 + Name string `gorm:"uniqueIndex;size:255;not null" json:"name"` // 应用名称(唯一) + Description string `gorm:"type:text" json:"description,omitempty"` // 应用描述 + Icon string `gorm:"size:255" json:"icon,omitempty"` // 图标 + Category string `gorm:"size:255" json:"category,omitempty"` // 分类 + ScriptTemplate string `gorm:"type:text;not null" json:"script_template"` // 脚本模板 + Parameters json.RawMessage `gorm:"type:json" json:"parameters,omitempty"` // 参数表单JSON + Scope string `gorm:"size:50;default:'system'" json:"scope,omitempty"` // 作用域(system/user) + CreatedBy int64 `json:"created_by,omitempty"` // 创建者ID + CreatedAt time.Time `json:"created_at"` // 创建时间 + UpdatedAt time.Time `json:"updated_at"` // 更新时间 } func (Application) TableName() string { return "hpc_applications" } -// ParameterSchema defines a single parameter in an application's form schema. +// ParameterSchema 定义应用表单中单个参数的格式。 type ParameterSchema struct { - Name string `json:"name"` - Label string `json:"label,omitempty"` - Type string `json:"type"` - Required bool `json:"required,omitempty"` - Default string `json:"default,omitempty"` - Options []string `json:"options,omitempty"` - Description string `json:"description,omitempty"` + Name string `json:"name"` // 参数名 + Label string `json:"label,omitempty"` // 显示名称 + Type string `json:"type"` // 参数类型 + Required bool `json:"required,omitempty"` // 是否必填 + Default string `json:"default,omitempty"` // 默认值 + Options []string `json:"options,omitempty"` // 枚举选项列表 + Description string `json:"description,omitempty"` // 参数说明 } -// CreateApplicationRequest is the DTO for creating a new application. +// CreateApplicationRequest 是创建应用的 API 请求。 type CreateApplicationRequest struct { - Name string `json:"name" binding:"required"` - Description string `json:"description,omitempty"` - Icon string `json:"icon,omitempty"` - Category string `json:"category,omitempty"` - ScriptTemplate string `json:"script_template" binding:"required"` - Parameters json.RawMessage `json:"parameters,omitempty"` - Scope string `json:"scope,omitempty"` + Name string `json:"name" binding:"required"` // 应用名称(必填) + Description string `json:"description,omitempty"` // 应用描述 + Icon string `json:"icon,omitempty"` // 图标 + Category string `json:"category,omitempty"` // 分类 + ScriptTemplate string `json:"script_template" binding:"required"` // 脚本模板(必填) + Parameters json.RawMessage `json:"parameters,omitempty"` // 参数表单JSON + Scope string `json:"scope,omitempty"` // 作用域 } -// UpdateApplicationRequest is the DTO for updating an existing application. -// All fields are optional. Parameters uses *json.RawMessage to distinguish -// between "not provided" (nil) and "set to empty" (non-nil). +// UpdateApplicationRequest 是更新应用的 API 请求。所有字段可选。 type UpdateApplicationRequest struct { - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - Icon *string `json:"icon,omitempty"` - Category *string `json:"category,omitempty"` - ScriptTemplate *string `json:"script_template,omitempty"` - Parameters *json.RawMessage `json:"parameters,omitempty"` - Scope *string `json:"scope,omitempty"` + Name *string `json:"name,omitempty"` // 应用名称 + Description *string `json:"description,omitempty"` // 应用描述 + Icon *string `json:"icon,omitempty"` // 图标 + Category *string `json:"category,omitempty"` // 分类 + ScriptTemplate *string `json:"script_template,omitempty"` // 脚本模板 + Parameters *json.RawMessage `json:"parameters,omitempty"` // 参数表单JSON + Scope *string `json:"scope,omitempty"` // 作用域 } -// ApplicationSubmitRequest is the DTO for submitting a job from an application. -// ApplicationID is parsed from the URL :id parameter, not included in the body. +// ApplicationSubmitRequest 是通过应用提交作业的 API 请求。 type ApplicationSubmitRequest struct { - Values map[string]string `json:"values" binding:"required"` + Values map[string]string `json:"values" binding:"required"` // 脚本业务参数键值对 } diff --git a/internal/model/cluster.go b/internal/model/cluster.go index 470df00..cf352fc 100644 --- a/internal/model/cluster.go +++ b/internal/model/cluster.go @@ -1,6 +1,6 @@ package model -// NodeResponse is the API response for a node. +// NodeResponse 是节点查询 API 响应。 type NodeResponse struct { // Identity Name string `json:"name"` // 节点主机名 @@ -37,7 +37,7 @@ type NodeResponse struct { ActiveFeatures string `json:"active_features,omitempty"` // 当前生效的特性标签 (只读) } -// PartitionResponse is the API response for a partition. +// PartitionResponse 是分区查询 API 响应。 type PartitionResponse struct { // Identity Name string `json:"name"` // 分区名称 diff --git a/internal/model/file.go b/internal/model/file.go index f80fc70..9d58ab8 100644 --- a/internal/model/file.go +++ b/internal/model/file.go @@ -9,159 +9,159 @@ import ( "gorm.io/gorm" ) -// FileBlob represents a physical file stored in MinIO, deduplicated by SHA256. +// FileBlob 表示存储在 MinIO 中的物理文件,按 SHA256 去重。 type FileBlob struct { - ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` - SHA256 string `gorm:"uniqueIndex;size:64;not null" json:"sha256"` - MinioKey string `gorm:"size:255;not null" json:"minio_key"` - FileSize int64 `gorm:"not null" json:"file_size"` - MimeType string `gorm:"size:255" json:"mime_type"` - RefCount int `gorm:"not null;default:0" json:"ref_count"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键 + SHA256 string `gorm:"uniqueIndex;size:64;not null" json:"sha256"` // 文件哈希 + MinioKey string `gorm:"size:255;not null" json:"minio_key"` // MinIO对象键 + FileSize int64 `gorm:"not null" json:"file_size"` // 文件大小(字节) + MimeType string `gorm:"size:255" json:"mime_type"` // MIME类型 + RefCount int `gorm:"not null;default:0" json:"ref_count"` // 引用计数 + CreatedAt time.Time `json:"created_at"` // 创建时间 + UpdatedAt time.Time `json:"updated_at"` // 更新时间 } func (FileBlob) TableName() string { return "hpc_file_blobs" } -// File represents a logical file visible to users, backed by a FileBlob. +// File 表示用户可见的逻辑文件,底层由 FileBlob 存储。 type File struct { - ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` - Name string `gorm:"size:255;not null" json:"name"` - FolderID *int64 `gorm:"index" json:"folder_id,omitempty"` - BlobSHA256 string `gorm:"size:64;not null" json:"blob_sha256"` - UserID *int64 `gorm:"index" json:"user_id,omitempty"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"` + ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键 + Name string `gorm:"size:255;not null" json:"name"` // 文件名 + FolderID *int64 `gorm:"index" json:"folder_id,omitempty"` // 所属文件夹ID + BlobSHA256 string `gorm:"size:64;not null" json:"blob_sha256"` // 关联的文件blob哈希 + UserID *int64 `gorm:"index" json:"user_id,omitempty"` // 所有者ID + CreatedAt time.Time `json:"created_at"` // 创建时间 + UpdatedAt time.Time `json:"updated_at"` // 更新时间 + DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"` // 软删除时间 } func (File) TableName() string { return "hpc_files" } -// Folder represents a directory in the virtual file system. +// Folder 表示虚拟文件系统中的目录。 type Folder struct { - ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` - Name string `gorm:"size:255;not null" json:"name"` - ParentID *int64 `gorm:"index" json:"parent_id,omitempty"` - Path string `gorm:"uniqueIndex;size:768;not null" json:"path"` - UserID *int64 `gorm:"index" json:"user_id,omitempty"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"` + ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键 + Name string `gorm:"size:255;not null" json:"name"` // 文件夹名称 + ParentID *int64 `gorm:"index" json:"parent_id,omitempty"` // 父文件夹ID + Path string `gorm:"uniqueIndex;size:768;not null" json:"path"` // 完整路径(唯一) + UserID *int64 `gorm:"index" json:"user_id,omitempty"` // 所有者ID + CreatedAt time.Time `json:"created_at"` // 创建时间 + UpdatedAt time.Time `json:"updated_at"` // 更新时间 + DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"` // 软删除时间 } func (Folder) TableName() string { return "hpc_folders" } -// UploadSession represents an in-progress chunked upload. -// State transitions: pending→uploading, pending→completed(zero-byte), uploading→merging, +// UploadSession 表示一个进行中的分块上传会话。 +// 状态转换: pending→uploading, pending→completed(零字节), uploading→merging, // uploading→cancelled, merging→completed, merging→failed, any→expired type UploadSession struct { - ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` - FileName string `gorm:"size:255;not null" json:"file_name"` - FileSize int64 `gorm:"not null" json:"file_size"` - ChunkSize int64 `gorm:"not null" json:"chunk_size"` - TotalChunks int `gorm:"not null" json:"total_chunks"` - SHA256 string `gorm:"size:64;not null" json:"sha256"` - FolderID *int64 `gorm:"index" json:"folder_id,omitempty"` - Status string `gorm:"size:20;not null;default:pending" json:"status"` - MinioPrefix string `gorm:"size:255;not null" json:"minio_prefix"` - MimeType string `gorm:"size:255;default:'application/octet-stream'" json:"mime_type"` - UserID *int64 `gorm:"index" json:"user_id,omitempty"` - ExpiresAt time.Time `gorm:"not null" json:"expires_at"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键 + FileName string `gorm:"size:255;not null" json:"file_name"` // 文件名 + FileSize int64 `gorm:"not null" json:"file_size"` // 文件总大小 + ChunkSize int64 `gorm:"not null" json:"chunk_size"` // 分块大小 + TotalChunks int `gorm:"not null" json:"total_chunks"` // 总分块数 + SHA256 string `gorm:"size:64;not null" json:"sha256"` // 文件哈希 + FolderID *int64 `gorm:"index" json:"folder_id,omitempty"` // 目标文件夹ID + Status string `gorm:"size:20;not null;default:pending" json:"status"` // 会话状态 + MinioPrefix string `gorm:"size:255;not null" json:"minio_prefix"` // MinIO存储前缀 + MimeType string `gorm:"size:255;default:'application/octet-stream'" json:"mime_type"` // MIME类型 + UserID *int64 `gorm:"index" json:"user_id,omitempty"` // 上传者ID + ExpiresAt time.Time `gorm:"not null" json:"expires_at"` // 过期时间 + CreatedAt time.Time `json:"created_at"` // 创建时间 + UpdatedAt time.Time `json:"updated_at"` // 更新时间 } func (UploadSession) TableName() string { return "hpc_upload_sessions" } -// UploadChunk represents a single chunk of an upload session. +// UploadChunk 表示上传会话中的单个分块。 type UploadChunk struct { - ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` - SessionID int64 `gorm:"not null;uniqueIndex:idx_session_chunk" json:"session_id"` - ChunkIndex int `gorm:"not null;uniqueIndex:idx_session_chunk" json:"chunk_index"` - MinioKey string `gorm:"size:255;not null" json:"minio_key"` - SHA256 string `gorm:"size:64" json:"sha256,omitempty"` - Size int64 `gorm:"not null" json:"size"` - Status string `gorm:"size:20;not null;default:pending" json:"status"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键 + SessionID int64 `gorm:"not null;uniqueIndex:idx_session_chunk" json:"session_id"` // 所属会话ID + ChunkIndex int `gorm:"not null;uniqueIndex:idx_session_chunk" json:"chunk_index"` // 分块序号 + MinioKey string `gorm:"size:255;not null" json:"minio_key"` // MinIO对象键 + SHA256 string `gorm:"size:64" json:"sha256,omitempty"` // 分块哈希 + Size int64 `gorm:"not null" json:"size"` // 分块大小 + Status string `gorm:"size:20;not null;default:pending" json:"status"` // 分块状态 + CreatedAt time.Time `json:"created_at"` // 创建时间 + UpdatedAt time.Time `json:"updated_at"` // 更新时间 } func (UploadChunk) TableName() string { return "hpc_upload_chunks" } -// InitUploadRequest is the DTO for initiating a chunked upload. +// InitUploadRequest 是初始化分块上传的 API 请求。 type InitUploadRequest struct { - FileName string `json:"file_name" binding:"required"` - FileSize int64 `json:"file_size" binding:"required"` - SHA256 string `json:"sha256" binding:"required"` - FolderID *int64 `json:"folder_id,omitempty"` - ChunkSize *int64 `json:"chunk_size,omitempty"` - MimeType string `json:"mime_type,omitempty"` + FileName string `json:"file_name" binding:"required"` // 文件名(必填) + FileSize int64 `json:"file_size" binding:"required"` // 文件大小(必填) + SHA256 string `json:"sha256" binding:"required"` // 文件哈希(必填) + FolderID *int64 `json:"folder_id,omitempty"` // 目标文件夹ID + ChunkSize *int64 `json:"chunk_size,omitempty"` // 分块大小 + MimeType string `json:"mime_type,omitempty"` // MIME类型 } -// CreateFolderRequest is the DTO for creating a new folder. +// CreateFolderRequest 是创建文件夹的 API 请求。 type CreateFolderRequest struct { - Name string `json:"name" binding:"required"` - ParentID *int64 `json:"parent_id,omitempty"` + Name string `json:"name" binding:"required"` // 文件夹名称(必填) + ParentID *int64 `json:"parent_id,omitempty"` // 父文件夹ID } -// UploadSessionResponse is the DTO returned when creating/querying an upload session. +// UploadSessionResponse 是上传会话的 API 响应。 type UploadSessionResponse struct { - ID int64 `json:"id"` - FileName string `json:"file_name"` - FileSize int64 `json:"file_size"` - ChunkSize int64 `json:"chunk_size"` - TotalChunks int `json:"total_chunks"` - SHA256 string `json:"sha256"` - Status string `json:"status"` - UploadedChunks []int `json:"uploaded_chunks"` - ExpiresAt time.Time `json:"expires_at"` - CreatedAt time.Time `json:"created_at"` + ID int64 `json:"id"` // 主键 + FileName string `json:"file_name"` // 文件名 + FileSize int64 `json:"file_size"` // 文件大小 + ChunkSize int64 `json:"chunk_size"` // 分块大小 + TotalChunks int `json:"total_chunks"` // 总分块数 + SHA256 string `json:"sha256"` // 文件哈希 + Status string `json:"status"` // 会话状态 + UploadedChunks []int `json:"uploaded_chunks"` // 已上传的分块序号列表 + ExpiresAt time.Time `json:"expires_at"` // 过期时间 + CreatedAt time.Time `json:"created_at"` // 创建时间 } -// FileResponse is the DTO for a file in API responses. +// FileResponse 是文件 API 响应。 type FileResponse struct { - ID int64 `json:"id"` - Name string `json:"name"` - FolderID *int64 `json:"folder_id"` - FolderPath *string `json:"folder_path"` - UserID *int64 `json:"user_id"` - Size int64 `json:"size"` - MimeType string `json:"mime_type"` - SHA256 string `json:"sha256"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + ID int64 `json:"id"` // 主键 + Name string `json:"name"` // 文件名 + FolderID *int64 `json:"folder_id"` // 所属文件夹ID + FolderPath *string `json:"folder_path"` // 所属文件夹路径 + UserID *int64 `json:"user_id"` // 所有者ID + Size int64 `json:"size"` // 文件大小 + MimeType string `json:"mime_type"` // MIME类型 + SHA256 string `json:"sha256"` // 文件哈希 + CreatedAt time.Time `json:"created_at"` // 创建时间 + UpdatedAt time.Time `json:"updated_at"` // 更新时间 } -// FolderResponse is the DTO for a folder in API responses. +// FolderResponse 是文件夹 API 响应。 type FolderResponse struct { - ID int64 `json:"id"` - Name string `json:"name"` - ParentID *int64 `json:"parent_id,omitempty"` - Path string `json:"path"` - FileCount int64 `json:"file_count"` - SubFolderCount int64 `json:"subfolder_count"` - CreatedAt time.Time `json:"created_at"` + ID int64 `json:"id"` // 主键 + Name string `json:"name"` // 文件夹名称 + ParentID *int64 `json:"parent_id,omitempty"` // 父文件夹ID + Path string `json:"path"` // 完整路径 + FileCount int64 `json:"file_count"` // 文件数量 + SubFolderCount int64 `json:"subfolder_count"` // 子文件夹数量 + CreatedAt time.Time `json:"created_at"` // 创建时间 } -// ListFilesResponse is the paginated response for listing files. +// ListFilesResponse 是文件列表分页响应。 type ListFilesResponse struct { - Files []FileResponse `json:"files"` - Total int64 `json:"total"` - Page int `json:"page"` - PageSize int `json:"page_size"` + Files []FileResponse `json:"files"` // 文件列表 + Total int64 `json:"total"` // 总数 + Page int `json:"page"` // 页码 + PageSize int `json:"page_size"` // 每页条数 } -// ValidateFileName rejects empty, "..", "/", "\", null bytes, control chars, leading/trailing spaces. +// ValidateFileName 校验文件名:禁止空值、"..", "/", "\", null字节、控制字符、首尾空格。 func ValidateFileName(name string) error { if name == "" { return fmt.Errorf("file name cannot be empty") @@ -186,7 +186,7 @@ func ValidateFileName(name string) error { return nil } -// ValidateFolderName rejects same as ValidateFileName plus ".". +// ValidateFolderName 校验文件夹名:在 ValidateFileName 基础上额外禁止 "."。 func ValidateFolderName(name string) error { if name == "." { return fmt.Errorf("folder name cannot be '.'") diff --git a/internal/model/job.go b/internal/model/job.go index dcc282e..9fbc0f2 100644 --- a/internal/model/job.go +++ b/internal/model/job.go @@ -1,19 +1,42 @@ package model -// SubmitJobRequest is the API request for submitting a job. +// SubmitJobRequest 是提交作业的 API 请求。 type SubmitJobRequest struct { Script string `json:"script"` // 作业脚本内容 Partition string `json:"partition,omitempty"` // 提交到的分区 QOS string `json:"qos,omitempty"` // 使用的 QOS 策略 CPUs int32 `json:"cpus,omitempty"` // 请求的 CPU 核数 - Memory string `json:"memory,omitempty"` // 请求的内存大小 + Memory string `json:"memory,omitempty"` // Deprecated: Use MemoryPerNode or MemoryPerCpu instead TimeLimit string `json:"time_limit,omitempty"` // 运行时间限制 (分钟) JobName string `json:"job_name,omitempty"` // 作业名称 Environment map[string]string `json:"environment,omitempty"` // 环境变量键值对 WorkDir string `json:"work_dir,omitempty"` // 作业工作目录 + + MemoryPerNode *int64 `json:"memory_per_node,omitempty"` // 每节点内存(MB) + MemoryPerCpu *int64 `json:"memory_per_cpu,omitempty"` // 每CPU内存(MB) + Nodes *string `json:"nodes,omitempty"` // 请求的节点数(支持范围如"2-4") + Tasks *int32 `json:"tasks,omitempty"` // 任务数 + CpusPerTask *int32 `json:"cpus_per_task,omitempty"` // 每任务CPU核数 + Constraints *string `json:"constraints,omitempty"` // 节点特性约束 + Reservation *string `json:"reservation,omitempty"` // 预约名称 + Account *string `json:"account,omitempty"` // 计费账户 + Nice *int32 `json:"nice,omitempty"` // nice调整值 + MailType *string `json:"mail_type,omitempty"` // 邮件通知类型(逗号分隔) + MailUser *string `json:"mail_user,omitempty"` // 邮件地址 + StandardOutput *string `json:"standard_output,omitempty"` // 标准输出路径 + StandardError *string `json:"standard_error,omitempty"` // 标准错误路径 + StandardInput *string `json:"standard_input,omitempty"` // 标准输入路径 + RequiredNodes *string `json:"required_nodes,omitempty"` // 指定运行的节点(逗号分隔) + ExcludedNodes *string `json:"excluded_nodes,omitempty"` // 排除的节点(逗号分隔) + BeginTime *int64 `json:"begin_time,omitempty"` // 最早开始时间(Unix时间戳) + Deadline *int64 `json:"deadline,omitempty"` // 截止时间(Unix时间戳) + Array *string `json:"array,omitempty"` // 数组作业规格 + Dependency *string `json:"dependency,omitempty"` // 作业依赖关系 + Requeue *bool `json:"requeue,omitempty"` // 失败后是否重新排队 + KillOnNodeFail *bool `json:"kill_on_node_fail,omitempty"` // 节点故障时是否终止作业 } -// JobResponse is the API response for a job. +// JobResponse 是作业查询 API 响应。 type JobResponse struct { // Identity JobID int32 `json:"job_id"` // Slurm 作业 ID @@ -59,7 +82,7 @@ type JobResponse struct { ArrayTaskID *int32 `json:"array_task_id,omitempty"` // 数组作业中的子任务 ID } -// JobListResponse is the paginated response for job listings. +// JobListResponse 是作业列表分页响应。 type JobListResponse struct { Jobs []JobResponse `json:"jobs"` // 作业列表 Total int `json:"total"` // 符合条件的作业总数 @@ -67,13 +90,13 @@ type JobListResponse struct { PageSize int `json:"page_size"` // 每页条数 } -// JobListQuery contains pagination parameters for active job listing. +// JobListQuery 是活跃作业列表查询参数。 type JobListQuery struct { Page int `form:"page,default=1" json:"page,omitempty"` // 页码 (从 1 开始) PageSize int `form:"page_size,default=20" json:"page_size,omitempty"` // 每页条数 } -// JobHistoryQuery contains query parameters for job history. +// JobHistoryQuery 是作业历史查询参数。 type JobHistoryQuery struct { Users string `form:"users" json:"users,omitempty"` // 按用户名过滤 (逗号分隔) StartTime string `form:"start_time" json:"start_time,omitempty"` // 作业开始时间下限 (Unix 时间戳) diff --git a/internal/model/task.go b/internal/model/task.go index 36aaa78..0232fcf 100644 --- a/internal/model/task.go +++ b/internal/model/task.go @@ -7,7 +7,7 @@ import ( "gorm.io/gorm" ) -// Task status constants. +// 任务状态常量 const ( TaskStatusSubmitted = "submitted" TaskStatusPreparing = "preparing" @@ -19,75 +19,155 @@ const ( TaskStatusFailed = "failed" ) -// Task step constants for step-level retry tracking. +// 任务步骤常量,用于步骤级重试追踪 const ( TaskStepPreparing = "preparing" TaskStepDownloading = "downloading" TaskStepSubmitting = "submitting" ) -// Task represents an HPC task submitted through the application framework. +// Task 表示通过应用框架提交的 HPC 任务记录。 type Task struct { - ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` - TaskName string `gorm:"size:255" json:"task_name"` - AppID int64 `json:"app_id"` - AppName string `gorm:"size:255" json:"app_name"` - Status string `json:"status"` - CurrentStep string `json:"current_step"` - RetryCount int `json:"retry_count"` - Values json.RawMessage `gorm:"type:text" json:"values,omitempty"` - InputFileIDs json.RawMessage `json:"input_file_ids" gorm:"column:input_file_ids;type:text"` - Script string `json:"script,omitempty"` - SlurmJobID *int32 `json:"slurm_job_id,omitempty"` - WorkDir string `json:"work_dir,omitempty"` - Partition string `json:"partition,omitempty"` - ErrorMessage string `json:"error_message,omitempty"` - UserID string `json:"user_id"` - SubmittedAt time.Time `json:"submitted_at"` - 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"` - DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"` + ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键 + TaskName string `gorm:"size:255" json:"task_name"` // 任务名称 + AppID int64 `json:"app_id"` // 所属应用ID + AppName string `gorm:"size:255" json:"app_name"` // 应用名称 + Status string `json:"status"` // 任务状态 + CurrentStep string `json:"current_step"` // 当前执行步骤 + RetryCount int `json:"retry_count"` // 重试次数 + Values json.RawMessage `gorm:"type:text" json:"values,omitempty"` // 业务参数JSON + InputFileIDs json.RawMessage `json:"input_file_ids" gorm:"column:input_file_ids;type:text"` // 输入文件ID列表JSON + Script string `json:"script,omitempty"` // 渲染后的脚本内容 + SlurmJobID *int32 `json:"slurm_job_id,omitempty"` // Slurm作业ID + WorkDir string `json:"work_dir,omitempty"` // 工作目录 + Partition string `json:"partition,omitempty"` // 提交到的分区(空字符串表示未设置) + ErrorMessage string `json:"error_message,omitempty"` // 错误信息 + UserID string `json:"user_id"` // 提交用户ID + SubmittedAt time.Time `json:"submitted_at"` // 提交时间 + 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"` // 更新时间 + DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"` // 软删除时间 + Cpus *int32 // 请求的CPU核数 + MemoryPerNode *int64 // 每节点内存(MB) + MemoryPerCpu *int64 // 每CPU内存(MB) + TimeLimit *int32 // 运行时间限制(分钟) + QOS *string // 服务质量策略 + JobName *string // 作业名称 + Nodes *string // 请求的节点数(支持范围如"2-4") + Tasks *int32 // 任务数 + CpusPerTask *int32 // 每任务CPU核数 + Constraints *string // 节点特性约束 + Reservation *string // 预约名称 + Account *string // 计费账户 + Nice *int32 // nice调整值 + MailType *string // 邮件通知类型(逗号分隔) + MailUser *string // 邮件地址 + StandardOutput *string // 标准输出路径 + StandardError *string // 标准错误路径 + StandardInput *string // 标准输入路径 + RequiredNodes *string // 指定运行的节点(逗号分隔) + ExcludedNodes *string // 排除的节点(逗号分隔) + BeginTime *int64 // 最早开始时间(Unix时间戳) + Deadline *int64 // 截止时间(Unix时间戳) + Array *string // 数组作业规格 + Dependency *string // 作业依赖关系 + Requeue *bool // 失败后是否重新排队 + KillOnNodeFail *bool // 节点故障时是否终止作业 } func (Task) TableName() string { return "hpc_tasks" } -// CreateTaskRequest is the DTO for creating a new task. +// CreateTaskRequest 是创建任务的 API 请求。 type CreateTaskRequest struct { - AppID int64 `json:"app_id" binding:"required"` - TaskName string `json:"task_name"` - Values map[string]string `json:"values"` - InputFileIDs []int64 `json:"file_ids"` + AppID int64 `json:"app_id" binding:"required"` // 所属应用ID(必填) + TaskName string `json:"task_name"` // 任务名称 + Values map[string]string `json:"values"` // 脚本业务参数键值对 + InputFileIDs []int64 `json:"file_ids"` // 输入文件ID列表 + Partition *string `json:"partition,omitempty"` // 提交到的分区 + Cpus *int32 `json:"cpus,omitempty"` // 请求的CPU核数 + MemoryPerNode *int64 `json:"memory_per_node,omitempty"` // 每节点内存(MB) + MemoryPerCpu *int64 `json:"memory_per_cpu,omitempty"` // 每CPU内存(MB) + TimeLimit *int32 `json:"time_limit,omitempty"` // 运行时间限制(分钟) + QOS *string `json:"qos,omitempty"` // 服务质量策略 + JobName *string `json:"job_name,omitempty"` // 作业名称 + Nodes *string `json:"nodes,omitempty"` // 请求的节点数(支持范围如"2-4") + Tasks *int32 `json:"tasks,omitempty"` // 任务数 + CpusPerTask *int32 `json:"cpus_per_task,omitempty"` // 每任务CPU核数 + Constraints *string `json:"constraints,omitempty"` // 节点特性约束 + Reservation *string `json:"reservation,omitempty"` // 预约名称 + Account *string `json:"account,omitempty"` // 计费账户 + Nice *int32 `json:"nice,omitempty"` // nice调整值 + MailType *string `json:"mail_type,omitempty"` // 邮件通知类型(逗号分隔) + MailUser *string `json:"mail_user,omitempty"` // 邮件地址 + StandardOutput *string `json:"standard_output,omitempty"` // 标准输出路径 + StandardError *string `json:"standard_error,omitempty"` // 标准错误路径 + StandardInput *string `json:"standard_input,omitempty"` // 标准输入路径 + RequiredNodes *string `json:"required_nodes,omitempty"` // 指定运行的节点(逗号分隔) + ExcludedNodes *string `json:"excluded_nodes,omitempty"` // 排除的节点(逗号分隔) + BeginTime *int64 `json:"begin_time,omitempty"` // 最早开始时间(Unix时间戳) + Deadline *int64 `json:"deadline,omitempty"` // 截止时间(Unix时间戳) + Array *string `json:"array,omitempty"` // 数组作业规格 + Dependency *string `json:"dependency,omitempty"` // 作业依赖关系 + Requeue *bool `json:"requeue,omitempty"` // 失败后是否重新排队 + KillOnNodeFail *bool `json:"kill_on_node_fail,omitempty"` // 节点故障时是否终止作业 } -// TaskResponse is the DTO returned in API responses. +// TaskResponse 是任务列表/详情 API 响应中的任务项。 type TaskResponse struct { - ID int64 `json:"id"` - TaskName string `json:"task_name"` - AppID int64 `json:"app_id"` - AppName string `json:"app_name"` - Status string `json:"status"` - CurrentStep string `json:"current_step"` - RetryCount int `json:"retry_count"` - SlurmJobID *int32 `json:"slurm_job_id"` - WorkDir string `json:"work_dir"` - ErrorMessage string `json:"error_message"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + ID int64 `json:"id"` // 主键 + TaskName string `json:"task_name"` // 任务名称 + AppID int64 `json:"app_id"` // 所属应用ID + AppName string `json:"app_name"` // 应用名称 + Status string `json:"status"` // 任务状态 + CurrentStep string `json:"current_step"` // 当前执行步骤 + RetryCount int `json:"retry_count"` // 重试次数 + SlurmJobID *int32 `json:"slurm_job_id"` // Slurm作业ID + WorkDir string `json:"work_dir"` // 工作目录 + ErrorMessage string `json:"error_message"` // 错误信息 + CreatedAt time.Time `json:"created_at"` // 创建时间 + UpdatedAt time.Time `json:"updated_at"` // 更新时间 + Partition string `json:"partition,omitempty"` // 提交到的分区 + Cpus *int32 `json:"cpus,omitempty"` // 请求的CPU核数 + MemoryPerNode *int64 `json:"memory_per_node,omitempty"` // 每节点内存(MB) + MemoryPerCpu *int64 `json:"memory_per_cpu,omitempty"` // 每CPU内存(MB) + TimeLimit *int32 `json:"time_limit,omitempty"` // 运行时间限制(分钟) + QOS *string `json:"qos,omitempty"` // 服务质量策略 + JobName *string `json:"job_name,omitempty"` // 作业名称 + Nodes *string `json:"nodes,omitempty"` // 请求的节点数(支持范围如"2-4") + Tasks *int32 `json:"tasks,omitempty"` // 任务数 + CpusPerTask *int32 `json:"cpus_per_task,omitempty"` // 每任务CPU核数 + Constraints *string `json:"constraints,omitempty"` // 节点特性约束 + Reservation *string `json:"reservation,omitempty"` // 预约名称 + Account *string `json:"account,omitempty"` // 计费账户 + Nice *int32 `json:"nice,omitempty"` // nice调整值 + MailType *string `json:"mail_type,omitempty"` // 邮件通知类型(逗号分隔) + MailUser *string `json:"mail_user,omitempty"` // 邮件地址 + StandardOutput *string `json:"standard_output,omitempty"` // 标准输出路径 + StandardError *string `json:"standard_error,omitempty"` // 标准错误路径 + StandardInput *string `json:"standard_input,omitempty"` // 标准输入路径 + RequiredNodes *string `json:"required_nodes,omitempty"` // 指定运行的节点(逗号分隔) + ExcludedNodes *string `json:"excluded_nodes,omitempty"` // 排除的节点(逗号分隔) + BeginTime *int64 `json:"begin_time,omitempty"` // 最早开始时间(Unix时间戳) + Deadline *int64 `json:"deadline,omitempty"` // 截止时间(Unix时间戳) + Array *string `json:"array,omitempty"` // 数组作业规格 + Dependency *string `json:"dependency,omitempty"` // 作业依赖关系 + Requeue *bool `json:"requeue,omitempty"` // 失败后是否重新排队 + KillOnNodeFail *bool `json:"kill_on_node_fail,omitempty"` // 节点故障时是否终止作业 } -// TaskListResponse is the paginated response for listing tasks. +// TaskListResponse 是任务列表分页响应。 type TaskListResponse struct { - Items []TaskResponse `json:"items"` - Total int64 `json:"total"` + Items []TaskResponse `json:"items"` // 任务列表 + Total int64 `json:"total"` // 总数 } -// TaskListQuery contains query parameters for listing tasks. +// TaskListQuery 是任务列表查询参数。 type TaskListQuery struct { - Page int `form:"page" json:"page,omitempty"` - PageSize int `form:"page_size" json:"page_size,omitempty"` - Status string `form:"status" json:"status,omitempty"` + Page int `form:"page" json:"page,omitempty"` // 页码 + PageSize int `form:"page_size" json:"page_size,omitempty"` // 每页条数 + Status string `form:"status" json:"status,omitempty"` // 按状态过滤 }