在中小型物流公司担任车队主管三年后,我深刻体会到传统Excel表格管理车辆的痛点:调度冲突频发、维修记录丢失、油耗数据混乱。去年用SpringBoot+Vue重构了我们公司的车辆管理系统后,管理效率提升了60%。本文将分享这套经过实战检验的企业级车辆管理系统完整实现方案。
这个系统最核心的价值在于实现了四个维度的数字化管理:
后端选择SpringBoot的三大理由:
前端Vue.js的优势实践:
数据库设计关键决策:
系统采用经典DDD分层架构:
code复制src/
├── main/
│ ├── java/
│ │ └── com/
│ │ └── fleet/
│ │ ├── application/ # 应用服务层
│ │ ├── domain/ # 领域模型层
│ │ ├── infrastructure/ # 基础设施层
│ │ └── interfaces/ # 接口层
│ └── resources/
│ ├── mapper/ # MyBatis映射文件
│ └── static/ # 前端静态资源
数据库表优化实践:
sql复制CREATE TABLE `t_vehicle` (
`id` bigint NOT NULL AUTO_INCREMENT,
`plate_number` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '车牌号(带校验规则)',
`vin` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '车架号',
`gps_device_id` varchar(30) DEFAULT NULL COMMENT 'GPS设备ID',
`insurance_expire` date DEFAULT NULL COMMENT '保险到期日',
`annual_inspection` date DEFAULT NULL COMMENT '年检日期',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_plate` (`plate_number`) USING BTREE,
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
关键业务逻辑:
java复制public boolean validatePlateNumber(String plate) {
String pattern = "([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF])|([DF]([A-HJ-NP-Z0-9])[0-9]{4})))|([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1})";
return Pattern.matches(pattern, plate);
}
mermaid复制stateDiagram
[*] --> 闲置
闲置 --> 使用中: 分配任务
使用中 --> 维修中: 上报故障
维修中 --> 闲置: 维修完成
使用中 --> 闲置: 任务结束
核心业务规则:
实现代码片段:
java复制@Scheduled(cron = "0 0 9 * * ?") // 每天9点检查
public void checkMaintenance() {
List<Vehicle> vehicles = vehicleMapper.selectAll();
vehicles.forEach(v -> {
// 里程检查
if (v.getMileage() - v.getLastMaintainMileage() > 5000) {
alertService.sendAlert(v.getId(),
"里程保养提醒",
"当前里程已达" + v.getMileage() + "公里");
}
// 时间检查
if (ChronoUnit.MONTHS.between(
v.getLastMaintainDate(),
LocalDate.now()) >= 6) {
// 发送提醒逻辑
}
});
}
使用时间重叠算法防止同一车辆被重复分配:
java复制public boolean checkScheduleConflict(Long vehicleId,
LocalDateTime start, LocalDateTime end) {
List<Schedule> exists = scheduleMapper
.selectByVehicle(vehicleId);
return exists.stream().anyMatch(s ->
!(end.isBefore(s.getStartTime()) ||
start.isAfter(s.getEndTime())));
}
基于历史数据的三西格玛原则实现:
java复制public boolean isFuelAbnormal(Long vehicleId, Double currentFuel) {
VehicleStats stats = statsMapper.selectById(vehicleId);
double lower = stats.getAvgFuel() - 3*stats.getStdDev();
double upper = stats.getAvgFuel() + 3*stats.getStdDev();
return currentFuel < lower || currentFuel > upper;
}
缓存策略:
java复制@Cacheable(value = "vehicle", key = "#id")
public Vehicle getById(Long id) {
return vehicleMapper.selectById(id);
}
@Caching(evict = {
@CacheEvict(value = "vehicle", key = "#entity.id"),
@CacheEvict(value = "vehicleList", allEntries = true)
})
public void update(Vehicle entity) {
vehicleMapper.updateById(entity);
}
SQL优化案例:
sql复制-- 原查询(执行时间1.2s)
SELECT * FROM t_maintenance
WHERE vehicle_id = 123
ORDER BY maintenance_date DESC;
-- 优化后(添加复合索引后0.03s)
ALTER TABLE t_maintenance
ADD INDEX idx_vehicle_date (vehicle_id, maintenance_date DESC);
java复制public String encryptPassword(String raw) {
return new BCryptPasswordEncoder().encode(raw);
}
xml复制<if test="plateNumber != null">
AND plate_number = #{plateNumber}
<!-- 不要使用 ${plateNumber} -->
</if>
文件上传的坑:
nginx复制client_max_body_size 20M;
时间格式问题:
javascript复制axios.interceptors.request.use(config => {
if (config.params && config.params.startTime) {
config.params.startTime = moment(config.params.startTime)
.utcOffset(8).format();
}
return config;
});
事务失效场景:
java复制((VehicleService) AopContext.currentProxy()).updateWithTx(entity);
对接高德地图API实现:
增加OBD设备接入:
微信小程序端开发:
这套系统在我们公司稳定运行一年多,日均处理调度请求300+次,累计生成维修报告1200余份。最大的收获是培养了对业务细节的掌控能力——比如发现夜间加油量普遍偏高后,我们调整了油卡使用策略,年节省油费15万元