# 车辆租赁合同模块实施计划 ## Context(背景) ### 为什么需要这个变更 车辆租赁业务是 OneOS 系统的核心业务模块,需要管理与客户签订的车辆租赁合同,包括合同信息、车辆订单、服务项目、被授权人、审批流程等。该模块需要与 BPM 工作流引擎深度集成,实现合同的审批流转。 ### 问题或需求 1. **业务需求**:管理车辆租赁合同的全生命周期(创建→审批→执行→到期→续签/终止) 2. **审批流程**:4级审批(业务部主管→事业部主管→财务部→法务部) 3. **复杂关联**:合同关联客户、车辆、服务项目、被授权人、附件等多个实体 4. **特殊业务**:续签合同、转正式合同、变更为三方合同、新增车辆等 5. **状态管理**:审批状态 × 合同状态的复杂状态机 ### 预期结果 构建一个完整的车辆租赁合同管理模块,支持合同的 CRUD、审批流程、特殊业务操作,并与现有的客户管理、车辆管理、BPM 工作流模块无缝集成。 --- ## 架构设计 ### 整体架构 ``` Controller (REST API) ↓ Service (业务逻辑) ↓ ├── Mapper (数据访问) ├── BpmProcessInstanceApi (流程启动) └── FileApi (附件上传) ↓ BpmProcessInstanceStatusEventListener (流程状态监听) ↓ Service (更新合同状态) ``` ### 模块结构 ``` yudao-module-asset/yudao-module-asset-server/ ├── controller/admin/contract/ │ └── ContractController.java ├── service/contract/ │ ├── ContractService.java │ ├── ContractServiceImpl.java │ └── listener/ │ └── ContractBpmListener.java ├── dal/ │ ├── dataobject/contract/ │ │ ├── ContractDO.java │ │ ├── ContractVehicleDO.java │ │ ├── ContractVehicleServiceDO.java │ │ ├── ContractAuthorizedDO.java │ │ ├── ContractAttachmentDO.java │ │ └── ContractChangeHistoryDO.java │ └── mysql/contract/ │ ├── ContractMapper.java │ ├── ContractVehicleMapper.java │ ├── ContractVehicleServiceMapper.java │ ├── ContractAuthorizedMapper.java │ ├── ContractAttachmentMapper.java │ └── ContractChangeHistoryMapper.java ├── controller/admin/contract/vo/ │ ├── ContractBaseVO.java │ ├── ContractSaveReqVO.java │ ├── ContractRespVO.java │ ├── ContractPageReqVO.java │ ├── ContractDetailRespVO.java │ ├── ContractVehicleVO.java │ ├── ContractVehicleServiceVO.java │ └── ContractAuthorizedVO.java ├── convert/contract/ │ └── ContractConvert.java └── enums/ ├── ContractTypeEnum.java ├── ContractApprovalStatusEnum.java └── ContractStatusEnum.java ``` --- ## 数据库表设计 ### 1. asset_contract(合同主表) ```sql CREATE TABLE `asset_contract` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', -- 合同基本信息 `contract_code` VARCHAR(50) NOT NULL COMMENT '合同编码', `contract_type` TINYINT NOT NULL DEFAULT 1 COMMENT '合同类型(1=试用 2=正式)', `project_name` VARCHAR(200) NOT NULL COMMENT '项目名称', `start_date` DATE NOT NULL COMMENT '生效日期', `end_date` DATE NOT NULL COMMENT '结束日期', `payment_method` VARCHAR(50) COMMENT '付款方式', `payment_cycle` VARCHAR(50) COMMENT '付款周期', `signing_company` VARCHAR(200) COMMENT '签约公司(乙方)', `delivery_province` VARCHAR(50) COMMENT '交车省份', `delivery_city` VARCHAR(50) COMMENT '交车城市', `delivery_location` VARCHAR(255) COMMENT '交车地点', `remark` VARCHAR(1000) COMMENT '备注', -- 甲方客户信息(关联 asset_customer) `customer_id` BIGINT NOT NULL COMMENT '客户ID', `customer_name` VARCHAR(200) NOT NULL COMMENT '客户名称(冗余)', -- 丙方客户信息(三方合同,可选) `third_party_enabled` BIT(1) NOT NULL DEFAULT b'0' COMMENT '是否三方合同', `third_party_customer_id` BIGINT COMMENT '丙方客户ID', `third_party_name` VARCHAR(200) COMMENT '丙方名称', -- 业务信息 `business_dept_id` BIGINT NOT NULL COMMENT '业务部门ID', `business_manager_id` BIGINT NOT NULL COMMENT '业务负责人ID', -- 审批状态 `approval_status` TINYINT NOT NULL DEFAULT 0 COMMENT '审批状态(0=草稿 1=审批中 2=审批通过 3=审批拒绝 4=已撤回)', `bpm_instance_id` VARCHAR(64) COMMENT 'BPM流程实例ID', -- 合同状态 `contract_status` TINYINT NOT NULL DEFAULT 0 COMMENT '合同状态(0=草稿 1=待生效 2=进行中 3=已到期 4=已终止 5=已续签)', `effective_time` DATETIME COMMENT '实际生效时间', `terminate_time` DATETIME COMMENT '终止时间', `terminate_reason` VARCHAR(500) COMMENT '终止原因', -- 续签信息 `renewed_contract_id` BIGINT COMMENT '续签后的新合同ID', `original_contract_id` BIGINT COMMENT '原合同ID(如果是续签合同)', -- 系统字段 `creator` VARCHAR(64) DEFAULT '' COMMENT '创建者', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updater` VARCHAR(64) DEFAULT '' COMMENT '更新者', `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`), UNIQUE KEY `uk_contract_code` (`contract_code`, `deleted`), INDEX `idx_customer_id` (`customer_id`), INDEX `idx_approval_status` (`approval_status`), INDEX `idx_contract_status` (`contract_status`), INDEX `idx_business_dept` (`business_dept_id`), INDEX `idx_start_date` (`start_date`), INDEX `idx_end_date` (`end_date`), INDEX `idx_create_time` (`create_time`), INDEX `idx_tenant_id` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='车辆租赁合同表'; ``` ### 2. asset_contract_vehicle(车辆租赁订单) ```sql CREATE TABLE `asset_contract_vehicle` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', `contract_id` BIGINT NOT NULL COMMENT '合同ID', `vehicle_id` BIGINT COMMENT '车辆ID(关联 asset_vehicle_base)', `brand` VARCHAR(100) NOT NULL COMMENT '品牌', `model` VARCHAR(100) NOT NULL COMMENT '型号', `plate_no` VARCHAR(20) COMMENT '车牌号', `vin` VARCHAR(50) COMMENT 'VIN码', `month_rent` DECIMAL(10,2) NOT NULL COMMENT '月租金(元)', `deposit` DECIMAL(10,2) NOT NULL COMMENT '保证金(元)', `vehicle_status` TINYINT DEFAULT 0 COMMENT '车辆状态(0=待交车 1=已交车 2=已退车)', `actual_delivery_time` DATETIME COMMENT '实际交车时间', `delivery_person` VARCHAR(50) COMMENT '交车人', `remark` VARCHAR(500) COMMENT '备注', `creator` VARCHAR(64) DEFAULT '' COMMENT '创建者', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updater` VARCHAR(64) DEFAULT '' COMMENT '更新者', `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`), INDEX `idx_contract_id` (`contract_id`), INDEX `idx_vehicle_id` (`vehicle_id`), INDEX `idx_plate_no` (`plate_no`), INDEX `idx_tenant_id` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='合同车辆租赁订单表'; ``` ### 3. asset_contract_vehicle_service(服务项目) ```sql CREATE TABLE `asset_contract_vehicle_service` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', `contract_vehicle_id` BIGINT NOT NULL COMMENT '合同车辆ID', `service_name` VARCHAR(100) NOT NULL COMMENT '服务项目名称', `service_fee` DECIMAL(10,2) NOT NULL COMMENT '服务费用(元)', `effective_date` DATE COMMENT '生效日期', `creator` VARCHAR(64) DEFAULT '' COMMENT '创建者', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updater` VARCHAR(64) DEFAULT '' COMMENT '更新者', `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`), INDEX `idx_contract_vehicle_id` (`contract_vehicle_id`), INDEX `idx_tenant_id` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='合同车辆服务项目表'; ``` ### 4. asset_contract_authorized(被授权人) ```sql CREATE TABLE `asset_contract_authorized` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', `contract_id` BIGINT NOT NULL COMMENT '合同ID', `name` VARCHAR(50) NOT NULL COMMENT '姓名', `phone` VARCHAR(20) NOT NULL COMMENT '电话', `id_card` VARCHAR(18) NOT NULL COMMENT '身份证号', `creator` VARCHAR(64) DEFAULT '' COMMENT '创建者', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updater` VARCHAR(64) DEFAULT '' COMMENT '更新者', `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`), INDEX `idx_contract_id` (`contract_id`), INDEX `idx_tenant_id` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='合同被授权人表'; ``` ### 5. asset_contract_attachment(合同附件) ```sql CREATE TABLE `asset_contract_attachment` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', `contract_id` BIGINT NOT NULL COMMENT '合同ID', `attachment_type` TINYINT NOT NULL COMMENT '附件类型(1=合同原件 2=盖章合同)', `file_id` BIGINT NOT NULL COMMENT '文件ID(关联 infra_file)', `file_name` VARCHAR(255) NOT NULL COMMENT '文件名称', `file_url` VARCHAR(500) NOT NULL COMMENT '文件URL', `file_size` BIGINT COMMENT '文件大小(字节)', `upload_time` DATETIME NOT NULL COMMENT '上传时间', `uploader` VARCHAR(64) COMMENT '上传人', `creator` VARCHAR(64) DEFAULT '' COMMENT '创建者', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updater` VARCHAR(64) DEFAULT '' COMMENT '更新者', `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`), INDEX `idx_contract_id` (`contract_id`), INDEX `idx_file_id` (`file_id`), INDEX `idx_tenant_id` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='合同附件表'; ``` ### 6. asset_contract_change_history(变更历史) ```sql CREATE TABLE `asset_contract_change_history` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', `contract_id` BIGINT NOT NULL COMMENT '合同ID', `change_type` VARCHAR(50) NOT NULL COMMENT '变更类型(保存/提交审批/审批通过/审批驳回/撤回/终止/续签/转正式/变更三方/新增车辆等)', `change_content` VARCHAR(1000) COMMENT '变更内容', `operator` VARCHAR(64) NOT NULL COMMENT '操作人', `operate_time` DATETIME NOT NULL COMMENT '操作时间', `creator` VARCHAR(64) DEFAULT '' COMMENT '创建者', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `deleted` BIT(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`), INDEX `idx_contract_id` (`contract_id`), INDEX `idx_operate_time` (`operate_time`), INDEX `idx_tenant_id` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='合同变更历史表'; ``` --- ## BPM 集成方案 ### 流程定义 - **流程定义 Key**: `rental_contract_approval` - **流程名称**: 车辆租赁合同审批 - **审批节点**: 1. 业务部主管审批 2. 事业部主管审批 3. 财务部审批 4. 法务部审批(上传盖章合同) ### 流程变量 ```java Map variables = new HashMap<>(); variables.put("contractId", contractId); variables.put("contractCode", contractCode); variables.put("contractType", contractType); variables.put("customerName", customerName); variables.put("projectName", projectName); variables.put("totalAmount", totalAmount); variables.put("businessDeptId", businessDeptId); variables.put("businessManagerId", businessManagerId); ``` ### 候选人策略 - **业务部主管**: 根据 `businessDeptId` 动态分配(部门主管角色) - **事业部主管**: 固定角色 `BUSINESS_DIRECTOR` - **财务部**: 财务部角色 `FINANCE_DEPT` - **法务部**: 法务部角色 `LEGAL_DEPT` ### 事件监听器 ```java @Component public class ContractBpmListener { @Resource private ContractService contractService; @EventListener public void onProcessInstanceStatusChange(BpmProcessInstanceStatusEvent event) { // 只处理租赁合同审批流程 if (!"rental_contract_approval".equals(event.getProcessDefinitionKey())) { return; } Long contractId = Long.parseLong(event.getBusinessKey()); switch (event.getStatus()) { case APPROVE: // 审批通过 contractService.handleApprovalApproved(contractId, event.getResult()); break; case REJECT: // 审批驳回 contractService.handleApprovalRejected(contractId, event.getResult()); break; case CANCEL: // 取消/撤回 contractService.handleApprovalCancelled(contractId); break; } } } ``` --- ## API 接口设计 ### 基础 CRUD 接口 ``` POST /asset/contract/create - 创建合同 PUT /asset/contract/update - 更新合同 DELETE /asset/contract/delete - 删除合同 GET /asset/contract/get - 获取合同详情 GET /asset/contract/page - 分页查询合同 ``` ### 审批相关接口 ``` POST /asset/contract/submit - 提交审批 POST /asset/contract/withdraw - 撤回合同 GET /asset/contract/approval-history - 审批历史 ``` ### 特殊业务接口 ``` POST /asset/contract/terminate - 终止合同 POST /asset/contract/renew - 续签合同 POST /asset/contract/convert-formal - 转正式合同 POST /asset/contract/convert-tripartite - 变更为三方合同 POST /asset/contract/add-vehicle - 新增车辆 ``` ### 查询接口 ``` GET /asset/contract/change-history - 变更历史 GET /asset/contract/vehicle/list - 合同车辆列表 GET /asset/contract/authorized/list - 被授权人列表 GET /asset/contract/attachment/list - 合同附件列表 ``` --- ## 状态机设计 ### 审批状态(approval_status) ``` 0 - 草稿(DRAFT) 1 - 审批中(APPROVING) 2 - 审批通过(APPROVED) 3 - 审批拒绝(REJECTED) 4 - 已撤回(WITHDRAWN) ``` ### 合同状态(contract_status) ``` 0 - 草稿(DRAFT) 1 - 待生效(PENDING) 2 - 进行中(IN_PROGRESS) 3 - 已到期(EXPIRED) 4 - 已终止(TERMINATED) 5 - 已续签(RENEWED) ``` ### 状态流转规则 **审批状态流转:** ``` 草稿 → 审批中 → 审批通过 ↓ ↓ 已撤回 审批拒绝 ``` **合同状态流转:** ``` 草稿 → 待生效 → 进行中 → 已到期 → 已续签 ↓ 已终止 ``` ### 状态与操作关系矩阵 | 操作 | 草稿 | 审批中 | 审批通过 | 审批拒绝 | 已撤回 | |------|------|--------|----------|----------|--------| | 编辑 | ✓ | ✗ | ✗ | ✓ | ✓ | | 删除 | ✓ | ✗ | ✗ | ✓ | ✓ | | 提交审批 | ✓ | ✗ | ✗ | ✓ | ✓ | | 撤回 | ✗ | ✓ | ✗ | ✗ | ✗ | | 终止 | ✗ | ✗ | ✓ | ✗ | ✗ | | 续签 | ✗ | ✗ | ✓ | ✗ | ✗ | | 转正式 | ✗ | ✗ | ✓ | ✗ | ✗ | | 变更三方 | ✗ | ✗ | ✓ | ✗ | ✗ | | 新增车辆 | ✗ | ✗ | ✓ | ✗ | ✗ | --- ## 实现分阶段计划 ### 阶段1:基础合同管理(不含审批) **目标**: 实现合同的基础 CRUD 功能 **关键文件**: 1. 数据库表创建脚本 2. DO 类(6个) 3. Mapper 接口(6个) 4. VO 类(8个) 5. Convert 接口 6. Service 接口和实现 7. Controller 8. 枚举类(3个) **功能清单**: - ✓ 创建合同(包含车辆订单、服务项目、被授权人) - ✓ 更新合同 - ✓ 删除合同 - ✓ 查询合同详情 - ✓ 分页查询合同列表 - ✓ 查询变更历史 **验证方式**: - 使用 Postman 测试所有 CRUD 接口 - 验证数据库数据正确性 - 验证多表关联查询 ### 阶段2:BPM 审批集成 **目标**: 集成 Flowable 工作流引擎,实现审批流程 **关键文件**: 1. BPM 流程定义文件(BPMN 2.0 XML) 2. ContractBpmListener.java(事件监听器) 3. 审批相关 Service 方法 4. 审批相关 Controller 接口 **功能清单**: - ✓ 提交审批(启动 BPM 流程) - ✓ 撤回审批 - ✓ 监听审批结果(通过/驳回) - ✓ 更新合同审批状态 - ✓ 查询审批历史 **验证方式**: - 创建合同并提交审批 - 在 BPM 管理界面审批 - 验证合同状态自动更新 - 验证审批历史记录 ### 阶段3:特殊业务流程 **目标**: 实现续签、转正式、变更三方、新增车辆等特殊业务 **关键文件**: 1. 特殊业务 Service 方法 2. 特殊业务 Controller 接口 3. 特殊业务 VO 类 **功能清单**: - ✓ 终止合同 - ✓ 续签合同(创建新合同,关联原合同) - ✓ 转正式合同(试用→正式) - ✓ 变更为三方合同(增加丙方客户) - ✓ 新增车辆(合同执行中新增车辆) **验证方式**: - 测试每个特殊业务流程 - 验证数据关联正确性 - 验证状态流转正确性 --- ## 关键文件清单 ### 数据库脚本 - `/Users/kkfluous/Projects/ai-coding/ln-oneos/oneos-backend/yudao-module-asset/sql/mysql/contract.sql` ### DO 类 - `ContractDO.java` - 合同主表 - `ContractVehicleDO.java` - 车辆订单 - `ContractVehicleServiceDO.java` - 服务项目 - `ContractAuthorizedDO.java` - 被授权人 - `ContractAttachmentDO.java` - 合同附件 - `ContractChangeHistoryDO.java` - 变更历史 ### Mapper 接口 - `ContractMapper.java` - `ContractVehicleMapper.java` - `ContractVehicleServiceMapper.java` - `ContractAuthorizedMapper.java` - `ContractAttachmentMapper.java` - `ContractChangeHistoryMapper.java` ### VO 类 - `ContractBaseVO.java` - 基础 VO - `ContractSaveReqVO.java` - 创建/更新请求 VO - `ContractRespVO.java` - 响应 VO - `ContractPageReqVO.java` - 分页查询请求 VO - `ContractDetailRespVO.java` - 详情响应 VO - `ContractVehicleVO.java` - 车辆订单 VO - `ContractVehicleServiceVO.java` - 服务项目 VO - `ContractAuthorizedVO.java` - 被授权人 VO ### Service - `ContractService.java` - Service 接口 - `ContractServiceImpl.java` - Service 实现 - `ContractBpmListener.java` - BPM 事件监听器 ### Controller - `ContractController.java` - REST API 控制器 ### Convert - `ContractConvert.java` - MapStruct 转换器 ### 枚举类 - `ContractTypeEnum.java` - 合同类型枚举 - `ContractApprovalStatusEnum.java` - 审批状态枚举 - `ContractStatusEnum.java` - 合同状态枚举 ### BPM 流程定义 - `rental_contract_approval.bpmn20.xml` - 租赁合同审批流程定义 --- ## 注意事项 1. **数据一致性**: 合同、车辆订单、服务项目、被授权人等多表操作需要使用事务 2. **状态校验**: 每个操作前需要校验当前状态是否允许该操作 3. **权限控制**: 使用 `@PreAuthorize` 控制接口权限 4. **多租户**: 所有表都包含 `tenant_id` 字段,自动过滤 5. **软删除**: 使用 `deleted` 字段实现软删除 6. **审计字段**: 自动记录 `creator`、`create_time`、`updater`、`update_time` 7. **BPM 集成**: 流程实例 ID 需要保存到合同表,便于查询审批状态 8. **文件上传**: 使用 `FileApi` 上传附件,保存文件 ID 和 URL 9. **变更历史**: 每次重要操作都需要记录变更历史 10. **合同编码**: 自动生成,格式:HT-YYYY-NNN(HT=合同,YYYY=年份,NNN=序号) --- ## 时间估算 - **阶段1**: 基础合同管理 - 2天 - 数据库表设计和创建 - 4小时 - DO/Mapper/VO/Convert - 4小时 - Service 实现 - 6小时 - Controller 实现 - 2小时 - 测试验证 - 2小时 - **阶段2**: BPM 审批集成 - 1天 - BPM 流程定义 - 2小时 - 事件监听器 - 2小时 - 审批相关接口 - 2小时 - 测试验证 - 2小时 - **阶段3**: 特殊业务流程 - 1天 - 续签/转正式/变更三方 - 4小时 - 新增车辆/终止合同 - 2小时 - 测试验证 - 2小时 **总计:约 4 天**