Skip to content

客戶行為模擬文檔

概述

客戶行為模擬是 front-stage-vue 測試系統的核心功能之一,旨在創建真實的客戶互動場景,幫助驗證後台管理系統的各項功能和業務邏輯。

模擬場景設計

客戶生命週期模擬

1. 新客戶註冊場景

typescript
interface NewCustomerScenario {
  registrationMethod: 'email' | 'phone' | 'social'
  onboardingPath: 'guided' | 'self-exploration'
  firstPurchaseTimeframe: 'immediate' | 'within-7-days' | 'within-30-days' | 'no-purchase'
}

class NewCustomerSimulator {
  static async simulateRegistration(): Promise<Customer> {
    const customer = generateCustomer()
    
    // 模擬註冊流程
    console.log(`🆕 新客戶註冊: ${customer.name}`)
    
    // 1. 註冊
    await CustomerApiService.register(customer)
    
    // 2. 電子郵件驗證 (模擬)
    await this.simulateEmailVerification(customer.id)
    
    // 3. 新手導覽 (30% 機率完成)
    if (faker.datatype.boolean(0.3)) {
      await this.simulateOnboarding(customer.id)
    }
    
    // 4. 首次瀏覽行為
    await this.simulateFirstVisitBehavior(customer.id)
    
    return customer
  }
  
  private static async simulateOnboarding(customerId: string) {
    const onboardingSteps = [
      'profile-completion',
      'preference-setting', 
      'newsletter-subscription',
      'first-product-view'
    ]
    
    for (const step of onboardingSteps) {
      await new Promise(resolve => setTimeout(resolve, 1000)) // 模擬用戶思考時間
      console.log(`📝 完成新手導覽步驟: ${step}`)
    }
  }
}

2. 回頭客行為模擬

typescript
class ReturningCustomerSimulator {
  static async simulateReturnVisit(customerId: string) {
    const customer = await CustomerApiService.getById(customerId)
    const visitType = faker.helpers.weightedArrayElement([
      { weight: 40, value: 'browse' },           // 純瀏覽
      { weight: 35, value: 'specific-purchase' }, // 目標性購買
      { weight: 15, value: 'comparison' },       // 比較商品
      { weight: 10, value: 'customer-service' }  // 客服諮詢
    ])
    
    console.log(`🔄 回頭客訪問: ${customer.name}, 類型: ${visitType}`)
    
    switch (visitType) {
      case 'browse':
        await this.simulateBrowsingBehavior(customerId)
        break
      case 'specific-purchase':
        await this.simulateTargetedPurchase(customerId)
        break
      case 'comparison':
        await this.simulateComparisonShopping(customerId)
        break
      case 'customer-service':
        await this.simulateCustomerService(customerId)
        break
    }
  }
  
  private static async simulateBrowsingBehavior(customerId: string) {
    const browsingSession = {
      duration: faker.number.int({ min: 300, max: 1800 }), // 5-30分鐘
      pagesViewed: faker.number.int({ min: 3, max: 15 }),
      productsViewed: faker.number.int({ min: 2, max: 10 }),
      addToCart: faker.datatype.boolean(0.15), // 15% 機率加入購物車
      purchase: faker.datatype.boolean(0.05)   // 5% 機率購買
    }
    
    // 模擬頁面瀏覽
    for (let i = 0; i < browsingSession.pagesViewed; i++) {
      const pageType = faker.helpers.arrayElement([
        'product-list', 'product-detail', 'category', 'search-results'
      ])
      await this.simulatePageView(customerId, pageType)
      await new Promise(resolve => setTimeout(resolve, 2000)) // 模擬停留時間
    }
    
    if (browsingSession.addToCart) {
      await this.simulateAddToCart(customerId)
    }
  }
}

購物行為模擬

3. 購買決策流程

