pet_data/core/api-service.js

391 lines
9.2 KiB
JavaScript

// API 服務層 - 支援 mock 和真實 API 切換
export class ApiService {
constructor(config = {}) {
this.baseUrl = config.baseUrl || 'http://localhost:3000/api'
this.useMock = config.useMock !== false // 預設使用 mock
this.mockDelay = config.mockDelay || 100 // mock 延遲模擬網路請求
}
// 通用請求方法
async request(endpoint, options = {}) {
if (this.useMock) {
return this.mockRequest(endpoint, options)
}
return this.realRequest(endpoint, options)
}
// Mock 請求(使用本地資料)
async mockRequest(endpoint, options) {
await this.delay(this.mockDelay)
const [resource, action] = endpoint.split('/').filter(Boolean)
switch (`${resource}/${action}`) {
case 'pet/state':
return this.getMockPetState()
case 'pet/update':
return this.updateMockPetState(options.body)
case 'events/list':
return this.getMockEvents()
case 'events/trigger':
return this.triggerMockEvent(options.body)
case 'buffs/apply':
return this.applyMockBuff(options.body)
case 'deities/list':
return this.getMockDeities()
case 'deities/pray':
return this.mockPrayToDeity(options.body)
case 'fortune/draw':
return this.mockDrawFortune()
case 'temple/throw-jiaobei':
return { localFallback: true } // 使用本地邏輯
case 'items/list':
return this.getMockItems()
case 'items/use':
return this.mockUseItem(options.body)
case 'pet/save':
return this.saveMockPetState(options.body)
case 'pet/delete':
return this.deleteMockPetState()
case 'achievements/get':
return this.getMockAchievements()
case 'achievements/save':
return this.saveMockAchievements(options.body)
case 'inventory/get':
return this.getMockInventory()
case 'inventory/save':
return this.saveMockInventory(options.body)
case 'adventure/list':
return this.getMockAdventures()
case 'adventure/start':
return { success: true, message: '冒險開始' } // 實際邏輯在 AdventureSystem
case 'adventure/complete':
return { success: true, message: '冒險完成' }
default:
throw new Error(`Unknown endpoint: ${endpoint}`)
}
}
// 真實 API 請求
async realRequest(endpoint, options = {}) {
const url = `${this.baseUrl}/${endpoint}`
const config = {
method: options.method || 'GET',
headers: {
'Content-Type': 'application/json',
...options.headers
}
}
if (options.body) {
config.body = JSON.stringify(options.body)
}
const response = await fetch(url, config)
if (!response.ok) {
throw new Error(`API Error: ${response.status} ${response.statusText}`)
}
return response.json()
}
// 延遲模擬
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
// ========== API 端點方法 ==========
// 寵物狀態相關
async getPetState() {
return this.request('pet/state')
}
async updatePetState(updates) {
return this.request('pet/update', {
method: 'POST',
body: updates
})
}
async savePetState(state) {
return this.request('pet/save', {
method: 'POST',
body: state
})
}
async deletePetState() {
return this.request('pet/delete', {
method: 'DELETE'
})
}
// 事件相關
async getEvents() {
return this.request('events/list')
}
async triggerEvent(eventId, petState) {
return this.request('events/trigger', {
method: 'POST',
body: { eventId, petState }
})
}
// Buff 相關
async applyBuff(buffData) {
return this.request('buffs/apply', {
method: 'POST',
body: buffData
})
}
// 神明相關
async getDeities() {
return this.request('deities/list')
}
async prayToDeity(deityId, petState) {
return this.request('deities/pray', {
method: 'POST',
body: { deityId, petState }
})
}
// 籤詩相關
async drawFortune() {
return this.request('fortune/draw', {
method: 'POST'
})
}
// 擲筊相關
async throwJiaobei(params) {
return this.request('temple/throw-jiaobei', {
method: 'POST',
body: params
})
}
// 道具相關
async getItems() {
return this.request('items/list')
}
async useItem(itemId, count = 1) {
return this.request('items/use', {
method: 'POST',
body: { itemId, count }
})
}
// 冒險相關
async getAdventures() {
return this.request('adventure/list')
}
async startAdventure(adventureId) {
return this.request('adventure/start', {
method: 'POST',
body: { adventureId }
})
}
async completeAdventure(result) {
return this.request('adventure/complete', {
method: 'POST',
body: result
})
}
// ========== Mock 資料方法 ==========
getMockPetState() {
// 從 localStorage 或預設值讀取
const stored = localStorage.getItem('petState')
if (stored) {
return JSON.parse(stored)
}
return null
}
updateMockPetState(updates) {
const current = this.getMockPetState()
if (!current) {
console.warn('[ApiService] No current state found, cannot update')
return { success: false, message: '找不到當前狀態' }
}
// 使用深度合併,確保不會丟失原有字段
const updated = { ...current, ...updates }
localStorage.setItem('petState', JSON.stringify(updated))
return { success: true, data: updated }
}
saveMockPetState(state) {
localStorage.setItem('petState', JSON.stringify(state))
return { success: true, message: '狀態已儲存' }
}
getMockEvents() {
// 動態載入事件配置
return import('../data/events.js').then(m => m.EVENT_CONFIG)
}
triggerMockEvent({ eventId, petState }) {
// 模擬事件觸發邏輯
return { success: true, eventId, effects: [] }
}
applyMockBuff(buffData) {
return { success: true, buff: buffData }
}
async getMockDeities() {
const { DEITIES } = await import('../data/deities.js')
return DEITIES
}
mockPrayToDeity({ deityId, petState }) {
return {
success: true,
favorIncrease: 5,
message: '祈福成功'
}
}
mockDrawFortune() {
// 模擬抽籤邏輯
const grades = ['上上', '上', '中', '下', '下下']
const weights = [0.05, 0.15, 0.40, 0.30, 0.10]
const random = Math.random()
let sum = 0
let selectedGrade = '中'
for (let i = 0; i < weights.length; i++) {
sum += weights[i]
if (random <= sum) {
selectedGrade = grades[i]
break
}
}
return {
success: true,
lot: {
id: Math.floor(Math.random() * 100) + 1,
grade: selectedGrade,
poem1: '示例籤詩',
meaning: '示例解釋'
}
}
}
async getMockItems() {
const { ITEMS } = await import('../data/items.js')
return Object.values(ITEMS)
}
mockUseItem({ itemId, count }) {
return {
success: true,
itemId,
count,
effects: {}
}
}
deleteMockPetState() {
localStorage.removeItem('petState')
return { success: true, message: '寵物已刪除' }
}
// 成就相關
async getAchievements() {
return this.request('achievements/get')
}
async saveAchievements(data) {
return this.request('achievements/save', {
method: 'POST',
body: data
})
}
getMockAchievements() {
const stored = localStorage.getItem('achievements')
if (stored) {
return JSON.parse(stored)
}
return null
}
saveMockAchievements(data) {
localStorage.setItem('achievements', JSON.stringify(data))
return { success: true, message: '成就已儲存' }
}
// 背包相關
async getInventory() {
return this.request('inventory/get')
}
async saveInventory(data) {
return this.request('inventory/save', {
method: 'POST',
body: data
})
}
getMockInventory() {
const stored = localStorage.getItem('inventory')
if (stored) {
return JSON.parse(stored)
}
return {
inventory: {},
equipped: {
weapon: null,
armor: null,
hat: null,
accessory: null,
talisman: null,
special: null
},
appearance: {}
}
}
saveMockInventory(data) {
try {
const dataToSave = {
inventory: data.inventory || {},
equipped: data.equipped || {},
appearance: data.appearance || {}
}
localStorage.setItem('inventory', JSON.stringify(dataToSave))
console.log('[ApiService] 背包已儲存到 localStorage:', {
inventoryCount: Object.keys(dataToSave.inventory).length,
equipped: dataToSave.equipped
})
return { success: true, message: '背包已儲存' }
} catch (error) {
console.error('[ApiService] 儲存背包失敗:', error)
return { success: false, message: '儲存失敗: ' + error.message }
}
}
async getMockAdventures() {
const { ADVENTURES } = await import('../data/adventures.js')
return ADVENTURES
}
}
// 預設實例
export const apiService = new ApiService({
useMock: true, // 開發時使用 mock
mockDelay: 100
})