zlwu 10 месяцев назад
Родитель
Сommit
82f87d3c61

+ 40 - 1
backend/main.py

@@ -24,7 +24,7 @@ from services.redis_cache_service import RedisCacheService
 from services.user_service import user_service
 from services.wechat_jsapi_service import WechatJSAPIService
 from services.energy_stone_service import energy_stone_service
-from models_db import VerificationCode, UserAvatar, UserBackgroundImage, Feedback
+from models_db import VerificationCode, UserAvatar, UserBackgroundImage, Feedback, BackgroundImage
 
 # 配置日志
 logging.basicConfig(
@@ -370,6 +370,45 @@ async def get_user_avatars(user_id: str):
         raise HTTPException(status_code=500, detail=f"获取用户头像信息失败: {str(e)}")
 
 
+@app.get("/api/background-images")
+async def get_background_images():
+    """
+    获取所有背景图片配置列表
+    
+    Returns:
+        {
+            "success": true,
+            "data": [
+                {
+                    "image_id": "default",
+                    "image_name": "默认背景",
+                    "image_url": "https://...",
+                    "thumbnail_url": "https://...",
+                    "unlock_type": "default",
+                    "display_order": 0,
+                    "is_active": true,
+                    ...
+                },
+                ...
+            ]
+        }
+    """
+    try:
+        async with db_manager.get_session() as session:
+            result = await session.execute(
+                select(BackgroundImage).where(BackgroundImage.is_active == True).order_by(BackgroundImage.display_order)
+            )
+            background_images = result.scalars().all()
+            
+            return {
+                "success": True,
+                "data": [bg_image.to_dict() for bg_image in background_images]
+            }
+    except Exception as e:
+        logger.error(f"获取背景图片配置失败: {e}")
+        raise HTTPException(status_code=500, detail=f"获取背景图片配置失败: {str(e)}")
+
+
 @app.get("/api/users/{user_id}/background-images")
 async def get_user_background_images(user_id: str):
     """

+ 1 - 1
backend/migrate_add_background_images.py

@@ -161,7 +161,7 @@ async def migrate_create_background_images_tables() -> bool:
                     INSERT INTO background_images 
                     (image_id, image_name, image_url, unlock_type, display_order, description)
                     VALUES 
-                    ('happynewyear', '新年背景', 'https://lyrria-pro.oss-cn-beijing.aliyuncs.com/happynewyear.jpg', 'default', 2, '新年背景图片')
+                    ('happynewyear', '新年背景', 'https://lyrria-pro.oss-cn-beijing.aliyuncs.com/bg-happynewyear.png', 'default', 2, '新年背景图片')
                     """
                 )
                 print("✅ 新年背景图片已添加")

+ 4 - 2
frontend/src/pages/FirstLoginRewardPage.tsx

