diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..ac1c212 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,14 @@ +{ + "permissions": { + "allow": [ + "Bash(tree:*)", + "Bash(pnpm add:*)", + "Bash(mvn exec:java:*)", + "Bash(mvn compile exec:java:*)", + "Bash(git rm:*)", + "Bash(git commit:*)", + "Bash(git push:*)", + "Bash(git config:*)" + ] + } +} diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..7ad1adf --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,27 @@ +# Maven +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +# IDE +.idea/ +*.iml +.vscode/ +.eclipse/ + +# Logs +*.log +logs/ + +# OS +.DS_Store +Thumbs.db + +# Git +.git/ +.gitignore + +# Docker +Dockerfile +.dockerignore diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..2469274 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,33 @@ +# 多阶段构建:构建阶段 +FROM maven:3.8.6-openjdk-8 AS builder + +WORKDIR /app + +# 复制 pom.xml 并下载依赖(利用 Docker 缓存) +COPY pom.xml . +RUN mvn dependency:go-offline + +# 复制源代码并构建 +COPY src ./src +RUN mvn clean package -DskipTests + +# 运行阶段 +FROM openjdk:8-jre-alpine + +WORKDIR /app + +# 从构建阶段复制 jar 文件 +COPY --from=builder /app/target/*.jar app.jar + +# 创建日志目录 +RUN mkdir -p /app/logs + +# 暴露端口 +EXPOSE 8080 + +# 健康检查 +HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \ + CMD wget --quiet --tries=1 --spider http://localhost:8080/actuator/health || exit 1 + +# 启动应用 +ENTRYPOINT ["java", "-jar", "app.jar"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..0d63293 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,87 @@ +version: '3.8' + +networks: + crm_network: + driver: bridge + ipam: + config: + - subnet: 192.168.100.0/24 + +services: + # MySQL 数据库 + crm-mysql: + image: mysql:8 + container_name: crm-mysql + restart: always + environment: + MYSQL_ROOT_PASSWORD: MySQL123s56 + MYSQL_DATABASE: crm_db + TZ: Asia/Shanghai + ports: + - "3306:3306" + volumes: + - ./mysql/data:/var/lib/mysql + - ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql:ro + networks: + - crm_network + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-pMySQL123s56"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + + # 后端服务 + crm-backend: + build: + context: ./backend + dockerfile: Dockerfile + container_name: crm-backend + restart: always + depends_on: + crm-mysql: + condition: service_healthy + environment: + # 数据库配置 + SPRING_DATASOURCE_URL: jdbc:mysql://crm-mysql:3306/crm_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai + SPRING_DATASOURCE_USERNAME: root + SPRING_DATASOURCE_PASSWORD: MySQL123s56 + + # JWT 配置 + JWT_SECRET: your-secret-key-change-in-production + JWT_EXPIRATION: 86400000 + + # 应用配置 + SERVER_PORT: 8080 + TZ: Asia/Shanghai + ports: + - "8080:8080" + volumes: + - ./backend/logs:/app/logs + networks: + - crm_network + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/actuator/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + + # 前端服务(Nginx) + crm-frontend: + build: + context: ./frontend + dockerfile: Dockerfile + container_name: crm-frontend + restart: always + depends_on: + - crm-backend + ports: + - "80:80" + networks: + - crm_network + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80"] + interval: 30s + timeout: 5s + retries: 3 diff --git a/docs/docker-deployment.md b/docs/docker-deployment.md new file mode 100644 index 0000000..e63ed2c --- /dev/null +++ b/docs/docker-deployment.md @@ -0,0 +1,334 @@ +# Docker 部署指南 + +## 项目架构 + +本项目采用前后端分离架构,使用 Docker Compose 进行编排部署。 + +### 服务说明 + +- **crm-frontend**: 前端服务(Nginx),端口 80 +- **crm-backend**: 后端服务(Spring Boot),端口 8080 +- **crm-mysql**: MySQL 8 数据库,端口 3306 + +### 网络架构 + +``` +Internet + │ + ▼ +Nginx (crm-frontend:80) + │ + ├─► 静态文件 (Vue SPA) + │ + └─► /api/* 代理到 Spring Boot (crm-backend:8080) + │ + └─► MySQL (crm-mysql:3306) +``` + +## 快速开始 + +### 1. 准备工作 + +确保已安装以下软件: +- Docker (20.10+) +- Docker Compose (2.0+) + +检查安装: +```bash +docker --version +docker-compose --version +``` + +### 2. 配置环境变量 + +编辑 `docker-compose.yml`,根据需要修改以下配置: + +**数据库配置:** +```yaml +environment: + MYSQL_ROOT_PASSWORD: MySQL123s56 # 修改为强密码 + MYSQL_DATABASE: crm_db +``` + +**后端配置:** +```yaml +environment: + JWT_SECRET: your-secret-key-change-in-production # 修改为随机密钥 + JWT_EXPIRATION: 86400000 + SPRING_DATASOURCE_PASSWORD: MySQL123s56 # 与数据库密码一致 +``` + +### 3. 构建并启动 + +```bash +# 进入项目根目录 +cd by-crm + +# 构建并启动所有服务(后台运行) +docker-compose up -d + +# 查看日志 +docker-compose logs -f + +# 查看服务状态 +docker-compose ps +``` + +### 4. 访问应用 + +- **前端应用**: http://localhost +- **后端 API**: http://localhost:8080/api +- **默认账号**: + - 管理员: `admin` / `admin123` + - 经销商: `user001` / `admin123` + +## 常用命令 + +### 服务管理 + +```bash +# 启动所有服务 +docker-compose up -d + +# 停止所有服务 +docker-compose stop + +# 重启所有服务 +docker-compose restart + +# 停止并删除容器 +docker-compose down + +# 停止并删除容器、网络、数据卷 +docker-compose down -v +``` + +### 查看日志 + +```bash +# 查看所有服务日志 +docker-compose logs -f + +# 查看特定服务日志 +docker-compose logs -f crm-backend +docker-compose logs -f crm-frontend +docker-compose logs -f crm-mysql + +# 查看最近 100 行日志 +docker-compose logs --tail=100 crm-backend +``` + +### 重新构建 + +```bash +# 重新构建并启动 +docker-compose up -d --build + +# 重新构建特定服务 +docker-compose build crm-backend +docker-compose build crm-frontend +``` + +### 进入容器 + +```bash +# 进入后端容器 +docker-compose exec crm-backend sh + +# 进入数据库容器 +docker-compose exec crm-mysql mysql -uroot -p + +# 进入前端容器 +docker-compose exec crm-frontend sh +``` + +## 数据持久化 + +### 数据卷挂载 + +```yaml +# MySQL 数据 +./mysql/data:/var/lib/mysql + +# 后端日志 +./backend/logs:/app/logs +``` + +### 备份数据库 + +```bash +# 导出数据库 +docker-compose exec crm-mysql mysqldump -uroot -pMySQL123s56 crm_db > backup.sql + +# 导入数据库 +docker-compose exec -T crm-mysql mysql -uroot -pMySQL123s56 crm_db < backup.sql +``` + +## 生产环境部署 + +### 1. 修改端口映射 + +根据实际情况修改端口映射: + +```yaml +services: + crm-frontend: + ports: + - "80:80" # 或者使用 443:443 (HTTPS) + + crm-backend: + ports: + - "8080:8080" # 生产环境建议不对外暴露,仅通过 Nginx 访问 + + crm-mysql: + ports: + - "3306:3306" # 生产环境建议注释掉,不对外暴露 +``` + +### 2. 配置 HTTPS(推荐) + +修改 `frontend/nginx.conf`: + +```nginx +server { + listen 443 ssl http2; + server_name your-domain.com; + + ssl_certificate /etc/nginx/ssl/cert.pem; + ssl_certificate_key /etc/nginx/ssl/key.pem; + + # 其他配置... +} + +# HTTP 重定向到 HTTPS +server { + listen 80; + server_name your-domain.com; + return 301 https://$server_name$request_uri; +} +``` + +挂载 SSL 证书: + +```yaml +crm-frontend: + volumes: + - ./ssl:/etc/nginx/ssl:ro + ports: + - "443:443" + - "80:80" +``` + +### 3. 优化配置 + +**后端 JVM 参数**(backend/Dockerfile): + +```dockerfile +ENTRYPOINT ["java", \ + "-Xms512m", \ + "-Xmx1024m", \ + "-XX:+UseG1GC", \ + "-jar", "app.jar"] +``` + +**MySQL 优化**(docker-compose.yml): + +```yaml +crm-mysql: + command: [ + '--default-authentication-plugin=mysql_native_password', + '--character-set-server=utf8mb4', + '--collation-server=utf8mb4_unicode_ci', + '--max_connections=500', + '--innodb_buffer_pool_size=1G' + ] +``` + +### 4. 安全加固 + +- 修改所有默认密码 +- 使用强密码策略 +- 配置防火墙规则 +- 启用 HTTPS +- 定期备份数据 +- 限制数据库访问(不对外暴露 3306 端口) + +## 故障排查 + +### 容器启动失败 + +```bash +# 查看详细日志 +docker-compose logs crm-backend + +# 检查容器状态 +docker-compose ps + +# 检查网络 +docker network ls +docker network inspect by-crm_crm_network +``` + +### 数据库连接失败 + +```bash +# 等待数据库健康检查通过 +docker-compose logs crm-mysql + +# 检查数据库是否就绪 +docker-compose exec crm-mysql mysql -uroot -pMySQL123s56 -e "SHOW DATABASES;" +``` + +### 前端访问后端跨域 + +检查 `frontend/nginx.conf` 中的代理配置: + +```nginx +location /api/ { + proxy_pass http://crm-backend:8080/api/; + # 确保包含这些头 + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; +} +``` + +### 性能监控 + +```bash +# 查看容器资源使用 +docker stats + +# 查看特定服务资源使用 +docker stats crm-backend crm-frontend crm-mysql +``` + +## 升级部署 + +```bash +# 1. 备份数据 +docker-compose exec crm-mysql mysqldump -uroot -pMySQL123s56 crm_db > backup_$(date +%Y%m%d).sql + +# 2. 拉取最新代码 +git pull + +# 3. 重新构建并启动 +docker-compose up -d --build + +# 4. 清理旧镜像 +docker image prune -f +``` + +## 注意事项 + +1. **首次启动**:数据库初始化可能需要 1-2 分钟,请耐心等待 +2. **健康检查**:后端服务依赖数据库健康检查通过后才会启动 +3. **数据安全**:生产环境务必修改默认密码 +4. **资源限制**:可根据服务器配置添加资源限制(cpus、memory) +5. **日志管理**:建议配置日志轮转,避免磁盘占满 + +## 参考资源 + +- [Docker 官方文档](https://docs.docker.com/) +- [Docker Compose 文档](https://docs.docker.com/compose/) +- [Nginx 配置指南](https://nginx.org/en/docs/) +- [Spring Boot Docker 部署](https://docs.spring.io/spring-boot/docs/current/reference/html/docker.html) diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..7313206 --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,40 @@ +# Dependencies +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Build output +dist/ +dist-ssr/ + +# IDE +.idea/ +.vscode/ +*.iml + +# OS +.DS_Store +Thumbs.db + +# Git +.git/ +.gitignore + +# Environment +.env.local +.env.*.local + +# Docker +Dockerfile +.dockerignore +nginx.conf + +# Logs +*.log +logs/ + +# Temporary files +*.tmp +tmpclaude-* diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..0f4f04a --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,34 @@ +# 多阶段构建:构建阶段 +FROM node:18-alpine AS builder + +WORKDIR /app + +# 安装 pnpm +RUN npm install -g pnpm + +# 复制 package.json 和 pnpm-lock.yaml +COPY package.json pnpm-lock.yaml ./ + +# 安装依赖 +RUN pnpm install + +# 复制源代码 +COPY . . + +# 构建生产版本 +RUN pnpm build + +# 运行阶段:使用 Nginx +FROM nginx:alpine + +# 从构建阶段复制构建产物到 Nginx +COPY --from=builder /app/dist /usr/share/nginx/html + +# 复制自定义 Nginx 配置 +COPY nginx.conf /etc/nginx/conf.d/default.conf + +# 暴露端口 +EXPOSE 80 + +# 启动 Nginx +CMD ["nginx", "-g", "daemon off;"] diff --git a/frontend/nginx.conf b/frontend/nginx.conf new file mode 100644 index 0000000..41c36b7 --- /dev/null +++ b/frontend/nginx.conf @@ -0,0 +1,43 @@ +server { + listen 80; + server_name localhost; + + root /usr/share/nginx/html; + index index.html; + + # Gzip 压缩 + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json; + + # 处理前端路由(Vue Router History 模式) + location / { + try_files $uri $uri/ /index.html; + } + + # 代理后端 API + location /api/ { + proxy_pass http://crm-backend:8080/api/; + 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; + + # WebSocket 支持(如果需要) + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + # 静态资源缓存 + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # 安全头 + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; +}