dramaling-vocab-learning/docs/02_design/AI句子分析規格/系統整合與部署規格.md

23 KiB
Raw Blame History

系統整合與部署規格

📋 文件資訊

  • 文件名稱: 系統整合與部署規格
  • 版本: v2.0
  • 建立日期: 2025-01-25
  • 最後更新: 2025-01-25
  • 負責團隊: DramaLing DevOps團隊
  • 適用系統: AI句子分析功能全棧系統

🏗️ 系統架構圖

整體架構

┌─────────────────┐    HTTP/JSON     ┌──────────────────┐    Gemini API    ┌─────────────────┐
│                 │    Request       │                  │    Request       │                 │
│  Frontend       │ ──────────────► │  Backend API     │ ──────────────► │  Google Gemini  │
│  (Next.js)      │                 │  (.NET Core)     │                 │  AI Service     │
│  Port 3000      │ ◄────────────── │  Port 5008       │ ◄────────────── │                 │
│                 │    Response      │                  │    Response      │                 │
└─────────────────┘                 └──────────────────┘                 └─────────────────┘
        │                                      │
        │                                      │
        ▼                                      ▼
┌─────────────────┐                 ┌──────────────────┐
│  Local Storage  │                 │  SQLite Database │
│  - user_level   │                 │  - Cache Data    │
│  - auth_token   │                 │  - Usage Stats   │
└─────────────────┘                 └──────────────────┘

數據流向

sequenceDiagram
    participant U as 用戶
    participant F as 前端(3000)
    participant B as 後端(5008)
    participant G as Gemini API
    participant D as 資料庫

    U->>F: 1. 輸入英文句子
    U->>F: 2. 點擊「分析句子」
    F->>F: 3. 驗證輸入(≤300字符)
    F->>F: 4. 讀取userLevel (localStorage)
    F->>B: 5. POST /api/ai/analyze-sentence
    B->>B: 6. 輸入驗證和處理
    B->>G: 7. 調用Gemini API
    G->>B: 8. 返回AI分析結果
    B->>B: 9. 解析和格式化數據
    B->>D: 10. 記錄使用統計 (可選)
    B->>F: 11. 返回結構化分析結果
    F->>F: 12. 計算個人化統計
    F->>F: 13. 渲染詞彙標記和統計卡片
    F->>U: 14. 顯示完整分析結果

🔄 前後端整合規格

API整合詳細設計

前端請求實現

