Skip to content

E-commerce Admin Platform API 服務架構文檔


文檔資訊

  • 最後更新:2025-07-22
  • 版本:1.0.0
  • 同步狀態:✅ 已與代碼同步
  • 自動更新:✅ 啟用

本文檔詳細說明 admin-platform-vue 專案的 API 服務層架構設計,包含 12 個專業化服務類、工廠模式實現、基礎抽象類設計及最佳實踐指南。

1. API 服務架構概覽

1.1 服務層設計原則

  • 面向對象設計:每個業務域有專門的服務類
  • 統一基礎類:所有服務繼承 BaseApiService
  • 工廠模式:統一的服務實例管理
  • 類型安全:完整的 TypeScript 支持
  • 錯誤處理:統一的錯誤處理機制
  • 可測試性:支持依賴注入和模擬測試

1.2 架構層次圖

                    Service Factory (工廠層)

     ┌─────────────────────────────────────────────────────┐
     │              Specific Services (服務層)              │
     ├─────────────────────────────────────────────────────┤
     │  UserApiService │ ProductApiService │ OrderApiService │
     │ CustomerApiService │ NotificationApiService │ ...     │
     └─────────────────────────────────────────────────────┘

     ┌─────────────────────────────────────────────────────┐
     │              Base API Service (基礎層)               │
     ├─────────────────────────────────────────────────────┤
     │  BaseApiService + QueryOptions + PaginationInfo    │
     └─────────────────────────────────────────────────────┘

     ┌─────────────────────────────────────────────────────┐
     │               Supabase Client (數據層)              │
     └─────────────────────────────────────────────────────┘

1.3 服務統計概覽

服務分類服務數量主要功能複雜度等級
用戶管理3個用戶、角色、權限⭐⭐⭐
業務核心4個客戶、產品、訂單、庫存⭐⭐⭐⭐
客服系統2個對話、客服代理⭐⭐⭐⭐
通知系統3個通知、群組通知、監控⭐⭐⭐⭐⭐
分析系統4個訂單、客戶、支援、活動分析⭐⭐⭐⭐
AI 增強2個Prompt模板、AI警示增強⭐⭐⭐⭐⭐
總計18個完整企業級服務⭐⭐⭐⭐⭐

2. 基礎架構設計

2.1 BaseApiService 抽象類

typescript
// src/api/services/base/BaseApiService.ts
export abstract class BaseApiService<T extends BaseEntity> {
  protected supabase: SupabaseClient
  protected tableName: string
  
  constructor(supabase: SupabaseClient, tableName: string) {
    this.supabase = supabase
    this.tableName = tableName
  }

  // CRUD 基礎操作
  async findAll(options?: QueryOptions<T>): Promise<T[]>
  async findById(id: string): Promise<T | null>
  async create(data: Partial<T>): Promise<T>
  async update(id: string, data: Partial<T>): Promise<T>
  async delete(id: string): Promise<void>
  
  // 分頁查詢
  async findWithPagination(options: QueryOptions<T>): Promise<{
    data: T[]
    pagination: PaginationInfo
  }>
  
  // 查詢構建器
  protected buildQuery(options?: QueryOptions<T>): QueryBuilder<T>
  
  // 錯誤處理
  protected handleError(error: unknown): never
}

2.2 核心類型定義

typescript
// src/api/services/base/types.ts

// 基礎實體接口
export interface BaseEntity {
  id: string
  created_at: string
  updated_at: string
}

// 查詢選項
export interface QueryOptions<T> {
  select?: string[]
  where?: Partial<T>
  orderBy?: { field: keyof T; direction: 'asc' | 'desc' }[]
  limit?: number
  offset?: number
  include?: string[]
}

// 分頁信息
export interface PaginationInfo {
  page: number
  pageSize: number
  total: number
  totalPages: number
  hasNext: boolean
  hasPrev: boolean
}

// 查詢構建器
export interface QueryBuilder<T> {
  select(columns?: string[]): QueryBuilder<T>
  eq(column: keyof T, value: any): QueryBuilder<T>
  in(column: keyof T, values: any[]): QueryBuilder<T>
  like(column: keyof T, pattern: string): QueryBuilder<T>
  order(column: keyof T, ascending?: boolean): QueryBuilder<T>
  limit(count: number): QueryBuilder<T>
  range(from: number, to: number): QueryBuilder<T>
}

2.3 錯誤處理系統

typescript
// 統一錯誤處理
export class ApiError extends Error {
  constructor(
    message: string,
    public statusCode: number,
    public code: string,
    public details?: any
  ) {
    super(message)
    this.name = 'ApiError'
  }
}

// 基礎類中的錯誤處理
protected handleError(error: unknown): never {
  if (error instanceof PostgrestError) {
    throw new ApiError(
      error.message,
      400,
      error.code || 'DATABASE_ERROR',
      error.details
    )
  }
  throw new ApiError('Unknown error occurred', 500, 'UNKNOWN_ERROR')
}

