Files
nl-blogs/server/repositories/about_repository.go
2026-01-16 17:03:34 +08:00

423 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package repositories
import (
"database/sql"
"encoding/json"
"log"
"strconv"
"strings"
"time"
"github.com/niangaodev/art-code/config"
"github.com/niangaodev/art-code/models"
)
// 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) {
query := `
SELECT id, name, avatar, location, bio, email, wechat, tech_stack, experiences, is_primary, created_at, updated_at, deleted_at
FROM about_profiles
WHERE is_primary = TRUE AND deleted_at = 0
LIMIT 1
`
row := config.DB.QueryRow(query)
var profile models.AboutProfile
if err := row.Scan(
&profile.ID,
&profile.Name,
&profile.Avatar,
&profile.Location,
&profile.Bio,
&profile.Email,
&profile.Wechat,
&profile.TechStack,
&profile.ExperiencesStr,
&profile.IsPrimary,
&profile.CreatedAt,
&profile.UpdatedAt,
&profile.DeletedAt,
); err != nil {
if err == sql.ErrNoRows {
// If no primary profile, try to get the first one
return GetFirstAboutProfile()
}
log.Printf("Error scanning 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) {
query := `
SELECT id, name, avatar, location, bio, email, wechat, tech_stack, experiences, is_primary, created_at, updated_at, deleted_at
FROM about_profiles
WHERE deleted_at = 0
ORDER BY id ASC
LIMIT 1
`
row := config.DB.QueryRow(query)
var profile models.AboutProfile
if err := row.Scan(
&profile.ID,
&profile.Name,
&profile.Avatar,
&profile.Location,
&profile.Bio,
&profile.Email,
&profile.Wechat,
&profile.TechStack,
&profile.ExperiencesStr,
&profile.IsPrimary,
&profile.CreatedAt,
&profile.UpdatedAt,
&profile.DeletedAt,
); err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
log.Printf("Error scanning 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) {
query := `
SELECT id, name, avatar, location, bio, email, wechat, tech_stack, experiences, is_primary, created_at, updated_at, deleted_at
FROM about_profiles
WHERE id = ? AND deleted_at = 0
LIMIT 1
`
row := config.DB.QueryRow(query, id)
var profile models.AboutProfile
if err := row.Scan(
&profile.ID,
&profile.Name,
&profile.Avatar,
&profile.Location,
&profile.Bio,
&profile.Email,
&profile.Wechat,
&profile.TechStack,
&profile.ExperiencesStr,
&profile.IsPrimary,
&profile.CreatedAt,
&profile.UpdatedAt,
&profile.DeletedAt,
); err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
log.Printf("Error scanning 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) {
query := "SELECT id, name, avatar, location, bio, email, wechat, tech_stack, experiences, is_primary, created_at, updated_at, deleted_at FROM about_profiles WHERE deleted_at = 0"
rows, err := config.DB.Query(query)
if err != nil {
log.Printf("Error querying about profiles: %v", err)
return nil, err
}
defer rows.Close()
var profiles []models.AboutProfile
for rows.Next() {
var p models.AboutProfile
if err := rows.Scan(
&p.ID,
&p.Name,
&p.Avatar,
&p.Location,
&p.Bio,
&p.Email,
&p.Wechat,
&p.TechStack,
&p.ExperiencesStr,
&p.IsPrimary,
&p.CreatedAt,
&p.UpdatedAt,
&p.DeletedAt,
); err != nil {
continue
}
// 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{}
}
}
}
profiles = append(profiles, p)
}
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)
now := time.Now().Unix()
query := `
INSERT INTO about_profiles (name, avatar, location, bio, email, wechat, tech_stack, experiences, is_primary, created_at, updated_at, deleted_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0)
`
result, err := config.DB.Exec(
query,
profile.Name,
profile.Avatar,
profile.Location,
profile.Bio,
profile.Email,
profile.Wechat,
profile.TechStack,
profile.ExperiencesStr,
profile.IsPrimary,
now,
now,
)
if err != nil {
log.Printf("Error creating about profile: %v", err)
return err
}
id, err := result.LastInsertId()
if err != nil {
return err
}
profile.ID = uint(id)
profile.CreatedAt = now
profile.UpdatedAt = now
// 确保返回的数据包含 TechList 和 ExperienceList(已从 JSON 解析)
// 这些字段已经在上面被 Marshal 了,现在需要确保它们被正确设置
// 由于我们已经 Marshal 了,TechList 和 ExperienceList 应该保持原样
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)
now := time.Now().Unix()
query := `
UPDATE about_profiles
SET name = ?, avatar = ?, location = ?, bio = ?, email = ?, wechat = ?, tech_stack = ?, experiences = ?, is_primary = ?, updated_at = ?
WHERE id = ? AND deleted_at = 0
`
_, err := config.DB.Exec(
query,
profile.Name,
profile.Avatar,
profile.Location,
profile.Bio,
profile.Email,
profile.Wechat,
profile.TechStack,
profile.ExperiencesStr,
profile.IsPrimary,
now,
profile.ID,
)
if err != nil {
log.Printf("Error updating about profile: %v", err)
return err
}
return nil
}
// DeleteAboutProfile 删除个人资料 (Soft Delete)
func DeleteAboutProfile(id uint) error {
now := time.Now().Unix()
query := "UPDATE about_profiles SET deleted_at = ? WHERE id = ?"
_, err := config.DB.Exec(query, now, id)
if err != nil {
log.Printf("Error deleting about profile: %v", err)
return err
}
return nil
}