# 还车应结款模块 — 完整实施计划 ## Context OneOS 需要实现完整的「还车应结款」多角色费用核算模块。当前状态: - **原型**: 3个JSX页面(列表/查看/费用明细),需求文档完整 - **老系统**: lingniu_asset_server 有完整实现(4表+多部门审批+能源校验),可参考角色设计和业务流程 - **OneOS后端**: ReturnPaymentController/Service 已有骨架,但本质是纯CRUD,**return_payment 系列表尚未建到数据库** - **OneOS前端**: 页面UI完成度高,但100%使用mock数据 - **数据库现状**: return_vehicle_task(81条)、delivery_vehicle(32条)、traffic_violation(0条)、accident_info(1条)、energy_account(有数据)、vehicle_check_item(轮胎检查项齐全) ### 核心目标 实现多角色(4个部门)独立填报→提交→审批→生成账单的完整业务流程,各部门只能看到和操作自己的费用区块。 --- ## 第一阶段:数据库建表 + 角色定义 ### 1.1 角色映射(参照老系统 SettlementDepartment) 老系统4个结算部门 → OneOS 已有系统角色映射: | 结算部门 | depCode | OneOS 角色 | role_key | 职责 | |---------|---------|-----------|----------|------| | 业务服务组 | BUSINESS | 业务服务组(id=3) / 业务服务主管 / 业务经理 | 业务服务组 | 填写违章违约金、保险上浮、ETC费用、租金计算 | | 能源采购组 | ENERGY | **需新建角色: 能源采购组** | energy_group | 填写氢量差、能源费补缴、预付款退费 | | 运维部 | OPERATION | 运维专员 / 运维主管 / 运维助理 | 运维专员 | 填写清洗费、保养维修、车损、证件丢失、轮胎磨损 | | 安全组 | SAFETY | 安全(id=2026890857448787969) | 安全 | 确认违章清单、事故清单 | 审批角色: | 角色 | 职责 | |------|------| | 业务服务主管 / 业务负责人 | 汇总审核,提交总审批 | | 财务 / 财务主管 | 生成账单、付款确认 | | 总经理 | 终审(可选) | ### 1.2 建表SQL(重新设计,参照老系统+原型需求) ```sql -- 主表:还车应结款单(一车一单) CREATE TABLE return_settlement ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, return_task_id BIGINT UNSIGNED NOT NULL COMMENT '关联 return_vehicle_task.id', delivery_vehicle_id BIGINT UNSIGNED NOT NULL COMMENT '关联 delivery_vehicle.id', contract_id BIGINT NOT NULL COMMENT '关联 vehicle_lease_contract_info.id', order_detail_id BIGINT COMMENT '关联 vehicle_lease_order_detail.id', contract_code VARCHAR(100) COMMENT '合同编号', project_name VARCHAR(200) COMMENT '项目名称', customer_id BIGINT COMMENT '客户ID', customer_name VARCHAR(200) COMMENT '客户名称', plate_number VARCHAR(50) COMMENT '车牌号', vehicle_id BIGINT UNSIGNED COMMENT '车辆ID', business_dept_name VARCHAR(100) COMMENT '业务部门', business_owner_name VARCHAR(100) COMMENT '业务负责人', delivery_time DATETIME COMMENT '交车时间', return_time DATETIME COMMENT '还车时间', returner_name VARCHAR(100) COMMENT '还车人', -- 保险标识(从合同/服务项推导) has_fragile_insurance TINYINT(1) DEFAULT 0 COMMENT '易损保 0否1是', has_tire_insurance TINYINT(1) DEFAULT 0 COMMENT '轮胎保 0否1是', has_maintenance_insurance TINYINT(1) DEFAULT 0 COMMENT '养护保 0否1是', -- 金额汇总 deposit_amount DECIMAL(18,2) DEFAULT 0 COMMENT '保证金总额', pending_settle_amount DECIMAL(18,2) DEFAULT 0 COMMENT '待结算总额', should_refund_amount DECIMAL(18,2) DEFAULT 0 COMMENT '应退还总额', should_pay_amount DECIMAL(18,2) DEFAULT 0 COMMENT '应补缴总额', -- 审批状态(参照老系统) approval_status TINYINT DEFAULT 0 COMMENT '0待提交 1撤回 10待审批 20审批中 30审批完成 40审批驳回', approval_submit_time DATETIME COMMENT '提交审批时间', is_last_vehicle TINYINT(1) DEFAULT 0 COMMENT '是否合同最后一辆车', generated_at DATETIME COMMENT '还车应结款生成时间(15天倒计时起点)', -- 审计字段 del_flag CHAR(1) DEFAULT '0', create_by BIGINT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_by BIGINT, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_return_task (return_task_id), INDEX idx_contract (contract_id), INDEX idx_plate (plate_number), INDEX idx_status (approval_status) ) COMMENT '还车应结款主表'; -- 部门提交状态表(4个部门各一行) CREATE TABLE return_settlement_dept_status ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, settlement_id BIGINT UNSIGNED NOT NULL COMMENT '关联 return_settlement.id', dep_code VARCHAR(20) NOT NULL COMMENT 'BUSINESS/ENERGY/OPERATION/SAFETY', dep_name VARCHAR(50) COMMENT '部门名称', status TINYINT DEFAULT 0 COMMENT '0待提交 1已提交 2已撤回', submit_by BIGINT COMMENT '提交人ID', submit_by_name VARCHAR(100) COMMENT '提交人姓名', submit_time DATETIME COMMENT '提交时间', total_amount DECIMAL(18,2) DEFAULT 0 COMMENT '部门费用合计', remark VARCHAR(500), del_flag CHAR(1) DEFAULT '0', create_by BIGINT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_by BIGINT, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_settlement (settlement_id), UNIQUE KEY uk_settlement_dep (settlement_id, dep_code) ) COMMENT '部门提交状态'; -- 业务服务组费用明细 CREATE TABLE return_settlement_business_fee ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, settlement_id BIGINT UNSIGNED NOT NULL, seq INT COMMENT '序号', fee_item VARCHAR(200) NOT NULL COMMENT '费用项名称', amount DECIMAL(18,2) DEFAULT 0 COMMENT '金额', remark VARCHAR(500), photos TEXT COMMENT '照片URL JSON数组', attachments TEXT COMMENT '附件URL JSON数组', is_fixed TINYINT(1) DEFAULT 0 COMMENT '是否固定费用项(不可删)', update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, del_flag CHAR(1) DEFAULT '0', create_by BIGINT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_settlement (settlement_id) ) COMMENT '业务服务组费用'; -- 业务服务组-车辆租金 CREATE TABLE return_settlement_rent ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, settlement_id BIGINT UNSIGNED NOT NULL, bill_start_date DATE COMMENT '账单开始日期', vehicle_month_rent DECIMAL(18,2) COMMENT '车辆月租金', received_rent DECIMAL(18,2) DEFAULT 0 COMMENT '本期已收租金', actual_rent DECIMAL(18,2) DEFAULT 0 COMMENT '车辆实际租金', should_refund_rent DECIMAL(18,2) DEFAULT 0 COMMENT '车辆应退租金', del_flag CHAR(1) DEFAULT '0', create_by BIGINT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_by BIGINT, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_settlement (settlement_id) ) COMMENT '车辆租金'; -- 能源采购组 CREATE TABLE return_settlement_energy ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, settlement_id BIGINT UNSIGNED NOT NULL, delivery_hydrogen DECIMAL(10,2) COMMENT '交车氢量(MPa)', return_hydrogen DECIMAL(10,2) COMMENT '还车氢量(MPa)', hydrogen_unit_price DECIMAL(10,2) COMMENT '退还车氢气单价', hydrogen_supplement DECIMAL(18,2) DEFAULT 0 COMMENT '氢量差补缴金额', hydrogen_fee DECIMAL(18,2) DEFAULT 0 COMMENT '氢费补缴金额', electric_fee DECIMAL(18,2) DEFAULT 0 COMMENT '电费补缴金额', prepay_refund DECIMAL(18,2) DEFAULT 0 COMMENT '预付款退费金额', user_balance DECIMAL(18,2) DEFAULT 0 COMMENT '项目预充值余额(快照)', del_flag CHAR(1) DEFAULT '0', create_by BIGINT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_by BIGINT, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_settlement (settlement_id) ) COMMENT '能源采购组费用'; -- 运维部费用明细 CREATE TABLE return_settlement_operation_fee ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, settlement_id BIGINT UNSIGNED NOT NULL, seq INT COMMENT '序号', fee_item VARCHAR(200) NOT NULL COMMENT '费用项', amount DECIMAL(18,2) DEFAULT 0 COMMENT '金额', worry_free_discount DECIMAL(18,2) DEFAULT 0 COMMENT '无忧包减免', remark VARCHAR(500), photos TEXT COMMENT '照片URL JSON数组', attachments TEXT COMMENT '附件URL JSON数组', is_fixed TINYINT(1) DEFAULT 0 COMMENT '是否固定费用项', is_readonly TINYINT(1) DEFAULT 0 COMMENT '是否只读(送车/接车服务费)', update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, del_flag CHAR(1) DEFAULT '0', create_by BIGINT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_settlement (settlement_id) ) COMMENT '运维部费用'; -- 运维部-轮胎胎纹明细 CREATE TABLE return_settlement_tire_tread ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, settlement_id BIGINT UNSIGNED NOT NULL, tire_name VARCHAR(100) COMMENT '轮胎名称(左前轮等)', delivery_depth DECIMAL(6,2) COMMENT '交车胎纹深度(mm)', return_depth DECIMAL(6,2) COMMENT '还车胎纹深度(mm)', depth_diff DECIMAL(6,2) COMMENT '胎纹差(mm)', unit_price DECIMAL(10,2) COMMENT '单价(元/mm)', total_amount DECIMAL(18,2) DEFAULT 0 COMMENT '总金额', del_flag CHAR(1) DEFAULT '0', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_settlement (settlement_id) ) COMMENT '轮胎胎纹明细'; -- 安全组-违章快照 CREATE TABLE return_settlement_violation ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, settlement_id BIGINT UNSIGNED NOT NULL, source_violation_id BIGINT COMMENT '来源 traffic_violation.id', violation_code VARCHAR(100) COMMENT '违章编码', plate_number VARCHAR(50), violation_behavior VARCHAR(500), violation_time DATETIME, penalty_amount DECIMAL(10,2) DEFAULT 0, payment_status VARCHAR(20) COMMENT '缴费状态', score INT DEFAULT 0 COMMENT '计分值', handle_status VARCHAR(20) COMMENT '处理状态', violation_customer VARCHAR(200), violation_photo VARCHAR(500), remark VARCHAR(500), del_flag CHAR(1) DEFAULT '0', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_settlement (settlement_id) ) COMMENT '安全组违章快照'; -- 安全组-事故快照 CREATE TABLE return_settlement_accident ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, settlement_id BIGINT UNSIGNED NOT NULL, source_accident_id BIGINT COMMENT '来源 accident_info.id', accident_code VARCHAR(100), plate_number VARCHAR(50), accident_time DATETIME, accident_place VARCHAR(200), accident_type VARCHAR(50), customer_name VARCHAR(200), our_claim_amount DECIMAL(10,2), their_claim_amount DECIMAL(10,2), responsibility VARCHAR(50), accident_status VARCHAR(50), close_time DATE, other_fee DECIMAL(10,2), remark VARCHAR(500), del_flag CHAR(1) DEFAULT '0', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_settlement (settlement_id) ) COMMENT '安全组事故快照'; ``` ### 1.3 新建角色 ```sql -- 能源采购组角色(系统中尚未有) INSERT INTO `ry-cloud`.sys_role (role_id, role_name, role_key, role_sort, status, del_flag, create_by, create_time) VALUES (2040000000000000001, '能源采购组', 'energy_group', 15, '0', '0', 1, NOW()); ``` --- ## 第二阶段:后端核心重构 ### 2.1 文件结构(在 ln-asset-management 内) ``` modules/contract/ ├── controller/ │ └── ReturnSettlementController.java ← 新建,替代 ReturnPaymentController ├── service/ │ ├── ReturnSettlementService.java ← 接口 │ └── impl/ReturnSettlementServiceImpl.java ← 核心实现 ├── entity/settlement/ │ ├── po/ │ │ ├── ReturnSettlement.java │ │ ├── ReturnSettlementDeptStatus.java │ │ ├── ReturnSettlementBusinessFee.java │ │ ├── ReturnSettlementRent.java │ │ ├── ReturnSettlementEnergy.java │ │ ├── ReturnSettlementOperationFee.java │ │ ├── ReturnSettlementTireTread.java │ │ ├── ReturnSettlementViolation.java │ │ └── ReturnSettlementAccident.java │ ├── req/ ← 各部门提交请求DTO │ ├── vo/ ← 列表/详情/查看响应DTO │ └── enums/ │ ├── SettlementDeptCode.java ← BUSINESS/ENERGY/OPERATION/SAFETY │ ├── DeptSubmitStatus.java ← 待提交/已提交/已撤回 │ └── SettlementApprovalStatus.java ← 0/1/10/20/30/40 ├── mapper/ │ └── (9个mapper,对应9张表) ``` ### 2.2 核心API设计 ``` 前缀: /asset/return-settlement 列表与查询: POST /page ← 分页列表(筛选:合同/客户/项目/车牌/还车时间/审批状态) GET /detail/{id} ← 详情(根据角色过滤可见区块) GET /view/{id} ← 只读查看(同详情但全部只读) 创建(还车完成时自动触发): POST /create-from-return/{returnTaskId} ← 从还车任务创建结算单,自动拉取关联数据 各部门提交/撤回(需角色鉴权): POST /business/save ← 业务服务组保存 POST /business/submit ← 业务服务组提交 POST /business/revoke/{id} ← 业务服务组撤回 POST /energy/save ← 能源采购组保存 POST /energy/submit ← 能源采购组提交 POST /energy/revoke/{id} ← 能源采购组撤回 POST /operation/save ← 运维部保存 POST /operation/submit ← 运维部提交 POST /operation/revoke/{id} ← 运维部撤回 POST /safety/save ← 安全组保存 POST /safety/submit ← 安全组提交 POST /safety/revoke/{id} ← 安全组撤回 审批: POST /submit-review/{id} ← 提交总审批(校验:4组已提交 + 15天倒计时) POST /revoke-review/{id} ← 撤回审批 POST /generate-bill/{id} ← 生成账单 ``` ### 2.3 核心业务逻辑 **create-from-return 自动拉取数据(关键):** ``` 1. 从 return_vehicle_task 取:还车氢量、还车里程、接车服务费、还车时间、还车人 2. 从 delivery_vehicle 取:交车氢量、交车里程、送车服务费、月租金、保证金、交车时间 3. 从 vehicle_lease_contract_info 取:合同编号、项目名称、客户、业务部门/负责人 4. 从 vehicle_lease_order_detail 取:月租金(vehicle_rent)、保证金(deposit)、账单开始日 5. 从 vehicle_lease_order_service_item 取:推导保险标识 - 有"设备损坏金(包含易损件)"→ has_fragile_insurance=1 - 有"轮胎磨损费"→ has_tire_insurance=1 - (养护保需确认service_item名称) 6. 从 vehicle_check_item + delivery_vehicle_check_item 取:交车轮胎胎纹深度 7. 从 return_vehicle_check_item 取:还车轮胎胎纹深度 8. 从 traffic_violation 按 vehicle_id + 交车~还车时间范围 查:违章记录快照 9. 从 accident_info 按 vehicle_id + 交车~还车时间范围 查:事故记录快照 10. 从 ln_energy.energy_account 通过 Dubbo 查:客户能源账户余额 11. 判断 is_last_vehicle:查同合同下其他 delivery_vehicle 是否都已有还车记录 12. 自动初始化4个 dept_status 记录(BUSINESS/ENERGY/OPERATION/SAFETY,状态=待提交) 13. 自动初始化固定费用行(业务5项 + 运维10项) 14. 自动计算轮胎磨损(胎纹差 * 单价) 15. 自动计算证件丢失费(对比交车/还车检查项中的行驶证、营运证等) 16. generated_at = NOW()(15天倒计时起点) ``` **角色鉴权逻辑:** ```java // 根据当前用户角色判断可见/可操作的部门区块 public SettlementDeptCode resolveUserDept(Long userId) { List roleKeys = getUserRoleKeys(userId); if (roleKeys.contains("业务服务组") || roleKeys.contains("业务服务主管") || roleKeys.contains("业务经理") || roleKeys.contains("业务负责人") || roleKeys.contains("业务总负责人")) return BUSINESS; if (roleKeys.contains("energy_group")) return ENERGY; if (roleKeys.contains("运维专员") || roleKeys.contains("运维主管") || roleKeys.contains("运维助理") || roleKeys.contains("运维总负责人")) return OPERATION; if (roleKeys.contains("安全")) return SAFETY; if (roleKeys.contains("财务") || roleKeys.contains("财务主管") || roleKeys.contains("财务总监")) return null; // 财务可见全部,但不可编辑 if (roleKeys.contains("superadmin") || roleKeys.contains("总经理")) return null; // 全部可见 return null; } ``` **费用汇总计算(服务端计算,不依赖前端):** ``` businessTotal = SUM(business_fee.amount) + rent.should_refund_rent energyTotal = hydrogen_supplement + hydrogen_fee + electric_fee - prepay_refund operationTotal = SUM(operation_fee.amount) pendingSettle = businessTotal + energyTotal + operationTotal shouldRefund = max(0, deposit - pendingSettle) shouldPay = max(0, pendingSettle - deposit) ``` --- ## 第三阶段:前端对接 ### 3.1 替换mock为真实API 修改文件: - `apps/web-antd/src/api/financial/return-payment/index.ts` — 所有API函数指向新后端端点 - `apps/web-antd/src/views/financial/returnPayment/index.vue` — 列表页调真实API - `apps/web-antd/src/views/financial/returnPayment/detail.vue` — 详情页调真实API ### 3.2 角色可见性控制 ```typescript // 根据当前用户角色控制UI区块 const userDeptCode = computed(() => { // 从用户信息中获取角色,映射到部门 const roles = userStore.getRoles; if (roles.includes('业务服务组') || roles.includes('业务服务主管') ...) return 'BUSINESS'; if (roles.includes('energy_group')) return 'ENERGY'; if (roles.includes('运维专员') || roles.includes('运维主管') ...) return 'OPERATION'; if (roles.includes('安全')) return 'SAFETY'; return 'ALL'; // 财务/管理员 }); // 业务服务组区块:仅业务角色可编辑,其他角色不可见或只读 const canEditBusiness = computed(() => userDeptCode.value === 'BUSINESS' || userDeptCode.value === 'ALL'); const canEditEnergy = computed(() => userDeptCode.value === 'ENERGY' || userDeptCode.value === 'ALL'); // ... ``` ### 3.3 关键交互保持原型一致 - 保留15天倒计时(后端校验 + 前端展示) - 保留4组折叠/展开 - 保留Popover费用明细弹出 - 保留轮胎磨损hover弹窗 - 保留无忧包减免联动(根据保险标识启用/禁用) --- ## 第四阶段:Dubbo接口(跨服务数据) ### 4.1 ln-energy 暴露接口 ```java // 在 ln-energy 中新建 public interface RemoteEnergySettlementService { /** 查询客户/项目的能源账户余额 */ BigDecimal getAccountBalance(Long customerId); /** 查询客户ETC未缴费用合计 */ BigDecimal getUnpaidEtcFee(Long customerId, String plateNumber, LocalDate startDate, LocalDate endDate); } ``` ### 4.2 ln-asset-management 调用 在 ReturnSettlementServiceImpl 中通过 `@DubboReference` 注入调用。 --- ## 实施顺序 | 步骤 | 内容 | 依赖 | |------|------|------| | S1 | 执行建表SQL + 新建能源采购组角色 | 无 | | S2 | 后端Entity/Mapper/Service骨架 | S1 | | S3 | create-from-return 自动数据拉取 | S2 | | S4 | 4组 save/submit/revoke API | S2 | | S5 | 列表/详情/查看 API + 角色鉴权 | S4 | | S6 | 前端 mock→API 切换 + 角色可见性 | S5 | | S7 | submit-review + 15天校验 | S4 | | S8 | Dubbo 接口(能源余额/ETC费用) | S5 | | S9 | 生成账单/导出 | S7 | | S10 | 端到端测试 | S6-S9 | --- ## 验证计划 1. **建表验证**: 连接数据库确认9张表已创建 2. **数据拉取验证**: 用已有还车记录调 create-from-return,确认自动填充的数据完整性 3. **角色隔离验证**: 分别用不同角色登录,确认只能看到/操作自己的费用区块 4. **费用计算验证**: 修改各部门费用后,确认汇总金额正确 5. **提交流程验证**: 4组依次提交→倒计时结束→提交审核→审批 6. **前端联调**: 列表筛选、详情展示、Popover、轮胎弹窗、15天倒计时 ### 关键文件清单 **后端(修改/新建):** - `ln-asset-management/src/main/java/com/ln/asset/modules/contract/controller/ReturnSettlementController.java` - `ln-asset-management/src/main/java/com/ln/asset/modules/contract/service/ReturnSettlementService.java` - `ln-asset-management/src/main/java/com/ln/asset/modules/contract/service/impl/ReturnSettlementServiceImpl.java` - `ln-asset-management/src/main/java/com/ln/asset/modules/contract/entity/settlement/` (9个PO + req/vo/enums) - `ln-asset-management/src/main/java/com/ln/asset/modules/contract/mapper/` (9个mapper) - `ln-asset-management/src/main/resources/db/migration/` (建表SQL) **前端(修改):** - `ln-one-os-web/apps/web-antd/src/api/financial/return-payment/index.ts` - `ln-one-os-web/apps/web-antd/src/views/financial/returnPayment/index.vue` - `ln-one-os-web/apps/web-antd/src/views/financial/returnPayment/detail.vue` - `ln-one-os-web/apps/web-antd/src/types/return-payment.ts` **跨服务(新建):** - `ln-energy/src/main/java/.../api/RemoteEnergySettlementService.java`