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:
@@ -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 '.'")
|
||||
|
||||
Reference in New Issue
Block a user