448 lines
11 KiB
Go
448 lines
11 KiB
Go
package handlers
|
||
|
||
import (
|
||
"log"
|
||
"strconv"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"github.com/niangaodev/art-code/models"
|
||
"github.com/niangaodev/art-code/repositories"
|
||
"github.com/niangaodev/art-code/utils"
|
||
)
|
||
|
||
// AdminUploadAttachment 上传附件
|
||
func AdminUploadAttachment(c *gin.Context) {
|
||
// 获取文件
|
||
file, err := c.FormFile("file")
|
||
if err != nil {
|
||
utils.Error(c, 400, "No file uploaded")
|
||
return
|
||
}
|
||
|
||
// 获取分类ID(可选)
|
||
categoryIDStr := c.PostForm("categoryId")
|
||
var categoryID *uint
|
||
if categoryIDStr != "" {
|
||
if id, err := strconv.ParseUint(categoryIDStr, 10, 32); err == nil {
|
||
idUint := uint(id)
|
||
categoryID = &idUint
|
||
}
|
||
}
|
||
|
||
// 获取存储类型
|
||
storageType := c.DefaultPostForm("storageType", "local")
|
||
|
||
// 打开文件
|
||
src, err := file.Open()
|
||
if err != nil {
|
||
utils.Error(c, 500, "Failed to open file")
|
||
return
|
||
}
|
||
defer src.Close()
|
||
|
||
// 获取OSS配置
|
||
var ossConfig *models.OSSConfig
|
||
if storageType != "local" {
|
||
ossConfig, err = repositories.GetActiveOSSConfig(storageType)
|
||
if err != nil || ossConfig == nil {
|
||
utils.Error(c, 400, "OSS config not found or not active")
|
||
return
|
||
}
|
||
}
|
||
|
||
// 创建OSS配置对象
|
||
config := &utils.OSSConfig{
|
||
StorageType: storageType,
|
||
}
|
||
|
||
if ossConfig != nil {
|
||
// 解密AccessKey和SecretKey
|
||
accessKey, err := utils.DecryptAES(ossConfig.AccessKey)
|
||
if err != nil {
|
||
log.Printf("Failed to decrypt access key: %v", err)
|
||
utils.Error(c, 500, "Failed to decrypt OSS credentials")
|
||
return
|
||
}
|
||
|
||
secretKey, err := utils.DecryptAES(ossConfig.SecretKey)
|
||
if err != nil {
|
||
log.Printf("Failed to decrypt secret key: %v", err)
|
||
utils.Error(c, 500, "Failed to decrypt OSS credentials")
|
||
return
|
||
}
|
||
|
||
config.AccessKey = accessKey
|
||
config.SecretKey = secretKey
|
||
config.Bucket = ossConfig.Bucket
|
||
config.Region = ossConfig.Region
|
||
config.Domain = ossConfig.Domain
|
||
}
|
||
|
||
// 获取上传器
|
||
uploader, err := utils.GetOSSUploader(config)
|
||
if err != nil {
|
||
utils.Error(c, 500, err.Error())
|
||
return
|
||
}
|
||
|
||
// 上传文件
|
||
filePath, fileURL, err := uploader.Upload(src, file.Filename, file.Size)
|
||
if err != nil {
|
||
utils.Error(c, 500, "Failed to upload file: "+err.Error())
|
||
return
|
||
}
|
||
|
||
// 获取文件类型
|
||
fileType := utils.GetFileType(file.Header.Get("Content-Type"))
|
||
|
||
// 创建附件记录
|
||
attachment := &models.Attachment{
|
||
CategoryID: categoryID,
|
||
OriginalName: file.Filename,
|
||
StoredName: file.Filename,
|
||
FilePath: filePath,
|
||
FileURL: fileURL,
|
||
FileSize: file.Size,
|
||
FileType: fileType,
|
||
MimeType: file.Header.Get("Content-Type"),
|
||
StorageType: storageType,
|
||
}
|
||
|
||
if ossConfig != nil {
|
||
ossConfigID := ossConfig.ID
|
||
attachment.OSSConfigID = &ossConfigID
|
||
}
|
||
|
||
if err := repositories.CreateAttachment(attachment); err != nil {
|
||
// 如果数据库保存失败,尝试删除已上传的文件
|
||
uploader.Delete(filePath)
|
||
utils.ServerError(c, err)
|
||
return
|
||
}
|
||
|
||
utils.SuccessWithMsg(c, "File uploaded successfully", attachment)
|
||
}
|
||
|
||
// AdminGetAttachments 获取附件列表
|
||
func AdminGetAttachments(c *gin.Context) {
|
||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "20"))
|
||
categoryIDStr := c.Query("categoryId")
|
||
fileType := c.Query("fileType")
|
||
|
||
var categoryID *uint
|
||
if categoryIDStr != "" {
|
||
if id, err := strconv.ParseUint(categoryIDStr, 10, 32); err == nil {
|
||
idUint := uint(id)
|
||
categoryID = &idUint
|
||
}
|
||
}
|
||
|
||
attachments, total, err := repositories.GetAttachments(page, pageSize, categoryID, fileType)
|
||
if err != nil {
|
||
utils.ServerError(c, err)
|
||
return
|
||
}
|
||
|
||
res := gin.H{
|
||
"list": attachments,
|
||
"total": total,
|
||
"page": page,
|
||
"size": pageSize,
|
||
}
|
||
|
||
utils.Success(c, res)
|
||
}
|
||
|
||
// AdminDeleteAttachment 删除附件
|
||
func AdminDeleteAttachment(c *gin.Context) {
|
||
idStr := c.Param("id")
|
||
var id uint
|
||
if _, err := strconv.ParseUint(idStr, 10, 32); err != nil {
|
||
utils.Error(c, 400, "Invalid attachment ID")
|
||
return
|
||
}
|
||
|
||
// 获取附件信息
|
||
attachment, err := repositories.GetAttachmentByID(id)
|
||
if err != nil || attachment == nil {
|
||
utils.Error(c, 404, "Attachment not found")
|
||
return
|
||
}
|
||
|
||
// 获取OSS配置并删除文件
|
||
if attachment.StorageType != "" {
|
||
var ossConfig *models.OSSConfig
|
||
if attachment.OSSConfigID != nil {
|
||
ossConfig, err = repositories.GetOSSConfigByID(*attachment.OSSConfigID)
|
||
} else {
|
||
ossConfig, err = repositories.GetActiveOSSConfig(attachment.StorageType)
|
||
}
|
||
|
||
if err == nil && ossConfig != nil {
|
||
config := &utils.OSSConfig{
|
||
StorageType: attachment.StorageType,
|
||
}
|
||
|
||
// 解密密钥
|
||
if accessKey, err := utils.DecryptAES(ossConfig.AccessKey); err == nil {
|
||
config.AccessKey = accessKey
|
||
}
|
||
if secretKey, err := utils.DecryptAES(ossConfig.SecretKey); err == nil {
|
||
config.SecretKey = secretKey
|
||
}
|
||
config.Bucket = ossConfig.Bucket
|
||
config.Region = ossConfig.Region
|
||
config.Domain = ossConfig.Domain
|
||
|
||
uploader, err := utils.GetOSSUploader(config)
|
||
if err == nil {
|
||
uploader.Delete(attachment.FilePath)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 删除数据库记录
|
||
if err := repositories.DeleteAttachment(id); err != nil {
|
||
utils.ServerError(c, err)
|
||
return
|
||
}
|
||
|
||
utils.SuccessWithMsg(c, "Attachment deleted successfully", nil)
|
||
}
|
||
|
||
// AdminGetAttachmentCategories 获取附件分类列表
|
||
func AdminGetAttachmentCategories(c *gin.Context) {
|
||
categories, err := repositories.GetAttachmentCategories()
|
||
if err != nil {
|
||
utils.ServerError(c, err)
|
||
return
|
||
}
|
||
|
||
utils.Success(c, categories)
|
||
}
|
||
|
||
// AdminCreateAttachmentCategory 创建附件分类
|
||
func AdminCreateAttachmentCategory(c *gin.Context) {
|
||
var category models.AttachmentCategory
|
||
if err := c.ShouldBindJSON(&category); err != nil {
|
||
utils.Error(c, 400, "Invalid request")
|
||
return
|
||
}
|
||
|
||
if err := repositories.CreateAttachmentCategory(&category); err != nil {
|
||
utils.ServerError(c, err)
|
||
return
|
||
}
|
||
|
||
utils.SuccessWithMsg(c, "Category created successfully", category)
|
||
}
|
||
|
||
// AdminUpdateAttachmentCategory 更新附件分类
|
||
func AdminUpdateAttachmentCategory(c *gin.Context) {
|
||
idStr := c.Param("id")
|
||
var id uint
|
||
if _, err := strconv.ParseUint(idStr, 10, 32); err != nil {
|
||
utils.Error(c, 400, "Invalid category ID")
|
||
return
|
||
}
|
||
|
||
var category models.AttachmentCategory
|
||
if err := c.ShouldBindJSON(&category); err != nil {
|
||
utils.Error(c, 400, "Invalid request")
|
||
return
|
||
}
|
||
|
||
category.ID = id
|
||
|
||
if err := repositories.UpdateAttachmentCategory(&category); err != nil {
|
||
utils.ServerError(c, err)
|
||
return
|
||
}
|
||
|
||
utils.SuccessWithMsg(c, "Category updated successfully", nil)
|
||
}
|
||
|
||
// AdminDeleteAttachmentCategory 删除附件分类
|
||
func AdminDeleteAttachmentCategory(c *gin.Context) {
|
||
idStr := c.Param("id")
|
||
var id uint
|
||
if _, err := strconv.ParseUint(idStr, 10, 32); err != nil {
|
||
utils.Error(c, 400, "Invalid category ID")
|
||
return
|
||
}
|
||
|
||
if err := repositories.DeleteAttachmentCategory(id); err != nil {
|
||
utils.ServerError(c, err)
|
||
return
|
||
}
|
||
|
||
utils.SuccessWithMsg(c, "Category deleted successfully", nil)
|
||
}
|
||
|
||
// AdminGetOSSConfigs 获取OSS配置列表
|
||
func AdminGetOSSConfigs(c *gin.Context) {
|
||
configs, err := repositories.GetOSSConfigs()
|
||
if err != nil {
|
||
utils.ServerError(c, err)
|
||
return
|
||
}
|
||
|
||
// 不返回加密的密钥
|
||
for i := range configs {
|
||
configs[i].AccessKey = "***"
|
||
configs[i].SecretKey = "***"
|
||
}
|
||
|
||
utils.Success(c, configs)
|
||
}
|
||
|
||
// AdminCreateOSSConfig 创建OSS配置
|
||
func AdminCreateOSSConfig(c *gin.Context) {
|
||
var req struct {
|
||
Name string `json:"name" binding:"required"`
|
||
StorageType string `json:"storageType" binding:"required"`
|
||
AccessKey string `json:"accessKey" binding:"required"`
|
||
SecretKey string `json:"secretKey" binding:"required"`
|
||
Bucket string `json:"bucket"`
|
||
Region string `json:"region"`
|
||
Domain string `json:"domain"`
|
||
IsActive int `json:"isActive"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
utils.Error(c, 400, "Invalid request")
|
||
return
|
||
}
|
||
|
||
// 加密密钥
|
||
encryptedAccessKey, err := utils.EncryptAES(req.AccessKey)
|
||
if err != nil {
|
||
utils.Error(c, 500, "Failed to encrypt access key")
|
||
return
|
||
}
|
||
|
||
encryptedSecretKey, err := utils.EncryptAES(req.SecretKey)
|
||
if err != nil {
|
||
utils.Error(c, 500, "Failed to encrypt secret key")
|
||
return
|
||
}
|
||
|
||
ossConfig := &models.OSSConfig{
|
||
Name: req.Name,
|
||
StorageType: req.StorageType,
|
||
AccessKey: encryptedAccessKey,
|
||
SecretKey: encryptedSecretKey,
|
||
Bucket: req.Bucket,
|
||
Region: req.Region,
|
||
Domain: req.Domain,
|
||
IsActive: req.IsActive,
|
||
}
|
||
|
||
if err := repositories.CreateOSSConfig(ossConfig); err != nil {
|
||
utils.ServerError(c, err)
|
||
return
|
||
}
|
||
|
||
// 不返回加密的密钥
|
||
ossConfig.AccessKey = "***"
|
||
ossConfig.SecretKey = "***"
|
||
|
||
utils.SuccessWithMsg(c, "OSS config created successfully", ossConfig)
|
||
}
|
||
|
||
// AdminUpdateOSSConfig 更新OSS配置
|
||
func AdminUpdateOSSConfig(c *gin.Context) {
|
||
idStr := c.Param("id")
|
||
var id uint
|
||
if _, err := strconv.ParseUint(idStr, 10, 32); err != nil {
|
||
utils.Error(c, 400, "Invalid OSS config ID")
|
||
return
|
||
}
|
||
|
||
var req struct {
|
||
Name string `json:"name"`
|
||
StorageType string `json:"storageType"`
|
||
AccessKey string `json:"accessKey"`
|
||
SecretKey string `json:"secretKey"`
|
||
Bucket string `json:"bucket"`
|
||
Region string `json:"region"`
|
||
Domain string `json:"domain"`
|
||
IsActive int `json:"isActive"`
|
||
}
|
||
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
utils.Error(c, 400, "Invalid request")
|
||
return
|
||
}
|
||
|
||
// 获取现有配置
|
||
existingConfig, err := repositories.GetOSSConfigByID(id)
|
||
if err != nil || existingConfig == nil {
|
||
utils.Error(c, 404, "OSS config not found")
|
||
return
|
||
}
|
||
|
||
// 更新字段
|
||
if req.Name != "" {
|
||
existingConfig.Name = req.Name
|
||
}
|
||
if req.StorageType != "" {
|
||
existingConfig.StorageType = req.StorageType
|
||
}
|
||
if req.AccessKey != "" && req.AccessKey != "***" {
|
||
encryptedAccessKey, err := utils.EncryptAES(req.AccessKey)
|
||
if err != nil {
|
||
utils.Error(c, 500, "Failed to encrypt access key")
|
||
return
|
||
}
|
||
existingConfig.AccessKey = encryptedAccessKey
|
||
}
|
||
if req.SecretKey != "" && req.SecretKey != "***" {
|
||
encryptedSecretKey, err := utils.EncryptAES(req.SecretKey)
|
||
if err != nil {
|
||
utils.Error(c, 500, "Failed to encrypt secret key")
|
||
return
|
||
}
|
||
existingConfig.SecretKey = encryptedSecretKey
|
||
}
|
||
if req.Bucket != "" {
|
||
existingConfig.Bucket = req.Bucket
|
||
}
|
||
if req.Region != "" {
|
||
existingConfig.Region = req.Region
|
||
}
|
||
if req.Domain != "" {
|
||
existingConfig.Domain = req.Domain
|
||
}
|
||
existingConfig.IsActive = req.IsActive
|
||
|
||
if err := repositories.UpdateOSSConfig(existingConfig); err != nil {
|
||
utils.ServerError(c, err)
|
||
return
|
||
}
|
||
|
||
// 不返回加密的密钥
|
||
existingConfig.AccessKey = "***"
|
||
existingConfig.SecretKey = "***"
|
||
|
||
utils.SuccessWithMsg(c, "OSS config updated successfully", existingConfig)
|
||
}
|
||
|
||
// AdminDeleteOSSConfig 删除OSS配置
|
||
func AdminDeleteOSSConfig(c *gin.Context) {
|
||
idStr := c.Param("id")
|
||
var id uint
|
||
if _, err := strconv.ParseUint(idStr, 10, 32); err != nil {
|
||
utils.Error(c, 400, "Invalid OSS config ID")
|
||
return
|
||
}
|
||
|
||
if err := repositories.DeleteOSSConfig(id); err != nil {
|
||
utils.ServerError(c, err)
|
||
return
|
||
}
|
||
|
||
utils.SuccessWithMsg(c, "OSS config deleted successfully", nil)
|
||
}
|