typescript
class PurchaseDecisionSimulator {
  static async simulatePurchaseJourney(customerId: string) {
    const journey = {
      trigger: faker.helpers.arrayElement([
        'need-based',      // 需求驅動
        'impulse',         // 衝動購買
        'promotion',       // 促銷活動
        'recommendation'   // 推薦商品
      ]),
      researchIntensity: faker.helpers.arrayElement(['low', 'medium', 'high']),
      decisionTime: faker.number.int({ min: 1, max: 2880 }) // 1分鐘到2天
    }
    
    console.log(`🛒 購買決策開始: 觸發因素=${journey.trigger}, 研究強度=${journey.researchIntensity}`)
    
    // 1. 商品發現階段
    await this.simulateProductDiscovery(customerId, journey.trigger)
    
    // 2. 研究比較階段
    if (journey.researchIntensity !== 'low') {
      await this.simulateResearchPhase(customerId, journey.researchIntensity)
    }
    
    // 3. 決策階段
    const purchaseDecision = await this.simulatePurchaseDecision(customerId, journey)
    
    if (purchaseDecision.willPurchase) {
      await this.simulateCheckoutProcess(customerId, purchaseDecision.items)
    }
    
    return journey
  }
  
  private static async simulateProductDiscovery(customerId: string, trigger: string) {
    const discoveryMethods = {
      'need-based': ['search', 'category-browse'],
      'impulse': ['homepage-banner', 'product-recommendation'],
      'promotion': ['email-campaign', 'promotion-page'],
      'recommendation': ['related-products', 'ai-recommendation']
    }
    
    const method = faker.helpers.arrayElement(discoveryMethods[trigger])
    console.log(`🔍 商品發現: ${method}`)
    
    // 模擬不同發現方式的行為
    switch (method) {
      case 'search':
        await this.simulateSearchBehavior(customerId)
        break
      case 'category-browse':
        await this.simulateCategoryBrowsing(customerId)
        break
      // ... 其他方法
    }
  }
  
  private static async simulateCheckoutProcess(customerId: string, items: CartItem[]) {
    const checkoutFlow = {
      steps: [
        'cart-review',
        'shipping-info',
        'payment-method',
        'order-confirmation'
      ],
      abandonmentPoints: ['shipping-cost', 'payment-form', 'account-creation']
    }
    
    let currentStep = 0
    
    for (const step of checkoutFlow.steps) {
      console.log(`📋 結帳步驟: ${step}`)
      
      // 模擬每個步驟的放棄率
      const abandonmentRate = this.getAbandonmentRate(step)
      if (faker.datatype.boolean(abandonmentRate)) {
        console.log(`❌ 在 ${step} 步驟放棄結帳`)
        await this.trackAbandonmentEvent(customerId, step)
        return null
      }
      
      await new Promise(resolve => setTimeout(resolve, 3000)) // 模擬填寫時間
      currentStep++
    }
    
    // 完成購買
    const order = await this.completePurchase(customerId, items)
    console.log(`✅ 購買完成: 訂單 ${order.orderNumber}`)
    return order
  }
}

客戶服務互動模擬

4. 客服對話場景

typescript
class CustomerServiceSimulator {
  static async simulateCustomerServiceInteraction(customerId: string) {
    const inquiryTypes = [
      'product-question',    // 商品問題
      'order-status',        // 訂單狀態
      'return-request',      // 退貨申請
      'technical-issue',     // 技術問題
      'billing-inquiry',     // 帳單問題
      'general-feedback'     // 一般回饋
    ]
    
    const inquiryType = faker.helpers.arrayElement(inquiryTypes)
    const urgency = faker.helpers.weightedArrayElement([
      { weight: 60, value: 'low' },
      { weight: 30, value: 'medium' },
      { weight: 8, value: 'high' },
      { weight: 2, value: 'urgent' }
    ])
    
    console.log(`💬 客服諮詢: ${inquiryType}, 緊急度: ${urgency}`)
    
    // 創建對話
    const conversation = await ConversationApiService.create({
      customerId,
      subject: this.getSubjectByType(inquiryType),
      priority: urgency,
      channel: faker.helpers.arrayElement(['chat', 'email', 'phone'])
    })
    
    // 模擬對話流程
    await this.simulateConversationFlow(conversation.id, inquiryType)
    
    return conversation
  }
  
