🦞 从零部署小龙虾电商平台 | 超详细小白入坑指南(Docker + Spring Boot + Vue3)

📖 前言:写给技术小白的部署指南

阅读对象:零基础小白、刚入行的程序员、想学习完整项目部署的同学

学习目标:通过本文,你将学会如何从零开始部署一个完整的小龙虾电商平台。即使你是完全的新手,只要跟着步骤一步步操作,也能成功部署!

项目简介:这是一个基于Spring Boot + Vue3的小龙虾电商平台,包含用户端、商家端、后台管理三个模块。


🎯 第一章:环境准备(必须完成)

1.1 操作系统选择

  • 推荐:Ubuntu 20.04/22.04 LTS(本文以Ubuntu 22.04为例)
  • 备选:CentOS 7/8、Windows WSL2
  • 最低配置:2核CPU、4GB内存、50GB硬盘

1.2 购买云服务器(如果还没有)

腾讯云轻量应用服务器为例:

  1. 访问:https://cloud.tencent.com/product/lighthouse
  2. 选择配置:2核4G、Ubuntu 22.04、50GB SSD
  3. 设置密码:记住root密码或设置密钥
  4. 购买完成:获得公网IP地址

1.3 连接服务器

# Windows用户使用 PowerShell 或 CMD
ssh root@你的服务器IP地址

# 首次连接会提示,输入 yes
# 然后输入密码

# 连接成功后,你会看到类似这样的提示符:
# root@VM-0-16-ubuntu:~#

1.4 更新系统(第一步操作)

# 1. 更新软件包列表
apt update

# 2. 升级已安装的软件包
apt upgrade -y

# 3. 安装常用工具
apt install -y vim curl wget git htop net-tools

# 4. 设置时区为上海
timedatectl set-timezone Asia/Shanghai

# 5. 查看当前时间
date

🐳 第二章:安装Docker和Docker Compose

2.1 为什么用Docker?

  • 简化部署:一键安装所有依赖
  • 环境隔离:避免"在我电脑上能运行"的问题
  • 快速迁移:轻松复制到其他服务器

2.2 安装Docker

# 1. 卸载旧版本(如果有)
apt remove docker docker-engine docker.io containerd runc

# 2. 安装依赖包
apt install -y \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

# 3. 添加Docker官方GPG密钥
mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# 4. 设置稳定版仓库
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 5. 更新并安装Docker
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

# 6. 验证安装
docker --version
# 应该显示:Docker version 24.0.7, build xxxxx

# 7. 启动Docker并设置开机自启
systemctl start docker
systemctl enable docker

# 8. 测试运行
docker run hello-world
# 如果看到"Hello from Docker!"说明安装成功

2.3 安装Docker Compose

# 1. 下载Docker Compose(如果上一步没安装插件版)
DOCKER_COMPOSE_VERSION="v2.23.0"
curl -SL "https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-linux-x86_64" -o /usr/local/bin/docker-compose

# 2. 添加执行权限
chmod +x /usr/local/bin/docker-compose

# 3. 验证安装
docker-compose --version
# 应该显示:Docker Compose version v2.23.0

2.4 配置Docker加速器(国内用户必做)

# 创建配置文件目录
mkdir -p /etc/docker

# 创建配置文件
cat > /etc/docker/daemon.json << EOF
{
  "registry-mirrors": [
    "https://docker.mirrors.ustc.edu.cn",
    "https://hub-mirror.c.163.com",
    "https://mirror.baidubce.com"
  ],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  }
}
EOF

# 重启Docker使配置生效
systemctl daemon-reload
systemctl restart docker

# 验证配置
docker info | grep -A 5 "Registry Mirrors"

🗄️ 第三章:安装MySQL数据库

3.1 使用Docker安装MySQL 8.0

# 1. 创建数据目录(防止数据丢失)
mkdir -p /data/mysql/{data,conf,logs}

# 2. 创建MySQL配置文件
cat > /data/mysql/conf/my.cnf << EOF
[mysqld]
user=mysql
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
default_authentication_plugin=mysql_native_password

# 性能优化
max_connections=1000
max_connect_errors=1000
table_open_cache=2000
thread_cache_size=128

# 日志
slow_query_log=1
slow_query_log_file=/var/log/mysql/slow.log
long_query_time=2
log_error=/var/log/mysql/error.log