3. 服務層實現詳解

3.1 用戶管理服務群 (3個服務)

UserApiService (用戶基礎服務)

typescript
// src/api/services/UserApiService.ts
export class UserApiService extends BaseApiService<User> {
  constructor(supabase: SupabaseClient) {
    super(supabase, 'users')
  }

  // 用戶專用方法
  async findByEmail(email: string): Promise<User | null>
  async updateProfile(id: string, profile: UserProfile): Promise<User>
  async changePassword(id: string, newPassword: string): Promise<void>
  async getUserRoles(userId: string): Promise<Role[]>
  async assignRole(userId: string, roleId: string): Promise<void>
  async revokeRole(userId: string, roleId: string): Promise<void>
  
  // 用戶狀態管理
  async activateUser(id: string): Promise<void>
  async deactivateUser(id: string): Promise<void>
  async getUserActivity(userId: string): Promise<UserActivity[]>
}

RoleApiService (角色管理服務)

typescript
// src/api/services/RoleApiService.ts
export class RoleApiService extends BaseApiService<Role> {
  constructor(supabase: SupabaseClient) {
    super(supabase, 'roles')
  }

  // 角色權限管理
  async getRolePermissions(roleId: string): Promise<Permission[]>
  async assignPermission(roleId: string, permissionId: string): Promise<void>
  async revokePermission(roleId: string, permissionId: string): Promise<void>
  
  // 角色用戶管理
  async getRoleUsers(roleId: string): Promise<User[]>
  async getUsersWithRole(roleName: string): Promise<User[]>
  
  // 權限檢查
  async hasPermission(userId: string, permission: string): Promise<boolean>
  async getUserPermissions(userId: string): Promise<Permission[]>
}

3.2 業務核心服務群 (4個服務)

CustomerApiService (客戶管理服務)

typescript
// src/api/services/CustomerApiService.ts
export class CustomerApiService extends BaseApiService<Customer> {
  constructor(supabase: SupabaseClient) {
    super(supabase, 'customers')
  }

  // 客戶分析功能
  async getCustomerAnalytics(customerId: string): Promise<CustomerAnalytics>
  async getRFMAnalysis(customerId: string): Promise<RFMScore>
  async getCustomerLifecycle(customerId: string): Promise<LifecycleStage>
  
  // 客戶訂單關聯
  async getCustomerOrders(
    customerId: string, 
    options?: QueryOptions<Order>
  ): Promise<Order[]>
  
  // 客戶價值分析
  async getCustomerLTV(customerId: string): Promise<number>
  async getCustomerSegment(customerId: string): Promise<string>
  
  // 批量操作
  async bulkUpdateSegment(customerIds: string[], segment: string): Promise<void>
  async exportCustomerData(options?: ExportOptions): Promise<ExportResult>
}

ProductApiService (產品管理服務)

typescript
// src/api/services/ProductApiService.ts
export class ProductApiService extends BaseApiService<Product> {
  constructor(supabase: SupabaseClient) {
    super(supabase, 'products')
  }

  // 產品庫存管理
  async getProductStock(productId: string): Promise<InventoryStatus>
  async updateStock(productId: string, quantity: number): Promise<void>
  async reserveStock(productId: string, quantity: number): Promise<boolean>
  async releaseStock(productId: string, quantity: number): Promise<void>
  
  // 產品分類管理
  async getProductsByCategory(categoryId: string): Promise<Product[]>
  async updateProductCategory(productId: string, categoryId: string): Promise<void>
  
  // 產品性能分析
  async getProductPerformance(productId: string): Promise<ProductPerformance>
  async getTopSellingProducts(limit?: number): Promise<Product[]>
  async getLowStockProducts(threshold?: number): Promise<Product[]>
  
  // 批量操作
  async bulkUpdatePrices(updates: PriceUpdate[]): Promise<void>
  async bulkImportProducts(products: ProductImport[]): Promise<ImportResult>
}

OrderApiService (訂單管理服務)

typescript
// src/api/services/OrderApiService.ts
export class OrderApiService extends BaseApiService<Order> {
  constructor(supabase: SupabaseClient) {
    super(supabase, 'orders')
  }

  // 訂單狀態管理
  async updateOrderStatus(orderId: string, status: OrderStatus): Promise<Order>
  async cancelOrder(orderId: string, reason: string): Promise<void>
  async refundOrder(orderId: string, amount?: number): Promise<Refund>
  
  // 訂單項目管理
  async getOrderItems(orderId: string): Promise<OrderItem[]>
  async addOrderItem(orderId: string, item: OrderItemCreate): Promise<OrderItem>
  async updateOrderItem(itemId: string, updates: Partial<OrderItem>): Promise<OrderItem>
  async removeOrderItem(itemId: string): Promise<void>
  
