Selaa lähdekoodia

重构types\services\api\stores结构

wuzj 6 päivää sitten
vanhempi
sitoutus
bb4fc62f1a

+ 1 - 1
project.config.json

@@ -2,7 +2,7 @@
   "miniprogramRoot": "./dist",
   "projectname": "miniprogram-linejoy",
   "description": "linejoy",
-  "appid": "wxa9425d95031e81a0",
+  "appid": "wx0b170c7cbab32743",
   "setting": {
     "urlCheck": true,
     "es6": false,

+ 40 - 24
src/services/cloud.ts → src/api/cloud.ts

@@ -1,6 +1,6 @@
-//封装微信小程序云开发的调用方法,简化云函数使用
-
+// api/cloud.ts
 import Taro from '@tarojs/taro'
+import { Result, createSuccess, createError } from '@/types/result'
 
 // 请求选项
 interface CloudOptions {
@@ -22,11 +22,11 @@ const defaultOptions: CloudOptions = {
  * @param data 请求数据
  * @param options 请求选项
  */
-export async function callCloudFunction<T = any>(
+export async function callCloud<T = any>(
   name: string,
   data?: any,
   options?: CloudOptions
-): Promise<T> {
+): Promise<Result<T>> {
   const opt = { ...defaultOptions, ...options }
   
   // 显示加载中
@@ -51,29 +51,22 @@ export async function callCloudFunction<T = any>(
       data
     })
 
+    // 隐藏加载中
+    if (opt.showLoading) {
+      Taro.hideLoading()
+    }
+
     if (result && result.result) {
-      // 隐藏加载中
-      if (opt.showLoading) {
-        Taro.hideLoading()
-      }
-      
-      // 业务错误处理 (假设result可能包含error和success字段)
-      const typedResult = result.result as any
-      if (typedResult.error || typedResult.success === false) {
-        if (opt.showError) {
-          Taro.showToast({
-            title: typedResult.error || '请求失败',
-            icon: 'none'
-          })
-        }
-        throw new Error(typedResult.error || '请求失败')
+      // 检查是否已经是标准Result格式
+      if (typeof result.result === 'object' && 'success' in result.result) {
+        return result.result as Result<T>;
       }
       
-      // 返回data字段或整个result对象
-      return (typedResult.data !== undefined ? typedResult.data : typedResult) as T
+      // 返回成功结果
+      return createSuccess<T>(result.result as T);
     }
     
-    throw new Error(`云函数 ${name} 没有返回结果`)
+    return createError('云函数调用失败: 没有返回结果');
   } catch (error: any) {
     // 隐藏加载中
     if (opt.showLoading) {
@@ -89,6 +82,29 @@ export async function callCloudFunction<T = any>(
     }
     
     console.error(`调用云函数 ${name} 失败:`, error)
-    return Promise.reject(error)
+    return createError(error.message || `调用云函数 ${name} 失败`);
+  }
+}
+
+/**
+ * 获取云数据库实例
+ */
+export function getCloudDB() {
+  try {
+    Taro.cloud.init({
+      env: 'cloud1' // 替换为您实际的云环境ID
+    })
+  } catch (e) {
+    // 忽略已初始化的错误
   }
-}
+  
+  return Taro.cloud.database();
+}
+
+/**
+ * 云开发API集合
+ */
+export const cloudApi = {
+  call: callCloud,
+  db: getCloudDB
+};

+ 4 - 0
src/api/index.ts

@@ -0,0 +1,4 @@
+// api/index.ts
+export * from './cloud';
+// 未来可能的其他API导出
+// export * from './http';

+ 0 - 0
src/services/request.ts → src/api/request.ts


+ 2 - 2
src/services/sync.ts → src/api/sync.ts

@@ -1,11 +1,11 @@
 // 提供数据同步服务,确保本地数据与云端保持一致
 import Taro from '@tarojs/taro'
-import { type IRoom } from '@/types/room'
+import { type Room } from '@/types/room'
 
 // 数据同步服务
 export const syncService = {
   // 同步房间到云端
-  async syncRoom(room: IRoom) {
+  async syncRoom(room: Room) {
     if (process.env.TARO_ENV !== 'weapp' || !Taro.cloud) {
       return false
     }

+ 2 - 2
src/pages/game-detail/index.vue

@@ -150,7 +150,7 @@
 import Taro from '@tarojs/taro'
 import { ref } from 'vue'
 import { useGameStore} from '@/stores/game'
-import { type IGame } from '@/types/game'
+import { type Game } from '@/types/game'
 import { useUserStore } from '@/stores/user'
 import { People, Clock } from '@nutui/icons-vue-taro'
 import Tabbar from '@/components/Tabbar.vue'
@@ -181,7 +181,7 @@ export default {
 
     // 页面状态
     const loading = ref(true)
-    const game = ref<IGame | null>(null)
+    const game = ref<Game | null>(null)
     
     // 获取游戏详情
     const getGameDetail = async () => {

+ 2 - 2
src/pages/room/create/index.vue

@@ -64,7 +64,7 @@
 import Taro from '@tarojs/taro'
 import { ref } from 'vue'
 import { useGameStore } from '@/stores/game'
-import { type IGame } from '@/types/game'
+import { type Game } from '@/types/game'  
 import { useUserStore } from '@/stores/user'
 import { useRoomStore } from '@/stores/room'
 // 移除 roomAPI 导入
@@ -100,7 +100,7 @@ export default {
     const roomPassword = ref('')
 
     // 添加明确的类型注解
-    const gameInfo = ref<IGame | null>(null)
+    const gameInfo = ref<Game | null>(null)
     
     // 生成随机房间名称
     const generateRandomName = () => {

+ 2 - 2
src/pages/room/join/index.vue

@@ -71,7 +71,7 @@ import { ref, computed } from 'vue'
 import Taro from '@tarojs/taro'
 import { useUserStore } from '@/stores/user'
 import { useRoomStore } from '@/stores/room'
-import { type IRecentRoom } from '@/types/room'
+import { type RecentRoom } from '@/types/room'
 import { Scan2, Right } from '@nutui/icons-vue-taro'
 import Tabbar from '@/components/Tabbar.vue'
 
@@ -205,7 +205,7 @@ export default {
     }
 
     // 加入最近的房间
-    const joinRecentRoom = (room: IRecentRoom) => {
+    const joinRecentRoom = (room: RecentRoom) => {
       roomCode.value = room.id
       if (room.needPassword) {
         needPassword.value = true

+ 0 - 163
src/services/api/game.ts

@@ -1,163 +0,0 @@
-//处理通用游戏列表和游戏详情的获取
-import Taro from '@tarojs/taro'
-import { type IGame , type IApiResult } from '@/types/game'
-
-// 控制是否使用云函数(true)或mock数据(false)
-const USE_CLOUD = false
-
-// Mock数据
-const mockGames: IGame[] = [
-  {
-    id: '1',
-    title: '海龟汤',
-    image: 'https://images.unsplash.com/photo-1582845512747-e42001c95638?ixlib=rb-1.2.1&auto=format&fit=crop&w=400&h=100&q=80',
-    players: '3-10',
-    duration: '30-60',
-    rating: 4.8,
-    isNew: true,
-    isHot: true,
-    description: '海龟汤是一种猜谜游戏,游戏开始时,主持人会讲述一个故事的结果,参与者需要通过提问来猜测故事的真相。',
-    rules: '1. 主持人讲述一个故事的结果\n2. 参与者通过提问来猜测故事的真相\n3. 主持人只能回答"是"、"否"或"不重要"',
-    category: '推理',
-    playCount: 1000,
-    completionRate: 0.8,
-    tips: [
-      '提问时要注意逻辑思维',
-      '尝试从多个角度思考问题',
-      '注意主持人的回答,特别是"不重要"的部分'
-    ],
-    examples: [
-      {
-        question: '一个人走进酒吧,点了一杯水,喝完就离开了。为什么?',
-        answer: '这个人有打嗝的困扰,想通过喝水来缓解。'
-      }
-    ]
-  },
-  {
-    id: '2',
-    title: '剧本杀',
-    image: 'https://images.unsplash.com/photo-1529156069898-49953e39b3ac?ixlib=rb-1.2.1&auto=format&fit=crop&w=400&h=100&q=80',
-    players: '4-8',
-    duration: '120-180',
-    rating: 4.5,
-    isNew: false,
-    isHot: true,
-    description: '剧本杀是一种角色扮演游戏,每个玩家扮演一个角色,通过阅读剧本和相互交流来解决一个谜题。',
-    rules: '1. 每个玩家扮演一个角色\n2. 阅读剧本并获取个人信息\n3. 通过交流和推理解决谜题',
-    category: '角色扮演',
-    playCount: 1500,
-    completionRate: 0.7,
-    tips: [
-      '认真阅读你的角色背景',
-      '注意收集和分析信息',
-      '积极参与角色扮演和讨论'
-    ],
-    examples: [
-      {
-        question: '谁是凶手?',
-        answer: '根据线索和证据,管家是凶手。'
-      }
-    ]
-  },
-  {
-    id: '3',
-    title: '狼人杀',
-    image: 'https://images.unsplash.com/photo-1529156069898-49953e39b3ac?ixlib=rb-1.2.1&auto=format&fit=crop&w=400&h=100&q=80',
-    players: '6-12',
-    duration: '30-45',
-    rating: 4.7,
-    isNew: false,
-    isHot: true,
-    description: '狼人杀是一种桌游,玩家分为狼人和村民两个阵营,狼人试图消灭村民,村民则要找出并消灭狼人。',
-    rules: '1. 玩家分为狼人和村民两个阵营\n2. 夜晚狼人选择一名玩家"杀死"\n3. 白天所有人讨论并投票处决一名玩家',
-    category: '桌游',
-    playCount: 2000,
-    completionRate: 0.6,
-    tips: [
-      '仔细观察其他玩家的言行',
-      '理性分析每晚的死亡情况',
-      '善用自己的角色技能'
-    ],
-    examples: [
-      {
-        question: '如何判断谁是狼人?',
-        answer: '通过分析发言逻辑、投票行为和表情变化,找出可疑的玩家。'
-      }
-    ]
-  }
-]
-
-// 游戏通用API
-export const gameService = {
-  // 获取游戏列表
-  async getGames(): Promise<IApiResult<IGame[]>> {
-    try {
-      if (USE_CLOUD && process.env.TARO_ENV === 'weapp') {
-        // 云函数实现
-        const result = await Taro.cloud.callFunction({
-          name: 'getGames',
-        })
-        
-        // 使用类型断言处理结果
-        if (result && typeof result === 'object' && 'result' in result) {
-          const cloudResult = result.result as any
-          return { 
-            success: true, 
-            data: cloudResult?.data || mockGames 
-          }
-        }
-        return { success: false, message: '获取游戏列表失败' }
-      } else {
-        // Mock实现
-        return new Promise(resolve => {
-          setTimeout(() => {
-            resolve({ success: true, data: mockGames })
-          }, 500) // 模拟网络延迟
-        })
-      }
-    } catch (error) {
-      console.error('获取游戏列表失败:', error)
-      return { 
-        success: false, 
-        message: error.message || '获取游戏列表失败' 
-      }
-    }
-  },
-
-  // 获取游戏详情
-  async getGameDetail(id: string): Promise<IApiResult<IGame | null>> {
-    try {
-      if (USE_CLOUD && process.env.TARO_ENV === 'weapp') {
-        // 云函数实现
-        const result = await Taro.cloud.callFunction({
-          name: 'getGameDetail',
-          data: { id }
-        })
-        
-        // 使用类型断言处理结果
-        if (result && typeof result === 'object' && 'result' in result) {
-          const cloudResult = result.result as any
-          return { 
-            success: true, 
-            data: cloudResult?.data || null 
-          }
-        }
-        return { success: false, message: '获取游戏详情失败' }
-      } else {
-        // Mock实现
-        return new Promise(resolve => {
-          setTimeout(() => {
-            const game = mockGames.find(g => g.id === id)
-            resolve({ success: true, data: game || null })
-          }, 300) // 模拟网络延迟
-        })
-      }
-    } catch (error) {
-      console.error('获取游戏详情失败:', error)
-      return { 
-        success: false, 
-        message: error.message || '获取游戏详情失败' 
-      }
-    }
-  }
-}

+ 0 - 52
src/services/api/games/turtlesoup.ts

@@ -1,52 +0,0 @@
-//海龟汤游戏特定的 API
-import { TurtleSoupGame } from '@/stores/games/turtlesoup'
-import { callCloudFunction } from '../../cloud'
-import { type ITurtleSoupGame, type ITurtleSoupQuestion, type ITurtleSoupGameResult } from '@/types/games/turtlesoup'
-
-// 海龟汤游戏相关API
-export const turtleSoupService = {
-  /**
-   * 获取游戏数据
-   * @param gameId 游戏ID
-   * @param role 用户角色
-   */
-  getGameData(gameId: string, role: string) {
-    return callCloudFunction<{ game: TurtleSoupGame }>('getTurtleSoupGame', { gameId, role })
-  },
-
-  /**
-   * 提交问题
-   * @param gameId 游戏ID
-   * @param content 问题内容
-   */
-  submitQuestion(gameId: string, content: string) {
-    return callCloudFunction<{ questionId: string }>('submitTurtleSoupQuestion', { gameId, content })
-  },
-
-  /**
-   * 回答问题
-   * @param questionId 问题ID
-   * @param answer 答案
-   */
-  answerQuestion(questionId: string, answer: string) {
-    return callCloudFunction<{ success: boolean }>('answerTurtleSoupQuestion', { questionId, answer })
-  },
-
-  /**
-   * 公开提示
-   * @param gameId 游戏ID
-   * @param hintIndex 提示索引
-   */
-  revealHint(gameId: string, hintIndex: number) {
-    return callCloudFunction<{ success: boolean }>('revealTurtleSoupHint', { gameId, hintIndex })
-  },
-
-  /**
-   * 结束游戏
-   * @param gameId 游戏ID
-   * @param result 游戏结果
-   */
-  endGame(gameId: string, result: { solved: boolean; solvedBy?: string }) {
-    return callCloudFunction<{ gameResult: ITurtleSoupGameResult }>('endTurtleSoupGame', { gameId, result })
-  }
-}

+ 0 - 3
src/services/api/index.ts

@@ -1,3 +0,0 @@
-export { gameService } from './game'
-export { roomService } from './room'
-export * from './games'

+ 0 - 135
src/services/api/user.ts

@@ -1,135 +0,0 @@
-// services/api/user.ts
-import Taro from '@tarojs/taro'
-import { ensureStringId } from '@/utils/db-helper'
-import { type IApiResult,type IUserInfo,type IOpenIdResult } from '@/types/user'
-
-// 控制是否使用云函数(true)或mock数据(false)
-const USE_CLOUD = true
-
-// Mock用户数据
-const mockUser = {
-  nickname: '测试用户',
-  avatar: 'https://example.com/avatar.jpg',
-  openid: 'mock_openid_123456',
-}
-
-export const userService = {
-  // 获取用户信息
-  async getUserInfo(): Promise<IApiResult<IUserInfo>> {
-    try {
-      if (USE_CLOUD && process.env.TARO_ENV === 'weapp') {
-        // 云函数实现
-        const result = await Taro.cloud.callFunction({
-          name: 'getUserInfo',
-        })
-        
-        // 使用类型断言处理结果
-        if (result && typeof result === 'object' && 'result' in result) {
-          const cloudResult = result.result as any
-          return { 
-            success: true, 
-            data: cloudResult?.data || mockUser 
-          }
-        }
-        return { success: false, message: '获取用户信息失败' }
-      } else {
-        // Mock实现
-        return new Promise(resolve => {
-          setTimeout(() => {
-            resolve({ success: true, data: mockUser })
-          }, 300)
-        })
-      }
-    } catch (error) {
-      console.error('获取用户信息失败:', error)
-      return { success: false, message: error.message || '获取用户信息失败' }
-    }
-  },
-  
-  // 获取OpenID
-  async getOpenId(code: string): Promise<IApiResult<IOpenIdResult>> {
-    try {
-      if (USE_CLOUD && process.env.TARO_ENV === 'weapp') {
-        const result = await Taro.cloud.callFunction({
-          name: 'getOpenId',
-          data: { code }
-        })
-        
-        if (result && result.result) {
-          return { success: true, data: result.result as IOpenIdResult }
-        }
-        return { success: false, message: '获取OpenID失败' }
-      }
-      
-      // Mock实现
-      return { 
-        success: true, 
-        data: { openid: 'mock_openid_' + Date.now() } 
-      }
-    } catch (error) {
-      console.error('获取OpenID失败:', error)
-      return { success: false, message: error.message || '获取OpenID失败' }
-    }
-  },
-  
-  // 保存用户信息到云端
-  async saveUserToCloud(userData: IUserInfo): Promise<IApiResult<string>> {
-    try {
-      if (!USE_CLOUD || process.env.TARO_ENV !== 'weapp') {
-        return { success: true, data: 'mock_doc_id' } // Mock成功
-      }
-      
-      // 确保云已初始化
-      try {
-        await Taro.cloud.init({
-          env: (Taro.cloud as any).DYNAMIC_CURRENT_ENV
-        })
-      } catch (initError) {
-        console.warn('云初始化可能已完成:', initError)
-        // 继续执行,即使初始化报错
-      }
-      
-      const db = Taro.cloud.database()
-      
-      // 查询用户是否存在
-      const userQuery = await db.collection('users').where({
-        openid: userData.openid
-      }).get()
-      
-      // 如果用户已存在,更新信息
-      if (userQuery.data && userQuery.data.length > 0) {
-        // 修复: 使用类型断言确保 _id 转换为字符串
-        const docId = ensureStringId(userQuery.data[0]._id)
-        
-        await db.collection('users').doc(docId).update({
-          data: {
-            nickname: userData.nickname,
-            avatar: userData.avatar,
-            role: userData.role,
-            updateTime: db.serverDate()
-          }
-        })
-        return { success: true, data: docId }
-      } else {
-        // 创建新用户
-        const addResult = await db.collection('users').add({
-          data: {
-            ...userData,
-            createTime: db.serverDate(),
-            updateTime: db.serverDate()
-          }
-        })
-        
-        // 修复: 确保返回的 _id 是字符串
-        const newId = typeof addResult._id === 'number' 
-          ? String(addResult._id) 
-          : addResult._id as string
-          
-        return { success: true, data: newId }
-      }
-    } catch (error) {
-      console.error('保存用户信息到云端失败:', error)
-      return { success: false, message: error.message || '保存用户信息失败' }
-    }
-  }
-}

+ 266 - 0
src/services/game.ts

@@ -0,0 +1,266 @@
+// services/game.ts
+import { Result, createSuccess, createError } from '@/types/result'
+import { cloudApi } from '@/api'
+import { type Game } from '@/types/game'
+import { USE_MOCK } from '@/services'
+
+// Mock游戏数据
+const mockGames: Game[] = [
+  {
+    id: '1',
+    title: '海龟汤',
+    image: 'https://images.unsplash.com/photo-1582845512747-e42001c95638?ixlib=rb-1.2.1&auto=format&fit=crop&w=400&h=100&q=80',
+    players: '3-10',
+    duration: '30-60',
+    rating: 4.8,
+    isNew: true,
+    isHot: true,
+    description: '海龟汤是一种猜谜游戏,游戏开始时,主持人会讲述一个故事的结果,参与者需要通过提问来猜测故事的真相。',
+    rules: '1. 主持人讲述一个故事的结果\n2. 参与者通过提问来猜测故事的真相\n3. 主持人只能回答"是"、"否"或"不重要"',
+    category: '推理',
+    playCount: 1000,
+    completionRate: 0.8,
+    tips: [
+      '提问时要注意逻辑思维',
+      '尝试从多个角度思考问题',
+      '注意主持人的回答,特别是"不重要"的部分'
+    ],
+    examples: [
+      {
+        question: '一个人走进酒吧,点了一杯水,喝完就离开了。为什么?',
+        answer: '这个人有打嗝的困扰,想通过喝水来缓解。'
+      }
+    ]
+  },
+  {
+    id: '2',
+    title: '剧本杀',
+    image: 'https://images.unsplash.com/photo-1529156069898-49953e39b3ac?ixlib=rb-1.2.1&auto=format&fit=crop&w=400&h=100&q=80',
+    players: '4-8',
+    duration: '120-180',
+    rating: 4.5,
+    isNew: false,
+    isHot: true,
+    description: '剧本杀是一种角色扮演游戏,每个玩家扮演一个角色,通过阅读剧本和相互交流来解决一个谜题。',
+    rules: '1. 每个玩家扮演一个角色\n2. 阅读剧本并获取个人信息\n3. 通过交流和推理解决谜题',
+    category: '角色扮演',
+    playCount: 1500,
+    completionRate: 0.7,
+    tips: [
+      '认真阅读你的角色背景',
+      '注意收集和分析信息',
+      '积极参与角色扮演和讨论'
+    ],
+    examples: [
+      {
+        question: '谁是凶手?',
+        answer: '根据线索和证据,管家是凶手。'
+      }
+    ]
+  },
+  {
+    id: '3',
+    title: '狼人杀',
+    image: 'https://images.unsplash.com/photo-1529156069898-49953e39b3ac?ixlib=rb-1.2.1&auto=format&fit=crop&w=400&h=100&q=80',
+    players: '6-12',
+    duration: '30-45',
+    rating: 4.7,
+    isNew: false,
+    isHot: true,
+    description: '狼人杀是一种桌游,玩家分为狼人和村民两个阵营,狼人试图消灭村民,村民则要找出并消灭狼人。',
+    rules: '1. 玩家分为狼人和村民两个阵营\n2. 夜晚狼人选择一名玩家"杀死"\n3. 白天所有人讨论并投票处决一名玩家',
+    category: '桌游',
+    playCount: 2000,
+    completionRate: 0.6,
+    tips: [
+      '仔细观察其他玩家的言行',
+      '理性分析每晚的死亡情况',
+      '善用自己的角色技能'
+    ],
+    examples: [
+      {
+        question: '如何判断谁是狼人?',
+        answer: '通过分析发言逻辑、投票行为和表情变化,找出可疑的玩家。'
+      }
+    ]
+  }
+]
+
+// 游戏通用服务
+export const gameService = {
+  // 获取游戏列表
+  async getGames(options?: { category?: string }): Promise<Result<Game[]>> {
+    // 如果在开发环境或非小程序环境,使用Mock数据
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return new Promise(resolve => {
+        setTimeout(() => {
+          let filteredGames = [...mockGames];
+          
+          // 如果指定了分类,进行过滤
+          if (options?.category) {
+            filteredGames = filteredGames.filter(
+              game => game.category === options.category
+            );
+          }
+          
+          resolve(createSuccess(filteredGames));
+        }, 500); // 模拟网络延迟
+      });
+    }
+    
+    // 使用cloudApi调用云函数
+    return cloudApi.call<Game[]>('getGames', options)
+      .then(result => {
+        // 如果没有返回数据,使用mock数据
+        if (result.success && (!result.data || result.data.length === 0)) {
+          return createSuccess(mockGames);
+        }
+        return result;
+      })
+      .catch(error => {
+        console.error('获取游戏列表失败:', error);
+        return createError(error.message || '获取游戏列表失败');
+      });
+  },
+
+  // 获取游戏详情
+  async getGameDetail(id: string): Promise<Result<Game>> {
+    // 如果在开发环境或非小程序环境,使用Mock数据
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return new Promise(resolve => {
+        setTimeout(() => {
+          const game = mockGames.find(g => g.id === id);
+          
+          if (game) {
+            resolve(createSuccess(game));
+          } else {
+            resolve(createError('未找到指定游戏'));
+          }
+        }, 300); // 模拟网络延迟
+      });
+    }
+    
+    // 使用cloudApi调用云函数
+    return cloudApi.call<Game>('getGameDetail', { id })
+      .then(result => {
+        // 如果没有返回数据,尝试从mock找
+        if (result.success && !result.data) {
+          const mockGame = mockGames.find(g => g.id === id);
+          if (mockGame) {
+            return createSuccess(mockGame);
+          }
+          return createError('未找到指定游戏');
+        }
+        return result;
+      })
+      .catch(error => {
+        console.error('获取游戏详情失败:', error);
+        return createError(error.message || '获取游戏详情失败');
+      });
+  },
+  
+  // 获取热门游戏
+  async getHotGames(limit: number = 5): Promise<Result<Game[]>> {
+    const result = await this.getGames();
+    
+    if (!result.success) {
+      return result;
+    }
+    
+    const hotGames = result.data
+      ?.filter(game => game.isHot)
+      .sort((a, b) => b.rating - a.rating)
+      .slice(0, limit);
+      
+    return createSuccess(hotGames || []);
+  },
+  
+  // 获取新游戏
+  async getNewGames(limit: number = 5): Promise<Result<Game[]>> {
+    const result = await this.getGames();
+    
+    if (!result.success) {
+      return result;
+    }
+    
+    const newGames = result.data
+      ?.filter(game => game.isNew)
+      .slice(0, limit);
+      
+    return createSuccess(newGames || []);
+  },
+  
+  // 搜索游戏
+  async searchGames(keyword: string): Promise<Result<Game[]>> {
+    // 如果在开发环境或非小程序环境,使用Mock数据
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return new Promise(resolve => {
+        setTimeout(() => {
+          if (!keyword.trim()) {
+            resolve(createSuccess(mockGames));
+            return;
+          }
+          
+          const searchKey = keyword.toLowerCase();
+          const results = mockGames.filter(game => 
+            game.title.toLowerCase().includes(searchKey) || 
+            game.description.toLowerCase().includes(searchKey) ||
+            game.category.toLowerCase().includes(searchKey)
+          );
+          
+          resolve(createSuccess(results));
+        }, 300);
+      });
+    }
+    
+    // 使用cloudApi调用云函数
+    return cloudApi.call<Game[]>('searchGames', { keyword })
+      .then(result => {
+        // 如果云函数搜索失败,降级到前端搜索mock数据
+        if (!result.success || !result.data || result.data.length === 0) {
+          const searchKey = keyword.toLowerCase();
+          const results = mockGames.filter(game => 
+            game.title.toLowerCase().includes(searchKey) || 
+            game.description.toLowerCase().includes(searchKey) ||
+            game.category.toLowerCase().includes(searchKey)
+          );
+          
+          return createSuccess(results);
+        }
+        return result;
+      })
+      .catch(error => {
+        console.error('搜索游戏失败:', error);
+        return createError(error.message || '搜索游戏失败');
+      });
+  },
+  
+  // 获取游戏分类
+  async getCategories(): Promise<Result<string[]>> {
+    // 如果在开发环境或非小程序环境,使用Mock数据
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return new Promise(resolve => {
+        setTimeout(() => {
+          // 从mock数据中提取唯一分类
+          const categories = [...new Set(mockGames.map(game => game.category))];
+          resolve(createSuccess(categories));
+        }, 200);
+      });
+    }
+    
+    // 使用cloudApi调用云函数
+    return cloudApi.call<string[]>('getGameCategories')
+      .then(result => {
+        // 如果没有返回数据,从mock数据提取
+        if (result.success && (!result.data || result.data.length === 0)) {
+          const categories = [...new Set(mockGames.map(game => game.category))];
+          return createSuccess(categories);
+        }
+        return result;
+      })
+      .catch(error => {
+        console.error('获取游戏分类失败:', error);
+        return createError(error.message || '获取游戏分类失败');
+      });
+  }
+}

+ 0 - 0
src/services/api/games/index.ts → src/services/games/index.ts


+ 355 - 0
src/services/games/turtlesoup.ts

@@ -0,0 +1,355 @@
+// services/games/turtlesoup.ts
+import { Result, createSuccess, createError } from '@/types/result'
+import { cloudApi } from '@/api'
+import { USE_MOCK } from '@/services'
+import { 
+  type TurtleSoupGame, 
+  type TurtleSoupQuestion, 
+  type TurtleSoupGameResult,
+  type TurtleSoupGameStatus,
+  type TurtleSoupDifficulty,
+  type CreateTurtleSoupGameParams
+} from '@/types/games/turtlesoup'
+
+// Mock数据
+const mockGame: TurtleSoupGame = {
+  id: 'mock-ts-1',
+  title: '死亡的摄影师',
+  description: '一个摄影师在自己的工作室里死亡,周围没有任何人,死因是什么?',
+  storyteller: 'user-123',
+  status: 'active',
+  solution: '这个摄影师在深海拍摄鲨鱼时,使用了闪光灯,引来了鲨鱼攻击并死亡。',
+  difficulty: 'medium',
+  createTime: new Date().toISOString(),
+  hints: [
+    { content: '死亡地点不是工作室', revealed: false },
+    { content: '摄影师当时正在进行水下摄影', revealed: false },
+    { content: '死亡与水中的生物有关', revealed: false }
+  ],
+  questions: [],
+  players: [
+    { id: 'user-456', name: '玩家1', avatar: 'https://example.com/avatar1.png', joinedAt: new Date().toISOString() },
+    { id: 'user-789', name: '玩家2', avatar: 'https://example.com/avatar2.png', joinedAt: new Date().toISOString() }
+  ]
+}
+
+// 海龟汤游戏服务
+export const turtleSoupService = {
+  /**
+   * 获取游戏数据
+   * @param gameId 游戏ID
+   * @param role 用户角色 (storyteller/player)
+   */
+  async getGameData(gameId: string, role: string): Promise<Result<TurtleSoupGame>> {
+    // 如果在开发环境或非小程序环境,使用Mock数据
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return new Promise(resolve => {
+        setTimeout(() => {
+          // 根据角色调整返回的数据
+          if (role === 'storyteller') {
+            // 主持人可以看到所有信息
+            resolve(createSuccess({
+              ...mockGame,
+              hints: mockGame.hints.map(h => ({ ...h, revealed: true }))
+            }));
+          } else {
+            // 普通玩家只能看到已揭示的提示
+            resolve(createSuccess(mockGame));
+          }
+        }, 300);
+      });
+    }
+    
+    // 使用cloudApi调用云函数
+    return cloudApi.call<{ game: TurtleSoupGame }>('getTurtleSoupGame', { gameId, role })
+      .then(result => {
+        if (result.success && result.data && result.data.game) {
+          // 确保返回的是TurtleSoupGame对象
+          return createSuccess(result.data.game);
+        }
+        
+        // 如果云函数调用成功但没有返回游戏数据
+        return createError('未找到游戏数据');
+      })
+      .catch(error => {
+        console.error('获取海龟汤游戏数据失败:', error);
+        return createError(error.message || '获取游戏数据失败');
+      });
+  },
+
+  /**
+   * 提交问题
+   * @param gameId 游戏ID
+   * @param content 问题内容
+   */
+  async submitQuestion(gameId: string, content: string): Promise<Result<{ questionId: string }>> {
+    if (!content.trim()) {
+      return createError('问题内容不能为空');
+    }
+    
+    // 如果在开发环境或非小程序环境,使用Mock数据
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return new Promise(resolve => {
+        setTimeout(() => {
+          const questionId = `q-${Date.now()}`;
+          resolve(createSuccess({ questionId }));
+        }, 300);
+      });
+    }
+    
+    // 使用cloudApi调用云函数
+    return cloudApi.call<{ questionId: string }>('submitTurtleSoupQuestion', { gameId, content });
+  },
+
+  /**
+   * 回答问题
+   * @param questionId 问题ID
+   * @param answer 答案 ('yes', 'no', 'irrelevant')
+   */
+  async answerQuestion(questionId: string, answer: string): Promise<Result<{ success: boolean }>> {
+    const validAnswers = ['yes', 'no', 'irrelevant'];
+    
+    if (!validAnswers.includes(answer)) {
+      return createError('无效的答案,只能是"yes"、"no"或"irrelevant"');
+    }
+    
+    // 如果在开发环境或非小程序环境,使用Mock数据
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return new Promise(resolve => {
+        setTimeout(() => {
+          resolve(createSuccess({ success: true }));
+        }, 200);
+      });
+    }
+    
+    // 使用cloudApi调用云函数
+    return cloudApi.call<{ success: boolean }>('answerTurtleSoupQuestion', { questionId, answer });
+  },
+
+  /**
+   * 公开提示
+   * @param gameId 游戏ID
+   * @param hintIndex 提示索引
+   */
+  async revealHint(gameId: string, hintIndex: number): Promise<Result<{ success: boolean }>> {
+    if (hintIndex < 0) {
+      return createError('提示索引无效');
+    }
+    
+    // 如果在开发环境或非小程序环境,使用Mock数据
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return new Promise(resolve => {
+        setTimeout(() => {
+          // 检查提示索引是否有效
+          if (hintIndex >= mockGame.hints.length) {
+            resolve(createError('提示索引超出范围'));
+            return;
+          }
+          
+          resolve(createSuccess({ success: true }));
+        }, 200);
+      });
+    }
+    
+    // 使用cloudApi调用云函数
+    return cloudApi.call<{ success: boolean }>('revealTurtleSoupHint', { gameId, hintIndex });
+  },
+
+  /**
+   * 结束游戏
+   * @param gameId 游戏ID
+   * @param result 游戏结果对象
+   */
+  async endGame(gameId: string, result: { solved: boolean; solvedBy?: string }): Promise<Result<TurtleSoupGameResult>> {
+    // 如果在开发环境或非小程序环境,使用Mock数据
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return new Promise(resolve => {
+        setTimeout(() => {
+          const gameResult: TurtleSoupGameResult = {
+            gameId,
+            solved: result.solved,
+            solvedBy: result.solvedBy,
+            duration: 1800, // 30分钟
+            questionCount: 15,
+            hintsRevealed: 2,
+            completionTime: new Date().toISOString()
+          };
+          
+          resolve(createSuccess(gameResult));
+        }, 400);
+      });
+    }
+    
+    // 使用cloudApi调用云函数
+    return cloudApi.call<{ gameResult: TurtleSoupGameResult }>('endTurtleSoupGame', { gameId, result })
+      .then(response => {
+        if (response.success && response.data && response.data.gameResult) {
+          return createSuccess(response.data.gameResult);
+        }
+        
+        return createError(response.message || '结束游戏失败');
+      });
+  },
+  
+  /**
+   * 创建新游戏
+   * @param gameData 游戏数据
+   */
+  async createGame(gameData: CreateTurtleSoupGameParams): Promise<Result<{ gameId: string }>> {
+    // 数据验证
+    if (!gameData.title.trim()) return createError('游戏标题不能为空');
+    if (!gameData.description.trim()) return createError('游戏描述不能为空');
+    if (!gameData.solution.trim()) return createError('游戏答案不能为空');
+    if (!gameData.hints || gameData.hints.length === 0) return createError('至少需要提供一个提示');
+    
+    // 如果在开发环境或非小程序环境,使用Mock数据
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return new Promise(resolve => {
+        setTimeout(() => {
+          const gameId = `game-${Date.now()}`;
+          resolve(createSuccess({ gameId }));
+        }, 500);
+      });
+    }
+    
+    // 使用cloudApi调用云函数
+    return cloudApi.call<{ gameId: string }>('createTurtleSoupGame', gameData);
+  },
+  
+  /**
+   * 获取用户创建的游戏列表
+   */
+  async getCreatedGames(): Promise<Result<TurtleSoupGame[]>> {
+    // 如果在开发环境或非小程序环境,使用Mock数据
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return new Promise(resolve => {
+        setTimeout(() => {
+          resolve(createSuccess([mockGame]));
+        }, 400);
+      });
+    }
+    
+    // 使用cloudApi调用云函数
+    return cloudApi.call<{ games: TurtleSoupGame[] }>('getCreatedTurtleSoupGames')
+      .then(result => {
+        if (result.success && result.data && result.data.games) {
+          return createSuccess(result.data.games);
+        }
+        
+        if (result.success) {
+          return createSuccess([]);
+        }
+        
+        return createError(result.message || '获取游戏列表失败');
+      });
+  },
+  
+  /**
+   * 获取用户参与的游戏列表
+   */
+  async getJoinedGames(): Promise<Result<TurtleSoupGame[]>> {
+    // 如果在开发环境或非小程序环境,使用Mock数据
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return new Promise(resolve => {
+        setTimeout(() => {
+          resolve(createSuccess([{...mockGame, storyteller: 'other-user'}]));
+        }, 400);
+      });
+    }
+    
+    // 使用cloudApi调用云函数
+    return cloudApi.call<{ games: TurtleSoupGame[] }>('getJoinedTurtleSoupGames')
+      .then(result => {
+        if (result.success && result.data && result.data.games) {
+          return createSuccess(result.data.games);
+        }
+        
+        if (result.success) {
+          return createSuccess([]);
+        }
+        
+        return createError(result.message || '获取游戏列表失败');
+      });
+  },
+  
+  /**
+   * 提交游戏解答
+   * @param gameId 游戏ID
+   * @param solution 玩家提交的解答
+   */
+  async submitSolution(gameId: string, solution: string): Promise<Result<{ correct: boolean }>> {
+    if (!solution.trim()) {
+      return createError('解答内容不能为空');
+    }
+    
+    // 如果在开发环境或非小程序环境,使用Mock数据
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return new Promise(resolve => {
+        setTimeout(() => {
+          // 模拟50%的概率答对
+          const correct = Math.random() > 0.5;
+          resolve(createSuccess({ correct }));
+        }, 300);
+      });
+    }
+    
+    // 使用cloudApi调用云函数
+    return cloudApi.call<{ correct: boolean }>('submitTurtleSoupSolution', { gameId, solution });
+  },
+  
+  /**
+   * 加入游戏
+   * @param gameId 游戏ID
+   * @param code 游戏邀请码(私人游戏需要)
+   */
+  async joinGame(gameId: string, code?: string): Promise<Result<{ success: boolean }>> {
+    // 如果在开发环境或非小程序环境,使用Mock数据
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return new Promise(resolve => {
+        setTimeout(() => {
+          resolve(createSuccess({ success: true }));
+        }, 300);
+      });
+    }
+    
+    // 使用cloudApi调用云函数
+    return cloudApi.call<{ success: boolean }>('joinTurtleSoupGame', { gameId, code });
+  },
+  
+  /**
+   * 离开游戏
+   * @param gameId 游戏ID
+   */
+  async leaveGame(gameId: string): Promise<Result<{ success: boolean }>> {
+    // 如果在开发环境或非小程序环境,使用Mock数据
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return new Promise(resolve => {
+        setTimeout(() => {
+          resolve(createSuccess({ success: true }));
+        }, 300);
+      });
+    }
+    
+    // 使用cloudApi调用云函数
+    return cloudApi.call<{ success: boolean }>('leaveTurtleSoupGame', { gameId });
+  },
+  
+  /**
+   * 更新游戏状态
+   * @param gameId 游戏ID
+   * @param status 新状态
+   */
+  async updateGameStatus(gameId: string, status: TurtleSoupGameStatus): Promise<Result<{ success: boolean }>> {
+    // 如果在开发环境或非小程序环境,使用Mock数据
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return new Promise(resolve => {
+        setTimeout(() => {
+          resolve(createSuccess({ success: true }));
+        }, 300);
+      });
+    }
+    
+    // 使用cloudApi调用云函数
+    return cloudApi.call<{ success: boolean }>('updateTurtleSoupGameStatus', { gameId, status });
+  }
+};