# InnoDB配置
innodb_buffer_pool_size=256M
innodb_log_file_size=128M
innodb_flush_log_at_trx_commit=1
innodb_lock_wait_timeout=50

[client]
default-character-set=utf8mb4

[mysql]
default-character-set=utf8mb4
EOF

# 3. 运行MySQL容器
docker run -d \
  --name mysql8 \
  --restart=always \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=Crayfish123! \
  -e MYSQL_DATABASE=crayfish_db \
  -e MYSQL_USER=crayfish_user \
  -e MYSQL_PASSWORD=Crayfish123! \
  -v /data/mysql/data:/var/lib/mysql \
  -v /data/mysql/conf:/etc/mysql/conf.d \
  -v /data/mysql/logs:/var/log/mysql \
  mysql:8.0 \
  --character-set-server=utf8mb4 \
  --collation-server=utf8mb4_unicode_ci

# 4. 查看容器状态
docker ps | grep mysql
# 应该看到mysql8容器正在运行

# 5. 等待30秒让MySQL完全启动
sleep 30

# 6. 测试连接
docker exec -it mysql8 mysql -uroot -pCrayfish123! -e "SHOW DATABASES;"
# 应该看到包含crayfish_db的数据库列表

3.2 创建小龙虾项目数据库表

# 1. 进入MySQL容器
docker exec -it mysql8 mysql -uroot -pCrayfish123!

# 2. 选择数据库
USE crayfish_db;

# 3. 创建用户表
CREATE TABLE IF NOT EXISTS `user` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `username` varchar(50) NOT NULL COMMENT '用户名',
  `password` varchar(100) NOT NULL COMMENT '密码',
  `nickname` varchar(50) DEFAULT NULL COMMENT '昵称',
  `phone` varchar(20) DEFAULT NULL COMMENT '手机号',
  `email` varchar(100) DEFAULT NULL COMMENT '邮箱',
  `avatar` varchar(500) DEFAULT NULL COMMENT '头像',
  `status` tinyint DEFAULT '1' COMMENT '状态:0-禁用,1-正常',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_username` (`username`),
  KEY `idx_phone` (`phone`),
  KEY `idx_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';

