从零部署小龙虾电商平台 | 超详细小白入坑指南(Docker + Spring Boot + Vue3)
·
🦞 从零部署小龙虾电商平台 | 超详细小白入坑指南(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 购买云服务器(如果还没有)
以腾讯云轻量应用服务器为例:
- 访问:https://cloud.tencent.com/product/lighthouse
- 选择配置:2核4G、Ubuntu 22.04、50GB SSD
- 设置密码:记住root密码或设置密钥
- 购买完成:获得公网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:
更多推荐
所有评论(0)