- 删除 models/attachment.go,Attachment 模型由 File(Type=attachments)替代 - handlers/attachment.go:上传/删除/列表/绑定全部改读写 files 表, 查询按 type='attachments' 过滤(为 avatar/logo 等类型预留隔离) - models/db.go:AutoMigrate 移除 Attachment;启动迁移仅当旧表仍存在时执行 (升级路径保障,已删除则为无操作) - 测试同步切换到 File 模型(security_test/bodylimit_test) - scripts/drop_attachments_table.sql:旧表删除脚本(含前置校验说明)
52 lines
2.5 KiB
Go
52 lines
2.5 KiB
Go
package models
|
||
|
||
import (
|
||
"time"
|
||
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// File 是全站统一的上传文件登记表,用于管理所有类型的上传文件
|
||
// (附件、头像、Logo 等)。字段与原 attachments 表对齐,额外通过
|
||
// Type 字段区分文件归属类型。
|
||
//
|
||
// 生命周期(方案 A——先上传后绑定):
|
||
// - 在文章创建页面上文章尚不存在,因此 ArticleID 为 0,
|
||
// 该行暂时由 SessionToken(为页面会话生成的随机值)持有。
|
||
// - 保存文章时,ArticleCreate 通过 SessionToken 绑定待处理行,
|
||
// 设置它们的 ArticleID 并清除令牌。
|
||
// - 在编辑页面上,上传直接携带真实的 ArticleID。
|
||
//
|
||
// 磁盘去重:StoredName 是文件内容的 SHA-256。写入前,
|
||
// 处理器会检查磁盘上是否已存在同名文件;若已存在则复用(不重写)。
|
||
// 删除采用引用计数——仅当没有任何 File 行引用时,才删除磁盘文件。
|
||
type File struct {
|
||
ID uint `gorm:"primarykey" json:"id"`
|
||
Type string `gorm:"size:32;index;default:attachments" json:"type"` // 归属类型:attachment 等
|
||
ArticleID uint `gorm:"index" json:"article_id"` // 在创建页面上待绑定时为 0
|
||
SessionToken string `gorm:"size:64;index" json:"-"` // 创建页面上的临时归属令牌
|
||
UploaderID uint `gorm:"index" json:"uploader_id"`
|
||
Filename string `gorm:"size:255" json:"filename"` // 原始文件名
|
||
StoredName string `gorm:"size:64;index" json:"stored_name"` // SHA-256 十六进制字符串,磁盘文件名
|
||
Ext string `gorm:"size:32" json:"ext"`
|
||
MIME string `gorm:"size:128" json:"mime"`
|
||
Size int64 `gorm:"default:0" json:"size"`
|
||
Category string `gorm:"size:32" json:"category"` // image/document/archive/video/other
|
||
CreatedAt time.Time `json:"created_at"`
|
||
UpdatedAt time.Time `json:"updated_at"`
|
||
DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"`
|
||
}
|
||
|
||
// TableName 覆盖 GORM 默认的表名。
|
||
func (File) TableName() string {
|
||
return "files"
|
||
}
|
||
|
||
// FileTypeAttachment 是文件归属类型常量:文章附件(原 attachments 表历史数据)。
|
||
const FileTypeAttachment = "attachments"
|
||
|
||
// IsImage 报告该文件是否为图片(用于决定 Markdown 插入形式:![]() 还是 []())。
|
||
func (f *File) IsImage() bool {
|
||
return f.Category == CategoryImage
|
||
}
|