  private static async simulateConversationFlow(conversationId: string, inquiryType: string) {
    const flowTemplates = {
      'product-question': [
        { sender: 'customer', template: 'product-inquiry' },
        { sender: 'agent', template: 'product-information' },
        { sender: 'customer', template: 'follow-up-question' },
        { sender: 'agent', template: 'detailed-explanation' },
        { sender: 'customer', template: 'satisfaction-confirmation' }
      ],
      'return-request': [
        { sender: 'customer', template: 'return-request' },
        { sender: 'agent', template: 'return-policy-explanation' },
        { sender: 'customer', template: 'reason-explanation' },
        { sender: 'agent', template: 'return-approval' },
        { sender: 'customer', template: 'gratitude' }
      ]
      // ... 其他對話流程
    }
    
    const flow = flowTemplates[inquiryType] || flowTemplates['product-question']
    
    for (let i = 0; i < flow.length; i++) {
      const messageData = flow[i]
      const content = this.generateMessageByTemplate(messageData.template)
      
      await ConversationApiService.addMessage(conversationId, {
        sender: messageData.sender,
        content,
        timestamp: new Date()
      })
      
      // 模擬回應時間
      const responseTime = messageData.sender === 'agent' ? 
        faker.number.int({ min: 30, max: 300 }) :    // 客服 30秒-5分鐘
        faker.number.int({ min: 60, max: 1800 })     // 客戶 1分鐘-30分鐘
      
      await new Promise(resolve => setTimeout(resolve, responseTime * 1000))
    }
    
    // 結束對話
    await ConversationApiService.updateStatus(conversationId, 'resolved')
  }
}

行為分析模擬

客戶細分模擬

typescript
class CustomerSegmentationSimulator {
  static generateCustomerPersonas() {
    return [
      {
        name: 'VIP大客戶',
        characteristics: {
          monthlySpending: { min: 10000, max: 50000 },
          purchaseFrequency: { min: 8, max: 15 },
          productCategories: ['3C電子', '精品配件'],
          loyaltyScore: { min: 80, max: 100 },
          serviceExpectation: 'premium'
        },
        behaviors: {
          browsingPattern: 'goal-oriented',
          decisionSpeed: 'fast',
          pricesensitivity: 'low',
          channelPreference: ['website', 'phone']
        }
      },
      {
        name: '價格敏感型客戶',
        characteristics: {
          monthlySpending: { min: 500, max: 2000 },
          purchaseFrequency: { min: 1, max: 3 },
          productCategories: ['家居用品', '食品飲料'],
          loyaltyScore: { min: 30, max: 60 },
          serviceExpectation: 'standard'
        },
        behaviors: {
          browsingPattern: 'comparison-heavy',
          decisionSpeed: 'slow',
          pricesensitivity: 'high',
          channelPreference: ['website', 'mobile-app']
        }
      }
      // ... 更多客戶人設
    ]
  }
  
  static async simulatePersonaBehavior(persona: CustomerPersona, customerId: string) {
    const customer = await CustomerApiService.getById(customerId)
    
    // 根據人設調整行為模式
    const behaviorModifiers = {
      browsingDuration: persona.behaviors.browsingPattern === 'comparison-heavy' ? 2.5 : 1.0,
      purchaseProbability: persona.characteristics.loyaltyScore / 100,
      averageOrderValue: (persona.characteristics.monthlySpending.min + persona.characteristics.monthlySpending.max) / 2 / persona.characteristics.purchaseFrequency.max
    }
    
    // 執行符合人設的行為模擬
    if (persona.behaviors.decisionSpeed === 'fast') {
      await this.simulateQuickPurchaseDecision(customerId, behaviorModifiers)
    } else {
      await this.simulateDeliberatedPurchaseDecision(customerId, behaviorModifiers)
    }
  }
}

