🎓 手把手教你搭建一个「学生证书管理系统」— Spring Boot + Vue 3 全栈毕设开源项目

适合人群:Java/Vue 初学者、正在做毕设的同学、想学习全栈项目实战的新手
技术栈:Spring Boot 4.0 + Vue 3 + Element Plus + JWT + MySQL/H2
代码量:后端 ~1500 行,前端 ~1200 行,适合新手
开源地址:文末附完整项目结构


📖 目录

  1. 项目是干什么的?
  2. 系统功能一览
  3. 技术栈选型——为什么选这些?
  4. 数据库设计——两张表搞定
  5. 后端核心实现——逐层拆解
  6. 前端核心实现——页面是怎么跑起来的
  7. 项目亮点——毕设答辩加分项
  8. 如何跑起来?
  9. 总结 & 可扩展方向

1. 项目是干什么的?

简单说,这是一个 学生提交证书 + 教师审核 + 导出报表 的 Web 系统。

真实场景:大学里经常需要学生提交各种证书(四六级、计算机等级、竞赛获奖等),辅导员或教务老师要一个个收集、审核、汇总。用 Excel 传来传去很乱,这个系统就是解决这个痛点的。

两种用户角色

角色 能干什么
🧑‍🎓 学生 注册登录 → 提交证书(名称+机构+日期+编号+附件图片/PDF)→ 查看审核结果
👩‍🏫 教师 注册登录 → 查看所有学生证书 → 审核(通过/拒绝+意见)→ 导出 Excel / ZIP 压缩包

2. 系统功能一览

2.1 学生端

注册/登录 → 首页仪表盘 → 我的证书列表 → 提交新证书 → 编辑/删除证书 → 查看审核意见
  • 注册:选择"学生"角色,填写用户名、密码、姓名
  • 提交证书:填写证书名称、颁发机构、获得日期、证书编号,还可以拖拽上传证书图片/PDF
  • 证书列表:按状态筛选(待审核 / 已通过 / 已拒绝)
  • 编辑/删除:审核前可以修改或删除

2.2 教师端

注册/登录 → 首页仪表盘 → 证书审核页 → 通过/拒绝 + 审核意见 → 导出 Excel / ZIP
  • 审核:看到所有学生的证书,点击"通过"或"拒绝",填写审核意见
  • 导出 Excel:导出所有证书的文字信息(名称、学生、状态等),方便打印
  • 导出 ZIP:按学生姓名分文件夹,打包所有证书图片 + 一份汇总 CSV

2.3 系统截图(文字版)

点击展开页面结构说明
┌──────────────────────────────────────────────┐
│  侧边栏                │  主内容区            │
│  ┌─────────────┐      │  ┌──────────────┐   │
│  │ 🏫 系统名    │      │  │ 页面标题      │   │
│  ├─────────────┤      │  ├──────────────┤   │
│  │ 📊 首页     │      │  │              │   │
│  │ 📄 我的证书  │      │  │  表格/表单/   │   │
│  │ ➕ 提交证书  │      │  │  数据展示     │   │
│  └─────────────┘      │  │              │   │
│   (可折叠)            │  └──────────────┘   │
└──────────────────────────────────────────────┘

3. 技术栈选型——为什么选这些?

后端

技术 作用 为什么选它?
Spring Boot 4.0 后端框架 Java 生态最主流,自动配置省心,新手友好
Spring Security 权限控制 和 Spring Boot 无缝集成,注解式鉴权
JWT 身份认证 无状态,不用存 Session,适合前后端分离
JPA/Hibernate 数据库操作 不用手写 SQL,Entity 类直接映射成表
H2 内存数据库 开发环境 零安装!程序启动就有数据库,毕设演示超方便
Apache POI Excel 导出 Java 操作 Excel 的事实标准

前端

技术 作用 为什么选它?
Vue 3 前端框架 中文文档最好,上手快,组合式 API 更灵活
Element Plus UI 组件库 饿了么出品,组件丰富(表格/表单/弹窗全有)
Vue Router 路由管理 页面跳转 + 路由守卫,不登录就跳回登录页
Pinia 状态管理 Vue 官方推荐,比 Vuex 简洁太多
Axios HTTP 请求 拦截器自动带 JWT Token,自动处理 401

💡 新手选型口诀

“Spring Boot 做后端、Vue 3 做前端、Element Plus 画界面、JWT 管登录、H2 省装数据库”
这套组合是 2024-2026 年毕设最常见的搭配,网上资料最多,出问题最好查。


