
如果你正在准备Java方向的毕业设计看到“基于JavaSpring Boot的家教管理系统”这个题目我先跟你交个底这不是一个普通的“学生管理”增删改查项目而是一个包含了家长、教师、运营方三方角色的全流程业务系统。你的答辩成绩、项目含金量取决于你对“业务闭环”的理解而不是你写了多少行代码。这个系统要解决的真正问题是家教市场上的信息不对称和流程管理混乱。家长找不到合适的老师老师找不到稳定的生源机构没法管控教学质量和财务结算。所以这个毕设的价值在于它把一个真实的服务交易场景搬到了线上用Java技术栈实现了从需求发布、师资审核、智能匹配、预约试听、下单支付、课程排期、课后评价到财务结算的完整链路。这篇文章不写给代码生成器用户写给真正想把这个项目做扎实、想顺利通过答辩、甚至以后想以此为基础扩展成完整商用项目的同学。我会从业务设计、技术选型、数据库建模、核心功能实现到高频坑的排查把我带项目时积累的经验全部拆开讲清楚。1. 先从业务说起家教管理系统的核心边界在哪里1.1 三个使用角色决定了系统的基本骨架很多同学拿到题目后第一反应是建表、写登录、写CRUD结果做到一半发现系统变成了“用户管理课程管理”两个孤立模块。这是最典型的毕设误区。做业务系统第一步永远是梳理角色和使用场景。家教管理系统的角色非常清晰就是三类人家长端发布家教需求、浏览教师列表、查看教师详情和评价、预约试听、下单购买课时包、查看孩子课程安排、课后评价。教师端注册并提交资质材料、等待平台审核、查看可接的需求订单、抢单或接受平台匹配、管理自己的课时表和上课记录、申请结算提现。管理员端审核教师入驻资料、管理学科和年级分类、处理订单纠纷与退款、查看平台运营数据、管理教师结算。你要注意一个细节家长和教师本质上都是用户所以用户表可以统一但家长和教师的扩展信息完全不同。教师需要审核资质、学科标签、授课年级、可授课区域、时薪报价家长需要关联孩子信息比如孩子姓名、所在年级、薄弱科目。这两种角色如果硬塞在同一张表里后期扩展一定会出问题。我建议的三段式设计是统一账户表存登录凭证分离的档案表存角色扩展信息再通过一个角色字段把它们关联起来。这样做的好处是以后想加“机构管理员”或者“课程顾问”这类角色时不需要改动用户主表只需要新增档案表。1.2 一条需求从发布到结算中间经历了什么“全流程对接与管控”这几个字是整个题目的核心得分点。你要让答辩老师看到你设计的不是一个信息展示平台而是一个有状态流转、有业务规则、有交易闭环的管理系统。我拿一条完整的家教订单来举例家长注册登录填写孩子的基本信息和辅导需求比如“高一数学每周六下午2点到4点家在朝阳区希望女老师时薪150左右”。系统生成一条需求单状态为“待匹配”。需求单进入匹配池有两种处理方式一种是教师主动浏览需求池并抢单一种是系统按学科、区域、年级自动推荐给符合条件的教师。教师申请接单后状态变为“待家长确认”。家长同意后双方可以发起“预约试听”试听课时不扣费或扣体验课时。试听满意家长下单购买正式课时包订单状态变为“待支付”。支付成功后生成“已支付订单”家长和教师开始排课。教师按课表上课家长确认消课系统扣减课时。课程完成后家长对教师进行评价教师获得对应的课时费结算记录。教师账户金额达到可提现标准发起提现申请管理员审核打款。你发现没有这条链路里踩了好几个关键状态需求状态、接单状态、订单状态、课程状态、结算状态。这些状态之间的流转规则才是你的系统区别于普通展示网站的核心。我建议画一张状态流转图放在论文里答辩的时候直接用这张图讲业务比任何文字都有说服力。2. 技术选型的底层逻辑为什么偏偏是Spring Boot2.1 Spring Boot MyBatis-Plus毕设性价比最高的组合如果你参加过招聘或者看过最新的Java岗位描述会发现Spring Boot几乎是所有Web后端岗位的标配。但作为毕设技术选型它真正的优势不是“流行”而是它把工程化门槛降到了极致。Spring Boot核心价值是自动配置。你不用再像SSM时代那样手写一大堆XML配置文件引入一个spring-boot-starter-web依赖内嵌Tomcat自动启动写一个RestController就能对外提供接口。这意味着你可以把精力集中在业务逻辑上而不是跟配置环境搏斗。数据访问层我强烈推荐用MyBatis-Plus而不是原生MyBatis。原因很直接这个项目至少有八九张核心表每张表都要写增删改查用原生MyBatis意味着你要为每个实体写Mapper接口和XML文件工作量巨大。MyBatis-Plus提供内置的BaseMapper单表CRUD零SQL还支持分页插件、逻辑删除、自动填充、乐观锁插件。毕设场景下这些功能能帮你省掉起码30%的代码量。不要盲目追求微服务架构。有的同学为了看上去高级把系统拆成用户服务、订单服务、消息服务三个Spring Cloud模块结果一个Demo级别的项目被拆得漏洞百出部署答辩时服务之间通信失败当场翻车。家教管理系统就是一个典型的单体应用用单体架构完全能撑住把代码分层做好比什么都重要。2.2 技术栈组合推荐和核心配置思路我在实际带项目时给学员推荐的是这样一套组合你可以直接参考JDK 8 Maven 3.6稳定兼容性最好。如果你要用JDK 17或Spring Boot 3.0不是不行但要注意MyBatis-Plus和某些工具类的版本兼容问题答辩环境里折腾版本不如求稳。Spring Boot 2.7.x这是2.x系列的长期维护版本资料多遇到问题好查。MySQL 8.0存业务数据。Redis存登录Token、做分布式锁、缓存热点数据。MyBatis-Plus 3.5.x数据访问增强。Hutool工具类库处理日期、随机数、ID生成都很方便。JWT Spring Security或拦截器做登录认证和权限控制。毕设项目用拦截器就够了Spring Security的过滤器链对新手不友好写不好反而是负担。Lombok消除Getter/Setter样板代码。文件存储本地磁盘存储就够用把上传目录配置到application.yml里。不要为了“云存储”硬上MinIO或阿里云OSS除非你有现成的服务器和对象存储资源。提醒一句Spring Boot 2.7.x对应的是Java 8Redis客户端用LettuceMySQL驱动用com.mysql.cj.jdbc.Driver。这些细节看似不起眼但配置错了项目启动直接报错而且错误信息对新手很不友好。2.3 包结构规划别把Controller写成万能类我评审过很多学生的代码最常见的毛病是Controller里写业务逻辑、Service里调Service、一个方法几百行。代码能跑但答辩时老师一问“你这个项目怎么分层的”你支支吾吾答不上来分数就下来了。规范的项目结构应该长这样com.example.tutor ├── common // 通用类返回结果封装、全局异常、常量、枚举 ├── config // 配置类MyBatis-Plus分页、CORS、Redis、拦截器注册 ├── controller // 控制层只负责参数接收和结果返回 ├── service // 业务层业务逻辑和事务控制 │ └── impl ├── mapper // 数据访问层继承BaseMapper的接口 ├── entity // 数据库实体 ├── dto // 前端传入参数对象 ├── vo // 返回给前端的视图对象 └── utils // 工具类JWT、日期处理、文件上传这里我特别想强调一个点DTO和Entity必须分离。有的同学图省事直接把数据库实体暴露给前端接口结果就是前端能传什么字段你完全不可控比如用户传一个roleadmin进来就能越权。正确做法是写一个LoginDTO接收登录参数再通过UserVO返回给前端需要的信息密码、盐值、逻辑删除标记这些敏感字段统统不返回。这不仅是安全要求也是答辩老师很在意的工程素养。3. 数据库设计这个项目的命根子3.1 九张核心表每一张都要有明确职责数据表设计直接决定了你的业务逻辑能走多远。我见过有同学把订单、课程、评价全塞在一张表里字段列表拉出来快30列看着都头疼。这里我给你一套经过实战验证的表结构方案可以直接参考用户主表userid主键用自增或雪花IDusername登录账号唯一索引passwordBCrypt加密后的密码phone手机号唯一索引role角色1-家长2-教师3-管理员avatar头像URLstatus账号状态0-禁用1-正常created_at、updated_at时间字段教师档案表teacher_profileid、user_id关联用户表real_name真实姓名qualification学历或毕业院校teaching_years教龄id_card_url身份证照片仅做审核材料certificate_url教师资格证等资质材料subject_tags可教科目标签比如“高中数学”“初中物理”area可授课区域hourly_rate期望时薪audit_status审核状态0-待审核1-通过2-驳回audit_remark审核备注introduction自我介绍学生档案表studentid、parent_user_id关联家长账户name孩子姓名grade年级school学校weak_subjects薄弱科目家长需求表demandid、parent_user_id、student_idsubject需要辅导的科目grade孩子年级area上课区域salary_expectation心理预期时薪schedule_desc时间安排描述status0-待匹配1-已接单2-已完成3-已取消remark补充说明订单表orderid、order_no订单编号唯一索引生成规则时间戳随机数demand_id关联需求单order_type1-试听订单2-正式课时订单teacher_user_id、parent_user_idtotal_hours总课时unit_price单课时单价存数值单位是元total_amount实付金额pay_status0-未支付1-已支付2-已退款order_status0-待支付1-待上课2-进行中3-已完成4-已取消5-退款中created_at、paid_at、completed_at课表/排课表course_scheduleid、order_idstudent_id、teacher_user_idstart_time、end_time上课时间段location上课地点或线上链接status0-待上课1-已确认2-已完成3-已取消4-缺勤checkin_code课消码或确认码评价表evaluationid、order_id、course_schedule_idfrom_user_id、to_user_idscore评分1到5content文字评价created_at结算/提现表withdraw_recordid、teacher_user_idamount提现金额status0-申请中1-审核通过2-已打款3-驳回apply_time、audit_time、remark3.2 金额、状态与约束新手最容易踩的三个细节先说明金额问题。家教课时费可能出现“150元每小时”这样的报价数据库里用DECIMAL(10,2)存是可以的但我更建议以“分”为单位存整数比如150元存15000。原因很简单浮点数运算有精度问题整数运算永远不会出现0.10.2不等于0.3的情况。如果你觉得以分存储阅读不方便可以加一个VO转换层返回给前端时除以100展示成元。再说状态字段。我见过很多同学的数据库里状态字段是varchar类型塞的是“待支付”“已完成”这种中文文案。这非常糟糕。状态应该用整数枚举含义统一集中在枚举类里管理。Java侧定义一个枚举类public enum OrderStatus { WAIT_PAY(0, 待支付), WAIT_CLASS(1, 待上课), IN_PROGRESS(2, 进行中), COMPLETED(3, 已完成), CANCELED(4, 已取消), REFUNDING(5, 退款中); private final int value; private final String desc; }这样做的好处是业务代码里不会出现魔法数字状态流转时你调用枚举做校验逻辑一目了然。答辩老师看到你用了枚举而不是散落的字符串常量观感会好很多。第三是唯一约束。表设计时要把“这笔订单不能重复创建”“同一时间段老师不能有两节课”这类业务规则用数据库约束兜底。比如订单表的order_no设置唯一索引课表里加一个UNIQUE KEY uk_teacher_time (teacher_user_id, start_time, end_time)这样即使代码有并发漏洞数据库也能挡住重复数据。多表关系设计不要用物理外键但必须建普通索引。如今主流实践是业务层保证数据一致性物理外键在高并发插入时会影响性能且调用链复杂。你只需要在user_id、order_id、teacher_user_id这类关联字段上建索引查询时就能命中索引不会全表扫描。4. 从空目录到一个能演示的完整系统核心环节实操4.1 工程初始化与基础配置打开Spring InitializrGroup填com.exampleArtifact填tutor-system语言选Java包选JarJava版本选8依赖选Spring Web、MySQL Driver、Lombok。生成后拉进IDEA再手动补上MyBatis-Plus和Redis相关依赖。pom.xml里你要额外加的核心依赖dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependency dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.18/version /dependencyapplication.yml核心配置如下server: port: 8080 servlet: multipart: max-file-size: 20MB max-request-size: 50MB spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/tutor_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 redis: host: localhost port: 6379 database: 0 mybatis-plus: mapper-locations: classpath*:/mapper/**/*.xml 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这里有个很容易忽略的配置MyBatis-Plus分页插件必须单独注册不注册的话selectPage查出来的数据全是第一页不会真正分页。在config包下新建MybatisPlusConfigConfiguration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }4.2 登录认证与权限控制JWT 拦截器方案登录接口的逻辑并不复杂但也是整个系统安全的地基。流程是这样的前端提交username和password。后端根据用户名查用户用BCrypt校验密码。校验通过后生成JWT Token把用户ID、角色、过期时间装进Token里。把Token存入Redis键为login:token:{userId}设置过期时间。返回给前端前端每次请求在请求头Header里带Authorization: Bearer {token}。后端定义拦截器在进入Controller之前校验Token是否合法、是否过期、Redis中是否存在。拦截器实现的关键代码public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录接口 if (request.getRequestURI().contains(/auth/login)) { return true; } String token request.getHeader(Authorization); if (token null || !token.startsWith(Bearer )) { throw new BusinessException(401, 未登录或登录已过期); } String realToken token.substring(7); Claims claims JwtUtil.parseToken(realToken); Long userId Long.valueOf(claims.get(userId).toString()); // 校验Redis中Token是否存在实现单点登录或主动退出 String redisToken redisTemplate.opsForValue().get(login:token: userId); if (!realToken.equals(redisToken)) { throw new BusinessException(401, 账号已在其他设备登录); } // 把用户信息放入ThreadLocal后续Service可直接获取 UserContext.set(userId); return true; } Override public void afterCompletion(...) { UserContext.clear(); } }这里有个加分设计把Token存入Redis并做校验可以实现“退出登录即失效”和“同一账号互踢”。如果不存RedisJWT天然无状态用户点了退出后Token在有效期内依然是可用的这是很多毕设被追问时答不上来的安全漏洞。权限控制方面我建议不用Spring Security的重型过滤器链而是写一个简单的角色校验注解。比如定义一个RequireRole(teacher)注解在教师接单、排课这些接口上加注解拦截器里解析注解并校验当前登录用户角色。既能体现你对RBAC模型的理解又不会把自己绕晕在Spring Security的过滤器链里。4.3 教师入驻审核与需求匹配从0到1打通第一个闭环教师入驻审核是管理员端最重要的功能也是体现“全流程管控”的招牌场景。教师提交入驻申请后teacher_profile表的audit_status变为0待审核。管理员端页面上展示待审核列表管理员点击“通过”或“驳回”后端更新审核状态并给教师发送系统消息或站内信。如果驳回必须填写audit_remark教师端可以查看驳回原因并修改资料后重新提交。这个模块里你不需要写复杂的代码但要注意一点审核操作必须添加事务和状态校验。比如教师提交申请后管理员通过审核此时如果教师状态被并发重复提交可能导致audit_status从“已通过”改回“待审核”。解决办法就是在执行审核前先查询当前状态并做判断Transactional(rollbackFor Exception.class) public void auditTeacher(Long teacherId, Integer auditResult, String remark) { TeacherProfile profile teacherProfileMapper.selectById(teacherId); // 只有待审核状态才能审核 if (profile.getAuditStatus() ! 0) { throw new BusinessException(该申请已审核请勿重复操作); } profile.setAuditStatus(auditResult); profile.setAuditRemark(remark); teacherProfileMapper.updateById(profile); }需求匹配这个环节是很多同学觉得“没东西可做”的地方。其实最简单的方案就是给需求单打上“科目 年级 区域”三个标签然后写一个教师端的需求列表接口按这些标签做过滤和排序public PageResultDemandVO queryCanAcceptDemand(Long teacherUserId, int page, int size) { // 1. 查询教师自己的资质拿到它可授学科和区域 TeacherProfile teacher teacherProfileMapper.selectByUserId(teacherUserId); // 2. 按学科和区域过滤需求单排除自己已接单或已取消的 LambdaQueryWrapperDemand wrapper new LambdaQueryWrapper(); wrapper.eq(Demand::getSubject, teacher.getSubjectTags()) .like(Demand::getArea, teacher.getArea()) .eq(Demand::getStatus, 0) .orderByDesc(Demand::getCreateTime); // 3. 分页返回 }这样虽然简单但足以演示“系统给你推荐了合适的单子”这个业务概念。如果你想做得更漂亮一点可以给需求单和教师分别定义标签用标签重叠度计算匹配分按匹配分倒序输出。代码量不大但是答辩时你能讲出“基于标签的推荐策略”比一句“我直接查数据库”高出一个段位。4.4 家长下单、支付与课程消课把核心交易链路跑通家长发起订单时后端要做的事很多先根据需求单找到确认接单的教师、计算订单总金额、生成唯一订单号、把订单状态置为“待支付”。我这里给你一个订单号生成规则String orderNo T System.currentTimeMillis() String.format(%04d, new Random().nextInt(10000));生成订单号后不要直接返回链接而是调一个“模拟支付”接口。因为真实支付需要商户号、证书等资源毕设环境里不可能真接微信或支付宝。模拟支付流程是前端弹出一个支付确认框输入支付密码后调用后端/pay/mock接口后端校验订单为待支付状态直接把pay_status置为已支付order_status置为待上课。这看似简单但有一个必须处理的问题支付回调与订单状态的一致性。如果用户重复点击“支付成功”后端会重复修改状态吗所以支付接口要做幂等处理Transactional public void mockPay(String orderNo) { Orders order orderMapper.selectByOrderNo(orderNo); if (order.getPayStatus() 1) { // 已支付过了直接返回不重复处理 return; } if (order.getOrderStatus() ! 0) { throw new BusinessException(订单状态异常无法支付); } order.setPayStatus(1); order.setOrderStatus(1); order.setPaidAt(LocalDateTime.now()); orderMapper.updateById(order); }课程消课的思路类似。教师上完课后发起“确认上课”家长端收到待确认提醒家长点击确认后系统扣减订单里的剩余课时。如果家长在规定时间内没有确认系统可以做成自动确认。这块业务逻辑不难关键是把字段设计好订单表里有total_hours和used_hours两个字段每次消课就是对used_hours累加当used_hours等于total_hours时订单状态变为已完成。4.5 定时任务与消息提醒让系统自己跑起来一个让答辩老师觉得“这个系统是活的”的加分项是定时任务。我建议你至少做两个场景第一个是超时未支付订单自动关闭。用户下单后20分钟未支付订单自动作废释放教师时间排期。用Spring自带的Scheduled就能实现Component public class OrderTimeoutTask { Scheduled(cron 0 */1 * * * ?) public void closeExpiredOrders() { LocalDateTime deadline LocalDateTime.now().minusMinutes(20); ListOrders expiredOrders orderMapper.selectList( new LambdaQueryWrapperOrders() .eq(Orders::getOrderStatus, 0) .lt(Orders::getCreateTime, deadline) .eq(Orders::getPayStatus, 0) ); for (Orders order : expiredOrders) { order.setOrderStatus(4); orderMapper.updateById(order); } } }第二个是上课前提醒。任务每30分钟扫描一次课表找到距上课还有1小时且状态为“已确认”的课程调用一个发消息的服务。这个“消息服务”在毕设里可以简化为站内信或邮件。如果不想引入消息队列就是最朴素的定时轮询状态标记。定时任务有一个坑必须提醒任务执行要保证幂等不能重复执行产生重复提醒。最简单的处理方式是在执行逻辑里加状态判断比如查询只查status 1的未提醒课表执行后把状态改为2已提醒。不要试图用分布式锁解决所有问题毕设场景下做到业务幂等就够了。5. 实战中的高频坑和我的排查手册5.1 抢单并发导致的数据错乱家教系统里教师抢单是一个真实的并发场景。多个教师同时点击“接单”后端接口如果这么写Demand demand demandMapper.selectById(demandId); if (demand.getStatus() 0) { demand.setStatus(1); demand.setTeacherUserId(currentUserId); demandMapper.updateById(demand); }两个请求同时读到status 0同时通过校验先后执行更新最后一个覆盖前一个你以为只有一个老师抢到了实际有两个人都显示抢单成功。解决思路有两个层面第一是数据库层面兜底。在需求表里加一个设计上“乐观锁”的版本号字段version用MyBatis-Plus的Version标注public class Demand { Version private Integer version; }更新时MyBatis-Plus会自动带上WHERE version ?条件更新失败影响行数为0业务层判断到影响行数为0就提示“手慢了需求已被抢”。第二是Redis分布式锁。抢单接口入口处用setIfAbsent加锁锁的key可以是lock:demand:{demandId}拿到锁才能进入后续逻辑执行完释放锁。这样能在高并发下保证同一时刻只有一个线程在处理某个需求单。5.2 支付回调失败和重复回调真实支付系统会有网络抖动导致异步通知失败的情况模拟支付同样会面临前端把请求重试多次的问题。我的排查经验总结为三个要点支付接口必须幂等订单号作为唯一业务键已支付状态直接放行。订单状态和支付状态是两回事写一个定时任务每天核对一次“已支付订单”的order_status是否应该推进。这是我前面说的“对账补偿”思路。如果出现重复支付理论上不应该要支持退款流程订单增加一个“退款中”状态管理员审批后把pay_status置为已退款。5.3 跨域、Token过期与文件上传的连环坑前后端分离项目必然遇到跨域。后端不做任何配置浏览器直接拦截响应。在config目录加一个跨域配置类Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }Token过期是另一个常见的用户体感问题。方案是前端在Axios统一响应拦截器里判断HTTP状态码401跳到登录页并清除本地存储。后端还需要处理一个细节拦截器里放行的接口不要一股脑全放行比如/auth/login和/auth/register放行但/admin/**必须严格校验管理员角色。文件上传最玄学的坑是“本地能传部署到服务器就不行”。排查思路按顺序来先看Spring Boot的multipart配置再看服务器的/tmp目录权限最后记得在配置文件里指定一个持久化上传目录不要用系统临时目录。如果上传的文件是图片还要做图片格式后缀校验防止有人传可执行文件。5.4 MyBatis-Plus逻辑删除与唯一索引的冲突MyBatis-Plus的逻辑删除功能很方便但它和一个细节有冲突如果user表的手机号是唯一索引用户删除后逻辑删除deleted1你再注册一个同号码的新用户时唯一索引仍然存在导致插入失败。解决方案有三种一是唯一索引改成联合索引把deleted字段加入索引比如UNIQUE KEY uk_phone_deleted (phone, deleted)二是删除时把手机号改成旧号码_时间戳三是干脆不做用户删除功能只做禁用。我建议用第二种简单直观也不会破坏索引结构。还有一个常见问题MyBatis-Plus更新时null字段不更新。默认策略下updateById传入的实体字段为null时该字段不会被更新到数据库。有些同学想用updateById把某个字段置空发现没生效一脸懵。解决办法是字段上加TableField(updateStrategy FieldStrategy.IGNORED)或者改用UpdateWrapper里的set方法。6. 演示、答辩与项目扩展的实战思路6.1 巧用数据看板让系统看起来更完整家教管理系统天然适合做数据运营看板。管理员端你可以放三个图表近30天订单量和营收趋势折线图。热门学科分布饼图按订单里的subject字段统计。教师服务评分排行列表。图表用ECharts就行后端提供一个聚合统计接口用MyBatis-Plus的selectMaps加group by查询返回ListMapString, Object。这部分代码量不大但效果极其抢眼。答辩现场把看板一展示老师立刻会觉得这个项目不是简单的CRUD——它具备了数据分析和决策辅助的雏形。6.2 答辩演示脚本按用户故事讲不要按接口讲我见过太多学生答辩时带着电脑一个一个点菜单从用户管理点到课程管理毫无逻辑。正确演示方式是按“用户故事”串起来讲先用管理员账号登录展示教师待审核列表通过一位数学老师的入驻申请。切换教师账号登录后看到自己审核通过进入需求池抢到一单高中数学需求。切换家长账号发布一条孩子数学辅导需求注意发布前系统里最好已经有这条预置数据现场输入浪费时间。演示系统自动匹配到了刚才那位数学老师家长确认试听。教师上传课表家长确认课程排期生成。切换到教师端点击“确认上课”切回家长端确认消课并评价。切回管理员端查看订单统计和营收图表顺带演示财务结算列表。这七个步骤展示下来所有核心功能都覆盖到了而且逻辑是一条完整的业务线。这就是“全流程对接与管控系统”最好的诠释。6.3 时间不够时什么功能可以砍很多同学做毕设时觉得功能越多越好结果把自己拖死在半路上。我给你的建议是抓大放小。核心中的核心是用户登录、教师入驻审核、需求发布、订单支付、课程排课、评价。这六个功能构成完整闭环必须做扎实。以下功能如果时间紧张可以先砍掉或用最简单方案代替站内信通知用数据库消息表页面轮询实现不做WebSocket。优惠券/打折活动完全不必要家教平台不靠这个盈利。多级分销别碰业务复杂度高且论文不好写。在线网课直播这是另一个项目了你的题目是“对接与管控”不是“在线教学”。我在实际项目里见过太多学生因为纠结“要不要做个在线课堂”而拖了进度最后连核心流程都没跑通。务必记住毕设系统要的是完整和流畅不是功能堆砌。最后再分享一个我反复强调的小技巧所有核心表都要预置演示数据并且演示数据要贴近真实。老师名字可以叫“王明老师”孩子叫“李小雨”科目是“高一数学”地址写“北京市朝阳区”金额别出现负数或超长小数。真实感强的数据能让答辩老师快速理解你的业务场景也会让整个演示过程顺畅很多。这个项目做下来你对Spring Boot的理解、对数据库设计的体会、对业务流程的敏感度都会远超一个只会写增删改查的人。把这套东西吃透毕业设计答辩就是一次普通的功能演示而已。