  // 訂單分析
  async getOrderAnalytics(dateRange?: DateRange): Promise<OrderAnalytics>
  async getRevenueAnalytics(dateRange?: DateRange): Promise<RevenueAnalytics>
  async getOrderTrends(period: 'daily' | 'weekly' | 'monthly'): Promise<TrendData[]>
  
  // 實時訂單
  async getRecentOrders(limit?: number): Promise<Order[]>
  async subscribeToOrderUpdates(callback: (order: Order) => void): Promise<() => void>
}

3.3 客服系統服務群 (2個服務)

ConversationApiService (對話管理服務)

typescript
// src/api/services/ConversationApiService.ts
export class ConversationApiService extends BaseApiService<Conversation> {
  constructor(supabase: SupabaseClient) {
    super(supabase, 'conversations')
  }

  // 對話消息管理
  async getMessages(conversationId: string): Promise<Message[]>
  async sendMessage(conversationId: string, message: MessageCreate): Promise<Message>
  async markMessageAsRead(messageId: string): Promise<void>
  async markConversationAsRead(conversationId: string): Promise<void>
  
  // 對話狀態管理
  async assignAgent(conversationId: string, agentId: string): Promise<void>
  async closeConversation(conversationId: string): Promise<void>
  async reopenConversation(conversationId: string): Promise<void>
  
  // 對話分析
  async getConversationAnalytics(dateRange?: DateRange): Promise<ConversationAnalytics>
  async getResponseTimeMetrics(): Promise<ResponseTimeMetrics>
  async getAgentPerformance(agentId: string): Promise<AgentPerformance>
  
  // 實時功能
  async subscribeToConversation(
    conversationId: string, 
    callback: (message: Message) => void
  ): Promise<() => void>
}

AgentApiService (客服代理服務)

typescript
// src/api/services/AgentApiService.ts
export class AgentApiService extends BaseApiService<Agent> {
  constructor(supabase: SupabaseClient) {
    super(supabase, 'agents')
  }

  // 代理狀態管理
  async setAgentStatus(agentId: string, status: AgentStatus): Promise<void>
  async getAgentWorkload(agentId: string): Promise<AgentWorkload>
  async assignConversation(agentId: string, conversationId: string): Promise<void>
  
  // 代理性能
  async getAgentMetrics(agentId: string, dateRange?: DateRange): Promise<AgentMetrics>
  async getAgentActivity(agentId: string): Promise<AgentActivity[]>
  async updateAgentCapacity(agentId: string, capacity: number): Promise<void>
  
  // 代理排班
  async getAgentSchedule(agentId: string): Promise<AgentSchedule[]>
  async updateAgentSchedule(agentId: string, schedule: ScheduleUpdate[]): Promise<void>
  
  // 團隊管理
  async getAvailableAgents(): Promise<Agent[]>
  async getAgentTeam(teamId: string): Promise<Agent[]>
}

3.4 通知系統服務群 (3個服務) - 最複雜

NotificationApiService (核心通知服務)

typescript
// src/api/services/NotificationApiService.ts
export class NotificationApiService extends BaseApiService<Notification> {
  constructor(supabase: SupabaseClient) {
    super(supabase, 'notifications')
  }

  // 通知發送
  async sendNotification(notification: NotificationCreate): Promise<Notification>
  async sendBulkNotification(notifications: NotificationCreate[]): Promise<Notification[]>
  async sendToUser(userId: string, notification: NotificationCreate): Promise<Notification>
  async sendToRole(roleId: string, notification: NotificationCreate): Promise<Notification[]>
  
  // 通知狀態管理
  async markAsRead(notificationId: string): Promise<void>
  async markAllAsRead(userId: string): Promise<void>
  async dismissNotification(notificationId: string): Promise<void>
  
  // 通知查詢
  async getUserNotifications(userId: string, options?: QueryOptions<Notification>): Promise<Notification[]>
  async getUnreadCount(userId: string): Promise<number>
  async getNotificationsByType(type: NotificationType): Promise<Notification[]>
  
  // 通知設置
  async getUserPreferences(userId: string): Promise<NotificationPreference>
  async updateUserPreferences(userId: string, preferences: Partial<NotificationPreference>): Promise<void>
  
  // 實時通知
  async subscribeToUserNotifications(
    userId: string, 
    callback: (notification: Notification) => void
  ): Promise<() => void>
}

GroupNotificationApiService (群組通知服務)

typescript
// src/api/services/GroupNotificationApiService.ts
export class GroupNotificationApiService extends BaseApiService<GroupNotification> {
  constructor(supabase: SupabaseClient) {
    super(supabase, 'group_notifications')
  }

