# 系统集成和部署文档

## 概述

本文档详细描述了解班客平台的系统集成方案和部署流程，包括开发环境搭建、生产环境部署、CI/CD流程、监控配置等内容。系统采用现代化的微服务架构，支持容器化部署和自动化运维。

## 系统架构

### 整体架构图

```mermaid
graph TB
    subgraph "前端层"
        A[Web前端] --> B[移动端H5]
        A --> C[管理后台]
    end
    
    subgraph "网关层"
        D[Nginx反向代理]
        E[API网关]
    end
    
    subgraph "应用层"
        F[用户服务]
        G[动物服务]
        H[认领服务]
        I[管理服务]
    end
    
    subgraph "数据层"
        J[MySQL主库]
        K[MySQL从库]
        L[Redis缓存]
        M[文件存储]
    end
    
    subgraph "基础设施"
        N[日志系统]
        O[监控系统]
        P[告警系统]
    end
    
    A --> D
    B --> D
    C --> D
    D --> E
    E --> F
    E --> G
    E --> H
    E --> I
    F --> J
    G --> J
    H --> J
    I --> J
    J --> K
    F --> L
    G --> L
    H --> L
    I --> L
    F --> M
    G --> M
    H --> M
    I --> M
    F --> N
    G --> N
    H --> N
    I --> N
    N --> O
    O --> P
```

### 技术栈

#### 前端技术栈
- **框架**: Vue.js 3.x
- **构建工具**: Vite
- **UI组件**: Element Plus
- **状态管理**: Pinia
- **路由**: Vue Router
- **HTTP客户端**: Axios
- **样式**: SCSS

#### 后端技术栈
- **运行时**: Node.js 18+
- **框架**: Express.js
- **数据库**: MySQL 8.0
- **缓存**: Redis 6.0
- **ORM**: Mongoose (MongoDB) / Sequelize (MySQL)
- **认证**: JWT
- **文件上传**: Multer + Sharp
- **日志**: Winston
- **测试**: Jest

#### 基础设施
- **容器化**: Docker + Docker Compose
- **反向代理**: Nginx
- **进程管理**: PM2
- **监控**: Prometheus + Grafana
- **日志收集**: ELK Stack
- **CI/CD**: GitHub Actions / GitLab CI

## 环境配置

### 开发环境

#### 系统要求
- **操作系统**: macOS 10.15+ / Ubuntu 18.04+ / Windows 10+
- **Node.js**: 18.0+
- **npm**: 8.0+
- **MySQL**: 8.0+
- **Redis**: 6.0+
- **Docker**: 20.0+ (可选)

#### 环境搭建步骤

1. **克隆项目**
```bash
git clone https://github.com/your-org/jiebanke.git
cd jiebanke
```

2. **安装依赖**
```bash
# 安装后端依赖
cd backend
npm install

# 安装前端依赖
cd ../frontend
npm install

# 安装管理后台依赖
cd ../admin
npm install
```

3. **配置环境变量**
```bash
# 复制环境变量模板
cp backend/.env.example backend/.env
cp frontend/.env.example frontend/.env
cp admin/.env.example admin/.env

# 编辑环境变量文件
vim backend/.env
```

4. **数据库初始化**
```bash
# 创建数据库
mysql -u root -p -e "CREATE DATABASE jiebanke_dev CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"

# 运行数据库迁移
cd backend
npm run migrate

# 导入初始数据
npm run seed
```

5. **启动服务**
```bash
# 启动后端服务
cd backend
npm run dev

# 启动前端服务
cd ../frontend
npm run dev

# 启动管理后台
cd ../admin
npm run dev
```

#### 开发环境配置文件

**backend/.env**
```env
# 应用配置
NODE_ENV=development
PORT=3000
APP_NAME=解班客
APP_VERSION=1.0.0

# 数据库配置
DB_HOST=localhost
DB_PORT=3306
DB_NAME=jiebanke_dev
DB_USER=root
DB_PASSWORD=your_password

# Redis配置
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0

# JWT配置
JWT_SECRET=your_jwt_secret_key
JWT_EXPIRES_IN=7d
JWT_REFRESH_EXPIRES_IN=30d

# 文件上传配置
UPLOAD_PATH=./uploads
MAX_FILE_SIZE=10485760
ALLOWED_FILE_TYPES=jpg,jpeg,png,gif,pdf,doc,docx

# 邮件配置
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your_email@gmail.com
SMTP_PASS=your_app_password

# 日志配置
LOG_LEVEL=debug
LOG_FILE=logs/app.log

# 第三方服务
WECHAT_APP_ID=your_wechat_app_id
WECHAT_APP_SECRET=your_wechat_app_secret
ALIPAY_APP_ID=your_alipay_app_id
ALIPAY_PRIVATE_KEY=your_alipay_private_key
```

**frontend/.env**
```env
# API配置
VITE_API_BASE_URL=http://localhost:3000/api/v1
VITE_UPLOAD_URL=http://localhost:3000/uploads

# 应用配置
VITE_APP_TITLE=解班客
VITE_APP_DESCRIPTION=宠物认领平台

# 第三方服务
VITE_WECHAT_APP_ID=your_wechat_app_id
VITE_MAP_API_KEY=your_map_api_key
```