4. 数据库设计——两张表搞定

整个系统只需要 两张表,这可能是你见过最简单的毕设数据库设计了。

4.1 用户表(users)

CREATE TABLE users (
    id          BIGINT AUTO_INCREMENT PRIMARY KEY,  -- 用户ID,自增
    username    VARCHAR(50)  NOT NULL UNIQUE,        -- 登录用户名
    password    VARCHAR(255) NOT NULL,               -- 密码(BCrypt加密)
    name        VARCHAR(100) NOT NULL,               -- 真实姓名
    role        VARCHAR(20)  NOT NULL,               -- STUDENT 或 TEACHER
    email       VARCHAR(100),                        -- 邮箱(选填)
    phone       VARCHAR(20),                         -- 电话(选填)
    created_at  DATETIME,                            -- 注册时间
    updated_at  DATETIME                             -- 更新时间
);

4.2 证书表(certificates)

CREATE TABLE certificates (
    id              BIGINT AUTO_INCREMENT PRIMARY KEY,  -- 证书ID
    name            VARCHAR(200) NOT NULL,               -- 证书名称
    issuing_org     VARCHAR(200) NOT NULL,               -- 颁发机构
    issue_date      DATE         NOT NULL,               -- 获得日期
    cert_number     VARCHAR(100) NOT NULL,               -- 证书编号
    file_path       VARCHAR(500),                        -- 附件存储路径
    file_name       VARCHAR(255),                        -- 原始文件名
    file_type       VARCHAR(50),                         -- 文件类型
    file_size       BIGINT,                              -- 文件大小(字节)
    status          VARCHAR(20)  NOT NULL DEFAULT 'PENDING',  -- 审核状态
    user_id         BIGINT       NOT NULL,               -- 提交学生(外键)
    reviewer_id     BIGINT,                              -- 审核教师(外键)
    review_comment  VARCHAR(500),                        -- 审核意见
    created_at      DATETIME,
    updated_at      DATETIME,
    FOREIGN KEY (user_id)     REFERENCES users(id),
    FOREIGN KEY (reviewer_id) REFERENCES users(id)
);

📊 两张表的关系