季節性行為模擬

typescript
class SeasonalBehaviorSimulator {
  static getSeasonalModifiers(date: Date) {
    const month = date.getMonth() + 1
    
    // 定義季節性修正係數
    const seasonalModifiers = {
      // 春節期間 (1-2月)
      chinese_new_year: {
        months: [1, 2],
        modifiers: {
          purchaseVolume: 1.8,
          giftPurchases: 3.0,
          categories: {
            '服飾配件': 2.0,
            '食品飲料': 2.5,
            '精品配件': 1.5
          }
        }
      },
      
      // 夏季促銷 (6-8月)
      summer_sale: {
        months: [6, 7, 8],
        modifiers: {
          purchaseVolume: 1.3,
          discountSensitivity: 1.5,
          categories: {
            '服飾配件': 1.8,
            '運動健身': 1.4
          }
        }
      },
      
      // 雙11購物節 (11月)
      double_eleven: {
        months: [11],
        modifiers: {
          purchaseVolume: 2.5,
          cartSize: 2.0,
          comparisonBehavior: 1.8
        }
      },
      
      // 聖誕節 (12月)
      christmas: {
        months: [12],
        modifiers: {
          purchaseVolume: 1.6,
          giftPurchases: 2.2,
          premiumProducts: 1.3
        }
      }
    }
    
    // 找到適用的季節性修正
    for (const [season, config] of Object.entries(seasonalModifiers)) {
      if (config.months.includes(month)) {
        return { season, ...config.modifiers }
      }
    }
    
    return { season: 'normal', purchaseVolume: 1.0 }
  }
  
  static async simulateSeasonalCustomerBehavior(customerId: string, date: Date) {
    const modifiers = this.getSeasonalModifiers(date)
    console.log(`🌱 季節性行為模擬: ${modifiers.season}`)
    
    // 根據季節調整購買行為
    if (modifiers.giftPurchases && modifiers.giftPurchases > 1) {
      await this.simulateGiftPurchaseBehavior(customerId, modifiers.giftPurchases)
    }
    
    if (modifiers.discountSensitivity && modifiers.discountSensitivity > 1) {
      await this.simulatePromotionHuntingBehavior(customerId, modifiers.discountSensitivity)
    }
  }
}

🔄 自動化測試腳本

持續行為模擬

typescript
class ContinuousSimulationEngine {
  private simulationConfig = {
    customerCount: 100,
    dailyActiveUsers: 30,
    sessionDuration: { min: 300, max: 3600 }, // 5分鐘到1小時
    simulationInterval: 60000 // 每分鐘執行一次
  }
  
  private isRunning = false
  
  async startSimulation() {
    if (this.isRunning) {
      console.log('⚠️ 模擬已在運行中')
      return
    }
    
    this.isRunning = true
    console.log('🚀 啟動持續客戶行為模擬')
    
    // 生成基礎客戶群
    const customers = await this.initializeCustomerBase()
    
    // 開始模擬循環
    while (this.isRunning) {
      await this.simulateDailyActivity(customers)
      await new Promise(resolve => setTimeout(resolve, this.simulationConfig.simulationInterval))
    }
  }
  
  private async simulateDailyActivity(customers: Customer[]) {
    const activeCustomers = faker.helpers.arrayElements(
      customers, 
      this.simulationConfig.dailyActiveUsers
    )
    
    const activities = activeCustomers.map(async (customer) => {
      const activityType = faker.helpers.weightedArrayElement([
        { weight: 50, value: 'browse' },
        { weight: 25, value: 'purchase' },
        { weight: 15, value: 'customer-service' },
        { weight: 10, value: 'account-management' }
      ])
      
      try {
        switch (activityType) {
          case 'browse':
            await ReturningCustomerSimulator.simulateBrowsingBehavior(customer.id)
            break
          case 'purchase':
            await PurchaseDecisionSimulator.simulatePurchaseJourney(customer.id)
            break
          case 'customer-service':
            await CustomerServiceSimulator.simulateCustomerServiceInteraction(customer.id)
            break
          case 'account-management':
            await this.simulateAccountActivity(customer.id)
            break
        }
        
        console.log(`✅ 完成 ${customer.name} 的 ${activityType} 活動模擬`)
      } catch (error) {
        console.error(`❌ 客戶 ${customer.name} 活動模擬失敗:`, error)
      }
    })
    
    await Promise.all(activities)
  }
  
