最近这几个月,陆陆续续有开发朋友给我发同一个链接,问的是同一件事:“这套企业级车辆管理系统源码到底能不能直接用?”我点开一看,SpringBoot+Vue+MyBatis+MySQL,标准的原生技术栈,没有整花活。问题问得多了,我就想把话摊开说清楚——一套挂着“企业级”名头的车辆管理系统,背后到底有多少门道;源码拿到手里,哪些部分可以直接抄,哪些部分必须二次开发;SpringBoot+Vue+MyBatis+MySQL这套组合,在企业内部系统里为什么被反复使用,又有哪些细节坑等着你踩。
这篇文章不写源码逐行注释,也不贴全量代码,而是以这套企业级车辆管理系统的架构为主线,讲清楚业务边界、表结构设计、MyBatis实战、Vue权限控制和部署上线的完整链路。如果你正在挑选车辆管理系统模板做二次开发,或者打算用这个技术栈做毕业设计、企业内部后台,又或者单纯想搞明白SSM+前端分离架构在企业项目里到底怎么写才不翻车,这篇应该能把你的疑问串起来。
1. 先别急着看代码:这套系统解决的是车队的哪些真实问题
1.1 “企业级”和“单机版”的差别体现在哪
“企业级”这个前缀,在源码下载站里已经被用滥了,但落到车辆管理系统上,它确实有一些硬指标。
车辆管理系统不是简单的车辆增删改查。企业自己有车队、有专职司机、员工要申请用车、后勤要审批、财务要核算油费和维修费,这套东西的本质是“车辆资产全生命周期管理”加上“用车审批流程”。从车辆购置建档、保险年检,到派车调度、出车回车、维修保养、违章处理,再到按部门统计用车成本,是一条完整的数据链。
所谓“企业级”,和“课程设计级”“单机演示版”的区别,我总结成五点:
- 权限模型:不是表单一套登录就完事,而是超级管理员、车管员、调度员、驾驶员、普通员工按角色区分数据权限和操作权限。
- 审批流:用车申请不是直接改数据库,而是要走到待审批、已批准、已驳回、出车中、已回车这样的状态流转。
- 数据字典:车辆类型、状态、费用类型这些枚举值,不是写死在代码里,而是存在字典表,能维护扩展。
- 操作审计:谁在什么时间改了什么字段,出车单谁批的,车辆状态谁变的,要有迹可循。
- 成本核算:每辆车的油耗、维修、保险、罚款按部门或按项目分摊,这是老板真正关心的东西。
如果你拿到的源码只做到了车辆表和派车单表的CRUD,那它离“企业级”还差着十万八千里。真正有价值的部分,恰恰是这套权限、审批、统计报表的骨架。
1.2 核心业务模块拆解:从车辆档案到成本核算
我接手过的企业车辆管理系统,核心业务模块大概是这样几个:
- 车辆档案管理:车牌号、品牌型号、车辆类型(轿车/SUV/商务车/货车)、座位数、排量、发动机号、车架号、购置日期、所属部门、责任人、当前状态。状态一般分为可用、派车中、维修中、停用、已报废。
- 驾驶员管理:驾驶证号、准驾车型、驾驶证有效期、从业资格证、联系方式、当前是否在岗。有效期临期提醒是做这个模块必须留的口子。
- 派车管理:员工提交用车申请(用车事由、目的地、预计出发和回场时间、乘车人数),调度员审批后指定车辆和驾驶员,出车时登记起始里程,回车时登记结束里程,系统自动计算本次里程。
- 维修保养管理:维修项目、保养类型(日常保养/大保)、费用、维修厂、下次保养里程或时间提醒。
- 保险与年检管理:交强险、商业险的起止日期、保险公司、年检到期日,提前30天、15天、7天自动提醒。
- 违章管理:违章时间、地点、扣分、罚款金额、处理状态、责任人。
- 成本统计:按月度、按部门、按车辆汇总油耗、维修费、路桥费、罚款,算出单车百公里油耗和单车月均成本。
我在最开始设计这套系统的时候犯过一个错——把车辆状态和派车单状态混在一张表里,结果车辆明明在维修中,派车单还能被审批通过。后来把“车辆状态”和“派车单状态”拆开,用状态机去约束联动关系,这个核心问题才算解决。
1.3 角色模型与状态机设计
角色模型上,我一般把用户分五类:
- 超级管理员:管系统配置、用户维护、数据字典、菜单分配,不参与业务。
- 车管员/调度员:管车辆档案、驾驶员档案、审批派车单、人工调车、看统计报表。
- 驾驶员:接收派车任务,登记出车/回车信息,提交维修申请。
- 普通员工:申请用车、查看自己的申请进度。
- 审计/财务:只看成本数据和车辆使用台账,没有任何写操作权限。
这个模型的灵魂在于“角色-菜单-按钮”三层权限,而不是简单的“管理员和普通用户”二分。
状态机设计是整个系统最容易写出一堆if-else的地方。我建议用两条状态链,并且单独建常量类或枚举类去约束:
- 车辆状态链:
可用 -> 派车中 -> 可用,可用 -> 维修中 -> 可用,可用 -> 停用。 - 派车单状态链:
待审批 -> 已批准 -> 出车中 -> 已回车 -> 已完成,其中任何状态都可以被已取消/已驳回终结。
如果要支持“用车超过3天需要部门负责人和车管员双审批”这种需求,别急着引入Flowable或Activiti流程引擎。对于车辆管理这种轻量审批场景,自己写一个approve_record表,记录每个节点的审批人和审批意见,状态机上多挂一个“等待第二审批人”状态就够用了。引入重量级流程引擎,半个项目的复杂度都会转移到流程部署和流程图维护上,得不偿失。
2. 技术栈选择的真实逻辑:SpringBoot+Vue+MyBatis+MySQL为什么被反复使用
2.1 每个组件干的事情
这套技术栈其实可以理解成“一条流水线”:
- SpringBoot负责后端基础设施:控制反转、依赖注入、自动配置、内嵌Tomcat、事务管理、接口暴露。它最大的价值是让开发者不用再处理大量XML配置,一个
application.yml把数据源、端口、日志全部搞定,打包成单jar直接部署。 - Vue负责前端界面:组件化开发,数据驱动视图,配合Element UI或Element Plus组件库,后台管理页面的表格、表单、弹窗、树形菜单几乎都是现成轮子。
- MyBatis负责数据库访问:把SQL写在XML里,让SQL对开发者完全透明。复杂查询、多表关联、统计报表,写起来直接,调优也直接。
- MySQL负责数据落地:开源免费、生态成熟、运维资料多,对中小型企业内部系统来说,性能和稳定性完全够用。
这四个组件不是最前沿的,但是它们拼在一起,恰好覆盖了企业管理系统最核心的几个诉求:开发效率高、SQL可控可调优、前后端分工明确、部署运维简单。
2.2 为什么不选JPA、不选Spring Cloud
很多人问过我:为什么不直接用JPA/Hibernate?我的回答很直接——车辆管理系统有太多统计SQL。
JPA确实在单表CRUD上非常优雅,但一旦遇到“按部门汇总月度用车次数、百公里油耗、维修费用排名”这类报表需求,JPQL自动生成的查询要么性能拉胯,要么语句长到根本没法维护。MyBatis把SQL主动权交还给开发者,一对多、多对多的结果映射也足够直观,团队里哪怕是个刚入职的新人,打开XML文件也能看懂这个查询到底在干什么。
还有人问:现在不都流行微服务吗,为什么不拆成Spring Cloud?我的看法是,企业车辆系统通常跑在内网,使用人数从几十到几千,数据量撑死百万级。单体应用加缓存加一套良好的SQL,完全能稳住。硬拆成六个微服务,服务注册发现、配置中心、链路追踪、分布式事务全都要上,凭空多出一堆维护成本。对这种内部业务系统来说,微服务带来的复杂度是负资产。
2.3 版本选型的坑:JDK、SpringBoot 3.x、Vue 2/3、MySQL 8.0
选型很容易,版本选型才是真正容易踩坑的地方。这块我必须单独说,因为源码下载站上的项目,版本经常是老掉牙的。
SpringBoot 2.x还是3.x:如果你拿到的源码是SpringBoot 2.x的老结构,先别急着升3.x。SpringBoot 3.0要求JDK17、命名空间从
javax.*改成jakarta.*,很多老依赖里的javax.servlet直接报ClassNotFoundException。SpringBoot 2.7.18是2.x系列的最后一个版本,安全更新还在,跑老源码最稳的就是先把SpringBoot固定到2.7.18,而不是盲目升3。Vue 2还是Vue 3:Vue 2.7在2023年底停止维护,新项目直接上Vue3+Element Plus。但如果拿到的源码是Vue2+Element UI,也别急着整体重构,老项目在维护期内能跑就继续跑。我的经验是:老项目新增页面继续用Vue2保持一致,全新项目才上Vue3,两头并行不冲突。
MySQL 5.7还是8.0:8.0默认认证插件是
caching_sha2_password,老驱动连不上,报Public Key Retrieval is not allowed。这不是代码问题,是驱动版本问题。要么把mysql-connector-java升到8.0.33,要么在JDBC URL里加allowPublicKeyRetrieval=true&useSSL=false。5.7还有人在用,能用,但新部署建议直接8.0。
3. 数据库模型与索引实践:车辆管理系统的数据底座
3.1 车辆档案、派车单、维保记录三张核心表怎么定
数据库设计是这类源码里最有含金量的部分。我从实际项目里抽了三张核心表来讲。
第一张是vehicle_info车辆信息表,字段大致是:id主键、vehicle_no车牌号、brand品牌、model型号、vehicle_type车辆类型、seat_count座位数、engine_no发动机号、vin车架号、buy_date购置日期、dept_id所属部门、driver_id当前责任人、status车辆状态、del_flag删除标记、create_time和update_time。注意车辆类型和状态都用TINYINT存数字,再用字典表解释数字含义,不要直接存“可用”“维修中”这种字符串。字符串枚举在业务扩展时就是灾难。
第二张是dispatch_order派车单,字段包括:id、order_no派车单号、apply_user_id申请人、vehicle_id车辆ID、driver_id驾驶员ID、use_reason用车事由、destination目的地、passenger_count乘车人数、plan_start_time预计出发、plan_end_time预计回场、real_start_time实际出发、real_end_time实际回场、start_odo起始里程、end_odo结束里程、total_odo本次里程、status状态。这张表是整个系统的业务中枢,几乎所有报表统计都从它出发。
第三张是maintain_record维修保养记录,字段包括:id、vehicle_id车辆ID、maintain_type保养类型、maintain_date维保日期、mileage维保时里程、cost费用、factory_name维修厂、next_maintain_date下次保养日期、remark备注。
这三张表的关联逻辑是:dispatch_order通过vehicle_id挂到vehicle_info,通过driver_id挂到driver_info,maintain_record通过vehicle_id挂到车辆;统计报表统一以dispatch_order为主表,左连接车辆和部门表。
3.2 软删除、唯一索引、审计字段的处理细节
企业系统里删除操作一般不用物理删除,而是del_flag软删除。但这里有一个非常隐蔽的坑:如果vehicle_no字段上有唯一索引,软删除后再录入同一车牌的新车,就会触发唯一索引冲突,插入失败。
我踩过这个坑之后的解法是:删除时把del_flag从0改成主键id。这样默认del_flag=0表示未删除,删除后del_flag=id,同车牌重复录入时唯一索引不再冲突。这个技巧对用户表、部门表、车辆表这类有唯一标识的主数据都适用。
审计字段方面,我建议create_time用数据库默认值CURRENT_TIMESTAMP,update_time用ON UPDATE CURRENT_TIMESTAMP自动更新,减少应用层代码。如果你用的是MyBatis-Plus,也可以用它的字段自动填充功能,两种方式选一种就行,别同时搞,不然数据不一致。
3.3 让查询快的关键:索引设计、排序与慢查询
车辆管理系统的查询高频场景是:车辆列表按状态过滤、按部门过滤、按车牌模糊搜索;派车单按时间范围查询、按申请人查询。索引设计围绕这三个高频条件来建。
比如vehicle_info表上,我会建一个联合索引(status, dept_id, create_time DESC),匹配“查某部门下某状态的车辆”这种最常见的列表查询。dispatch_order表上,(vehicle_id, plan_start_time)和(apply_user_id, plan_start_time)各建一个普通索引,对应行车记录统计和个人申请记录查询。
排序上的一个典型坑是:如果表里有create_time索引,但你在WHERE里写DATE(create_time) = '2025-01-01',这个索引就废了,MySQL只能全表扫描。正确写法是范围查询:create_time >= '2025-01-01 00:00:00' AND create_time < '2025-01-02 00:00:00'。
我一直建议项目早期就把slow_query_log打开,long_query_time设成1秒。车辆管理系统平时不觉得,到了月底做成本报表统计的时候,几条没走索引的大表关联查询会把数据库拖到接口超时。慢查询日志是我排查这类问题时的第一工具。
4. 后端落地的关键:MyBatis在真实项目里怎么用才能不翻车
4.1 Mapper接口+XML的配合逻辑
MyBatis的企业级用法,核心就是Mapper接口 + XML文件分工。接口里定义方法签名,XML里写SQL,两者通过namespace和statementId对应。
这套写法最大的好处是SQL对开发者完全透明,调优时直接看XML就行。我见过很多团队用注解写SQL,短查询没问题,但动态SQL一复杂,@Select里面塞一堆<script>标签,可读性直线下降。XML单独拆出来,还可以做到改SQL不重新编译项目(开发环境下)。
这里有几个对应关系必须一一对上,否则启动就报Invalid bound statement (not found):
namespace必须等于Mapper接口的全限定名。- XML里的
id必须等于接口方法名。 - 接口方法的参数类型、返回类型,要和XML里
parameterType、resultType匹配。 - Mapper XML文件要放在能被编译到
target/classes的目录下。多模块项目里,XML漏打包是我见过最高频的问题。
4.2 动态SQL和分页:写得好是效率,写不好是深渊
车辆列表高级搜索,十个查询条件八个可空,这种场景就是MyBatis动态SQL的看家本领。我贴一段实际用的车辆分页查询:
<select id="selectVehiclePage" resultType="com.demo.vehicle.dto.VehicleDTO"> SELECT v.*, d.dept_name FROM vehicle_info v LEFT JOIN sys_dept d ON v.dept_id = d.id <where> <if test="vehicleNo != null and vehicleNo != ''"> AND v.vehicle_no LIKE CONCAT('%', #{vehicleNo}, '%') </if> <if test="deptId != null"> AND v.dept_id = #{deptId} </if> <if test="status != null"> AND v.status = #{status} </if> <if test="startTime != null"> AND v.create_time >= #{startTime} </if> <if test="endTime != null"> AND v.create_time <= #{endTime} </if> </where> ORDER BY v.create_time DESC </select><where>标签会自动处理掉第一条件前面的AND,这个设计很贴心,但要注意:车牌号的%abc%双侧通配符是走不了索引的。数据量小没事,数据量大了就得考虑全文索引或者搜索引擎方案。状态、部门这种等值条件,建议和排序字段组合成联合索引。
分页上,企业项目中我用PageHelper比较多,用法是PageHelper.startPage(pageNum, pageSize)之后紧跟Mapper查询。但PageHelper有脾气,几个坑必须知道:
startPage和真正查询之间不能插入别的SQL,否则分页参数会被其他查询消费掉。- 多数据源环境下,PageHelper的
ThreadLocal有串参数风险,要用PageHelper.clearPage()兜底。 LIMIT 100000, 20这种深分页,性能极差。数据量大了以后,改成先取MAX(id)或游标方式,再分页,能快一个数量级。
4.3 缓存到底开不开:MyBatis三级缓存体系实测
“MyBatis缓存”是很多人面试时倒背如流、实际项目却用不明白的模块。我直接说结论。
一级缓存是SqlSession级别的,默认开启。Spring和MyBatis整合后,每次Service方法基本都会新建SqlSession,所以跨Service调用时一级缓存基本共享不到,意义有限。
二级缓存是namespace级别的,默认关闭。我个人的实践是:企业管理系统里,二级缓存一律不开。原因很简单——两张表关联查询,结果缓存在两个Mapper的namespace里,只要其中一张表的数据更新,另一个Mapper的缓存不会自动失效,你查出来的就是脏数据。除非你能保证这个表只有唯一的Mapper在访问,否则别碰二级缓存。
那企业项目里的缓存怎么做?我的做法是:统计报表这种读多写少的数据,用Redis手动缓存,设置60秒或5分钟过期,由Service层自己控制清空;列表查询高频且实时性要求不高的(比如部门树、字典项),Redis存JSON或直接本地Caffeine。比起MyBatis的二级缓存,应用层缓存的可控性和可观测性强得多。
4.4 自定义Configuration、TypeHandler与拦截器扩展
MyBatis真正值钱的能力在扩展点上。热搜词里经常有人搜“mybatis中自定义configuration”“mybatis中xmlconfigbuilser”,说明很多人卡在配置和原理上了。
先说XMLConfigBuilder。它是MyBatis启动时解析mybatis-config.xml的入口,按照XML里的节点顺序,把properties、settings、typeAliases、typeHandlers、environments、mappers逐个加载进全局Configuration对象。如果你发现某个配置不生效,大概率是XML节点顺序错了——比如settings节点写在了environments后面,XMLConfigBuilder已经处理完那一阶段,自然就忽略了。
再说两个我常用的扩展点:
- TypeHandler:处理数据库字段和Java对象的类型映射。比如车辆JSON扩展字段存在MySQL的
json类型里,Java端想直接拿到List<String>或对象,就写一个自定义TypeHandler,在setParameter里做序列化,在getResult里做反序列化。 - Interceptor:MyBatis的四大对象(
Executor、StatementHandler、ParameterHandler、ResultSetHandler)都能被拦截。我实际用过的场景包括:拦截Executor.query统计慢SQL执行时间;拦截StatementHandler.prepare给SQL自动追加数据权限条件(比如“非管理员只能查自己部门的车辆”);拦截ParameterHandler给插入SQL自动填充create_time和update_time。
自定义拦截器要注意的是@Intercepts注解里args的声明必须和真实方法签名完全一致,否则不生效。另外,拦截器执行顺序和定义顺序有关,多个拦截器叠加的时候,顺序错了会改出意想不到的SQL。
4.5 事务边界:多表联动下的正确姿势
车辆管理业务里,事务边界很容易划错。最典型的是派车审批:更新派车单状态为“已批准”、把车辆状态改成“派车中”、给驾驶员生成一条待出车任务,这三件事必须在一个事务里。如果拆成三个Service方法分开调,中间任何一步失败,数据就处于“派车单批了但车辆还是可用”的中间态。
@Transactional默认只在RuntimeException和Error触发回滚,受检异常不会回滚。我见过太多人在这上面翻车:代码里try-catch吞掉异常,或者捕获了IOException没继续向上抛,事务形同虚设。
还要注意同类内部方法调用导致的事务失效。this.approveOrder()直接调用同类里的@Transactional方法,事务注解是不生效的,必须通过代理对象调用,或者把事务方法抽到另一个Service里。遇到这种情况,用TransactionTemplate写编程式事务反而更直接:
transactionTemplate.execute(status -> { dispatchOrderMapper.updateStatus(orderId, "APPROVED"); vehicleMapper.updateStatus(vehicleId, 2); taskMapper.insertDriverTask(vehicleId, driverId); return null; });并发场景下,审批操作最好对派车单行加锁:SELECT ... FOR UPDATE,防止两个调度员同时对同一张单子做了不同操作。这个我是在生产环境被坑过一次才补上的,两个人同时点了“通过”,结果状态被覆盖,车辆被派给了两个驾驶员。
5. Vue端的工程化细节:动态路由、按钮权限与一体化打包
5.1 环境准备:Node版本、代理配置、依赖安装
Vue工程这块,先解决环境,再聊代码。很多人卡在第一步:npm run dev跑不起来,报错还不是语法错误,而是Node版本不匹配。Vue3项目要求Node 16以上,Vue2项目最好用Node 14到16之间,Node 18跑老项目偶尔会出OpenSSL相关的构建报错。处理方式是给Node挂上NODE_OPTIONS=--openssl-legacy-provider,或者用nvm切换版本,我一般直接用nvm,省心。
依赖安装慢是另一大痛点。npm config set registry https://registry.npmmirror.com把镜像切换一下,基本能解决大多数拉包慢或超时的问题。如果项目里已经用了pnpm,那就统一用pnpm,不要混用npm和pnpm,不然node_modules里会出现重复依赖,构建结果五花八门。
开发环境跨域也要提前配好。Vue项目默认端口是8080或5173,后端接口在8081,浏览器直接请求必然跨域。在vue.config.js里配devServer.proxy,把/api开头的请求代理到后端地址,就能绕开跨域。这个配置和上线部署的nginx反向代理逻辑一致,开发环境不配好,后面联调会浪费大量时间。
5.2 菜单与权限的落地方式
Vue端的权限控制,我见过不少“demo项目”的做法——登录后不管什么角色,router里全部路由都注册了,只是菜单隐藏了一部分。这种做法安全性为零,因为用户直接改URL就能访问未授权页面。
正确做法是动态路由加按钮级权限,链路是:
- 用户登录,后端返回
token和用户信息。 - 前端把
token存进pinia(或vuex)和localStorage。 - 路由守卫
router.beforeEach里判断:没有token就跳登录页;有token但菜单没加载过,就调用getUserMenus接口拿菜单树,用router.addRoute逐个动态注册。 - 注册完成后,再执行一次
next({ path: to.fullPath, replace: true }),避免页面空白。 - 按钮级权限用一个自定义指令
v-permission,传权限编码数组,比如v-permission="['vehicle:add']",没有权限的按钮直接remove掉。
这里有一个高频坑:动态路由只存在内存里,页面一刷新就丢了。解决方法是,在路由守卫每次进入时检查store里的menuLoaded标记,如果为false,重新调菜单接口并addRoute,再跳转一次。这是所有做动态路由的项目都避不开的环节,我把它固定成一个标准流程写进模板里。
5.3 打包进SpringBoot:一次搞定内网部署
企业内部系统追求的是部署简单,我不太建议一个小系统单独再买一台服务器搞nginx。更实际的做法是:前端npm run build之后,把生成的dist目录整个拷贝到SpringBoot项目的src/main/resources/static下,随SpringBoot一起打包成一个jar。这样部署时只需要一条java -jar命令,前端后端全起来了。
这条路有两个注意点:
- 如果SpringBoot配置了
server.servlet.context-path,静态资源的访问路径会跟着变,dist里的资源引用的根路径要提前用相对路径或模板变量处理好。 - Vue Router如果用的
history模式,刷新子路由页面时会404,因为后端没有对应的接口。解决方案是让SpringBoot把所有非/api开头的GET请求都转发到index.html,或者在WebMvcConfigurer里加一个addViewControllers兜底。更省事的方案是用hash模式,URL多一个#,但不用做任何后端转发配置。内网系统我一般直接hash模式,省心。
6. 部署与二次开发:从跑起来到真正用得稳
6.1 MySQL连接故障排查:SSL、认证插件与驱动版本
源码拿到手,配置改完,最刺激的环节就是启动。MySQL相关的连接报错,我遇到过的十有八九是下面几类:
| 报错关键词 | 原因 | 处理方式 |
|---|---|---|
Public Key Retrieval is not allowed | MySQL 8.0默认caching_sha2_password认证,驱动版本过旧或未允许公钥检索 | JDBC URL加allowPublicKeyRetrieval=true&useSSL=false,或升级驱动到8.0.33 |
Communications link failure且提示SSL | 客户端开启SSL但服务端不支持,或证书校验失败 | JDBC URL加useSSL=false,云数据库强制SSL的按服务商要求配证书 |
Access denied for user 'root'@'localhost' | 账号密码错误,或认证插件不匹配 | 检查密码;必要时ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '密码' |
Unknown database | 数据库没创建或没授权 | CREATE DATABASE后执行GRANT ALL PRIVILEGES ON xxx.* TO 'user'@'%' |
最揪心的是前两条,它们长得一模一样,都是连接超时或直接拒绝,但一个要从驱动版本解决,一个要关SSL。我的排查顺序是:先看驱动版本,再看JDBC URL参数,最后看MySQL服务端的require_secure_transport配置。
6.2 连接池和大页面的性能保障
连接池选择上,SpringBoot默认集成的是HikariCP,性能很好,配置也简单。但企业内部系统如果希望有可视化监控,Druid更合适。它的StatViewServlet能直接看活跃连接数、SQL执行次数、慢SQL统计,对运维友好。
给一个Druid常用配置示例:
spring: datasource: druid: initial-size: 5 max-active: 20 min-idle: 5 max-wait: 60000 test-while-idle: true validation-query: SELECT 1 time-between-eviction-runs-millis: 60000 stat-view-servlet: enabled: true url-pattern: /druid/* login-username: admin login-password: admin123initial-size和min-idle控制空闲连接数,max-active控制峰值连接数,max-wait是获取连接的最大等待时间。企业车辆系统的并发量不会太高,max-active=20通常足够,别上来就配100,反而浪费数据库资源。
还有一个我建议所有车辆管理系统都做的优化:车辆列表和派车单列表接口返回的字段要精简,别把remark这种长文本放到列表里。很多后台管理系统慢,不是因为SQL写得差,而是返回了大量用不上的字段,前端渲染也跟着卡。
6.3 源码阅读顺序与二次开发建议
最后聊聊拿到一套完整源码之后,怎么读才高效。
我的阅读顺序是固定的,先跑起来再抠代码。第一步看README和数据库初始化脚本,把表结构和初始数据导进去;第二步改application.yml里的数据源、Redis地址、文件存储路径;第三步启动后端,登录页面能进得去;第四步才开始读代码——按这个顺序走一遍:Controller层看接口清单,Service层看业务逻辑,Mapper层的XML看SQL写法。
源码里最值得精读的是两个模块:权限模块和派车审批模块。权限模块能让你搞懂动态路由和数据权限是怎么实现的;派车审批模块是整个业务的核心链路,把状态流转看懂,这套系统的骨架也就摸透了。
二次开发方向上,如果需求是加车辆实时定位,后端要对接GPS终端协议或第三方地图服务商的API,前端接入地图组件。这里有个经验:地图选型上,国内业务就认真评估高德/百度/腾讯,别为了酷炫去碰那些水土不服的国外方案,偏转坐标系问题会让你的车辆轨迹偏离真实道路。如果需要行车记录视频回放,前端要加video.js和hls.js,m3u8流不能直接丢给原生video标签,这是踩过坑的结论。
如果需求是更复杂的审批流,比如会签、或签、多级审批,再引入Flowable或Activite这样的流程引擎不迟;如果只是文档会签这种轻量场景,自己扩展approve_record表完全够用。另外,违章描述、用车事由这类文本如果要做分析,可以用HanLP分词做关键词提取,这个我在扩展日志分析模块的时候用过,效果不错。
我最后想分享的一个判断是:源码下载站里的“企业级”项目,能直接开箱就用的很少,但作为骨架参考价值非常大。真正决定系统能不能落地的,是你对业务的理解——权限模型怎么划、状态机怎么设计、统计报表怎么和财务口径对齐,这些代码之外的东西,才是这套系统能不能在企业里用得稳的关键。把这套SpringBoot+Vue+MyBatis+MySQL的架构吃透,再把车辆管理的业务骨架搭对,二次开发就只是往骨架上填肉的事情。