@@ -133,8 +133,10 @@ export const FirstLoginRewardPage = () => {
         setCurrentBackgroundImageId(backgroundImageId);
         
         // 获取背景图片 URL(从映射中获取)
-        const { getBackgroundImageUrlById } = await import('@/utils/storage');
-        const backgroundImageUrl = getBackgroundImageUrlById(backgroundImageId);
+        const { getBackgroundImageUrlByIdSync, loadBackgroundImageConfig } = await import('@/utils/storage');
+        // 确保配置已加载
+        await loadBackgroundImageConfig();
+        const backgroundImageUrl = getBackgroundImageUrlByIdSync(backgroundImageId);
         if (backgroundImageUrl) {
           setCurrentBackgroundImageUrl(backgroundImageUrl);
         }

+ 23 - 4
frontend/src/pages/ShareSuccessPage.tsx

@@ -1,6 +1,6 @@
-import { useEffect } from 'react';
+import { useEffect, useState } from 'react';
 import { useNavigate } from 'react-router-dom';
-import { setStorage, STORAGE_KEYS, getUserId, getVisitedRoutes } from '@/utils/storage';
+import { setStorage, STORAGE_KEYS, getUserId, getVisitedRoutes, loadBackgroundImageConfig, getBackgroundImageUrlByIdSync } from '@/utils/storage';
 import { updateUserUIState } from '@/utils/api';
 import { px2vmin } from '@/utils/viewport';
 import { getOssUrl } from '@/utils/oss';
@@ -9,10 +9,12 @@ const fullProgressBar = getOssUrl('full-progress-bar.png');
 const avatarAnimaZone = getOssUrl('avatar-anima-zone-copy.png');
 const continueChatBtn = getOssUrl('continue-chat.png');
 
-const BACKGROUND_IMAGE_URL = 'https://lyrria-pro.oss-cn-beijing.aliyuncs.com/happynewyear.jpg';
+// 新年背景图片 ID
+const NEW_YEAR_BACKGROUND_IMAGE_ID = 'happynewyear';
 
 export const ShareSuccessPage = () => {
   const navigate = useNavigate();
+  const [backgroundImageUrl, setBackgroundImageUrl] = useState<string>('');
 
   useEffect(() => {
     // 记录用户当前所在页面
@@ -28,6 +30,23 @@ export const ShareSuccessPage = () => {
         // 同步失败不影响前端流程
       });
     }
+
+    // 加载背景图片配置并获取新年背景图片 URL
+    const loadBackgroundImage = async () => {
+      try {
+        await loadBackgroundImageConfig();
+        const url = getBackgroundImageUrlByIdSync(NEW_YEAR_BACKGROUND_IMAGE_ID);
+        if (url) {
+          setBackgroundImageUrl(url);
+        } else {
+          console.warn(`[ShareSuccessPage] 未找到背景图片: ${NEW_YEAR_BACKGROUND_IMAGE_ID}`);
+        }
+      } catch (error) {
+        console.error('[ShareSuccessPage] 加载背景图片配置失败:', error);
+      }
+    };
+
+    loadBackgroundImage();
   }, []);
 
   const handleContinueChat = () => {
@@ -42,7 +61,7 @@ export const ShareSuccessPage = () => {
         left: 0,
         width: '100vw',
         height: '100vh',
-        backgroundImage: `url(${BACKGROUND_IMAGE_URL})`,
+        backgroundImage: backgroundImageUrl ? `url(${backgroundImageUrl})` : 'none',
         backgroundSize: 'cover',
         backgroundPosition: 'center',
         backgroundRepeat: 'no-repeat',

+ 40 - 0
frontend/src/utils/api.ts

@@ -705,3 +705,43 @@ export const claimShareReward = async (user_id: string): Promise<ClaimShareRewar
   return data;
 };
 
+/**
+ * 获取所有背景图片配置
+ */
+export interface BackgroundImageConfig {
+  image_id: string;
+  image_name: string;
+  image_url: string;
+  thumbnail_url?: string;
+  unlock_type: 'default' | 'level' | 'purchase' | 'mission';
+  unlock_level?: number;
+  unlock_mission_type?: string;
+  display_order: number;
+  is_active: boolean;
+  description?: string;
+  metadata?: Record<string, any>;
+  created_at?: string;
+  updated_at?: string;
+}
+
+export interface GetBackgroundImagesResponse {
+  success: boolean;
+  data: BackgroundImageConfig[];
+}
+
+export const getBackgroundImages = async (): Promise<GetBackgroundImagesResponse> => {
+  const apiBaseUrl = getApiBaseUrl();
+  const response = await fetch(`${apiBaseUrl}/api/background-images`, {
+    method: 'GET',
+    headers: {
+      'Content-Type': 'application/json',
+    },
+  });
+
+  if (!response.ok) {
+    throw new Error(`获取背景图片配置失败: ${response.statusText}`);
+  }
+
+  return response.json();
+};
+

+ 85 - 9
frontend/src/utils/storage.ts

@@ -316,21 +316,87 @@ export const clearBackgroundImage = (): void => {
 };
 
 /**
- * 背景图片 URL 映射
+ * 背景图片配置缓存(从数据库获取)
  */
-export const BACKGROUND_IMAGE_URL_MAP: Record<string, string> = {
-  default: 'https://lyrria-pro.oss-cn-beijing.aliyuncs.com/init.svg',
-  happynewyear: 'https://lyrria-pro.oss-cn-beijing.aliyuncs.com/happynewyear.jpg',
+let backgroundImageConfigCache: Record<string, { image_url: string; [key: string]: any }> | null = null;
+let backgroundImageConfigLoading: Promise<void> | null = null;
+
+/**
+ * 从 API 获取并缓存背景图片配置
+ */
+export const loadBackgroundImageConfig = async (): Promise<void> => {
+  // 如果正在加载,等待加载完成
+  if (backgroundImageConfigLoading) {
+    return backgroundImageConfigLoading;
+  }
+
+  // 如果已有缓存,直接返回
+  if (backgroundImageConfigCache !== null) {
+    return Promise.resolve();
+  }
+
+  // 开始加载
+  backgroundImageConfigLoading = (async () => {
+    try {
+      const { getBackgroundImages } = await import('./api');
+      const response = await getBackgroundImages();
+      
+      if (response.success && response.data) {
+        // 构建映射表:image_id -> { image_url, ... }
+        backgroundImageConfigCache = {};
+        for (const config of response.data) {
+          if (config.image_id && config.image_url) {
+            backgroundImageConfigCache[config.image_id] = {
+              image_url: config.image_url,
+              ...config,
+            };
+          }
+        }
+        console.log('[storage] 背景图片配置加载成功:', Object.keys(backgroundImageConfigCache));
+      }
+    } catch (error) {
+      console.error('[storage] 加载背景图片配置失败:', error);
+      // 加载失败时使用默认值,避免阻塞应用
+      backgroundImageConfigCache = {
+        default: {
+          image_url: 'https://lyrria-pro.oss-cn-beijing.aliyuncs.com/init.svg',
+        },
+      };
+    } finally {
+      backgroundImageConfigLoading = null;
+    }
+  })();
+
+  return backgroundImageConfigLoading;
 };
 
 /**
  * 根据背景图片ID获取URL
  */
-export const getBackgroundImageUrlById = (image_id: string | null | undefined): string | null => {
+export const getBackgroundImageUrlById = async (image_id: string | null | undefined): Promise<string | null> => {
   if (!image_id) {
     return null;
   }
-  return BACKGROUND_IMAGE_URL_MAP[image_id] || null;
+
+  // 如果缓存未加载,先加载
+  if (backgroundImageConfigCache === null) {
+    await loadBackgroundImageConfig();
+  }
+
+  // 从缓存中获取
+  const config = backgroundImageConfigCache?.[image_id];
+  return config?.image_url || null;
+};
+
+/**
+ * 同步版本:根据背景图片ID获取URL(使用缓存,如果缓存未加载则返回 null)
+ * 注意:此函数不会触发 API 请求,仅从缓存读取
+ */
+export const getBackgroundImageUrlByIdSync = (image_id: string | null | undefined): string | null => {
+  if (!image_id || backgroundImageConfigCache === null) {
+    return null;
+  }
+  return backgroundImageConfigCache[image_id]?.image_url || null;
 };
 
 /**
@@ -376,7 +442,7 @@ export const syncBackgroundImageToStorage = (
   if (!image_id) {
     // 如果没有 image_id,使用默认值
     const defaultId = 'default';
-    const defaultUrl = getBackgroundImageUrlById(defaultId);
+    const defaultUrl = getBackgroundImageUrlByIdSync(defaultId);
     if (defaultUrl) {
       setBackgroundImage(defaultId, defaultUrl);
     }
@@ -385,13 +451,23 @@ export const syncBackgroundImageToStorage = (
     return;
   }
   
-  const image_url = getBackgroundImageUrlById(image_id);
+  const image_url = getBackgroundImageUrlByIdSync(image_id);
   if (image_url) {
     setBackgroundImage(image_id, image_url);
   } else {
     // 如果找不到对应的 URL,只存储 ID
     setCurrentBackgroundImageId(image_id);
-    console.warn(`[storage] 未找到背景图片URL映射: image_id=${image_id}`);
+    // 如果缓存未加载,尝试异步加载
+    if (backgroundImageConfigCache === null) {
+      loadBackgroundImageConfig().then(() => {
+        const url = getBackgroundImageUrlByIdSync(image_id);
+        if (url) {
+          setBackgroundImage(image_id, url);
+        }
+      });
+    } else {
+      console.warn(`[storage] 未找到背景图片URL映射: image_id=${image_id}`);
+    }
   }
   
   // 如果提供了metadata,提取并存储透明度配置