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 <clio-agent@sisyphuslabs.ai>
This commit is contained in:
dailz
2026-04-20 10:37:58 +08:00
parent 0c7a282386
commit db06e99967
5 changed files with 299 additions and 199 deletions

View File

@@ -5,7 +5,7 @@ import (
"time" "time"
) )
// Parameter type constants for ParameterSchema.Type. // 参数类型常量
const ( const (
ParamTypeString = "string" ParamTypeString = "string"
ParamTypeInteger = "integer" ParamTypeInteger = "integer"
@@ -15,62 +15,59 @@ const (
ParamTypeBoolean = "boolean" ParamTypeBoolean = "boolean"
) )
// Application represents a parameterized application definition for HPC job submission. // Application 表示一个参数化的 HPC 应用定义。
type Application struct { type Application struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键
Name string `gorm:"uniqueIndex;size:255;not null" json:"name"` Name string `gorm:"uniqueIndex;size:255;not null" json:"name"` // 应用名称(唯一)
Description string `gorm:"type:text" json:"description,omitempty"` Description string `gorm:"type:text" json:"description,omitempty"` // 应用描述
Icon string `gorm:"size:255" json:"icon,omitempty"` Icon string `gorm:"size:255" json:"icon,omitempty"` // 图标
Category string `gorm:"size:255" json:"category,omitempty"` Category string `gorm:"size:255" json:"category,omitempty"` // 分类
ScriptTemplate string `gorm:"type:text;not null" json:"script_template"` ScriptTemplate string `gorm:"type:text;not null" json:"script_template"` // 脚本模板
Parameters json.RawMessage `gorm:"type:json" json:"parameters,omitempty"` Parameters json.RawMessage `gorm:"type:json" json:"parameters,omitempty"` // 参数表单JSON
Scope string `gorm:"size:50;default:'system'" json:"scope,omitempty"` Scope string `gorm:"size:50;default:'system'" json:"scope,omitempty"` // 作用域(system/user)
CreatedBy int64 `json:"created_by,omitempty"` CreatedBy int64 `json:"created_by,omitempty"` // 创建者ID
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"` // 创建时间
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"` // 更新时间
} }
func (Application) TableName() string { func (Application) TableName() string {
return "hpc_applications" return "hpc_applications"
} }
// ParameterSchema defines a single parameter in an application's form schema. // ParameterSchema 定义应用表单中单个参数的格式。
type ParameterSchema struct { type ParameterSchema struct {
Name string `json:"name"` Name string `json:"name"` // 参数名
Label string `json:"label,omitempty"` Label string `json:"label,omitempty"` // 显示名称
Type string `json:"type"` Type string `json:"type"` // 参数类型
Required bool `json:"required,omitempty"` Required bool `json:"required,omitempty"` // 是否必填
Default string `json:"default,omitempty"` Default string `json:"default,omitempty"` // 默认值
Options []string `json:"options,omitempty"` Options []string `json:"options,omitempty"` // 枚举选项列表
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"` // 参数说明
} }
// CreateApplicationRequest is the DTO for creating a new application. // CreateApplicationRequest 是创建应用的 API 请求。
type CreateApplicationRequest struct { type CreateApplicationRequest struct {
Name string `json:"name" binding:"required"` Name string `json:"name" binding:"required"` // 应用名称(必填)
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"` // 应用描述
Icon string `json:"icon,omitempty"` Icon string `json:"icon,omitempty"` // 图标
Category string `json:"category,omitempty"` Category string `json:"category,omitempty"` // 分类
ScriptTemplate string `json:"script_template" binding:"required"` ScriptTemplate string `json:"script_template" binding:"required"` // 脚本模板(必填)
Parameters json.RawMessage `json:"parameters,omitempty"` Parameters json.RawMessage `json:"parameters,omitempty"` // 参数表单JSON
Scope string `json:"scope,omitempty"` Scope string `json:"scope,omitempty"` // 作用域
} }
// UpdateApplicationRequest is the DTO for updating an existing application. // UpdateApplicationRequest 是更新应用的 API 请求。所有字段可选。
// All fields are optional. Parameters uses *json.RawMessage to distinguish
// between "not provided" (nil) and "set to empty" (non-nil).
type UpdateApplicationRequest struct { type UpdateApplicationRequest struct {
Name *string `json:"name,omitempty"` Name *string `json:"name,omitempty"` // 应用名称
Description *string `json:"description,omitempty"` Description *string `json:"description,omitempty"` // 应用描述
Icon *string `json:"icon,omitempty"` Icon *string `json:"icon,omitempty"` // 图标
Category *string `json:"category,omitempty"` Category *string `json:"category,omitempty"` // 分类
ScriptTemplate *string `json:"script_template,omitempty"` ScriptTemplate *string `json:"script_template,omitempty"` // 脚本模板
Parameters *json.RawMessage `json:"parameters,omitempty"` Parameters *json.RawMessage `json:"parameters,omitempty"` // 参数表单JSON
Scope *string `json:"scope,omitempty"` Scope *string `json:"scope,omitempty"` // 作用域
} }
// ApplicationSubmitRequest is the DTO for submitting a job from an application. // ApplicationSubmitRequest 是通过应用提交作业的 API 请求。
// ApplicationID is parsed from the URL :id parameter, not included in the body.
type ApplicationSubmitRequest struct { type ApplicationSubmitRequest struct {
Values map[string]string `json:"values" binding:"required"` Values map[string]string `json:"values" binding:"required"` // 脚本业务参数键值对
} }