  // 群組通知發送
  async sendToGroup(groupId: string, notification: NotificationCreate): Promise<GroupNotification>
  async sendToMultipleGroups(groupIds: string[], notification: NotificationCreate): Promise<GroupNotification[]>
  async sendToDepartment(department: string, notification: NotificationCreate): Promise<GroupNotification>
  
  // 群組管理
  async createNotificationGroup(group: NotificationGroupCreate): Promise<NotificationGroup>
  async addUserToGroup(groupId: string, userId: string): Promise<void>
  async removeUserFromGroup(groupId: string, userId: string): Promise<void>
  async getGroupMembers(groupId: string): Promise<User[]>
  
  // 群組通知統計
  async getGroupNotificationStats(groupId: string): Promise<GroupNotificationStats>
  async getDeliveryReport(notificationId: string): Promise<DeliveryReport>
  
  // 批量操作
  async bulkUpdateGroupMembership(updates: GroupMembershipUpdate[]): Promise<void>
}

MonitoredNotificationService (通知監控服務)

typescript
// src/api/services/MonitoredNotificationService.ts
export class MonitoredNotificationService {
  constructor(private supabase: SupabaseClient) {}

  // 通知監控
  async getDeliveryMetrics(dateRange?: DateRange): Promise<DeliveryMetrics>
  async getFailureReport(): Promise<NotificationFailure[]>
  async getPerformanceMetrics(): Promise<NotificationPerformance>
  
  // 通知健康檢查
  async healthCheck(): Promise<HealthCheckResult>
  async validateNotificationRules(): Promise<ValidationResult[]>
  async getSystemStatus(): Promise<NotificationSystemStatus>
  
  // 通知分析
  async getEngagementAnalytics(): Promise<EngagementAnalytics>
  async getNotificationTrends(): Promise<NotificationTrend[]>
  async getUserEngagementScore(userId: string): Promise<number>
  
  // 自動化規則
  async createAutomationRule(rule: AutomationRuleCreate): Promise<AutomationRule>
  async updateAutomationRule(ruleId: string, updates: Partial<AutomationRule>): Promise<AutomationRule>
  async executeRule(ruleId: string, context: any): Promise<RuleExecutionResult>
}

4. 服務工廠模式實現

4.1 ServiceFactory 設計

typescript
// src/api/services/ServiceFactory.ts
export class ServiceFactory {
  private services: Map<string, any> = new Map()
  private supabase: SupabaseClient

  constructor(supabase: SupabaseClient) {
    this.supabase = supabase
  }

  // 泛型服務獲取方法
  private getService<T>(key: string, ServiceClass: new (supabase: SupabaseClient) => T): T {
    if (!this.services.has(key)) {
      this.services.set(key, new ServiceClass(this.supabase))
    }
    return this.services.get(key) as T
  }

  // 具體服務獲取方法
  getUserService(): UserApiService {
    return this.getService('user', UserApiService)
  }

  getProductService(): ProductApiService {
    return this.getService('product', ProductApiService)
  }

  getOrderService(): OrderApiService {
    return this.getService('order', OrderApiService)
  }

  getCustomerService(): CustomerApiService {
    return this.getService('customer', CustomerApiService)
  }

  getNotificationService(): NotificationApiService {
    return this.getService('notification', NotificationApiService)
  }

  getCampaignAnalyticsService(): CampaignAnalyticsApiService {
    return this.getService('campaignAnalytics', CampaignAnalyticsApiService)
  }

  getAIPromptTemplateService(): AIPromptTemplateService {
    return this.getService('aiPromptTemplate', AIPromptTemplateService)
  }

  getAIEnhancedAlertService(): AIEnhancedAlertService {
    return this.getService('aiEnhancedAlert', AIEnhancedAlertService)
  }

  // ... 其他服務獲取方法

  // 批量初始化
  initializeAllServices(): void {
    this.getUserService()
    this.getProductService()
    this.getOrderService()
    // ... 初始化所有服務
  }

  // 清理資源
  dispose(): void {
    this.services.clear()
  }
}

4.2 服務實例管理

typescript
// src/api/services/index.ts

// 預設服務工廠實例
import { supabase } from '@/lib/supabase'
import { ServiceFactory } from './ServiceFactory'

export const defaultServiceFactory = new ServiceFactory(supabase)

// 快捷方法取得服務實例
export const getUserService = () => defaultServiceFactory.getUserService()
export const getProductService = () => defaultServiceFactory.getProductService()
export const getOrderService = () => defaultServiceFactory.getOrderService()
export const getCustomerService = () => defaultServiceFactory.getCustomerService()

// 通知服務快捷方法
export const getNotificationService = () => new NotificationService(supabase)
export const getNotificationApiService = () => new NotificationApiService(supabase)
export const getGroupNotificationApiService = () => new GroupNotificationApiService(supabase)
export const getMonitoredNotificationService = () => new MonitoredNotificationService(supabase)

