news 2026/10/25 6:51:38

SpringBoot+MyBatis-Plus打造交通违章管理系统:毕设实战全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot+MyBatis-Plus打造交通违章管理系统:毕设实战全流程

做毕设选了交通违章管理系统这个题目的同学,十有八九是被"Web 版车辆违章查询处理系统"这个场景吸引来的。我自己当年就是从这套系统入手的 SpringBoot,从数据库设计到前端页面再到最后的部署上线,全程踩了无数坑,熬夜修过 Bug,也推倒重来过两版。这篇博客不灌水,把整套系统的设计思路、核心实现、排错经验完整拉出来说清楚,正在做毕业设计的同学可以直接照着搭,想在 SpringBoot 项目里练手的小白也能找到自己想要的干货。

SpringBoot 加持下的交通违章管理平台,核心其实就是三件事:违章数据的管理、违章处理流程的闭环、以及不同角色的权限划分。听起来像政府项目,但落到毕设层面,它本质上是一套标准的 Java Web 增删改查增强版系统,难点不在于某个技术的深度,而在于业务关系的设计,以及对权限、统计报表、日志记录这类非核心功能的取舍。

这套系统适合两类人:一类是正在做计算机毕业设计、选题方向是 Java Web 系统开发的同学,另一类是刚学完 SSM 基础、想用 SpringBoot+MyBatis-Plus 做完整项目练手的初级开发者。因为它的模块边界清晰、业务逻辑有代表性,麻雀虽小五脏俱全,RESTful 接口、ORM 映射、权限拦截、数据统计全都覆盖得到,做完这一套,你对整个 Java Web 开发链路的理解会系统很多。

1. 项目整体设计与技术选型思路

1.1 先把业务边界画清楚,别一上来就写代码

很多同学拿到课题第一反应是建工程、写实体类,这是最大的坑。交通违章管理系统这个词看起来很具体,但你仔细想一想,里面包含了好几套完全不同的业务场景:违章数据的录入和查询是一套逻辑,违章处理(确认、处罚、缴款、销案)是一套流程,车辆和驾驶员的档案管理是另一套数据,再加上后台的统计分析和系统管理,每一块都能拆出不少东西。

我先给当年自己的设计做了一个减法,把系统边界定为三条线:管理端负责车辆信息、驾驶员信息、违章记录的维护;处理端负责对违章记录做审核、处罚登记、处理状态变更;查询端面向普通用户,提供根据车牌号或驾驶证号的违章查询和详情查看。系统管理作为辅助模块,负责用户账号、角色权限和基础数据字典的维护。

这个边界的价值在于,它天然形成了三种角色:管理员、处理员、普通用户,角色的权限差异直接决定了接口设计和菜单显示的逻辑。如果你不做角色拆分,把所有功能堆在一个页面上,答辩的时候老师第一个问题就是"你的系统如何保证不同身份的人只能操作自己能操作的数据",到那个时候再改权限模型,成本就大了。

1.2 为什么 SpringBoot 是毕设选题的默认答案

技术选型上,SpringBoot 几乎是这类毕设的标准答案,原因很实在。

第一,它把 Spring 生态的配置成本压到了极低。以前用 SSM 做项目,光 applicationContext.xml 就要写一大堆 Bean 定义,SpringBoot 用自动配置全部接管了,我可以把精力集中在业务代码上,而不是反复调整 XML 配置。对我来说,这节省了至少两天的时间。

第二,内置 Tomcat,打包即部署。我做完之后直接mvn package打成 jar 包,放到服务器上java -jar就启动了,不用单独装 Tomcat 配置端口、发布 war 包,这个体验对没有服务器运维经验的学生党非常友好。

第三,社区生态成熟,遇到问题搜得到答案。SpringBoot 相关的博客、示例代码、踩坑记录全网到处都是,毕设阶段你大概率会遇到各种连环报错,如果一个技术搜不到解决方案,那才是真的灾难。

结合同学们的实际情况,我建议技术栈固定为:SpringBoot 2.x + MyBatis-Plus + MySQL 8.x + Thymeleaf(或 Vue,后面我会细说前端选型)。值得一提的是 MyBatis-Plus,它让我省去了绝大部分手写 SQL 的活,单表 CRUD 直接继承 BaseMapper 就能用,这让代码量肉眼可见地降了下来,非常适合时间紧任务重的毕设节奏。