+ 4 - 4
src/services/api/games/werewolf.ts → src/services/games/werewolf.ts

@@ -1,16 +1,16 @@
-import { callCloudFunction } from '../../cloud'
-import type { WerewolfGame, WerewolfPlayer } from '@/stores/games/werewolf'
+import { cloudApi } from '@/api'
+import type { WerewolfGame, WerewolfPlayer } from '@/types/games/werewolf'
 
 // 狼人杀游戏相关API
 export const werewolfService = {
   // 获取游戏数据
   getGameData(gameId: string, role: string) {
-    return callCloudFunction<{ game: WerewolfGame }>('getWerewolfGame', { gameId, role })
+    return cloudApi.call<{ game: WerewolfGame }>('getWerewolfGame', { gameId, role })
   },
 
   // 玩家行动
   playerAction(gameId: string, playerId: string, action: string, target?: string) {
-    return callCloudFunction<{ success: boolean }>('werewolfAction', { 
+    return cloudApi.call<{ success: boolean }>('werewolfAction', { 
       gameId, playerId, action, target 
     })
   },

+ 6 - 0
src/services/index.ts

@@ -0,0 +1,6 @@
+export { gameService } from './game'
+export { roomService } from './room'
+export { userService } from './user'
+export * from './games'
+
+export const USE_MOCK = true;

+ 13 - 13
src/services/api/room.ts → src/services/room.ts

@@ -1,7 +1,7 @@
 // services/api/room.ts - 处理房间相关API请求
 
 import Taro from '@tarojs/taro'
-import { type IRecentRoom, type IRoom, type IRoomUser } from '@/types/room'
+import { type RecentRoom, type Room, type RoomUser } from '@/types/room'
 
 // Mock房间数据(测试用)
 const mockRooms = {
@@ -53,7 +53,7 @@ const mockRooms = {
 // 房间API - 重构后的纯净版
 export const roomService = {
   // 获取最近房间
-  async getRecentRooms(): Promise<IRecentRoom[]> {
+  async getRecentRooms(): Promise<RecentRoom[]> {
     try {
       // 从缓存获取
       const cachedRooms = Taro.getStorageSync('recentRooms')
@@ -118,7 +118,7 @@ export const roomService = {
   },
   
   // 创建新房间 - 不再依赖store,返回纯数据
-  async createRoom(roomData: IRoom, userInfo: {openid: string, nickname: string, avatar: string}): Promise<{roomId: string | null, success: boolean, message?: string}> {
+  async createRoom(roomData: Room, userInfo: {openid: string, nickname: string, avatar: string}): Promise<{roomId: string | null, success: boolean, message?: string}> {
     try {
       if (!userInfo.openid) {
         return { roomId: null, success: false, message: '用户未登录' }
@@ -160,10 +160,10 @@ export const roomService = {
   },
   
   // 加入房间 - 纯粹的数据操作,不依赖store
-  async joinRoom(roomId: string, userData: IRoomUser): Promise<{success: boolean, roomData?: IRoom, message?: string}> {
+  async joinRoom(roomId: string, userData: RoomUser): Promise<{success: boolean, roomData?: Room, message?: string}> {
     try {
       // 查找房间 - 先从本地缓存查询
-      let room: IRoom | null = null
+      let room: Room | null = null
       
       // 检查本地缓存中是否有该房间
       const allRooms = Taro.getStorageSync('allRooms') || '{}'
@@ -173,7 +173,7 @@ export const roomService = {
         room = roomsObj[roomId]
       } else if (mockRooms[roomId]) {
         // 使用mock数据
-        room = mockRooms[roomId] as IRoom
+        room = mockRooms[roomId] as Room
       }
       
       if (!room) {
@@ -197,7 +197,7 @@ export const roomService = {
       }
       
       // 保存到最近加入的房间
-      const roomInfo: IRecentRoom = {
+      const roomInfo: RecentRoom = {
         id: room.id,
         name: room.name,
         game: room.gameTitle,
@@ -217,7 +217,7 @@ export const roomService = {
   },
   
   // 保存到最近加入的房间
-  async saveToRecentRooms(roomInfo: IRecentRoom): Promise<void> {
+  async saveToRecentRooms(roomInfo: RecentRoom): Promise<void> {
     try {
       const recentRooms = Taro.getStorageSync('recentRooms') || '[]'
       const rooms = JSON.parse(recentRooms)
@@ -269,7 +269,7 @@ export const roomService = {
   },
   
   // 获取房间信息
-  async getRoomInfo(roomId: string): Promise<IRoom | null> {
+  async getRoomInfo(roomId: string): Promise<Room | null> {
     try {
       // 检查本地缓存中是否有该房间
       const allRooms = Taro.getStorageSync('allRooms') || '{}'
@@ -281,7 +281,7 @@ export const roomService = {
       
       // 如果本地没有,使用mock数据
       if (mockRooms[roomId]) {
-        return mockRooms[roomId] as IRoom
+        return mockRooms[roomId] as Room
       }
       
       return null
@@ -292,14 +292,14 @@ export const roomService = {
   },
 
   // 获取进行中的房间
-  async getActiveRooms(userId: string): Promise<IRoom[]> {
+  async getActiveRooms(userId: string): Promise<Room[]> {
     try {
       // 从缓存或模拟数据获取所有房间
       const allRooms = Taro.getStorageSync('allRooms') || '{}'
       const roomsObj = JSON.parse(allRooms)
       
       // 添加类型断言,确保 Object.values 返回的是 Room[] 类型
-      const activeRooms = Object.values(roomsObj) as IRoom[];
+      const activeRooms = Object.values(roomsObj) as Room[];
       
       // 过滤出用户参与的且状态为"waiting"或"playing"的房间
       const userActiveRooms = activeRooms.filter(room => {
@@ -337,7 +337,7 @@ export const roomService = {
             maxPlayers: 8,
             visibility: 'public'
           }
-        ] as IRoom[]
+        ] as Room[]
       }
       
       return userActiveRooms

+ 145 - 0
src/services/user.ts

@@ -0,0 +1,145 @@
+// services/user.ts
+import { ensureStringId } from '@/utils/db-helper'
+import { Result, createSuccess, createError } from '@/types/result'
+import { cloudApi } from '@/api'
+import { type UserInfo, type OpenIdResult } from '@/types/user'
+import { USE_MOCK } from '@/services'
+
+// Mock用户数据
+const mockUser: UserInfo = {
+  nickname: '测试用户',
+  avatar: 'https://example.com/avatar.jpg',
+  openid: 'mock_openid_123456',
+}
+
+export const userService = {
+  // 获取用户信息
+  async getUserInfo(): Promise<Result<UserInfo>> {
+    // 如果在开发环境或非小程序环境,使用Mock数据
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return new Promise(resolve => {
+        setTimeout(() => {
+          resolve(createSuccess(mockUser))
+        }, 300)
+      })
+    }
+    
+    // 使用cloudApi调用云函数
+    return cloudApi.call<UserInfo>('getUserInfo')
+      .then(result => {
+        // 如果没有返回数据,使用mock数据
+        if (result.success && !result.data) {
+          return createSuccess(mockUser)
+        }
+        return result
+      })
+      .catch(error => {
+        console.error('获取用户信息失败:', error)
+        return createError(error.message || '获取用户信息失败')
+      })
+  },
+  
+  // 获取OpenID
+  async getOpenId(code: string): Promise<Result<OpenIdResult>> {
+    // 如果在开发环境或非小程序环境,使用Mock数据
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return createSuccess({ 
+        openid: 'mock_openid_' + Date.now() 
+      })
+    }
+    
+    // 使用cloudApi调用云函数
+    return cloudApi.call<OpenIdResult>('getOpenId', { code })
+      .catch(error => {
+        console.error('获取OpenID失败:', error)
+        return createError(error.message || '获取OpenID失败')
+      })
+  },
+  
+  // 保存用户信息到云端
+  async saveUserToCloud(userData: UserInfo): Promise<Result<string>> {
+    // 如果在开发环境或非小程序环境,返回Mock成功
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return createSuccess('mock_doc_id')
+    }
+    
+    try {
+      const db = cloudApi.db()
+      
+      // 查询用户是否存在
+      const userQuery = await db.collection('users').where({
+        openid: userData.openid
+      }).get()
+      
+      // 如果用户已存在,更新信息
+      if (userQuery.data && userQuery.data.length > 0) {
+        const docId = ensureStringId(userQuery.data[0]._id)
+        
+        await db.collection('users').doc(docId).update({
+          data: {
+            nickname: userData.nickname,
+            avatar: userData.avatar,
+            role: userData.role,
+            updateTime: db.serverDate()
+          }
+        })
+        return createSuccess(docId)
+      } 
+      
+      // 创建新用户
+      const addResult = await db.collection('users').add({
+        data: {
+          ...userData,
+          createTime: db.serverDate(),
+          updateTime: db.serverDate()
+        }
+      })
+      
+      // 确保返回的 _id 是字符串
+      const newId = typeof addResult._id === 'number' 
+        ? String(addResult._id) 
+        : addResult._id as string
+        
+      return createSuccess(newId)
+    } catch (error) {
+      console.error('保存用户信息到云端失败:', error)
+      return createError(error.message || '保存用户信息失败')
+    }
+  },
+  
+  // 获取用户角色(用于权限控制)
+  async getUserRole(openid?: string): Promise<Result<string>> {
+    // 如果在开发环境或非小程序环境,返回Mock角色
+    if (USE_MOCK || process.env.TARO_ENV !== 'weapp') {
+      return createSuccess('user')
+    }
+    
+    try {
+      // 如果没有提供openid,获取当前用户
+      if (!openid) {
+        const userResult = await this.getUserInfo()
+        if (!userResult.success || !userResult.data) {
+          return createError('无法获取当前用户信息')
+        }
+        openid = userResult.data.openid
+      }
+      
+      const db = cloudApi.db()
+      
+      // 查询用户记录
+      const userQuery = await db.collection('users').where({
+        openid
+      }).get()
+      
+      if (userQuery.data && userQuery.data.length > 0) {
+        return createSuccess(userQuery.data[0].role || 'user')
+      }
+      
+      // 用户不存在,返回默认角色
+      return createSuccess('user')
+    } catch (error) {
+      console.error('获取用户角色失败:', error)
+      return createError(error.message || '获取用户角色失败')
+    }
+  }
+}

+ 6 - 6
src/stores/game.ts

@@ -1,13 +1,13 @@
 // 管理游戏列表和通用游戏详情
 import { defineStore } from 'pinia'
 import { ref, computed } from 'vue'
-import { gameService} from '@/services/api/game'
-import { IGame } from '@/types/game'
+import { gameService} from '@/services/game'
+import { Game } from '@/types/game'
 
 export const useGameStore = defineStore('game', () => {
   // 状态
-  const games = ref<IGame[]>([])
-  const currentGame = ref<IGame | null>(null)
+  const games = ref<Game[]>([])
+  const currentGame = ref<Game | null>(null)
   const loading = ref(false)
   const error = ref<string | null>(null)
   
@@ -39,7 +39,7 @@ export const useGameStore = defineStore('game', () => {
   }
   
   // 获取游戏详情
-  async function getGameDetail(id: string): Promise<IGame | null> {
+  async function getGameDetail(id: string): Promise<Game | null> {
     // 先从缓存中查找
     const cachedGame = games.value.find(game => game.id === id)
     if (cachedGame) {
@@ -73,7 +73,7 @@ export const useGameStore = defineStore('game', () => {
   }
   
   // 设置当前游戏
-  function setCurrentGame(game: IGame | null) {
+  function setCurrentGame(game: Game | null) {
     currentGame.value = game
   }
   

+ 1 - 1
src/stores/games/index.ts

@@ -7,7 +7,7 @@ export { useWerewolfStore } from './werewolf'
 // 游戏类型与Store的映射
 import { useTurtleSoupStore } from './turtlesoup'
 import { useWerewolfStore } from './werewolf'
-import { GAME_TYPES } from '@/services/api/games'
+import { GAME_TYPES } from '@/services/games'
 
 // 根据游戏类型获取对应的Store
 export function getGameStoreByType(type: string) {

+ 404 - 43
src/stores/games/turtlesoup.ts

@@ -1,70 +1,431 @@
-//海龟汤游戏特定的状态管理
+// stores/games/turtlesoup.ts
 import { defineStore } from 'pinia'
-import { turtleSoupService } from '@/services/api/games'
-
-// 海龟汤游戏状态
-export interface TurtleSoupGame {
-  id: string
-  title: string
-  story: string
-  solution: string
-  hints: string[]
-  questions: TurtleSoupQuestion[]
-  // ... 其他特定字段
-}
-
-export interface TurtleSoupQuestion {
-  id: string
-  content: string
-  answer: string
-  askedBy: string
-  timestamp: number
-}
+import { turtleSoupService } from '@/services/games/turtlesoup'
+import { 
+  type TurtleSoupGame, 
+  type TurtleSoupQuestion,
+  type TurtleSoupHint,
+  type TurtleSoupGameResult,
+  type TurtleSoupAnswerType
+} from '@/types/games/turtlesoup'
+import { useUserStore } from '@/stores/user'
 
 export const useTurtleSoupStore = defineStore('turtlesoup', {
   state: () => ({
     currentGame: null as TurtleSoupGame | null,
     loading: false,
-    revealedHints: [] as number[],
-    // ... 其他状态
+    submitting: false,
+    error: null as string | null,
+    userRole: 'player' as 'player' | 'storyteller',
+    pendingQuestions: [] as TurtleSoupQuestion[], // 待回答的问题
+    isSolved: false,
+    // 用于本地临时存储
+    localDraft: '', // 问题/解答草稿
   }),
   
+  getters: {
+    // 获取已公开的提示
+    revealedHints(): TurtleSoupHint[] {
+      if (!this.currentGame) return [];
+      return this.currentGame.hints.filter(hint => hint.revealed);
+    },
+    
+    // 获取未公开的提示
+    unrevealedHints(): TurtleSoupHint[] {
+      if (!this.currentGame) return [];
+      return this.currentGame.hints.filter(hint => !hint.revealed);
+    },
+    
+    // 获取排序后的问题列表(最新的在前)
+    sortedQuestions(): TurtleSoupQuestion[] {
+      if (!this.currentGame) return [];
+      return [...this.currentGame.questions].sort((a, b) => 
+        new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
+      );
+    },
+    
+    // 游戏是否激活
+    isGameActive(): boolean {
+      return this.currentGame?.status === 'active';
+    },
+    
+    // 当前用户是否为主持人
+    isStoryteller(): boolean {
+      if (!this.currentGame) return false;
+      const userStore = useUserStore();
+      return this.currentGame.storyteller === userStore.openid;
+    },
+    
+    // 获取游戏时长(秒)
+    gameDuration(): number {
+      if (!this.currentGame || !this.currentGame.startTime) return 0;
+      
+      const start = new Date(this.currentGame.startTime).getTime();
+      const end = this.currentGame.endTime 
+        ? new Date(this.currentGame.endTime).getTime() 
+        : Date.now();
+      
+      return Math.floor((end - start) / 1000);
+    }
+  },
+  
   actions: {
+    // 重置状态
+    resetState() {
+      this.currentGame = null;
+      this.loading = false;
+      this.submitting = false;
+      this.error = null;
+      this.userRole = 'player';
+      this.pendingQuestions = [];
+      this.isSolved = false;
+      this.localDraft = '';
+    },
+    
     // 加载游戏数据
-    async loadGame(gameId: string, role: string) {
-      this.loading = true
+    async loadGame(gameId: string, role = 'player') {
+      this.loading = true;
+      this.error = null;
+      this.userRole = role as 'player' | 'storyteller';
+      
       try {
-        const result = await turtleSoupService.getGameData(gameId, role)
-        if (result && result.game) {
-          this.currentGame = result.game
-          return true
+        const result = await turtleSoupService.getGameData(gameId, role);
+        
+        if (result.success && result.data) {
+          this.currentGame = result.data;
+          
+          // 如果是讲述者,找出待回答的问题
+          if (role === 'storyteller') {
+            this.pendingQuestions = result.data.questions.filter(q => !q.answered);
+          }
+          
+          return true;
+        } else {
+          this.error = result.message || '加载游戏数据失败';
+          return false;
         }
-        return false
       } catch (error) {
-        console.error('加载海龟汤游戏失败:', error)
-        return false
+        console.error('加载海龟汤游戏失败:', error);
+        this.error = error instanceof Error ? error.message : '未知错误';
+        return false;
       } finally {
-        this.loading = false
+        this.loading = false;
       }
     },
     
     // 提交问题
     async submitQuestion(content: string) {
-      if (!this.currentGame) return false
+      if (!this.currentGame || !content.trim()) {
+        this.error = '无效的问题或游戏未加载';
+        return false;
+      }
+      
+      this.submitting = true;
+      this.error = null;
+      
+      try {
+        const result = await turtleSoupService.submitQuestion(this.currentGame.id, content);
+        
+        if (result.success && result.data?.questionId) {
+          // 获取用户信息
+          const userStore = useUserStore();
+          
+          // 创建新问题对象
+          const newQuestion: TurtleSoupQuestion = {
+            id: result.data.questionId,
+            content,
+            askedBy: userStore.openid,
+            askedByName: userStore.userInfo?.nickname,
+            timestamp: new Date().toISOString(),
+            answered: false
+          };
+          
+          // 更新本地游戏状态
+          if (this.currentGame) {
+            this.currentGame = {
+              ...this.currentGame,
+              questions: [...this.currentGame.questions, newQuestion]
+            };
+          }
+          
+          // 清空草稿
+          this.localDraft = '';
+          return true;
+        } else {
+          this.error = result.message || '提交问题失败';
+          return false;
+        }
+      } catch (error) {
+        console.error('提交问题失败:', error);
+        this.error = error instanceof Error ? error.message : '提交问题时发生错误';
+        return false;
+      } finally {
+        this.submitting = false;
+      }
+    },
+    
+    // 回答问题(讲述者)
+    async answerQuestion(questionId: string, answer: TurtleSoupAnswerType) {
+      if (!this.currentGame || this.userRole !== 'storyteller') {
+        this.error = '无权限回答问题';
+        return false;
+      }
+      
+      this.submitting = true;
+      this.error = null;
+      
+      try {
+        const result = await turtleSoupService.answerQuestion(questionId, answer);
+        
+        if (result.success && result.data?.success) {
+          // 更新本地状态
+          if (this.currentGame) {
+            const updatedQuestions = this.currentGame.questions.map(q => {
+              if (q.id === questionId) {
+                return {
+                  ...q,
+                  answer,
+                  answered: true,
+                  answeredAt: new Date().toISOString()
+                };
+              }
+              return q;
+            });
+            
+            this.currentGame = {
+              ...this.currentGame,
+              questions: updatedQuestions
+            };
+            
+            // 从待回答列表中移除
+            this.pendingQuestions = this.pendingQuestions.filter(q => q.id !== questionId);
+          }
+          return true;
+        } else {
+          this.error = result.message || '回答问题失败';
+          return false;
+        }
+      } catch (error) {
+        console.error('回答问题失败:', error);
+        this.error = error instanceof Error ? error.message : '回答问题时发生错误';
+        return false;
+      } finally {
+        this.submitting = false;
+      }
+    },
+    
+    // 公开提示
+    async revealHint(hintIndex: number) {
+      if (!this.currentGame || this.userRole !== 'storyteller') {
+        this.error = '无权限公开提示';
+        return false;
+      }
+      
+      // 检查索引是否有效
+      if (hintIndex < 0 || hintIndex >= (this.currentGame.hints?.length || 0)) {
+        this.error = '无效的提示索引';
+        return false;
+      }
+      
+      this.submitting = true;
+      this.error = null;
       
       try {
-        const result = await turtleSoupService.submitQuestion(this.currentGame.id, content)
-        if (result && result.questionId) {
-          // 更新本地状态...
-          return true
+        const result = await turtleSoupService.revealHint(this.currentGame.id, hintIndex);
+        
+        if (result.success && result.data?.success) {
+          // 更新本地状态
+          if (this.currentGame) {
+            const updatedHints = this.currentGame.hints.map((hint, index) => {
+              if (index === hintIndex) {
+                return {
+                  ...hint,
+                  revealed: true,
+                  revealedAt: new Date().toISOString()
+                };
+              }
+              return hint;
+            });
+            
+            this.currentGame = {
+              ...this.currentGame,
+              hints: updatedHints
+            };
+          }
+          return true;
+        } else {
+          this.error = result.message || '公开提示失败';
+          return false;
         }
-        return false
       } catch (error) {
-        console.error('提交问题失败:', error)
-        return false
+        console.error('公开提示失败:', error);
+        this.error = error instanceof Error ? error.message : '公开提示时发生错误';
+        return false;
+      } finally {
+        this.submitting = false;
+      }
+    },
+    
+    // 提交解答
+    async submitSolution(solution: string) {
+      if (!this.currentGame || !solution.trim()) {
+        this.error = '无效的解答或游戏未加载';
+        return false;
+      }
+      
+      this.submitting = true;
+      this.error = null;
+      
+      try {
+        const result = await turtleSoupService.submitSolution(this.currentGame.id, solution);
+        
+        if (result.success) {
+          // 如果答案正确
+          if (result.data?.correct) {
+            this.isSolved = true;
+            // 获取用户信息
+            const userStore = useUserStore();
+            
+            // 结束游戏
+            await this.endGame({
+              solved: true,
+              solvedBy: userStore.openid
+            });
+            
+            return { success: true, correct: true };
+          }
+          
+          // 答案错误
+          return { success: true, correct: false };
+        } else {
+          this.error = result.message || '提交解答失败';
+          return { success: false, correct: false };
+        }
+      } catch (error) {
+        console.error('提交解答失败:', error);
+        this.error = error instanceof Error ? error.message : '提交解答时发生错误';
+        return { success: false, correct: false };
+      } finally {
+        this.submitting = false;
+      }
+    },
+    
+    // 结束游戏
+    async endGame(result: { solved: boolean; solvedBy?: string }) {
+      if (!this.currentGame) {
+        this.error = '游戏未加载';
+        return null;
+      }
+      
+      this.submitting = true;
+      this.error = null;
+      
+      try {
+        const response = await turtleSoupService.endGame(this.currentGame.id, result);
+        
+        if (response.success && response.data) {
+          // 更新游戏状态
+          if (this.currentGame) {
+            this.currentGame = {
+              ...this.currentGame,
+              status: 'completed',
+              endTime: new Date().toISOString()
+            };
+          }
+          
+          return response.data;
+        } else {
+          this.error = response.message || '结束游戏失败';
+          return null;
+        }
+      } catch (error) {
+        console.error('结束游戏失败:', error);
+        this.error = error instanceof Error ? error.message : '结束游戏时发生错误';
+        return null;
+      } finally {
+        this.submitting = false;
+      }
+    },
+    
+    // 创建新游戏
+    async createGame(gameData: {
+      title: string;
+      description: string;
+      solution: string;
+      hints: string[];
+      difficulty: string;
+    }) {
+      this.submitting = true;
+      this.error = null;
+      
+      try {
+        const result = await turtleSoupService.createGame(gameData);
+        
+        if (result.success && result.data?.gameId) {
+          // 成功创建游戏
+          return result.data.gameId;
+        } else {
+          this.error = result.message || '创建游戏失败';
+          return null;
+        }
+      } catch (error) {
+        console.error('创建游戏失败:', error);
+        this.error = error instanceof Error ? error.message : '创建游戏时发生错误';
+        return null;
+      } finally {
+        this.submitting = false;
+      }
+    },
+    
+    // 获取用户创建的游戏列表
+    async fetchCreatedGames() {
+      this.loading = true;
+      this.error = null;
+      
+      try {
+        const result = await turtleSoupService.getCreatedGames();
+        
+        if (result.success) {
+          return result.data || [];
+        } else {
+          this.error = result.message || '获取创建的游戏列表失败';
+          return [];
+        }
+      } catch (error) {
+        console.error('获取创建的游戏列表失败:', error);
+        this.error = error instanceof Error ? error.message : '获取游戏列表时发生错误';
+        return [];
+      } finally {
+        this.loading = false;
+      }
+    },
+    
+    // 获取用户参与的游戏列表
+    async fetchJoinedGames() {
+      this.loading = true;
+      this.error = null;
+      
+      try {
+        const result = await turtleSoupService.getJoinedGames();
+        
+        if (result.success) {
+          return result.data || [];
+        } else {
+          this.error = result.message || '获取参与的游戏列表失败';
+          return [];
+        }
+      } catch (error) {
+        console.error('获取参与的游戏列表失败:', error);
+        this.error = error instanceof Error ? error.message : '获取游戏列表时发生错误';
+        return [];
+      } finally {
+        this.loading = false;
       }
     },
     
-    // ... 其他方法
+    // 保存问题草稿
+    saveDraft(content: string) {
+      this.localDraft = content;
+    }
   }
-})
+});

+ 2 - 108
src/stores/games/werewolf.ts

@@ -1,112 +1,6 @@
 import { defineStore } from 'pinia'
-import { werewolfService } from '@/services/api/games'
-
-// 玩家角色类型
-export type WerewolfRole = 
-  | 'werewolf'     // 狼人
-  | 'villager'     // 村民
-  | 'seer'         // 预言家
-  | 'witch'        // 女巫
-  | 'hunter'       // 猎人
-  | 'guard'        // 守卫
-  | 'idiot'        // 白痴
-  | 'elder'        // 长老
-  | 'cupid'        // 丘比特
-  | 'thief'        // 盗贼
-  | 'moderator';   // 主持人/上帝
-
-// 游戏阶段
-export type GamePhase =
-  | 'waiting'      // 等待开始
-  | 'night'        // 夜晚
-  | 'day'          // 白天
-  | 'vote'         // 投票
-  | 'ended';       // 游戏结束
-
-// 行动类型
-export type ActionType = 
-  | 'kill'         // 狼人杀人
-  | 'check'        // 预言家查验
-  | 'save'         // 女巫救人
-  | 'poison'       // 女巫毒人
-  | 'protect'      // 守卫保护
-  | 'shoot'        // 猎人开枪
-  | 'vote'         // 投票
-  | 'pair';        // 丘比特连线
-
-// 玩家状态
-export type PlayerStatus = 
-  | 'alive'        // 存活
-  | 'dead'         // 死亡
-  | 'protected'    // 受保护
-  | 'poisoned'     // 中毒
-  | 'coupled';     // 情侣
-
-// 游戏结果类型
-export type GameResult = 
-  | 'werewolf_win'  // 狼人阵营胜利
-  | 'villager_win'  // 村民阵营胜利
-  | 'couple_win'    // 情侣胜利
-  | 'draw'          // 平局
-  | null;           // 游戏进行中
-
-// 玩家信息
-export interface WerewolfPlayer {
-  id: string;
-  name: string;
-  avatar: string;
-  role: WerewolfRole;
-  status: PlayerStatus;
-  isReady: boolean;
-  isDead: boolean;
-  actions: {
-    [key in ActionType]?: boolean;
-  };
-  // 特殊状态
-  isLovers?: boolean;      // 是否为情侣
-  lastWords?: string;      // 遗言
-  voteTarget?: string;     // 投票目标
-  deadRound?: number;      // 死亡回合
-}
-
-// 游戏日志
-export interface GameLog {
-  round: number;
-  phase: GamePhase;
-  content: string;
-  timestamp: number;
-  isPublic: boolean;
-  targetPlayers?: string[];
-}
-
-// 游戏状态
-export interface WerewolfGame {
-  id: string;
-  title: string;
-  hosterId: string;
-  hosterName: string;
-  createTime: number;
-  startTime: number | null;
-  endTime: number | null;
-  players: WerewolfPlayer[];
-  currentRound: number;
-  phase: GamePhase;
-  result: GameResult;
-  currentAction: ActionType | null;
-  timeLimit: number;      // 每个阶段的时间限制(秒)
-  logs: GameLog[];
-  options: {
-    roles: {
-      [key in WerewolfRole]?: number; // 角色数量配置
-    };
-    includeSpecialRoles: boolean; // 是否包含特殊角色
-    allowLastWords: boolean;      // 是否允许遗言
-    nightTimeLimit: number;       // 夜晚时间限制
-    dayTimeLimit: number;         // 白天时间限制
-    voteTimeLimit: number;        // 投票时间限制
-  };
-}
-
+import { werewolfService } from '@/services/games'
+import { type WerewolfGame, type WerewolfPlayer, type WerewolfRole, type ActionType, type GameResult } from '@/types/games/werewolf'
 // 定义 Store
 export const useWerewolfStore = defineStore('werewolf', {
   state: () => ({

+ 13 - 13
src/stores/room.ts

@@ -1,8 +1,8 @@
 // stores/room.ts - 管理房间状态
 import { defineStore } from 'pinia'
 import { ref, reactive, computed } from 'vue'
-import { roomService } from '@/services/api/room'
-import { type IRoom, type IRoomUser, type IRecentRoom } from '@/types/room'
+import { roomService } from '@/services/room'
+import { type Room, type RoomUser, type RecentRoom } from '@/types/room'
 import { useUserStore } from '@/stores/user'
 import Taro from '@tarojs/taro'
 
@@ -11,20 +11,20 @@ export const useRoomStore = defineStore('room', () => {
   const userStore = useUserStore()
   
   // 当前房间信息
-  const currentRoom = ref<IRoom | null>(null)
+  const currentRoom = ref<Room | null>(null)
   
   // 最近加入的房间
-  const recentRooms = ref<IRecentRoom[]>([])
+  const recentRooms = ref<RecentRoom[]>([])
   
   // 历史记录状态
   const historyState = reactive({
-    activeRooms: [] as IRoom[],
+    activeRooms: [] as Room[],
     endedGames: [] as any[],
     isLoading: false
   })
   
   // 设置当前房间
-  function setCurrentRoom(room: IRoom | null) {
+  function setCurrentRoom(room: Room | null) {
     currentRoom.value = room
   }
   
@@ -36,7 +36,7 @@ export const useRoomStore = defineStore('room', () => {
     }
     
     // 从已加载的活跃房间和历史记录构建最近房间列表
-    const recentList: IRecentRoom[] = [
+    const recentList: RecentRoom[] = [
       // 活跃房间
       ...historyState.activeRooms.map(room => ({
         id: room.id,
@@ -98,7 +98,7 @@ export const useRoomStore = defineStore('room', () => {
   }
   
   // 创建房间 - 调用API并更新store
-  async function createRoom(roomData: IRoom) {
+  async function createRoom(roomData: Room) {
     try {
       // 检查用户是否已注册
       const userCheck = await checkUserRegistered()
@@ -140,7 +140,7 @@ export const useRoomStore = defineStore('room', () => {
       }
       
       // 构建用户数据
-      const userData: IRoomUser = {
+      const userData: RoomUser = {
         id: userStore.openid,
         name: userStore.nickname,
         avatar: userStore.avatar,
@@ -208,7 +208,7 @@ export const useRoomStore = defineStore('room', () => {
   }
   
   // 添加用户到当前房间
-  function addUserToRoom(user: IRoomUser) {
+  function addUserToRoom(user: RoomUser) {
     if (currentRoom.value) {
       // 检查是否已存在
       const existingUserIndex = currentRoom.value.users.findIndex(u => u.id === user.id)
@@ -233,7 +233,7 @@ export const useRoomStore = defineStore('room', () => {
   }
   
   // 更新用户信息
-  function updateUserInRoom(userId: string, data: Partial<IRoomUser>) {
+  function updateUserInRoom(userId: string, data: Partial<RoomUser>) {
     if (currentRoom.value) {
       const userIndex = currentRoom.value.users.findIndex(u => u.id === userId)
       if (userIndex !== -1) {
@@ -294,7 +294,7 @@ export const useRoomStore = defineStore('room', () => {
   }
   
   // 更新房间设置
-  function updateRoomSettings(settings: Partial<IRoom>) {
+  function updateRoomSettings(settings: Partial<Room>) {
     if (currentRoom.value) {
       currentRoom.value = {
         ...currentRoom.value,
@@ -363,7 +363,7 @@ export const useRoomStore = defineStore('room', () => {
     success: boolean, 
     message?: string, 
     needPassword?: boolean,
-    roomData?: IRoom  // 添加房间数据
+    roomData?: Room  // 添加房间数据
   }> {
     try {
       // 1. 检查用户是否注册

+ 3 - 3
src/stores/user.ts

@@ -1,8 +1,8 @@
 // stores/user.ts - 管理用户信息、登录状态、角色权限等
 import { defineStore } from 'pinia'
 import Taro from '@tarojs/taro'
-import { userService} from '@/services/api/user'
-import { type IUserInfo } from '@/types/user'
+import { userService} from '@/services/user'
+import { type UserInfo } from '@/types/user'
 import { ref, computed } from 'vue'
 
 export type UserRole = 'player' | 'hoster'
@@ -30,7 +30,7 @@ export const useUserStore = defineStore('user', () => {
   const roomCount = ref(0)   // 创建房间数量
   
   // 计算属性
-  const userInfo = computed<IUserInfo>(() => ({
+  const userInfo = computed<UserInfo>(() => ({
     openid: openid.value,
     nickname: nickname.value,
     avatar: avatar.value,

+ 1 - 8
src/types/game.ts

@@ -1,5 +1,5 @@
 // 游戏数据接口
-export interface IGame {
+export interface Game {
     id: string; // 游戏ID
     title: string; // 游戏标题
     image: string; // 游戏图片
@@ -18,11 +18,4 @@ export interface IGame {
       question: string; // 游戏示例问题
       answer: string; // 游戏示例答案 
     }[];
-  }
-  
-  // API返回结果接口
-  export interface IApiResult<T> {
-    success: boolean;
-    data?: T;
-    message?: string;
   }

+ 125 - 26
src/types/games/turtlesoup.ts

@@ -1,26 +1,125 @@
-// 海龟汤游戏数据类型
-export interface ITurtleSoupGame {
-    id: string;
-    title: string;
-    story: string;
-    solution: string;
-    hints: string[];
-    questions: ITurtleSoupQuestion[];
-    // 其他海龟汤特有属性
-  }
-  
-  export interface ITurtleSoupQuestion {
-    id: string;
-    content: string;
-    answer: string;
-    askedBy: string;
-    timestamp: number;
-  }
-  
-  export interface ITurtleSoupGameResult {
-    solved: boolean;
-    solvedBy?: string;
-    timeUsed: number;
-    questionsCount: number;
-    hintsUsed: number[];
-  }
+// types/games/turtlesoup.ts
+
+/**
+ * 海龟汤游戏状态
+ */
+export type TurtleSoupGameStatus = 'created' | 'active' | 'completed' | 'abandoned';
+
+/**
+ * 海龟汤游戏难度
+ */
+export type TurtleSoupDifficulty = 'easy' | 'medium' | 'hard' | 'expert';
+
+/**
+ * 海龟汤问题答案类型
+ */
+export type TurtleSoupAnswerType = 'yes' | 'no' | 'irrelevant';
+
+/**
+ * 游戏提示结构
+ */
+export interface TurtleSoupHint {
+  content: string;     // 提示内容
+  revealed: boolean;   // 是否已公开
+  revealedAt?: string; // 公开时间
+}
+
+/**
+ * 海龟汤问题
+ */
+export interface TurtleSoupQuestion {
+  id: string;                   // 问题ID
+  content: string;              // 问题内容
+  answer?: TurtleSoupAnswerType;// 回答类型:是/否/不重要
+  askedBy: string;              // 提问者ID
+  askedByName?: string;         // 提问者名称
+  timestamp: string;            // 提问时间(ISO格式)
+  answered: boolean;            // 是否已回答
+  answeredAt?: string;          // 回答时间
+}
+
+/**
+ * 游戏参与者结构
+ */
+export interface TurtleSoupPlayer {
+  id: string;          // 用户ID
+  name: string;        // 用户名称
+  avatar?: string;     // 头像
+  joinedAt: string;    // 加入时间
+  role?: 'player' | 'storyteller'; // 角色:玩家/讲述者
+}
+
+/**
+ * 海龟汤游戏
+ */
+export interface TurtleSoupGame {
+  id: string;                        // 游戏ID
+  title: string;                     // 游戏标题
+  description: string;               // 游戏描述/故事
+  solution: string;                  // 解决方案
+  hints: TurtleSoupHint[];           // 提示列表
+  questions: TurtleSoupQuestion[];   // 问题列表
+  status: TurtleSoupGameStatus;      // 游戏状态
+  createTime: string;                // 创建时间(ISO格式)
+  startTime?: string;                // 开始时间
+  endTime?: string;                  // 结束时间
+  storyteller: string;               // 讲述者ID
+  storytellerName?: string;          // 讲述者名称 
+  players: TurtleSoupPlayer[];       // 参与者列表
+  difficulty: TurtleSoupDifficulty;  // 游戏难度
+  maxPlayers?: number;               // 最大玩家数
+  isPrivate?: boolean;               // 是否为私人游戏
+  code?: string;                     // 加入码(私人游戏时使用)
+  tags?: string[];                   // 标签
+  likeCount?: number;                // 点赞数
+  playCount?: number;                // 游玩次数
+  solvedCount?: number;              // 被解决次数
+}
+
+/**
+ * 海龟汤游戏结果
+ */
+export interface TurtleSoupGameResult {
+  gameId: string;                    // 游戏ID
+  solved: boolean;                   // 是否解决
+  solvedBy?: string;                 // 解决者ID
+  solvedByName?: string;             // 解决者名称
+  solution?: string;                 // 提交的解答
+  duration: number;                  // 游戏时长(秒)
+  questionCount: number;             // 提问次数
+  hintsRevealed: number;             // 使用的提示数
+  completionTime: string;            // 完成时间(ISO格式)
+  satisfaction?: number;             // 满意度评分(1-5)
+  feedback?: string;                 // 反馈
+}
+
+/**
+ * 创建海龟汤游戏参数
+ */
+export interface CreateTurtleSoupGameParams {
+  title: string;                     // 游戏标题
+  description: string;               // 游戏描述/故事
+  solution: string;                  // 解决方案
+  hints: string[];                   // 提示列表
+  difficulty: TurtleSoupDifficulty;  // 游戏难度
+  isPrivate?: boolean;               // 是否为私人游戏
+  tags?: string[];                   // 标签
+}
+
+/**
+ * 游戏统计信息
+ */
+export interface TurtleSoupGameStats {
+  totalGames: number;                // 总游戏数
+  completedGames: number;            // 已完成游戏数
+  averageDuration: number;           // 平均游戏时长(秒)
+  averageQuestions: number;          // 平均提问数
+  solveRate: number;                 // 解决率(0-1)
+  mostUsedHints: number;             // 最常使用的提示次数
+  difficultyDistribution: {          // 难度分布
+    easy: number;
+    medium: number;
+    hard: number;
+    expert: number;
+  };
+}

+ 106 - 0
src/types/games/werewolf.ts

@@ -0,0 +1,106 @@
+// types/games/werewolf.ts
+// 玩家角色类型
+export type WerewolfRole = 
+  | 'werewolf'     // 狼人
+  | 'villager'     // 村民
+  | 'seer'         // 预言家
+  | 'witch'        // 女巫
+  | 'hunter'       // 猎人
+  | 'guard'        // 守卫
+  | 'idiot'        // 白痴
+  | 'elder'        // 长老
+  | 'cupid'        // 丘比特
+  | 'thief'        // 盗贼
+  | 'moderator';   // 主持人/上帝
+
+// 游戏阶段
+export type GamePhase =
+  | 'waiting'      // 等待开始
+  | 'night'        // 夜晚
+  | 'day'          // 白天
+  | 'vote'         // 投票
+  | 'ended';       // 游戏结束
+
+// 行动类型
+export type ActionType = 
+  | 'kill'         // 狼人杀人
+  | 'check'        // 预言家查验
+  | 'save'         // 女巫救人
+  | 'poison'       // 女巫毒人
+  | 'protect'      // 守卫保护
+  | 'shoot'        // 猎人开枪
+  | 'vote'         // 投票
+  | 'pair';        // 丘比特连线
+
+// 玩家状态
+export type PlayerStatus = 
+  | 'alive'        // 存活
+  | 'dead'         // 死亡
+  | 'protected'    // 受保护
+  | 'poisoned'     // 中毒
+  | 'coupled';     // 情侣
+
+// 游戏结果类型
+export type GameResult = 
+  | 'werewolf_win'  // 狼人阵营胜利
+  | 'villager_win'  // 村民阵营胜利
+  | 'couple_win'    // 情侣胜利
+  | 'draw'          // 平局
+  | null;           // 游戏进行中
+
+// 玩家信息
+export interface WerewolfPlayer {
+  id: string;
+  name: string;
+  avatar: string;
+  role: WerewolfRole;
+  status: PlayerStatus;
+  isReady: boolean;
+  isDead: boolean;
+  actions: {
+    [key in ActionType]?: boolean;
+  };
+  // 特殊状态
+  isLovers?: boolean;      // 是否为情侣
+  lastWords?: string;      // 遗言
+  voteTarget?: string;     // 投票目标
+  deadRound?: number;      // 死亡回合
+}
+
+// 游戏日志
+export interface GameLog {
+  round: number;
+  phase: GamePhase;
+  content: string;
+  timestamp: number;
+  isPublic: boolean;
+  targetPlayers?: string[];
+}
+
+// 游戏状态
+export interface WerewolfGame {
+  id: string;
+  title: string;
+  hosterId: string;
+  hosterName: string;
+  createTime: number;
+  startTime: number | null;
+  endTime: number | null;
+  players: WerewolfPlayer[];
+  currentRound: number;
+  phase: GamePhase;
+  result: GameResult;
+  currentAction: ActionType | null;
+  timeLimit: number;      // 每个阶段的时间限制(秒)
+  logs: GameLog[];
+  options: {
+    roles: {
+      [key in WerewolfRole]?: number; // 角色数量配置
+    };
+    includeSpecialRoles: boolean; // 是否包含特殊角色
+    allowLastWords: boolean;      // 是否允许遗言
+    nightTimeLimit: number;       // 夜晚时间限制
+    dayTimeLimit: number;         // 白天时间限制
+    voteTimeLimit: number;        // 投票时间限制
+  };
+}

+ 16 - 0
src/types/result.ts

@@ -0,0 +1,16 @@
+// types/result.ts
+// 通用返回类型定义
+export interface Result<T> {
+  success: boolean;
+  data?: T;
+  message?: string;
+  code?: number;
+}
+
+export function createSuccess<T>(data: T, message?: string): Result<T> {
+  return { success: true, data, message };
+}
+
+export function createError<T = never>(message: string, code?: number): Result<T> {
+  return { success: false, message, code };
+}

+ 4 - 4
src/types/room.ts

@@ -1,5 +1,5 @@
 // 导出类型定义以便共享
-export interface IRoomUser {
+export interface RoomUser {
     id: string
     name: string
     avatar: string
@@ -7,7 +7,7 @@ export interface IRoomUser {
     joinTime: number
   }
   
-  export interface IRoom {
+  export interface Room {
     id: string
     name: string
     gameId: string
@@ -19,10 +19,10 @@ export interface IRoomUser {
     hosterName: string
     createTime: number
     status: 'waiting' | 'playing' | 'ended'
-    users: IRoomUser[]
+    users: RoomUser[]
   }
   
-  export interface IRecentRoom {
+  export interface RecentRoom {
     id: string
     name: string
     game: string

+ 2 - 2
src/types/user.ts

@@ -1,12 +1,12 @@
 // 明确定义接口类型
-export interface IUserInfo {
+export interface UserInfo {
     openid: string;
     nickname?: string;
     avatar?: string;
     role?: string;
   }
   
-  export interface IOpenIdResult {
+  export interface OpenIdResult {
     openid: string;
     [key: string]: any;
   }