  stopSimulation() {
    this.isRunning = false
    console.log('⏹️ 停止客戶行為模擬')
  }
}

// 使用範例
const simulationEngine = new ContinuousSimulationEngine()

// 啟動模擬
simulationEngine.startSimulation()

// 在需要時停止
// simulationEngine.stopSimulation()

Vue 元件整合

vue
<template>
  <div class="customer-simulation-panel">
    <h3>客戶行為模擬控制台</h3>
    
    <div class="simulation-status">
      <div class="status-indicator" :class="{ active: isSimulating }">
        {{ isSimulating ? '模擬運行中' : '模擬已停止' }}
      </div>
      <div class="metrics">
        <span>活躍客戶: {{ activeCustomers }}</span>
        <span>今日訂單: {{ todayOrders }}</span>
        <span>客服對話: {{ conversations }}</span>
      </div>
    </div>
    
    <div class="control-buttons">
      <button 
        @click="toggleSimulation" 
        :class="{ 'stop': isSimulating, 'start': !isSimulating }"
      >
        {{ isSimulating ? '停止模擬' : '開始模擬' }}
      </button>
      
      <button @click="runSingleSimulation" :disabled="isSimulating">
        單次模擬
      </button>
      
      <button @click="generateTestScenario">
        生成測試場景
      </button>
    </div>
    
    <div class="recent-activities" v-if="recentActivities.length">
      <h4>最近活動</h4>
      <ul>
        <li 
          v-for="activity in recentActivities" 
          :key="activity.id"
          :class="activity.type"
        >
          {{ activity.timestamp }} - {{ activity.customer }} - {{ activity.description }}
        </li>
      </ul>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'

const isSimulating = ref(false)
const activeCustomers = ref(0)
const todayOrders = ref(0)
const conversations = ref(0)
const recentActivities = ref([])

const simulationEngine = new ContinuousSimulationEngine()

const toggleSimulation = async () => {
  if (isSimulating.value) {
    simulationEngine.stopSimulation()
    isSimulating.value = false
  } else {
    await simulationEngine.startSimulation()
    isSimulating.value = true
  }
}

const runSingleSimulation = async () => {
  const customer = await generateCustomer()
  await ReturningCustomerSimulator.simulateReturnVisit(customer.id)
}
</script>

📈 效果監控

模擬品質指標

typescript
interface SimulationMetrics {
  customerEngagement: {
    averageSessionDuration: number
    pageViewsPerSession: number
    bounceRate: number
  }
  
  conversionMetrics: {
    visitToPurchaseRate: number
    cartAbandonmentRate: number
    averageOrderValue: number
  }
  
  customerService: {
    inquiryVolume: number
    resolutionRate: number
    averageResponseTime: number
  }
  
  dataQuality: {
    consistencyScore: number
    completenessRate: number
    realismIndex: number
  }
}

class SimulationAnalytics {
  static async generateMetricsReport(): Promise<SimulationMetrics> {
    // 收集和分析模擬數據
    const metrics = await this.collectSimulationMetrics()
    
    // 評估數據品質
    const qualityScore = await this.assessDataQuality()
    
    return {
      ...metrics,
      dataQuality: qualityScore
    }
  }
}

相關文檔


最後更新: $(date "+%Y-%m-%d")適用版本: front-stage-vue v1.0.0