# 财务与收费域 - 详细设计 **文档类型**: 详细设计文档 **生成日期**: 2026-05-18 **数据来源**: REVERSE-FINANCE.md(反推文档)+ 04-FINANCE.md(原设计文档)+ 实际代码分析 --- ## 一、功能点清单 | 编号 | 功能模块 | 功能点 | 实现状态 | 说明 | |------|---------|--------|---------|------| | FIN-001 | 能耗计量管理 | 计量点CRUD | 已实现 | module-mdm 中 EnergyMeter | | FIN-002 | 能耗计量管理 | 计量点编码自动生成 | 已实现 | EM + yyyyMMddHHmmss | | FIN-003 | 能耗计量管理 | 计量点软删除 | 已实现 | 状态设为 INACTIVE | | FIN-004 | 能耗录入 | 能耗数据录入 | 已实现 | 手动录入 + IoT预留 | | FIN-005 | 能耗录入 | 读数递增校验 | 已实现 | 当前读数 >= 上次读数 | | FIN-006 | 能耗录入 | 自动计算消耗量与费用 | 已实现 | consumption = current - previous; amount = consumption * unitPrice | | FIN-007 | 能耗统计 | 按类型统计消耗 | 已实现(有缺陷) | 当前全部归入LIGHTING,需修复 | | FIN-008 | 能耗统计 | 单方能耗 | 已实现 | 遍历项目ACTIVE计量点累加 | | FIN-009 | 收费项目管理 | 收费项目CRUD | 未实现 | FeeItem 实体不存在 | | FIN-010 | 收费项目管理 | 周期性收费配置 | 未实现 | 按月/季/年收费 | | FIN-011 | 收费项目管理 | 一次性收费配置 | 未实现 | 临时收费项目 | | FIN-012 | 收费项目管理 | 按面积计费配置 | 未实现 | 面积 * 单价 | | FIN-013 | 收费项目管理 | 按用量计费配置 | 未实现 | 用量 * 单价(对接能耗) | | FIN-014 | 收费项目管理 | 固定金额计费配置 | 未实现 | 每月固定金额 | | FIN-015 | 账单管理 | 账单自动生成 | 未实现 | 根据收费项目规则按账期生成 | | FIN-016 | 账单管理 | 账单手动生成 | 未实现 | 人工创建单笔账单 | | FIN-017 | 账单管理 | 批量账单生成 | 未实现 | 一次性为项目所有业主生成 | | FIN-018 | 账单管理 | 账单状态流转 | 未实现 | UNPAID/PARTIAL_PAID/PAID/OVERDUE/CANCELLED | | FIN-019 | 账单管理 | 催缴提醒 | 未实现 | 到期前3天提醒 + 逾期催缴 | | FIN-020 | 账单管理 | 账单导出 | 未实现 | Excel/PDF 格式 | | FIN-021 | 支付管理 | 线下收款登记 | 未实现 | 现金/银行转账/刷卡 | | FIN-022 | 支付管理 | 线上支付对接 | 未实现 | 微信/支付宝 | | FIN-023 | 支付管理 | 支付确认 | 未实现 | 支付状态确认 | | FIN-024 | 支付管理 | 支付记录查询 | 未实现 | 按账单/业主/时间查询 | | FIN-025 | 退款管理 | 退款申请 | 未实现 | 业主发起退款 | | FIN-026 | 退款管理 | 退款审核 | 未实现 | 审批流程 | | FIN-027 | 退款管理 | 退款执行 | 未实现 | 原路退回/线下退款 | | FIN-028 | 滞纳金 | 滞纳金自动计算 | 未实现 | 逾期天数 * 日利率 | | FIN-029 | 滞纳金 | 滞纳金上限控制 | 未实现 | 不超过 maxLateFee | | FIN-030 | 能耗对接 | 能耗数据与账单对接 | 未实现 | EnergyConsumption -> FeeBill | | FIN-031 | 能耗修复 | 修复按类型统计 | 未实现 | 按 meter.energyType 真正分项汇总 | | FIN-032 | 能耗修复 | 统一前后端能源类型枚举 | 未实现 | 后端6种 vs 前端5种 | | FIN-033 | 财务报表 | 收费统计 | 未实现 | 按项目/类型/时间维度 | | FIN-034 | 财务报表 | 欠费分析 | 未实现 | 逾期/欠费金额统计 | | FIN-035 | 财务报表 | 收入趋势 | 未实现 | 月度/季度趋势图 | --- ## 二、数据结构设计 ### 2.1 已实现实体 #### 2.1.1 EnergyMeter(能源计量点) **所在模块**: module-mdm **数据库表**: `ops_energy_meter` **源码**: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/entity/EnergyMeter.java` | 字段名 | 类型 | 数据库列名 | 约束 | 说明 | |--------|------|-----------|------|------| | id | UUID | id | PK | 主键 | | projectId | UUID | project_id | NOT NULL | 所属项目 | | meterCode | String | meter_code | NOT NULL, UNIQUE | 计量点编码(EM + yyyyMMddHHmmss) | | meterName | String | meter_name | NOT NULL | 计量点名称 | | energyType | EnergyType | energy_type | NOT NULL | 能源类型枚举 | | spaceNodeId | UUID | space_node_id | -- | 关联空间节点 | | installationLocation | String | installation_location | -- | 安装位置 | | ratedCapacity | BigDecimal(10,2) | rated_capacity | -- | 额定容量 | | unitPrice | BigDecimal(10,4) | unit_price | -- | 单价(4位小数精度) | | status | Status | status | NOT NULL, 默认ACTIVE | 状态枚举 | | createdAt | LocalDateTime | created_at | -- | 创建时间 | | updatedAt | LocalDateTime | updated_at | -- | 更新时间 | **枚举定义**: ```java public enum EnergyType { LIGHTING("照明插座用电"), HVAC("空调用电"), POWER("动力用电"), SPECIAL("特殊用电"), WATER("给排水"), GAS("燃气"); } public enum Status { ACTIVE, INACTIVE } ``` #### 2.1.2 EnergyConsumption(能耗记录) **所在模块**: module-mdm **数据库表**: `ops_energy_consumption` **源码**: `ether-pms/module-mdm/src/main/java/com/ether/pms/mdm/entity/EnergyConsumption.java` | 字段名 | 类型 | 数据库列名 | 约束 | 说明 | |--------|------|-----------|------|------| | id | UUID | id | PK | 主键 | | projectId | UUID | project_id | NOT NULL | 所属项目 | | meterId | UUID | meter_id | NOT NULL | 关联计量点 | | consumptionDate | LocalDate | consumption_date | NOT NULL | 记录日期 | | previousReading | BigDecimal(12,2) | previous_reading | -- | 上次读数 | | currentReading | BigDecimal(12,2) | current_reading | -- | 当前读数 | | consumption | BigDecimal(12,2) | consumption | NOT NULL | 消耗量 = current - previous | | amount | BigDecimal(10,2) | amount | -- | 费用 = consumption * unitPrice | | recordedBy | UUID | recorded_by | -- | 记录人 | | recordMethod | RecordMethod | record_method | -- | 记录方式,默认MANUAL | | remarks | String(1000) | remarks | -- | 备注 | | createdAt | LocalDateTime | created_at | -- | 创建时间 | **数据库索引**: ```sql idx_ec_meter_date -- (meter_id, consumption_date) idx_ec_project_date -- (project_id, consumption_date) ``` **枚举定义**: ```java public enum RecordMethod { MANUAL, // 手动录入 IOT // IoT自动采集 } ``` --- ### 2.2 待实现实体 #### 2.2.1 FeeItem(收费项目) **目标模块**: module-finance(待创建) **目标表**: `fin_fee_item` | 字段名 | 类型 | 数据库列名 | 约束 | 说明 | |--------|------|-----------|------|------| | id | UUID | id | PK | 主键 | | projectId | UUID | project_id | NOT NULL | 所属项目 | | code | VARCHAR(50) | code | NOT NULL, UNIQUE | 收费项目编码(FI + yyyyMMddHHmmss) | | name | VARCHAR(100) | name | NOT NULL | 收费项目名称 | | type | VARCHAR(20) | type | NOT NULL | 收费类型枚举 | | billingMethod | VARCHAR(20) | billing_method | NOT NULL | 计费方式枚举 | | unitPrice | DECIMAL(12,4) | unit_price | -- | 单价(按用量/面积计费时使用) | | unit | VARCHAR(20) | unit | -- | 计量单位(kWh/吨/平方米/月/次) | | fixedAmount | DECIMAL(12,2) | fixed_amount | -- | 固定金额(FIXED计费方式) | | billDay | INT | bill_day | -- | 出账日(每月几号出账,1-28) | | dueDay | INT | due_day | -- | 到期日(出账后第N天到期) | | overdueDay | INT | overdue_day | -- | 逾期日(到期后第N天开始计滞纳金) | | enableLateFee | BOOLEAN | enable_late_fee | NOT NULL, 默认false | 是否启用滞纳金 | | lateFeeRate | DECIMAL(8,6) | late_fee_rate | -- | 日滞纳金利率(如0.0005 = 万分之五/天) | | maxLateFee | DECIMAL(12,2) | max_late_fee | -- | 滞纳金上限金额 | | description | VARCHAR(500) | description | -- | 收费项目说明 | | status | VARCHAR(20) | status | NOT NULL, 默认ENABLED | 状态:ENABLED/DISABLED | | createdAt | TIMESTAMP | created_at | NOT NULL | 创建时间 | | updatedAt | TIMESTAMP | updated_at | NOT NULL | 更新时间 | | createdBy | UUID | created_by | -- | 创建人 | **枚举定义**: ```java public enum FeeType { PROPERTY("物业费"), PARKING("停车费"), WATER("水费"), ELECTRICITY("电费"), GAS("燃气费"), HEATING("供暖费"), REPAIR_FUND("维修基金"), OTHER("其他"); } public enum BillingMethod { FIXED("固定金额"), // 每月固定金额 AREA("按面积计费"), // 面积 * 单价 USAGE("按用量计费"), // 用量 * 单价(对接能耗) CUSTOM("自定义"); // 自定义计费规则 } ``` **数据库索引**: ```sql idx_fi_project_status -- (project_id, status) idx_fi_code -- (code) -- UNIQUE约束自带索引 idx_fi_type -- (type, status) ``` **外键关系**: ```sql fk_fi_project -- project_id REFERENCES mdm_project(id) ``` --- #### 2.2.2 FeeBill(收费账单) **目标模块**: module-finance(待创建) **目标表**: `fin_fee_bill` | 字段名 | 类型 | 数据库列名 | 约束 | 说明 | |--------|------|-----------|------|------| | id | UUID | id | PK | 主键 | | billNo | VARCHAR(30) | bill_no | NOT NULL, UNIQUE | 账单编号(BL + yyyyMMdd + 4位序号) | | feeItemId | UUID | fee_item_id | NOT NULL | 关联收费项目 | | projectId | UUID | project_id | NOT NULL | 所属项目 | | spaceNodeId | UUID | space_node_id | -- | 关联空间节点(房产) | | ownerId | UUID | owner_id | -- | 关联业主ID | | billPeriod | VARCHAR(20) | bill_period | NOT NULL | 账期(如2026-05) | | billDate | DATE | bill_date | NOT NULL | 出账日期 | | dueDate | DATE | due_date | NOT NULL | 到期日期 | | overdueDate | DATE | overdue_date | -- | 逾期日期 | | usageAmount | DECIMAL(12,2) | usage_amount | -- | 用量(按用量计费时) | | usageUnit | VARCHAR(20) | usage_unit | -- | 用量单位 | | area | DECIMAL(12,2) | area | -- | 面积(按面积计费时) | | amount | DECIMAL(12,2) | amount | NOT NULL | 应收金额 | | lateFee | DECIMAL(12,2) | late_fee | 默认0 | 滞纳金 | | discount | DECIMAL(12,2) | discount | 默认0 | 优惠金额 | | payableAmount | DECIMAL(12,2) | payable_amount | NOT NULL | 应付金额 = amount + lateFee - discount | | paidAmount | DECIMAL(12,2) | paid_amount | 默认0 | 已付金额 | | status | VARCHAR(20) | status | NOT NULL, 默认UNPAID | 账单状态枚举 | | sourceType | VARCHAR(20) | source_type | -- | 来源类型:AUTO/MANUAL/IMPORT | | sourceId | UUID | source_id | -- | 来源ID(如能耗记录ID) | | remark | VARCHAR(500) | remark | -- | 备注 | | createdAt | TIMESTAMP | created_at | NOT NULL | 创建时间 | | updatedAt | TIMESTAMP | updated_at | NOT NULL | 更新时间 | | createdBy | UUID | created_by | -- | 创建人 | **枚举定义**: ```java public enum BillStatus { UNPAID("未支付"), PARTIAL_PAID("部分支付"), PAID("已支付"), OVERDUE("已逾期"), CANCELLED("已取消"); } public enum BillSourceType { AUTO("自动生成"), MANUAL("手动创建"), IMPORT("导入"); } ``` **数据库索引**: ```sql idx_fb_bill_no -- (bill_no) -- UNIQUE约束自带索引 idx_fb_project_period -- (project_id, bill_period) idx_fb_owner_status -- (owner_id, status) idx_fb_fee_item -- (fee_item_id, bill_period) idx_fb_space_node -- (space_node_id, bill_period) idx_fb_due_date -- (due_date, status) -- 用于催缴查询 idx_fb_overdue -- (status, overdue_date) -- 用于滞纳金计算 ``` **外键关系**: ```sql fk_fb_fee_item -- fee_item_id REFERENCES fin_fee_item(id) fk_fb_project -- project_id REFERENCES mdm_project(id) fk_fb_space_node -- space_node_id REFERENCES mdm_space_node(id) ``` --- #### 2.2.3 FeePayment(支付记录) **目标模块**: module-finance(待创建) **目标表**: `fin_fee_payment` | 字段名 | 类型 | 数据库列名 | 约束 | 说明 | |--------|------|-----------|------|------| | id | UUID | id | PK | 主键 | | paymentNo | VARCHAR(30) | payment_no | NOT NULL, UNIQUE | 支付编号(PY + yyyyMMddHHmmss) | | billId | UUID | bill_id | NOT NULL | 关联账单 | | amount | DECIMAL(12,2) | amount | NOT NULL | 支付金额 | | method | VARCHAR(20) | method | NOT NULL | 支付方式枚举 | | thirdPartyNo | VARCHAR(100) | third_party_no | -- | 第三方交易号(微信/支付宝) | | status | VARCHAR(20) | status | NOT NULL, 默认PENDING | 支付状态枚举 | | failReason | VARCHAR(500) | fail_reason | -- | 失败原因 | | paymentTime | TIMESTAMP | payment_time | -- | 实际支付时间 | | operatorId | UUID | operator_id | -- | 操作人(线下收款时为收费员) | | receiptNo | VARCHAR(30) | receipt_no | -- | 收据编号 | | remark | VARCHAR(500) | remark | -- | 备注 | | createdAt | TIMESTAMP | created_at | NOT NULL | 创建时间 | | updatedAt | TIMESTAMP | updated_at | NOT NULL | 更新时间 | **枚举定义**: ```java public enum PaymentMethod { WECHAT("微信支付"), ALIPAY("支付宝"), CASH("现金"), BANK_TRANSFER("银行转账"), CARD("刷卡"); } public enum PaymentStatus { PENDING("待支付"), SUCCESS("支付成功"), FAILED("支付失败"), REFUNDED("已退款"); } ``` **数据库索引**: ```sql idx_fp_payment_no -- (payment_no) -- UNIQUE约束自带索引 idx_fp_bill_id -- (bill_id) idx_fp_status -- (status, payment_time) idx_fp_method -- (method, payment_time) idx_fp_operator -- (operator_id, payment_time) ``` **外键关系**: ```sql fk_fp_bill -- bill_id REFERENCES fin_fee_bill(id) ``` --- #### 2.2.4 FeeRefund(退款记录) **目标模块**: module-finance(待创建) **目标表**: `fin_fee_refund` | 字段名 | 类型 | 数据库列名 | 约束 | 说明 | |--------|------|-----------|------|------| | id | UUID | id | PK | 主键 | | refundNo | VARCHAR(30) | refund_no | NOT NULL, UNIQUE | 退款编号(RF + yyyyMMddHHmmss) | | paymentId | UUID | payment_id | NOT NULL | 关联支付记录 | | billId | UUID | bill_id | NOT NULL | 关联账单 | | amount | DECIMAL(12,2) | amount | NOT NULL | 退款金额 | | reason | VARCHAR(500) | reason | NOT NULL | 退款原因 | | status | VARCHAR(20) | status | NOT NULL, 默认PENDING | 退款状态枚举 | | applicantId | UUID | applicant_id | NOT NULL | 申请人ID | | approverId | UUID | approver_id | -- | 审批人ID | | approveTime | TIMESTAMP | approve_time | -- | 审批时间 | | approveRemark | VARCHAR(500) | approve_remark | -- | 审批备注 | | thirdPartyRefundNo | VARCHAR(100) | third_party_refund_no | -- | 第三方退款单号 | | refundTime | TIMESTAMP | refund_time | -- | 实际退款时间 | | refundMethod | VARCHAR(20) | refund_method | -- | 退款方式:ORIGINAL/OFFLINE | | remark | VARCHAR(500) | remark | -- | 备注 | | createdAt | TIMESTAMP | created_at | NOT NULL | 创建时间 | | updatedAt | TIMESTAMP | updated_at | NOT NULL | 更新时间 | **枚举定义**: ```java public enum RefundStatus { PENDING("待审批"), APPROVED("已审批"), REJECTED("已拒绝"), REFUNDED("已退款"), FAILED("退款失败"); } public enum RefundMethod { ORIGINAL("原路退回"), OFFLINE("线下退款"); } ``` **数据库索引**: ```sql idx_fr_refund_no -- (refund_no) -- UNIQUE约束自带索引 idx_fr_payment -- (payment_id) idx_fr_bill -- (bill_id) idx_fr_status -- (status) idx_fr_applicant -- (applicant_id, status) ``` **外键关系**: ```sql fk_fr_payment -- payment_id REFERENCES fin_fee_payment(id) fk_fr_bill -- bill_id REFERENCES fin_fee_bill(id) ``` --- ### 2.3 实体关系图 ``` EnergyMeter (module-mdm) FeeItem (module-finance) | | v v EnergyConsumption ---费用计算---> FeeBill (自动/手动生成) (抄表数据) | | | +---> FeePayment (支付记录) | | | v | FeeRefund (退款记录) | +-- sourceType=AUTO, sourceId=EnergyConsumption.id ``` **跨模块引用关系**: - FeeBill.sourceId 可指向 EnergyConsumption.id(按用量计费时) - FeeBill.spaceNodeId 引用 mdm_space_node.id - FeeBill.ownerId 引用 auth_user.id(业主) - FeePayment.operatorId 引用 auth_user.id(收费员) - FeeRefund.applicantId / approverId 引用 auth_user.id --- ## 三、API设计 ### 3.1 已实现API:能耗管理相关端点 **基础路径**: `/api/ops/energy` **控制器**: `EnergyController` #### 3.1.1 计量点管理 | 方法 | 路径 | 说明 | 请求参数 | 响应类型 | |------|------|------|---------|---------| | POST | `/meters` | 创建计量点 | EnergyMeter JSON | `ApiResponse` | | GET | `/meters` | 查询计量点列表 | projectId(必填), energyType(可选) | `ApiResponse>` | | GET | `/meters/{id}` | 获取计量点详情 | path: id | `ApiResponse` | | PUT | `/meters/{id}` | 更新计量点 | path: id, EnergyMeter JSON | `ApiResponse` | | DELETE | `/meters/{id}` | 删除计量点(软删除) | path: id | `ApiResponse` | #### 3.1.2 能耗记录 | 方法 | 路径 | 说明 | 请求参数 | 响应类型 | |------|------|------|---------|---------| | POST | `/consumption` | 录入能耗数据 | `{meterId, currentReading, recordedBy}` | `ApiResponse` | | GET | `/consumption/{meterId}` | 查询能耗记录 | path: meterId, startDate(可选), endDate(可选) | `ApiResponse>` | #### 3.1.3 能耗统计 | 方法 | 路径 | 说明 | 请求参数 | 响应类型 | |------|------|------|---------|---------| | GET | `/statistics/by-type` | 按类型统计消耗 | projectId, month(yyyy-MM-dd) | `ApiResponse>` | | GET | `/statistics/unit-consumption` | 单方能耗 | projectId, month(yyyy-MM-dd) | `ApiResponse` | --- ### 3.2 待实现API:收费项目/账单/支付/退款相关端点 #### 3.2.1 FeeItemController -- 收费项目管理 **基础路径**: `/api/finance/fee-items` | 方法 | 路径 | 说明 | 请求体/参数 | 响应类型 | |------|------|------|------------|---------| | POST | `/` | 创建收费项目 | FeeItemForm JSON | `ApiResponse` | | GET | `/` | 查询收费项目列表 | projectId(必填), type(可选), status(可选), page, size | `ApiResponse>` | | GET | `/{id}` | 获取收费项目详情 | path: id | `ApiResponse` | | PUT | `/{id}` | 更新收费项目 | path: id, FeeItemForm JSON | `ApiResponse` | | PUT | `/{id}/status` | 启用/禁用收费项目 | `{status: "ENABLED"/"DISABLED"}` | `ApiResponse` | | DELETE | `/{id}` | 删除收费项目 | path: id | `ApiResponse` | **FeeItemForm**: ```json { "projectId": "UUID", "name": "物业费", "type": "PROPERTY", "billingMethod": "AREA", "unitPrice": 3.50, "unit": "平方米/月", "fixedAmount": null, "billDay": 1, "dueDay": 15, "overdueDay": 5, "enableLateFee": true, "lateFeeRate": 0.0005, "maxLateFee": 500.00, "description": "住宅物业费" } ``` #### 3.2.2 FeeBillController -- 账单管理 **基础路径**: `/api/finance/bills` | 方法 | 路径 | 说明 | 请求体/参数 | 响应类型 | |------|------|------|------------|---------| | POST | `/` | 手动创建账单 | FeeBillForm JSON | `ApiResponse` | | POST | `/generate` | 按收费项目自动生成账单 | `{projectId, feeItemId, billPeriod}` | `ApiResponse>` | | POST | `/generate-batch` | 批量生成账单 | `{projectId, billPeriod, feeItemIds[]}` | `ApiResponse` | | GET | `/` | 查询账单列表 | projectId, ownerId, status, billPeriod, page, size | `ApiResponse>` | | GET | `/{id}` | 获取账单详情 | path: id | `ApiResponse` | | PUT | `/{id}` | 更新账单 | path: id, FeeBillForm JSON | `ApiResponse` | | POST | `/{id}/cancel` | 取消账单 | `{reason}` | `ApiResponse` | | GET | `/overdue` | 查询逾期账单 | projectId, page, size | `ApiResponse>` | | GET | `/statistics` | 账单统计 | projectId, billPeriod | `ApiResponse` | | GET | `/export` | 导出账单 | projectId, billPeriod, format | `Blob` | **FeeBillForm**: ```json { "feeItemId": "UUID", "projectId": "UUID", "spaceNodeId": "UUID", "ownerId": "UUID", "billPeriod": "2026-05", "usageAmount": 150.50, "usageUnit": "kWh", "area": 120.00, "amount": 525.00, "remark": "" } ``` **BillStatistics**: ```json { "totalAmount": 150000.00, "paidAmount": 120000.00, "unpaidAmount": 30000.00, "overdueAmount": 15000.00, "lateFeeAmount": 500.00, "totalCount": 200, "paidCount": 160, "unpaidCount": 30, "overdueCount": 10 } ``` #### 3.2.3 FeePaymentController -- 支付管理 **基础路径**: `/api/finance/payments` | 方法 | 路径 | 说明 | 请求体/参数 | 响应类型 | |------|------|------|------------|---------| | POST | `/` | 创建支付记录(线下收款) | PaymentForm JSON | `ApiResponse` | | GET | `/` | 查询支付记录 | billId, ownerId, method, status, startDate, endDate, page, size | `ApiResponse>` | | GET | `/{id}` | 获取支付详情 | path: id | `ApiResponse` | | POST | `/{id}/confirm` | 确认支付 | `{status: "SUCCESS"/"FAILED", failReason}` | `ApiResponse` | | POST | `/online/prepare` | 发起线上支付 | `{billId, method}` | `ApiResponse` | | POST | `/online/callback` | 支付回调 | 第三方回调数据 | `ApiResponse` | **PaymentForm**: ```json { "billId": "UUID", "amount": 525.00, "method": "CASH", "receiptNo": "RCP20260518001", "remark": "" } ``` #### 3.2.4 FeeRefundController -- 退款管理 **基础路径**: `/api/finance/refunds` | 方法 | 路径 | 说明 | 请求体/参数 | 响应类型 | |------|------|------|------------|---------| | POST | `/` | 申请退款 | RefundForm JSON | `ApiResponse` | | GET | `/` | 查询退款记录 | billId, status, startDate, endDate, page, size | `ApiResponse>` | | GET | `/{id}` | 获取退款详情 | path: id | `ApiResponse` | | POST | `/{id}/approve` | 审批通过 | `{approveRemark}` | `ApiResponse` | | POST | `/{id}/reject` | 审批拒绝 | `{approveRemark}` | `ApiResponse` | | POST | `/{id}/execute` | 执行退款 | `{thirdPartyRefundNo, refundMethod}` | `ApiResponse` | **RefundForm**: ```json { "paymentId": "UUID", "billId": "UUID", "amount": 525.00, "reason": "重复缴费", "refundMethod": "ORIGINAL" } ``` #### 3.2.5 FeeReminderJob -- 催缴定时任务 | 任务 | Cron表达式 | 说明 | |------|-----------|------| | 到期提醒 | `0 0 9 * * ?` | 每天上午9点检查3天内到期账单,发送提醒 | | 逾期催缴 | `0 0 10 * * ?` | 每天上午10点检查逾期账单,发送催缴通知 | | 滞纳金计算 | `0 0 2 * * ?` | 每天凌晨2点计算逾期账单滞纳金 | | 周汇总 | `0 0 18 ? * MON` | 每周一18点汇总逾期账单,通知项目经理 | --- ## 四、业务规则 ### 4.1 能耗计费规则(已实现 + 待完善) #### 4.1.1 已实现规则 | 规则编号 | 规则名称 | 规则描述 | 实现位置 | |---------|---------|---------|---------| | E-001 | 仪表状态校验 | 只能对ACTIVE状态的仪表进行抄表 | EnergyConsumptionServiceImpl | | E-002 | 读数递增校验 | 当前读数不能小于上次读数 | EnergyConsumptionServiceImpl | | E-003 | 自动获取上次读数 | 从最近一条记录获取previousReading,首次为0 | EnergyConsumptionServiceImpl | | E-004 | 自动计算消耗量 | consumption = currentReading - previousReading | EnergyConsumptionServiceImpl | | E-005 | 自动计算费用 | amount = consumption * meter.unitPrice(单价为空时为0) | EnergyConsumptionServiceImpl | | E-006 | 按月统计 | 根据month参数计算月份起止日期 | EnergyConsumptionServiceImpl | | E-007 | 单方能耗 | 遍历项目所有ACTIVE计量点,累加月度消耗量 | EnergyConsumptionServiceImpl | #### 4.1.2 待修复缺陷 | 缺陷编号 | 描述 | 严重度 | 修复方案 | |---------|------|--------|---------| | E-BUG-001 | getConsumptionByType()将总消耗全部归入LIGHTING | 高 | 按 meter.energyType 真正分项汇总 | | E-BUG-002 | 前后端能源类型枚举不一致 | 中 | 统一为后端6种枚举,前端对齐 | --- ### 4.2 收费项目规则(待设计) | 规则编号 | 规则名称 | 规则描述 | 适用计费方式 | |---------|---------|---------|------------| | FI-001 | 周期性收费 | 按月/季/年定期出账,billDay指定出账日 | FIXED / AREA / USAGE | | FI-002 | 一次性收费 | 临时创建,不参与自动出账 | CUSTOM | | FI-003 | 临时收费 | 针对特定业主的临时性收费 | CUSTOM | | FI-004 | 固定金额计费 | 每期收取固定金额(如停车费300元/月) | FIXED | | FI-005 | 按面积计费 | 面积 * 单价(如物业费3.5元/平方米/月) | AREA | | FI-006 | 按用量计费 | 用量 * 单价(如电费0.85元/kWh),对接能耗数据 | USAGE | | FI-007 | 编码自动生成 | 格式:FI + yyyyMMddHHmmss,冲突时追加后缀 | 全部 | | FI-008 | 禁用不删除 | 收费项目禁用后不再参与自动出账,已有账单不受影响 | 全部 | | FI-009 | 项目隔离 | 收费项目属于特定项目,跨项目不可见 | 全部 | --- ### 4.3 账单生成规则(待设计) | 规则编号 | 规则名称 | 规则描述 | |---------|---------|---------| | BL-001 | 自动生成 | 根据收费项目的billDay,定时任务自动为项目下所有业主生成账单 | | BL-002 | 手动生成 | 管理员手动为指定业主创建账单 | | BL-003 | 批量生成 | 一次性为项目所有业主生成某类收费项目的账单 | | BL-004 | 账单编号生成 | 格式:BL + yyyyMMdd + 4位序号,序号按天递增 | | BL-005 | 到期日计算 | dueDate = billDate + feeItem.dueDay天 | | BL-006 | 逾期日计算 | overdueDate = dueDate + feeItem.overdueDay天 | | BL-007 | 按用量计费账单 | 从EnergyConsumption获取当期用量,计算金额 | | BL-008 | 按面积计费账单 | 从SpaceNode获取面积,计算金额 = 面积 * 单价 | | BL-009 | 固定金额账单 | 金额 = feeItem.fixedAmount | | BL-010 | 防重复生成 | 同一收费项目 + 同一业主 + 同一账期不可重复生成 | | BL-011 | 催缴提醒 | 到期前3天发送提醒,逾期后发送催缴通知 | | BL-012 | 账单取消 | 已支付/部分支付的账单不可取消,需先退款 | --- ### 4.4 支付流程(待设计) | 规则编号 | 规则名称 | 规则描述 | |---------|---------|---------| | PY-001 | 线下收款 | 管理员登记现金/转账/刷卡收款,直接确认成功 | | PY-002 | 线上支付 | 调用微信/支付宝SDK生成预支付单,等待回调确认 | | PY-003 | 支付确认 | 线上支付由回调确认,线下支付由操作员确认 | | PY-004 | 部分支付 | 支持部分支付,账单状态变为PARTIAL_PAID | | PY-005 | 超额支付 | 支付金额不可超过应付金额(payableAmount - paidAmount) | | PY-006 | 支付编号生成 | 格式:PY + yyyyMMddHHmmss | | PY-007 | 账单状态联动 | 支付成功后更新账单paidAmount和status | | PY-008 | 收据编号 | 线下收款时生成收据编号 | **支付状态流转**: ``` PENDING --(支付成功)--> SUCCESS PENDING --(支付失败)--> FAILED SUCCESS --(发起退款)--> REFUNDED ``` **账单状态联动**: ``` UNPAID --(部分支付)--> PARTIAL_PAID UNPAID/PARTIAL_PAID --(全额支付)--> PAID UNPAID --(超过逾期日)--> OVERDUE 任意状态 --(取消)--> CANCELLED(需先退款) ``` --- ### 4.5 退款流程(待设计) | 规则编号 | 规则名称 | 规则描述 | |---------|---------|---------| | RF-001 | 退款申请 | 业主或管理员发起退款申请,需指定退款原因 | | RF-002 | 退款金额限制 | 退款金额不可超过原支付金额 | | RF-003 | 退款审批 | 退款金额 > 1000元需审批,否则自动通过 | | RF-004 | 审批通过 | 审批人确认后,退款状态变为APPROVED | | RF-005 | 审批拒绝 | 审批人可拒绝退款,需填写拒绝原因 | | RF-006 | 原路退回 | 线上支付的退款原路退回至支付账户 | | RF-007 | 线下退款 | 现金/转账支付的退款通过线下处理 | | RF-008 | 退款执行 | 记录第三方退款单号和实际退款时间 | | RF-009 | 账单金额回退 | 退款成功后,账单paidAmount减少,状态可能回退 | | RF-010 | 退款编号生成 | 格式:RF + yyyyMMddHHmmss | **退款状态流转**: ``` PENDING --(审批通过)--> APPROVED PENDING --(审批拒绝)--> REJECTED APPROVED --(退款成功)--> REFUNDED APPROVED --(退款失败)--> FAILED ``` --- ### 4.6 滞纳金计算(待设计) | 规则编号 | 规则名称 | 规则描述 | |---------|---------|---------| | LF-001 | 计算触发 | 每天凌晨2点定时任务检查逾期账单 | | LF-002 | 计算公式 | lateFee = 逾期天数 * payableAmount * lateFeeRate | | LF-003 | 逾期天数 | 从overdueDate开始计算,到当前日期 | | LF-004 | 上限控制 | lateFee不超过feeItem.maxLateFee | | LF-005 | 累加计算 | 每天累加,不覆盖之前的滞纳金 | | LF-006 | 前提条件 | 仅当feeItem.enableLateFee = true时计算 | | LF-007 | 已支付账单 | 已全额支付的账单不再计算滞纳金 | | LF-008 | 已取消账单 | 已取消的账单不再计算滞纳金 | | LF-009 | 部分支付 | 滞纳金基于剩余应付金额计算 | **滞纳金计算示例**: ``` 假设: payableAmount = 1000元 lateFeeRate = 0.0005(万分之五/天) maxLateFee = 500元 overdueDate = 2026-05-10 当前日期 = 2026-05-18 计算: 逾期天数 = 8天 每日滞纳金 = 1000 * 0.0005 = 0.5元 累计滞纳金 = 8 * 0.5 = 4元(未超过上限500元) ``` --- ## 五、执行约束 | 约束编号 | 约束名称 | 约束描述 | |---------|---------|---------| | CON-001 | 金额精度 | 所有金额字段使用DECIMAL(12,2),单价使用DECIMAL(12,4),利率使用DECIMAL(8,6) | | CON-002 | 事务一致性 | 账单生成、支付确认、退款执行必须在同一事务中完成账单状态更新 | | CON-003 | 并发控制 | 支付操作使用乐观锁(version字段或状态校验),防止重复支付 | | CON-004 | 审计日志 | 所有收费/支付/退款操作必须记录审计日志 | | CON-005 | 项目隔离 | 所有查询必须按projectId过滤,确保项目间数据隔离 | | CON-006 | 软删除 | 收费项目使用启用/禁用替代物理删除,账单/支付/退款不允许删除 | | CON-007 | 编码唯一性 | billNo/paymentNo/refundNo全局唯一,生成时需处理冲突 | | CON-008 | 定时任务幂等 | 催缴/滞纳金计算任务必须幂等,重复执行不产生副作用 | --- ## 六、权限控制 | 权限编码 | 权限名称 | 适用角色 | 说明 | |---------|---------|---------|------| | finance:fee-item:list | 查看收费项目 | 项目管理员/财务人员 | 查看项目下收费项目列表 | | finance:fee-item:create | 创建收费项目 | 系统管理员 | 创建新的收费项目 | | finance:fee-item:update | 修改收费项目 | 系统管理员 | 修改收费项目配置 | | finance:fee-item:delete | 删除收费项目 | 系统管理员 | 删除/禁用收费项目 | | finance:bill:list | 查看账单 | 项目管理员/财务人员 | 查看项目下账单列表 | | finance:bill:create | 创建账单 | 财务人员 | 手动创建账单 | | finance:bill:generate | 生成账单 | 财务人员 | 自动/批量生成账单 | | finance:bill:cancel | 取消账单 | 财务主管 | 取消账单(需审批) | | finance:bill:export | 导出账单 | 财务人员 | 导出账单Excel/PDF | | finance:payment:create | 登记收款 | 财务人员 | 线下收款登记 | | finance:payment:confirm | 确认支付 | 财务人员 | 确认支付状态 | | finance:payment:list | 查看支付记录 | 财务人员 | 查看支付记录列表 | | finance:refund:apply | 申请退款 | 财务人员/业主 | 发起退款申请 | | finance:refund:approve | 审批退款 | 财务主管 | 审批退款申请 | | finance:refund:execute | 执行退款 | 财务人员 | 执行退款操作 | | finance:refund:list | 查看退款记录 | 财务人员 | 查看退款记录列表 | | finance:statistics:view | 查看财务统计 | 项目管理员/财务主管 | 查看财务报表 | | finance:late-fee:config | 配置滞纳金 | 系统管理员 | 配置滞纳金规则 | **数据级权限**: - 财务人员(PROJECT数据范围):只能查看/操作所属项目的财务数据 - 财务主管(ALL数据范围):可查看所有项目的财务数据 - 业主(SELF数据范围):只能查看自己的账单和支付记录 --- ## 七、例外情况处理 | 例外编号 | 例外场景 | 处理策略 | 错误码 | |---------|---------|---------|--------| | EX-001 | 重复生成账单 | 检查feeItemId + ownerId + billPeriod唯一性,已存在则跳过 | 7001 | | EX-002 | 支付金额超过应付金额 | 校验支付金额 <= payableAmount - paidAmount | 7002 | | EX-003 | 已支付账单取消 | 拒绝取消,提示需先退款 | 7003 | | EX-004 | 退款金额超过支付金额 | 校验退款金额 <= 原支付金额 | 7004 | | EX-005 | 重复支付 | 状态校验,已支付/已退款的支付记录不可再次确认 | 7005 | | EX-006 | 能耗数据缺失 | 按用量计费时无对应能耗记录,生成0元账单并标记异常 | 7006 | | EX-007 | 业主无关联房产 | 生成账单时业主无房产关联,跳过并记录日志 | 7007 | | EX-008 | 线上支付超时 | 支付状态保持PENDING,由对账任务处理 | 7008 | | EX-009 | 线上支付回调异常 | 记录原始回调数据,人工介入处理 | 7009 | | EX-010 | 滞纳金计算溢出 | lateFee不超过maxLateFee上限 | 7010 | | EX-011 | 收费项目被禁用后自动出账 | 跳过已禁用的收费项目 | 7011 | | EX-012 | 批量生成部分失败 | 记录成功/失败数量和失败原因,返回BatchResult | 7012 |