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

24 KiB
Raw Blame History

租赁业务全链路设计规格

概述

实现租赁业务全链路(合同 → 备车 → 交车 → 替换车 → 还车生产可用包括模块功能补全、跨模块状态联动、BPM 审批流程部署、共享验车模板系统。

架构决策

  • 方案选择:方案 C — 轻量模板表 + 记录表,模块内引用
  • 验车模板:共享表(asset_inspection_template + asset_inspection_record),放在 asset 模块
  • 流转机制:克隆 + 预填,各业务持有独立 inspection_record_id
  • 事件驱动Spring Event 解耦跨模块状态变更
  • BPM 集成:继承现有 BpmProcessInstanceStatusEventListener 模式
  • 表名前缀:统一使用 asset_ 前缀,与现有 asset_return_orderasset_delivery_order 等保持一致
  • BPM 字段名:统一使用 bpm_instance_id,与现有 ContractDOReturnOrderDO 保持一致

第一部分:共享验车模板系统

数据库表

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 方法

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_idcontract_codeenlarged_text_photo

关键变更

  • 移除 contract_id / contract_code — 备车与合同解耦,备车是运维常态
  • 移除 check_list JSON — 验车数据统一走 inspection_record
  • 新增 inspection_record_id — 关联共享验车记录

数据迁移:现有 asset_vehicle_prepare 表中 contract_idcontract_codecheck_listenlarged_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

// 替换车审批监听
@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 → InputNumbertext → 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)

实施顺序

  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 + 全链路测试