【Java】Java 实战项目:从零开发一个在线教育平台,附完整部署教程

Java 实战项目:从零开发一个在线教育平台,附完整部署教程


🌸你好呀!我是 lbb小魔仙
🌟 感谢陪伴~ 小白博主在线求友
🌿 跟着小白学Linux/Java/Python
📖 专栏汇总:
《Linux》专栏 | 《Java》专栏 | 《Python》专栏

  • [Java 实战项目:从零开发一个在线教育平台,附完整部署教程](#Java 实战项目:从零开发一个在线教育平台,附完整部署教程)
    • 一、项目概述
      • [1.1 核心功能](#1.1 核心功能)
      • [1.2 技术栈选型](#1.2 技术栈选型)
    • 二、系统架构图
    • 三、核心模块代码示例
      • [3.1 Spring Security + JWT 实现用户认证](#3.1 Spring Security + JWT 实现用户认证)
      • [3.2 课程服务分页查询接口(MyBatis Plus)](#3.2 课程服务分页查询接口(MyBatis Plus))
      • [3.3 支付回调处理逻辑(模拟支付宝)](#3.3 支付回调处理逻辑(模拟支付宝))
    • 四、开发流程说明
    • 五、部署教程
      • [5.1 项目打包(Maven)](#5.1 项目打包(Maven))
      • [5.2 Nginx 配置(反向代理 + 静态资源)](#5.2 Nginx 配置(反向代理 + 静态资源))
      • [5.3 Docker Compose 部署](#5.3 Docker Compose 部署)
      • [5.4 部署执行步骤](#5.4 部署执行步骤)
    • 六、注意事项与扩展建议
      • [6.1 注意事项](#6.1 注意事项)
      • [6.2 扩展建议](#6.2 扩展建议)
    • 七、总结

本文面向具备 Java 基础和 Spring Boot 入门知识的开发者,将带大家从零搭建一个功能完备的在线教育平台。通过实战掌握前后端分离架构、权限认证、数据持久化、缓存优化及容器化部署等核心技能,所有代码片段均可直接复用,部署步骤详细可落地。

一、项目概述

1.1 核心功能

本在线教育平台聚焦核心教学与运营需求,涵盖四大模块:

  • 用户模块:支持手机号/账号注册、登录、密码重置,基于角色的权限控制(普通用户/讲师/管理员);

  • 课程模块:课程发布、分类管理、详情展示、视频点播、章节划分、评论互动;

  • 交易模块:订单创建、购物车管理、模拟支付宝/微信支付、支付回调通知、订单查询;

  • 管理模块:用户数据统计、课程审核、订单对账、运营数据可视化。

1.2 技术栈选型

采用前后端分离架构,技术栈分工明确,兼顾开发效率与性能:

  • 后端:Spring Boot 2.7.x(快速开发框架)、Spring Security + JWT(权限认证)、MyBatis Plus(ORM 工具)、MySQL 8.0(关系型数据库)、Redis 6.x(缓存/会话存储);

  • 前端:Vue.js 3.x + Element Plus(UI 组件库)、Vue Router(路由)、Axios(HTTP 请求)、Video.js(视频播放);

  • 部署工具:Maven 3.8.x(项目打包)、Nginx 1.21.x(反向代理/静态资源服务)、Docker + Docker Compose(容器化部署);

  • 第三方服务:模拟支付宝支付接口、阿里云 OSS(视频/图片存储,可选)。

二、系统架构图

采用前后端分离架构,引入缓存、对象存储及第三方支付服务,整体架构如下(Mermaid 语法绘制,可直接复制到 Mermaid 在线编辑器 生成图表):
第三方服务层
数据层
业务层
网关层
前端层
1.HTTP请求
2.静态资源
3.接口转发
4.缓存查询
5.权限校验
6.业务逻辑
7.业务逻辑
8.业务逻辑
9.业务逻辑
10.数据操作
11.数据操作
12.数据操作
13.数据操作
14.缓存同步
15.支付请求
16.支付回调
17.视频存储
Vue.js 前端应用
Nginx反向代理
OSS/本地静态资源
Spring Boot后端服务
Redis缓存
Spring Security + JWT
用户服务
课程服务
订单支付服务
管理服务
MySQL数据库
模拟支付宝/微信支付

架构说明:前端请求经 Nginx 转发至后端服务,通过 JWT 认证后处理业务逻辑;高频数据(如用户会话、热门课程)存入 Redis 减少数据库压力;视频等大文件存储于 OSS,支付功能通过对接第三方接口实现,全程解耦,便于扩展。

三、核心模块代码示例

以下代码片段均来自实际项目,格式规范可直接运行,关键步骤添加注释说明。

3.1 Spring Security + JWT 实现用户认证

首先引入依赖(pom.xml):

xml 复制代码
<!-- Spring Security -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- JWT -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>

JWT 工具类(生成、解析 Token):

java 复制代码
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class JwtTokenUtil {
    // 过期时间(2小时)
    private static final long EXPIRATION_TIME = 7200000;

    @Value("${jwt.secret}")
    private String secret;

    // 生成Token
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("username", userDetails.getUsername());
        claims.put("roles", userDetails.getAuthorities());

        return Jwts.builder()
                .setClaims(claims)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    // 从Token中获取用户名
    public String getUsernameFromToken(String token) {
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
    }

    // 验证Token有效性
    public boolean validateToken(String token, UserDetails userDetails) {
        String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }

    // 判断Token是否过期
    private boolean isTokenExpired(String token) {
        Date expiration = Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody()
                .getExpiration();
        return expiration.before(new Date());
    }
}

Spring Security 配置类:

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Resource
    private JwtAuthenticationFilter jwtAuthFilter;

    @Resource
    private CustomUserDetailsService userDetailsService;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                // 关闭CSRF,前后端分离场景下无需开启
                .csrf(csrf -> csrf.disable())
                // 不使用Session,基于Token认证
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                // 接口权限控制
                .authorizeHttpRequests(auth -> auth
                        // 公开接口
                        .antMatchers("/api/user/register", "/api/user/login", "/api/course/list").permitAll()
                        // 管理员接口
                        .antMatchers("/api/admin/**").hasRole("ADMIN")
                        // 讲师接口
                        .antMatchers("/api/teacher/**").hasRole("TEACHER")
                        // 其他接口需认证
                        .anyRequest().authenticated()
                )
                // 自定义用户详情服务
                .userDetailsService(userDetailsService);

        // 在用户名密码认证过滤器前添加JWT过滤器
        http.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    // 密码加密器
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    // 认证管理器
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }
}

3.2 课程服务分页查询接口(MyBatis Plus)

引入 MyBatis Plus 依赖(pom.xml):

xml 复制代码
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>
<!-- 分页插件 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-extension</artifactId>
    <version>3.5.3.1</version>
</dependency>

课程实体类(Course.java):

java 复制代码
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;

@Data
@TableName("course")
public class Course {
    @TableId(type = IdType.AUTO)
    private Long id;
    // 课程名称
    private String title;
    // 课程分类ID
    private Long categoryId;
    // 讲师ID
    private Long teacherId;
    // 课程价格
    private BigDecimal price;
    // 课程封面图URL
    private String coverUrl;
    // 课程状态(0-草稿,1-已发布)
    private Integer status;
    // 创建时间
    private LocalDateTime createTime;
    // 更新时间
    private LocalDateTime updateTime;
}

课程 Mapper 接口(CourseMapper.java):

java 复制代码
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.education.entity.Course;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface CourseMapper extends BaseMapper<Course> {
    // 自定义分页查询(按分类、状态筛选)
    IPage<Course> selectCoursePage(Page<Course> page, 
                                   @Param("categoryId") Long categoryId, 
                                   @Param("status") Integer status);
}

课程服务实现类(CourseServiceImpl.java):

java 复制代码
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.education.entity.Course;
import com.example.education.mapper.CourseMapper;
import com.example.education.service.CourseService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> implements CourseService {

    @Resource
    private CourseMapper courseMapper;

    @Override
    public IPage<Course> getCoursePage(Integer pageNum, Integer pageSize, Long categoryId, Integer status) {
        // 初始化分页对象(pageNum:当前页,pageSize:每页条数)
        Page<Course> page = new Page<>(pageNum, pageSize);
        // 调用自定义分页查询
        return courseMapper.selectCoursePage(page, categoryId, status);
    }
}

课程控制器(CourseController.java):

java 复制代码
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.example.education.entity.Course;
import com.example.education.service.CourseService;
import com.example.education.vo.ResultVO;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("/api/course")
public class CourseController {

    @Resource
    private CourseService courseService;

    /**
     * 课程分页查询
     * @param pageNum 页码(默认1)
     * @param pageSize 每页条数(默认10)
     * @param categoryId 分类ID(可选)
     * @param status 课程状态(可选,0-草稿,1-已发布)
     * @return 分页结果
     */
    @GetMapping("/list")
    public ResultVO<IPage<Course>> getCourseList(
            @RequestParam(defaultValue = "1") Integer pageNum,
            @RequestParam(defaultValue = "10") Integer pageSize,
            @RequestParam(required = false) Long categoryId,
            @RequestParam(required = false) Integer status) {
        IPage<Course> coursePage = courseService.getCoursePage(pageNum, pageSize, categoryId, status);
        return ResultVO.success(coursePage);
    }
}

3.3 支付回调处理逻辑(模拟支付宝)

支付回调接口用于接收第三方支付平台的支付结果通知,需验证签名、更新订单状态,核心代码如下:

java 复制代码
import com.example.education.entity.Order;
import com.example.education.service.OrderService;
import com.example.education.util.AlipayUtil;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api/pay")
public class PayCallbackController {

    @Resource
    private OrderService orderService;

    @Resource
    private AlipayUtil alipayUtil;

    /**
     * 支付宝支付回调接口
     * 注意:真实环境需使用HTTPS,且接口地址需在支付宝开放平台配置
     */
    @PostMapping("/alipay/callback")
    public String alipayCallback(HttpServletRequest request) {
        // 1. 读取回调参数
        Map<String, String> params = new HashMap<>();
        request.getParameterMap().forEach((key, values) -> params.put(key, values[0]));

        // 2. 验证签名(模拟支付宝签名验证逻辑,真实环境需调用支付宝官方SDK)
        boolean signValid = alipayUtil.verifySign(params);
        if (!signValid) {
            // 签名验证失败,返回支付宝失败标识
            return "fail";
        }

        // 3. 处理业务逻辑
        String outTradeNo = params.get("out_trade_no"); // 商户订单号
        String tradeStatus = params.get("trade_status"); // 支付状态

        // 4. 只处理支付成功的回调(TRADE_SUCCESS:即时到账)
        if ("TRADE_SUCCESS".equals(tradeStatus)) {
            // 5. 查询订单,更新状态(防止重复回调)
            Order order = orderService.getByOrderNo(outTradeNo);
            if (order != null && order.getStatus() == 0) { // 0-未支付
                order.setStatus(1); // 1-已支付
                order.setPayTime(new java.util.Date());
                orderService.updateById(order);

                // 6. 后续业务:给用户开通课程权限、发送支付成功通知等
                // coursePermissionService.grantCoursePermission(order.getUserId(), order.getCourseId());
            }
        }

        // 7. 返回支付宝成功标识(必须返回"success",否则支付宝会重复回调)
        return "success";
    }
}

模拟支付宝签名验证工具类(AlipayUtil.java):

java 复制代码
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@Component
public class AlipayUtil {

    @Value("${alipay.appId}")
    private String appId;

    @Value("${alipay.privateKey}")
    private String privateKey;

    @Value("${alipay.publicKey}")
    private String publicKey;

    // 模拟签名验证
    public boolean verifySign(Map<String, String> params) {
        // 1. 剔除sign和sign_type参数
        Map<String, String> sortedParams = new HashMap<>(params);
        sortedParams.remove("sign");
        sortedParams.remove("sign_type");

        // 2. 按参数名ASCII码排序
        List<String> keys = new ArrayList<>(sortedParams.keySet());
        Collections.sort(keys);

        // 3. 拼接参数为字符串
        StringBuilder sb = new StringBuilder();
        for (String key : keys) {
            sb.append(key).append("=").append(sortedParams.get(key)).append("&");
        }
        String content = sb.substring(0, sb.length() - 1);

        // 4. 模拟签名验证(真实环境需用支付宝公钥验签)
        String sign = params.get("sign");
        String generatedSign = md5(content + privateKey); // 模拟签名生成逻辑
        return generatedSign.equals(sign);
    }

    // 模拟MD5加密
    private String md5(String content) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] bytes = md.digest(content.getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (byte b : bytes) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (Exception e) {
            throw new RuntimeException("MD5加密失败", e);
        }
    }
}

四、开发流程说明

按以下步骤循序渐进开发,确保各环节衔接顺畅,降低开发风险:

  1. 环境搭建(1-2天):搭建本地开发环境,包括 JDK 1.8+、Maven 3.8+、MySQL 8.0、Redis 6.x、Node.js 16+;创建 Spring Boot 后端项目和 Vue.js 前端项目,初始化目录结构,引入核心依赖。

  2. 数据库设计(1天):根据业务模块设计数据表,包括用户表(user)、角色表(role)、课程表(course)、章节表(chapter)、订单表(order)、支付记录表(pay_log)等;建立表关联,设置主键、索引、非空约束;初始化数据库脚本(SQL 文件)。

  3. 后端 API 开发(3-5天):按模块开发后端接口,先实现基础 CRUD,再开发复杂业务逻辑;集成 Spring Security + JWT 实现认证授权;引入 MyBatis Plus 分页插件、Redis 缓存;开发支付接口及回调逻辑,完成接口单元测试。

  4. 前端对接(2-3天):基于 Vue.js 开发页面,包括登录/注册页、课程列表页、课程详情页、视频播放页、购物车页、支付页等;使用 Axios 对接后端 API,处理请求拦截(添加 Token)和响应拦截(统一异常处理);实现视频播放、表单验证、分页展示等交互功能。

  5. 安全加固(1天):处理 CORS 跨域问题;添加接口参数校验(使用 JSR-380 注解);防止 SQL 注入、XSS 攻击;对敏感数据(如密码)加密存储;限制接口访问频率(基于 Redis 实现限流)。

  6. 测试优化(1-2天):进行集成测试,验证前后端交互、支付流程、权限控制等功能;排查性能问题,优化 Redis 缓存策略、数据库查询语句;修复 Bug,确保系统稳定运行。

  7. 部署上线(1天):使用 Maven 打包后端项目;配置 Nginx 反向代理;通过 Docker Compose 部署后端服务、MySQL、Redis 容器;测试线上环境功能,完成上线。

五、部署教程

本教程采用 Docker 容器化部署,简化环境依赖,可快速在服务器上搭建运行环境。

5.1 项目打包(Maven)

  1. 后端项目打包:在项目根目录执行以下命令,生成 Jar 包(输出至 target 目录):
    mvn clean package -Dmaven.test.skip=true

  2. 前端项目打包:进入前端项目根目录,执行以下命令,生成静态资源(输出至 dist 目录):
    npm run build

5.2 Nginx 配置(反向代理 + 静态资源)

创建 Nginx 配置文件(nginx.conf),内容如下:

nginx 复制代码
worker_processes  1;

events {
    worker_connections  1024;
}

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

    sendfile        on;
    keepalive_timeout  65;

    # 前端静态资源配置
    server {
        listen       80;
        server_name  localhost;

        # 前端静态资源目录(指向打包后的dist目录)
        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
            try_files $uri $uri/ /index.html; # 解决Vue路由刷新404问题
        }

        # 后端API反向代理
        location /api {
            proxy_pass http://backend:8080/api; # backend为Docker容器名
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

        # 视频资源代理(可选,若视频存储在本地)
        location /video {
            root   /usr/share/nginx/video;
            expires 7d; # 缓存7天
        }
    }
}

5.3 Docker Compose 部署

创建 docker-compose.yml 文件,整合后端服务、MySQL、Redis、Nginx 容器,内容如下:

yaml 复制代码
version: '3.8'

services:
  # MySQL数据库
  mysql:
    image: mysql:8.0
    container_name: edu-mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root123 # 根密码
      MYSQL_DATABASE: education_db # 初始化数据库名
      MYSQL_CHARSET: utf8mb4
      MYSQL_COLLATION: utf8mb4_unicode_ci
    volumes:
      - mysql-data:/var/lib/mysql # 数据持久化
      - ./sql:/docker-entrypoint-initdb.d # 初始化SQL脚本目录
    ports:
      - "3306:3306"
    networks:
      - edu-network

  # Redis缓存
  redis:
    image: redis:6-alpine
    container_name: edu-redis
    restart: always
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    command: redis-server --requirepass redis123 # 设置密码
    networks:
      - edu-network

  # 后端服务
  backend:
    image: openjdk:8-jdk-alpine
    container_name: edu-backend
    restart: always
    depends_on:
      - mysql
      - redis
    volumes:
      - ./backend/target/education-platform-0.0.1-SNAPSHOT.jar:/app/app.jar # 挂载Jar包
      - ./logs:/app/logs # 日志目录
    command: java -jar /app/app.jar --spring.profiles.active=prod
    environment:
      - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/education_db?useSSL=false&serverTimezone=Asia/Shanghai
      - SPRING_DATASOURCE_USERNAME=root
      - SPRING_DATASOURCE_PASSWORD=root123
      - SPRING_REDIS_HOST=redis
      - SPRING_REDIS_PASSWORD=redis123
      - JWT_SECRET=edu-platform-jwt-secret-key-2026
    networks:
      - edu-network

  # Nginx服务
  nginx:
    image: nginx:1.21-alpine
    container_name: edu-nginx
    restart: always
    depends_on:
      - backend
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf # 挂载配置文件
      - ./frontend/dist:/usr/share/nginx/html # 挂载前端静态资源
      - ./video:/usr/share/nginx/video # 挂载视频资源(可选)
      - ./nginx/logs:/var/log/nginx # 挂载日志目录
    networks:
      - edu-network

# 数据卷(持久化数据)
volumes:
  mysql-data:
  redis-data:

# 自定义网络(容器间通信)
networks:
  edu-network:
    driver: bridge

5.4 部署执行步骤

  1. 服务器准备:安装 Docker 和 Docker Compose(参考 官方文档)。

  2. 目录结构整理:在服务器上创建如下目录结构,将对应文件放入目录:

    edu-platform/

    ├── backend/

    │ └── target/

    │ └── education-platform-0.0.1-SNAPSHOT.jar # 后端Jar包

    ├── frontend/

    │ └── dist/ # 前端打包后的静态资源

    ├── nginx/

    │ ├── nginx.conf # Nginx配置文件

    │ └── logs/ # Nginx日志目录

    ├── sql/

    │ └── init.sql # 数据库初始化脚本

    ├── video/ # 视频资源目录(可选)

    ├── logs/ # 后端日志目录

    └── docker-compose.yml # Docker Compose配置文件

  3. 启动服务:在 edu-platform 目录下执行以下命令,启动所有容器:
    docker-compose up -d

  4. 验证服务:

  5. 停止服务(可选):
    docker-compose down

六、注意事项与扩展建议

6.1 注意事项

  • HTTPS 配置:生产环境必须启用 HTTPS,避免数据传输泄露。可通过 Let's Encrypt 申请免费 SSL 证书,在 Nginx 中配置证书路径和 443 端口,同时将 HTTP 请求重定向至 HTTPS。

  • CORS 处理 :前后端分离项目需正确配置 CORS,后端可通过 Spring Boot 全局配置允许指定域名跨域,避免浏览器跨域拦截。示例配置:

    @Configuration

    public class CorsConfig implements WebMvcConfigurer {

    @Override

    public void addCorsMappings(CorsRegistry registry) {

    registry.addMapping("/api/**")

    .allowedOrigins("https://your-domain.com") // 允许的前端域名

    .allowedMethods("GET", "POST", "PUT", "DELETE")

    .allowedHeaders("*")

    .allowCredentials(true)

    .maxAge(3600);

    }

    }

  • 敏感信息加密:数据库密码、JWT 密钥、支付平台密钥等敏感信息,不可硬编码在代码中,可通过配置中心(如 Nacos)管理,或使用 Docker 环境变量注入。

  • 日志管理:生产环境需集成 Logback/Log4j2 实现日志分级输出,同时配置日志滚动策略,避免日志文件过大;可将日志输出至 ELK 栈,便于日志分析和问题排查。

6.2 扩展建议

  • 高并发优化

    • 缓存优化:热门课程、首页数据等高频访问数据,通过 Redis 缓存,设置合理的过期时间和缓存更新策略(如布隆过滤器防止缓存穿透);

    • 数据库优化:分库分表(如订单表按时间分表)、添加索引、开启 MySQL 读写分离;

    • 服务集群:将后端服务部署为集群,通过 Nginx 或 Spring Cloud Gateway 实现负载均衡;

    • 异步处理:支付回调、消息通知等非核心流程,通过 RabbitMQ/Kafka 实现异步处理,提高接口响应速度。

  • 功能扩展

    • 集成真实支付接口:替换模拟支付逻辑,对接支付宝/微信支付官方 SDK,完成商户入驻和接口配置;

    • 视频点播优化:集成阿里云/腾讯云视频点播服务,实现视频转码、加密播放、防盗链等功能;

    • 用户画像与推荐:基于用户浏览、购买记录,通过协同过滤算法实现课程个性化推荐;

    • 多端适配:开发小程序、APP 端,通过统一后端 API 实现多端数据同步。

  • 运维优化

    • 监控告警:集成 Prometheus + Grafana 监控服务器资源、容器状态、接口性能,设置异常告警(如邮件、钉钉通知);

    • 自动部署:通过 Jenkins/GitLab CI 实现代码提交后自动打包、测试、部署,提高迭代效率;

    • 备份策略:定期备份 MySQL 数据,可通过 Docker 数据卷结合脚本实现自动备份,避免数据丢失。

七、总结

本文通过从零开发在线教育平台,详细讲解了前后端分离架构、权限认证、数据持久化、容器化部署等核心技术,代码片段可直接复用,部署步骤清晰可落地。开发者可基于本文内容,根据实际需求扩展功能、优化性能,逐步掌握企业级 Java 项目的开发与运维能力。建议在实战过程中多调试、多思考,深入理解各技术栈的底层原理,提升问题排查和系统设计能力。

📕个人领域 :Linux/C++/java/AI

🚀 个人主页有点流鼻涕 · CSDN

💬 座右铭 : "向光而行,沐光而生。"

相关推荐
Scott9999HH7 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔7 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社8 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海8 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖9 小时前
JDK 26 新特性详解
java·开发语言
马优晨9 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区11 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大11 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai12 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说12 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json