feat: 书库导入导出/图片变体/书籍搜索/小组件运行时等
新增: - 书库导入导出(library_import/library_export)及测试 - 图片变体生成(image_variants)与响应式图片(responsiveImage) - 书籍搜索(bookSearch)+ BookSearch/LibrarySearchGrid 组件 - 小组件运行时(widgetRuntime)与静态检查(widgetLint) - 上传缓存中间件(upload_cache)与字体 CSS 提取脚本 其它: - 后端 handlers/services 全量调整 - 前端页面、组件、库函数与配置更新
This commit is contained in:
@@ -238,7 +238,7 @@ func (s *UploadService) transcodeImageToWebP(tmp, name, ext string) (string, str
|
||||
}
|
||||
|
||||
// SaveImage 流式保存帖子插图:校验格式/大小/尺寸 → JPEG/PNG 转 WebP → 落盘 → 写 attachments(kind=image)
|
||||
func (s *UploadService) SaveImage(userID uint, src io.Reader) (*model.Attachment, error) {
|
||||
func (s *UploadService) SaveImage(userID uint, src io.Reader, source string) (*model.Attachment, error) {
|
||||
if src == nil {
|
||||
return nil, errors.New("文件为空")
|
||||
}
|
||||
@@ -354,6 +354,7 @@ func (s *UploadService) SaveImage(userID uint, src io.Reader) (*model.Attachment
|
||||
att := &model.Attachment{
|
||||
UserID: userID,
|
||||
Kind: model.AttachmentKindImage,
|
||||
Source: normalizeImageSource(source),
|
||||
URL: "/uploads/images/" + storeName,
|
||||
MIME: storeMime,
|
||||
Size: int(storeSize),
|
||||
@@ -585,9 +586,20 @@ func (s *UploadService) CopyBackgroundFromMedia(userID, attachmentID uint) (stri
|
||||
return s.SaveBackground(f)
|
||||
}
|
||||
|
||||
// normalizeImageSource 把外部传入的图片来源收敛到白名单,未知值回退为 post
|
||||
func normalizeImageSource(s string) string {
|
||||
switch s {
|
||||
case model.AttachmentSourceLibraryCover, model.AttachmentSourceLibraryContent:
|
||||
return s
|
||||
default:
|
||||
return model.AttachmentSourcePost
|
||||
}
|
||||
}
|
||||
|
||||
// CopyImageFromMedia 把当前用户媒体库里的一张图复制一份新插图(落 uploads/images 并记一条本人附件)
|
||||
// 用于书籍封面等长期引用场景:与原图解耦,原图删除后副本仍可用
|
||||
func (s *UploadService) CopyImageFromMedia(userID, attachmentID uint) (*model.Attachment, error) {
|
||||
// source 决定副本的来源标记(如 library_cover)
|
||||
func (s *UploadService) CopyImageFromMedia(userID, attachmentID uint, source string) (*model.Attachment, error) {
|
||||
var att model.Attachment
|
||||
if err := s.db.Where("id = ? AND user_id = ?", attachmentID, userID).First(&att).Error; err != nil {
|
||||
return nil, errors.New("图片不存在或不属于你")
|
||||
@@ -600,7 +612,7 @@ func (s *UploadService) CopyImageFromMedia(userID, attachmentID uint) (*model.At
|
||||
return nil, e
|
||||
}
|
||||
defer r.Close()
|
||||
return s.SaveImage(userID, r)
|
||||
return s.SaveImage(userID, r, source)
|
||||
}
|
||||
abs, ok := s.safeUploadPath(att.URL)
|
||||
if !ok {
|
||||
@@ -611,7 +623,7 @@ func (s *UploadService) CopyImageFromMedia(userID, attachmentID uint) (*model.At
|
||||
return nil, errors.New("读取图片失败")
|
||||
}
|
||||
defer f.Close()
|
||||
return s.SaveImage(userID, f)
|
||||
return s.SaveImage(userID, f, source)
|
||||
}
|
||||
|
||||
// BackgroundFileExists 确认 URL 对应文件在 backgrounds 目录内
|
||||
@@ -667,6 +679,15 @@ func (s *UploadService) ListMedia(userID uint) ([]model.Attachment, error) {
|
||||
return list, err
|
||||
}
|
||||
|
||||
// FindMediaBySource 按来源查本人附件(存在返回记录,不存在返回 error)
|
||||
func (s *UploadService) FindMediaBySource(userID, attachmentID uint, source string) (*model.Attachment, error) {
|
||||
var att model.Attachment
|
||||
if err := s.db.Where("id = ? AND user_id = ? AND source = ?", attachmentID, userID, source).First(&att).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &att, nil
|
||||
}
|
||||
|
||||
// ErrAttachmentInUse 图片正被帖子内容引用,不可物理删除
|
||||
var ErrAttachmentInUse = errors.New("该图片已被帖子使用,无法删除")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user