Files
li 11c177a432 feat: 第一阶段基础框架搭建
后端 (Laravel 10 + Sanctum):
- RBAC 四层权限系统 (users→roles→permissions→menus)
- 门店隔离中间件 (BelongsToStore Trait + StoreIsolation Middleware)
- 操作日志中间件 (自动记录写操作)
- 权限检查中间件 (CheckPermission)
- 16张数据库表迁移 (系统基础+RBAC+字典+配置)
- 11个 Eloquent Model
- Auth API (登录/登出/用户信息)
- 系统设置模块 CRUD (门店/部门/职务/用户/角色/菜单/权限/字典/日志)
- 45条 RESTful API 路由
- InitSeeder 初始数据 (超管/角色/76权限/31菜单)

前端 (Vue 3 + Element Plus + Vite):
- Axios 请求封装 + Token 注入
- Pinia 状态管理 (user + permission store)
- 动态路由 (服务端菜单→前端路由自动生成)
- 后台布局 (侧边栏+顶栏+主内容区)
- 登录页 + 仪表盘首页
- 系统设置 7 个 CRUD 页面

技术方案文档 (7卷):
- 技术总览/数据库设计/API规范/RBAC设计/模块详设/小程序设计/部署方案
2026-03-13 19:52:09 +08:00

15 KiB
Raw Permalink Blame History

宫中有喜 — 部署方案

一、部署架构

┌─────────────────────────────────────────┐
│              CDN (可选)                   │
│         静态资源加速                      │
└──────────────────┬──────────────────────┘
                   │
┌──────────────────▼──────────────────────┐
│          Nginx (反向代理)                 │
│   80/443 → 静态资源 / API / WebSocket    │
└────┬──────────────┬─────────────────────┘
     │              │
     ▼              ▼
┌─────────┐  ┌──────────────┐
│ Vue SPA │  │  PHP-FPM      │
│ (静态)   │  │  Laravel App  │
└─────────┘  └──────┬───────┘
                    │
        ┌───────────┼───────────┐
        ▼           ▼           ▼
  ┌──────────┐ ┌────────┐ ┌─────────┐
  │ MySQL 8  │ │ Redis  │ │ Storage │
  │          │ │        │ │ (文件)   │
  └──────────┘ └────────┘ └─────────┘

二、环境要求

组件 最低版本 推荐
OS Ubuntu 20.04 / CentOS 8 Ubuntu 22.04
PHP 8.1 8.2
MySQL 8.0 8.0
Redis 6.0 7.0
Nginx 1.18 1.24
Node.js 16 18 LTS
Composer 2.x 2.x
Docker 20.10 24.x
Docker Compose 2.x 2.x

PHP 扩展

php-fpm php-mysql php-redis php-mbstring php-xml
php-curl php-gd php-zip php-bcmath php-intl
php-opcache php-fileinfo

三、Docker Compose 部署

3.1 docker-compose.yml

version: '3.8'

services:
  # Nginx 反向代理
  nginx:
    image: nginx:1.24-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./docker/nginx/conf.d:/etc/nginx/conf.d
      - ./docker/nginx/ssl:/etc/nginx/ssl
      - ./frontend/dist:/var/www/frontend
      - ./backend/public:/var/www/backend/public
      - ./backend/storage/app/public:/var/www/backend/storage/app/public
    depends_on:
      - php
    networks:
      - app-network
    restart: unless-stopped

  # PHP-FPM
  php:
    build:
      context: ./docker/php
      dockerfile: Dockerfile
    volumes:
      - ./backend:/var/www/backend
    depends_on:
      - mysql
      - redis
    networks:
      - app-network
    restart: unless-stopped

  # Laravel 队列 Worker
  queue:
    build:
      context: ./docker/php
      dockerfile: Dockerfile
    command: php artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
    volumes:
      - ./backend:/var/www/backend
    depends_on:
      - mysql
      - redis
    networks:
      - app-network
    restart: unless-stopped

  # Laravel 定时任务
  scheduler:
    build:
      context: ./docker/php
      dockerfile: Dockerfile
    command: /bin/sh -c "while true; do php /var/www/backend/artisan schedule:run --verbose --no-interaction >> /dev/null 2>&1; sleep 60; done"
    volumes:
      - ./backend:/var/www/backend
    depends_on:
      - mysql
      - redis
    networks:
      - app-network
    restart: unless-stopped

  # MySQL
  mysql:
    image: mysql:8.0
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_USER: ${DB_USERNAME}
      MYSQL_PASSWORD: ${DB_PASSWORD}
    volumes:
      - mysql-data:/var/lib/mysql
      - ./docker/mysql/my.cnf:/etc/mysql/conf.d/my.cnf
      - ./docker/mysql/init:/docker-entrypoint-initdb.d
    networks:
      - app-network
    restart: unless-stopped

  # Redis
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    command: redis-server --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis-data:/data
    networks:
      - app-network
    restart: unless-stopped