### 测试环境

#### 环境特点
- **用途**: 功能测试、集成测试、性能测试
- **数据**: 使用测试数据，定期重置
- **配置**: 接近生产环境，但资源配置较低
- **访问**: 内网访问，需要VPN

#### 配置差异
```env
# backend/.env.test
NODE_ENV=test
DB_NAME=jiebanke_test
LOG_LEVEL=info
REDIS_DB=1

# 测试专用配置
TEST_MODE=true
MOCK_EXTERNAL_API=true
DISABLE_EMAIL=true
```

### 预生产环境

#### 环境特点
- **用途**: 生产前最后验证
- **数据**: 生产数据副本或仿真数据
- **配置**: 与生产环境完全一致
- **访问**: 限制访问，仅核心团队

#### 配置要求
- 与生产环境相同的硬件配置
- 相同的网络拓扑和安全配置
- 完整的监控和日志系统
- 自动化部署流程验证

### 生产环境

#### 硬件要求

**Web服务器**
- **CPU**: 4核心 2.4GHz+
- **内存**: 8GB+
- **存储**: 100GB SSD
- **网络**: 100Mbps+

**数据库服务器**
- **CPU**: 8核心 2.4GHz+
- **内存**: 16GB+
- **存储**: 500GB SSD (RAID 1)
- **网络**: 1Gbps+

**缓存服务器**
- **CPU**: 2核心 2.4GHz+
- **内存**: 4GB+
- **存储**: 50GB SSD
- **网络**: 100Mbps+

#### 软件环境
- **操作系统**: Ubuntu 20.04 LTS
- **Node.js**: 18.19.0 (LTS)
- **MySQL**: 8.0.35
- **Redis**: 6.2.14
- **Nginx**: 1.18.0
- **Docker**: 24.0.7
- **PM2**: 5.3.0

#### 生产环境配置

**backend/.env.production**
```env
# 应用配置
NODE_ENV=production
PORT=3000
APP_NAME=解班客
APP_VERSION=1.0.0

# 数据库配置
DB_HOST=db.internal.jiebanke.com
DB_PORT=3306
DB_NAME=jiebanke_prod
DB_USER=jiebanke_user
DB_PASSWORD=complex_secure_password

# Redis配置
REDIS_HOST=redis.internal.jiebanke.com
REDIS_PORT=6379
REDIS_PASSWORD=redis_secure_password
REDIS_DB=0

# JWT配置
JWT_SECRET=very_complex_jwt_secret_key_for_production
JWT_EXPIRES_IN=24h
JWT_REFRESH_EXPIRES_IN=7d

# 文件上传配置
UPLOAD_PATH=/var/www/jiebanke/uploads
MAX_FILE_SIZE=10485760
ALLOWED_FILE_TYPES=jpg,jpeg,png,gif,pdf,doc,docx

# 邮件配置
SMTP_HOST=smtp.exmail.qq.com
SMTP_PORT=465
SMTP_SECURE=true
SMTP_USER=noreply@jiebanke.com
SMTP_PASS=email_secure_password

# 日志配置
LOG_LEVEL=info
LOG_FILE=/var/log/jiebanke/app.log

# 安全配置
CORS_ORIGIN=https://www.jiebanke.com,https://admin.jiebanke.com
RATE_LIMIT_WINDOW=15
RATE_LIMIT_MAX=100

# 监控配置
ENABLE_METRICS=true
METRICS_PORT=9090
HEALTH_CHECK_PATH=/health

# 第三方服务
WECHAT_APP_ID=production_wechat_app_id
WECHAT_APP_SECRET=production_wechat_app_secret
ALIPAY_APP_ID=production_alipay_app_id
ALIPAY_PRIVATE_KEY=production_alipay_private_key
```

## 容器化部署

### Docker配置

#### 后端Dockerfile
```dockerfile
# backend/Dockerfile
FROM node:18-alpine AS builder

# 设置工作目录
WORKDIR /app

# 复制package文件
COPY package*.json ./

# 安装依赖
RUN npm ci --only=production && npm cache clean --force

# 复制源代码
COPY . .

# 构建应用
RUN npm run build

# 生产镜像
FROM node:18-alpine AS production

# 创建应用用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001

# 设置工作目录
WORKDIR /app

# 复制构建结果
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/package.json ./package.json

# 创建必要目录
RUN mkdir -p /app/logs /app/uploads && chown -R nodejs:nodejs /app

# 切换到应用用户
USER nodejs

# 暴露端口
EXPOSE 3000

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node healthcheck.js

# 启动应用
CMD ["node", "dist/server.js"]
```

#### 前端Dockerfile
```dockerfile
# frontend/Dockerfile
FROM node:18-alpine AS builder

# 设置工作目录
WORKDIR /app

# 复制package文件
COPY package*.json ./

# 安装依赖
RUN npm ci

# 复制源代码
COPY . .

# 构建应用
RUN npm run build

# 生产镜像
FROM nginx:alpine AS production

# 复制构建结果
COPY --from=builder /app/dist /usr/share/nginx/html

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

# 暴露端口
EXPOSE 80

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

#### Docker Compose配置
```yaml
# docker-compose.yml
version: '3.8'

