🚗 从业务到代码:实现车辆保养里程与时间提醒系统的一次深度拆解

53 阅读4分钟

🚗 从业务到代码:实现车辆保养里程与时间提醒系统的一次深度拆解

作者:天天摸鱼的java工程师
标签:Java 后端、系统设计、MySQL、并发、定时任务、架构思维


🧭 一、业务背景

在车联网的时代,车辆保养提醒系统已成为智能出行服务中的标配之一。每辆车都需要定期保养,常见的触发条件有两种:

  • 按里程:比如每 5000 公里保养一次
  • 按时间:比如每 6 个月保养一次

车主经常忘记这些时间节点,而平台的价值就是在合适的时间,提醒用户进行保养


🎯 二、核心目标

  • 实时更新车辆当前里程
  • 动态计算下次保养时间与里程
  • 及时推送保养提醒
  • 系统高并发支持,保证性能可扩展

📐 三、系统设计概览

模块划分清晰,职责单一,易于扩展

+-------------------+
|   数据接入层      | <-- 车辆上报当前里程
+-------------------+
         |
         v
+-------------------+
|   保养规则引擎    | <-- 计算下次保养时间/里程
+-------------------+
         |
         v
+-------------------+
|   提醒调度任务    | <-- 定时扫描即将保养车辆
+-------------------+
         |
         v
+-------------------+
|   消息推送服务    | <-- 推送提醒(短信/APP)
+-------------------+

🧩 四、数据表设计

以下是简化后的核心表结构设计:

1. vehicle_info — 车辆基础信息表

字段名类型说明
idBIGINT PK车辆ID
user_idBIGINT用户ID
plate_numberVARCHAR车牌号
current_mileageINT当前里程(km)
last_update_timeDATETIME最近上报时间

2. vehicle_maintenance_plan — 保养计划表

字段名类型说明
idBIGINT PK主键
vehicle_idBIGINT车辆ID
last_maintenance_timeDATETIME上次保养时间
last_maintenance_mileageINT上次保养里程
mileage_intervalINT每隔多少公里保养
time_interval_monthINT每隔几个月保养
next_maintenance_timeDATETIME下次保养时间
next_maintenance_mileageINT下次保养里程

3. maintenance_remind_log — 保养提醒记录表

字段名类型说明
idBIGINT PK主键
vehicle_idBIGINT车辆ID
remind_timeDATETIME提醒时间
remind_typeVARCHAR里程/时间
is_sentTINYINT是否已发送

🧠 五、核心逻辑实现

1. 🚘 车辆里程上报接口

@PostMapping("/api/mileage/report")
public void reportMileage(@RequestBody MileageReportDTO dto) {
    vehicleService.updateMileage(dto.getVehicleId(), dto.getMileage());
}
@Transactional
public void updateMileage(Long vehicleId, int mileage) {
    Vehicle vehicle = vehicleRepository.findById(vehicleId)
        .orElseThrow(() -> new NotFoundException("车辆不存在"));

    if (mileage > vehicle.getCurrentMileage()) {
        vehicle.setCurrentMileage(mileage);
        vehicle.setLastUpdateTime(LocalDateTime.now());
        vehicleRepository.save(vehicle);

        // 检查是否需要更新保养计划
        maintenanceService.checkAndUpdatePlan(vehicle);
    }
}

2. 🔁 保养计划自动更新逻辑

public void checkAndUpdatePlan(Vehicle vehicle) {
    MaintenancePlan plan = maintenancePlanRepository.findByVehicleId(vehicle.getId());

    int nextMileage = plan.getLastMaintenanceMileage() + plan.getMileageInterval();
    LocalDateTime nextTime = plan.getLastMaintenanceTime().plusMonths(plan.getTimeIntervalMonth());

    plan.setNextMaintenanceMileage(nextMileage);
    plan.setNextMaintenanceTime(nextTime);

    maintenancePlanRepository.save(plan);
}

3. ⏰ 定时任务:扫描即将保养车辆

@Scheduled(cron = "0 0 * * * ?") // 每小时执行一次
public void scanVehiclesForRemind() {
    List<MaintenancePlan> plans = maintenancePlanRepository.findUpcomingMaintenance(
        LocalDateTime.now().plusDays(3), // 提前3天提醒
        200 // 公里差范围
    );

    for (MaintenancePlan plan : plans) {
        if (!remindAlreadySent(plan)) {
            pushReminder(plan);
            saveRemindLog(plan);
        }
    }
}

4. 🚀 推送逻辑(伪代码)

private void pushReminder(MaintenancePlan plan) {
    String message = String.format("您的车辆即将需要保养,建议在 %s 或行驶至 %d 公里时进行保养。",
        plan.getNextMaintenanceTime().toLocalDate(),
        plan.getNextMaintenanceMileage());

    pushService.send(plan.getVehicleId(), message);
}

⚙️ 六、性能与高并发优化建议

✅ 1. 表设计优化

  • 所有主表使用 BIGINT 自增主键
  • 高频表如 vehicle_info 使用 单表分区或垂直拆分
  • 建立复合索引 (vehicle_id, last_update_time) 提高查询效率

✅ 2. 缓存策略

  • 使用 Redis 缓存车辆当前里程,减少数据库写压力
  • 缓存即将保养计划列表,调度任务只处理缓存数据

✅ 3. 异步解耦

  • 推送逻辑使用 Kafka、RocketMQ 等 异步消息队列解耦
  • 定时任务可使用 ElasticJob / XXL-Job 实现分布式调度

✅ 4. 批量处理

  • 推送提醒、日志入库等操作采用 批处理 提升吞吐能力

🧪 七、测试策略

  • 单元测试:覆盖 mileage 上报、保养计算逻辑
  • 集成测试:模拟高并发上报 + 推送提醒
  • 压力测试:使用 JMeter 模拟百万级车辆数据上报

🧵 八、总结与思考

从一个简单的“保养提醒”功能出发,背后其实是一个高并发 + 实时计算 + 定时调度 + 消息推送 + 数据一致性的完整系统。它融合了我们作为 Java 工程师需要掌握的多种知识点:

  • 领域建模
  • 服务解耦
  • 高性能设计
  • 系统扩展能力

“代码只是冰山一角,业务理解与系统设计才是立足之本”。