ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于Spring Boot与Vue的培训机构管理系统设计与实现

基于Spring Boot与Vue的培训机构管理系统设计与实现 做毕设选了这个题的话你算是摸到Java全栈最容易出活儿的一条路了。“培训机构管理系统”属于典型的企业级信息管理系统前后台、权限、报表、支付该有的全都有而且业务规则足够清晰不会像“智慧校园”那种大而空泛的题目把人绕晕。这个题目在本科毕设和课程设计里的出镜率极高本质上是把Spring Boot Vue这套现代Web主流程跑一遍属于投入产出比非常高的选题。技术栈上Spring Boot提供后端接口服务Vue负责前端页面渲染MySQL存数据Redis做缓存和Token管理再用Nginx一部署一套完整闭环就出来了。它的价值不只是交一份能跑的代码而是把开发流程、工程规范、前后端协作这些实际工作中的核心能力全部覆盖到了。这篇文章我会从项目拆解、表结构设计、后端实现、前端交互、部署排错到答辩追问把整个项目完整过一遍直接给你一个能照着改的底稿。1. 项目整体设计与技术选型思路1.1 先看清系统到底在管什么培训机构管理系统名字看着泛但拆开来看业务边界很清楚。核心对象就是学员、课程、班级、老师四个围绕它们衍生出排课、选课、考勤、缴费这些动作。学员端查课程、选课报名、查看课表与考勤、缴纳学费、查看学习记录。教师端接收排课任务、上课打卡、登记学员考勤、录入成绩或评语。管理员端管理基础数据学员/教师/课程/班级、审核并发布课程、排课调课、查看收入统计、管理角色权限。整个系统可以抽成四个功能域用户与权限域、教务管理域课程/班级/排课、学员服务域选课/考勤/成绩、财务统计域缴费/退费/营收报表。做数据库设计和接口规划的时候按这四个域去梳理逻辑会非常清晰后面写代码也不容易乱。还有些容易被忽略但实际特别重要的点课程下架逻辑、班级容量控制、课时消耗记录、缴费单生成与退款处理。这些是表字段设计和接口逻辑里真正出细节的地方答辩时老师问的往往也是这些。1.2 为什么定Spring Boot Vue而不是别的组合这个组合不是因为它最时髦而是它最适合做这类管理系统的交付。后端Spring Boot解决的问题是快速构建稳定的业务接口。它的自动配置机制把繁琐的Bean注册、数据源配置、事务管理全部收敛到极简一个SpringBootApplication注解就能把应用启动起来。配合MyBatis-Plus或Spring Data JPA单表CRUD几乎不写SQL开发效率比传统SSH高一个量级。更重要的是Spring Boot的生态足够成熟Shiro/Spring Security管安全、Redis管缓存、EasyExcel管导出、WebSocket管通知项目要扩展什么能力都有现成的轮子。前端Vue起步容易、组件化清晰配合Element Plus或Element UI这种组件库后台管理页面基本是搭积木Table、Form、Dialog、Pagination一套组件用到底。用Vue Router管理页面路由用Vuex/Pinia管理全局状态再用Axios统一发请求标准得不能再标准。另一个很实在的点是就业市场。Spring Boot Vue是国内中小型公司最普遍的Web开发组合招聘需求量大面试追问也多。做这个毕设等于把Java后端前端基础这条技术路线上最核心的技能点全部演练了一遍又不至于像微服务、大数据那么重难度刚好卡在本科阶段能驾驭的区间。版本选型我建议这样JDK用1.8或11都行Spring Boot用2.7.x稳定、教程多、踩坑记录全如果学校要求用JDK17或21那就上Spring Boot 3.x但要注意javax包名变成jakartaMyBatis-Plus也要换适配版本。前端Vue3配Element Plus是当前主流Vue2 Element UI虽然资料更多但新项目再学习Vue2性价比略低。MySQL用8.0Redis用本地Docker起一个就够。2. 数据库设计这套系统的地基2.1 核心表结构与关系梳理关于表的设计我的建议是要控制好粒度系统表控制在12~18张之间。太少显得工作量不足太多容易把自己拖死在繁琐关联里。以下是我在这类项目中常用的核心表清单表名说明关键字段sys_user系统用户表统一存账号username, password, status, typesys_role角色表role_code, role_name, descriptionsys_menu菜单权限表parent_id, menu_name, path, permssys_user_role用户角色关联表user_id, role_idsys_role_menu角色菜单关联表role_id, menu_idstudent学员信息表name, phone, parent_phone, grade_level, source_channelteacher教师信息表name, phone, subject, hire_date, edu_backgroundcourse课程表name, subject, total_hours, price, capacity, statuscourse_class班级表course_id, teacher_id, start_date, schedule, max_students, current_countenrollment选课报名表student_id, class_id, status, enroll_dateattendance考勤表class_id, student_id, course_date, statuspayment缴费记录表student_id, enrollment_id, amount, pay_method, pay_statuscourse_schedule排课表可选class_id, teacher_id, classroom, week_day, time_slotsys_user和student是分开的。这样设计的核心原因是登录账号体系与业务档案解耦——学员资料维护、密码重置、账号禁用互不影响将来接入微信登录或者OAuth第三方认证时也不用动业务表结构。课程和班级拆成两张表是因为同一门课程可能会开多个班次周末班、晚班、寒暑假班每个班的老师、时间、人数上限都不一样。如果不拆表同一个课程要复制多份数据冗余和一致性都有问题。选课报名表enrollment是连接学员与班级的桥同时记录报名状态待审核/已通过/已取消。将来查某学员报过哪些班或某班有哪些学员都走这张表不影响学员和班级主表。2.2 字段设计的几个实战细节密码字段存的是BCrypt加密后的密文长度建议设为varchar(100)BCrypt哈希长度是60个字符。有些老教程用MD5说句实话毕设里MD5也不是不能交差但面试官一旦追问密码怎么存的你说BCrypt加盐哈希和说MD5是完全不同的两个评价层级。金额相关字段数据库里用decimal(10,2)存Java里用BigDecimal接收前端展示时用toFixed(2)格式化。这里有个很容易踩的坑用double或float算金额会在精度上翻车0.10.2等于0.30000000000000004这种经典问题放在缴费统计里就是账不平。所有业务表加create_time、update_time两个时间字段逻辑删除字段deleted默认0。注意逻辑删除字段建议加TableLogic注解MyBatis-Plus这样所有查询都会自动带上deleted0的过滤条件但是你要小心一点如果业务里有唯一索引比如账号逻辑删除的行还在表里占着唯一索引位置会导致重新创建同名账号时冲突这个需要在删除时把username改成username_deleted_时间戳。3. 后端工程搭建与核心接口实现3.1 搭建Spring Boot工程骨架用Spring Initializrstart.spring.io生成工程或者直接用IDEA内置的Spring Initializr创建。依赖方面我建议至少引入dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-jsqlparser/artifactId version3.5.5/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency注意如果用的是Spring Boot 3.xMySQL驱动坐标会变成com.mysql:mysql-connector-j不需要再加版本号。MyBatis-Plus连表查询能力偏弱遇到多表查询时可以写XML里的自定义SQL配合TableName和Page分页比JPQL直观很多。application.yml的核心配置server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/training_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0 jwt: secret: your-secret-key expire: 86400有一个细节容易坑人数据库里的字段是create_time这种下划线风格Java属性是createTime驼峰风格。MyBatis-Plus默认开启map-underscore-to-camel-case自动映射没问题但如果你写自定义XML查询resultType直接用实体类总忘记这个映射关系查出来的时间字段就是null排查半天才找到原因。3.2 JWT认证与RBAC权限模型实现接口安全是整个系统的骨架认证方案我推荐JWT Spring Boot拦截器。思路是这样用户登录成功后后端把用户ID、角色编码、过期时间组合生成一个Token前端把Token存在本地之后每次请求在Header里带上Authorization: Bearer token后端拦截器校验Token有效性后放行。这里说一下选择JWT而不是session的原因。管理系统的前端是Vue单页应用可能需要部署到CDN或独立静态服务器与后端接口域名可能不同Session的Cookie跨域处理相对麻烦。JWT是无状态的前端拿到Token后往后端一扔就行后端不用保存会话状态天然适合前后端分离环境。有人担心JWT被截获后无法主动失效所以我在设计时把Token过期时间设短比如24小时内并且配合Redis做黑名单——用户主动退出登录时把这个Token加入Redis黑名单即便Token还在有效期内也无法继续使用。登录接口的核心逻辑大概是PostMapping(/login) public Result login(RequestBody LoginDTO dto) { // 1.根据用户名查用户校验密码BCrypt.matches // 2.校验用户状态status为0代表禁用 // 3.查询用户角色与权限 // 4.生成JWT返回token和用户信息 }拦截器中的校验逻辑Component public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录接口 if (request.getRequestURI().contains(/login)) { return true; } // 从Header取token解析并校验 String token request.getHeader(Authorization); if (token ! null token.startsWith(Bearer )) { token token.substring(7); // 解析JWT若解析失败或过期抛出异常 Claims claims JwtUtil.parseToken(token); // 将用户ID存入ThreadLocal后续业务从上下文取 UserContext.set(claims.get(userId).toString()); return true; } throw new BusinessException(401, 未登录或登录已过期); } }权限方面用RBAC模型。数据库里建sys_role、sys_menu、sys_user_role、sys_role_menu四张表。用户登录后查询角色取出角色对应的菜单列表和权限标识perms。前端根据菜单列表渲染侧边栏按钮级权限用Vue自定义指令控制比如管理员能看到新增学员按钮老师只能看到导出名单按钮。有一个非常容易掉进去的坑Token失效时间设置在yml里全局异常处理器也要对JWT的过期异常单独捕获否则过期Token抛出的异常没被统一处理前端拿到的是一堆看不懂的英文报错而不是规整的JSON提示登录已过期请重新登录。我一般在全局异常处理中对SignatureException和ExpiredJwtException分别处理返回401状态码和统一格式的错误信息。3.3 核心业务接口分页、选课、缴费分页查询是管理系统最高频的接口几乎所有列表页面都要用。MyBatis-Plus分页原理是拦截器自动拼接limit语句在配置类里加入分页拦截器Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }学员列表接口示例GetMapping(/students) public Result page(RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, RequestParam(required false) String keyword) { PageStudent page new Page(pageNum, pageSize); LambdaQueryWrapperStudent wrapper new LambdaQueryWrapper(); if (StringUtils.hasText(keyword)) { wrapper.like(Student::getName, keyword).or().like(Student::getPhone, keyword); } wrapper.orderByDesc(Student::getCreateTime); PageStudent result studentMapper.selectPage(page, wrapper); return Result.success(result); }选课报名这个接口涉及到事务一定加Transactional。逻辑是校验学员是否存在且状态正常校验班级是否还有剩余名额校验学员是否已报名过该课程防止重复报名然后往enrollment表插入一条待审核记录再更新班级current_count加一。名额校验有个并发问题需要留意极端情况下两个学员同时报名同一个班级都在事务里查到了剩余名额1然后同时插入最后班级报名人数超了。解决思路有两种一种是直接对course_class表加悲观锁select ... for update锁住该记录直到事务结束另一种是使用乐观锁在course_class表加version字段更新时比对version如果不匹配则重试。毕设项目中用悲观锁最简单加一句for update就行也确实能解决问题还可以向老师解释悲观锁与乐观锁的区别这是高分点。缴费接口的设计重点是记账也就是一个缴费操作要同时影响payment表和student表里一个累计缴费字段。如果只插入payment而忘了更新已缴总额后续统计报表就会出错。在同一个事务里完成这两步逻辑上就保证了数据一致。同时缴费单号建议单独生成用时间戳随机数或UUID方便后续退款对账。4. 前端页面设计与核心交互实现4.1 Vue工程搭建与目录规范前端用Vue3 Vite Element Plus这套组合。Vite比Webpack快得多开发体验好很多现在版本也稳定。创建工程npm create vuelatest cd training-admin npm install element-plus axios vue-router pinia npm run dev目录规范我习惯这样组织src/ ├── api/ │ ├── login.js │ ├── student.js │ └── course.js ├── router/ │ └── index.js ├── stores/ │ └── user.js ├── views/ │ ├── login/ │ ├── dashboard/ │ ├── student/ │ ├── course/ │ ├── class/ │ ├── attendance/ │ └── payment/ ├── components/ └── utils/ └── request.js一个重要的规范不要在页面组件里直接写axios请求统一封装在api目录下。这不是强迫症而是当后端接口地址发生变更、增加请求拦截逻辑、或者统一处理错误码时只需要改一个文件而不是全项目搜索。我刚做项目时偷懒直接在页面里写请求后来接口前缀从/api/v1改成/api/v2全项目改了几十个地方后来学乖了。4.2 Axios封装拦截器与错误处理Axios封装是整个前端工程质量的分水岭。我建议统一在utils/request.js里创建axios实例配置baseURL、请求超时时间、请求拦截器自动附带token和响应拦截器统一剥离data并处理错误状态码import axios from axios import { ElMessage } from element-plus import { useUserStore } from /stores/user import router from /router const request axios.create({ baseURL: /api, timeout: 10000 }) // 请求拦截器自动携带token request.interceptors.request.use(config { const userStore useUserStore() if (userStore.token) { config.headers.Authorization Bearer ${userStore.token} } return config }) // 响应拦截器处理业务错误 request.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res }, error { if (error.response error.response.status 401) { ElMessage.error(登录已过期请重新登录) const userStore useUserStore() userStore.logout() router.push(/login) } else { ElMessage.error(error.message || 网络异常) } return Promise.reject(error) } )注意Vite开发环境下baseURL写/api然后配置代理转发到后端8080端口。生产环境Nginx也会把/api反向代理到后端服务这样前端代码里就不用写死后端地址切换环境时只需要改代理配置前端代码一行不动。4.3 路由守卫、登录鉴权与动态菜单路由设计上建议采用静态路由 动态路由结合的方式。静态路由只有登录页、404页、首页。登录后前端拿到用户的菜单列表通过router.addRoute()动态添加业务页面路由。这样不同角色登录后看到的菜单和能访问的路由都不同未授权页面即便直接在地址栏输入路径动态路由未注册也会跳404安全性和体验都好。核心的路由守卫逻辑router.beforeEach((to, from, next) { const userStore useUserStore() if (to.path /login) { next() return } if (!userStore.token) { next(/login) return } // 动态路由只添加一次 if (!userStore.routesLoaded) { userStore.loadMenus().then(() { next({ ...to, replace: true }) }) return } next() })菜单渲染结合Element Plus的el-menu组件根据后端返回的菜单数据递归渲染图标、路径、子菜单全部动态生成。管理员只需在数据库菜单表里配数据前端会自动多出来页面和导航入口新增模块时不需要改前端路由注册代码这是权限管理系统的体验加分项。4.4 核心页面学员管理和课程管理学员管理页面是最典型的CRUD页面也是毕设代码量最集中的地方。我分享一下页面设计的套路表格区域el-table绑定学员列表数据columns显示姓名、手机号、年龄/年级、报名渠道、创建时间、操作按钮。操作按钮包括编辑、查看详情、删除。新增/编辑弹窗el-dialog里嵌el-form表单字段和新增时保持一致编辑时回填数据。注意表单校验姓名必填、手机号11位正则、年级下拉选择。分页区域el-pagination组件绑定current-page和page-size切换页码触发接口重新查询。这个页面还有一个实际操作中提高效率的技巧把新增和编辑合并成一个弹窗组件通过一个isEdit标记控制标题和提交接口。很多初学者写两套弹窗代码重复了两遍看起来工作量很大实际上不利于后期维护。我用一个表单弹窗组件搞定表单的初始化和提交逻辑统一处理。课程管理页面的区别在于状态字段。课程有上架/下架状态通常用el-switch实现更改状态时调一个updateStatus接口而不是提交整个表单。这里有一个设计要点课程已关联班级且班级内有学员时不能直接删除后端要返回该课程已有班级和学员数据不能删除的提示前端再以MessageBox告知用户。这个校验逻辑放在后端而不是前端表单禁用才能真正防止绕过接口直接调用删除。5. 前后端联调与部署上线全流程5.1 开发环境的跨域处理前后端分离开发中最常见的报错就是跨域。浏览器同源策略挡住了8080端口对8081端口的后端请求。解决方案有两个。前端Vite代理推荐开发环境用// vite.config.js export default defineConfig({ server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true, rewrite: path path.replace(/^\/api/, ) } } } })后端CORS配置备用方案也是答辩时会被问到的一个点Configuration public class CorsConfig { Bean public CorsFilter corsFilter() { CorsConfiguration config new CorsConfiguration(); config.addAllowedOriginPattern(*); config.addAllowedHeader(*); config.addAllowedMethod(*); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); return new CorsFilter(source); } }生产环境我建议用Nginx统一处理这样后端不需要开放CORS同源环境下也不会出现跨域问题。顺便提一句setAllowCredentials(true)如果配合addAllowedOrigin(*)会被浏览器拒绝必须用allowedOriginPattern很多人踩了这个坑不知道为什么前端请求一直在报跨域。5.2 生产环境构建与Nginx配置前端构建npm run build打包后在dist目录下生成静态文件把它上传到服务器/opt/training/frontend/目录。后端先打成jar包mvn clean package -DskipTests然后上传target/training-system.jar以nohup方式启动nohup java -jar training-system.jar --spring.profiles.activeprod app.log 21 Nginx配置server { listen 80; server_name your-domain.com; # 前端静态文件 location / { root /opt/training/frontend; try_files $uri $uri/ /index.html; } # 后端接口代理 location /api/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }try_files这个配置很关键。Vue Router如果使用history模式不带#号用户在浏览器地址栏直接访问/user/list或刷新页面时Nginx会找不到这个虚拟路径而返回404。try_files $uri $uri/ /index.html的作用是当请求路径不存在时统一返回index.html让前端路由接管。这个不配置生产环境一刷新页面就白屏是非常典型的部署问题。实际部署时还需注意一点数据库连接、Redis地址这些配置要抽到application-prod.yml里编译时用--spring.profiles.active指定。千万别把本地数据库连接地址直接写进提交的配置里数据库密码用自定义环境变量注入既是安全习惯答辩时也能说出道理。6. 常见问题与避坑实录做这种全栈项目最耗时的地方反而不是写代码而是排查各种隐蔽问题。我把自己遇到过的问题整理成一张速查表每一条都是真实踩过的坑现象根本原因解决方案前端请求接口报跨域开发时端口不同未配置代理或CORSVite proxy或后端CorsFilterToken过期后请求无响应全局异常未捕获JWT过期异常在RestControllerAdvice中处理ExpiredJwtException时间字段返回数组[2024,5,20,...]Jackson序列化LocalDateTime格式问题配置JsonFormat(patternyyyy-MM-dd HH:mm:ss)或全局Jackson配置删除业务数据报外键约束错误子表有关联数据未处理先删除或转移子表数据或逻辑删除刷新页面404Nginx未配置try_fileslocation / 中加 try_files $uri $uri/ /index.html启动时端口被占用上一个进程未关闭找到PID kill掉或server.port换端口数据库中文乱码连接字符集未指定utf8url中加characterEncodingutf8确认表字符集为utf8mb4分页查询total始终为0分页拦截器未配置配置MybatisPlusInterceptor并添加PaginationInnerInterceptorList 类型传参为空前端传的是字符串数组RequestParam List ids 配合 JSON序列化处理这里重点说一下时间字段那个坑。Spring Boot默认把LocalDateTime序列化成ISO格式的数组前端拿到的是一堆数字看着完全懵。常规解决办法是在字段上加JsonFormat(pattern yyyy-MM-dd HH:mm:ss)但项目里时间字段多挨个加注解太麻烦了。我更喜欢在全局配置里统一设置Bean public Jackson2ObjectMapperBuilderCustomizer jacksonCustomizer() { return builder - builder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss))) .deserializers(new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss))); }还有一种容易忽视的情况前端表单提交日期字符串2025-06-01后端LocalDateTime接收会报格式转换异常。如果业务只需要日期而不需要时分秒建议字段类型直接用LocalDate天然和字符串yyyy-MM-dd匹配少很多麻烦。还有MyBatis-Plus的LambdaQueryWrapper拼接like条件时如果有or一定要注意括号问题。like(...).or().like(...)生成的SQL是name like ? or phone like ?优先级没问题但如果后面又加了其他条件比如status 1拼接顺序不对会变成name like ? or (phone like ? and status 1)查询结果就完全不对了。这种情况下用wrapper.and(w - w.like(...).or().like(...))把or条件包起来逻辑才正确。7. 答辩高频追问与项目扩展思路7.1 这些问题老师爱问提前准备好做完项目之后最重要的一件事就是准备答辩。老师不会老老实实把代码从头看到尾他们喜欢围绕架构和关键点进行追问。为什么选择JWT而不是Session——从无状态、扩展性、前后端分离场景去答同时要承认JWT的短板主动提到用Redis黑名单补偿Token无法主动失效的问题这个深度很容易给对方留下好印象。怎么保证一个学员不能重复报名同一门课程——先是数据库层面的唯一索引student_id class_id组合unique再是业务代码里查重最后配合事务。如果老师继续追问并发场景把悲观锁/乐观锁那一套抛出来基本就是超纲加分了。你的权限怎么控制的——从RBAC模型讲起用户→角色→菜单权限三层结构后端用拦截器校验接口权限前端用路由守卫和按钮级指令控制展示。可以顺带提一下如果接口权限用Spring Security的PreAuthorize注解会更简洁但自己用拦截器实现是为了理解底层原理这个回答既体现实力又显谦虚。分页是怎么实现的——MyBatis-Plus分页拦截器自动拼接limit底层是物理分页。扩展到MySQL分页语法limit offset, size再提到数据量过大时深翻页性能问题可以从limit offset大数值讲起提到覆盖索引或游标分页优化。Redis在你的项目里用在哪些地方——Token黑名单、课程热榜缓存、验证码缓存。再补充提到缓存穿透、缓存击穿的概念哪怕只是概念性描述也足够展示广度。7.2 再给项目加点分扩展开源方向如果时间和能力允许建议在基础功能之上做一些差异化亮点。比如引入ECharts做一个管理端数据看板展示本月营收趋势、热门课程Top5、各年级学员分布饼图。数据来源就是已有的enrollment和payment表写几个统计SQL的事但视觉效果和答辩效果提升非常明显。引入EasyExcel或POI实现学员名单/成绩单的Excel导入导出。这个功能对培训机构来说是刚需期末导出成绩单、招生名单导出是业务上很真实的需求。用EasyExcel封装好了模板下载、导入校验工作量不大但项目完整度提升一截。用WebSocket做一个简单的通知功能选课审核通过后系统实时推送消息给前端。Spring Boot提供WebSocket原生支持配合STOMP协议前端用SockJS Stomp.js订阅消息通道。这是其他同学没有但你有的功能。关于这个话题我最后有一个很实在的体会不要在项目一开始就急着敲代码。先花几天把表结构设计清楚把页面原型画出来把接口文档定义出来之后再写代码会顺畅很多。很多同学做类似系统容易陷入边写边想的状态写学员管理就只写学员表写课程管理才开始想到要建课程班级关联结果写到一半发现表结构要推倒重来。先啃下数据库设计和接口设计这两块硬骨头后面就是体力活了。另外大可以放心的是做完整套流程之后Spring Boot Vue这套组合的常见套路你基本都摸透了以后再遇到类似的管理系统需求三两天能搭出同一套骨架这套东西的可迁移性远比想象中高。
返回列表