🔗 实战项目openharmonycrossplatform.csdn.net/content

📖 目录

  • 🏗️ 微服务架构

  • 🚀 CI/CD管道

  • 📦 容器化部署

  • 📊 监控与日志

🏗️ 一、微服务架构设计

1.1 微服务架构核心

dart

// lib/architecture/microservice_structure.dart
class MicroserviceArchitecture {
  // 微服务模块定义
  static const Map<String, List<String>> services = {
    'user_service': [
      'auth',
      'profile',
      'preferences',
    ],
    'product_service': [
      'catalog',
      'inventory',
      'pricing',
    ],
    'order_service': [
      'cart',
      'checkout',
      'payment',
    ],
    'notification_service': [
      'email',
      'push',
      'sms',
    ],
  };

  // 服务发现配置
  static const serviceDiscovery = {
    'user_service': 'https://user.api.example.com',
    'product_service': 'https://product.api.example.com',
    'order_service': 'https://order.api.example.com',
    'notification_service': 'https://notify.api.example.com',
  };
}

1.2 服务网关实现

dart

// lib/services/api_gateway.dart
import 'package:dio/dio.dart';
import 'package:retrofit/retrofit.dart';

part 'api_gateway.g.dart';

@RestApi(baseUrl: "https://gateway.api.example.com")
abstract class ApiGateway {
  factory ApiGateway(Dio dio, {String baseUrl}) = _ApiGateway;
  
  // 用户服务
  @GET("/users/{id}")
  Future<UserResponse> getUser(@Path("id") String userId);
  
  @POST("/users/login")
  Future<LoginResponse> login(@Body() LoginRequest request);
  
  // 产品服务
  @GET("/products")
  Future<ProductListResponse> getProducts(@Query("page") int page);
  
  @GET("/products/{id}")
  Future<ProductResponse> getProduct(@Path("id") String productId);
  
  // 订单服务
  @POST("/orders")
  Future<OrderResponse> createOrder(@Body() OrderRequest request);
  
  @GET("/orders/{id}")
  Future<OrderResponse> getOrder(@Path("id") String orderId);
  
  // 通知服务
  @POST("/notifications")
  Future<NotificationResponse> sendNotification(
    @Body() NotificationRequest request,
  );
}

// 错误处理中间件
class ApiGatewayInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    // 添加认证头
    options.headers.addAll({
      'X-Request-ID': _generateRequestId(),
      'Authorization': 'Bearer ${_getAuthToken()}',
    });
    
    // 记录请求日志
    _logRequest(options);
    
    handler.next(options);
  }
  
  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    // 记录响应日志
    _logResponse(response);
    
    // 统一响应格式处理
    if (response.data is Map<String, dynamic>) {
      final data = response.data as Map<String, dynamic>;
      if (data['code'] != 0) {
        // 业务错误处理
        handler.reject(DioException(
          requestOptions: response.requestOptions,
          response: response,
          type: DioExceptionType.badResponse,
          error: data['message'],
        ));
        return;
      }
    }
    
    handler.next(response);
  }
  
  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    // 统一错误处理
    _handleError(err);
    handler.next(err);
  }
  
  String _generateRequestId() {
    return '${DateTime.now().millisecondsSinceEpoch}_${Random().nextInt(9999)}';
  }
  
  String? _getAuthToken() {
    // 从本地存储获取token
    return null;
  }
  
  void _logRequest(RequestOptions options) {
    print('🌐 请求: ${options.method} ${options.uri}');
  }
  
  void _logResponse(Response response) {
    print('✅ 响应: ${response.statusCode} ${response.requestOptions.uri}');
  }
  
  void _handleError(DioException error) {
    print('❌ 错误: ${error.type} ${error.message}');
  }
}

// 服务熔断器
class CircuitBreaker {
  final Duration timeout;
  final int failureThreshold;
  final Duration resetTimeout;
  