services:
  # 数据库服务
  mysql:
    image: mysql:8.0
    container_name: jiebanke-mysql
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASSWORD}
    volumes:
      - mysql_data:/var/lib/mysql
      - ./mysql/init:/docker-entrypoint-initdb.d
      - ./mysql/conf:/etc/mysql/conf.d
    ports:
      - "3306:3306"
    networks:
      - jiebanke-network
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      timeout: 20s
      retries: 10

  # Redis服务
  redis:
    image: redis:6-alpine
    container_name: jiebanke-redis
    restart: unless-stopped
    command: redis-server --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis_data:/data
      - ./redis/redis.conf:/usr/local/etc/redis/redis.conf
    ports:
      - "6379:6379"
    networks:
      - jiebanke-network
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      timeout: 3s
      retries: 5

  # 后端服务
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: jiebanke-backend
    restart: unless-stopped
    environment:
      - NODE_ENV=production
    env_file:
      - ./backend/.env.production
    volumes:
      - ./uploads:/app/uploads
      - ./logs:/app/logs
    ports:
      - "3000:3000"
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - jiebanke-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      timeout: 10s
      retries: 3

  # 前端服务
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    container_name: jiebanke-frontend
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/ssl:/etc/nginx/ssl
      - ./nginx/conf.d:/etc/nginx/conf.d
    depends_on:
      - backend
    networks:
      - jiebanke-network

  # 管理后台
  admin:
    build:
      context: ./admin
      dockerfile: Dockerfile
    container_name: jiebanke-admin
    restart: unless-stopped
    ports:
      - "8080:80"
    depends_on:
      - backend
    networks:
      - jiebanke-network

volumes:
  mysql_data:
  redis_data:

networks:
  jiebanke-network:
    driver: bridge
```

### Kubernetes部署

#### 命名空间配置
```yaml
# k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: jiebanke
  labels:
    name: jiebanke
```

#### ConfigMap配置
```yaml
# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: jiebanke-config
  namespace: jiebanke
data:
  NODE_ENV: "production"
  LOG_LEVEL: "info"
  CORS_ORIGIN: "https://www.jiebanke.com,https://admin.jiebanke.com"
  RATE_LIMIT_WINDOW: "15"
  RATE_LIMIT_MAX: "100"
```

#### Secret配置
```yaml
# k8s/secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: jiebanke-secret
  namespace: jiebanke
type: Opaque
data:
  DB_PASSWORD: <base64-encoded-password>
  JWT_SECRET: <base64-encoded-jwt-secret>
  REDIS_PASSWORD: <base64-encoded-redis-password>
```

#### 后端部署配置
```yaml
# k8s/backend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jiebanke-backend
  namespace: jiebanke
spec:
  replicas: 3
  selector:
    matchLabels:
      app: jiebanke-backend
  template:
    metadata:
      labels:
        app: jiebanke-backend
    spec:
      containers:
      - name: backend
        image: jiebanke/backend:latest
        ports:
        - containerPort: 3000
        env:
        - name: NODE_ENV
          valueFrom:
            configMapKeyRef:
              name: jiebanke-config
              key: NODE_ENV
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: jiebanke-secret
              key: DB_PASSWORD
        - name: JWT_SECRET
          valueFrom:
            secretKeyRef:
              name: jiebanke-secret
              key: JWT_SECRET
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 5
        volumeMounts:
        - name: uploads
          mountPath: /app/uploads
        - name: logs
          mountPath: /app/logs
      volumes:
      - name: uploads
        persistentVolumeClaim:
          claimName: jiebanke-uploads-pvc
      - name: logs
        persistentVolumeClaim:
          claimName: jiebanke-logs-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: jiebanke-backend-service
  namespace: jiebanke
spec:
  selector:
    app: jiebanke-backend
  ports:
  - protocol: TCP
    port: 80
    targetPort: 3000
  type: ClusterIP
```

#### Ingress配置
```yaml
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: jiebanke-ingress
  namespace: jiebanke
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "10m"
spec:
  tls:
  - hosts:
    - www.jiebanke.com
    - admin.jiebanke.com
    secretName: jiebanke-tls
  rules:
  - host: www.jiebanke.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: jiebanke-backend-service
            port:
              number: 80
      - path: /
        pathType: Prefix
        backend:
          service:
            name: jiebanke-frontend-service
            port:
              number: 80
  - host: admin.jiebanke.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: jiebanke-backend-service
            port:
              number: 80
      - path: /
        pathType: Prefix
        backend:
          service:
            name: jiebanke-admin-service
            port:
              number: 80
```

## CI/CD流程

### GitHub Actions配置

#### 主工作流
```yaml
# .github/workflows/main.yml
name: CI/CD Pipeline

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