1.3 功能模块怎么拆才合理

模块划分是项目结构的骨架,我最终选的是按业务域分包,而不是按技术层分包。按技术层分包是那种 controller/service/mapper 一刀切三层目录的做法,乍一看规整,但业务一多就会乱。我用的方式是每个功能域一个完整的分层包:

  • modules/user:用户管理、登录认证、角色权限
  • modules/vehicle:车辆信息管理
  • modules/driver:驾驶员信息管理
  • modules/violation:违章记录、违章处理流程
  • modules/system:数据字典、系统配置
  • modules/statistics:统计报表

这样写的最大好处是,每个模块你都可以看作一个独立的小系统来开发,业务内聚性很强。我做违章模块的时候只需要在 violation 包内部操作,完全不需要去别的地方找代码,调试时定位问题也顺手得多。后面如果你想要加功能,比如增加一个"事故记录管理",直接新建一个 accident 包,不动其他模块的代码。

2. 数据库设计与实体建模

2.1 六张核心表搞定全部业务

数据库设计是整个系统里我会放最多心思的地方,因为表结构一旦定下来,后面改起来代价极高。我最早一版设计用了九张表,后来发现有几张表其实可以合并,自己做了一次重构,最终稳定在下面这六张核心表上:

表名用途关键字段说明
sys_user系统用户user_id, username, password, role_type, status三种角色用 role_type 区分
vehicle_info车辆档案vehicle_id, plate_number, owner_name, owner_id_card, vehicle_type, register_date车牌号是业务主键,做唯一索引
driver_info驾驶员档案driver_id, name, license_number, license_type, phone, address驾驶证号唯一
violation_record违章记录record_id, vehicle_id, driver_id, violation_type, violation_time, location, penalty_points, fine_amount, status核心表,外键关联车辆和驾驶员
violation_process违章处理记录process_id, record_id, handler_id, process_time, process_type, result, remark处理和违章记录是一对多关系
sys_dict数据字典dict_id, dict_type, dict_code, dict_value, sort_order存违章类型、车辆类型等枚举

这个设计的核心思路是:把车辆的静态信息和违章的动态行为拆开,开车的人(驾驶员)和管理对象(车辆)也分开。违章记录里同时存车辆 ID 和驾驶员 ID,这样查一辆车的历史违章,直接按车辆 ID 过滤就行;查一个驾驶员的扣分情况,按驾驶员 ID 分组求和就好,查询路径非常清晰。

业务主键这块我有话要说。车牌号和驾驶证号在现实业务里都是业务主键,但在系统里我仍然建议用自增 ID 做物理主键,车牌号只加唯一索引。原因很现实,车牌号可能因为车辆过户等原因变更,一旦用业务字段做物理主键,改数据的时候会牵扯所有外键关联,简直生不如死。自增 ID 是个无意义的纯技术标识,永远不需要改,这才是主键该有的样子。

2.2 实体类设计:从建表 SQL 到 Java 类的转换

表结构确定后,Java 实体类的设计要跟表字段一一对应,这里 MyBatis-Plus 的约定优于配置帮了大忙。我只需要保证驼峰命名和数据库下划线命名的自动映射开好,就完全不用手写 ResultMap。

以违章记录表为例,实体类的核心写法大概是这样的:

@Data @TableName("violation_record") public class ViolationRecord { @TableId(type = IdType.AUTO) private Long recordId; private Long vehicleId; private Long driverId; private String violationType; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime violationTime; private String location; private Integer penaltyPoints; private BigDecimal fineAmount; private Integer status; @TableField(exist = false) private String plateNumber; @TableField(exist = false) private String driverName; }

这里有两个细节容易被忽略,我特意标注出来。

@TableField(exist = false)这个注解太重要了。上面代码里的plateNumber和driverName不是数据库表里的字段,而是我为了前端展示方便,做多表联查时手动填充的"虚拟字段"。如果不加这个注解,MyBatis-Plus 在做插入和更新的时候会默认把这两个字段拼进 SQL,直接报 "Unknown column" 错误。第一次遇到这个报错的人,大概率会去改表结构加字段,其实完全没必要,一个注解就能解决。

