361 lines
8.6 KiB
Go
361 lines
8.6 KiB
Go
package repositories
|
||
|
||
import (
|
||
"encoding/json"
|
||
"log"
|
||
"time"
|
||
|
||
"github.com/niangaodev/art-code/config"
|
||
"github.com/niangaodev/art-code/models"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// GetWorks 获取所有作品
|
||
func GetWorks() ([]models.Work, error) {
|
||
var works []models.Work
|
||
err := config.DB.Model(&models.Work{}).
|
||
Where("deleted_at = ?", 0).
|
||
Order("id DESC").
|
||
Find(&works).Error
|
||
if err != nil {
|
||
log.Printf("Error querying works: %v", err)
|
||
return nil, err
|
||
}
|
||
return works, nil
|
||
}
|
||
|
||
// GetWorkByID 根据ID获取作品
|
||
func GetWorkByID(id string) (*models.Work, error) {
|
||
var work models.Work
|
||
err := config.DB.Model(&models.Work{}).
|
||
Where("id = ? AND deleted_at = ?", id, 0).
|
||
First(&work).Error
|
||
if err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, nil
|
||
}
|
||
log.Printf("Error getting work by ID: %v", err)
|
||
return nil, err
|
||
}
|
||
return &work, nil
|
||
}
|
||
|
||
// GetWorkTechStack 获取作品的技术栈
|
||
func GetWorkTechStack(workID string) ([]models.WorkTechStack, error) {
|
||
var techStacks []models.WorkTechStack
|
||
err := config.DB.Model(&models.WorkTechStack{}).
|
||
Where("work_id = ? AND deleted_at = ?", workID, 0).
|
||
Find(&techStacks).Error
|
||
if err != nil {
|
||
log.Printf("Error querying work tech stack: %v", err)
|
||
return nil, err
|
||
}
|
||
return techStacks, nil
|
||
}
|
||
|
||
// GetWorkGallery 获取作品的图库
|
||
func GetWorkGallery(workID string) ([]models.WorkGallery, error) {
|
||
var galleries []models.WorkGallery
|
||
err := config.DB.Model(&models.WorkGallery{}).
|
||
Where("work_id = ? AND deleted_at = ?", workID, 0).
|
||
Order("sort_order").
|
||
Find(&galleries).Error
|
||
if err != nil {
|
||
log.Printf("Error querying work gallery: %v", err)
|
||
return nil, err
|
||
}
|
||
return galleries, nil
|
||
}
|
||
|
||
// BuildWorkResponse 构建作品响应,包含关联数据
|
||
func BuildWorkResponse(work *models.Work) (*models.WorkResponse, error) {
|
||
// 获取技术栈
|
||
techStacks, err := GetWorkTechStack(work.ID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 按类别分组技术栈
|
||
techStackMap := make(map[string][]string)
|
||
for _, ts := range techStacks {
|
||
techStackMap[ts.Category] = append(techStackMap[ts.Category], ts.Item)
|
||
}
|
||
|
||
// 转换为前端期望的格式
|
||
var techStackResponse []map[string]interface{}
|
||
for category, items := range techStackMap {
|
||
techStackResponse = append(techStackResponse, map[string]interface{}{
|
||
"category": category,
|
||
"items": items,
|
||
})
|
||
}
|
||
|
||
// 获取图库
|
||
galleries, err := GetWorkGallery(work.ID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 提取图片URL
|
||
var galleryImages []string
|
||
for _, g := range galleries {
|
||
galleryImages = append(galleryImages, g.ImageURL)
|
||
}
|
||
|
||
// 解析links JSON
|
||
var linksData map[string]interface{}
|
||
if work.Links != "" {
|
||
if err := json.Unmarshal([]byte(work.Links), &linksData); err != nil {
|
||
log.Printf("Error parsing links JSON: %v", err)
|
||
linksData = map[string]interface{}{"live": ""}
|
||
}
|
||
} else {
|
||
linksData = map[string]interface{}{"live": ""}
|
||
}
|
||
|
||
// 获取下一个作品ID
|
||
nextWorkID, err := GetNextWorkID(work.ID)
|
||
if err != nil {
|
||
log.Printf("Error getting next work ID: %v", err)
|
||
nextWorkID = ""
|
||
}
|
||
|
||
// 解析视频播放地址:优先 video_id,否则 hero_video
|
||
videoURL := ""
|
||
if work.VideoID != "" {
|
||
videoURL = ResolveVideoURL(work.VideoID)
|
||
}
|
||
if videoURL == "" {
|
||
videoURL = work.HeroVideo
|
||
}
|
||
|
||
return &models.WorkResponse{
|
||
ID: work.ID,
|
||
Title: work.Title,
|
||
Category: work.Category,
|
||
Year: work.Year,
|
||
HeroImg: work.HeroImg,
|
||
HeroVideo: work.HeroVideo,
|
||
VideoID: work.VideoID,
|
||
VideoURL: videoURL,
|
||
Desc: work.Description,
|
||
TechStack: techStackResponse,
|
||
Gallery: galleryImages,
|
||
Links: linksData,
|
||
Next: nextWorkID,
|
||
}, nil
|
||
}
|
||
|
||
// GetNextWorkID 获取下一个作品ID(简单实现,实际可能需要更复杂的逻辑)
|
||
func GetNextWorkID(currentID string) (string, error) {
|
||
var ids []string
|
||
err := config.DB.Model(&models.Work{}).
|
||
Select("id").
|
||
Where("deleted_at = ?", 0).
|
||
Pluck("id", &ids).Error
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
// 找到当前ID的索引
|
||
index := -1
|
||
for i, id := range ids {
|
||
if id == currentID {
|
||
index = i
|
||
break
|
||
}
|
||
}
|
||
|
||
// 如果没找到或者是最后一个,返回第一个
|
||
if index == -1 || index == len(ids)-1 {
|
||
if len(ids) > 0 {
|
||
return ids[0], nil
|
||
}
|
||
return "", nil
|
||
}
|
||
|
||
// 返回下一个
|
||
return ids[index+1], nil
|
||
}
|
||
|
||
// CreateWorkTechStack 创建作品技术栈
|
||
func CreateWorkTechStack(workID string, techStack []map[string]interface{}) error {
|
||
// 先删除旧的技术栈(软删除)
|
||
err := config.DB.Model(&models.WorkTechStack{}).
|
||
Where("work_id = ?", workID).
|
||
Update("deleted_at", time.Now().Unix()).Error
|
||
if err != nil {
|
||
log.Printf("Error deleting old tech stack: %v", err)
|
||
}
|
||
|
||
// 创建新的技术栈
|
||
for _, categoryData := range techStack {
|
||
category, ok := categoryData["category"].(string)
|
||
if !ok {
|
||
continue
|
||
}
|
||
items, ok := categoryData["items"].([]interface{})
|
||
if !ok {
|
||
continue
|
||
}
|
||
for _, item := range items {
|
||
itemStr, ok := item.(string)
|
||
if !ok || itemStr == "" {
|
||
continue
|
||
}
|
||
techStackItem := models.WorkTechStack{
|
||
WorkID: workID,
|
||
Category: category,
|
||
Item: itemStr,
|
||
CreatedAt: time.Now().Unix(),
|
||
DeletedAt: 0,
|
||
}
|
||
if err := config.DB.Create(&techStackItem).Error; err != nil {
|
||
log.Printf("Error creating tech stack item: %v", err)
|
||
return err
|
||
}
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// CreateWorkGallery 创建作品图库
|
||
func CreateWorkGallery(workID string, gallery []string) error {
|
||
// 先删除旧的图库(软删除)
|
||
err := config.DB.Model(&models.WorkGallery{}).
|
||
Where("work_id = ?", workID).
|
||
Update("deleted_at", time.Now().Unix()).Error
|
||
if err != nil {
|
||
log.Printf("Error deleting old gallery: %v", err)
|
||
}
|
||
|
||
// 创建新的图库
|
||
for index, imageURL := range gallery {
|
||
if imageURL == "" {
|
||
continue
|
||
}
|
||
galleryItem := models.WorkGallery{
|
||
WorkID: workID,
|
||
ImageURL: imageURL,
|
||
SortOrder: uint(index + 1),
|
||
Description: "",
|
||
CreatedAt: time.Now().Unix(),
|
||
DeletedAt: 0,
|
||
}
|
||
if err := config.DB.Create(&galleryItem).Error; err != nil {
|
||
log.Printf("Error creating gallery item: %v", err)
|
||
return err
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// CreateWork 创建作品
|
||
func CreateWork(work *models.Work) error {
|
||
err := config.DB.Create(work).Error
|
||
if err != nil {
|
||
log.Printf("Error creating work: %v", err)
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// UpdateWork 更新作品
|
||
func UpdateWork(work *models.Work) error {
|
||
updateData := map[string]interface{}{
|
||
"title": work.Title,
|
||
"category": work.Category,
|
||
"year": work.Year,
|
||
"hero_img": work.HeroImg,
|
||
"hero_video": work.HeroVideo,
|
||
"video_id": work.VideoID,
|
||
"description": work.Description,
|
||
"is_featured": work.IsFeatured,
|
||
"updated_at": time.Now().Unix(),
|
||
}
|
||
if work.Links != "" {
|
||
updateData["links"] = work.Links
|
||
}
|
||
err := config.DB.Model(&models.Work{}).
|
||
Where("id = ? AND deleted_at = ?", work.ID, 0).
|
||
Updates(updateData).Error
|
||
if err != nil {
|
||
log.Printf("Error updating work: %v", err)
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// DeleteWork 删除作品 (Soft Delete)
|
||
func DeleteWork(id string) error {
|
||
err := config.DB.Model(&models.Work{}).
|
||
Where("id = ?", id).
|
||
Update("deleted_at", time.Now().Unix()).Error
|
||
if err != nil {
|
||
log.Printf("Error deleting work: %v", err)
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// GetAdminWorks 获取后台作品列表 (分页)
|
||
func GetAdminWorks(page, pageSize int) ([]models.Work, int, error) {
|
||
offset := (page - 1) * pageSize
|
||
|
||
var works []models.Work
|
||
var total int64
|
||
|
||
// 获取总数
|
||
err := config.DB.Model(&models.Work{}).
|
||
Where("deleted_at = ?", 0).
|
||
Count(&total).Error
|
||
if err != nil {
|
||
log.Printf("Error getting work count: %v", err)
|
||
return nil, 0, err
|
||
}
|
||
|
||
// 获取列表
|
||
err = config.DB.Model(&models.Work{}).
|
||
Where("deleted_at = ?", 0).
|
||
Order("created_at DESC").
|
||
Limit(pageSize).
|
||
Offset(offset).
|
||
Find(&works).Error
|
||
if err != nil {
|
||
log.Printf("Error querying admin works: %v", err)
|
||
return nil, 0, err
|
||
}
|
||
|
||
return works, int(total), nil
|
||
}
|
||
|
||
// BuildWorksResponse 构建作品列表响应
|
||
func BuildWorksResponse(works []models.Work) []models.WorkResponse {
|
||
var responses []models.WorkResponse
|
||
for _, work := range works {
|
||
// 这里不包含详情,简化处理
|
||
responses = append(responses, models.WorkResponse{
|
||
ID: work.ID,
|
||
Title: work.Title,
|
||
Category: work.Category,
|
||
Year: work.Year,
|
||
HeroImg: work.HeroImg,
|
||
Desc: work.Description,
|
||
})
|
||
}
|
||
return responses
|
||
}
|
||
|
||
// GetWorkCount 获取作品总数
|
||
func GetWorkCount() (int, error) {
|
||
var count int64
|
||
err := config.DB.Model(&models.Work{}).
|
||
Where("deleted_at = ?", 0).
|
||
Count(&count).Error
|
||
if err != nil {
|
||
log.Printf("Error getting work count: %v", err)
|
||
return 0, err
|
||
}
|
||
return int(count), nil
|
||
}
|