┌──────────────┐         ┌──────────────────────┐
│    users     │ 1─────N │    certificates      │
│              │         │                      │
│ id (PK)      │◄────────│ user_id (FK)         │
│ username     │         │ reviewer_id (FK)     │──┐
│ role         │         │ name, issuing_org... │  │
│ name         │         │ status: PENDING/     │  │
└──────────────┘         │   APPROVED/REJECTED  │  │
       ▲                 │ file_path            │  │
       └─────────────────│ reviewer_id (FK)     │──┘
                         └──────────────────────┘
  • 一个学生可以提交多个证书(一对多
  • 一个教师可以审核多个证书(一对多
  • 证书状态流转:PENDING(待审核)→ APPROVED(通过)/ REJECTED(拒绝)

5. 后端核心实现——逐层拆解

后端采用经典的 Controller → Service → Repository 三层架构:

请求进来 → Controller(接收参数)→ Service(处理业务)→ Repository(操作数据库)→ 返回 JSON

5.1 项目结构一览

backend/src/main/java/com/example/studentms/
├── StudentMsApplication.java          # Spring Boot 启动类
├── config/                            # 配置层
│   ├── SecurityConfig.java            # Spring Security 安全配置
│   ├── JwtUtil.java                   # JWT 生成/解析/验证工具
│   ├── JwtAuthFilter.java             # JWT 过滤器(每个请求先过这里)
│   ├── WebConfig.java                 # 跨域配置
│   └── PasswordEncoderConfig.java     # 密码加密器
├── entity/                            # 实体层(对应数据库表)
│   ├── User.java                      # 用户实体
│   └── Certificate.java               # 证书实体
├── dto/                               # 数据传输对象(前端传什么、后端回什么)
│   ├── LoginRequest.java              # 登录请求
│   ├── RegisterRequest.java           # 注册请求
│   ├── LoginResponse.java             # 登录响应(含 JWT Token)
│   ├── CertificateRequest.java        # 证书提交/编辑请求
│   ├── CertificateResponse.java       # 证书列表/详情响应
│   └── ReviewRequest.java             # 审核请求
├── repository/                        # 数据访问层(JPA 接口)
│   ├── UserRepository.java            # 用户查询
│   └── CertificateRepository.java     # 证书查询
├── service/                           # 业务逻辑层
│   ├── AuthService.java               # 注册/登录逻辑
│   ├── CertificateService.java        # 证书 CRUD + 审核
│   ├── FileStorageService.java        # 文件存储服务
│   └── ExportService.java             # Excel / ZIP 导出
├── controller/                        # 控制器层(路由入口)
│   ├── AuthController.java            # /api/auth/* 注册登录
│   ├── CertificateController.java     # /api/certificates/* 证书操作
│   └── FileController.java            # /api/files/* 文件下载
└── exception/                         # 异常处理
    ├── BusinessException.java         # 自定义业务异常
    └── GlobalExceptionHandler.java    # 全局异常拦截

💡 新手提示:这就是典型的 Spring Boot 分层结构,毕设按这个结构来写,答辩时层次清晰、涨分!

5.2 认证流程——JWT 是怎么工作的?

这是很多新手最困惑的地方,我画个流程图就清楚了:

【注册】
  用户填表单 → POST /api/auth/register → 密码用 BCrypt 加密 → 存入数据库

【登录】
  用户填用户名密码 → POST /api/auth/login
  → 查数据库验证密码(BCrypt 比对)
  → 生成 JWT Token(包含用户名+角色,24小时有效)
  → 返回 Token 给前端
  → 前端存到 localStorage

【后续请求】
  前端每次请求都在 Header 里带 "Authorization: Bearer <Token>"
  → JwtAuthFilter 拦截请求
  → 解析 Token,验证签名和有效期
  → 把用户信息放入 SecurityContext
  → Controller 通过 @AuthenticationPrincipal 拿到当前用户

核心代码JwtUtil.java):

// 生成 Token:把用户名和角色写进去,签名,设有效期
public String generateToken(UserDetails userDetails) {
    String role = userDetails.getAuthorities().stream()
            .findFirst().map(GrantedAuthority::getAuthority)
            .orElse("ROLE_STUDENT");
    
    return Jwts.builder()
            .subject(userDetails.getUsername())      // 存用户名
            .claim("role", role)                     // 存角色
            .issuedAt(new Date())                    // 签发时间
            .expiration(new Date(System.currentTimeMillis() + expiration))  // 过期时间
            .signWith(getKey())                      // HMAC-SHA256 签名
            .compact();
}

5.3 权限控制——学生和教师怎么区分?

用 Spring Security 的注解,超级简单:

// SecurityConfig.java 中的 URL 级别控制
.requestMatchers("/api/auth/**").permitAll()                    // 注册登录 - 谁都能访问
.requestMatchers("/api/certificates/all", 
                 "/api/certificates/export").hasRole("TEACHER")  // 只有教师能访问
.requestMatchers("/api/certificates/**").hasAnyRole("STUDENT", "TEACHER")  // 学生教师都能访问

用户实体 实现 UserDetails 接口,把角色映射成 Spring Security 的权限:

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    return List.of(new SimpleGrantedAuthority("ROLE_" + role.name()));
    // STUDENT → ROLE_STUDENT
    // TEACHER → ROLE_TEACHER
}

5.4 证书 CRUD —— 核心业务代码

以"学生提交证书"为例,展示完整的数据流转:

// CertificateController.java —— 接收前端请求
@PostMapping
public ResponseEntity<CertificateResponse> create(
        @RequestPart("data") @Valid CertificateRequest request,    // JSON 数据
        @RequestPart(value = "file", required = false) MultipartFile file,  // 附件文件
        @AuthenticationPrincipal UserDetails userDetails) {        // 当前登录用户
        
    User student = authService.getCurrentUser(userDetails.getUsername());
    CertificateResponse response = certificateService.create(request, file, student);
    return ResponseEntity.ok(response);   // 返回 200 + JSON
}

// CertificateService.java —— 处理业务逻辑
@Transactional
public CertificateResponse create(CertificateRequest request, MultipartFile file, User student) {
    // 1. 创建实体对象
    Certificate certificate = new Certificate();
    certificate.setName(request.getName());
    certificate.setIssuingOrg(request.getIssuingOrg());
    certificate.setIssueDate(request.getIssueDate());
    certificate.setCertNumber(request.getCertNumber());
    certificate.setUser(student);              // 关联学生
    certificate.setStatus(Certificate.Status.PENDING);  // 默认待审核
    
    // 2. 处理文件上传
    if (file != null && !file.isEmpty()) {
        String storedName = fileStorageService.store(file);  // 存到 uploads/ 目录
        certificate.setFilePath(storedName);
        certificate.setFileName(file.getOriginalFilename());
        certificate.setFileSize(file.getSize());
    }
    
    // 3. 保存到数据库
    certificate = certificateRepository.save(certificate);
    
    // 4. 转成 DTO 返回(不直接暴露 Entity)
    return toResponse(certificate);
}

