Files
oneos-backend/docs/superpowers/specs/2026-03-13-rental-full-chain-design.md
kkfluous 072196aad4 docs: add rental full-chain design spec
Covers shared inspection template system, vehicle prepare simplification,
delivery enhancement, replacement vehicle module (new), return order
improvements, BPM approval flows, and event-driven cross-module linkage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 09:42:06 +08:00

588 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 租赁业务全链路设计规格
## 概述
实现租赁业务全链路(合同 → 备车 → 交车 → 替换车 → 还车生产可用包括模块功能补全、跨模块状态联动、BPM 审批流程部署、共享验车模板系统。
## 架构决策
- **方案选择**:方案 C — 轻量模板表 + 记录表,模块内引用
- **验车模板**:共享表(`asset_inspection_template` + `asset_inspection_record`),放在 asset 模块
- **流转机制**:克隆 + 预填,各业务持有独立 `inspection_record_id`
- **事件驱动**Spring Event 解耦跨模块状态变更
- **BPM 集成**:继承现有 `BpmProcessInstanceStatusEventListener` 模式
- **表名前缀**:统一使用 `asset_` 前缀,与现有 `asset_return_order``asset_delivery_order` 等保持一致
- **BPM 字段名**:统一使用 `bpm_instance_id`,与现有 `ContractDO``ReturnOrderDO` 保持一致
---
## 第一部分:共享验车模板系统
### 数据库表
#### `asset_inspection_template`(模板定义)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint | 主键 |
| code | varchar(50) | 模板编码,如 `TPL-BC-001` |
| name | varchar(100) | 模板名称,如"氢能车备车检查模板" |
| biz_type | tinyint | 适用业务1=备车 2=交车 3=还车(一个模板可适用多种业务) |
| vehicle_type | varchar(50) | 适用车辆类型(如"氢能车"、"电动车"nullable 表示通用) |
| status | tinyint | 0=禁用 1=启用 |
| remark | varchar(500) | 备注 |
| + BaseDO 字段 | | creator, create_time, updater, update_time, deleted, tenant_id |
**模板匹配规则**:创建验车记录时,先按 `biz_type` + `vehicle_type` 精确匹配,无结果则按 `biz_type` + `vehicle_type IS NULL` 匹配通用模板。
#### `asset_inspection_template_item`(模板检查项)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint | 主键 |
| template_id | bigint | 关联模板 |
| category | varchar(50) | 分类,如"制动系统"、"外观检查" |
| item_name | varchar(100) | 检查项名称 |
| item_code | varchar(50) | 检查项编码,如 `BRAKE-001` |
| input_type | varchar(20) | 输入类型:`checkbox`(合格/不合格)、`number`(数值)、`text`(文本) |
| sort | int | 排序 |
| required | tinyint | 是否必填0=否 1=是 |
| + BaseDO | | |
#### `asset_inspection_record`(验车记录实例)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint | 主键 |
| record_code | varchar(50) | 记录编码,如 `BC-V001-20260313-001` |
| template_id | bigint | 使用的模板 |
| source_type | tinyint | 来源1=备车 2=交车 3=还车 |
| source_id | bigint | 来源业务ID |
| vehicle_id | bigint | 车辆ID |
| inspector_name | varchar(50) | 检查人 |
| inspection_time | datetime | 检查时间 |
| status | tinyint | 0=待检查 1=检查中 2=已完成 |
| overall_result | tinyint | 总结果1=合格 2=不合格 |
| remark | varchar(500) | 总备注 |
| cloned_from_id | bigint | 克隆来源记录IDnullable |
| + BaseDO | | |
#### `asset_inspection_record_item`(逐项检查结果)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint | 主键 |
| record_id | bigint | 关联记录 |
| item_code | varchar(50) | 检查项编码(与模板 item 对应) |
| category | varchar(50) | 分类(冗余,便于展示) |
| item_name | varchar(100) | 检查项名称(冗余) |
| input_type | varchar(20) | 输入类型(冗余,便于前端渲染) |
| result | tinyint | 1=合格 2=不合格 3=不适用 |
| value | varchar(200) | 数值/文本类型的输入值 |
| remark | varchar(500) | 该项备注 |
| image_urls | varchar(2000) | 图片URLJSON数组 |
| + BaseDO | | |
### 克隆流转机制
```
备车完成record status=已完成)
↓ 交车时,查找该 vehicleId 最近的备车 record
↓ InspectionRecordService.cloneRecord(sourceRecordId, DELIVERY, newSourceId)
↓ 生成新 record + 复制所有 itemresult/value 保留,可修改)
↓ cloned_from_id = 源 record.id
交车完成
↓ 还车时同理克隆交车 record
```
### 后端结构
```
asset-server/
├── dal/dataobject/inspection/
│ ├── InspectionTemplateDO.java
│ ├── InspectionTemplateItemDO.java
│ ├── InspectionRecordDO.java
│ └── InspectionRecordItemDO.java
├── dal/mysql/inspection/
│ ├── InspectionTemplateMapper.java
│ ├── InspectionTemplateItemMapper.java
│ ├── InspectionRecordMapper.java
│ └── InspectionRecordItemMapper.java
├── service/inspection/
│ ├── InspectionTemplateService.java
│ ├── InspectionTemplateServiceImpl.java
│ ├── InspectionRecordService.java
│ └── InspectionRecordServiceImpl.java
├── controller/admin/inspection/
│ ├── InspectionTemplateController.java (模板 CRUD)
│ └── InspectionRecordController.java (记录查询)
└── convert/inspection/
└── InspectionConvert.java
```
### 核心 Service 方法
```java
public interface InspectionRecordService {
// 根据模板创建空白验车记录
Long createRecord(Long templateId, Integer sourceType, Long sourceId, Long vehicleId);
// 克隆已有记录(跨模块流转)
Long cloneRecord(Long sourceRecordId, Integer newSourceType, Long newSourceId);
// 更新检查项结果
void updateRecordItem(Long recordItemId, Integer result, String value, String remark, String imageUrls);
// 完成验车
void completeRecord(Long recordId, String inspectorName);
// 查询记录详情(含所有 item
InspectionRecordDetailVO getRecordDetail(Long recordId);
// 查找车辆最近的某类型验车记录
InspectionRecordDO getLatestRecord(Long vehicleId, Integer sourceType);
}
```
---
## 第二部分:备车模块精简
### 现状
`VehiclePrepareDO` 有 ~30 个字段,其中 `checkList`(JSON) 存储检查数据。需精简为纯业务字段 + `inspection_record_id` 关联。
### 精简后 `asset_vehicle_prepare` 字段
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint | 主键 |
| vehicle_id | bigint | 车辆ID |
| plate_no | varchar(20) | 车牌号(冗余) |
| vin | varchar(50) | VIN码冗余 |
| vehicle_model_id | bigint | 车型ID |
| brand | varchar(50) | 品牌(冗余) |
| model | varchar(50) | 型号(冗余) |
| vehicle_type | varchar(50) | 车辆类型(冗余) |
| parking_lot | varchar(100) | 停车场 |
| preparation_type | varchar(50) | 备车类型 |
| mileage | int | 里程数 |
| hydrogen_remaining | decimal(10,2) | 剩余氢量 |
| hydrogen_unit | varchar(10) | 氢量单位 |
| battery_remaining | decimal(10,2) | 剩余电量 |
| has_body_ad | tinyint | 是否有车身广告 |
| body_ad_photos | varchar(2000) | 车身广告照片JSON |
| has_tail_lift | tinyint | 是否有尾板 |
| spare_tire_depth | decimal(5,2) | 备胎深度 |
| spare_tire_photo | varchar(500) | 备胎照片 |
| trailer_plate_no | varchar(20) | 挂车车牌 |
| defect_photos | varchar(2000) | 缺陷照片JSON |
| inspection_record_id | bigint | **新增:关联验车记录** |
| remark | varchar(500) | 备注 |
| status | tinyint | 0=待检查 1=已完成 |
| complete_time | datetime | 完成时间 |
| + BaseDO | | |
**移除字段**`check_list`(JSON)、`contract_id``contract_code``enlarged_text_photo`
**关键变更**
- 移除 `contract_id` / `contract_code` — 备车与合同解耦,备车是运维常态
- 移除 `check_list` JSON — 验车数据统一走 `inspection_record`
- 新增 `inspection_record_id` — 关联共享验车记录
**数据迁移**:现有 `asset_vehicle_prepare` 表中 `contract_id``contract_code``check_list``enlarged_text_photo` 字段标记为废弃ALTER TABLE DROP COLUMN现有数据通过脚本迁移 `check_list` JSON 到 `asset_inspection_record` + `asset_inspection_record_item`。若现有数据量小或尚未投产,可直接删除旧列无需迁移。
### 备车创建流程
1. 用户选择车辆 → 创建备车单
2. 系统根据 `biz_type=1(备车)` + 车辆的 `vehicleType` 匹配验车模板 → 调用 `InspectionRecordService.createRecord()` 生成记录
3. 备车人员逐项检查填写(前端动态渲染 inspection_record_item按 category 分组,根据 input_type 渲染不同输入控件)
4. 提交 → 调用 `InspectionRecordService.completeRecord()` → 备车单 status=已完成
---
## 第三部分:交车模块增强
### 数据库变更
`asset_delivery_order` 表变更:
| 操作 | 字段 | 说明 |
|------|------|------|
| 新增 | inspection_record_id (bigint) | 关联验车记录 |
| 保留 | cost_list (JSON) | 费用列表保持原样,不做改动 |
**关于 `inspection_data`**:现有 JSON 字段标记为废弃,新数据不再写入。验车数据统一通过 `inspection_record_id` 关联。待确认无历史依赖后可 DROP COLUMN。
### 交车验车流程
1. 创建交车单时,查找该车最近的备车 `inspection_record`
2. 调用 `InspectionRecordService.cloneRecord()` 克隆为交车记录
3. 预填备车检查结果,交车人员逐项确认/修改
4. 提交验车 → 记录状态=已完成
### 操作列扩展
交车管理页面已完成的交车单,操作列新增:
| 按钮 | 条件 | 行为 |
|------|------|------|
| 还车 | status=已完成 | 弹窗选择该交车单关联的车辆(可多选),调用 `POST /return-order/create-from-delivery`,跳转还车单详情 |
| 替换车 | status=已完成 | 弹窗选择该交车单关联的某辆车,路由跳转替换车新建页,携带 `contractId`, `vehicleId`, `deliveryOrderId` |
---
## 第四部分:替换车模块(从零构建)
### 数据库表
#### `asset_vehicle_replacement`
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint | 主键 |
| replacement_code | varchar(50) | 替换单编码,如 `TH-20260313-001` |
| replacement_type | tinyint | 1=临时替换 2=永久替换 |
| contract_id | bigint | 合同ID |
| contract_code | varchar(50) | 合同编号(冗余) |
| customer_id | bigint | 客户ID |
| customer_name | varchar(100) | 客户名称(冗余) |
| delivery_order_id | bigint | 来源交车单ID从交车页面触发时记录 |
| original_vehicle_id | bigint | 原车ID |
| original_plate_no | varchar(20) | 原车车牌号(冗余) |
| original_vin | varchar(50) | 原车VIN冗余 |
| new_vehicle_id | bigint | 新车ID |
| new_plate_no | varchar(20) | 新车车牌号(冗余) |
| new_vin | varchar(50) | 新车VIN冗余 |
| replacement_reason | varchar(500) | 替换原因 |
| expected_date | date | 预计替换日期 |
| actual_date | date | 实际替换日期 |
| return_date | date | 临时替换预计归还日期 |
| actual_return_date | date | 临时替换实际归还日期 |
| status | tinyint | 业务状态(见状态流转) |
| approval_status | tinyint | 审批状态 |
| bpm_instance_id | varchar(64) | BPM流程实例ID |
| remark | varchar(500) | 备注 |
| + BaseDO | | |
**说明**:冗余字段使用 `plate_no`(车牌号)而非 `vehicleCode`,因为现有 DO 中车辆标识统一使用 `plateNo` + `vin`
### 状态流转
```
草稿(0) → 审批中(1) → 审批通过(2) → 执行中(3) → 已完成(4)
→ 审批驳回(5)
→ 已撤回(6)
```
- 审批通过 + 永久替换 → 自动创建原车还车单壳子,状态进入"执行中"
- 审批通过 + 临时替换 → 状态进入"执行中"
- 执行中 + 临时替换 → 确认换回 → "已完成"
- 执行中 + 永久替换 → 原车还车单完成后 → "已完成"
### 从交车页面快捷触发
交车管理操作列"替换车"按钮 → 路由跳转:
```
/asset/vehicle-replacement/create?contractId=xx&vehicleId=xx&deliveryOrderId=xx
```
替换车表单自动填充合同信息、原车信息(只读),用户只需选择新车、填写原因。
### 后端结构
```
asset-server/
├── dal/dataobject/replacement/
│ └── VehicleReplacementDO.java
├── dal/mysql/replacement/
│ └── VehicleReplacementMapper.java
├── service/replacement/
│ ├── VehicleReplacementService.java
│ ├── VehicleReplacementServiceImpl.java
│ └── listener/
│ └── ReplacementBpmListener.java
├── controller/admin/replacement/
│ ├── VehicleReplacementController.java
│ └── vo/
│ ├── VehicleReplacementSaveReqVO.java
│ ├── VehicleReplacementRespVO.java
│ └── VehicleReplacementPageReqVO.java
└── convert/replacement/
└── VehicleReplacementConvert.java
```
### 接口设计
| 端点 | 方法 | 说明 |
|------|------|------|
| POST /asset/vehicle-replacement/create | create | 创建替换申请 |
| PUT /asset/vehicle-replacement/update | update | 更新(草稿/驳回) |
| DELETE /asset/vehicle-replacement/delete | delete | 删除(草稿) |
| GET /asset/vehicle-replacement/get | get | 获取详情 |
| GET /asset/vehicle-replacement/page | page | 分页查询 |
| POST /asset/vehicle-replacement/submit | submit | 提交BPM审批 |
| POST /asset/vehicle-replacement/withdraw | withdraw | 撤回审批 |
| POST /asset/vehicle-replacement/confirm-return | confirmReturn | 临时替换确认换回 |
---
## 第五部分:还车模块完善
### 现有架构
还车模块采用主子表结构:
- **`asset_return_order`**(主表):还车单基本信息、合同/客户关联、审批状态
- **`asset_return_order_vehicle`**(子表):每辆车的还车详情,含里程、氢量差、各项费用、`checkList`(JSON)
### 数据库变更
#### `asset_return_order`(主表)新增字段
| 字段 | 操作 | 类型 | 说明 |
|------|------|------|------|
| source_type | 新增 | tinyint | 来源1=手动(从交车触发) 2=替换车触发 |
| source_id | 新增 | bigint | 来源业务ID替换车申请ID 或 交车单ID |
| delivery_order_id | 新增 | bigint | 关联交车单ID追溯来源 |
#### `asset_return_order_vehicle`(子表)变更
| 字段 | 操作 | 类型 | 说明 |
|------|------|------|------|
| inspection_record_id | 新增 | bigint | 关联验车记录(每辆车独立验车记录) |
| check_list | 废弃 | JSON | 不再写入新数据,验车统一走 inspection_record |
**关键设计**`inspection_record_id` 放在子表 `asset_return_order_vehicle` 上(而非主表),因为一个还车单可包含多辆车,每辆车有独立的验车记录。
### 状态流转
还车单状态(`status` 字段)保持现有值域不变:
```
待验车(0) → 验车完成(1) → 已结算(2)
```
审批通过 `approval_status` 字段(已有)独立管理:
```
待审批(0) → 审批中(1) → 审批通过(2) → 审批驳回(3) → 已撤回(4)
```
**完整流程**:待验车 → 逐车验车 → 全部验车完成(status=1) → 提交审批(approvalStatus=1) → 审批通过(approvalStatus=2)
费用结算status=2暂不纳入本期。
### 触发方式
1. **从交车管理操作列触发**:用户在交车单操作列点"还车"→ 弹窗选择车辆 → `POST /return-order/create-from-delivery` → 创建还车单壳子(含选中的车辆子记录)→ 跳转还车单详情页
2. **永久替换审批通过自动创建**事件监听器创建source_type=2自动创建一条主记录 + 一条车辆子记录
### 验车流程
1. 用户进入还车单详情 → 选择某辆车 → 点击"开始验车"
2. 系统查找该车最近的交车 inspection_record → 克隆为还车 record
3. 预填交车检查结果,用户逐项确认/修改
4. 提交单车验车 → 该车辆子记录的 `inspection_record_id` 更新
5. 所有车辆验车完成 → 还车单 status=1(验车完成) → 可提交审批
### 接口设计
| 端点 | 方法 | 说明 |
|------|------|------|
| POST /asset/return-order/create | create | 手动创建 |
| POST /asset/return-order/create-from-delivery | createFromDelivery | 从交车记录创建壳子参数deliveryOrderId + vehicleIds[] |
| PUT /asset/return-order/update | update | 更新 |
| DELETE /asset/return-order/delete | delete | 删除(待验车) |
| GET /asset/return-order/get | get | 获取详情(含车辆子表) |
| GET /asset/return-order/page | page | 分页查询 |
| POST /asset/return-order/start-inspection | startInspection | 开始单车验车参数returnOrderVehicleId克隆交车record |
| POST /asset/return-order/complete-inspection | completeInspection | 完成单车验车 |
| POST /asset/return-order/submit | submit | 提交BPM审批验车完成后 |
| POST /asset/return-order/withdraw | withdraw | 撤回审批 |
---
## 第六部分BPM 审批流程
### 需要审批的业务
| 业务 | 流程Key | 触发时机 | 审批通过后动作 |
|------|---------|---------|---------------|
| 租赁合同 | `asset_contract` | 已有,无需新建 | 合同状态→已审批 |
| 替换车申请 | `asset_vehicle_replacement` | 替换申请提交 | 临时→执行中;永久→执行中+创建还车单壳子 |
| 还车 | `asset_return_order` | 验车完成后提交 | 还车单 approvalStatus→审批通过 |
### BPM 集成模式
继承现有 `BpmProcessInstanceStatusEventListener`
```java
// 替换车审批监听
@Component
public class ReplacementBpmListener extends BpmProcessInstanceStatusEventListener {
public static final String PROCESS_KEY = "asset_vehicle_replacement";
@Override
protected String getProcessDefinitionKey() { return PROCESS_KEY; }
@Override
protected void onEvent(BpmProcessInstanceStatusEvent event) {
replacementService.updateApprovalStatus(
Long.parseLong(event.getBusinessKey()), event.getStatus());
}
}
// 还车审批监听
@Component
public class ReturnOrderBpmListener extends BpmProcessInstanceStatusEventListener {
public static final String PROCESS_KEY = "asset_return_order";
@Override
protected String getProcessDefinitionKey() { return PROCESS_KEY; }
@Override
protected void onEvent(BpmProcessInstanceStatusEvent event) {
returnOrderService.updateApprovalStatus(
Long.parseLong(event.getBusinessKey()), event.getStatus());
}
}
```
### BPMN XML 文件
新增两个流程定义,放在 `resources/processes/`
**`asset_vehicle_replacement.bpmn20.xml`** — 带网关:发起 → 部门主管 → (永久替换? → 总经理审批 → 完成
**`asset_return_order.bpmn20.xml`** — 简单串行:发起 → 部门主管审批 → 完成
命名规则与现有 `asset_contract.bpmn20.xml` 一致。
---
## 第七部分:事件驱动 & 跨模块联动
### 事件清单
| 事件类 | 发布者 | 监听者 | 动作 |
|--------|--------|--------|------|
| `DeliveryCompletedEvent` | DeliveryOrderService | VehicleStatusListener | 车辆状态→已交付 |
| `ReplacementApprovedEvent` | ReplacementBpmListener | ReturnOrderEventListener | 永久替换→创建还车单壳子 |
| `ReplacementReturnConfirmedEvent` | ReplacementService | VehicleStatusListener | 临时替换换回→恢复原车状态 |
| `ReturnApprovedEvent` | ReturnOrderBpmListener | VehicleStatusListener | 还车审批通过→车辆状态→可用 |
### 事件监听实现
```java
@Component
public class VehicleStatusEventListener {
@TransactionalEventListener
public void onDeliveryCompleted(DeliveryCompletedEvent event) {
vehicleService.updateStatus(event.getVehicleId(), VehicleStatus.DELIVERED);
}
@TransactionalEventListener
public void onReturnApproved(ReturnApprovedEvent event) {
// 还车单有多辆车,逐辆更新
for (Long vehicleId : event.getVehicleIds()) {
vehicleService.updateStatus(vehicleId, VehicleStatus.AVAILABLE);
}
}
@TransactionalEventListener
public void onReplacementReturnConfirmed(ReplacementReturnConfirmedEvent event) {
vehicleService.updateStatus(event.getOriginalVehicleId(), VehicleStatus.AVAILABLE);
}
}
@Component
public class ReturnOrderEventListener {
// 使用 @EventListener 而非 @TransactionalEventListener
// 确保还车单创建在同一事务中,避免审批已提交但还车单创建失败的不一致
@EventListener
public void onReplacementApproved(ReplacementApprovedEvent event) {
if (event.getReplacementType() == ReplacementType.PERMANENT) {
returnOrderService.createFromReplacement(
event.getReplacementId(),
event.getContractId(),
event.getOriginalVehicleId()
);
}
}
}
```
**关于事务安全**`ReturnOrderEventListener.onReplacementApproved()` 使用 `@EventListener`(非 `@TransactionalEventListener`),确保还车单创建与替换车状态更新在同一事务内完成。`@TransactionalEventListener` 默认 `AFTER_COMMIT`,会导致还车单创建在事务外执行,失败时无法回滚。
---
## 前端变更概要
### 共享验车组件
创建通用验车表单组件 `InspectionForm.vue`
- 按 category 分组展示检查项
- 根据 `input_type` 渲染不同控件:`checkbox` → Radio(合格/不合格/不适用)`number` → InputNumber`text` → Input
- 每项支持:备注 + 图片上传
- 只读模式(查看历史记录)和编辑模式
- 被备车/交车/还车表单复用
### 替换车前端
新建 `views/asset/vehicle-replacement/`
- `index.vue` — 列表页(分页、筛选、操作列)
- `modules/form.vue` — 表单create/edit/view 三模式)
- `data.ts` — 搜索/列/常量定义
### 备车前端改造
- 移除硬编码 `checkList` 表单
- 改用 `InspectionForm.vue` 组件动态渲染
### 交车/还车前端改造
- 交车表单中验车区域改用 `InspectionForm.vue`
- 交车列表操作列增加"还车"和"替换车"按钮
- 还车表单中每辆车增加"开始验车"按钮 + `InspectionForm.vue`
### 新增 API
```typescript
// inspection.ts
export function getInspectionRecord(id: number)
export function updateInspectionRecordItem(data: {...})
export function completeInspection(recordId: number, inspectorName: string)
// vehicle-replacement.ts
export function createReplacement(data: VehicleReplacementSaveReqVO)
export function updateReplacement(data: VehicleReplacementSaveReqVO)
export function deleteReplacement(id: number)
export function getReplacement(id: number)
export function getReplacementPage(params: VehicleReplacementPageReqVO)
export function submitReplacement(id: number)
export function withdrawReplacement(id: number)
export function confirmReplacementReturn(id: number)
// return-order.ts (补充)
export function createFromDelivery(deliveryOrderId: number, vehicleIds: number[])
export function startVehicleInspection(returnOrderVehicleId: number)
export function completeVehicleInspection(returnOrderVehicleId: number)
export function submitReturnOrder(id: number)
export function withdrawReturnOrder(id: number)
```
---
## 实施顺序
1. **共享验车模板** — SQL建表 → DO/Mapper → Service → Controller → 前端组件
2. **备车精简** — 移除旧字段 → 对接 inspection_record → 前端改造
3. **交车增强** — 新增 inspection_record_id → 废弃 inspection_data → 操作列扩展 → 前端改造
4. **替换车模块** — 全套新建DO → VO → Mapper → Service → BPM listener → Controller → 前端)
5. **还车完善** — 新增字段 → 对接验车模板(子表级别) → 从交车/替换车触发 → BPM listener → 前端改造
6. **BPM 部署** — 创建 BPMN XML → 部署到引擎
7. **事件驱动** — 事件类 → 监听器 → 车辆状态同步
8. **端到端验证**`mvn compile` + `pnpm run build:antd` + 全链路测试