// 客服服務快捷方法
export const getConversationService = () => defaultServiceFactory.getConversationService()
export const getAgentService = () => defaultServiceFactory.getAgentService()

// 角色權限服務快捷方法
export const getRoleService = () => defaultServiceFactory.getRoleService()

// Campaign 和 AI 服務快捷方法
export const getCampaignAnalyticsService = () => defaultServiceFactory.getCampaignAnalyticsService()
export const getAIPromptTemplateService = () => defaultServiceFactory.getAIPromptTemplateService()
export const getAIEnhancedAlertService = () => defaultServiceFactory.getAIEnhancedAlertService()

5. 服務使用模式

5.1 在 Composables 中使用

typescript
// src/composables/useCustomer.ts
export function useCustomer() {
  const customerService = getCustomerService()
  
  const customers = ref<Customer[]>([])
  const loading = ref(false)
  const error = ref<string | null>(null)

  const fetchCustomers = async (options?: QueryOptions<Customer>) => {
    loading.value = true
    error.value = null
    
    try {
      customers.value = await customerService.findAll(options)
    } catch (err) {
      error.value = err instanceof Error ? err.message : 'Unknown error'
    } finally {
      loading.value = false
    }
  }

  const createCustomer = async (customerData: Partial<Customer>) => {
    try {
      const newCustomer = await customerService.create(customerData)
      customers.value.push(newCustomer)
      return newCustomer
    } catch (err) {
      error.value = err instanceof Error ? err.message : 'Create failed'
      throw err
    }
  }

  return {
    customers: readonly(customers),
    loading: readonly(loading),
    error: readonly(error),
    fetchCustomers,
    createCustomer
  }
}

5.2 在組件中使用

vue
<template>
  <div>
    <CustomerList 
      :customers="customers"
      :loading="loading"
      @create="handleCreate"
      @update="handleUpdate"
    />
  </div>
</template>

<script setup lang="ts">
import { useCustomer } from '@/composables/useCustomer'

const {
  customers,
  loading,
  error,
  fetchCustomers,
  createCustomer
} = useCustomer()

onMounted(() => {
  fetchCustomers()
})

const handleCreate = async (customerData: Partial<Customer>) => {
  await createCustomer(customerData)
}

const handleUpdate = async (id: string, updates: Partial<Customer>) => {
  // 使用服務進行更新操作
}
</script>

5.3 與 Vue Query 整合

typescript
// src/composables/queries/useCustomerQueries.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/vue-query'
import { getCustomerService } from '@/api/services'

export function useCustomerQueries() {
  const customerService = getCustomerService()
  const queryClient = useQueryClient()

  // 查詢所有客戶
  const useCustomersQuery = (options?: QueryOptions<Customer>) => {
    return useQuery({
      queryKey: ['customers', options],
      queryFn: () => customerService.findAll(options),
      staleTime: 5 * 60 * 1000, // 5分鐘
    })
  }

  // 創建客戶 Mutation
  const useCreateCustomerMutation = () => {
    return useMutation({
      mutationFn: (data: Partial<Customer>) => customerService.create(data),
      onSuccess: () => {
        queryClient.invalidateQueries({ queryKey: ['customers'] })
      },
    })
  }

  // 更新客戶 Mutation
  const useUpdateCustomerMutation = () => {
    return useMutation({
      mutationFn: ({ id, data }: { id: string; data: Partial<Customer> }) =>
        customerService.update(id, data),
      onSuccess: (updatedCustomer) => {
        queryClient.setQueryData(['customer', updatedCustomer.id], updatedCustomer)
        queryClient.invalidateQueries({ queryKey: ['customers'] })
      },
    })
  }

  return {
    useCustomersQuery,
    useCreateCustomerMutation,
    useUpdateCustomerMutation,
  }
}

6. 錯誤處理與調試

6.1 統一錯誤處理

typescript
// src/utils/error-handler.ts
export class ApiErrorHandler {
  static handle(error: unknown): ApiError {
    if (error instanceof ApiError) {
      return error
    }
    
    if (error instanceof PostgrestError) {
      return new ApiError(
        error.message,
        400,
        error.code || 'DATABASE_ERROR',
        error.details
      )
    }
    
    if (error instanceof AuthError) {
      return new ApiError(
        'Authentication failed',
        401,
        'AUTH_ERROR',
        error.message
      )
    }
    
    // 未知錯誤
    return new ApiError(
      'An unexpected error occurred',
      500,
      'UNKNOWN_ERROR',
      error
    )
  }

  static async logError(error: ApiError, context: string): Promise<void> {
    console.error(`[${context}] ${error.code}: ${error.message}`, error.details)
    
    // 可以在這裡添加錯誤報告服務
    // await sendErrorReport(error, context)
  }
}

6.2 調試和監控

