通用后台管理系统 / 毕业设计指南
一、通用后台管理系统架构
1.1 前后端分离架构
通用后台管理系统采用前后端分离架构,前端负责页面渲染与用户交互,后端提供 RESTful API 服务。
前端技术栈方案
| 方案 | 技术栈 | 适用场景 |
|---|---|---|
| Vue 方案 | Vue 3 + Element Plus + Pinia + Vue Router + Vite | 中小型项目,开发效率高 |
| React 方案 | React 18 + Ant Design + Redux Toolkit + React Router + Vite | 大型项目,组件生态丰富 |
| 混合方案 | Vue 3 + Ant Design Vue / Naive UI | 按需选择 UI 框架 |
后端技术栈(Spring Boot)
| 层级 | 技术选型 | 说明 |
|---|---|---|
| 核心框架 | Spring Boot 2.7+ / 3.x | 自动配置、起步依赖 |
| ORM | MyBatis-Plus / Spring Data JPA | 数据持久化 |
| 安全框架 | Spring Security + JWT | 认证与授权 |
| API 文档 | SpringDoc OpenAPI (Swagger) / Knife4j | 接口文档自动生成 |
| 数据库 | MySQL 8.0+ | 关系型数据库 |
| 缓存 | Redis | 会话缓存、数据缓存 |
| 工具库 | Hutool / Guava | 通用工具 |
项目结构(Maven 多模块)
admin-system/
├── admin-common # 公共模块:工具类、统一返回结果、异常定义
│ ├── src/main/java
│ └── src/main/resources
├── admin-framework # 框架配置:安全、日志、跨域、全局异常处理
│ ├── src/main/java
│ └── src/main/resources
├── admin-system # 核心业务:用户、角色、菜单、字典、日志
│ ├── src/main/java
│ │ ├── controller
│ │ ├── service
│ │ ├── mapper
│ │ ├── entity
│ │ └── dto
│ └── src/main/resources
│ └── mapper
├── admin-generator # 代码生成器模块(可选)
├── admin-monitor # 系统监控(可选)
└── admin-api # 对外 API 接口模块(可选)1.2 RBAC 权限模型
RBAC(Role-Based Access Control,基于角色的访问控制)是后台管理系统最通用的权限模型。
模型关系
用户 (User) ──多对多──> 角色 (Role) ──多对多──> 菜单 (Menu)
│
└── 按钮权限 (Permission)- 一个用户可以拥有多个角色
- 一个角色可以关联多个菜单/权限
- 菜单支持多级树形结构
- 按钮权限作为菜单下的细粒度权限标识
数据库表设计
-- 用户表
CREATE TABLE sys_user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL,
nickname VARCHAR(50),
email VARCHAR(100),
phone VARCHAR(20),
avatar VARCHAR(200),
sex TINYINT DEFAULT 0,
status TINYINT DEFAULT 1 COMMENT '0=禁用 1=正常',
del_flag TINYINT DEFAULT 0,
dept_id BIGINT COMMENT '所属部门',
create_by VARCHAR(50),
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_by VARCHAR(50),
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 角色表
CREATE TABLE sys_role (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
role_name VARCHAR(50) NOT NULL,
role_key VARCHAR(50) NOT NULL UNIQUE COMMENT '角色标识',
role_sort INT DEFAULT 0,
status TINYINT DEFAULT 1,
del_flag TINYINT DEFAULT 0,
remark VARCHAR(500),
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 用户角色关联表
CREATE TABLE sys_user_role (
user_id BIGINT NOT NULL,
role_id BIGINT NOT NULL,
PRIMARY KEY (user_id, role_id)
);
-- 菜单表(支持多级树形 + 按钮权限)
CREATE TABLE sys_menu (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
menu_name VARCHAR(50) NOT NULL,
parent_id BIGINT DEFAULT 0,
order_num INT DEFAULT 0,
path VARCHAR(200) COMMENT '路由地址',
component VARCHAR(200) COMMENT '组件路径',
query VARCHAR(200) COMMENT '路由参数',
is_frame TINYINT DEFAULT 1 COMMENT '是否外链',
menu_type CHAR(1) DEFAULT 'M' COMMENT 'M=目录 C=菜单 F=按钮',
visible TINYINT DEFAULT 1,
status TINYINT DEFAULT 1,
perms VARCHAR(100) COMMENT '权限标识,如 system:user:list',
icon VARCHAR(100),
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 角色菜单关联表
CREATE TABLE sys_role_menu (
role_id BIGINT NOT NULL,
menu_id BIGINT NOT NULL,
PRIMARY KEY (role_id, menu_id)
);1.3 通用功能模块
登录/登出
采用 JWT(JSON Web Token)无状态认证方案:
- 用户提交用户名 + 密码
- 后端验证通过后生成 JWT,包含用户 ID、用户名、角色标识
- 前端将 Token 存储在 localStorage 或 Pinia/Vuex 中
- 每次请求通过 Authorization Header 携带 Token
- 后端通过拦截器 / Spring Security 过滤器解析 Token
- 登出时前端清除 Token,后端可将 Token 加入黑名单(Redis)
密码修改
- 用户输入原密码、新密码、确认密码
- 后端校验原密码是否正确
- 校验新密码复杂度(长度、大小写、特殊字符)
- 使用 BCryptPasswordEncoder 加密存储
个人中心
- 查看/编辑个人信息(昵称、邮箱、手机号、头像)
- 头像上传:使用文件上传接口,返回文件 URL
- 修改密码功能
1.4 通用组件
表格组件
基于 Element Plus el-table / Ant Design Table 封装:
- 分页查询(页码、每页条数)
- 列排序、筛选
- 批量操作(多选删除、导出)
- 自定义列模板(状态标签、操作按钮)
- 表格高度自适应
表单组件
封装通用表单:
- 表单校验规则
- 表单联动(级联选择)
- 表单重置
- 弹出对话框中的表单
- 表单分步提交
弹窗组件
- 新增/编辑公用弹窗
- 详情查看弹窗
- 确认提示弹窗
- 导入导出进度弹窗
导入导出
- 基于 Apache POI / EasyExcel
- 导入:上传 Excel -> 解析 -> 校验 -> 入库
- 导出:查询数据 -> 写入 Excel -> 下载
- 支持大数据量异步导出
文件上传
- 单文件/多文件上传
- 分片上传(大文件)
- 文件格式/大小校验
- 上传进度显示
- 文件预览(图片、PDF)
二、权限管理完整设计
2.1 菜单树管理
菜单管理是权限系统的核心模块,支持多级树形结构。
前端实现
<!-- Vue 3 + Element Plus 菜单树示例 -->
<template>
<el-tree
:data="menuTree"
:props="{ label: 'menuName', children: 'children' }"
node-key="id"
default-expand-all
draggable
@node-drop="handleDrop"
>
<template #default="{ node, data }">
<span class="menu-node">
<el-icon><component :is="data.icon" /></el-icon>
<span>{{ data.menuName }}</span>
<el-tag v-if="data.menuType === 'M'" size="small">目录</el-tag>
<el-tag v-else-if="data.menuType === 'C'" type="success" size="small">菜单</el-tag>
<el-tag v-else type="warning" size="small">按钮</el-tag>
</span>
</template>
</el-tree>
</template>菜单字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
| menuName | String | 菜单显示名称 |
| parentId | Long | 父菜单 ID,顶级为 0 |
| path | String | 路由地址,如 /system/user |
| component | String | 组件路径,如 system/user/index |
| menuType | Char | M=目录(有子节点)/ C=菜单(叶子节点)/ F=按钮 |
| perms | String | 权限标识,如 system:user:add |
| icon | String | 菜单图标 |
| orderNum | Integer | 排序号 |
| visible | Boolean | 是否显示 |
| status | Boolean | 状态(启用/禁用) |
路由自动生成
后端返回菜单树后,前端根据 menuType=C 的菜单自动生成 Vue Router 路由配置:
function generateRoutes(menus) {
return menus
.filter(menu => menu.menuType === 'C' || menu.menuType === 'M')
.map(menu => ({
path: menu.path,
component: () => import(`@/views/${menu.component}.vue`),
children: menu.children ? generateRoutes(menu.children) : [],
meta: { title: menu.menuName, icon: menu.icon }
}));
}2.2 按钮级权限
按钮级权限(也称操作权限)是 RBAC 的细化,控制页面内某个按钮或操作是否可见/可用。
前端指令方式(v-permission)
// Vue 3 自定义指令
app.directive('permission', {
mounted(el, binding) {
const { value } = binding;
const userPermissions = usePermissionStore().permissions;
if (value && value instanceof Array && value.length > 0) {
const hasPermission = value.some(p => userPermissions.includes(p));
if (!hasPermission) {
el.parentNode && el.parentNode.removeChild(el);
}
}
}
});
// 使用示例
<el-button v-permission="['system:user:add']" type="primary">
新增用户
</el-button>
<el-button v-permission="['system:user:edit']" type="warning">
编辑
</el-button>
<el-button v-permission="['system:user:remove']" type="danger">
删除
</el-button>后端拦截(Spring Security)
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/list")
public Result list(UserQuery query) {
return Result.success(userService.list(query));
}
@PreAuthorize("@ss.hasPermi('system:user:add')")
@PostMapping
public Result add(@Valid @RequestBody UserDTO dto) {
return Result.success(userService.add(dto));
}
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@PutMapping
public Result edit(@Valid @RequestBody UserDTO dto) {
return Result.success(userService.edit(dto));
}
@PreAuthorize("@ss.hasPermi('system:user:remove')")
@DeleteMapping("/{ids}")
public Result remove(@PathVariable Long[] ids) {
return Result.success(userService.remove(ids));
}权限校验实现
@Component("ss")
public class PermissionService {
public boolean hasPermi(String permission) {
if (StringUtils.isEmpty(permission)) {
return false;
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null) {
return false;
}
// 管理员拥有所有权限
if (loginUser.isAdmin()) {
return true;
}
return loginUser.getPermissions().contains(permission);
}
}2.3 数据权限
数据权限解决"不同角色看到不同数据范围"的问题,常见的数据范围级别:
| 级别 | 范围 | 说明 |
|---|---|---|
| 1 | 仅本人数据 | 只能查看自己创建的数据 |
| 2 | 本部门数据 | 查看所在部门的数据 |
| 3 | 本部门及下级部门 | 查看本部门及所有子部门数据 |
| 4 | 自定义数据 | 选择指定部门的数据 |
| 5 | 全部数据 | 管理员级别,查看所有数据 |
数据库设计
-- 角色中增加数据权限字段
ALTER TABLE sys_role ADD COLUMN data_scope CHAR(1) DEFAULT '5';
-- data_scope: 1=本人 2=本部门 3=本部门及下级 4=自定义 5=全部
-- 角色和部门关联表(用于自定义数据权限)
CREATE TABLE sys_role_dept (
role_id BIGINT NOT NULL,
dept_id BIGINT NOT NULL,
PRIMARY KEY (role_id, dept_id)
);后端 SQL 拦截实现(MyBatis-Plus 拦截器)
@Component
public class DataScopeInterceptor implements InnerInterceptor {
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter,
RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null || loginUser.isAdmin()) {
return;
}
// 根据数据权限范围拼接 SQL 过滤条件
String dataScopeSql = buildDataScopeSql(loginUser);
if (StringUtils.isNotEmpty(dataScopeSql)) {
// 将过滤条件注入到原有 SQL 中
String originalSql = boundSql.getSql();
String newSql = originalSql.replace("WHERE", "WHERE " + dataScopeSql + " AND ");
// 通过反射更新 BoundSql
ReflectUtil.setFieldValue(boundSql, "sql", newSql);
}
}
}自定义 SQL 过滤示例
-- 原始 SQL
SELECT * FROM sys_order WHERE status = 1
-- 数据权限拦截后(仅本人数据)
SELECT * FROM sys_order WHERE status = 1 AND create_by = 'admin'
-- 数据权限拦截后(本部门及下级部门)
SELECT * FROM sys_order o
WHERE status = 1
AND o.dept_id IN (SELECT id FROM sys_dept WHERE dept_path LIKE '0/100/101%')三、代码生成器设计
3.1 模板引擎配置
代码生成器基于模板引擎(Velocity / FreeMarker / Beetl)实现,通过配置模板路径和生成规则,一键生成项目各层代码。
生成器配置
# application.yml - 代码生成器配置
gen:
# 模板配置
template:
path: templates/ # 模板文件目录
suffix: .vm # 模板文件后缀(Velocity)
# 生成配置
output:
base-path: D:/generated # 输出根目录
package-name: com.example.admin # 基础包名
author: admin # 作者
table-prefix: sys_ # 表前缀(生成时移除)
# 数据库配置
database:
url: jdbc:mysql://localhost:3306/admin_db
username: root
password: 123456模板目录结构
templates/
├── java/
│ ├── controller.java.vm # Controller 模板
│ ├── service.java.vm # Service 接口模板
│ ├── serviceImpl.java.vm # Service 实现模板
│ ├── mapper.java.vm # Mapper 模板
│ └── entity.java.vm # Entity 模板
├── xml/
│ └── mapper.xml.vm # MyBatis XML 模板
└── vue/
├── index.vue.vm # 列表页模板
├── form.vue.vm # 表单页模板
└── api.js.vm # API 请求模板3.2 各层模板
Controller 模板示例
package ${packageName}.controller;
import ${packageName}.common.Result;
import ${packageName}.entity.${ClassName};
import ${packageName}.service.${ClassName}Service;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* ${functionName}控制器
*
* @author ${author}
* @date ${date}
*/
@RestController
@RequestMapping("/${moduleName}/${businessName}")
public class ${ClassName}Controller {
@Resource
private ${ClassName}Service ${className}Service;
@GetMapping("/list")
public Result list(${ClassName}Query query) {
return Result.success(${className}Service.queryPage(query));
}
@GetMapping("/{id}")
public Result get(@PathVariable Long id) {
return Result.success(${className}Service.getById(id));
}
@PostMapping
public Result add(@RequestBody ${ClassName} ${className}) {
return Result.success(${className}Service.save(${className}));
}
@PutMapping
public Result edit(@RequestBody ${ClassName} ${className}) {
return Result.success(${className}Service.updateById(${className}));
}
@DeleteMapping("/{ids}")
public Result remove(@PathVariable Long[] ids) {
return Result.success(${className}Service.deleteByIds(ids));
}
}Service 模板示例
package ${packageName}.service;
import ${packageName}.entity.${ClassName};
import com.baomidou.mybatisplus.extension.service.IService;
public interface ${ClassName}Service extends IService<${ClassName}> {
Result queryPage(${ClassName}Query query);
boolean deleteByIds(Long[] ids);
}Entity 模板示例
package ${packageName}.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
/**
* ${functionName}实体
*
* @author ${author}
* @date ${date}
*/
@TableName("${tableName}")
public class ${ClassName} {
@TableId(type = IdType.AUTO)
private ${primaryKeyType} id;
#foreach($column in $columns)
/** ${column.columnComment} */
#if($column.columnType == "datetime")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
#end
private ${column.javaType} ${column.javaField};
#end
// getter / setter 略
}Vue 列表页模板
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true">
#foreach($column in $columns)
#if($column.query)
<el-form-item label="${column.columnComment}" prop="${column.javaField}">
<el-input
v-model="queryParams.${column.javaField}"
placeholder="请输入${column.columnComment}"
clearable
/>
</el-form-item>
#end
#end
<el-form-item>
<el-button type="primary" @click="handleQuery">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb-8">
<el-col :span="1.5">
<el-button type="primary" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" @click="handleDelete">批量删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button @click="handleExport">导出</el-button>
</el-col>
</el-row>
<el-table :data="dataList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="ID" prop="id" width="80" />
#foreach($column in $columns)
<el-table-column label="${column.columnComment}" prop="${column.javaField}" />
#end
<el-table-column label="操作" width="200">
<template #default="scope">
<el-button type="text" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="text" danger @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-if="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
/>
</div>
</template>3.3 自定义模板
当默认模板不满足项目规范时,可添加自定义模板。
自定义模板配置
gen:
template:
custom:
- name: service-impl-extra
path: templates/custom/serviceImplExtra.java.vm
output: ${basePath}/service/impl/${ClassName}ServiceImplExtra.java
condition: "table.type == 'MAIN' && module == 'biz'"
- name: dto
path: templates/custom/dto.java.vm
output: ${basePath}/dto/${ClassName}DTO.java
- name: converter
path: templates/custom/converter.java.vm
output: ${basePath}/convert/${ClassName}Converter.java
- name: unit-test
path: templates/custom/test.java.vm
output: ${basePath}/service/impl/${ClassName}ServiceImplTest.java自定义模板变量
代码生成器提供以下模板变量:
| 变量 | 类型 | 说明 |
|---|---|---|
| $ | String | 包名 |
| $ | String | 类名(首字母小写) |
| $ | String | 类名(首字母大写) |
| $ | String | 数据库表名 |
| $ | String | 功能名称(根据表注释生成) |
| $ | String | 模块名称 |
| $ | String | 业务名称 |
| $ | List | 列信息列表 |
| $ | String | 作者 |
| $ | String | 生成日期 |
四、通用功能
4.1 操作日志记录
基于 AOP + 自定义注解实现操作日志的自动记录。
自定义注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/** 模块名称 */
String title() default "";
/** 业务操作类型(新增/修改/删除/查询/导出/导入/其它) */
BusinessType businessType() default BusinessType.OTHER;
/** 是否保存请求参数 */
boolean isSaveRequestData() default true;
/** 是否保存响应参数 */
boolean isSaveResponseData() default true;
}
public enum BusinessType {
OTHER, INSERT, UPDATE, DELETE, SELECT, EXPORT, IMPORT, FORCE
}AOP 实现
@Aspect
@Component
public class LogAspect {
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
@Around("@annotation(logAnnotation)")
public Object around(ProceedingJoinPoint joinPoint, Log logAnnotation) throws Throwable {
long beginTime = System.currentTimeMillis();
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - beginTime;
// 异步保存日志
saveLog(joinPoint, logAnnotation, result, duration);
return result;
}
private void saveLog(ProceedingJoinPoint joinPoint, Log logAnnotation,
Object result, long duration) {
OperLog operLog = new OperLog();
operLog.setTitle(logAnnotation.title());
operLog.setBusinessType(logAnnotation.businessType().ordinal());
operLog.setMethod(joinPoint.getTarget().getClass().getName() + "."
+ joinPoint.getSignature().getName());
operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
operLog.setOperIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
operLog.setOperTime(new Date());
operLog.setDuration(duration);
// 获取当前登录用户
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser != null) {
operLog.setOperName(loginUser.getUsername());
operLog.setDeptName(loginUser.getDept().getDeptName());
}
// 保存请求参数
if (logAnnotation.isSaveRequestData()) {
operLog.setOperParam(JSON.toJSONString(joinPoint.getArgs()));
}
// 保存响应参数
if (logAnnotation.isSaveResponseData()) {
operLog.setJsonResult(JSON.toJSONString(result));
}
// 异步写入数据库
AsyncManager.me().execute(() -> operLogService.save(operLog));
}
}使用示例
@Log(title = "用户管理", businessType = BusinessType.INSERT)
@PostMapping
public Result add(@Valid @RequestBody UserDTO dto) {
return Result.success(userService.add(dto));
}
@Log(title = "用户管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public Result remove(@PathVariable Long[] ids) {
return Result.success(userService.remove(ids));
}
@Log(title = "用户管理", businessType = BusinessType.EXPORT)
@GetMapping("/export")
public void export(UserQuery query, HttpServletResponse response) {
userService.export(query, response);
}日志表设计
CREATE TABLE sys_oper_log (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(50) COMMENT '模块标题',
business_type INT DEFAULT 0 COMMENT '业务类型',
method VARCHAR(200) COMMENT '方法名称',
request_method VARCHAR(10) COMMENT '请求方式',
oper_url VARCHAR(200) COMMENT '请求URL',
oper_ip VARCHAR(50) COMMENT '操作IP',
oper_location VARCHAR(200) COMMENT '操作地点',
oper_name VARCHAR(50) COMMENT '操作人员',
dept_name VARCHAR(50) COMMENT '部门名称',
oper_param TEXT COMMENT '请求参数',
json_result TEXT COMMENT '返回参数',
status INT DEFAULT 0 COMMENT '状态 0=正常 1=异常',
error_msg TEXT COMMENT '错误消息',
duration BIGINT COMMENT '耗时(ms)',
oper_time DATETIME COMMENT '操作时间'
);4.2 数据字典管理
数据字典用于统一管理系统中键值对类型的数据,如状态、性别、类型等,避免硬编码。
字典表设计
-- 字典类型表
CREATE TABLE sys_dict_type (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
dict_name VARCHAR(100) COMMENT '字典名称',
dict_type VARCHAR(100) NOT NULL UNIQUE COMMENT '字典类型标识',
status TINYINT DEFAULT 1,
remark VARCHAR(500),
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 字典数据表
CREATE TABLE sys_dict_data (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
dict_sort INT DEFAULT 0 COMMENT '排序',
dict_label VARCHAR(100) COMMENT '字典标签',
dict_value VARCHAR(100) COMMENT '字典键值',
dict_type VARCHAR(100) NOT NULL COMMENT '字典类型',
css_class VARCHAR(50) COMMENT '样式属性',
list_class VARCHAR(50) COMMENT '表格回显样式',
is_default CHAR(1) DEFAULT 'N',
status TINYINT DEFAULT 1,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 示例数据
INSERT INTO sys_dict_type VALUES (1, '用户性别', 'sys_user_sex', 1, '用户性别字典');
INSERT INTO sys_dict_data VALUES (1, 1, '男', '0', 'sys_user_sex', '', 'default', 'N', 1, NOW());
INSERT INTO sys_dict_data VALUES (2, 2, '女', '1', 'sys_user_sex', '', 'default', 'N', 1, NOW());
INSERT INTO sys_dict_data VALUES (3, 3, '未知', '2', 'sys_user_sex', '', 'default', 'Y', 1, NOW());前端使用
<template>
<!-- 下拉框使用字典 -->
<el-select v-model="form.sex">
<el-option
v-for="item in dictData.sys_user_sex"
:key="item.dictValue"
:label="item.dictLabel"
:value="item.dictValue"
/>
</el-select>
<!-- 表格中字典回显 -->
<el-table-column label="性别">
<template #default="scope">
<dict-tag :options="dictData.sys_user_sex" :value="scope.row.sex" />
</template>
</el-table-column>
</template>
<script setup>
import { useDict } from '@/hooks/dict';
const { dictData } = useDict('sys_user_sex', 'sys_normal_disable');
</script>4.3 通知公告 / 消息中心
通知公告
CREATE TABLE sys_notice (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(100) NOT NULL COMMENT '公告标题',
content TEXT COMMENT '公告内容',
type CHAR(1) DEFAULT '1' COMMENT '公告类型 1=通知 2=公告',
status TINYINT DEFAULT 1,
create_by VARCHAR(50),
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
remark VARCHAR(500)
);消息中心设计
消息中心支持站内信,设计如下:
-- 消息模板表
CREATE TABLE sys_message_template (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
template_code VARCHAR(50) NOT NULL UNIQUE,
template_name VARCHAR(100),
template_content TEXT,
template_type VARCHAR(20) COMMENT 'sms/email/site',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 消息记录表
CREATE TABLE sys_message (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(200),
content TEXT,
message_type VARCHAR(20) COMMENT '通知/提醒/系统消息',
sender_id BIGINT COMMENT '发送人',
receiver_id BIGINT COMMENT '接收人',
is_read TINYINT DEFAULT 0,
read_time DATETIME,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_receiver (receiver_id, is_read)
);
-- 消息推送实现思路
-- 1. 系统通知:后台发送给全部用户或指定角色
-- 2. 个人消息:审批提醒、流程通知等定向发送
-- 3. 前端轮询或 WebSocket 实时推送未读消息数4.4 系统配置(参数管理)
系统参数管理用于维护全局配置项。
CREATE TABLE sys_config (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
config_name VARCHAR(100) COMMENT '参数名称',
config_key VARCHAR(100) NOT NULL UNIQUE COMMENT '参数键名',
config_value VARCHAR(500) COMMENT '参数键值',
config_type CHAR(1) DEFAULT 'Y' COMMENT 'Y=系统内置 N=自定义',
remark VARCHAR(500),
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 示例数据
INSERT INTO sys_config VALUES (1, '用户默认头像', 'sys.user.defaultAvatar', '/avatar/default.png', 'Y', '默认头像地址', NOW());
INSERT INTO sys_config VALUES (2, '密码错误次数', 'sys.account.maxRetryCount', '5', 'Y', '锁定前允许错误次数', NOW());
INSERT INTO sys_config VALUES (3, '会话超时时间', 'sys.session.timeout', '30', 'Y', '分钟', NOW());后端使用
// 配置工具类
@Component
public class ConfigService {
@Autowired
private SysConfigMapper configMapper;
@Cacheable(value = "sys_config", key = "#configKey")
public String getConfigValue(String configKey) {
SysConfig config = configMapper.selectByKey(configKey);
return config != null ? config.getConfigValue() : null;
}
public String getDefaultAvatar() {
return getConfigValue("sys.user.defaultAvatar");
}
public int getMaxRetryCount() {
String val = getConfigValue("sys.account.maxRetryCount");
return Integer.parseInt(val);
}
}五、毕业设计指南
5.1 选题建议
选题原则
| 原则 | 说明 |
|---|---|
| 难度适中 | 既不简单到无技术含量,也不复杂到无法完成。建议在已有框架基础上做功能扩展 |
| 技术覆盖面全 | 涵盖前后端交互、数据库设计、权限管理、文件处理等主流技术点 |
| 有实际应用价值 | 解决真实场景问题,有明确用户群体和使用场景 |
选题目检清单
- [ ] 是否有明确的功能需求?(不是"做一个系统"这种空泛的描述)
- [ ] 功能是否至少包含 5-8 个核心模块?
- [ ] 是否涉及至少 6-8 张数据库表?
- [ ] 是否需要权限管理?(大部分系统都需要)
- [ ] 是否包含文件上传/导入导出功能?
- [ ] 是否有明确的界面原型或页面设计?
- [ ] 选题是否符合指导老师的方向要求?
5.2 技术选型建议
推荐组合:Spring Boot + Vue 前后端分离
| 层级 | 推荐选型 | 评分 | 说明 |
|---|---|---|---|
| 后端框架 | Spring Boot 2.7.x | 5/5 | 生态成熟,文档丰富,就业市场需求大 |
| ORM | MyBatis-Plus | 5/5 | 开发效率高,代码生成方便 |
| 数据库 | MySQL 8.0 | 5/5 | 稳定可靠,社区支持好 |
| 缓存 | Redis | 4/5 | 加分项,非必需但推荐 |
| 安全框架 | Spring Security + JWT | 4/5 | 学习曲线陡峭,但含金量高 |
| 前端框架 | Vue 3 + Element Plus | 5/5 | 组件丰富,上手快 |
| 状态管理 | Pinia | 4/5 | 轻量,TypeScript 友好 |
| 接口文档 | Knife4j | 4/5 | 自动生成,演示加分 |
| 部署 | Docker + Nginx | 4/5 | 加分项,展示运维能力 |
备选方案
| 方向 | 技术栈 | 适用场景 |
|---|---|---|
| 全栈统一语言 | Spring Boot + Thymeleaf / JSP | 小型项目,不求前端分离 |
| React 方向 | Spring Boot + React + Ant Design | 求职 React 方向 |
| Python 方向 | Django / Flask + Vue | 对 Java 不熟悉 |
| 微服务方向 | Spring Cloud Alibaba + Vue | 追求架构深度(风险较高) |
不建议的选型
- 纯 JSP/Servlet:技术过于陈旧,答辩容易被质疑技术视野
- SSH(Struts2 + Spring + Hibernate):框架已过时
- 不使用任何框架的原生开发:安全、效率、可维护性都不足
- 过于冷门的框架:社区资源少,遇到问题难以解决
5.3 项目结构规范
Maven 多模块分包约定
project-name/
├── project-common # 公共模块
│ ├── exception/ # 全局异常定义
│ ├── result/ # 统一返回结果
│ ├── constant/ # 常量定义
│ ├── enums/ # 枚举
│ └── utils/ # 工具类
├── project-framework # 框架配置
│ ├── config/ # 配置类(跨域、Swagger、Redis)
│ ├── security/ # 安全配置
│ ├── interceptor/ # 拦截器
│ └── aspect/ # AOP 切面
├── project-system # 系统核心模块
│ ├── controller/ # 控制器层
│ ├── service/ # 服务层接口
│ │ └── impl/ # 服务实现
│ ├── mapper/ # 数据访问层
│ ├── entity/ # 实体类
│ ├── dto/ # 数据传输对象
│ ├── vo/ # 视图对象(返回给前端)
│ └── query/ # 查询参数对象
├── project-generator # 代码生成(可选)
├── sql/ # SQL 脚本
│ ├── schema.sql # 建表语句
│ └── data.sql # 初始数据
└── doc/ # 文档
├── api.md # 接口说明
└── database.md # 数据库设计说明包命名规范
- 包名全小写,使用单数形式
- controller/service/mapper/entity 为标准分层
- dto/vo/query 分类清晰,避免混用
- 不同模块按业务分包,如
system、monitor、tool
Java 编码规范要点
- 类名:大驼峰,如 SysUserController
- 方法名:小驼峰,如 getUserById
- 常量:全大写下划线,如 MAX_RETRY_COUNT
- 接口和实现:接口名
XxxService,实现名XxxServiceImpl - 统一使用
@Resource或@Autowired注入(保持项目一致)
5.4 数据库设计规范
命名规范
| 对象 | 规范 | 示例 |
|---|---|---|
| 数据库名 | 全小写,下划线分隔 | admin_db |
| 表名 | 全小写,下划线分隔,加模块前缀 | sys_user, sys_role |
| 字段名 | 全小写,下划线分隔 | user_name, create_time |
| 主键 | 统一命名为 id | id BIGINT |
| 外键 | 引用表名_id | role_id, dept_id |
| 索引名 | idx_表名_字段名 | idx_user_username |
字段类型选择
| 数据类型 | 适用场景 | 说明 |
|---|---|---|
| BIGINT | 主键 ID | 自增主键,支持大数据量 |
| VARCHAR(50-500) | 名称、描述等短文本 | 根据实际长度设置 |
| TEXT | 长文本内容 | 如公告内容、日志详情 |
| TINYINT(1) | 布尔值 / 状态 | 0/1/2 等有限枚举值 |
| INT | 数字、排序、计数 | 普通整数 |
| DECIMAL(10,2) | 金额、价格 | 精确小数 |
| DATETIME | 时间戳 | 统一精度到秒 |
| CHAR(1) | 固定长度标识 | 如性别 M/F,类型标识 |
索引设计原则
- 主键索引:每个表必须有,使用 BIGINT 自增
- 唯一索引:业务唯一字段(如 username、email)
- 普通索引:查询频繁的字段(如 status、dept_id)
- 联合索引:多字段组合查询,最左前缀原则
- 索引数量:单表不超过 5-8 个,过多影响写入性能
建表示例
CREATE TABLE sys_xxxx (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键',
field1 VARCHAR(50) NOT NULL COMMENT '字段1',
field2 VARCHAR(100) DEFAULT '' COMMENT '字段2',
status TINYINT DEFAULT 1 COMMENT '状态 0=禁用 1=启用',
sort INT DEFAULT 0 COMMENT '排序',
remark VARCHAR(500) COMMENT '备注',
del_flag TINYINT DEFAULT 0 COMMENT '删除标记',
create_by VARCHAR(50) COMMENT '创建人',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_by VARCHAR(50) COMMENT '更新人',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX idx_xxx_field1 (field1),
INDEX idx_xxx_status (status, del_flag)
) COMMENT '表注释';5.5 核心功能清单
毕业设计的功能深度直接决定评分等级。以下为不同评分等级对应的功能要求:
基础功能(60-70 分:及格)
| 功能 | 说明 |
|---|---|
| CRUD | 至少 3 张表的增删改查 |
| 登录/登出 | 基本的会话管理 |
| 分页查询 | 前端分页组件 + 后端分页接口 |
| 简单的权限控制 | 区分管理员和普通用户 |
中等功能(70-85 分:良好)
| 功能 | 说明 |
|---|---|
| CRUD + 多表关联 | 5 张表以上,含关联查询 |
| RBAC 权限 | 完整的用户/角色/菜单管理 |
| 文件上传 | 单文件/多文件上传 + 预览 |
| 导入导出 | Excel 导入导出 |
| 数据字典 | 字典管理 + 字典回显 |
| 操作日志 | 操作记录查询 |
高级功能(85-100 分:优秀)
| 功能 | 说明 |
|---|---|
| 以上全部 | 包含中等功能所有内容 |
| 代码生成器 | 自动生成 CRUD 代码 |
| 数据权限 | 部门级/用户级数据隔离 |
| 通知公告 | 消息推送 + 未读提醒 |
| 系统监控 | 在线用户、操作日志、登录日志 |
| 缓存优化 | Redis 缓存热点数据 |
| Docker 部署 | 容器化部署 + Docker Compose |
5.6 答辩注意事项
答辩准备清单
亮点准备
- 技术亮点:列出项目中 3-5 个技术难点及解决方案
- 业务亮点:说明系统解决了什么实际问题
- 性能亮点:如有缓存优化、SQL 优化等,准备数据对比
- 架构亮点:如果用了微服务、消息队列等,说明选型理由
常见亮点示例:
1. 基于 RBAC 的细粒度权限控制,支持按钮级别和数据级别权限
2. 基于 AOP 的操作日志记录,零侵入式嵌入业务代码
3. 自定义代码生成器,一键生成 CRUD,提升开发效率 80%
4. Redis 缓存热点数据,接口响应时间从 500ms 降至 20ms
5. 基于 EasyExcel 的大数据量导入导出,支持万级数据项目演示
- 演示前检查环境(数据库是否启动、后端是否运行、前端是否正常)
- 准备演示账号(管理员账号 + 普通用户账号)
- 先演示核心功能(登录 -> 首页 -> 用户管理 -> 角色管理 -> 菜单管理)
- 再演示特色功能(代码生成器、数据字典、操作日志)
- 最后演示加分功能(文件上传、导入导出、Docker 部署)
- 演示时语速适中,边操作边讲解技术实现
演示注意事项
- 不要只点按钮不说话,要讲清楚"做了什么"和"为什么这么做"
- 遇到 Bug 不要慌张,可以说"这个问题我在开发时也遇到过,原因是..."
- 不要拿线上别人的系统截图来演示(答辩老师可能见过)
- 准备一份演示数据,避免现场数据不足
源码讲解
- 项目结构:先展示整体模块划分
- 核心流程:选择一个核心功能(如登录流程),从请求到响应的完整链路
- 代码亮点:展示自己写的核心代码(不是框架自动生成的)
- 数据库设计:展示 ER 图,说明设计思路
- 答辩老师常问的问题:
Q: 为什么选这个技术栈?
A: 结合就业市场需求、技术成熟度、学习资源丰富度回答
Q: 权限是怎么控制的?
A: 从数据库设计 -> 后端拦截 -> 前端指令,完整链路说明
Q: 系统有什么改进空间?
A: 可以说分布式部署、消息队列、微服务拆分等方向(展示视野)
Q: 这个功能是你自己实现的还是用框架/插件的?
A: 如实回答,但可以说"虽然用了框架,但我在框架基础上做了 XX 定制化"
Q: 数据库为什么这么设计?
A: 从业务需求、查询效率、扩展性三方面回答六、毕设推荐选题库
6.1 电商系统
核心功能模块
| 模块 | 功能点 | 技术要点 |
|---|---|---|
| 商品管理 | 商品分类、SPU/SKU、商品上下架、库存管理 | 多级分类、规格组合 |
| 订单管理 | 购物车、下单、订单状态流转、售后 | 状态机、事务处理 |
| 会员管理 | 注册登录、等级、积分 | 会员体系 |
| 支付管理 | 支付宝/微信支付对接 | 支付回调、退款 |
| 营销管理 | 优惠券、秒杀、满减活动 | 并发控制 |
数据库表数量:建议 12-15 张 推荐理由:需求明确,技术覆盖面广,有实际商业价值
6.2 内容管理系统(CMS)
核心功能模块
| 模块 | 功能点 | 技术要点 |
|---|---|---|
| 栏目管理 | 多级栏目树、栏目模板 | 树形结构 |
| 文章管理 | 富文本编辑、文章审核、标签分类 | 富文本处理 |
| 评论管理 | 评论审核、回复 | 多级评论 |
| 站点配置 | 站点信息、SEO 配置、广告位 | 配置管理 |
| 统计分析 | 文章浏览量、用户行为 | 数据统计 |
数据库表数量:建议 8-10 张 推荐理由:结构清晰,适合入门,可以结合前端展示
6.3 在线考试系统
核心功能模块
| 模块 | 功能点 | 技术要点 |
|---|---|---|
| 题库管理 | 选择题/判断题/简答、批量导入、分类 | 题目类型多样化 |
| 组卷管理 | 手动组卷、随机组卷 | 算法设计 |
| 考试管理 | 考试发布、防作弊、自动计时 | WebSocket、时间控制 |
| 成绩管理 | 自动判分、成绩统计、错题分析 | 判分逻辑 |
| 学生管理 | 班级、学生信息 | 角色区分 |
数据库表数量:建议 10-12 张 推荐理由:逻辑性强,算法有亮点,区分学生/教师/管理员三种角色
6.4 会议室预约系统
核心功能模块
| 模块 | 功能点 | 技术要点 |
|---|---|---|
| 会议室管理 | 会议室 CRUD、设备信息、容量 | 资源管理 |
| 预约管理 | 预约提交、冲突检测、取消 | 时间冲突算法 |
| 审批管理 | 预约审核流程 | 简单工作流 |
| 日历视图 | 按日/周/月查看预约 | 日历组件 |
| 统计分析 | 会议室使用率、预约趋势 | 可视化图表 |
数据库表数量:建议 6-8 张 推荐理由:场景真实,冲突检测算法是答辩亮点,复杂度适中
6.5 实验室设备管理系统
核心功能模块
| 模块 | 功能点 | 技术要点 |
|---|---|---|
| 设备管理 | 设备入库、领用、归还、维修 | 生命周期管理 |
| 设备分类 | 多级分类、规格参数模板 | 属性继承 |
| 借用管理 | 借用申请、审批、逾期提醒 | 状态流转 |
| 耗材管理 | 耗材入库、领用、库存预警 | 库存管理 |
| 统计报表 | 设备使用率、耗材消耗 | 报表生成 |
数据库表数量:建议 10-12 张 推荐理由:业务完整,设备生命周期管理有技术深度
6.6 课程选课系统
核心功能模块
| 模块 | 功能点 | 技术要点 |
|---|---|---|
| 课程管理 | 课程信息、教师安排、排课 | 时间冲突检测 |
| 选课管理 | 选课/退课、选课规则、容量限制 | 并发选课、事务 |
| 成绩管理 | 成绩录入、统计、排名 | 数据统计 |
| 教学评价 | 学生评教、评价统计 | 评分计算 |
| 通知公告 | 选课通知、成绩通知 | 消息推送 |
数据库表数量:建议 8-10 张 推荐理由:角色清晰(学生/教师/管理员),选课并发是技术亮点
6.7 个人博客系统
核心功能模块
| 模块 | 功能点 | 技术要点 |
|---|---|---|
| 文章管理 | 发布/编辑/删除、Markdown 编辑、标签分类 | Markdown 渲染 |
| 分类管理 | 文章分类、多级分类 | 树形结构 |
| 评论管理 | 文章评论、回复、审核 | 多级评论 |
| 友链管理 | 友情链接申请、审核 | 简单 CRUD |
| 站点统计 | 访问量、文章数、评论数 | 缓存计数 |
| 个人中心 | 个人信息、头像、关于我 | 文件上传 |
数据库表数量:建议 6-8 张 推荐理由:功能适中,易于完成,可同时做前台展示+后台管理
选题对比总结
| 选题 | 难度 | 数据库表数 | 技术覆盖面 | 实际价值 | 推荐指数 |
|---|---|---|---|---|---|
| 电商系统 | 高 | 12-15 | 广 | 高 | 4/5 |
| 内容管理系统 | 低 | 8-10 | 中 | 中 | 4/5 |
| 在线考试系统 | 中 | 10-12 | 中 | 高 | 5/5 |
| 会议室预约 | 低 | 6-8 | 中 | 高 | 4/5 |
| 实验室设备管理 | 中 | 10-12 | 广 | 高 | 5/5 |
| 课程选课系统 | 中 | 8-10 | 中 | 高 | 4/5 |
| 个人博客系统 | 低 | 6-8 | 中 | 中 | 3/5 |
选题建议:优先选择自己感兴趣、且有实际使用场景的题目。如果时间充裕,选择难度中上(在线考试系统 / 实验室设备管理)更容易获得高分。如果时间紧张,建议选择功能适中的题目(会议室预约 / 个人博客),把更多精力放在代码质量和答辩准备上。