feat: MBTI头像与用户规则链路升级,三端页面与接口同步
Made-with: Cursor
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user