另一个细节是 LocalDateTime 的时间格式化。数据库存的是 datetime 类型,Java 这边用 LocalDateTime 接收,如果不对 JSON 序列化做处理,前端拿到的是一长串数字时间戳。加上@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")后,接口返回值会自动变成人类可读的时间字符串。这个我在第一个版本里没加,结果前端页面时间显示全乱了,后来才发现是这个注解的问题。

2.3 时间字段与状态字段设计的四个关键细节

建表时的字段类型选择,看起来是小事,实际影响很大。这里分享我总结出来的四个经验,都是改过线上数据才记住的教训。

第一,时间字段一律用 datetime 类型,不要用 timestamp。timestamp 的范围到 2038 年就结束了(2038 年问题),而且它依赖数据库服务器的时区设置,一旦服务器时区配置不对,你存进去的时间跟实际时间差八个小时。datetime 不受时区影响,你用 MySQL 连接串里的 serverTimezone 参数来控制,反而更可控。

第二,凡是涉及金额的字段,一律用 Decimal,不要用 float 或者 double。违章罚款金额虽然都是整数,但如果以后扩展出滞纳金、折扣这类逻辑,浮点类型会出现精度丢失问题,0.1+0.2 不等于 0.3 这种事情在财务数据里是绝对不能出现的。Decimal(10, 2) 足够覆盖绝大部分业务金额场景。

第三,最重要的表都要带 status 字段,而且定义一个全局统一的状态含义。比如违章记录表里,我用 0 表示待处理、1 表示已处理、2 表示已撤销。这个状态字段是把系统的查询和统计串起来的关键,后面做处理流程和首页统计都要靠它。

第四,每个表都要有create_time和update_time两个审计字段。MyBatis-Plus 有自动填充的机制,实现MetaObjectHandler接口就能自动在插入和更新时填充这两个字段,不用手动 set。花十分钟配置一次,后面排查数据问题的时间能省好几个小时。

3. 核心业务功能实现

3.1 违章记录录入与多条件组合查询

违章记录录入是整个系统使用频率最高的功能之一。处理员在录入界面选择车辆、选择驾驶员、选择违章类型、填写地点、选择时间,系统自动带出该类型对应的罚款金额和扣分,确认后保存到数据库。

这里有个很实用的实现技巧:违章类型不要硬编码在前端下拉框里,而是从 sys_dict 数据字典表读取。我在系统管理里维护了一张字典表,存违章类型的代码(比如 "speeding" 对应超速)和值(对应 200 元和 3 分),前端加载页面时调用字典接口动态渲染下拉选项。这样做的好处是,以后违章类型要调整,只需要在系统管理界面改字典数据,代码一行都不用动。

违章查询功能我用的是 MyBatis-Plus 的 LambdaQueryWrapper 做动态条件拼装,这是它最让我省心的地方:

public Page<ViolationRecordVO> queryViolationPage(ViolationQueryDTO dto) { LambdaQueryWrapper<ViolationRecord> wrapper = Wrappers.lambdaQuery(); wrapper.eq(StringUtils.hasText(dto.getPlateNumber()), ViolationRecord::getVehicleId, vehicleIdCache.get(dto.getPlateNumber())); wrapper.eq(dto.getStatus() != null, ViolationRecord::getStatus, dto.getStatus()); wrapper.ge(dto.getStartTime() != null, ViolationRecord::getViolationTime, dto.getStartTime()); wrapper.le(dto.getEndTime() != null, ViolationRecord::getViolationTime, dto.getEndTime()); wrapper.orderByDesc(ViolationRecord::getViolationTime); return violationMapper.selectPage(new Page<>(dto.getPageNum(), dto.getPageSize()), wrapper); }

注意我用的全部是 Lambda 表达式引用实体类的 getter 方法,而不是字符串字段名。这么做的好处是,如果实体类字段重命名,编译期就能发现错误,不会等到运行时报 SQL 异常。

车牌号转 vehicleId 这块,我用了一个缓存集合来避免每次查询都去查一次车辆表。这个优化其实很小,但在数据量大、用户反复查询同一个车牌的场景下,响应速度提升是能感知到的。当然这只是毕设级别的优化,真实生产系统会用 Redis 缓存,但毕设做这种程度已经能展现思考深度了。