env:
  NODE_VERSION: '18'
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  # 代码质量检查
  lint-and-test:
    runs-on: ubuntu-latest
    
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_ROOT_PASSWORD: test_password
          MYSQL_DATABASE: jiebanke_test
        ports:
          - 3306:3306
        options: >-
          --health-cmd="mysqladmin ping"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=3
      
      redis:
        image: redis:6-alpine
        ports:
          - 6379:6379
        options: >-
          --health-cmd="redis-cli ping"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=3
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: ${{ env.NODE_VERSION }}
        cache: 'npm'
        cache-dependency-path: |
          backend/package-lock.json
          frontend/package-lock.json
          admin/package-lock.json
    
    - name: Install backend dependencies
      run: |
        cd backend
        npm ci
    
    - name: Install frontend dependencies
      run: |
        cd frontend
        npm ci
    
    - name: Install admin dependencies
      run: |
        cd admin
        npm ci
    
    - name: Run backend linting
      run: |
        cd backend
        npm run lint
    
    - name: Run frontend linting
      run: |
        cd frontend
        npm run lint
    
    - name: Run admin linting
      run: |
        cd admin
        npm run lint
    
    - name: Run backend tests
      run: |
        cd backend
        npm run test:coverage
      env:
        NODE_ENV: test
        DB_HOST: localhost
        DB_PORT: 3306
        DB_NAME: jiebanke_test
        DB_USER: root
        DB_PASSWORD: test_password
        REDIS_HOST: localhost
        REDIS_PORT: 6379
    
    - name: Run frontend tests
      run: |
        cd frontend
        npm run test:coverage
    
    - name: Upload coverage reports
      uses: codecov/codecov-action@v3
      with:
        files: |
          backend/coverage/lcov.info
          frontend/coverage/lcov.info
        flags: unittests
        name: codecov-umbrella

  # 安全扫描
  security-scan:
    runs-on: ubuntu-latest
    needs: lint-and-test
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Run Trivy vulnerability scanner
      uses: aquasecurity/trivy-action@master
      with:
        scan-type: 'fs'
        scan-ref: '.'
        format: 'sarif'
        output: 'trivy-results.sarif'
    
    - name: Upload Trivy scan results
      uses: github/codeql-action/upload-sarif@v2
      with:
        sarif_file: 'trivy-results.sarif'

  # 构建和推送镜像
  build-and-push:
    runs-on: ubuntu-latest
    needs: [lint-and-test, security-scan]
    if: github.ref == 'refs/heads/main'
    
    permissions:
      contents: read
      packages: write
    
    strategy:
      matrix:
        service: [backend, frontend, admin]
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Log in to Container Registry
      uses: docker/login-action@v3
      with:
        registry: ${{ env.REGISTRY }}
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}
    
    - name: Extract metadata
      id: meta
      uses: docker/metadata-action@v5
      with:
        images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.service }}
        tags: |
          type=ref,event=branch
          type=ref,event=pr
          type=sha,prefix={{branch}}-
          type=raw,value=latest,enable={{is_default_branch}}
    
    - name: Build and push Docker image
      uses: docker/build-push-action@v5
      with:
        context: ./${{ matrix.service }}
        push: true
        tags: ${{ steps.meta.outputs.tags }}
        labels: ${{ steps.meta.outputs.labels }}
        cache-from: type=gha
        cache-to: type=gha,mode=max

  # 部署到测试环境
  deploy-test:
    runs-on: ubuntu-latest
    needs: build-and-push
    if: github.ref == 'refs/heads/develop'
    environment: test
    
    steps:
    - name: Deploy to test environment
      run: |
        echo "Deploying to test environment..."
        # 这里添加部署到测试环境的脚本

  # 部署到生产环境
  deploy-prod:
    runs-on: ubuntu-latest
    needs: build-and-push
    if: github.ref == 'refs/heads/main'
    environment: production
    
    steps:
    - name: Deploy to production environment
      run: |
        echo "Deploying to production environment..."
        # 这里添加部署到生产环境的脚本
```

#### 部署脚本
```yaml
# .github/workflows/deploy.yml
name: Deploy to Production

on:
  workflow_run:
    workflows: ["CI/CD Pipeline"]
    types:
      - completed
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    environment: production
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Setup kubectl
      uses: azure/setup-kubectl@v3
      with:
        version: 'v1.28.0'
    
    - name: Configure kubectl
      run: |
        echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > kubeconfig
        export KUBECONFIG=kubeconfig
    
    - name: Deploy to Kubernetes
      run: |
        export KUBECONFIG=kubeconfig
        
        # 更新镜像标签
        kubectl set image deployment/jiebanke-backend backend=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/backend:${{ github.sha }} -n jiebanke
        kubectl set image deployment/jiebanke-frontend frontend=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/frontend:${{ github.sha }} -n jiebanke
        kubectl set image deployment/jiebanke-admin admin=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/admin:${{ github.sha }} -n jiebanke
        
        # 等待部署完成
        kubectl rollout status deployment/jiebanke-backend -n jiebanke --timeout=300s
        kubectl rollout status deployment/jiebanke-frontend -n jiebanke --timeout=300s
        kubectl rollout status deployment/jiebanke-admin -n jiebanke --timeout=300s
    
    - name: Run smoke tests
      run: |
        # 运行冒烟测试
        curl -f https://www.jiebanke.com/health || exit 1
        curl -f https://admin.jiebanke.com/health || exit 1
    
    - name: Notify deployment success
      uses: 8398a7/action-slack@v3
      with:
        status: success
        text: '🎉 Production deployment successful!'
      env:
        SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
```

### 部署脚本

#### 自动化部署脚本
```bash
#!/bin/bash
# scripts/deploy.sh

set -e