volumes:
  mysql-data:
  redis-data:

networks:
  app-network:
    driver: bridge

3.2 PHP Dockerfile

# docker/php/Dockerfile
FROM php:8.2-fpm-alpine

# 安装扩展
RUN apk add --no-cache \
    libpng-dev libjpeg-turbo-dev freetype-dev \
    libzip-dev icu-dev oniguruma-dev \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install \
    pdo_mysql mbstring xml curl gd zip bcmath intl opcache pcntl \
    && pecl install redis \
    && docker-php-ext-enable redis

# Composer
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

# PHP 配置
COPY php.ini /usr/local/etc/php/conf.d/custom.ini

WORKDIR /var/www/backend

# 权限
RUN chown -R www-data:www-data /var/www/backend

USER www-data

EXPOSE 9000
CMD ["php-fpm"]

3.3 PHP 配置

; docker/php/php.ini
upload_max_filesize = 20M
post_max_size = 25M
memory_limit = 256M
max_execution_time = 60
date.timezone = Asia/Shanghai

; OPcache
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.validate_timestamps=0
opcache.revalidate_freq=0

3.4 Nginx 配置

# docker/nginx/conf.d/default.conf

# 后台管理 SPA
server {
    listen 80;
    server_name admin.example.com;

    root /var/www/frontend;
    index index.html;

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

    # 静态资源缓存
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # Gzip
    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml;
    gzip_min_length 1024;
}

# API 服务
server {
    listen 80;
    server_name api.example.com;

    root /var/www/backend/public;
    index index.php;

    # Laravel 路由
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # PHP-FPM
    location ~ \.php$ {
        fastcgi_pass php:9000;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_read_timeout 60;
    }

    # 文件上传大小
    client_max_body_size 25m;

    # 静态资源
    location /storage {
        alias /var/www/backend/storage/app/public;
        expires 30d;
    }

    # 禁止访问隐藏文件
    location ~ /\. {
        deny all;
    }
}

3.5 MySQL 配置

# docker/mysql/my.cnf
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
default-time-zone = '+08:00'

# InnoDB
innodb_buffer_pool_size = 512M
innodb_log_file_size = 128M
innodb_flush_log_at_trx_commit = 2

# 连接
max_connections = 200
wait_timeout = 600

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

# 二进制日志(用于备份恢复)
log-bin = mysql-bin
binlog_expire_logs_seconds = 604800
binlog_format = ROW

[client]
default-character-set = utf8mb4

四、环境变量 (.env)

# .env (生产环境示例)
APP_NAME="宫中有喜"
APP_ENV=production
APP_KEY=base64:xxxx
APP_DEBUG=false
APP_URL=https://api.example.com

# 数据库
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=gongzhongyouxi
DB_USERNAME=appuser
DB_PASSWORD=strong_password_here
DB_ROOT_PASSWORD=root_strong_password

# Redis
REDIS_HOST=redis
REDIS_PASSWORD=redis_password
REDIS_PORT=6379

# 缓存/队列/会话
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis

# 文件存储
FILESYSTEM_DISK=local
# 或 OSS:
# FILESYSTEM_DISK=oss
# OSS_ACCESS_KEY_ID=xxx
# OSS_ACCESS_KEY_SECRET=xxx
# OSS_BUCKET=xxx
# OSS_ENDPOINT=xxx

