【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

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

相关推荐
开开心心_Every2 小时前
免费视频画质增强:智能超分辨率无损放大
java·服务器·前端·python·学习·edge·powerpoint
FJW0208142 小时前
Python面向对象三大特征封装,继承,多态
开发语言·python
正在走向自律2 小时前
时序数据管理:金仓数据库破局之道
java·后端·struts·时序数据库·金仓kes v9
七夜zippoe2 小时前
Python算法优化实战:时间与空间复杂度的艺术平衡
开发语言·python·算法·贪心算法·动态规划·复杂度
全栈前端老曹2 小时前
【前端】Hammer.js 快速上手入门教程
开发语言·前端·javascript·vue·react·移动端开发·hammer.js
学编程的小程2 小时前
告别链接混乱❗️Sun-Panel+cpolar 让 NAS 服务远程一键直达
java·开发语言
青槿吖2 小时前
【Java集合通关秘籍】从List到Set:解锁无序不重复的集合魔法✨
java·开发语言·算法
do better myself2 小时前
php导入关键词的脚本 300条分批导入
java·服务器·前端
冬奇Lab2 小时前
【Kotlin系列07】类型系统深度解析:从空安全到智能类型推断的设计哲学
android·开发语言·安全·kotlin