feat: MBTI头像与用户规则链路升级,三端页面与接口同步

Made-with: Cursor
This commit is contained in:
卡若
2026-03-24 01:22:50 +08:00
parent fa3da12b16
commit 1d56d0336c
71 changed files with 3848 additions and 1621 deletions

View File

@@ -85,6 +85,8 @@ type cachedPartRow struct {
Subtitle string `json:"subtitle"`
ChapterCount int `json:"chapterCount"`
MinSortOrder int `json:"minSortOrder"`
// Icon 可选system_config.book_part_icons JSON 中按 part_id 配置的封面图 URL
Icon string `json:"icon,omitempty"`
}
type cachedFixedItem struct {
ID string `json:"id"`
@@ -109,6 +111,48 @@ var bookPartsCache struct {
const bookPartsCacheTTL = 30 * time.Second
// loadBookPartIconURLs 读取 system_config.book_part_icons{"part-1":"https://..."}key 与 chapters.part_id 一致
func loadBookPartIconURLs() map[string]string {
out := map[string]string{}
var row model.SystemConfig
if err := database.DB().Where("config_key = ?", "book_part_icons").First(&row).Error; err != nil {
return out
}
var raw map[string]interface{}
if err := json.Unmarshal(row.ConfigValue, &raw); err != nil {
return out
}
for k, v := range raw {
k = strings.TrimSpace(k)
if k == "" {
continue
}
if s, ok := v.(string); ok {
s = strings.TrimSpace(s)
if s != "" {
out[k] = s
}
}
}
return out
}
// mergeBookPartIcons 将配置中的篇封面 URL 写入 parts每次接口响应前调用避免 Redis 旧缓存缺 icon
func mergeBookPartIcons(parts []cachedPartRow) {
if len(parts) == 0 {
return
}
m := loadBookPartIconURLs()
if len(m) == 0 {
return
}
for i := range parts {
if u := strings.TrimSpace(m[parts[i].PartID]); u != "" {
parts[i].Icon = u
}
}
}
// chaptersByPartCache 篇章内章节列表内存缓存30 秒 TTL
type chaptersByPartEntry struct {
data []model.Chapter
@@ -320,6 +364,7 @@ func BookParts(c *gin.Context) {
// 1. 优先 Redis后台无更新时长期有效
var redisPayload bookPartsRedisPayload
if cache.Get(context.Background(), cache.KeyBookParts, &redisPayload) && len(redisPayload.Parts) > 0 {
mergeBookPartIcons(redisPayload.Parts)
c.JSON(http.StatusOK, gin.H{
"success": true,
"parts": redisPayload.Parts,
@@ -336,6 +381,7 @@ func BookParts(c *gin.Context) {
total := bookPartsCache.total
fixed := bookPartsCache.fixed
bookPartsCache.mu.RUnlock()
mergeBookPartIcons(parts)
c.JSON(http.StatusOK, gin.H{
"success": true,
"parts": parts,
@@ -348,6 +394,7 @@ func BookParts(c *gin.Context) {
// 3. DB 查询并更新 Redis + 内存
parts, total, fixed := fetchAndCacheBookParts()
mergeBookPartIcons(parts)
payload := bookPartsRedisPayload{Parts: parts, TotalSections: total, FixedSections: fixed}
cache.Set(context.Background(), cache.KeyBookParts, payload, cache.BookPartsTTL)