# 4. 创建小龙虾商品表
CREATE TABLE IF NOT EXISTS `crayfish_product` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '商品ID',
  `name` varchar(100) NOT NULL COMMENT '商品名称',
  `description` text COMMENT '商品描述',
  `price` decimal(10,2) NOT NULL COMMENT '价格(元/斤)',
  `stock` int NOT NULL DEFAULT '0' COMMENT '库存(斤)',
  `category` varchar(50) DEFAULT NULL COMMENT '分类:清水虾、麻辣虾、蒜蓉虾等',
  `weight` varchar(20) DEFAULT NULL COMMENT '规格:大虾、中虾、小虾',
  `origin` varchar(100) DEFAULT NULL COMMENT '产地',
  `image_url` varchar(500) DEFAULT NULL COMMENT '商品图片',
  `status` tinyint DEFAULT '1' COMMENT '状态:0-下架,1-上架',
  `sales_count` int DEFAULT '0' COMMENT '销量',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_category` (`category`),
  KEY `idx_status` (`status`),
  KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='小龙虾商品表';

# 5. 创建订单表
CREATE TABLE IF NOT EXISTS `order` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单ID',
  `order_no` varchar(32) NOT NULL COMMENT '订单号',
  `user_id` bigint NOT NULL COMMENT '用户ID',
  `total_amount` decimal(10,2) NOT NULL COMMENT '订单总金额',
  `status` tinyint NOT NULL DEFAULT '0' COMMENT '状态:0-待支付,1-已支付,2-已发货,3-已完成,4-已取消',
  `payment_method` varchar(20) DEFAULT NULL COMMENT '支付方式:wechat、alipay',
  `payment_time` datetime DEFAULT NULL COMMENT '支付时间',
  `delivery_address` varchar(500) NOT NULL COMMENT '配送地址',
  `contact_name` varchar(50) NOT NULL COMMENT '联系人',
  `contact_phone` varchar(20) NOT NULL COMMENT '联系电话',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_order_no` (`order_no`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_status` (`status`),
  KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单表';

# 6. 创建订单商品表
CREATE TABLE IF NOT EXISTS `order_item` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单项ID',
  `order_id` bigint NOT NULL COMMENT '订单ID',
  `product_id` bigint NOT NULL COMMENT '商品ID',
  `product_name` varchar(100) NOT NULL COMMENT '商品名称',
  `product_image` varchar(500) DEFAULT NULL COMMENT '商品图片',
  `price` decimal(10,2) NOT NULL COMMENT '单价',
  `quantity` int NOT NULL COMMENT '数量',
  `total_price` decimal(10,2) NOT NULL COMMENT '总价',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  KEY `idx_order_id` (`order_id`),
  KEY `idx_product_id` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单商品表';

# 7. 插入测试数据
INSERT INTO `crayfish_product` (`name`, `description`, `price`, `stock`, `category`, `weight`, `origin`, `image_url`) VALUES
('清水小龙虾', '新鲜清水养殖,肉质鲜嫩,无污染', 45.00, 1000, '清水虾', '大虾', '江苏盱眙', 'https://images.unsplash.com/photo-1565299624946-b28f40a0ca4b'),
('麻辣小龙虾', '秘制麻辣配方,香辣过瘾', 68.00, 800, '麻辣虾', '中虾', '湖北潜江', 'https://images.unsplash.com/photo-1563379091339-03246963d9d6'),
('蒜蓉小龙虾', '黄金蒜蓉,香气扑鼻', 58.00, 600, '蒜蓉虾', '大虾', '安徽合肥', 'https://images.unsplash.com/photo-1563379926898-05f4575a45d8'),
('十三香小龙虾', '传统十三香,回味无穷', 65.00, 700, '十三香', '中虾', '湖南岳阳', 'https://images.unsplash.com/photo-1565299507177-b0ac66763828');

# 8. 查看创建的表
SHOW TABLES;

# 9. 退出MySQL
EXIT;

🚀 第四章:安装Redis缓存

4.1 使用Docker安装Redis

# 1. 创建Redis数据目录
mkdir -p /data/redis/{data,conf}

# 2. 创建Redis配置文件
cat > /data/redis/conf/redis.conf << EOF
# Redis配置文件
bind 0.0.0.0
port 6379
protected-mode no
daemonize no

# 持久化配置
save 900 1
save 300 10
save 60 10000
rdbcompression yes
dbfilename dump.rdb
dir /data

# 内存配置
maxmemory 256mb
maxmemory-policy allkeys-lru

# 日志配置
loglevel notice
logfile "/data/redis.log"

# 慢查询日志
slowlog-log-slower-than 10000
slowlog-max-len 128

# AOF配置
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

# 连接配置
timeout 0
tcp-keepalive 300
maxclients 10000
EOF

# 3. 运行Redis容器
docker run -d \
  --name redis7 \
  --restart=always \
  -p 6379:6379 \
  -v /data/redis/data:/data \
  -v /data/redis/conf/redis.conf:/usr/local/etc/redis/redis.conf \
  redis:7.0-alpine \
  redis-server /usr/local/etc/redis/redis.conf

# 4. 测试Redis连接
docker exec -it redis7 redis-cli ping
# 应该返回:PONG

# 5. 设置测试数据
docker exec -it redis7 redis-cli set test_key "hello_crayfish"
docker exec -it redis7 redis-cli get test_key
# 应该返回:hello_crayfish

🌐 第五章:部署后端Spring Boot应用

5.1 创建项目目录结构

# 1. 创建项目根目录
mkdir -p /opt/crayfish-project/{backend,frontend,nginx,logs}

# 2. 进入后端目录
cd /opt/crayfish-project/backend

# 3. 创建Dockerfile
cat > Dockerfile << 'EOF'
# 使用OpenJDK 17作为基础镜像
FROM openjdk:17-jdk-slim

# 设置工作目录
WORKDIR /app

# 设置时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' > /etc/timezone

# 复制JAR文件
COPY crayfish-backend.jar app.jar

# 暴露端口
EXPOSE 8080

# 启动命令
ENTRYPOINT ["java", "-jar", "app.jar"]
EOF

# 4. 创建application.yml配置文件
mkdir -p config
cat > config/application.yml << 'EOF'
server:
  port: 8080
  servlet:
    context-path: /api
  tomcat:
    max-threads: 200
    min-spare-threads: 20

spring:
  application:
    name: crayfish-backend
  
  # 数据源配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://mysql8:3306/crayfish_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
    username: crayfish_user
    password: Crayfish123!
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000
  
  # Redis配置
  redis:
    host: redis7
    port: 6379
    password:
    database: 0
    timeout: 5000ms
    lettuce:
      pool:
        max-active: 20
        max-idle: 10
        min-idle: 5
        max-wait: -1ms
  
  # JPA配置
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        format_sql: true
        dialect: org.hibernate.dialect.MySQL8Dialect
  
  # 文件上传配置
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 100MB

# MyBatis配置
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.crayfish.entity
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

# 日志配置
logging:
  level:
    com.crayfish: debug
    org.springframework.web: info
  file:
    name: /app/logs/crayfish.log
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
    file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"

# 项目配置
crayfish:
  # JWT配置
  jwt:
    secret: crayfish-jwt-secret-key-2024
    expiration: 86400000  # 24小时
    header: Authorization
    
  # 文件上传路径
  upload:
    path: /app/uploads
    access-path: /uploads/**
    
  # 微信支付配置(示例)
  wechat:
    app-id: wx1234567890abcdef
    mch-id: 1234567890
    api-key: crayfish-wechat-pay-key
    notify-url: https://your-domain.com/api/pay/notify
EOF

# 5. 创建docker-compose.yml
cat > docker-compose.yml << 'EOF'
version: '3.8'

services:
  # MySQL数据库
  mysql:
    image: mysql:8.0
    container_name: crayfish-mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: Crayfish123!
      MYSQL_DATABASE: crayfish_db
      MYSQL_USER: crayfish_user
      MYSQL_PASSWORD: Crayfish123!
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
      - ./config/mysql/my.cnf:/etc/mysql/conf.d/my.cnf
    command: 
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
    networks:
      - crayfish-network

  # Redis缓存
  redis:
    image: redis:7.0-alpine
    container_name: crayfish-redis
    restart: always
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
      - ./config/redis/redis.conf:/usr/local/etc/redis/redis.conf
    command: redis-server /usr/local/etc/redis/redis.conf
    networks:
      - crayfish-network

  # 后端Spring Boot应用
  backend:
    build: 
      context: .
      dockerfile: Dockerfile
    container_name: crayfish-backend
    restart: always
    depends_on:
      - mysql
      - redis
    ports:
      - "8080:8080"
    volumes:
      - ./logs:/app/logs
      - ./uploads:/app/uploads
      - ./config/application.yml:/app/config/application.yml
    environment:
      - TZ=Asia/Shanghai
      - SPRING_PROFILES_ACTIVE=prod
    networks:
      - crayfish-network

  # 前端Vue3应用(下一章部署)
  frontend:
    image: nginx:alpine
    container_name: crayfish-frontend
    restart: always
    ports:
      - "80:80"
    volumes:
      - ../frontend/dist:/usr/share/nginx/html
      - ./config/nginx/nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - backend
    networks:
      - crayfish-network

volumes:
  mysql_data:
  redis_data:

networks:
  crayfish-network:
    driver: bridge
EOF

# 6. 创建MySQL和Redis配置目录
mkdir -p config/{mysql,redis,nginx}

# 7. 创建MySQL配置文件
cat > config/mysql/my.cnf << 'EOF'
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
default_authentication_plugin=mysql_native_password

# 性能优化
max_connections=1000
max_connect_errors=1000
table_open_cache=2000
thread_cache_size=128

# InnoDB配置
innodb_buffer_pool_size=256M
innodb_log_file_size=128M
innodb_flush_log_at_trx_commit=1
innodb_lock_wait_timeout=50

[client]
default-character-set=utf8mb4

[mysql]
default-character-set=utf8mb4
EOF

# 8. 创建Redis配置文件
cat > config/redis/redis.conf << 'EOF'
bind 0.0.0.0
port 6379
protected-mode no
daemonize no

save 900 1
save 300 10
save 60 10000
rdbcompression yes
dbfilename dump.rdb
dir /data

maxmemory 256mb
maxmemory-policy allkeys-lru

appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

timeout 0
tcp-keepalive 300
maxclients 10000
EOF

# 9. 创建Nginx配置文件
cat > config/nginx/nginx.conf << 'EOF'
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
}

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

    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log main;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

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

    # 前端静态文件服务
    server {
        listen 80;
        server_name localhost;
        root /usr/share/nginx/html;
        index index.html;

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

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

        # 后端API代理
        location /api/ {
            proxy_pass http://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;
            
            # 超时设置
            proxy_connect_timeout 60s;
            proxy_send_timeout 60s;
            proxy_read_timeout 60s;
        }

        # 文件上传访问
        location /uploads/ {
            proxy_pass http://backend:8080/uploads/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}
EOF

5.2 下载或编译后端JAR文件

# 1. 下载示例后端JAR文件(这里提供一个模拟下载)
cd /opt/crayfish-project/backend

# 如果没有现成的JAR,可以创建一个简单的Spring Boot项目
# 这里我们创建一个临时的测试JAR

# 创建简单的Spring Boot应用
cat > pom.xml << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>com.crayfish</groupId>
    <artifactId>crayfish-backend</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.14</version>
    </parent>
    
    <properties>
        <java.version>17</java.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
EOF

# 2. 创建简单的Spring Boot应用(如果无法编译,使用下面的测试JAR)
# 这里我们直接使用一个预编译的测试JAR
# 你可以从GitHub下载或使用自己的JAR文件

# 3. 创建一个简单的测试JAR(如果没有现成的)
cat > TestApplication.java << 'EOF'
package com.crayfish;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class TestApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }
    
    @GetMapping("/api/health")
    public String health() {
        return "{\"status\":\"UP\",\"service\":\"crayfish-backend\"}";
    }
    
    @GetMapping("/api/products")
    public String getProducts() {
        return "[{\"id\":1,\"name\":\"清水小龙虾\",\"price\":45.00},{\"id\":2,\"name\":\"麻辣小龙虾\",\"price\":68.00}]";
    }
}
EOF

# 4. 由于编译需要Maven环境,我们直接创建一个可运行的JAR
# 下载一个简单的Spring Boot示例JAR
wget -O crayfish-backend.jar https://repo1.maven.org/maven2/org/springframework/boot/spring-boot/2.7.14/spring-boot-2.7.14.jar

# 或者使用curl创建一个简单的可执行JAR
echo "创建测试JAR文件..."
cat > crayfish-backend.jar.base64 << 'EOF'
# 这里应该是JAR文件的base64编码,但由于篇幅限制省略
# 实际使用时,你应该使用自己编译的JAR文件
EOF

# 5. 创建一个简单的启动脚本
cat > start.sh << 'EOF'
#!/bin/bash
echo "启动小龙虾后端服务..."
java -jar crayfish-backend.jar
EOF

chmod +x start.sh

5.3 启动后端服务

# 1. 构建Docker镜像
cd /opt/crayfish-project/backend
docker build -t crayfish-backend:1.0.0 .

# 2. 使用docker-compose启动所有服务
docker-compose up -d

# 3. 查看服务状态
docker-compose ps

# 应该看到4个服务都在运行:
# crayfish-mysql
# crayfish-redis  
# crayfish-backend
# crayfish-frontend(前端还没部署,会失败,先忽略)

# 4. 查看后端日志
docker-compose logs -f backend

# 5. 测试后端接口
curl http://localhost:8080/api/health
# 应该返回:{"status":"UP","service":"crayfish-backend"}

curl http://localhost:8080/api/products
# 应该返回商品列表

🎨 第六章:部署前端Vue3应用

6.1 准备前端项目

# 1. 进入前端目录
cd /opt/crayfish-project/frontend

# 2. 创建Vue3项目结构
mkdir -p {public,src/{assets,components,views,router,store,api,utils}}

# 3. 创建package.json
cat > package.json << 'EOF'
{
  "name": "crayfish-frontend",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "vue": "^3.3.4",
    "vue-router": "^4.2.4",
    "pinia": "^2.1.6",
    "axios": "^1.5.0",
    "element-plus": "^2.3.8",
    "@element-plus/icons-vue": "^2.1.0"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.3.4",
    "vite": "^4.4.9",
    "sass": "^1.66.1"
  }
}
EOF

# 4. 创建vite.config.js
cat > vite.config.js << 'EOF'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src')
    }
  },
  server: {
    port: 3000,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true
      }
    }
  },
  build: {
    outDir: 'dist',
    assetsDir: 'assets',
    rollupOptions: {
      output: {
        chunkFileNames: 'assets/js/[name]-[hash].js',
        entryFileNames: 'assets/js/[name]-[hash].js',
        assetFileNames: 'assets/[ext]/[name]-[hash].[ext]'
      }
    }
  }
})
EOF

# 5. 创建index.html
cat > index.html << 'EOF'
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>小龙虾电商平台</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>
EOF

# 6. 创建主入口文件
cat > src/main.js << 'EOF'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { createPinia } from 'pinia'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'

const app = createApp(App)

// 注册Element Plus图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}

app.use(createPinia())
app.use(router)
app.use(ElementPlus)

app.mount('#app')
EOF

# 7. 创建App.vue
cat > src/App.vue << 'EOF'
<template>
  <div id="app">
    <router-view />
  </div>
</template>

<script setup>
import { onMounted } from 'vue'
import { useRouter } from 'vue-router'

const router = useRouter()

onMounted(() => {
  console.log('小龙虾电商平台启动成功')
})
</script>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

#app {
  min-height: 100vh;
}
</style>
EOF

# 8. 创建路由配置
cat > src/router/index.js << 'EOF'
import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('@/views/Home.vue'),
    meta: { title: '首页 - 小龙虾电商平台' }
  },
  {
    path: '/products',
    name: 'Products',
    component: () => import('@/views/Products.vue'),
    meta: { title: '商品列表 - 小龙虾电商平台' }
  },
  {
    path: '/product/:id',
    name: 'ProductDetail',
    component: () => import('@/views/ProductDetail.vue'),
    meta: { title: '商品详情 - 小龙虾电商平台' }
  },
  {
    path: '/cart',
    name: 'Cart',
    component: () => import('@/views/Cart.vue'),
    meta: { title: '购物车 - 小龙虾电商平台' }
  },
  {
    path: '/login',
    name: 'Login',
    component: () => import('@/views/Login.vue'),
    meta: { title: '登录 - 小龙虾电商平台' }
  },
  {
    path: '/register',
    name: 'Register',
    component: () => import('@/views/Register.vue'),
    meta: { title: '注册 - 小龙虾电商平台' }
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

// 路由守卫
router.beforeEach((to, from, next) => {
  // 设置页面标题
  if (to.meta.title) {
    document.title = to.meta.title
  }
  next()
})

export default router
EOF

# 9. 创建首页组件
cat > src/views/Home.vue << 'EOF'
<template>
  <div class="home">
    <!-- 导航栏 -->
    <el-header class="header">
      <div class="container">
        <div class="logo">
          <el-icon size="24" color="#e74c3c"><Food /></el-icon>
          <span class="logo-text">小龙虾商城</span>
        </div>
        <el-menu
          :default-active="activeIndex"
          class="nav-menu"
          mode="horizontal"
          @select="handleSelect"
        >
          <el-menu-item index="1">首页</el-menu-item>
          <el-menu-item index="2">商品分类</el-menu-item>
          <el-menu-item index="3">限时特惠</el-menu-item>
          <el-menu-item index="4">关于我们</el-menu-item>
        </el-menu>
        <div class="user-actions">
          <el-button type="text" @click="goToCart">
            <el-icon><ShoppingCart /></el-icon>
            购物车
            <el-badge :value="cartCount" class="badge" v-if="cartCount > 0" />
          </el-button>
          <el-button type="text" @click="goToLogin" v-if="!isLoggedIn">登录</el-button>
          <el-dropdown v-else>
            <span class="user-name">
              <el-avatar :size="32" :src="userAvatar" />
              {{ userName }}
            </span>
            <template #dropdown>
              <el-dropdown-menu>
                <el-dropdown-item>个人中心</el-dropdown-item>
                <el-dropdown-item>我的订单</el-dropdown-item>
                <el-dropdown-item divided @click="logout">退出登录</el-dropdown-item>
              </el-dropdown-menu>
            </template>
          </el-dropdown>
        </div>
      </div>
    </el-header>

    <!-- 轮播图 -->
    <el-carousel :interval="4000" type="card" height="400px" class="carousel">
      <el-carousel-item v-for="item in carouselItems" :key="item.id">
        <img :src="item.image" :alt="item.title" class="carousel-image" />
        <div class="carousel-content">
          <h3>{{ item.title }}</h3>
          <p>{{ item.description }}</p>
          <el-button type="danger" size="large" @click="goToProducts">
            立即购买
          </el-button>
        </div>
      </el-carousel-item>
    </el-carousel>

    <!-- 商品分类 -->
    <div class="categories">
      <div class="container">
        <h2 class="section-title">热门分类</h2>
        <div class="category-list">
          <div 
            v-for="category in categories" 
            :key="category.id"
            class="category-item"
            @click="goToCategory(category.id)"
          >
            <div class="category-icon">
              <el-icon :size="40" :color="category.color">
                <component :is="category.icon" />
              </el-icon>
            </div>
            <h3>{{ category.name }}</h3>
            <p>{{ category.description }}</p>
          </div>
        </div>
      </div>
    </div>

    <!-- 推荐商品 -->
    <div class="recommend-products">
      <div class="container">
        <div class="section-header">
          <h2 class="section-title">今日推荐</h2>
          <el-button type="text" @click="goToProducts">
            查看更多 <el-icon><ArrowRight /></el-icon>
          </el-button>
        </div>
        <div class="product-grid">
          <div 
            v-for="product in products" 
            :key="product.id"
            class="product-card"
            @click="goToProductDetail(product.id)"
          >
            <div class="product-image">
              <img :src="product.image" :alt="product.name" />
              <div class="product-tag" v-if="product.tag">
                {{ product.tag }}
              </div>
            </div>
            <div class="product-info">
              <h3 class="product-name">{{ product.name }}</h3>
              <p class="product-desc">{{ product.description }}</p>
              <div class="product-price">
                <span class="current-price">¥{{ product.price }}</span>
                <span class="original-price" v-if="product.originalPrice">
                  ¥{{ product.originalPrice }}
                </span>
              </div>
              <div class="product-actions">
                <el-button type="danger" size="small" @click.stop="addToCart(product)">
                  <el-icon><Plus /></el-icon>
                  加入购物车
                </el-button>
                <el-button type="info" size="small" plain @click.stop="viewDetail(product.id)">
                  查看详情
                </el-button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- 页脚 -->
    <el-footer class="footer">
      <div class="container">
        <div class="footer-content">
          <div class="footer-section">
            <h3>小龙虾商城</h3>
            <p>新鲜直达,美味到家</p>
            <p>专业的小龙虾电商平台</p>
          </div>
          <div class="footer-section">
            <h4>联系我们</h4>
            <p><el-icon><Phone /></el-icon> 400-123-4567</p>
            <p><el-icon><Message /></el-icon> service@crayfish.com</p>
            <p><el-icon><Location /></el-icon> 北京市朝阳区小龙虾大街1号</p>
          </div>
          <div class="footer-section">
            <h4>关注我们</h4>
            <div class="social-links">
              <el-icon><ChatDotRound /></el-icon>
              <el-icon><VideoCamera /></el-icon>
              <el-icon><Headset /></el-icon>
            </div>
          </div>
        </div>
        <div class="footer-bottom">
          <p>© 2024 小龙虾商城 版权所有 | ICP备案号:京ICP备12345678号</p>
        </div>
      </div>
    </el-footer>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import {
  Food, ShoppingCart, Phone, Message, Location,
  ChatDotRound, VideoCamera, Headset, ArrowRight, Plus
} from '@element-plus/icons-vue'

const router = useRouter()
const activeIndex = ref('1')
const cartCount = ref(3)
const isLoggedIn = ref(false)
const userName = ref('小龙虾爱好者')
const userAvatar = ref('https://images.unsplash.com/photo-1565299624946-b28f40a0ca4b')

// 轮播图数据
const carouselItems = ref([
  {
    id: 1,
    image: 'https://images.unsplash.com/photo-1565299624946-b28f40a0ca4b',
    title: '新鲜清水小龙虾',
    description: '当日捕捞,冷链直达'
  },
  {
    id: 2,
    image: 'https://images.unsplash.com/photo-1563379091339-03246963d9d6',
    title: '麻辣小龙虾套餐',
    description: '香辣过瘾,家庭装特惠'
  },
  {
    id: 3,
    image: 'https://images.unsplash.com/photo-1563379926898-05f4575a45d8',
    title: '蒜蓉小龙虾',
    description: '黄金蒜蓉,香气扑鼻'
  }
])

// 分类数据
const categories = ref([
  { id: 1, name: '清水虾', icon: 'Watermelon', color: '#3498db', description: '原汁原味' },
  { id: 2, name: '麻辣虾', icon: 'HotWater', color: '#e74c3c', description: '香辣过瘾' },
  { id: 3, name: '蒜蓉虾', icon: 'KnifeFork', color: '#f39c12', description: '蒜香浓郁' },
  { id: 4, name: '十三香', icon: 'Star', color: '#9b59b6', description: '传统风味' }
])

// 商品数据
const products = ref([
  {
    id: 1,
    name: '清水小龙虾',
    description: '新鲜清水养殖,肉质鲜嫩',
    price: '45.00',
    originalPrice: '58.00',
    image: 'https://images.unsplash.com/photo-1565299624946-b28f40a0ca4b',
    tag: '热卖'
  },
  {
    id: 2,
    name: '麻辣小龙虾',
    description: '秘制麻辣配方,香辣过瘾',
    price: '68.00',
    image: 'https://images.unsplash.com/photo-1563379091339-03246963d9d6',
    tag: '新品'
  },
  {
    id: 3,
    name: '蒜蓉小龙虾',
    description: '黄金蒜蓉,香气扑鼻',
    price: '58.00',
    originalPrice: '68.00',
    image: 'https://images.unsplash.com/photo-1563379926898-05f4575a45d8',
    tag: '特价'
  },
  {
    id: 4,
    name: '十三香小龙虾',
    description: '传统十三香,回味无穷',
    price: '65.00',
    image: 'https://images.unsplash.com/photo-1565299507177-b0ac66763828'
  }
])

const handleSelect = (key) => {
  console.log('选中菜单:', key)
}

const goToCart = () => {
  router.push('/cart')
}

const goToLogin = () => {
  router.push('/login')
}

const goToProducts = () => {
  router.push('/products')
}

const goToCategory = (categoryId) => {
  router.push(`/products?category=${categoryId}`)
}

const goToProductDetail = (productId) => {
  router.push(`/product/${productId}`)
}

const addToCart = (product) => {
  cartCount.value++
  ElMessage.success(`已添加 ${product.name} 到购物车`)
}

const viewDetail = (productId) => {
  router.push(`/product/${productId}`)
}

const logout = () => {
  isLoggedIn.value = false
  ElMessage.success('已退出登录')
}

onMounted(() => {
  // 模拟检查登录状态
  setTimeout(() => {
    isLoggedIn.value = true
  }, 1000)
})
</script>

<style scoped>
.home {
  min-height: 100vh;
  background: linear-gradient(180deg, #f8f9fa 0%, #ffffff 100%);
}

.container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 20px;
}

/* 头部样式 */
.header {
  background: white;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
  height: 64px;
  display: flex;
  align-items: center;
}

.header .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
}

.logo {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
}

.logo-text {
  font-size: 24px;
  font-weight: bold;
  background: linear-gradient(45deg, #e74c3c, #f39c12);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

.nav-menu {
  border-bottom: none;
  flex: 1;
  justify-content: center;
}

.user-actions {
  display: flex;
  align-items: center;
  gap: 20px;
}

.user-name {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
}

.badge {
  margin-left: 4px;
}

/* 轮播图样式 */
.carousel {
  margin: 20px auto;
  max-width: 1200px;
}

.carousel-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 8px;
}

.carousel-content {
  position: absolute;
  bottom: 40px;
  left: 40px;
  color: white;
  text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}

.carousel-content h3 {
  font-size: 32px;
  margin-bottom: 10px;
}

.carousel-content p {
  font-size: 18px;
  margin-bottom: 20px;
}

/* 分类样式 */
.categories {
  padding: 60px 0;
  background: white;
}

.section-title {
  font-size: 32px;
  text-align: center;
  margin-bottom: 40px;
  color: #333;
}

.category-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 30px;
}

.category-item {
  text-align: center;
  padding: 30px 20px;
  border-radius: 12px;
  background: #f8f9fa;
  cursor: pointer;
  transition: all 0.3s ease;
}

.category-item:hover {
  transform: translateY(-5px);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
  background: white;
}

.category-icon {
  margin-bottom: 15px;
}

.category-item h3 {
  font-size: 20px;
  margin-bottom: 10px;
  color: #333;
}

.category-item p {
  color: #666;
  font-size: 14px;
}

/* 推荐商品样式 */
.recommend-products {
  padding: 60px 0;
}

.section-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 40px;
}

.product-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 30px;
}

.product-card {
  background: white;
  border-radius: 12px;
  overflow: hidden;
  box-shadow:

更多推荐