# 微信小程序
WECHAT_MINI_APP_ID=wxXXXXXXXXXXX
WECHAT_MINI_SECRET=xxxxxxxxxx

# 微信支付
WECHAT_PAY_MCH_ID=xxxxxxxxxx
WECHAT_PAY_KEY=xxxxxxxxxx
WECHAT_PAY_CERT_PATH=/path/to/cert
WECHAT_PAY_KEY_PATH=/path/to/key
WECHAT_PAY_NOTIFY_URL=https://api.example.com/api/v1/client/pay/callback

# 短信
SMS_DRIVER=aliyun
SMS_KEY=xxx
SMS_SECRET=xxx
SMS_SIGN=宫中有喜

# 日志
LOG_CHANNEL=daily
LOG_LEVEL=warning

五、部署流程

5.1 首次部署

# 1. 克隆代码
git clone <repo> /opt/saas
cd /opt/saas

# 2. 配置环境变量
cp .env.example .env
vim .env  # 填入生产配置

# 3. 构建前端
cd frontend
npm install
npm run build
cd ..

# 4. 启动容器
docker compose up -d

# 5. 初始化 Laravel
docker compose exec php composer install --no-dev --optimize-autoloader
docker compose exec php php artisan key:generate
docker compose exec php php artisan migrate --force
docker compose exec php php artisan db:seed --class=InitSeeder
docker compose exec php php artisan storage:link
docker compose exec php php artisan config:cache
docker compose exec php php artisan route:cache
docker compose exec php php artisan view:cache

# 6. 验证
curl https://api.example.com/api/v1/health

5.2 更新部署

# 1. 拉取代码
cd /opt/saas
git pull origin main

# 2. 更新后端依赖
docker compose exec php composer install --no-dev --optimize-autoloader

# 3. 数据库迁移
docker compose exec php php artisan migrate --force

# 4. 清除缓存
docker compose exec php php artisan config:cache
docker compose exec php php artisan route:cache
docker compose exec php php artisan view:cache
docker compose exec php php artisan queue:restart

# 5. 更新前端(如有变更)
cd frontend
npm install
npm run build
cd ..

# 6. 重载 Nginx
docker compose exec nginx nginx -s reload

5.3 零停机部署(可选)

# 使用 Laravel 维护模式
docker compose exec php php artisan down --retry=60

# 执行部署步骤...

docker compose exec php php artisan up

六、备份策略

6.1 数据库备份

#!/bin/bash
# scripts/backup-db.sh
BACKUP_DIR="/opt/backups/mysql"
DATE=$(date +%Y%m%d_%H%M)
KEEP_DAYS=30

mkdir -p $BACKUP_DIR

# 备份
docker compose exec -T mysql mysqldump \
    -u root -p"${DB_ROOT_PASSWORD}" \
    --single-transaction \
    --routines --triggers \
    ${DB_DATABASE} | gzip > "${BACKUP_DIR}/db_${DATE}.sql.gz"

# 清理旧备份
find $BACKUP_DIR -name "*.sql.gz" -mtime +$KEEP_DAYS -delete

echo "[$(date)] Backup completed: db_${DATE}.sql.gz"

6.2 文件备份

#!/bin/bash
# scripts/backup-files.sh
BACKUP_DIR="/opt/backups/files"
DATE=$(date +%Y%m%d)

mkdir -p $BACKUP_DIR

# 备份上传文件
tar -czf "${BACKUP_DIR}/storage_${DATE}.tar.gz" \
    -C /opt/saas/backend storage/app/public

# 清理 30 天前
find $BACKUP_DIR -name "*.tar.gz" -mtime +30 -delete

6.3 定时任务

# crontab -e
# 每天凌晨 3 点备份数据库
0 3 * * * /opt/saas/scripts/backup-db.sh >> /var/log/backup.log 2>&1
# 每周日凌晨 4 点备份文件
0 4 * * 0 /opt/saas/scripts/backup-files.sh >> /var/log/backup.log 2>&1

七、监控与日志

7.1 健康检查

