316 lines
9.7 KiB
Go
316 lines
9.7 KiB
Go
package repositories
|
||
|
||
import (
|
||
"encoding/json"
|
||
"log"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/niangaodev/art-code/config"
|
||
"github.com/niangaodev/art-code/models"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// unescapeJSONString 解码转义的 JSON 字符串
|
||
// 处理两种情况:
|
||
// 1. 被引号包裹的转义 JSON 字符串:`"[\"Vue 3\",...]"`
|
||
// 2. 包含转义引号的 JSON 字符串:`[\"Vue 3\",...]`
|
||
func unescapeJSONString(s string) (string, error) {
|
||
// 如果字符串以引号开头和结尾,说明是被引号包裹的转义 JSON 字符串
|
||
if len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"' {
|
||
unquoted, err := strconv.Unquote(s)
|
||
if err != nil {
|
||
return s, err
|
||
}
|
||
return unquoted, nil
|
||
}
|
||
|
||
// 如果字符串包含转义的引号 \",需要将其转换为普通引号
|
||
// 例如:[\"Vue 3\",...] -> ["Vue 3",...]
|
||
if len(s) > 0 {
|
||
// 尝试直接解析,如果失败则尝试替换转义引号
|
||
var test interface{}
|
||
if err := json.Unmarshal([]byte(s), &test); err != nil {
|
||
// 如果解析失败,尝试将 \" 替换为 "
|
||
unescaped := s
|
||
// 替换转义的反斜杠+引号
|
||
// 注意:这里需要小心处理,因为 \\" 应该变成 \"
|
||
// 但 \" 应该变成 "
|
||
// 使用正则表达式或字符串替换
|
||
// 简单方法:将 \" 替换为 "(但需要确保不会误替换 \\")
|
||
// 更安全的方法:使用 json.Unmarshal 两次解析
|
||
// 或者使用 strings.ReplaceAll 但需要小心
|
||
|
||
// 尝试将 \" 替换为 "
|
||
unescaped = strings.ReplaceAll(unescaped, `\"`, `"`)
|
||
// 如果替换后能解析,返回替换后的字符串
|
||
if err2 := json.Unmarshal([]byte(unescaped), &test); err2 == nil {
|
||
return unescaped, nil
|
||
}
|
||
} else {
|
||
// 如果能直接解析,返回原字符串
|
||
return s, nil
|
||
}
|
||
}
|
||
|
||
// 如果都失败,返回原字符串
|
||
return s, nil
|
||
}
|
||
|
||
// GetPrimaryAboutProfile 获取主页个人资料
|
||
func GetPrimaryAboutProfile() (*models.AboutProfile, error) {
|
||
var profile models.AboutProfile
|
||
err := config.DB.Model(&models.AboutProfile{}).
|
||
Where("is_primary = ? AND deleted_at = ?", true, 0).
|
||
First(&profile).Error
|
||
|
||
if err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
// If no primary profile, try to get the first one
|
||
return GetFirstAboutProfile()
|
||
}
|
||
log.Printf("Error getting primary about profile: %v", err)
|
||
return nil, err
|
||
}
|
||
|
||
// Unmarshal JSON - 确保 TechList 和 ExperienceList 始终是数组而不是 nil
|
||
profile.TechList = []string{}
|
||
profile.ExperienceList = []models.Experience{}
|
||
|
||
if profile.TechStack != "" {
|
||
if err := json.Unmarshal([]byte(profile.TechStack), &profile.TechList); err != nil {
|
||
log.Printf("Error unmarshaling techStack: %v, raw: %s", err, profile.TechStack)
|
||
profile.TechList = []string{}
|
||
}
|
||
}
|
||
|
||
if profile.ExperiencesStr != "" {
|
||
if err := json.Unmarshal([]byte(profile.ExperiencesStr), &profile.ExperienceList); err != nil {
|
||
log.Printf("Error unmarshaling experiences: %v, raw: %s", err, profile.ExperiencesStr)
|
||
profile.ExperienceList = []models.Experience{}
|
||
}
|
||
}
|
||
|
||
return &profile, nil
|
||
}
|
||
|
||
// GetFirstAboutProfile 获取第一个个人资料(备用)
|
||
func GetFirstAboutProfile() (*models.AboutProfile, error) {
|
||
var profile models.AboutProfile
|
||
err := config.DB.Model(&models.AboutProfile{}).
|
||
Where("deleted_at = ?", 0).
|
||
Order("id ASC").
|
||
First(&profile).Error
|
||
|
||
if err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, nil
|
||
}
|
||
log.Printf("Error getting first about profile: %v", err)
|
||
return nil, err
|
||
}
|
||
|
||
// Unmarshal JSON - 确保 TechList 和 ExperienceList 始终是数组而不是 nil
|
||
profile.TechList = []string{}
|
||
profile.ExperienceList = []models.Experience{}
|
||
|
||
if profile.TechStack != "" {
|
||
// 先尝试解码转义的 JSON 字符串
|
||
unescaped, err := unescapeJSONString(profile.TechStack)
|
||
if err != nil {
|
||
log.Printf("Error unescaping techStack: %v, raw: %s", err, profile.TechStack)
|
||
} else {
|
||
if err := json.Unmarshal([]byte(unescaped), &profile.TechList); err != nil {
|
||
log.Printf("Error unmarshaling techStack: %v, raw: %s, unescaped: %s", err, profile.TechStack, unescaped)
|
||
profile.TechList = []string{}
|
||
}
|
||
}
|
||
}
|
||
|
||
if profile.ExperiencesStr != "" {
|
||
// 先尝试解码转义的 JSON 字符串
|
||
unescaped, err := unescapeJSONString(profile.ExperiencesStr)
|
||
if err != nil {
|
||
log.Printf("Error unescaping experiences: %v, raw: %s", err, profile.ExperiencesStr)
|
||
} else {
|
||
if err := json.Unmarshal([]byte(unescaped), &profile.ExperienceList); err != nil {
|
||
log.Printf("Error unmarshaling experiences: %v, raw: %s, unescaped: %s", err, profile.ExperiencesStr, unescaped)
|
||
profile.ExperienceList = []models.Experience{}
|
||
}
|
||
}
|
||
}
|
||
|
||
return &profile, nil
|
||
}
|
||
|
||
// GetAboutProfileByID 根据 ID 获取个人资料
|
||
func GetAboutProfileByID(id uint) (*models.AboutProfile, error) {
|
||
var profile models.AboutProfile
|
||
err := config.DB.Model(&models.AboutProfile{}).
|
||
Where("id = ? AND deleted_at = ?", id, 0).
|
||
First(&profile).Error
|
||
|
||
if err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, nil
|
||
}
|
||
log.Printf("Error getting about profile by ID: %v", err)
|
||
return nil, err
|
||
}
|
||
|
||
// Unmarshal JSON - 确保 TechList 和 ExperienceList 始终是数组而不是 nil
|
||
profile.TechList = []string{}
|
||
profile.ExperienceList = []models.Experience{}
|
||
|
||
if profile.TechStack != "" {
|
||
// 先尝试解码转义的 JSON 字符串
|
||
unescaped, err := unescapeJSONString(profile.TechStack)
|
||
if err != nil {
|
||
log.Printf("Error unescaping techStack: %v, raw: %s", err, profile.TechStack)
|
||
} else {
|
||
if err := json.Unmarshal([]byte(unescaped), &profile.TechList); err != nil {
|
||
log.Printf("Error unmarshaling techStack: %v, raw: %s, unescaped: %s", err, profile.TechStack, unescaped)
|
||
profile.TechList = []string{}
|
||
}
|
||
}
|
||
}
|
||
|
||
if profile.ExperiencesStr != "" {
|
||
// 先尝试解码转义的 JSON 字符串
|
||
unescaped, err := unescapeJSONString(profile.ExperiencesStr)
|
||
if err != nil {
|
||
log.Printf("Error unescaping experiences: %v, raw: %s", err, profile.ExperiencesStr)
|
||
} else {
|
||
if err := json.Unmarshal([]byte(unescaped), &profile.ExperienceList); err != nil {
|
||
log.Printf("Error unmarshaling experiences: %v, raw: %s, unescaped: %s", err, profile.ExperiencesStr, unescaped)
|
||
profile.ExperienceList = []models.Experience{}
|
||
}
|
||
}
|
||
}
|
||
|
||
return &profile, nil
|
||
}
|
||
|
||
// GetAllAboutProfiles 获取所有个人资料(管理用)
|
||
func GetAllAboutProfiles() ([]models.AboutProfile, error) {
|
||
var profiles []models.AboutProfile
|
||
err := config.DB.Model(&models.AboutProfile{}).
|
||
Where("deleted_at = ?", 0).
|
||
Find(&profiles).Error
|
||
if err != nil {
|
||
log.Printf("Error querying about profiles: %v", err)
|
||
return nil, err
|
||
}
|
||
|
||
for i := range profiles {
|
||
var p = &profiles[i]
|
||
// Unmarshal JSON - 确保 TechList 和 ExperienceList 始终是数组而不是 nil
|
||
p.TechList = []string{}
|
||
p.ExperienceList = []models.Experience{}
|
||
|
||
if p.TechStack != "" {
|
||
// 先尝试解码转义的 JSON 字符串
|
||
unescaped, err := unescapeJSONString(p.TechStack)
|
||
if err != nil {
|
||
log.Printf("Error unescaping techStack: %v, raw: %s", err, p.TechStack)
|
||
} else {
|
||
if err := json.Unmarshal([]byte(unescaped), &p.TechList); err != nil {
|
||
log.Printf("Error unmarshaling techStack: %v, raw: %s, unescaped: %s", err, p.TechStack, unescaped)
|
||
p.TechList = []string{}
|
||
}
|
||
}
|
||
}
|
||
|
||
if p.ExperiencesStr != "" {
|
||
// 先尝试解码转义的 JSON 字符串
|
||
unescaped, err := unescapeJSONString(p.ExperiencesStr)
|
||
if err != nil {
|
||
log.Printf("Error unescaping experiences: %v, raw: %s", err, p.ExperiencesStr)
|
||
} else {
|
||
if err := json.Unmarshal([]byte(unescaped), &p.ExperienceList); err != nil {
|
||
log.Printf("Error unmarshaling experiences: %v, raw: %s, unescaped: %s", err, p.ExperiencesStr, unescaped)
|
||
p.ExperienceList = []models.Experience{}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return profiles, nil
|
||
}
|
||
|
||
// CreateAboutProfile 创建个人资料
|
||
func CreateAboutProfile(profile *models.AboutProfile) error {
|
||
// 确保 TechList 和 ExperienceList 不为 nil
|
||
if profile.TechList == nil {
|
||
profile.TechList = []string{}
|
||
}
|
||
if profile.ExperienceList == nil {
|
||
profile.ExperienceList = []models.Experience{}
|
||
}
|
||
|
||
// Marshal JSON
|
||
techBytes, _ := json.Marshal(profile.TechList)
|
||
profile.TechStack = string(techBytes)
|
||
|
||
expBytes, _ := json.Marshal(profile.ExperienceList)
|
||
profile.ExperiencesStr = string(expBytes)
|
||
|
||
err := config.DB.Create(profile).Error
|
||
if err != nil {
|
||
log.Printf("Error creating about profile: %v", err)
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// UpdateAboutProfile 更新个人资料
|
||
func UpdateAboutProfile(profile *models.AboutProfile) error {
|
||
// 确保 TechList 和 ExperienceList 不为 nil
|
||
if profile.TechList == nil {
|
||
profile.TechList = []string{}
|
||
}
|
||
if profile.ExperienceList == nil {
|
||
profile.ExperienceList = []models.Experience{}
|
||
}
|
||
|
||
// Marshal JSON
|
||
techBytes, _ := json.Marshal(profile.TechList)
|
||
profile.TechStack = string(techBytes)
|
||
|
||
expBytes, _ := json.Marshal(profile.ExperienceList)
|
||
profile.ExperiencesStr = string(expBytes)
|
||
|
||
err := config.DB.Model(&models.AboutProfile{}).
|
||
Where("id = ? AND deleted_at = ?", profile.ID, 0).
|
||
Updates(map[string]interface{}{
|
||
"name": profile.Name,
|
||
"avatar": profile.Avatar,
|
||
"location": profile.Location,
|
||
"bio": profile.Bio,
|
||
"email": profile.Email,
|
||
"wechat": profile.Wechat,
|
||
"tech_stack": profile.TechStack,
|
||
"experiences": profile.ExperiencesStr,
|
||
"is_primary": profile.IsPrimary,
|
||
"updated_at": time.Now().Unix(),
|
||
}).Error
|
||
if err != nil {
|
||
log.Printf("Error updating about profile: %v", err)
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// DeleteAboutProfile 删除个人资料 (Soft Delete)
|
||
func DeleteAboutProfile(id uint) error {
|
||
err := config.DB.Model(&models.AboutProfile{}).
|
||
Where("id = ?", id).
|
||
Update("deleted_at", time.Now().Unix()).Error
|
||
if err != nil {
|
||
log.Printf("Error deleting about profile: %v", err)
|
||
return err
|
||
}
|
||
return nil
|
||
}
|