typescript
// src/utils/api-logger.ts
export class ApiLogger {
  static logRequest(service: string, method: string, params?: any): void {
    if (process.env.NODE_ENV === 'development') {
      console.log(`[API] ${service}.${method}`, params)
    }
  }

  static logResponse(service: string, method: string, response: any, duration: number): void {
    if (process.env.NODE_ENV === 'development') {
      console.log(`[API] ${service}.${method} completed in ${duration}ms`, response)
    }
  }

  static logError(service: string, method: string, error: Error): void {
    console.error(`[API] ${service}.${method} failed:`, error)
  }
}

// 在 BaseApiService 中使用
protected async executeWithLogging<T>(
  method: string,
  operation: () => Promise<T>
): Promise<T> {
  const start = Date.now()
  ApiLogger.logRequest(this.constructor.name, method)
  
  try {
    const result = await operation()
    ApiLogger.logResponse(this.constructor.name, method, result, Date.now() - start)
    return result
  } catch (error) {
    ApiLogger.logError(this.constructor.name, method, error as Error)
    throw this.handleError(error)
  }
}

7. 性能優化策略

7.1 查詢優化

typescript
// 查詢優化示例
export class OptimizedCustomerService extends CustomerApiService {
  // 使用索引優化的查詢
  async findByEmailOptimized(email: string): Promise<Customer | null> {
    return this.executeWithLogging('findByEmailOptimized', async () => {
      const { data, error } = await this.supabase
        .from(this.tableName)
        .select('*')
        .eq('email', email)
        .single()
      
      if (error && error.code !== 'PGRST116') throw error
      return data
    })
  }

  // 批量查詢優化
  async findByIdsOptimized(ids: string[]): Promise<Customer[]> {
    if (ids.length === 0) return []
    
    return this.executeWithLogging('findByIdsOptimized', async () => {
      const { data, error } = await this.supabase
        .from(this.tableName)
        .select('*')
        .in('id', ids)
      
      if (error) throw error
      return data || []
    })
  }

  // 分頁查詢優化
  async findWithOptimizedPagination(
    options: QueryOptions<Customer> & { pageSize: number; page: number }
  ): Promise<{ data: Customer[]; pagination: PaginationInfo }> {
    const { pageSize, page } = options
    const from = (page - 1) * pageSize
    const to = from + pageSize - 1

    return this.executeWithLogging('findWithOptimizedPagination', async () => {
      // 並行執行數據查詢和計數查詢
      const [dataResult, countResult] = await Promise.all([
        this.supabase
          .from(this.tableName)
          .select('*')
          .range(from, to),
        this.supabase
          .from(this.tableName)
          .select('*', { count: 'exact', head: true })
      ])

      if (dataResult.error) throw dataResult.error
      if (countResult.error) throw countResult.error

      const total = countResult.count || 0
      const totalPages = Math.ceil(total / pageSize)

      return {
        data: dataResult.data || [],
        pagination: {
          page,
          pageSize,
          total,
          totalPages,
          hasNext: page < totalPages,
          hasPrev: page > 1,
        },
      }
    })
  }
}

7.2 緩存策略

typescript
// src/utils/api-cache.ts
export class ApiCache {
  private cache: Map<string, { data: any; expiry: number }> = new Map()
  private defaultTTL = 5 * 60 * 1000 // 5分鐘

  set(key: string, data: any, ttl?: number): void {
    const expiry = Date.now() + (ttl || this.defaultTTL)
    this.cache.set(key, { data, expiry })
  }

  get<T>(key: string): T | null {
    const item = this.cache.get(key)
    if (!item) return null
    
    if (Date.now() > item.expiry) {
      this.cache.delete(key)
      return null
    }
    
    return item.data as T
  }

  clear(): void {
    this.cache.clear()
  }

  generateKey(service: string, method: string, params?: any): string {
    return `${service}:${method}:${JSON.stringify(params || {})}`
  }
}

// 在服務中使用緩存
export class CachedCustomerService extends CustomerApiService {
  private cache = new ApiCache()

  async findAllCached(options?: QueryOptions<Customer>): Promise<Customer[]> {
    const cacheKey = this.cache.generateKey('CustomerService', 'findAll', options)
    const cached = this.cache.get<Customer[]>(cacheKey)
    
    if (cached) {
      return cached
    }
    
    const result = await this.findAll(options)
    this.cache.set(cacheKey, result, 2 * 60 * 1000) // 2分鐘緩存
    return result
  }
}

8. 測試策略

8.1 單元測試

typescript
// src/api/services/__tests__/CustomerApiService.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { CustomerApiService } from '../CustomerApiService'
import { mockSupabaseClient } from '../__tests__/test-mocks'