# 配置变量
ENVIRONMENT=${1:-production}
VERSION=${2:-latest}
COMPOSE_FILE="docker-compose.${ENVIRONMENT}.yml"

echo "🚀 开始部署到 ${ENVIRONMENT} 环境..."

# 检查环境
if [ ! -f "${COMPOSE_FILE}" ]; then
    echo "❌ 找不到配置文件: ${COMPOSE_FILE}"
    exit 1
fi

# 拉取最新镜像
echo "📦 拉取最新镜像..."
docker-compose -f ${COMPOSE_FILE} pull

# 停止旧服务
echo "🛑 停止旧服务..."
docker-compose -f ${COMPOSE_FILE} down

# 备份数据库
echo "💾 备份数据库..."
./scripts/backup-db.sh

# 启动新服务
echo "🔄 启动新服务..."
docker-compose -f ${COMPOSE_FILE} up -d

# 等待服务启动
echo "⏳ 等待服务启动..."
sleep 30

# 健康检查
echo "🔍 执行健康检查..."
./scripts/health-check.sh

# 运行数据库迁移
echo "🗄️ 运行数据库迁移..."
docker-compose -f ${COMPOSE_FILE} exec backend npm run migrate

echo "✅ 部署完成!"

# 发送通知
./scripts/notify-deployment.sh ${ENVIRONMENT} ${VERSION}
```

#### 健康检查脚本
```bash
#!/bin/bash
# scripts/health-check.sh

set -e

BACKEND_URL="http://localhost:3000"
FRONTEND_URL="http://localhost:80"
ADMIN_URL="http://localhost:8080"

echo "🔍 开始健康检查..."

# 检查后端服务
echo "检查后端服务..."
if curl -f "${BACKEND_URL}/health" > /dev/null 2>&1; then
    echo "✅ 后端服务正常"
else
    echo "❌ 后端服务异常"
    exit 1
fi

# 检查前端服务
echo "检查前端服务..."
if curl -f "${FRONTEND_URL}" > /dev/null 2>&1; then
    echo "✅ 前端服务正常"
else
    echo "❌ 前端服务异常"
    exit 1
fi

# 检查管理后台
echo "检查管理后台..."
if curl -f "${ADMIN_URL}" > /dev/null 2>&1; then
    echo "✅ 管理后台正常"
else
    echo "❌ 管理后台异常"
    exit 1
fi

# 检查数据库连接
echo "检查数据库连接..."
if docker-compose exec backend npm run db:check > /dev/null 2>&1; then
    echo "✅ 数据库连接正常"
else
    echo "❌ 数据库连接异常"
    exit 1
fi

# 检查Redis连接
echo "检查Redis连接..."
if docker-compose exec backend npm run redis:check > /dev/null 2>&1; then
    echo "✅ Redis连接正常"
else
    echo "❌ Redis连接异常"
    exit 1
fi

echo "✅ 所有健康检查通过!"
```

#### 数据库备份脚本
```bash
#!/bin/bash
# scripts/backup-db.sh

set -e

# 配置变量
DB_HOST=${DB_HOST:-localhost}
DB_PORT=${DB_PORT:-3306}
DB_NAME=${DB_NAME:-jiebanke_prod}
DB_USER=${DB_USER:-root}
DB_PASSWORD=${DB_PASSWORD}
BACKUP_DIR="/var/backups/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="${BACKUP_DIR}/${DB_NAME}_${DATE}.sql"

echo "💾 开始备份数据库..."

# 创建备份目录
mkdir -p ${BACKUP_DIR}

# 执行备份
mysqldump \
    --host=${DB_HOST} \
    --port=${DB_PORT} \
    --user=${DB_USER} \
    --password=${DB_PASSWORD} \
    --single-transaction \
    --routines \
    --triggers \
    --events \
    --hex-blob \
    ${DB_NAME} > ${BACKUP_FILE}

# 压缩备份文件
gzip ${BACKUP_FILE}

echo "✅ 数据库备份完成: ${BACKUP_FILE}.gz"

# 清理旧备份（保留30天）
find ${BACKUP_DIR} -name "*.sql.gz" -mtime +30 -delete

echo "🧹 清理旧备份完成"
```

## 监控和日志

### Prometheus配置

#### Prometheus配置文件
```yaml
# monitoring/prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - "rules/*.yml"

alerting:
  alertmanagers:
    - static_configs:
        - targets:
          - alertmanager:9093

scrape_configs:
  # Prometheus自监控
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  # Node Exporter
  - job_name: 'node'
    static_configs:
      - targets: ['node-exporter:9100']

  # 应用监控
  - job_name: 'jiebanke-backend'
    static_configs:
      - targets: ['backend:9090']
    metrics_path: '/metrics'
    scrape_interval: 10s

  # MySQL监控
  - job_name: 'mysql'
    static_configs:
      - targets: ['mysql-exporter:9104']

  # Redis监控
  - job_name: 'redis'
    static_configs:
      - targets: ['redis-exporter:9121']

  # Nginx监控
  - job_name: 'nginx'
    static_configs:
      - targets: ['nginx-exporter:9113']