💡 关键概念:为什么要有 DTO?
Entity 直接和数据库挂钩,包含敏感字段(如 password);DTO 只返回前端需要的字段。Entity → DTO 转换 是后端开发的铁律。

5.5 Excel 导出——Apache POI 实战

// ExportService.java
public void exportToExcel(OutputStream outputStream, String status) {
    List<Certificate> certificates = certificateService.getCertificatesForExport(status);
    
    try (Workbook workbook = new XSSFWorkbook()) {   // 创建 .xlsx 工作簿
        Sheet sheet = workbook.createSheet("证书列表");
        
        // 1. 创建表头行(加粗 + 灰色背景)
        Row header = sheet.createRow(0);
        String[] columns = {"序号", "证书名称", "颁发机构", "获得日期", 
                            "证书编号", "学生姓名", "审核状态", "审核意见", "提交时间"};
        for (int i = 0; i < columns.length; i++) {
            Cell cell = header.createCell(i);
            cell.setCellValue(columns[i]);
            cell.setCellStyle(headerStyle);   // 加粗、背景色
        }
        
        // 2. 填充数据行
        int rowIdx = 1;
        for (Certificate c : certificates) {
            Row row = sheet.createRow(rowIdx++);
            row.createCell(0).setCellValue(rowIdx - 1);      // 序号
            row.createCell(1).setCellValue(c.getName());      // 证书名
            row.createCell(2).setCellValue(c.getIssuingOrg());// 颁发机构
            // ... 其他列
        }
        
        // 3. 自动调整列宽
        for (int i = 0; i < columns.length; i++) {
            sheet.autoSizeColumn(i);
        }
        
        workbook.write(outputStream);  // 写出到 HTTP 响应的 OutputStream
    }
}

学习 POI 只需要记住三个对象:

  • Workbook = 整个 Excel 文件
  • Sheet = 一个工作表
  • Row / Cell = 行 / 单元格

5.6 ZIP 导出——按学生分文件夹打包

这是本项目的一个独特功能,不只是导出 Excel,还把所有学生上传的证书图片/PDF 按学生分文件夹打成 ZIP:

// ZIP 内部结构:
// 张三_zhangsan/
//     OCJP认证_OCJP-001.pdf
//     英语六级_ENG-2025001.jpg
// 李四_lisi/
//     计算机二级_CS-2025032.pdf
// index.csv  ← 所有证书的总汇总表

核心代码逻辑:先按学生分组 → 遍历每个学生的证书 → 读取上传的文件写入 ZIP → 追加 index.csv。


6. 前端核心实现——页面是怎么跑起来的

6.1 项目结构

frontend/src/
├── main.ts                    # 入口:创建 Vue 应用,挂载 Router 和 Pinia
├── App.vue                    # 根组件
├── api/                       # API 请求封装
│   ├── index.ts               # Axios 实例 + 拦截器
│   ├── auth.ts                # 登录/注册 API
│   └── certificate.ts         # 证书相关 API
├── router/
│   └── index.ts               # 路由配置 + 导航守卫
├── stores/
│   └── auth.ts                # Pinia 状态管理(用户信息)
├── components/
│   └── AppLayout.vue          # 主布局:侧边栏 + 顶栏 + 内容区
└── views/
    ├── Login.vue              # 登录页
    ├── Register.vue           # 注册页
    ├── student/
    │   ├── StudentDashboard.vue  # 学生首页(统计数据)
    │   ├── CertificateList.vue   # 证书列表(筛选/查看/删除)
    │   └── CertificateForm.vue   # 提交/编辑证书表单
    └── teacher/
        ├── TeacherDashboard.vue   # 教师首页
        ├── CertificateReview.vue  # 审核页面
        └── CertificateExport.vue  # 导出页面

6.2 路由设计——页面怎么跳转的?

