文章和文件建立关联
This commit is contained in:
@@ -38,12 +38,12 @@ var adminMenus = []AdminMenuGroup{
|
||||
{Key: "posts", Name: "文章管理"},
|
||||
{Key: "categories", Name: "分类管理"},
|
||||
{Key: "tags", Name: "标签管理"},
|
||||
{Key: "files", Name: "文件管理"},
|
||||
{Key: "pages", Name: "页面管理"},
|
||||
{Key: "comments", Name: "评论管理"},
|
||||
}},
|
||||
{Group: "系统管理", Items: []AdminMenuItem{
|
||||
{Key: "users", Name: "用户管理"},
|
||||
{Key: "files", Name: "文件管理"},
|
||||
{Key: "themes", Name: "主题管理"},
|
||||
{Key: "settings", Name: "基础设置"},
|
||||
}},
|
||||
|
||||
@@ -2,6 +2,8 @@ package backend
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -34,6 +36,7 @@ type CreatePostRequest struct {
|
||||
Tags []string `json:"tags"`
|
||||
Status string `json:"status"`
|
||||
IsTop bool `json:"is_top"`
|
||||
FileIDs []uint `json:"file_ids"` // 正文中使用的文件ID列表(按出现顺序)
|
||||
}
|
||||
|
||||
// 更新文章请求
|
||||
@@ -46,6 +49,7 @@ type UpdatePostRequest struct {
|
||||
Tags []string `json:"tags"`
|
||||
Status string `json:"status"`
|
||||
IsTop bool `json:"is_top"`
|
||||
FileIDs []uint `json:"file_ids"` // 正文中使用的文件ID列表(按出现顺序)
|
||||
}
|
||||
|
||||
// Markdown 预览请求
|
||||
@@ -221,6 +225,15 @@ func CreatePost(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 处理文件关联(包含封面)
|
||||
if len(req.FileIDs) > 0 {
|
||||
var files []models.File
|
||||
database.DB.Where("id IN ?", req.FileIDs).Find(&files)
|
||||
if len(files) > 0 {
|
||||
database.DB.Model(&post).Association("Files").Replace(files)
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{"data": post})
|
||||
}
|
||||
|
||||
@@ -260,9 +273,8 @@ func UpdatePost(c *gin.Context) {
|
||||
if req.Summary != "" {
|
||||
updates["summary"] = req.Summary
|
||||
}
|
||||
if req.Cover != "" {
|
||||
updates["cover"] = req.Cover
|
||||
}
|
||||
// 封面始终更新(空字符串表示移除封面)
|
||||
updates["cover"] = req.Cover
|
||||
if req.CategoryID > 0 {
|
||||
updates["category_id"] = req.CategoryID
|
||||
}
|
||||
@@ -297,6 +309,20 @@ func UpdatePost(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 处理文件关联(包含封面)
|
||||
if req.FileIDs != nil {
|
||||
if len(req.FileIDs) > 0 {
|
||||
var files []models.File
|
||||
database.DB.Where("id IN ?", req.FileIDs).Find(&files)
|
||||
if len(files) > 0 {
|
||||
database.DB.Model(&post).Association("Files").Replace(files)
|
||||
}
|
||||
} else {
|
||||
// 空数组,清除所有关联
|
||||
database.DB.Model(&post).Association("Files").Clear()
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": post})
|
||||
}
|
||||
|
||||
@@ -319,6 +345,26 @@ func DeletePost(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取关联文件,删除物理文件和数据库记录
|
||||
var files []models.File
|
||||
database.DB.Model(&post).Association("Files").Find(&files)
|
||||
for _, f := range files {
|
||||
// 检查文件是否还被其他文章使用
|
||||
var count int64
|
||||
database.DB.Table("post_files").Where("file_id = ? AND post_id != ?", f.ID, post.ID).Count(&count)
|
||||
if count == 0 {
|
||||
// 没有其他文章使用,删除物理文件
|
||||
physPath := filepath.Join("./static/uploads", f.FilePath)
|
||||
if _, err := os.Stat(physPath); err == nil {
|
||||
os.Remove(physPath)
|
||||
}
|
||||
// 删除 files 表记录
|
||||
database.DB.Delete(&f)
|
||||
}
|
||||
}
|
||||
// 清除关联关系
|
||||
database.DB.Model(&post).Association("Files").Clear()
|
||||
|
||||
if err := database.DB.Delete(&post).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "删除文章失败"})
|
||||
return
|
||||
|
||||
@@ -156,6 +156,7 @@ func UploadImage(c *gin.Context) {
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"id": fileRecord.ID,
|
||||
"url": uploadURLPrefix + strings.ReplaceAll(relPath, "\\", "/"),
|
||||
"name": filename,
|
||||
"size": file.Size,
|
||||
@@ -189,11 +190,38 @@ func ListUploads(c *gin.Context) {
|
||||
MimeType string `json:"mime_type"`
|
||||
UserID uint `json:"user_id"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
PostTitle string `json:"post_title"` // 关联文章标题,为空表示未关联
|
||||
PostID uint `json:"post_id"` // 关联文章ID
|
||||
}
|
||||
|
||||
// 批量查询文件关联信息
|
||||
fileIDs := make([]uint, 0, len(files))
|
||||
for _, f := range files {
|
||||
fileIDs = append(fileIDs, f.ID)
|
||||
}
|
||||
|
||||
// 查询关联关系(带文章标题)
|
||||
type PostFileResult struct {
|
||||
FileID uint `json:"file_id"`
|
||||
PostID uint `json:"post_id"`
|
||||
PostTitle string `json:"post_title"`
|
||||
}
|
||||
var postFileResults []PostFileResult
|
||||
database.DB.Table("post_files").
|
||||
Select("post_files.file_id, post_files.post_id, posts.title as post_title").
|
||||
Joins("LEFT JOIN posts ON posts.id = post_files.post_id").
|
||||
Where("post_files.file_id IN ?", fileIDs).
|
||||
Find(&postFileResults)
|
||||
|
||||
// 构建 fileID -> PostFileResult 映射
|
||||
postFileMap := make(map[uint]PostFileResult)
|
||||
for _, pf := range postFileResults {
|
||||
postFileMap[pf.FileID] = pf
|
||||
}
|
||||
|
||||
items := make([]FileItem, 0, len(files))
|
||||
for _, f := range files {
|
||||
items = append(items, FileItem{
|
||||
item := FileItem{
|
||||
ID: f.ID,
|
||||
Name: f.FileName,
|
||||
OrigName: f.OrigName,
|
||||
@@ -202,7 +230,13 @@ func ListUploads(c *gin.Context) {
|
||||
MimeType: f.MimeType,
|
||||
UserID: f.UserID,
|
||||
CreatedAt: f.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
// 填充关联信息
|
||||
if pf, ok := postFileMap[f.ID]; ok {
|
||||
item.PostTitle = pf.PostTitle
|
||||
item.PostID = pf.PostID
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"data": items})
|
||||
@@ -230,6 +264,14 @@ func DeleteUpload(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检查文件是否被文章使用
|
||||
var count int64
|
||||
database.DB.Table("post_files").Where("file_id = ?", fileID).Count(&count)
|
||||
if count > 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "该文件已被文章使用,无法删除"})
|
||||
return
|
||||
}
|
||||
|
||||
// 删除物理文件
|
||||
physPath := filepath.Join(uploadDir, file.FilePath)
|
||||
if _, err := os.Stat(physPath); err == nil {
|
||||
|
||||
@@ -91,7 +91,7 @@ func IndexView(c *gin.Context) {
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
pageSize := 10
|
||||
pageSize := 5
|
||||
|
||||
categoryID, _ := strconv.Atoi(c.Query("category"))
|
||||
tagID, _ := strconv.Atoi(c.Query("tag"))
|
||||
|
||||
Reference in New Issue
Block a user