3.2 违章处理流程的状态机设计

违章处理是这套系统的灵魂功能,也是答辩时最能体现技术含量的一部分。我建议把它设计成一个简单的状态机:违章记录在录入时是待处理状态,处理员确认违章事实无误后,进入已处理状态并生成处理记录;如果录入时信息有误或者重复录入,可以作废,进入已撤销状态。

实现状态机时,我在 Service 层定义了清晰的状态流转方法,而不是直接在 Controller 里改状态字段:

@Service public class ViolationProcessService { @Transactional(rollbackFor = Exception.class) public void processViolation(ProcessRequestDTO dto) { ViolationRecord record = violationMapper.selectById(dto.getRecordId()); if (record == null || record.getStatus() != 0) { throw new BusinessException("该违章记录不存在或已被处理"); } // 更新违章记录状态 record.setStatus(1); violationMapper.updateById(record); // 插入处理记录 ViolationProcess process = new ViolationProcess(); process.setRecordId(record.getRecordId()); process.setHandlerId(CurrentUserHolder.getUserId()); process.setProcessType(dto.getProcessType()); process.setResult(dto.getResult()); process.setRemark(dto.getRemark()); processMapper.insert(process); } }

这里有两个关键点,一个是事务,一个是逻辑校验。

@Transactional(rollbackFor = Exception.class)必须加上,因为更新违章状态和插入处理记录是两个独立的数据库操作,任何一个失败都不能留下半完成的脏数据。RollbackFor 默认只回滚 RuntimeException,rollbackFor = Exception.class 是为了把受检异常也纳入回滚范围,这是很多教程里没讲的细节。

逻辑校验也很重要。用户传过来的 recordId 如果对应的记录不存在,或者状态已经是已处理,那就是非法请求,必须抛异常拒绝掉。没做这层校验的第一个版本里,我遇到过前端连续点击两次提交按钮,导致同一条违章记录被处理了两遍的问题。那时候我才意识到,除了后端校验,前端按钮提交后还要置为 disabled 状态,双重保险才靠得住。

处理流程里的扣分和罚款逻辑,建议在 service 层一并处理。处理员确认处罚后,系统自动把 penaltyPoints 和 fineAmount 从违章记录表读取出来,写入处理结果并形成汇总信息展示给用户。扣分累计超过 12 分的提示逻辑也可以做,这会成为答辩时一个很好的加分亮点。

3.3 车辆与驾驶员信息的联动管理

车辆和驾驶员的管理模块,技术上最核心的就是"联动"两个字。录入违章记录时要选择车辆,选择了车辆之后,系统要能自动带出这辆车所属的驾驶员信息。这个联动用 mybatis-plus 的条件查询很好实现。

我的做法是,车辆表和驾驶员表之间做一个映射字段 driver_id 放在车辆表上,一辆车查询时连带把车主信息查出来:

public VehicleVO getVehicleDetail(Long vehicleId) { VehicleInfo vehicle = vehicleMapper.selectById(vehicleId); DriverInfo driver = driverMapper.selectById(vehicle.getDriverId()); VehicleVO vo = new VehicleVO(); BeanUtils.copyProperties(vehicle, vo); vo.setDriverName(driver.getName()); vo.setDriverPhone(driver.getPhone()); return vo; }

这属于典型的单表查询加代码组装,比写复杂的多表 join 更好维护。我用这种方式做联查的经验是,在数据量不大、查询逻辑不复杂的毕设项目里,避免写长 SQL 是更明智的选择。因为一旦 SQL 写复杂了,MyBatis 的 XML 映射里各种占位符拼接就会成为新的 bug 源。

车辆和驾驶员的新增、修改、删除界面都有表单校验逻辑。车牌号要校验格式(省份简称+字母+数字的组合)、驾驶证号要校验位数、手机号要校验位数,这些校验在前端做一次,后端再校验一次。前端校验是为了用户体验,后端校验是为了数据安全,缺一不可。我说的"安全"不是指网络安全攻防,而是指这条数据进入数据库之前必须保证有效性,否则脏数据会污染所有关联的业务统计。

删除功能这块我还要提醒一句:车辆和驾驶员有关联的违章记录时,不能直接物理删除。我的做法是在删除前检查违反记录是否有关联数据,有关联就给出提示"该车辆存在违章记录,不能删除",你可以选择做逻辑删除(用一个 deleted 标志位顶替物理删除),这是生产系统里最常见的做法,答辩老师对这个设计通常是很认可的。

3.4 统计报表的实现思路

统计报表是交通违章管理系统里最能体现项目完成度的模块,我建议无论如何都要做。答辩时老师看到一堆表格页面和一堆图表页面,对项目的好感度会完全不一样。

我的统计模块主要在管理端,包括以下几个维度:按月份统计违章数量趋势、按违章类型统计占比、按区域统计违章分布、按驾驶员统计扣分排行。用 SpringBoot 做聚合查询的思路其实很朴素,在 Service 层写一个统计方法,使用 MyBatis-Plus 提供的selectMaps返回 map 列表:

public List<Map<String, Object>> countByMonth() { return violationMapper.selectMaps( new QueryWrapper<ViolationRecord>() .select("DATE_FORMAT(violation_time, '%Y-%m') as month", "COUNT(*) as total") .groupBy("month") .orderByAsc("month") ); }

前端拿到这个 list 之后,用 ECharts 的折线图直接渲染出来就行。这块我的经验是,后端只负责给出结构化的统计结果,所有的图表样式、颜色、动画都属于前端的事,后端不要做任何跟展示相关的处理,职责边界要清晰。

这里我要特别提一下selectMaps的这个用法。它的返回结果是List<Map<String, Object>>,因为 select 子句里的字段都不是实体类的属性,MyBatis-Plus 会把它包装成 map。注意这里的 map 的 key 是 SQL 里起的别名(month、total),对外接口的字段命名讲究的是稳定,所以我在 Controller 层把统计结果的 key 重命名成了count对应的英文,这样前端不管怎么改,只要接口字段约定不变就行。

4. 前端页面与 API 接口设计

4.1 Thymeleaf 还是 Vue:前端选型实录

前端方案是毕设里一个比较纠结的问题。如果你没有学过前端框架,我建议直接选 Thymeleaf 模板引擎,它跟 SpringBoot 天然集成,后端 Controller 返回视图名,模板里通过th:each循环渲染 list,前端代码量小很多,学习成本低,一个人也能完得成。

如果你学过 Vue 或者有一定前端基础,选 Vue 拆前后端项目会让系统架构更现代,答辩的时候也更好讲。但代价是你需要处理跨域问题,要把前端的 8080 端口和后端的 8081 端口协调好,还要做接口联调,工作量至少多出百分之三十。

我最终选的是 Thymeleaf + 原生 JavaScript + Bootstrap 的组合。选这个方案的原因很朴素:我不需要额外安装 Node 环境、不用配置代理转发,整个 SpringBoot 项目打包成一个可运行的 jar 就够了。我的心态是"毕设的核心是把业务做完整,前端够用就行",实际用下来 Bootstrap 的栅格布局和表格样式让页面不至于太丑。

不管是哪种方案,有一个原则都要守住:页面上所有的数据展示和操作结果,都必须依赖后端接口返回的 JSON 数据,不能通过直接拼接 SQL 的方式从页面上取数。页面只是"视图","模型"必须由后端统一提供。

4.2 接口路径与返回格式的统一约定

接口设计直接影响前后端联调的效率。我最早做的时候接口路径很随意,一会儿/getList,一会儿/queryList,维护起来非常混乱。后来我给自己定了一套 RESTful 风格的规范:

功能接口路径请求方式说明
违章分页查询/api/violationsGET参数 pageNum, pageSize, 各种筛选条件
违章详情/api/violations/{id}GET路径参数传违章记录 ID
新增违章记录/api/violationsPOST请求体传 JSON 数据
处理违章/api/violations/{id}/processPOST违章处理动作单独一个接口
删除违章记录/api/violations/{id}DELETE逻辑删除

返回格式上,我统一使用一个 Result 类包装:

public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> ok(T data) { ... } public static <T> Result<T> error(String message) { ... } }

code 为 200 表示成功,其他为失败,配合 message 给前端展示错误提示。这套约定让前端的错误处理变得很简单,只需要判断 code 是不是 200 就行,不需要为每个接口单独写 try-catch。建议在做所有接口之前先把 Result 类写好,后面每个 Controller 方法都直接复用。

4.3 前后端联调最容易踩的三个坑

第一个坑是日期格式不一致。后端默认返回的 LocalDateTime 格式如果你没做处理,前端拿到的是时间戳或者带 T 的 ISO 格式字符串,跟页面上显示的年月日不是一回事。这个在前面实体类设计时已经说过了,加@JsonFormat就能解决,但要注意时区。

第二个坑是分页参数对不上。MyBatis-Plus 的 Page 对象在 JSON 序列化时,分页信息字段名是records、total、current、size,前端如果要展示"共 xx 条记录",得从 total 字段拿。有的同学一开始写的分页接口返回的是整个 Page 对象,前端不熟悉 MP 的字段命名,直接找不到总数在哪儿,白白来回传了好几趟。

第三个坑是 POST 请求的 content-type。如果前端用原生 ajax POST 提交 form 格式的数据,而后端 Controller 是用@RequestBody接收 JSON 对象,那就会报 415 Unsupported Media Type。我的经验是,毕设项目里统一用 JSON 字符串交互,前端提交前用JSON.stringify(data),请求头设Content-Type: application/json,后端用@RequestBody接收,这套组合最省心。

5. 部署上线与问题排查实录

5.1 本地跑通的最小环境清单

我整理一份能跑起整套系统的环境清单,做毕设的同学装好这个就能开始开发了:

软件版本建议用途
JDK1.8 或 11Java 运行环境
Maven3.6+依赖管理和打包
MySQL8.0数据库
IDEA2022 及以上开发工具
Navicat 或 DBeaver均可数据库可视化工具

导入项目的步骤我这里不展开了,就说一个关键配置:application.yml 里的数据库连接串必须带时区参数。我推荐serverTimezone=Asia/Shanghai,如果不带这个参数,MySQL 8.x 会报 "The server time zone value" 的异常,这个错误几乎每个用 MySQL 8 的 SpringBoot 项目都会遇到。

项目跑起来之后先做一次完整的连通性测试:进管理员界面新建一个用户,再录一条车辆信息、一条驾驶员信息、一条违章记录,走一遍违章处理流程,最后看一眼统计图表是否正常。这五个动作覆盖了系统所有核心表,任何一个有问题都能及时暴露出,然后集中排查修复。

5.2 我遇到过的五个典型问题

这里把我踩过的坑整理成一张速查表,每一列都是从实际操作中记录的解决方案:

问题现象可能原因解决方案
启动时报端口被占用上一次启动没有正常关闭找到占用 8080 端口的进程并 kill 掉
数据库连接失败密码错误或 MySQL 服务未启动检查 application.yml 中的用户名密码配置
表单提交报 400 错误JSON 参数类型不匹配检查前端传参的字段名和后端实体类是否一致
列表页分页不生效没有配置 MyBatis-Plus 分页插件新建一个 MybatisPlusConfig 配置类,添加分页拦截器
时间显示差 8 小时MySQL 时区配置问题连接串加 serverTimezone=Asia/Shanghai,前端展示时统一用后端格式化后的字符串

分页不生效那个问题,我要着重说一下。很多人继承 BaseMapper 后发现selectPage方法返回的记录数就是全部数据,没有真正的分页效果,原因就是漏了 MybatisPlusConfig 里的分页插件配置。SpirngBoot 3.x 和 2.x 的配置写法有差异,你搜到的教程要确认跟你项目版本一致,不然照抄了还是报错。

5.3 让系统更稳的几个实用细节

系统能跑通只是第一步,稳定性和安全性是确保答辩顺利的关键。

第一个细节是密码加密。用户表的密码不能明文存储,SpringBoot 里最简单的方案是使用 BCryptPasswordEncoder 加密后入库,登录时再匹配。这个类是 Spring Security 的一部分,但单独引一个 spring-security-crypto 包就能用,不需要引入整套 Security。

第二个细节是登录拦截。没有登录的人不能直接访问管理端接口,做法是写一个 HandlerInterceptor 拦截所有 /api/** 请求,检查 session 里有没有用户信息,没有就返回 401 错误码。

第三个细节是全局异常处理。在 Controller 层加一个@RestControllerAdvice,统一捕获业务异常和系统异常,把异常信息包装成 Result 格式返回给前端,不要让堆栈信息直接暴露在前端页面。这个对你的代码可维护性和答辩观感都是加分项。

第二个细节和第三个细节是配套的,不写拦截器你的接口就是裸奔的,不写全局异常处理你的报错页面可能就是一堆英文堆栈,这两件事花半天时间做完,整个系统给人的感觉完全不一样。

这套系统做完之后,我对 SpringBoot 的整个开发链路有了完整的认知。从前端的页面渲染逻辑,到后端的接口处理和事务控制,再到数据库的表结构设计,每一个环节出了问题,你都能在这个系统里找到对应的排查路径。

最后再分享一个小经验,也是我踩过几次坑之后学到的:状态字段的取值含义一定要写注释,宁可注释写在实体类的每个字段上,也不要让后人(包括一个月后的自己)去猜 0 和 1 到底代表什么状态。我做第一版的时候没写注释,中间隔了两周回去改需求,自己都看不懂那个 status 字段什么意思了,只好去翻数据库用例数据才猜出来。

具体到交通违章管理系统,你还可以在基础功能上继续扩展,比如增加短信通知或邮件通知的违章提醒、增加微信小程序查询端、把统计报表升级成 ECharts 大屏展示,这些都是答辩时很有冲击力的扩展点。先把这套核心系统做扎实,后续的每一步扩展都能让你的项目上一个台阶。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 6:03:24

Kubernetes组件全解析:从控制面到工作节点

Kubernetes 这东西&#xff0c;光看名字可能觉得离自己很远&#xff0c;但只要是做后端、运维或者云原生相关工作的&#xff0c;早晚要跟它打交道。我早年刚接触 K8s 的时候&#xff0c;最头疼的就是面对一摞陌生的术语&#xff1a;API Server、etcd、controller、scheduler、k…

作者头像 李华
网站建设 2026/10/25 21:46:28

模拟银行账户转账系统:并发扣款、事务回滚与幂等性实战

简介&#xff1a;这是一份面向Java初学者与GUI编程练习者的模拟银行账户转账系统源码&#xff0c;围绕A、B两个初始余额均为1000元的账户展开&#xff0c;通过随机双向转账演示余额校验、零余额拦截等核心逻辑&#xff0c;适合用于课程设计、线程与界面交互的练手场景。压缩包内…

作者头像 李华
网站建设 2026/10/26 6:03:24

用Claude Code和Vibe Coding一天搞定macOS小工具开发

过去做一个 macOS 小工具&#xff0c;哪怕功能再简单&#xff0c;也要走完“搭建 Xcode 工程 → 写界面 → 写逻辑 → 打包签名 → 找测试用户 → 写文档 → 发布”这一整套流程。对没有 macOS 开发经验的人来说&#xff0c;光是搞清楚 SwiftUI 的MenuBarExtra怎么用、NSApplic…

作者头像 李华
网站建设 2026/10/18 6:32:00

电话语音提醒服务搭建指南:从TTS合成到自动外呼的完整实现

“想我可以打给我&#xff0c;我说的是电话”——这句话放在社交场景里是一句带情绪的表达&#xff0c;但放在技术场景里&#xff0c;完全可以变成一个真正可执行的需求&#xff1a;让程序、脚本或者后台服务在需要找人的时候&#xff0c;直接拨通手机&#xff0c;用语音把话说…

作者头像 李华
网站建设 2026/10/21 20:08:47

JavaWeb华为商城.zip实战:JSP+Servlet+MySQL部署与避坑指南

简介&#xff1a;一份基于JavaWeb技术实现的“华为商城”网上商城系统项目&#xff0c;面向JavaWeb与MySQL学习者&#xff0c;特别适合课程设计、毕业设计或综合实训时参考。资源共2个文件&#xff0c;合计58.24MB&#xff0c;包含RAR格式的完整项目源码包与SQL数据库脚本&…

作者头像 李华