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>
24 KiB
租赁业务全链路设计规格
概述
实现租赁业务全链路(合同 → 备车 → 交车 → 替换车 → 还车)生产可用,包括:模块功能补全、跨模块状态联动、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 | 克隆来源记录ID(nullable) |
| + 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) | 图片URL,JSON数组 |
| + BaseDO |
克隆流转机制
备车完成(record status=已完成)
↓ 交车时,查找该 vehicleId 最近的备车 record
↓ InspectionRecordService.cloneRecord(sourceRecordId, DELIVERY, newSourceId)
↓ 生成新 record + 复制所有 item(result/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 方法
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_listJSON — 验车数据统一走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。若现有数据量小或尚未投产,可直接删除旧列无需迁移。
备车创建流程
- 用户选择车辆 → 创建备车单
- 系统根据
biz_type=1(备车)+ 车辆的vehicleType匹配验车模板 → 调用InspectionRecordService.createRecord()生成记录 - 备车人员逐项检查填写(前端动态渲染 inspection_record_item,按 category 分组,根据 input_type 渲染不同输入控件)
- 提交 → 调用
InspectionRecordService.completeRecord()→ 备车单 status=已完成
第三部分:交车模块增强
数据库变更
asset_delivery_order 表变更:
| 操作 | 字段 | 说明 |
|---|---|---|
| 新增 | inspection_record_id (bigint) | 关联验车记录 |
| 保留 | cost_list (JSON) | 费用列表保持原样,不做改动 |
关于 inspection_data:现有 JSON 字段标记为废弃,新数据不再写入。验车数据统一通过 inspection_record_id 关联。待确认无历史依赖后可 DROP COLUMN。
交车验车流程
- 创建交车单时,查找该车最近的备车
inspection_record - 调用
InspectionRecordService.cloneRecord()克隆为交车记录 - 预填备车检查结果,交车人员逐项确认/修改
- 提交验车 → 记录状态=已完成
操作列扩展
交车管理页面已完成的交车单,操作列新增:
| 按钮 | 条件 | 行为 |
|---|---|---|
| 还车 | 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)暂不纳入本期。
触发方式
- 从交车管理操作列触发:用户在交车单操作列点"还车"→ 弹窗选择车辆 →
POST /return-order/create-from-delivery→ 创建还车单壳子(含选中的车辆子记录)→ 跳转还车单详情页 - 永久替换审批通过自动创建:事件监听器创建,source_type=2,自动创建一条主记录 + 一条车辆子记录
验车流程
- 用户进入还车单详情 → 选择某辆车 → 点击"开始验车"
- 系统查找该车最近的交车 inspection_record → 克隆为还车 record
- 预填交车检查结果,用户逐项确认/修改
- 提交单车验车 → 该车辆子记录的
inspection_record_id更新 - 所有车辆验车完成 → 还车单 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:
// 替换车审批监听
@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 | 还车审批通过→车辆状态→可用 |
事件监听实现
@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
// 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)
实施顺序
- 共享验车模板 — SQL建表 → DO/Mapper → Service → Controller → 前端组件
- 备车精简 — 移除旧字段 → 对接 inspection_record → 前端改造
- 交车增强 — 新增 inspection_record_id → 废弃 inspection_data → 操作列扩展 → 前端改造
- 替换车模块 — 全套新建(DO → VO → Mapper → Service → BPM listener → Controller → 前端)
- 还车完善 — 新增字段 → 对接验车模板(子表级别) → 从交车/替换车触发 → BPM listener → 前端改造
- BPM 部署 — 创建 BPMN XML → 部署到引擎
- 事件驱动 — 事件类 → 监听器 → 车辆状态同步
- 端到端验证 —
mvn compile+pnpm run build:antd+ 全链路测试