// routes/api.php
Route::get('/health', function () {
    try {
        DB::connection()->getPdo();
        Redis::ping();
        return response()->json([
            'status' => 'healthy',
            'timestamp' => now()->toISOString(),
            'services' => [
                'database' => 'ok',
                'redis' => 'ok',
                'storage' => is_writable(storage_path()) ? 'ok' : 'error',
            ]
        ]);
    } catch (\Exception $e) {
        return response()->json(['status' => 'unhealthy', 'error' => $e->getMessage()], 500);
    }
});

7.2 日志管理

// config/logging.php — 生产配置
'channels' => [
    'daily' => [
        'driver' => 'daily',
        'path' => storage_path('logs/laravel.log'),
        'level' => 'warning',
        'days' => 14,
    ],
    'business' => [
        'driver' => 'daily',
        'path' => storage_path('logs/business.log'),
        'level' => 'info',
        'days' => 30,
    ],
],

7.3 监控检查清单

监控项 方法 告警条件
服务存活 /health 接口 连续 3 次失败
CPU 使用率 系统监控 > 80% 持续 5 分钟
内存使用率 系统监控 > 85%
磁盘使用率 系统监控 > 80%
MySQL 连接数 SHOW STATUS > 150
MySQL 慢查询 慢查询日志 > 10 条/小时
Redis 内存 INFO memory > 80% maxmemory
队列积压 queue:monitor > 100 条待处理
Laravel 日志 日志文件 ERROR 级别
响应时间 Nginx access_log P95 > 2s
SSL 证书 定期检查 到期前 30 天

八、安全加固

8.1 服务器

# 防火墙 — 仅开放必要端口
ufw allow 22/tcp    # SSH
ufw allow 80/tcp    # HTTP
ufw allow 443/tcp   # HTTPS
ufw enable

# SSH 加固
# /etc/ssh/sshd_config
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes

8.2 Docker 安全

  • 不使用 root 运行容器(PHP Dockerfile 已配置 www-data
  • MySQL/Redis 端口不暴露到公网(仅限 Docker 内部网络)
  • 定期更新基础镜像
  • 使用 .dockerignore 排除敏感文件

8.3 应用安全

措施 配置
HTTPS 强制 Nginx redirect 80→443
CORS 限定域名白名单
CSRF Sanctum SPA 模式自动处理
Rate Limiting Laravel throttle 中间件
SQL 注入 Eloquent 参数绑定
XSS HTMLPurifier + CSP Header
文件上传 白名单 + 大小限制 + 病毒扫描
敏感数据 .env 不入 Git,密钥加密存储

8.4 SSL 证书

# Let's Encrypt 免费证书
apt install certbot python3-certbot-nginx
certbot --nginx -d admin.example.com -d api.example.com

# 自动续期
certbot renew --dry-run
# crontab
0 0 1 */2 * certbot renew --quiet

九、性能优化

9.1 Laravel 优化

# 配置/路由/视图缓存
php artisan config:cache
php artisan route:cache
php artisan view:cache

# Composer 自动加载优化
composer install --optimize-autoloader --no-dev

9.2 MySQL 优化

  • 索引覆盖常用查询(见 02-database-design.md 索引策略)
  • 开启查询缓存(Redis 层面)
  • 大表分页使用 cursor 分页
  • 定期 ANALYZE TABLE

9.3 Redis 缓存策略

缓存键 TTL 场景
user:{id} 30min 用户信息
menu:{role_id} 60min 角色菜单
room:board:{store_id}:{date} 5min 房态看板
config:{key} 60min 系统配置
dict:{type} 24h 数据字典

9.4 前端优化

  • Vite 构建:代码分割 + Tree Shaking
  • 路由懒加载:按模块动态 import
  • 图片压缩:WebP 格式 + CDN
  • GzipNginx 开启 gzip 压缩

十、目录结构(Docker 相关)

saas/
├── docker/
│   ├── nginx/
│   │   ├── conf.d/
│   │   │   └── default.conf
│   │   └── ssl/              # SSL 证书
│   ├── php/
│   │   ├── Dockerfile
│   │   └── php.ini
│   └── mysql/
│       ├── my.cnf
│       └── init/             # 初始化 SQL
├── scripts/
│   ├── backup-db.sh
│   ├── backup-files.sh
│   └── deploy.sh
├── docker-compose.yml
├── .env
└── .env.example