You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

236 lines
8.7 KiB

package model
import (
2 years ago
"path/filepath"
"strings"
"time"
"go.uber.org/zap"
"gorm.io/gorm"
)
// TODO: 附件管理需要有一个定时任务定时根据type和type_id清理无效的附件数据同时删除无效的文件
const (
AttachmentTypeUnknown = iota // 未知
AttachmentTypeAvatar // 用户头像
AttachmentTypeDocument // 文档
AttachmentTypeArticle // 文章
AttachmentTypeComment // 评论
AttachmentTypeBanner // 横幅
AttachmentTypeCategoryCover // 分类封面
AttachmentTypeConfig // 配置
)
var attachmentTypeName = map[int]string{
AttachmentTypeAvatar: "头像",
AttachmentTypeArticle: "文章",
AttachmentTypeBanner: "横幅",
AttachmentTypeCategoryCover: "分类封面",
AttachmentTypeComment: "评论",
AttachmentTypeDocument: "文档",
1 year ago
AttachmentTypeConfig: "配置",
}
type Attachment struct {
Id int64 `form:"id" json:"id,omitempty" gorm:"primaryKey;autoIncrement;column:id;comment:附件 id;"`
Hash string `form:"hash" json:"hash,omitempty" gorm:"column:hash;type:char(32);size:32;index:hash;comment:文件MD5;"`
UserId int64 `form:"user_id" json:"user_id,omitempty" gorm:"column:user_id;type:bigint(20);default:0;index:user_id;comment:用户 id;"`
TypeId int64 `form:"type_id" json:"type_id,omitempty" gorm:"column:type_id;type:bigint(20);default:0;index:idx_type_id;comment:类型数据ID对应与用户头像时则为用户id对应为文档时则为文档ID;"`
Type int `form:"type" json:"type,omitempty" gorm:"column:type;type:smallint(5);default:0;index:idx_type;comment:附件类型(0 未知1 头像2 文档3 文章附件 ...);"`
Enable bool `form:"enable" json:"enable,omitempty" gorm:"column:enable;type:tinyint(3);default:1;comment:是否合法;"`
Path string `form:"path" json:"path,omitempty" gorm:"column:path;type:varchar(255);size:255;comment:文件存储路径;"`
Name string `form:"name" json:"name,omitempty" gorm:"column:name;type:varchar(255);size:255;comment:文件原名称;"`
Size int64 `form:"size" json:"size,omitempty" gorm:"column:size;type:bigint(20);default:0;comment:文件大小;"`
Width int `form:"width" json:"width,omitempty" gorm:"column:width;type:int(11);default:0;comment:宽度;"`
Height int `form:"height" json:"height,omitempty" gorm:"column:height;type:int(11);default:0;comment:高度;"`
Ext string `form:"ext" json:"ext,omitempty" gorm:"column:ext;type:varchar(32);size:32;comment:文件类型,如 .pdf 。统一处理成小写;"`
Ip string `form:"ip" json:"ip,omitempty" gorm:"column:ip;type:varchar(64);size:64;comment:上传文档的用户IP地址;"`
Description string `form:"description" json:"description,omitempty" gorm:"column:description;type:varchar(255);size:255;comment:描述、备注;"`
CreatedAt *time.Time `form:"created_at" json:"created_at,omitempty" gorm:"column:created_at;type:datetime;comment:创建时间;"`
UpdatedAt *time.Time `form:"updated_at" json:"updated_at,omitempty" gorm:"column:updated_at;type:datetime;comment:更新时间;"`
DeletedAt *gorm.DeletedAt `form:"deleted_at" json:"deleted_at,omitempty" gorm:"column:deleted_at;type:datetime;comment:删除时间;index:idx_deleted_at"`
}
2 years ago
func (Attachment) TableName() string {
return tablePrefix + "attachment"
}
// CreateAttachment 创建Attachment
func (m *DBModel) CreateAttachment(attachment *Attachment) (err error) {
err = m.db.Create(attachment).Error
if err != nil {
m.logger.Error("CreateAttachment", zap.Error(err))
return
}
return
}
// CreateAttachment 创建Attachment
func (m *DBModel) CreateAttachments(attachments []*Attachment) (err error) {
err = m.db.Create(attachments).Error
if err != nil {
m.logger.Error("CreateAttachment", zap.Error(err))
return
}
return
}
// GetAttachmentTypeName 获取附件类型名称
func (m *DBModel) GetAttachmentTypeName(typ int) string {
name, _ := attachmentTypeName[typ]
return name
}
// UpdateAttachment 更新Attachment如果需要更新指定字段则请指定updateFields参数
func (m *DBModel) UpdateAttachment(attachment *Attachment, updateFields ...string) (err error) {
db := m.db.Model(attachment)
tableName := Attachment{}.TableName()
updateFields = m.FilterValidFields(tableName, updateFields...)
if len(updateFields) > 0 { // 更新指定字段
db = db.Select(updateFields)
} else { // 更新全部字段,包括零值字段
db = db.Select(m.GetTableFields(tableName))
}
err = db.Where("id = ?", attachment.Id).Updates(attachment).Error
if err != nil {
m.logger.Error("UpdateAttachment", zap.Error(err))
}
return
}
// GetAttachment 根据id获取Attachment
func (m *DBModel) GetAttachment(id int64, fields ...string) (attachment Attachment, err error) {
db := m.db
fields = m.FilterValidFields(Attachment{}.TableName(), fields...)
if len(fields) > 0 {
db = db.Select(fields)
}
err = db.Where("id = ?", id).First(&attachment).Error
return
}
type OptionGetAttachmentList struct {
Page int
Size int
WithCount bool // 是否返回总数
Ids []interface{} // id列表
SelectFields []string // 查询字段
QueryRange map[string][2]interface{} // map[field][]{min,max}
QueryIn map[string][]interface{} // map[field][]{value1,value2,...}
QueryLike map[string][]interface{} // map[field][]{value1,value2,...}
Sort []string
}
// GetAttachmentList 获取Attachment列表
func (m *DBModel) GetAttachmentList(opt *OptionGetAttachmentList) (attachmentList []Attachment, total int64, err error) {
tableName := Attachment{}.TableName()
db := m.db.Model(&Attachment{})
db = m.generateQueryRange(db, tableName, opt.QueryRange)
db = m.generateQueryIn(db, tableName, opt.QueryIn)
db = m.generateQueryLike(db, tableName, opt.QueryLike)
if len(opt.Ids) > 0 {
db = db.Where("id in (?)", opt.Ids)
}
if opt.WithCount {
err = db.Count(&total).Error
if err != nil {
m.logger.Error("GetAttachmentList", zap.Error(err))
return
}
}
opt.SelectFields = m.FilterValidFields(tableName, opt.SelectFields...)
if len(opt.SelectFields) > 0 {
db = db.Select(opt.SelectFields)
}
// TODO: 没有排序参数的话,可以自行指定排序字段
if len(opt.Sort) > 0 {
db = m.generateQuerySort(db, tableName, opt.Sort)
} else {
db = db.Order("id desc")
}
db = db.Offset((opt.Page - 1) * opt.Size).Limit(opt.Size)
err = db.Find(&attachmentList).Error
if err != nil && err != gorm.ErrRecordNotFound {
m.logger.Error("GetAttachmentList", zap.Error(err))
}
return
}
// DeleteAttachment 删除数据
// TODO: 删除数据之后,存在 attachment_id 的关联表,需要删除对应数据,同时相关表的统计数值,也要随着减少
// TODO: 检查是否有相同hash的文件存在没有的话需要同时删除文件
func (m *DBModel) DeleteAttachment(ids []int64) (err error) {
err = m.db.Where("id in (?)", ids).Delete(&Attachment{}).Error
if err != nil {
m.logger.Error("DeleteAttachment", zap.Error(err))
}
return
}
2 years ago
func (m *DBModel) GetAttachmentByTypeAndTypeId(typ int, typeId int64, fields ...string) (attachment Attachment) {
db := m.db
if len(fields) > 0 {
db = db.Select(fields)
}
err := db.Where("type = ? and type_id = ?", typ, typeId).Last(&attachment).Error
if err != nil && err != gorm.ErrRecordNotFound {
m.logger.Error("GetAttachmentByTypeAndTypeId", zap.Error(err))
}
return
}
func (m *DBModel) setAttachmentType(attachmentType int, attachmentTypeId int64, paths []string) {
2 years ago
var hashes []string
for _, path := range paths {
if strings.HasPrefix(strings.TrimLeft(path, "."), "/uploads/") {
filename := filepath.Base(path)
hashes = append(hashes, strings.TrimSuffix(filename, filepath.Ext(filename)))
}
}
if len(hashes) > 0 {
err := m.db.Model(&Attachment{}).Where("hash in (?) and type = ? and type_id = 0", hashes, attachmentType).Update("type_id", attachmentTypeId).Error
if err != nil {
m.logger.Error("setAttachmentType", zap.Error(err))
}
}
}
// 设置附件type_id。
// attachIdTypeIdMap map[attachment_id]type_id
func (m *DBModel) SetAttachmentTypeId(attachIdTypeIdMap map[int64]int64) {
if len(attachIdTypeIdMap) == 0 {
return
}
var err error
sess := m.db.Begin()
defer func() {
if err != nil {
sess.Rollback()
} else {
sess.Commit()
}
}()
for attachmentId, typeId := range attachIdTypeIdMap {
err = sess.Model(&Attachment{}).
Where("id = ?", attachmentId).
Update("type_id", typeId).Error
if err != nil {
m.logger.Error("SetAttachmentTypeId", zap.Error(err))
return
}
}
}