// router/index.ts
const routes = [
  // 公开页面(未登录也能访问)
  { path: '/login',    component: Login,    meta: { guest: true } },
  { path: '/register', component: Register, meta: { guest: true } },
  
  // 学生专区(需要登录 + STUDENT 角色)
  { path: '/student', component: AppLayout, meta: { requiresAuth: true, role: 'STUDENT' },
    children: [
      { path: '',                    component: StudentDashboard },
      { path: 'certificates',       component: CertificateList },
      { path: 'certificates/new',   component: CertificateForm },
      { path: 'certificates/:id/edit', component: CertificateForm },
    ]
  },
  
  // 教师专区(需要登录 + TEACHER 角色)
  { path: '/teacher', component: AppLayout, meta: { requiresAuth: true, role: 'TEACHER' },
    children: [
      { path: '',               component: TeacherDashboard },
      { path: 'certificates',   component: CertificateReview },
      { path: 'export',         component: CertificateExport },
    ]
  },
  
  // 其他路径全部重定向到登录页
  { path: '/:pathMatch(.*)*', redirect: '/login' }
]

6.3 路由守卫——不登录就进不来!

// 每次跳转页面前都会执行这个函数
router.beforeEach((to, from, next) => {
  const token = localStorage.getItem('token')
  const role = localStorage.getItem('role')
  
  // 已登录用户访问登录/注册页 → 跳到对应首页
  if (to.meta.guest && token) {
    return next(role === 'TEACHER' ? '/teacher' : '/student')
  }
  
  // 没登录访问需要登录的页面 → 跳回登录页
  if (to.meta.requiresAuth && !token) {
    return next('/login')
  }
  
  // 学生访问教师页面(或反过来)→ 跳回自己的首页
  if (to.meta.role && to.meta.role !== role) {
    return next(role === 'TEACHER' ? '/teacher' : '/student')
  }
  
  next()  // 放行
})

这就是前端权限控制的核心——三行判断,覆盖所有越权情况

6.4 Axios 拦截器——自动带 Token、自动处理错误

// api/index.ts

// 请求拦截器:每次请求自动带上 JWT Token
api.interceptors.request.use((config) => {
  const token = localStorage.getItem('token')
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  // FormData 请求不要设 Content-Type,让浏览器自动处理 multipart 边界
  if (config.data instanceof FormData) {
    delete config.headers['Content-Type']
  }
  return config
})

// 响应拦截器:统一处理错误
api.interceptors.response.use(
  (response) => response,    // 正常响应直接返回
  (error) => {
    if (error.response?.status === 401) {
      // Token 过期 → 清空登录状态 → 跳转登录页
      localStorage.clear()
      router.push('/login')
    } else if (error.response?.status === 403) {
      ElMessage.error('权限不足')
    } else {
      ElMessage.error(error.response?.data?.message || '请求失败')
    }
    return Promise.reject(error)
  }
)

有了这两个拦截器,每个 API 调用都不用手动处理 Token 和错误提示,代码清爽很多。

6.5 Pinia 状态管理——登录信息全局共享

// stores/auth.ts
export const useAuthStore = defineStore('auth', () => {
  const token = ref(localStorage.getItem('token') || '')
  const role = ref(localStorage.getItem('role') || '')
  const name = ref(localStorage.getItem('name') || '')
  
  const isStudent = () => role.value === 'STUDENT'
  const isTeacher = () => role.value === 'TEACHER'
  
  async function login(data: LoginRequest) {
    const res = await authApi.login(data)
    token.value = res.token
    role.value = res.role
    name.value = res.name
    // 同步存到 localStorage,防止刷新后丢失
    localStorage.setItem('token', res.token)
    localStorage.setItem('role', res.role)
    localStorage.setItem('name', res.name)
  }
  
  function logout() {
    token.value = ''
    role.value = ''
    name.value = ''
    localStorage.clear()
  }
  
  return { token, role, name, isStudent, isTeacher, login, logout }
})

💡 新手提示:为什么既用 Pinia 又用 localStorage?
Pinia 存内存中,刷新就没了;localStorage 持久化。两个都存,Pinia 用来响应式更新 UI,localStorage 用来刷新后恢复状态。


7. 项目亮点——毕设答辩加分项

看完上面,你可能觉得"这不就是一个增删改查的系统吗?"。确实骨架是 CRUD,但这个项目有几个让答辩老师眼前一亮的设计:

⭐ 亮点 1:双角色权限体系

不是简单的"登录/没登录",而是 学生/教师两种角色 + URL 级别 + 数据级别的双重权限

  • URL 级别:教师才能访问 /api/certificates/all,学生只能访问 /api/certificates(自己的)
  • 数据级别:学生只能看到自己的证书,Service 层校验 if (!certificate.getUser().getId().equals(student.getId())) throw 403