View File

@@ -1,6 +1,6 @@
package model package model
// NodeResponse is the API response for a node. // NodeResponse 是节点查询 API 响应。
type NodeResponse struct { type NodeResponse struct {
// Identity // Identity
Name string `json:"name"` // 节点主机名 Name string `json:"name"` // 节点主机名
@@ -37,7 +37,7 @@ type NodeResponse struct {
ActiveFeatures string `json:"active_features,omitempty"` // 当前生效的特性标签 (只读) ActiveFeatures string `json:"active_features,omitempty"` // 当前生效的特性标签 (只读)
} }
// PartitionResponse is the API response for a partition. // PartitionResponse 是分区查询 API 响应。
type PartitionResponse struct { type PartitionResponse struct {
// Identity // Identity
Name string `json:"name"` // 分区名称 Name string `json:"name"` // 分区名称

View File

@@ -9,159 +9,159 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
// FileBlob represents a physical file stored in MinIO, deduplicated by SHA256. // FileBlob 表示存储在 MinIO 中的物理文件,按 SHA256 去重。
type FileBlob struct { type FileBlob struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键
SHA256 string `gorm:"uniqueIndex;size:64;not null" json:"sha256"` SHA256 string `gorm:"uniqueIndex;size:64;not null" json:"sha256"` // 文件哈希
MinioKey string `gorm:"size:255;not null" json:"minio_key"` MinioKey string `gorm:"size:255;not null" json:"minio_key"` // MinIO对象键
FileSize int64 `gorm:"not null" json:"file_size"` FileSize int64 `gorm:"not null" json:"file_size"` // 文件大小(字节)
MimeType string `gorm:"size:255" json:"mime_type"` MimeType string `gorm:"size:255" json:"mime_type"` // MIME类型
RefCount int `gorm:"not null;default:0" json:"ref_count"` RefCount int `gorm:"not null;default:0" json:"ref_count"` // 引用计数
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"` // 创建时间
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"` // 更新时间
} }
func (FileBlob) TableName() string { func (FileBlob) TableName() string {
return "hpc_file_blobs" return "hpc_file_blobs"
} }
// File represents a logical file visible to users, backed by a FileBlob. // File 表示用户可见的逻辑文件,底层由 FileBlob 存储。
type File struct { type File struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键
Name string `gorm:"size:255;not null" json:"name"` Name string `gorm:"size:255;not null" json:"name"` // 文件名
FolderID *int64 `gorm:"index" json:"folder_id,omitempty"` FolderID *int64 `gorm:"index" json:"folder_id,omitempty"` // 所属文件夹ID
BlobSHA256 string `gorm:"size:64;not null" json:"blob_sha256"` BlobSHA256 string `gorm:"size:64;not null" json:"blob_sha256"` // 关联的文件blob哈希
UserID *int64 `gorm:"index" json:"user_id,omitempty"` UserID *int64 `gorm:"index" json:"user_id,omitempty"` // 所有者ID
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"` // 创建时间
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"` DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"` // 软删除时间
} }
func (File) TableName() string { func (File) TableName() string {
return "hpc_files" return "hpc_files"
} }
// Folder represents a directory in the virtual file system. // Folder 表示虚拟文件系统中的目录。
type Folder struct { type Folder struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键
Name string `gorm:"size:255;not null" json:"name"` Name string `gorm:"size:255;not null" json:"name"` // 文件夹名称
ParentID *int64 `gorm:"index" json:"parent_id,omitempty"` ParentID *int64 `gorm:"index" json:"parent_id,omitempty"` // 父文件夹ID
Path string `gorm:"uniqueIndex;size:768;not null" json:"path"` Path string `gorm:"uniqueIndex;size:768;not null" json:"path"` // 完整路径(唯一)
UserID *int64 `gorm:"index" json:"user_id,omitempty"` UserID *int64 `gorm:"index" json:"user_id,omitempty"` // 所有者ID
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"` // 创建时间
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"` DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"` // 软删除时间
} }
func (Folder) TableName() string { func (Folder) TableName() string {
return "hpc_folders" return "hpc_folders"
} }
// UploadSession represents an in-progress chunked upload. // UploadSession 表示一个进行中的分块上传会话。
// State transitions: pending→uploading, pending→completed(zero-byte), uploading→merging, // 状态转换: pending→uploading, pending→completed(零字节), uploading→merging,
// uploading→cancelled, merging→completed, merging→failed, any→expired // uploading→cancelled, merging→completed, merging→failed, any→expired
type UploadSession struct { type UploadSession struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键
FileName string `gorm:"size:255;not null" json:"file_name"` FileName string `gorm:"size:255;not null" json:"file_name"` // 文件名
FileSize int64 `gorm:"not null" json:"file_size"` FileSize int64 `gorm:"not null" json:"file_size"` // 文件总大小
ChunkSize int64 `gorm:"not null" json:"chunk_size"` ChunkSize int64 `gorm:"not null" json:"chunk_size"` // 分块大小
TotalChunks int `gorm:"not null" json:"total_chunks"` TotalChunks int `gorm:"not null" json:"total_chunks"` // 总分块数
SHA256 string `gorm:"size:64;not null" json:"sha256"` SHA256 string `gorm:"size:64;not null" json:"sha256"` // 文件哈希
FolderID *int64 `gorm:"index" json:"folder_id,omitempty"` FolderID *int64 `gorm:"index" json:"folder_id,omitempty"` // 目标文件夹ID
Status string `gorm:"size:20;not null;default:pending" json:"status"` Status string `gorm:"size:20;not null;default:pending" json:"status"` // 会话状态
MinioPrefix string `gorm:"size:255;not null" json:"minio_prefix"` MinioPrefix string `gorm:"size:255;not null" json:"minio_prefix"` // MinIO存储前缀
MimeType string `gorm:"size:255;default:'application/octet-stream'" json:"mime_type"` MimeType string `gorm:"size:255;default:'application/octet-stream'" json:"mime_type"` // MIME类型
UserID *int64 `gorm:"index" json:"user_id,omitempty"` UserID *int64 `gorm:"index" json:"user_id,omitempty"` // 上传者ID
ExpiresAt time.Time `gorm:"not null" json:"expires_at"` ExpiresAt time.Time `gorm:"not null" json:"expires_at"` // 过期时间
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"` // 创建时间
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"` // 更新时间
} }
func (UploadSession) TableName() string { func (UploadSession) TableName() string {
return "hpc_upload_sessions" return "hpc_upload_sessions"
} }
// UploadChunk represents a single chunk of an upload session. // UploadChunk 表示上传会话中的单个分块。
type UploadChunk struct { type UploadChunk struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键
SessionID int64 `gorm:"not null;uniqueIndex:idx_session_chunk" json:"session_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"` ChunkIndex int `gorm:"not null;uniqueIndex:idx_session_chunk" json:"chunk_index"` // 分块序号
MinioKey string `gorm:"size:255;not null" json:"minio_key"` MinioKey string `gorm:"size:255;not null" json:"minio_key"` // MinIO对象键
SHA256 string `gorm:"size:64" json:"sha256,omitempty"` SHA256 string `gorm:"size:64" json:"sha256,omitempty"` // 分块哈希
Size int64 `gorm:"not null" json:"size"` Size int64 `gorm:"not null" json:"size"` // 分块大小
Status string `gorm:"size:20;not null;default:pending" json:"status"` Status string `gorm:"size:20;not null;default:pending" json:"status"` // 分块状态
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"` // 创建时间
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"` // 更新时间
} }
func (UploadChunk) TableName() string { func (UploadChunk) TableName() string {
return "hpc_upload_chunks" return "hpc_upload_chunks"
} }
// InitUploadRequest is the DTO for initiating a chunked upload. // InitUploadRequest 是初始化分块上传的 API 请求。
type InitUploadRequest struct { type InitUploadRequest struct {
FileName string `json:"file_name" binding:"required"` FileName string `json:"file_name" binding:"required"` // 文件名(必填)
FileSize int64 `json:"file_size" binding:"required"` FileSize int64 `json:"file_size" binding:"required"` // 文件大小(必填)
SHA256 string `json:"sha256" binding:"required"` SHA256 string `json:"sha256" binding:"required"` // 文件哈希(必填)
FolderID *int64 `json:"folder_id,omitempty"` FolderID *int64 `json:"folder_id,omitempty"` // 目标文件夹ID
ChunkSize *int64 `json:"chunk_size,omitempty"` ChunkSize *int64 `json:"chunk_size,omitempty"` // 分块大小
MimeType string `json:"mime_type,omitempty"` MimeType string `json:"mime_type,omitempty"` // MIME类型
} }
// CreateFolderRequest is the DTO for creating a new folder. // CreateFolderRequest 是创建文件夹的 API 请求。
type CreateFolderRequest struct { type CreateFolderRequest struct {
Name string `json:"name" binding:"required"` Name string `json:"name" binding:"required"` // 文件夹名称(必填)
ParentID *int64 `json:"parent_id,omitempty"` ParentID *int64 `json:"parent_id,omitempty"` // 父文件夹ID
} }
// UploadSessionResponse is the DTO returned when creating/querying an upload session. // UploadSessionResponse 是上传会话的 API 响应。
type UploadSessionResponse struct { type UploadSessionResponse struct {
ID int64 `json:"id"` ID int64 `json:"id"` // 主键
FileName string `json:"file_name"` FileName string `json:"file_name"` // 文件名
FileSize int64 `json:"file_size"` FileSize int64 `json:"file_size"` // 文件大小
ChunkSize int64 `json:"chunk_size"` ChunkSize int64 `json:"chunk_size"` // 分块大小
TotalChunks int `json:"total_chunks"` TotalChunks int `json:"total_chunks"` // 总分块数
SHA256 string `json:"sha256"` SHA256 string `json:"sha256"` // 文件哈希
Status string `json:"status"` Status string `json:"status"` // 会话状态
UploadedChunks []int `json:"uploaded_chunks"` UploadedChunks []int `json:"uploaded_chunks"` // 已上传的分块序号列表
ExpiresAt time.Time `json:"expires_at"` ExpiresAt time.Time `json:"expires_at"` // 过期时间
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"` // 创建时间
} }
// FileResponse is the DTO for a file in API responses. // FileResponse 是文件 API 响应。
type FileResponse struct { type FileResponse struct {
ID int64 `json:"id"` ID int64 `json:"id"` // 主键
Name string `json:"name"` Name string `json:"name"` // 文件名
FolderID *int64 `json:"folder_id"` FolderID *int64 `json:"folder_id"` // 所属文件夹ID
FolderPath *string `json:"folder_path"` FolderPath *string `json:"folder_path"` // 所属文件夹路径
UserID *int64 `json:"user_id"` UserID *int64 `json:"user_id"` // 所有者ID
Size int64 `json:"size"` Size int64 `json:"size"` // 文件大小
MimeType string `json:"mime_type"` MimeType string `json:"mime_type"` // MIME类型
SHA256 string `json:"sha256"` SHA256 string `json:"sha256"` // 文件哈希
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"` // 创建时间
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"` // 更新时间
} }
// FolderResponse is the DTO for a folder in API responses. // FolderResponse 是文件夹 API 响应。
type FolderResponse struct { type FolderResponse struct {
ID int64 `json:"id"` ID int64 `json:"id"` // 主键
Name string `json:"name"` Name string `json:"name"` // 文件夹名称
ParentID *int64 `json:"parent_id,omitempty"` ParentID *int64 `json:"parent_id,omitempty"` // 父文件夹ID
Path string `json:"path"` Path string `json:"path"` // 完整路径
FileCount int64 `json:"file_count"` FileCount int64 `json:"file_count"` // 文件数量
SubFolderCount int64 `json:"subfolder_count"` SubFolderCount int64 `json:"subfolder_count"` // 子文件夹数量
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"` // 创建时间
} }
// ListFilesResponse is the paginated response for listing files. // ListFilesResponse 是文件列表分页响应。
type ListFilesResponse struct { type ListFilesResponse struct {
Files []FileResponse `json:"files"` Files []FileResponse `json:"files"` // 文件列表
Total int64 `json:"total"` Total int64 `json:"total"` // 总数
Page int `json:"page"` Page int `json:"page"` // 页码
PageSize int `json:"page_size"` PageSize int `json:"page_size"` // 每页条数
} }
// ValidateFileName rejects empty, "..", "/", "\", null bytes, control chars, leading/trailing spaces. // ValidateFileName 校验文件名:禁止空值、"..", "/", "\", null字节、控制字符、首尾空格。
func ValidateFileName(name string) error { func ValidateFileName(name string) error {
if name == "" { if name == "" {
return fmt.Errorf("file name cannot be empty") return fmt.Errorf("file name cannot be empty")
@@ -186,7 +186,7 @@ func ValidateFileName(name string) error {
return nil return nil
} }
// ValidateFolderName rejects same as ValidateFileName plus ".". // ValidateFolderName 校验文件夹名:在 ValidateFileName 基础上额外禁止 "."
func ValidateFolderName(name string) error { func ValidateFolderName(name string) error {
if name == "." { if name == "." {
return fmt.Errorf("folder name cannot be '.'") return fmt.Errorf("folder name cannot be '.'")

View File

@@ -1,19 +1,42 @@
package model package model
// SubmitJobRequest is the API request for submitting a job. // SubmitJobRequest 是提交作业的 API 请求。
type SubmitJobRequest struct { type SubmitJobRequest struct {
Script string `json:"script"` // 作业脚本内容 Script string `json:"script"` // 作业脚本内容
Partition string `json:"partition,omitempty"` // 提交到的分区 Partition string `json:"partition,omitempty"` // 提交到的分区
QOS string `json:"qos,omitempty"` // 使用的 QOS 策略 QOS string `json:"qos,omitempty"` // 使用的 QOS 策略
CPUs int32 `json:"cpus,omitempty"` // 请求的 CPU 核数 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"` // 运行时间限制 (分钟) TimeLimit string `json:"time_limit,omitempty"` // 运行时间限制 (分钟)
JobName string `json:"job_name,omitempty"` // 作业名称 JobName string `json:"job_name,omitempty"` // 作业名称
Environment map[string]string `json:"environment,omitempty"` // 环境变量键值对 Environment map[string]string `json:"environment,omitempty"` // 环境变量键值对
WorkDir string `json:"work_dir,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 { type JobResponse struct {
// Identity // Identity
JobID int32 `json:"job_id"` // Slurm 作业 ID JobID int32 `json:"job_id"` // Slurm 作业 ID
@@ -59,7 +82,7 @@ type JobResponse struct {
ArrayTaskID *int32 `json:"array_task_id,omitempty"` // 数组作业中的子任务 ID ArrayTaskID *int32 `json:"array_task_id,omitempty"` // 数组作业中的子任务 ID
} }
// JobListResponse is the paginated response for job listings. // JobListResponse 是作业列表分页响应。
type JobListResponse struct { type JobListResponse struct {
Jobs []JobResponse `json:"jobs"` // 作业列表 Jobs []JobResponse `json:"jobs"` // 作业列表
Total int `json:"total"` // 符合条件的作业总数 Total int `json:"total"` // 符合条件的作业总数
@@ -67,13 +90,13 @@ type JobListResponse struct {
PageSize int `json:"page_size"` // 每页条数 PageSize int `json:"page_size"` // 每页条数
} }
// JobListQuery contains pagination parameters for active job listing. // JobListQuery 是活跃作业列表查询参数。
type JobListQuery struct { type JobListQuery struct {
Page int `form:"page,default=1" json:"page,omitempty"` // 页码 (从 1 开始) Page int `form:"page,default=1" json:"page,omitempty"` // 页码 (从 1 开始)
PageSize int `form:"page_size,default=20" json:"page_size,omitempty"` // 每页条数 PageSize int `form:"page_size,default=20" json:"page_size,omitempty"` // 每页条数
} }
// JobHistoryQuery contains query parameters for job history. // JobHistoryQuery 是作业历史查询参数。
type JobHistoryQuery struct { type JobHistoryQuery struct {
Users string `form:"users" json:"users,omitempty"` // 按用户名过滤 (逗号分隔) Users string `form:"users" json:"users,omitempty"` // 按用户名过滤 (逗号分隔)
StartTime string `form:"start_time" json:"start_time,omitempty"` // 作业开始时间下限 (Unix 时间戳) StartTime string `form:"start_time" json:"start_time,omitempty"` // 作业开始时间下限 (Unix 时间戳)

View File

@@ -7,7 +7,7 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
// Task status constants. // 任务状态常量
const ( const (
TaskStatusSubmitted = "submitted" TaskStatusSubmitted = "submitted"
TaskStatusPreparing = "preparing" TaskStatusPreparing = "preparing"
@@ -19,75 +19,155 @@ const (
TaskStatusFailed = "failed" TaskStatusFailed = "failed"
) )
// Task step constants for step-level retry tracking. // 任务步骤常量,用于步骤级重试追踪
const ( const (
TaskStepPreparing = "preparing" TaskStepPreparing = "preparing"
TaskStepDownloading = "downloading" TaskStepDownloading = "downloading"
TaskStepSubmitting = "submitting" TaskStepSubmitting = "submitting"
) )
// Task represents an HPC task submitted through the application framework. // Task 表示通过应用框架提交的 HPC 任务记录。
type Task struct { type Task struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` ID int64 `gorm:"primaryKey;autoIncrement" json:"id"` // 主键
TaskName string `gorm:"size:255" json:"task_name"` TaskName string `gorm:"size:255" json:"task_name"` // 任务名称
AppID int64 `json:"app_id"` AppID int64 `json:"app_id"` // 所属应用ID
AppName string `gorm:"size:255" json:"app_name"` AppName string `gorm:"size:255" json:"app_name"` // 应用名称
Status string `json:"status"` Status string `json:"status"` // 任务状态
CurrentStep string `json:"current_step"` CurrentStep string `json:"current_step"` // 当前执行步骤
RetryCount int `json:"retry_count"` RetryCount int `json:"retry_count"` // 重试次数
Values json.RawMessage `gorm:"type:text" json:"values,omitempty"` Values json.RawMessage `gorm:"type:text" json:"values,omitempty"` // 业务参数JSON
InputFileIDs json.RawMessage `json:"input_file_ids" gorm:"column:input_file_ids;type:text"` InputFileIDs json.RawMessage `json:"input_file_ids" gorm:"column:input_file_ids;type:text"` // 输入文件ID列表JSON
Script string `json:"script,omitempty"` Script string `json:"script,omitempty"` // 渲染后的脚本内容
SlurmJobID *int32 `json:"slurm_job_id,omitempty"` SlurmJobID *int32 `json:"slurm_job_id,omitempty"` // Slurm作业ID
WorkDir string `json:"work_dir,omitempty"` WorkDir string `json:"work_dir,omitempty"` // 工作目录
Partition string `json:"partition,omitempty"` Partition string `json:"partition,omitempty"` // 提交到的分区(空字符串表示未设置)
ErrorMessage string `json:"error_message,omitempty"` ErrorMessage string `json:"error_message,omitempty"` // 错误信息
UserID string `json:"user_id"` UserID string `json:"user_id"` // 提交用户ID
SubmittedAt time.Time `json:"submitted_at"` SubmittedAt time.Time `json:"submitted_at"` // 提交时间
StartedAt *time.Time `json:"started_at,omitempty"` StartedAt *time.Time `json:"started_at,omitempty"` // 开始运行时间
FinishedAt *time.Time `json:"finished_at,omitempty"` FinishedAt *time.Time `json:"finished_at,omitempty"` // 完成时间
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"` // 创建时间
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"` 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 { func (Task) TableName() string {
return "hpc_tasks" return "hpc_tasks"
} }
// CreateTaskRequest is the DTO for creating a new task. // CreateTaskRequest 是创建任务的 API 请求。
type CreateTaskRequest struct { type CreateTaskRequest struct {
AppID int64 `json:"app_id" binding:"required"` AppID int64 `json:"app_id" binding:"required"` // 所属应用ID必填
TaskName string `json:"task_name"` TaskName string `json:"task_name"` // 任务名称
Values map[string]string `json:"values"` Values map[string]string `json:"values"` // 脚本业务参数键值对
InputFileIDs []int64 `json:"file_ids"` 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 { type TaskResponse struct {
ID int64 `json:"id"` ID int64 `json:"id"` // 主键
TaskName string `json:"task_name"` TaskName string `json:"task_name"` // 任务名称
AppID int64 `json:"app_id"` AppID int64 `json:"app_id"` // 所属应用ID
AppName string `json:"app_name"` AppName string `json:"app_name"` // 应用名称
Status string `json:"status"` Status string `json:"status"` // 任务状态
CurrentStep string `json:"current_step"` CurrentStep string `json:"current_step"` // 当前执行步骤
RetryCount int `json:"retry_count"` RetryCount int `json:"retry_count"` // 重试次数
SlurmJobID *int32 `json:"slurm_job_id"` SlurmJobID *int32 `json:"slurm_job_id"` // Slurm作业ID
WorkDir string `json:"work_dir"` WorkDir string `json:"work_dir"` // 工作目录
ErrorMessage string `json:"error_message"` ErrorMessage string `json:"error_message"` // 错误信息
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"` // 创建时间
UpdatedAt time.Time `json:"updated_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 { type TaskListResponse struct {
Items []TaskResponse `json:"items"` Items []TaskResponse `json:"items"` // 任务列表
Total int64 `json:"total"` Total int64 `json:"total"` // 总数
} }
// TaskListQuery contains query parameters for listing tasks. // TaskListQuery 是任务列表查询参数。
type TaskListQuery struct { type TaskListQuery struct {
Page int `form:"page" json:"page,omitempty"` Page int `form:"page" json:"page,omitempty"` // 页码
PageSize int `form:"page_size" json:"page_size,omitempty"` PageSize int `form:"page_size" json:"page_size,omitempty"` // 每页条数
Status string `form:"status" json:"status,omitempty"` Status string `form:"status" json:"status,omitempty"` // 按状态过滤
} }