  int _failureCount = 0;
  DateTime? _lastFailure;
  bool _circuitOpen = false;
  
  CircuitBreaker({
    this.timeout = const Duration(seconds: 5),
    this.failureThreshold = 3,
    this.resetTimeout = const Duration(seconds: 30),
  });
  
  Future<T> execute<T>(Future<T> Function() operation) async {
    if (_circuitOpen) {
      if (_lastFailure != null &&
          DateTime.now().difference(_lastFailure!) > resetTimeout) {
        _circuitOpen = false;
        _failureCount = 0;
      } else {
        throw CircuitBreakerException('服务熔断中');
      }
    }
    
    try {
      final result = await operation().timeout(timeout);
      _reset();
      return result;
    } catch (e) {
      _recordFailure();
      rethrow;
    }
  }
  
  void _recordFailure() {
    _failureCount++;
    _lastFailure = DateTime.now();
    
    if (_failureCount >= failureThreshold) {
      _circuitOpen = true;
    }
  }
  
  void _reset() {
    _failureCount = 0;
    _circuitOpen = false;
  }
}

class CircuitBreakerException implements Exception {
  final String message;
  
  const CircuitBreakerException(this.message);
  
  @override
  String toString() => 'CircuitBreakerException: $message';
}

🚀 二、CI/CD管道实现

2.1 GitHub Actions配置

yaml

# .github/workflows/flutter-ci.yml
name: Flutter CI/CD Pipeline

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

env:
  FLUTTER_VERSION: '3.19.0'

jobs:
  # 1. 代码质量检查
  quality-check:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Flutter
      uses: subosito/flutter-action@v2
      with:
        flutter-version: ${{ env.FLUTTER_VERSION }}
        cache: true
        
    - name: Analyze Code
      run: flutter analyze
      
    - name: Run Tests
      run: flutter test
      
    - name: Generate Coverage
      run: |
        flutter test --coverage
        genhtml coverage/lcov.info -o coverage/html
        
    - name: Upload Coverage
      uses: codecov/codecov-action@v3
      with:
        file: ./coverage/lcov.info
        fail_ci_if_error: false

  # 2. 构建检查
  build-check:
    needs: quality-check
    runs-on: ubuntu-latest
    strategy:
      matrix:
        platform: [android, ios]
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Flutter
      uses: subosito/flutter-action@v2
      with:
        flutter-version: ${{ env.FLUTTER_VERSION }}
        
    - name: Build APK
      if: matrix.platform == 'android'
      run: |
        flutter build apk --release --split-per-abi
        
    - name: Build AppBundle
      if: matrix.platform == 'android'
      run: |
        flutter build appbundle --release
        
    - name: Build iOS
      if: matrix.platform == 'ios'
      run: |
        flutter build ios --release --no-codesign
        
    - name: Upload Artifacts
      uses: actions/upload-artifact@v3
      with:
        name: build-${{ matrix.platform }}
        path: |
          build/app/outputs/
          
  # 3. 部署到测试环境
  deploy-test:
    needs: build-check
    if: github.ref == 'refs/heads/develop'
    runs-on: ubuntu-latest
    environment: test
    steps:
    - uses: actions/download-artifact@v3
      with:
        name: build-android
        
    - name: Deploy to Firebase
      run: |
        curl -X POST \
          -H "Authorization: Bearer ${{ secrets.FIREBASE_TOKEN }}" \
          -H "Content-Type: application/octet-stream" \
          --data-binary @app-release.apk \
          "https://firebaseappdistribution.googleapis.com/v1/projects/${{ secrets.FIREBASE_PROJECT_ID }}/releases:upload"
          
    - name: Notify Slack
      uses: 8398a7/action-slack@v3
      with:
        status: ${{ job.status }}
        author_name: Flutter CI/CD
      env:
        SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

  # 4. 生产环境部署
  deploy-prod:
    needs: build-check
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production
    steps:
    - uses: actions/download-artifact@v3
      with:
        name: build-android
        
    - name: Upload to Google Play
      uses: r0adkll/upload-google-play@v1
      with:
        serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT }}
        packageName: com.example.app
        releaseFiles: app-release.aab
        track: production
        status: completed
        
    - name: Create GitHub Release
      uses: softprops/action-gh-release@v1
      with:
        tag_name: v${{ github.run_number }}
        name: Release v${{ github.run_number }}
        body: |
          ### 🚀 新版本发布
          
          **构建编号:** ${{ github.run_number }}
          **提交:** ${{ github.sha }}
          **时间:** ${{ github.event.head_commit.timestamp }}
        draft: false
        prerelease: false