这是企业级权限控制的简化版,答辩时可以说"实现了 RBAC(基于角色的访问控制)"。

⭐ 亮点 2:H2 开发 + MySQL 生产 双环境切换

Spring Boot Profile 机制,改一行配置就能从 H2 内存数据库切到 MySQL:

# application.yml
spring:
  profiles:
    active: dev   # 改成 prod 就切 MySQL
  • dev 环境:H2 内存数据库,程序启动自动建表,演示时不需要装任何数据库
  • prod 环境:MySQL,数据持久化,可以真正部署上线

⭐ 亮点 3:文件上传 + 在线预览

multipart/form-data 上传,支持拖拽,支持图片和 PDF。教师可以直接在浏览器预览学生上传的证书附件。

⭐ 亮点 4:双格式导出(Excel + ZIP)

大多数毕设只做 Excel 导出。这个项目还做了 ZIP 导出——按学生分文件夹打包证书图片 + index.csv 汇总表。这在真实场景中非常实用,答辩时可以强调"考虑了实际使用便利性"。

⭐ 亮点 5:前后端分离 + JWT 无状态认证

  • 前后端完全解耦,可以分别部署到不同服务器
  • JWT 让服务端不用存 Session,水平扩展友好
  • 响应拦截器统一处理 401 过期跳转,用户体验好

8. 如何跑起来?

环境准备

工具 版本要求 安装方式
Java 25+ 已安装即可
Node.js 22+ winget install OpenJS.NodeJS.LTS
Maven 不需要 项目自带 mvnw.cmd(Maven Wrapper)
MySQL 不需要 开发环境用 H2 内存数据库
IDE 任意 VS Code / IntelliJ IDEA 都可以

三步启动

第一步:启动后端

cd backend
./mvnw.cmd spring-boot:run

看到 Started StudentMsApplication 就成功了,后端跑在 http://localhost:8080

可以访问 http://localhost:8080/h2-console 查看数据库(JDBC URL: jdbc:h2:mem:studentms,用户名 sa,密码空)

第二步:启动前端

cd frontend
npm install        # 第一次运行需要装依赖
npm run dev

前端跑在 http://localhost:5173

第三步:开始使用

  1. 打开 http://localhost:5173/register
  2. 先注册一个学生账号,再注册一个教师账号
  3. 学生登录 → 提交证书
  4. 教师登录 → 审核证书 → 导出 Excel / ZIP

切换 MySQL(如需部署)

  1. 在 MySQL 中建库:
CREATE DATABASE studentms CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
  1. 修改 application.yml
spring:
  profiles:
    active: prod   # 从 dev 改成 prod
  1. 编辑 application-prod.yml 里的数据库连接信息。

9. 总结 & 可扩展方向

适合什么样的人?

你的情况 建议
刚开始学 Spring Boot ✅ 看 entity、repository、controller 三层,理解 MVC 架构
刚开始学 Vue 3 ✅ 看组件拆分、路由守卫、Pinia 状态管理
正在做毕设 ✅ 直接拿去用,改改前端页面 + 加个你专业领域的字段
准备面试 ✅ 把 JWT、RBAC、文件上传、Excel 导出说清楚,面试加分

可以继续扩展的方向

扩展点 难度 说明
批量审核 表格加复选框,一键批量通过
证书分类/标签 certificates 表加 category 字段
数据可视化图表 ⭐⭐ 前端加 ECharts,按月份/状态统计证书数量
邮件通知 ⭐⭐ 审核通过后自动发邮件给学生
Docker 部署 ⭐⭐ 写 Dockerfile + docker-compose.yml
Redis 缓存 ⭐⭐⭐ Token 黑名单、热点数据缓存

写在最后

这个项目麻雀虽小五脏俱全,覆盖了全栈开发的核心知识点:注册登录、角色权限、文件上传、CRUD、Excel/ZIP 导出、前后端联调。非常适合作为 Spring Boot + Vue 3 的入门实战项目,也完全可以当作毕设的基础框架,在此基础上加你自己的业务字段和功能。

如果对你有帮助,欢迎 点赞 👍 + 收藏 ⭐ + 关注 三连支持!有问题欢迎评论区交流~
需要源代码源代码评论区扣1


作者:林行
技术栈:Spring Boot 4.0 + Vue 3 + Element Plus + JWT + H2/MySQL
适用场景:毕业设计 / 课程设计 / 新手全栈学习
项目结构:完整前后端分离,后端 ~1500 行,前端 ~1200 行

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