// 位置: frontend/app/generate/page.tsx
const handleAnalyzeSentence = async () => {
  try {
    const response = await fetch('http://localhost:5008/api/ai/analyze-sentence', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${getAuthToken()}` // 可選
      },
      body: JSON.stringify({
        inputText: textInput,
        analysisMode: 'full',
        options: {
          includeGrammarCheck: true,
          includeVocabularyAnalysis: true,
          includeTranslation: true,
          includeIdiomDetection: true,
          includeExamples: true
        }
      })
    });

    if (!response.ok) {
      throw new Error(`API請求失敗: ${response.status}`);
    }

    const result = await response.json();
    handleAnalysisResult(result.data);
  } catch (error) {
    handleAnalysisError(error);
  }
};

數據處理邏輯

// 前端個人化統計計算
const calculateVocabularyStats = (vocabularyAnalysis, idioms, userLevel) => {
  const userIndex = CEFR_LEVELS.indexOf(userLevel);
  let simple = 0, moderate = 0, difficult = 0;

  Object.values(vocabularyAnalysis).forEach(word => {
    const wordIndex = CEFR_LEVELS.indexOf(word.difficultyLevel);
    if (userIndex > wordIndex) simple++;
    else if (userIndex === wordIndex) moderate++;
    else difficult++;
  });

  return {
    simpleCount: simple,
    moderateCount: moderate,
    difficultCount: difficult,
    idiomCount: idioms.length
  };
};

錯誤處理整合

前端錯誤處理

const handleAnalysisError = (error) => {
  console.error('Analysis error:', error);

  // 顯示用戶友善的錯誤訊息
  if (error.message.includes('timeout')) {
    setErrorMessage('分析服務繁忙,請稍後再試');
  } else if (error.message.includes('network')) {
    setErrorMessage('網路連接問題,請檢查網路狀態');
  } else {
    setErrorMessage('分析過程中發生錯誤,請稍後再試');
  }

  // 提供降級體驗
  setFallbackAnalysisView(textInput);
};

後端錯誤映射

// 位置: backend/Controllers/AIController.cs
private ApiErrorResponse CreateErrorResponse(string code, string message, object? details, string requestId)
{
    var userFriendlyMessage = code switch
    {
        "INVALID_INPUT" => "輸入格式不正確,請檢查文本內容",
        "AI_SERVICE_ERROR" => "AI分析服務暫時不可用請稍後重試",
        "RATE_LIMIT_EXCEEDED" => "請求過於頻繁,請稍候再試",
        "TIMEOUT" => "分析超時,請嘗試較短的句子",
        _ => "系統暫時不可用,請稍後重試"
    };

    return new ApiErrorResponse
    {
        Success = false,
        Error = new ApiError
        {
            Code = code,
            Message = userFriendlyMessage,
            Details = details,
            Suggestions = GetSuggestionsForError(code)
        },
        RequestId = requestId,
        Timestamp = DateTime.UtcNow
    };
}

🔧 開發環境配置

環境準備

必要軟體

開發工具:
  - Node.js: >= 18.0.0
  - .NET SDK: >= 8.0.0
  - Git: >= 2.40.0
  - VSCode: 最新版本

瀏覽器支援:
  - Chrome: >= 90 (開發調試用)
  - Safari: >= 14 (測試用)
  - Firefox: >= 88 (測試用)

可選工具:
  - Docker: >= 20.0 (容器化部署)
  - Redis: >= 6.0 (本地快取測試)
  - Postman: API測試

環境變數配置

# 後端環境變數
export GEMINI_API_KEY="your-gemini-api-key"
export ASPNETCORE_ENVIRONMENT="Development"
export DRAMALING_DB_CONNECTION="Data Source=dramaling_test.db"

# 前端環境變數 (可選)
export NEXT_PUBLIC_API_URL="http://localhost:5008"
export NEXT_PUBLIC_ENVIRONMENT="development"

啟動流程

開發環境啟動腳本

#!/bin/bash
# 位置: start-development.sh

echo "🚀 啟動 DramaLing 開發環境..."

# 1. 檢查必要軟體
check_prerequisites() {
  command -v node >/dev/null 2>&1 || { echo "需要安裝 Node.js"; exit 1; }
  command -v dotnet >/dev/null 2>&1 || { echo "需要安裝 .NET SDK"; exit 1; }
}

# 2. 啟動後端 API (Port 5008)
start_backend() {
  echo "🔧 啟動後端 API..."
  cd backend/DramaLing.Api
  dotnet restore
  dotnet run &
  BACKEND_PID=$!
  echo "後端 PID: $BACKEND_PID"
}

# 3. 啟動前端 (Port 3000)
start_frontend() {
  echo "🎨 啟動前端..."
  cd ../../frontend
  npm install
  npm run dev &
  FRONTEND_PID=$!
  echo "前端 PID: $FRONTEND_PID"
}

# 4. 健康檢查
health_check() {
  echo "🏥 執行健康檢查..."
  sleep 10

  # 檢查後端
  if curl -f http://localhost:5008/health >/dev/null 2>&1; then
    echo "✅ 後端服務正常"
  else
    echo "❌ 後端服務異常"
  fi

  # 檢查前端
  if curl -f http://localhost:3000 >/dev/null 2>&1; then
    echo "✅ 前端服務正常"
  else
    echo "❌ 前端服務異常"
  fi
}

# 執行啟動流程
check_prerequisites
start_backend
start_frontend
health_check

echo "🎉 開發環境啟動完成!"
echo "前端: http://localhost:3000"
echo "後端API: http://localhost:5008"
echo "API文檔: http://localhost:5008/swagger"

🧪 測試整合策略

整合測試架構

API整合測試

[TestFixture]
public class AIAnalysisIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly WebApplicationFactory<Program> _factory;
    private readonly HttpClient _client;

    [SetUp]
    public void Setup()
    {
        _client = _factory.CreateClient();
    }

    [Test]
    public async Task AnalyzeSentence_EndToEnd_ReturnsValidResponse()
    {
        // Arrange
        var request = new
        {
            inputText = "She just join the team, so let's cut her some slack.",
            analysisMode = "full"
        };

        // Act
        var response = await _client.PostAsJsonAsync("/api/ai/analyze-sentence", request);

        // Assert
        response.EnsureSuccessStatusCode();
        var result = await response.Content.ReadFromJsonAsync<AnalysisResponse>();

        Assert.That(result.Success, Is.True);
        Assert.That(result.Data.VocabularyAnalysis.Count, Is.GreaterThan(0));
        Assert.That(result.Data.SentenceMeaning, Is.Not.Empty);
        Assert.That(result.Data.Idioms.Count, Is.GreaterThan(0));
    }
}

前端E2E測試

// 使用 Playwright 或 Cypress
describe('AI Analysis E2E Flow', () => {
  test('complete analysis workflow', async ({ page }) => {
    // 1. 導航到分析頁面
    await page.goto('http://localhost:3000/generate');

    // 2. 輸入測試句子
    await page.fill('[data-testid="text-input"]',
      'She just join the team, so let\'s cut her some slack.');

    // 3. 點擊分析按鈕
    await page.click('[data-testid="analyze-button"]');

    // 4. 等待分析完成
    await page.waitForSelector('[data-testid="analysis-result"]', { timeout: 10000 });

    // 5. 驗證結果
    await expect(page.locator('[data-testid="grammar-correction"]')).toBeVisible();
    await expect(page.locator('[data-testid="vocabulary-analysis"]')).toBeVisible();
    await expect(page.locator('[data-testid="idioms-section"]')).toBeVisible();
    await expect(page.locator('[data-testid="statistics-cards"]')).toBeVisible();

    // 6. 測試詞彙點擊
    await page.click('[data-testid="word-she"]');
    await expect(page.locator('[data-testid="vocab-popup"]')).toBeVisible();
  });
});

性能測試整合

負載測試配置

# 使用 k6 或 JMeter
負載測試場景:
  - 正常負載: 100 用戶,持續 10 分鐘
  - 壓力測試: 500 用戶,持續 5 分鐘
  - 尖峰測試: 1000 用戶,持續 2 分鐘

性能指標:
  - 回應時間P95: < 5秒
  - 錯誤率: < 1%
  - 吞吐量: > 100 RPS
  - 資源使用: CPU < 80%, Memory < 70%

🚀 部署架構

環境配置

開發環境 (Development)

基礎設施:
  - 本地開發機器
  - SQLite 資料庫
  - In-Memory 快取
  - Gemini API (測試金鑰)

配置特點:
  - 詳細日誌輸出
  - 熱重載支援
  - Swagger API 文檔
  - CORS 寬鬆政策

測試環境 (Staging)

基礎設施:
  - 雲端虛擬機 或 Docker 容器
  - PostgreSQL 資料庫
  - Redis 快取
  - Gemini API (測試金鑰)

配置特點:
  - 生產環境模擬
  - 效能監控啟用
  - 自動化測試整合
  - 安全掃描

生產環境 (Production)

基礎設施:
  - Kubernetes 叢集 或 雲端服務
  - PostgreSQL 高可用性叢集
  - Redis 叢集
  - Gemini API (生產金鑰)
  - CDN 和負載均衡

配置特點:
  - 高可用性 (99.9%+)
  - 自動擴容
  - 全面監控和告警
  - 災難恢復機制

容器化部署

Docker Compose 配置

# docker-compose.yml
version: '3.8'

services:
  # 後端 API 服務
  backend:
    build:
      context: ./backend/DramaLing.Api
      dockerfile: Dockerfile
    ports:
      - "5008:5008"
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - GEMINI_API_KEY=${GEMINI_API_KEY}
      - ConnectionStrings__DefaultConnection=${DB_CONNECTION}
    depends_on:
      - database
      - redis
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5008/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  # 前端服務
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - NEXT_PUBLIC_API_URL=http://backend:5008
    depends_on:
      - backend

  # 資料庫服務
  database:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=dramaling
      - POSTGRES_USER=${DB_USER}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  # 快取服務
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

volumes:
  postgres_data:
  redis_data:

Kubernetes 部署配置

# k8s-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dramaling-backend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: dramaling-backend
  template:
    metadata:
      labels:
        app: dramaling-backend
    spec:
      containers:
      - name: backend
        image: dramaling/backend:latest
        ports:
        - containerPort: 5008
        env:
        - name: GEMINI_API_KEY
          valueFrom:
            secretKeyRef:
              name: ai-secrets
              key: gemini-api-key
        - name: ConnectionStrings__DefaultConnection
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: db-connection
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 5008
          initialDelaySeconds: 30
          periodSeconds: 30
        readinessProbe:
          httpGet:
            path: /health
            port: 5008
          initialDelaySeconds: 5
          periodSeconds: 5

📊 監控與可觀測性

日誌整合

結構化日誌配置

// appsettings.Production.json
{
  "Serilog": {
    "Using": ["Serilog.Sinks.Console", "Serilog.Sinks.ApplicationInsights"],
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Warning",
        "System": "Warning",
        "DramaLing": "Information"
      }
    },
    "WriteTo": [
      {
        "Name": "Console",
        "Args": {
          "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}"
        }
      },
      {
        "Name": "ApplicationInsights",
        "Args": {
          "instrumentationKey": "{ApplicationInsights:InstrumentationKey}"
        }
      }
    ],
    "Enrich": ["FromLogContext", "WithMachineName", "WithThreadId"]
  }
}

前端錯誤追蹤

// 錯誤邊界和監控
class ErrorBoundary extends React.Component {
  componentDidCatch(error, errorInfo) {
    // 發送錯誤到監控服務
    console.error('React Error Boundary:', error, errorInfo);

    // 可選:整合 Sentry 或其他錯誤追蹤服務
    if (typeof window !== 'undefined' && window.gtag) {
      window.gtag('event', 'exception', {
        description: error.toString(),
        fatal: false
      });
    }
  }
}

健康檢查系統

深度健康檢查

public class SystemHealthCheck : IHealthCheck
{
    private readonly IGeminiService _geminiService;
    private readonly DramaLingDbContext _dbContext;

    public async Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        var checks = new Dictionary<string, HealthStatus>();

        // 檢查資料庫連接
        try
        {
            await _dbContext.Database.CanConnectAsync(cancellationToken);
            checks["database"] = HealthStatus.Healthy;
        }
        catch (Exception ex)
        {
            checks["database"] = HealthStatus.Unhealthy;
        }

        // 檢查 AI 服務
        try
        {
            var isHealthy = await _geminiService.HealthCheckAsync();
            checks["gemini_api"] = isHealthy ? HealthStatus.Healthy : HealthStatus.Degraded;
        }
        catch (Exception ex)
        {
            checks["gemini_api"] = HealthStatus.Unhealthy;
        }

        // 檢查記憶體使用
        var memoryUsage = GC.GetTotalMemory(false);
        checks["memory"] = memoryUsage < 500_000_000 ? HealthStatus.Healthy : HealthStatus.Degraded;

        var overallStatus = checks.Values.All(s => s == HealthStatus.Healthy)
            ? HealthStatus.Healthy
            : checks.Values.Any(s => s == HealthStatus.Unhealthy)
                ? HealthStatus.Unhealthy
                : HealthStatus.Degraded;

        return new HealthCheckResult(overallStatus,
            description: $"System health: {string.Join(", ", checks.Select(c => $"{c.Key}:{c.Value}"))}",
            data: checks.ToDictionary(c => c.Key, c => (object)c.Value.ToString()));
    }
}

🔒 安全整合

HTTPS 配置

開發環境:
  - HTTP: localhost:3000, localhost:5008
  - 自簽證書: dotnet dev-certs https --trust

生產環境:
  - HTTPS: 強制重定向
  - TLS 1.3: 最低版本要求
  - HSTS: 嚴格傳輸安全
  - 證書: Let's Encrypt 或企業CA

CORS 政策

// 開發環境 CORS 配置
services.AddCors(options =>
{
    options.AddPolicy("Development", policy =>
    {
        policy.WithOrigins("http://localhost:3000", "http://localhost:3001")
              .AllowAnyHeader()
              .AllowAnyMethod()
              .AllowCredentials();
    });

    options.AddPolicy("Production", policy =>
    {
        policy.WithOrigins("https://dramaling.com", "https://app.dramaling.com")
              .AllowAnyHeader()
              .AllowAnyMethod()
              .AllowCredentials();
    });
});

📈 監控整合

應用程式監控

關鍵指標儀表板

業務指標:
  - 每日分析次數
  - 用戶活躍度
  - 功能使用分佈
  - AI分析成功率

技術指標:
  - API回應時間分佈
  - 資料庫查詢性能
  - 記憶體和CPU使用
  - 錯誤率和異常統計

用戶體驗指標:
  - 頁面載入時間
  - 首次內容繪製 (FCP)
  - 最大內容繪製 (LCP)
  - 累積佈局偏移 (CLS)

告警配置

嚴重告警:
  - API 錯誤率 > 5% (5分鐘內)
  - 回應時間P95 > 10秒 (5分鐘內)
  - 服務不可用 > 2分鐘
  - 資料庫連接失敗

警告告警:
  - CPU 使用率 > 80% (10分鐘內)
  - 記憶體使用率 > 85% (10分鐘內)
  - AI API 調用失敗率 > 10%
  - 磁碟空間不足 < 10%

🔧 故障排除指南

常見問題和解決方案

連接問題

問題: CORS 錯誤
症狀: "Access to fetch blocked by CORS policy"
解決: 檢查後端 CORS 設定,確認前端域名在允許清單

問題: 連接被拒絕
症狀: "Connection refused" 或 "ECONNREFUSED"
解決: 確認後端服務正在運行,檢查埠號是否正確

問題: 超時錯誤
症狀: "Request timeout" 或響應超過 30 秒
解決: 檢查 AI API 金鑰,網路連接,增加超時設定

資料問題

問題: AI 回應格式錯誤
症狀: "Cannot read property 'vocabularyAnalysis' of undefined"
解決: 檢查 Gemini API 回應格式,更新錯誤處理邏輯

問題: 詞彙分析為空
症狀: 分析結果不包含詞彙資訊
解決: 檢查 AI Prompt 設計,確認輸入文本有效

問題: 統計數字不一致
症狀: 統計卡片數字與實際標記不符
解決: 檢查前端統計計算邏輯,確認分類算法正確

調試工具

開發調試指令

# 檢查服務狀態
curl -I http://localhost:5008/health
curl -I http://localhost:3000

# 測試 API 端點
curl -X POST http://localhost:5008/api/ai/analyze-sentence \
  -H "Content-Type: application/json" \
  -d '{"inputText":"Test sentence","analysisMode":"full"}'

# 檢查日誌
docker logs dramaling-backend
docker logs dramaling-frontend

# 檢查資源使用
docker stats
top -p $(pgrep dotnet)

生產監控指令

# 健康檢查
kubectl get pods -l app=dramaling
kubectl describe pod dramaling-backend-xxx

# 查看日誌
kubectl logs -f deployment/dramaling-backend
kubectl logs -f deployment/dramaling-frontend

# 性能監控
kubectl top pods
kubectl top nodes

📋 部署檢查清單

部署前檢查

  • 所有測試通過 (單元、整合、E2E)
  • 安全掃描無嚴重漏洞
  • 性能基準測試達標
  • 配置檔案正確設定
  • 環境變數和金鑰配置完成
  • 資料庫遷移腳本準備
  • 監控和告警配置完成
  • 回滾計劃準備

部署後驗證

  • 健康檢查端點回應正常
  • API 功能端到端測試通過
  • 前端頁面載入和功能正常
  • 監控指標顯示正常
  • 日誌記錄正確產生
  • 告警機制測試正常
  • 負載測試驗證性能
  • 安全掃描確認無新漏洞

🔄 CI/CD 流程

持續整合流程

觸發條件:
  - 主分支推送 (main)
  - Pull Request 建立
  - 標籤建立 (v*.*.*)

建置步驟:
  1. 程式碼檢出
  2. 依賴安裝
  3. 靜態分析 (ESLint, SonarQube)
  4. 單元測試執行
  5. 測試覆蓋率檢查
  6. 安全掃描
  7. 建置 Docker 映像
  8. 整合測試執行

部署條件:
  - 所有測試通過
  - 程式碼覆蓋率 > 80%
  - 安全掃描通過
  - 人工審核批准 (生產部署)

持續部署流程

測試環境自動部署:
  - 主分支每次推送自動部署
  - 自動執行煙霧測試
  - 通知團隊部署狀態

生產環境部署:
  - 手動觸發或定期發布
  - 藍綠部署或滾動更新
  - 自動回滾機制
  - 部署後監控和驗證

文件版本: v2.0 DevOps負責人: DramaLing DevOps團隊 最後更新: 2025-01-25 下次審查: 2025-02-25

關聯文件:

  • 《AI句子分析功能產品需求規格》- 產品需求和用戶故事
  • 《AI分析API技術實現規格》- API設計和技術實現
  • 《AI驅動產品後端技術架構指南》- 架構設計指導原則