describe('CustomerApiService', () => {
  let service: CustomerApiService
  let mockSupabase: any

  beforeEach(() => {
    mockSupabase = mockSupabaseClient()
    service = new CustomerApiService(mockSupabase)
  })

  describe('findAll', () => {
    it('should return all customers', async () => {
      const mockCustomers = [
        { id: '1', name: 'Customer 1', email: 'test1@example.com' },
        { id: '2', name: 'Customer 2', email: 'test2@example.com' }
      ]

      mockSupabase.from.mockReturnValue({
        select: vi.fn().mockReturnValue({
          data: mockCustomers,
          error: null
        })
      })

      const result = await service.findAll()

      expect(result).toEqual(mockCustomers)
      expect(mockSupabase.from).toHaveBeenCalledWith('customers')
    })

    it('should handle errors properly', async () => {
      const mockError = new Error('Database error')
      mockSupabase.from.mockReturnValue({
        select: vi.fn().mockReturnValue({
          data: null,
          error: mockError
        })
      })

      await expect(service.findAll()).rejects.toThrow('Database error')
    })
  })

  describe('create', () => {
    it('should create a new customer', async () => {
      const newCustomer = { name: 'New Customer', email: 'new@example.com' }
      const createdCustomer = { id: '3', ...newCustomer }

      mockSupabase.from.mockReturnValue({
        insert: vi.fn().mockReturnValue({
          select: vi.fn().mockReturnValue({
            single: vi.fn().mockReturnValue({
              data: createdCustomer,
              error: null
            })
          })
        })
      })

      const result = await service.create(newCustomer)

      expect(result).toEqual(createdCustomer)
    })
  })
})

8.2 整合測試

typescript
// src/api/services/__tests__/integration/CustomerService.integration.test.ts
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import { createClient } from '@supabase/supabase-js'
import { CustomerApiService } from '../../CustomerApiService'

describe('CustomerApiService Integration Tests', () => {
  let service: CustomerApiService
  let testCustomerId: string

  beforeAll(async () => {
    const supabase = createClient(
      process.env.VITE_SUPABASE_URL!,
      process.env.VITE_SUPABASE_ANON_KEY!
    )
    service = new CustomerApiService(supabase)
  })

  afterAll(async () => {
    // 清理測試數據
    if (testCustomerId) {
      await service.delete(testCustomerId)
    }
  })

  it('should perform CRUD operations successfully', async () => {
    // Create
    const newCustomer = {
      name: 'Test Customer',
      email: 'test@integration.com',
      phone: '1234567890'
    }

    const created = await service.create(newCustomer)
    testCustomerId = created.id

    expect(created).toMatchObject(newCustomer)
    expect(created.id).toBeDefined()

    // Read
    const found = await service.findById(created.id)
    expect(found).toMatchObject(newCustomer)

    // Update
    const updates = { name: 'Updated Customer' }
    const updated = await service.update(created.id, updates)
    expect(updated.name).toBe('Updated Customer')

    // Delete
    await service.delete(created.id)
    const deleted = await service.findById(created.id)
    expect(deleted).toBeNull()
  })
})

9. 部署與監控

9.1 環境配置

typescript
// src/config/api.ts
export const apiConfig = {
  development: {
    logLevel: 'debug',
    enableCache: false,
    requestTimeout: 10000,
    retryAttempts: 3,
  },
  staging: {
    logLevel: 'info',
    enableCache: true,
    requestTimeout: 8000,
    retryAttempts: 2,
  },
  production: {
    logLevel: 'error',
    enableCache: true,
    requestTimeout: 5000,
    retryAttempts: 1,
  }
}

export const getCurrentConfig = () => {
  return apiConfig[process.env.NODE_ENV as keyof typeof apiConfig] || apiConfig.development
}

9.2 性能監控

typescript
// src/utils/performance-monitor.ts
export class PerformanceMonitor {
  private metrics: Map<string, number[]> = new Map()

  recordApiCall(service: string, method: string, duration: number): void {
    const key = `${service}.${method}`
    if (!this.metrics.has(key)) {
      this.metrics.set(key, [])
    }
    this.metrics.get(key)!.push(duration)
  }

  getMetrics(service?: string): Record<string, {
    count: number
    avgDuration: number
    minDuration: number
    maxDuration: number
  }> {
    const result: Record<string, any> = {}

    for (const [key, durations] of this.metrics.entries()) {
      if (service && !key.startsWith(service)) continue

      result[key] = {
        count: durations.length,
        avgDuration: durations.reduce((a, b) => a + b, 0) / durations.length,
        minDuration: Math.min(...durations),
        maxDuration: Math.max(...durations),
      }
    }

    return result
  }

  reset(): void {
    this.metrics.clear()
  }
}

10. 最佳實踐與規範

10.1 代碼規範