2.2 自动化测试配置

dart

// test/integration/api_integration_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';

@GenerateMocks([http.Client])
void main() {
  group('API集成测试', () {
    late http.Client client;
    
    setUp(() {
      client = MockClient();
    });
    
    test('用户登录接口测试', () async {
      // 模拟成功响应
      when(client.post(
        Uri.parse('https://api.example.com/login'),
        headers: anyNamed('headers'),
        body: anyNamed('body'),
      )).thenAnswer((_) async => http.Response(
        '{"success": true, "token": "test_token"}',
        200,
      ));
      
      // 执行测试
      final response = await client.post(
        Uri.parse('https://api.example.com/login'),
        headers: {'Content-Type': 'application/json'},
        body: '{"email": "test@example.com", "password": "password"}',
      );
      
      // 验证结果
      expect(response.statusCode, 200);
      expect(response.body, contains('success'));
    });
    
    test('商品列表接口测试', () async {
      // 模拟分页响应
      when(client.get(
        Uri.parse('https://api.example.com/products?page=1'),
        headers: anyNamed('headers'),
      )).thenAnswer((_) async => http.Response(
        '{"products": [], "total": 0, "page": 1}',
        200,
      ));
      
      final response = await client.get(
        Uri.parse('https://api.example.com/products?page=1'),
        headers: {'Authorization': 'Bearer test_token'},
      );
      
      expect(response.statusCode, 200);
      expect(response.body, contains('products'));
    });
  });
}

// test/widget/widget_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('首页Widget测试', (WidgetTester tester) async {
    // 构建Widget
    await tester.pumpWidget(MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('首页')),
        body: const Center(child: Text('欢迎使用')),
      ),
    ));
    
    // 查找文本
    expect(find.text('首页'), findsOneWidget);
    expect(find.text('欢迎使用'), findsOneWidget);
    
    // 模拟点击
    await tester.tap(find.byType(AppBar));
    await tester.pump();
  });
  
  testWidgets('表单验证测试', (WidgetTester tester) async {
    await tester.pumpWidget(MaterialApp(
      home: Scaffold(
        body: Column(
          children: [
            TextField(
              decoration: const InputDecoration(labelText: '邮箱'),
              controller: TextEditingController(),
            ),
            ElevatedButton(
              onPressed: () {},
              child: const Text('提交'),
            ),
          ],
        ),
      ),
    ));
    
    // 输入测试
    await tester.enterText(find.byType(TextField), 'test@example.com');
    expect(find.text('test@example.com'), findsOneWidget);
  });
}

📦 三、容器化部署

3.1 Docker配置

dockerfile

# Dockerfile
FROM ubuntu:22.04 AS build