```

#### 告警规则
```yaml
# monitoring/rules/alerts.yml
groups:
- name: jiebanke.rules
  rules:
  # 服务可用性告警
  - alert: ServiceDown
    expr: up == 0
    for: 1m
    labels:
      severity: critical
    annotations:
      summary: "Service {{ $labels.job }} is down"
      description: "Service {{ $labels.job }} has been down for more than 1 minute."

  # 高错误率告警
  - alert: HighErrorRate
    expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.1
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "High error rate detected"
      description: "Error rate is {{ $value }} errors per second."

  # 高响应时间告警
  - alert: HighResponseTime
    expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 2
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "High response time detected"
      description: "95th percentile response time is {{ $value }} seconds."

  # 内存使用率告警
  - alert: HighMemoryUsage
    expr: (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes > 0.8
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "High memory usage"
      description: "Memory usage is {{ $value | humanizePercentage }}."

  # CPU使用率告警
  - alert: HighCPUUsage
    expr: 100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "High CPU usage"
      description: "CPU usage is {{ $value }}%."

  # 磁盘空间告警
  - alert: DiskSpaceLow
    expr: (node_filesystem_avail_bytes / node_filesystem_size_bytes) < 0.1
    for: 1m
    labels:
      severity: critical
    annotations:
      summary: "Disk space low"
      description: "Disk space is {{ $value | humanizePercentage }} full."

  # 数据库连接告警
  - alert: DatabaseConnectionHigh
    expr: mysql_global_status_threads_connected / mysql_global_variables_max_connections > 0.8
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "High database connections"
      description: "Database connections are {{ $value | humanizePercentage }} of maximum."
```

### Grafana仪表板

#### 系统概览仪表板
```json
{
  "dashboard": {
    "id": null,
    "title": "解班客系统概览",
    "tags": ["jiebanke"],
    "timezone": "browser",
    "panels": [
      {
        "id": 1,
        "title": "服务状态",
        "type": "stat",
        "targets": [
          {
            "expr": "up{job=~\"jiebanke-.*\"}",
            "legendFormat": "{{ job }}"
          }
        ],
        "fieldConfig": {
          "defaults": {
            "mappings": [
              {
                "options": {
                  "0": {
                    "text": "DOWN",
                    "color": "red"
                  },
                  "1": {
                    "text": "UP",
                    "color": "green"
                  }
                },
                "type": "value"
              }
            ]
          }
        }
      },
      {
        "id": 2,
        "title": "请求速率",
        "type": "graph",
        "targets": [
          {
            "expr": "rate(http_requests_total[5m])",
            "legendFormat": "{{ method }} {{ status }}"
          }
        ]
      },
      {
        "id": 3,
        "title": "响应时间",
        "type": "graph",
        "targets": [
          {
            "expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))",
            "legendFormat": "95th percentile"
          },
          {
            "expr": "histogram_quantile(0.50, rate(http_request_duration_seconds_bucket[5m]))",
            "legendFormat": "50th percentile"
          }
        ]
      },
      {
        "id": 4,
        "title": "系统资源",
        "type": "graph",
        "targets": [
          {
            "expr": "100 - (avg(irate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100)",
            "legendFormat": "CPU使用率"
          },
          {
            "expr": "(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100",
            "legendFormat": "内存使用率"
          }
        ]
      }
    ],
    "time": {
      "from": "now-1h",
      "to": "now"
    },
    "refresh": "5s"
  }
}
```

### ELK Stack配置

#### Elasticsearch配置
```yaml
# elk/elasticsearch.yml
cluster.name: jiebanke-logs
node.name: elasticsearch-1
network.host: 0.0.0.0
http.port: 9200
discovery.type: single-node

# 内存设置
bootstrap.memory_lock: true

# 索引设置
index.number_of_shards: 1
index.number_of_replicas: 0

# 安全设置
xpack.security.enabled: false
```

#### Logstash配置
```ruby
# elk/logstash.conf
input {
  beats {
    port => 5044
  }
}

filter {
  if [fields][service] == "jiebanke-backend" {
    json {
      source => "message"
    }
    
    date {
      match => [ "timestamp", "ISO8601" ]
    }
    
    mutate {
      add_field => { "service" => "backend" }
    }
  }
  
  if [fields][service] == "nginx" {
    grok {
      match => { 
        "message" => "%{NGINXACCESS}" 
      }
    }
    
    date {
      match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
    }
    
    mutate {
      add_field => { "service" => "nginx" }
      convert => { "response" => "integer" }
      convert => { "bytes" => "integer" }
    }
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "jiebanke-logs-%{+YYYY.MM.dd}"
  }
  
  stdout {
    codec => rubydebug
  }
}
```

#### Filebeat配置
```yaml
# elk/filebeat.yml
filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /var/log/jiebanke/*.log
  fields:
    service: jiebanke-backend
  fields_under_root: true
  multiline.pattern: '^\d{4}-\d{2}-\d{2}'
  multiline.negate: true
  multiline.match: after

- type: log
  enabled: true
  paths:
    - /var/log/nginx/access.log
  fields:
    service: nginx
  fields_under_root: true

output.logstash:
  hosts: ["logstash:5044"]

processors:
- add_host_metadata:
    when.not.contains.tags: forwarded
```

## 安全配置

### SSL/TLS配置

#### Nginx SSL配置
```nginx
# nginx/conf.d/ssl.conf
server {
    listen 443 ssl http2;
    server_name www.jiebanke.com;
    
    # SSL证书配置
    ssl_certificate /etc/nginx/ssl/jiebanke.com.crt;
    ssl_certificate_key /etc/nginx/ssl/jiebanke.com.key;
    
    # SSL安全配置
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    
    # HSTS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    
    # 其他安全头
    add_header X-Frame-Options DENY always;
    add_header X-Content-Type-Options nosniff always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    
    # CSP
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https:; media-src 'self'; object-src 'none'; child-src 'self'; frame-ancestors 'none'; form-action 'self'; base-uri 'self';" always;
    
    location / {
        root /usr/share/nginx/html;
        index index.html index.htm;
        try_files $uri $uri/ /index.html;
    }
    
    location /api {
        proxy_pass http://backend:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # 安全头
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        
        # 超时设置
        proxy_connect_timeout 30s;
        proxy_send_timeout 30s;
        proxy_read_timeout 30s;
    }
}

# HTTP重定向到HTTPS
server {
    listen 80;
    server_name www.jiebanke.com;
    return 301 https://$server_name$request_uri;
}
```

### 防火墙配置

#### UFW配置
```bash
#!/bin/bash
# scripts/setup-firewall.sh

# 重置防火墙规则
ufw --force reset

# 默认策略
ufw default deny incoming
ufw default allow outgoing

# SSH访问
ufw allow ssh

# HTTP/HTTPS
ufw allow 80/tcp
ufw allow 443/tcp

# 数据库（仅内网）
ufw allow from 10.0.0.0/8 to any port 3306
ufw allow from 172.16.0.0/12 to any port 3306
ufw allow from 192.168.0.0/16 to any port 3306

# Redis（仅内网）
ufw allow from 10.0.0.0/8 to any port 6379
ufw allow from 172.16.0.0/12 to any port 6379
ufw allow from 192.168.0.0/16 to any port 6379

# 监控端口（仅内网）
ufw allow from 10.0.0.0/8 to any port 9090
ufw allow from 172.16.0.0/12 to any port 9090
ufw allow from 192.168.0.0/16 to any port 9090

# 启用防火墙
ufw --force enable

echo "防火墙配置完成"
```

### 安全扫描

#### 漏洞扫描脚本
```bash
#!/bin/bash
# scripts/security-scan.sh

echo "🔍 开始安全扫描..."

# 依赖漏洞扫描
echo "📦 扫描依赖漏洞..."
cd backend && npm audit --audit-level moderate
cd ../frontend && npm audit --audit-level moderate
cd ../admin && npm audit --audit-level moderate

# 容器镜像扫描
echo "🐳 扫描容器镜像..."
trivy image jiebanke/backend:latest
trivy image jiebanke/frontend:latest
trivy image jiebanke/admin:latest

# 文件系统扫描
echo "📁 扫描文件系统..."
trivy fs .

# 配置文件安全检查
echo "⚙️ 检查配置文件..."
# 检查是否有硬编码的密码
grep -r "password\|secret\|key" --include="*.js" --include="*.json" --include="*.yml" . | grep -v node_modules | grep -v ".git"

echo "✅ 安全扫描完成"
```

## 性能优化

### 数据库优化

#### MySQL配置优化
```ini
# mysql/conf/my.cnf
[mysqld]
# 基础配置
port = 3306
socket = /var/run/mysqld/mysqld.sock
pid-file = /var/run/mysqld/mysqld.pid
datadir = /var/lib/mysql

# 字符集
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

# 内存配置
innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
innodb_log_buffer_size = 16M
key_buffer_size = 256M
max_connections = 200
thread_cache_size = 50

# 查询缓存
query_cache_type = 1
query_cache_size = 128M
query_cache_limit = 2M

# 慢查询日志
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2

# 二进制日志
log-bin = mysql-bin
binlog_format = ROW
expire_logs_days = 7

# InnoDB配置
innodb_file_per_table = 1
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
```

#### 数据库索引优化
```sql
-- 用户表索引
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_status ON users(status);
CREATE INDEX idx_users_created_at ON users(created_at);

-- 动物表索引
CREATE INDEX idx_animals_status ON animals(status);
CREATE INDEX idx_animals_type ON animals(type);
CREATE INDEX idx_animals_location ON animals(location);
CREATE INDEX idx_animals_created_at ON animals(created_at);

-- 认领记录索引
CREATE INDEX idx_adoptions_user_id ON adoptions(user_id);
CREATE INDEX idx_adoptions_animal_id ON adoptions(animal_id);
CREATE INDEX idx_adoptions_status ON adoptions(status);
CREATE INDEX idx_adoptions_created_at ON adoptions(created_at);

-- 复合索引
CREATE INDEX idx_animals_status_type ON animals(status, type);
CREATE INDEX idx_adoptions_user_status ON adoptions(user_id, status);
```

### 缓存策略

#### Redis缓存配置
```redis
# redis/redis.conf
# 内存配置
maxmemory 2gb
maxmemory-policy allkeys-lru

# 持久化配置
save 900 1
save 300 10
save 60 10000

# AOF配置
appendonly yes
appendfsync everysec

# 网络配置
timeout 300
tcp-keepalive 300

# 安全配置
requirepass your_redis_password
```

#### 应用层缓存策略
```javascript
// utils/cache.js
const redis = require('redis');
const client = redis.createClient({
  host: process.env.REDIS_HOST,
  port: process.env.REDIS_PORT,
  password: process.env.REDIS_PASSWORD
});

class CacheManager {
  // 用户信息缓存
  async getUserCache(userId) {
    const key = `user:${userId}`;
    const cached = await client.get(key);
    
    if (cached) {
      return JSON.parse(cached);
    }
    
    const user = await User.findById(userId);
    if (user) {
      await client.setex(key, 3600, JSON.stringify(user)); // 1小时过期
    }
    
    return user;
  }
  
  // 动物列表缓存
  async getAnimalListCache(filters) {
    const key = `animals:${JSON.stringify(filters)}`;
    const cached = await client.get(key);
    
    if (cached) {
      return JSON.parse(cached);
    }
    
    const animals = await Animal.find(filters);
    await client.setex(key, 600, JSON.stringify(animals)); // 10分钟过期
    
    return animals;
  }
  
  // 清除用户相关缓存
  async clearUserCache(userId) {
    const keys = await client.keys(`user:${userId}*`);
    if (keys.length > 0) {
      await client.del(keys);
    }
  }
  
  // 清除动物相关缓存
  async clearAnimalCache() {
    const keys = await client.keys('animals:*');
    if (keys.length > 0) {
      await client.del(keys);
    }
  }
}

module.exports = new CacheManager();
```

### CDN配置

#### 静态资源CDN
```javascript
// config/cdn.js
const CDN_CONFIG = {
  development: {
    baseUrl: 'http://localhost:3000',
    staticUrl: 'http://localhost:3000/static'
  },
  production: {
    baseUrl: 'https://cdn.jiebanke.com',
    staticUrl: 'https://static.jiebanke.com'
  }
};

function getCDNUrl(path) {
  const config = CDN_CONFIG[process.env.NODE_ENV] || CDN_CONFIG.development;
  return `${config.staticUrl}${path}`;
}

module.exports = {
  getCDNUrl,
  CDN_CONFIG
};
```

## 故障排除

### 常见问题

#### 1. 服务启动失败
**问题**: 容器启动失败或服务无法访问
**排查步骤**:
```bash
# 查看容器状态
docker ps -a

# 查看容器日志
docker logs container_name

# 查看系统资源
docker stats

# 检查端口占用
netstat -tlnp | grep :3000
```

#### 2. 数据库连接问题
**问题**: 应用无法连接数据库
**排查步骤**:
```bash
# 检查数据库服务状态
docker exec mysql mysqladmin ping

# 查看数据库日志
docker logs mysql

# 测试数据库连接
mysql -h localhost -u root -p -e "SELECT 1"

# 检查网络连接
docker network ls
docker network inspect network_name
```

#### 3. 性能问题
**问题**: 应用响应缓慢
**排查步骤**:
```bash
# 查看系统负载
top
htop

# 查看内存使用
free -h

# 查看磁盘IO
iostat -x 1

# 查看网络连接
ss -tuln

# 分析慢查询
mysql -e "SELECT * FROM information_schema.processlist WHERE time > 10"
```

### 监控告警处理

#### 告警响应流程
1. **接收告警**: 通过邮件、短信、钉钉等方式接收告警
2. **初步评估**: 评估告警严重程度和影响范围
3. **快速响应**: 根据告警类型执行相应的应急处理
4. **问题定位**: 使用监控工具和日志分析问题根因
5. **问题修复**: 实施修复方案并验证效果
6. **总结改进**: 记录问题处理过程并改进监控策略

#### 常见告警处理

**服务不可用告警**
```bash
# 检查服务状态
kubectl get pods -n jiebanke

# 查看Pod日志
kubectl logs -f pod_name -n jiebanke

# 重启服务
kubectl rollout restart deployment/jiebanke-backend -n jiebanke
```

**高内存使用告警**
```bash
# 查看内存使用详情
kubectl top pods -n jiebanke

# 查看Pod资源限制
kubectl describe pod pod_name -n jiebanke

# 调整资源限制
kubectl patch deployment jiebanke-backend -p '{"spec":{"template":{"spec":{"containers":[{"name":"backend","resources":{"limits":{"memory":"1Gi"}}}]}}}}'
```

## 总结

本文档详细描述了解班客平台的系统集成和部署方案，涵盖了从开发环境搭建到生产环境部署的完整流程。通过采用现代化的容器化技术、自动化CI/CD流程、完善的监控告警系统，确保了系统的高可用性、可扩展性和可维护性。

关键特性包括:
- **容器化部署**: 使用Docker和Kubernetes实现标准化部署
- **自动化CI/CD**: 通过GitHub Actions实现自动化测试和部署
- **完善监控**: 使用Prometheus、Grafana和ELK Stack实现全方位监控
- **安全防护**: 实施多层安全防护措施
- **性能优化**: 通过缓存、CDN等技术提升系统性能

未来将继续完善部署流程，增加更多自动化工具和监控指标，为平台的稳定运行提供更强有力的保障。