typescript
// ✅ 良好的服務方法設計
export class ProductApiService extends BaseApiService<Product> {
  // 1. 方法命名清晰明確
  async findProductsByCategoryId(categoryId: string): Promise<Product[]> {
    // 2. 參數驗證
    if (!categoryId) {
      throw new ApiError('Category ID is required', 400, 'INVALID_PARAMS')
    }

    // 3. 使用統一的錯誤處理
    return this.executeWithLogging('findProductsByCategoryId', async () => {
      const { data, error } = await this.supabase
        .from(this.tableName)
        .select('*')
        .eq('category_id', categoryId)
        .order('name', { ascending: true })

      if (error) throw error
      return data || []
    })
  }

  // 4. 複雜操作拆分為多個小方法
  async createProductWithInventory(
    productData: Partial<Product>,
    initialStock: number
  ): Promise<{ product: Product; inventory: Inventory }> {
    // 使用數據庫事務
    const { data, error } = await this.supabase.rpc('create_product_with_inventory', {
      product_data: productData,
      initial_stock: initialStock
    })

    if (error) throw this.handleError(error)
    return data
  }
}

10.2 性能最佳實踐

typescript
// ✅ 性能優化技巧
export class OptimizedOrderService extends OrderApiService {
  // 1. 使用適當的查詢選擇
  async getOrderSummary(orderId: string): Promise<OrderSummary> {
    return this.executeWithLogging('getOrderSummary', async () => {
      // 只選擇需要的欄位
      const { data, error } = await this.supabase
        .from('orders')
        .select(`
          id,
          total_amount,
          status,
          created_at,
          customer:customers(name, email),
          items:order_items(quantity, price, product:products(name))
        `)
        .eq('id', orderId)
        .single()

      if (error) throw error
      return this.transformToOrderSummary(data)
    })
  }

  // 2. 批量操作優化
  async bulkUpdateOrderStatus(
    orderIds: string[],
    status: OrderStatus
  ): Promise<void> {
    if (orderIds.length === 0) return

    return this.executeWithLogging('bulkUpdateOrderStatus', async () => {
      const { error } = await this.supabase
        .from('orders')
        .update({ status, updated_at: new Date().toISOString() })
        .in('id', orderIds)

      if (error) throw error
    })
  }

  // 3. 分頁查詢優化
  async getOrdersWithOptimizedPagination(options: {
    page: number
    pageSize: number
    status?: OrderStatus
    customerId?: string
  }): Promise<{ orders: Order[]; pagination: PaginationInfo }> {
    const { page, pageSize, status, customerId } = options
    const from = (page - 1) * pageSize
    const to = from + pageSize - 1

    let query = this.supabase.from('orders').select('*', { count: 'exact' })
    
    if (status) query = query.eq('status', status)
    if (customerId) query = query.eq('customer_id', customerId)

    const { data, error, count } = await query.range(from, to)

    if (error) throw error

    return {
      orders: data || [],
      pagination: {
        page,
        pageSize,
        total: count || 0,
        totalPages: Math.ceil((count || 0) / pageSize),
        hasNext: page * pageSize < (count || 0),
        hasPrev: page > 1,
      },
    }
  }
}

10.3 安全性最佳實踐

typescript
// ✅ 安全性考慮
export class SecureUserService extends UserApiService {
  // 1. 輸入驗證和清理
  async createUser(userData: Partial<User>): Promise<User> {
    // 驗證必要欄位
    this.validateUserData(userData)
    
    // 清理和格式化數據
    const sanitizedData = this.sanitizeUserData(userData)
    
    return super.create(sanitizedData)
  }

  private validateUserData(userData: Partial<User>): void {
    if (!userData.email || !this.isValidEmail(userData.email)) {
      throw new ApiError('Valid email is required', 400, 'INVALID_EMAIL')
    }
    
    if (userData.phone && !this.isValidPhone(userData.phone)) {
      throw new ApiError('Valid phone number is required', 400, 'INVALID_PHONE')
    }
  }

  private sanitizeUserData(userData: Partial<User>): Partial<User> {
    return {
      ...userData,
      email: userData.email?.toLowerCase().trim(),
      name: userData.name?.trim(),
      // 移除不安全的 HTML
      bio: userData.bio ? this.stripHtml(userData.bio) : undefined,
    }
  }

  // 2. 敏感操作需要額外驗證
  async deleteUser(userId: string, currentUserId: string): Promise<void> {
    // 檢查權限
    if (userId === currentUserId) {
      throw new ApiError('Cannot delete your own account', 403, 'SELF_DELETE_FORBIDDEN')
    }

    // 檢查用戶是否存在
    const user = await this.findById(userId)
    if (!user) {
      throw new ApiError('User not found', 404, 'USER_NOT_FOUND')
    }

    // 軟刪除而不是硬刪除
    await this.update(userId, {
      deleted_at: new Date().toISOString(),
      email: `deleted_${Date.now()}_${user.email}`, // 釋放 email 唯一約束
    })
  }
}

相關文檔

版本歷史

  • v1.0.0 (2025-07-22): 初始版本,完整的 API 服務架構文檔