# 安装Flutter依赖
RUN apt-get update && apt-get install -y \
    curl \
    git \
    unzip \
    xz-utils \
    zip \
    libglu1-mesa \
    openjdk-17-jdk \
    wget \
    && rm -rf /var/lib/apt/lists/*

# 安装Flutter
ARG FLUTTER_VERSION=3.19.0
RUN git clone https://github.com/flutter/flutter.git -b $FLUTTER_VERSION /usr/local/flutter
ENV PATH="/usr/local/flutter/bin:/usr/local/flutter/bin/cache/dart-sdk/bin:${PATH}"

# 验证安装
RUN flutter doctor

WORKDIR /app

# 复制项目文件
COPY pubspec.yaml ./
COPY lib ./lib
COPY assets ./assets
COPY test ./test

# 获取依赖
RUN flutter pub get

# 构建Web版本
RUN flutter build web --release --web-renderer canvaskit

# 使用Nginx服务
FROM nginx:alpine

# 复制构建产物
COPY --from=build /app/build/web /usr/share/nginx/html

# 复制Nginx配置
COPY nginx.conf /etc/nginx/nginx.conf

# 暴露端口
EXPOSE 80

# 启动Nginx
CMD ["nginx", "-g", "daemon off;"]

nginx

# nginx.conf
events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Gzip压缩
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css text/xml text/javascript 
               application/javascript application/xml+rss 
               application/json;

    server {
        listen 80;
        server_name _;
        root /usr/share/nginx/html;
        index index.html;

        # 静态文件缓存
        location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
            expires 1y;
            add_header Cache-Control "public, immutable";
        }

        # HTML文件不缓存
        location ~* \.html$ {
            expires -1;
            add_header Cache-Control "no-store, no-cache, must-revalidate";
        }

        # SPA路由支持
        location / {
            try_files $uri $uri/ /index.html;
        }

        # 健康检查
        location /health {
            access_log off;
            return 200 "healthy\n";
            add_header Content-Type text/plain;
        }
    }
}

3.2 Docker Compose配置

yaml

# docker-compose.yml
version: '3.8'

services:
  # Flutter Web应用
  flutter-web:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:80"
    environment:
      - NODE_ENV=production
    networks:
      - app-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  # 后端API服务
  api-service:
    image: node:18-alpine
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://user:pass@db:5432/app
    volumes:
      - ./api:/app
    working_dir: /app
    command: ["npm", "start"]
    networks:
      - app-network
    depends_on:
      db:
        condition: service_healthy

  # 数据库
  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
      - POSTGRES_DB=app
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - app-network
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user"]
      interval: 10s
      timeout: 5s
      retries: 5

  # Redis缓存
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    networks:
      - app-network

  # 监控服务
  monitoring:
    image: grafana/grafana:latest
    ports:
      - "3001:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana-data:/var/lib/grafana
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

volumes:
  postgres-data:
  grafana-data:

📊 四、监控与日志系统

4.1 应用监控

dart

// lib/services/monitoring_service.dart
import 'package:flutter/foundation.dart';
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:sentry_flutter/sentry_flutter.dart';

class MonitoringService {
  static final FirebaseAnalytics _analytics = FirebaseAnalytics.instance;
  static final SentryClient _sentry = SentryClient(SentryOptions(
    dsn: 'YOUR_SENTRY_DSN',
    debug: kDebugMode,
  ));
  
  // 跟踪用户行为
  static Future<void> trackEvent({
    required String name,
    Map<String, dynamic>? parameters,
  }) async {
    try {
      await _analytics.logEvent(
        name: name,
        parameters: parameters,
      );
      
      print('📊 事件跟踪: $name');
    } catch (e) {
      _captureError(e, stackTrace: StackTrace.current);
    }
  }
  
  // 跟踪屏幕浏览
  static Future<void> trackScreen({
    required String screenName,
    String? screenClass,
  }) async {
    try {
      await _analytics.logScreenView(
        screenName: screenName,
        screenClass: screenClass,
      );
      
      print('📱 屏幕浏览: $screenName');
    } catch (e) {
      _captureError(e, stackTrace: StackTrace.current);
    }
  }
  
  // 捕获错误
  static Future<void> captureError(
    dynamic error, {
    StackTrace? stackTrace,
    String? context,
  }) async {
    print('❌ 错误捕获: $error');
    
    if (kReleaseMode) {
      await _sentry.captureException(
        error,
        stackTrace: stackTrace,
        hint: Hint.withMap({'context': context}),
      );
    }
  }
  
  // 性能监控
  static Future<void> trackPerformance({
    required String name,
    required Duration duration,
    Map<String, dynamic>? attributes,
  }) async {
    print('⏱️ 性能监控 [$name]: ${duration.inMilliseconds}ms');
    
    await _analytics.logEvent(
      name: 'performance_$name',
      parameters: {
        'duration_ms': duration.inMilliseconds,
        ...?attributes,
      },
    );
  }
  
  // 用户属性设置
  static Future<void> setUserProperties({
    String? userId,
    String? userEmail,
    Map<String, dynamic>? properties,
  }) async {
    await _analytics.setUserId(id: userId);
    await _analytics.setUserProperty(name: 'email', value: userEmail);
    
    if (properties != null) {
      for (final entry in properties.entries) {
        await _analytics.setUserProperty(
          name: entry.key,
          value: entry.value.toString(),
        );
      }
    }
  }
}

4.2 日志系统

dart

// lib/services/logging_service.dart
import 'dart:developer' as developer;
import 'package:logger/logger.dart';

class LoggingService {
  static final Logger _logger = Logger(
    printer: PrettyPrinter(
      methodCount: 0,
      errorMethodCount: 8,
      lineLength: 120,
      colors: true,
      printEmojis: true,
      printTime: true,
    ),
  );
  
  // 调试日志
  static void debug(String message, [dynamic error, StackTrace? stackTrace]) {
    if (kDebugMode) {
      _logger.d(message, error: error, stackTrace: stackTrace);
      developer.log(message, name: 'DEBUG');
    }
  }
  
  // 信息日志
  static void info(String message) {
    _logger.i(message);
    developer.log(message, name: 'INFO');
  }
  
  // 警告日志
  static void warning(String message, [dynamic error, StackTrace? stackTrace]) {
    _logger.w(message, error: error, stackTrace: stackTrace);
    developer.log(message, name: 'WARNING');
  }
  
  // 错误日志
  static void error(String message, [dynamic error, StackTrace? stackTrace]) {
    _logger.e(message, error: error, stackTrace: stackTrace);
    developer.log(message, name: 'ERROR');
    
    // 发送到监控服务
    MonitoringService.captureError(
      error ?? message,
      stackTrace: stackTrace,
      context: message,
    );
  }
  
  // 网络请求日志
  static void network(
    String method,
    String url, {
    int? statusCode,
    dynamic requestData,
    dynamic responseData,
    Duration? duration,
  }) {
    final logMessage = StringBuffer();
    logMessage.write('🌐 $method $url');
    
    if (statusCode != null) {
      logMessage.write(' [$statusCode]');
    }
    
    if (duration != null) {
      logMessage.write(' (${duration.inMilliseconds}ms)');
    }
    
    info(logMessage.toString());
    
    if (kDebugMode && requestData != null) {
      debug('请求数据: $requestData');
    }
    
    if (kDebugMode && responseData != null) {
      debug('响应数据: $responseData');
    }
  }
  
  // 结构化日志
  static void structured(
    String type,
    Map<String, dynamic> data, {
    LogLevel level = LogLevel.info,
  }) {
    final logEntry = {
      'timestamp': DateTime.now().toIso8601String(),
      'type': type,
      'level': level.name,
      'data': data,
    };
    
    final jsonString = _encodeJson(logEntry);
    
    switch (level) {
      case LogLevel.debug:
        debug(jsonString);
      case LogLevel.info:
        info(jsonString);
      case LogLevel.warning:
        warning(jsonString);
      case LogLevel.error:
        error(jsonString);
    }
  }
  
  static String _encodeJson(Map<String, dynamic> data) {
    try {
      return const JsonEncoder.withIndent('  ').convert(data);
    } catch (e) {
      return data.toString();
    }
  }
}

enum LogLevel {
  debug,
  info,
  warning,
  error,
}

🎯 五、实战示例:完整的CI/CD流水线

yaml

# .github/workflows/full-pipeline.yml
name: Full CI/CD Pipeline

on:
  workflow_dispatch:
    inputs:
      environment:
        description: '部署环境'
        required: true
        default: 'staging'
        type: choice
        options:
        - staging
        - production

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Flutter
      uses: subosito/flutter-action@v2
      with:
        flutter-version: '3.19.0'
        
    - name: Validate Pubspec
      run: |
        flutter pub get
        flutter analyze
        
    - name: Run Tests
      run: |
        flutter test --coverage
        
    - name: SonarQube Analysis
      uses: SonarSource/sonarqube-scan-action@master
      env:
        SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        
  build:
    needs: validate
    runs-on: ubuntu-latest
    strategy:
      matrix:
        target: [apk, appbundle, web]
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Flutter
      uses: subosito/flutter-action@v2
      
    - name: Build ${{ matrix.target }}
      run: |
        case "${{ matrix.target }}" in
          apk)
            flutter build apk --release --split-per-abi
            ;;
          appbundle)
            flutter build appbundle --release
            ;;
          web)
            flutter build web --release --web-renderer canvaskit
            ;;
        esac
        
    - name: Upload Artifact
      uses: actions/upload-artifact@v3
      with:
        name: ${{ matrix.target }}-build
        path: build/
        
  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment: ${{ github.event.inputs.environment }}
    steps:
    - name: Download Artifacts
      uses: actions/download-artifact@v3
      
    - name: Configure AWS
      uses: aws-actions/configure-aws-credentials@v2
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: us-east-1
        
    - name: Deploy to S3 (Web)
      if: matrix.target == 'web'
      run: |
        aws s3 sync build/web s3://${{ secrets.S3_BUCKET }}/web \
          --acl public-read \
          --cache-control "max-age=31536000"
          
    - name: Deploy to ECR (Container)
      if: matrix.target == 'container'
      run: |
        docker build -t ${{ secrets.ECR_REPOSITORY }}:latest .
        docker push ${{ secrets.ECR_REPOSITORY }}:latest
        
    - name: Update ECS Service
      if: matrix.target == 'container'
      run: |
        aws ecs update-service \
          --cluster ${{ secrets.ECS_CLUSTER }} \
          --service ${{ secrets.ECS_SERVICE }} \
          --force-new-deployment
          
  notify:
    needs: deploy
    runs-on: ubuntu-latest
    steps:
    - name: Send Success Notification
      uses: 8398a7/action-slack@v3
      with:
        status: ${{ job.status }}
        author_name: CI/CD Pipeline
        fields: |
          environment=${{ github.event.inputs.environment }}
          commit=${{ github.sha }}
          runner=${{ runner.os }}
      env:
        SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
        
    - name: Create Deployment Record
      run: |
        curl -X POST \
          -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
          -H "Accept: application/vnd.github.v3+json" \
          https://api.github.com/repos/${{ github.repository }}/deployments \
          -d '{
            "ref": "${{ github.ref }}",
            "environment": "${{ github.event.inputs.environment }}",
            "auto_merge": false,
            "required_contexts": []
          }'

📊 总结

微服务与CI/CD要点:

  1. ✅ 微服务架构:服务拆分、API网关、熔断器

  2. ✅ CI/CD管道:自动化测试、构建、部署

  3. ✅ 容器化部署:Docker、编排、监控

  4. ✅ 监控日志:性能跟踪、错误捕获、日志管理

工具链推荐:

yaml

# pubspec.yaml 关键依赖
dependencies:
  retrofit: ^4.0.1
  logger: ^2.0.1
  sentry_flutter: ^7.10.0
  firebase_analytics: ^10.4.3

dev_dependencies:
  build_runner: ^2.4.0
  retrofit_generator: ^5.0.0

欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net),一起共建开源鸿蒙跨平台生态。

更多推荐