ether-docs/02-DESIGN/domains/04-FINANCE.md

444 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 财务计费领域技术方案
**领域编号**: 4.4
**微服务**: ether-finance
**最后更新**: 2026-02-10
---
## 一、领域概述
### 1.1 领域职责
财务计费领域负责物业费用管理全生命周期:
- 收费项目管理(物业费、停车费、水电费等)
- 账单生成与管理
- 支付处理与对账
- 费用催缴通知
- 财务报表与统计
### 1.2 核心概念
| 概念 | 说明 | 对应实体 |
|------|------|----------|
| **收费项目** | 可收费的项目定义 | FeeItem |
| **账单** | 应收费用单据 | FeeBill |
| **支付记录** | 支付流水记录 | FeePayment |
| **退款** | 退款申请与处理 | FeeRefund |
---
## 二、领域模型
### 2.1 聚合根设计
#### FeeItem收费项目
```java
@Entity
@Table(name = "fin_fee_item")
@Data
public class FeeItem {
@Id
private UUID id;
private UUID projectId;
private String code; // 项目编码
private String name; // 项目名称
private FeeType type; // PROPERTY/PARKING/WATER/ELECTRICITY/GAS/OTHER
// 计费规则
private BillingMethod billingMethod; // FIXED/AREA/USAGE/CUSTOM
private BigDecimal unitPrice; // 单价
private String unit; // 单位: 元/月、元/㎡·月、元/度
// 账期
private Integer billDay; // 账单日(每月几号)
private Integer dueDay; // 到期日(每月几号)
private Integer overdueDay; // 逾期日(每月几号)
// 滞纳金
private Boolean enableLateFee;
private BigDecimal lateFeeRate; // 滞纳金比例(日利率)
private BigDecimal maxLateFee; // 滞纳金上限
// 状态
private Boolean enabled;
// 审计字段
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
// 收费类型枚举
public enum FeeType {
PROPERTY("物业费"),
PARKING("停车费"),
WATER("水费"),
ELECTRICITY("电费"),
GAS("燃气费"),
HEATING("供暖费"),
REPAIR_FUND("维修基金"),
OTHER("其他");
}
// 计费方式枚举
public enum BillingMethod {
FIXED("固定金额"),
AREA("按面积"),
USAGE("按用量"),
CUSTOM("自定义");
}
```
#### FeeBill收费账单
```java
@Entity
@Table(name = "fin_fee_bill")
@Data
public class FeeBill {
@Id
private UUID id;
private UUID projectId;
private String billNo; // 账单编号
// 关联信息
private UUID feeItemId; // 收费项目
private UUID spaceNodeId; // 关联房产
private UUID ownerId; // 关联业主
// 账期
private String billPeriod; // 账期: 2024-02
private LocalDate billDate; // 账单日期
private LocalDate dueDate; // 到期日期
private LocalDate overdueDate; // 逾期日期
// 用量(按用量计费)
private BigDecimal usageAmount; // 用量
private String usageUnit; // 用量单位
// 金额
private BigDecimal amount; // 应收金额
private BigDecimal lateFee; // 滞纳金
private BigDecimal discount; // 优惠金额
private BigDecimal payableAmount; // 应付金额 = amount + lateFee - discount
private BigDecimal paidAmount; // 已付金额
// 状态
private BillStatus status; // UNPAID/PARTIAL_PAID/PAID/OVERDUE/CANCELLED
// 关联支付记录
@OneToMany(mappedBy = "bill")
private List<FeePayment> payments;
// 审计字段
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
// 账单状态枚举
public enum BillStatus {
UNPAID("未缴费"),
PARTIAL_PAID("部分缴费"),
PAID("已缴费"),
OVERDUE("已逾期"),
CANCELLED("已取消");
}
```
#### FeePayment支付记录
```java
@Entity
@Table(name = "fin_fee_payment")
@Data
public class FeePayment {
@Id
private UUID id;
private UUID billId;
private String paymentNo; // 支付流水号
private BigDecimal amount; // 支付金额
// 支付方式
private PaymentMethod method; // WECHAT/ALIPAY/CASH/BANK_TRANSFER/CARD
private String thirdPartyNo; // 第三方支付流水号
// 支付状态
private PaymentStatus status; // PENDING/SUCCESS/FAILED/REFUNDED
private String failReason; // 失败原因
// 时间
private LocalDateTime paymentTime;
private String remark;
// 操作人
private UUID operatorId;
private String operatorName;
// 审计字段
private LocalDateTime createdAt;
}
// 支付方式枚举
public enum PaymentMethod {
WECHAT("微信支付"),
ALIPAY("支付宝"),
CASH("现金"),
BANK_TRANSFER("银行转账"),
CARD("刷卡");
}
```
#### FeeRefund退款记录
```java
@Entity
@Table(name = "fin_fee_refund")
@Data
public class FeeRefund {
@Id
private UUID id;
private UUID projectId;
private UUID billId;
private UUID paymentId; // 关联支付记录
private String refundNo; // 退款单号
private BigDecimal amount; // 退款金额
private String reason; // 退款原因
// 状态
private RefundStatus status; // PENDING/APPROVED/REJECTED/REFUNDED
// 审批
private UUID approverId;
private String approverName;
private LocalDateTime approveTime;
private String approveRemark;
// 退款执行
private String thirdPartyRefundNo;
private LocalDateTime refundTime;
// 审计字段
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
```
---
## 三、费用催缴
### 3.1 定时任务
```java
@Component
public class FeeReminderJob {
@Autowired
private FeeBillRepository feeBillRepository;
@Autowired
private NotificationService notificationService;
// 每天9点执行提醒即将到期
@Scheduled(cron = "0 0 9 * * ?")
public void remindUpcomingDue() {
// 查询3天内到期的账单
LocalDate targetDate = LocalDate.now().plusDays(3);
List<FeeBill> upcomingBills = feeBillRepository
.findByDueDateBetweenAndStatus(LocalDate.now(), targetDate, BillStatus.UNPAID);
for (FeeBill bill : upcomingBills) {
notificationService.sendFeeReminder(bill, "FEE_UPCOMING_DUE");
}
}
// 每天9点和18点执行催缴逾期
@Scheduled(cron = "0 0 9,18 * * ?")
public void remindOverdue() {
// 查询已逾期的账单
List<FeeBill> overdueBills = feeBillRepository
.findByOverdueDateBeforeAndStatus(LocalDate.now(), BillStatus.UNPAID);
for (FeeBill bill : overdueBills) {
// 计算滞纳金
calculateLateFee(bill);
notificationService.sendFeeReminder(bill, "FEE_OVERDUE");
}
}
// 每周一9点执行发送汇总
@Scheduled(cron = "0 0 9 ? * MON")
public void sendWeeklySummary() {
// 发送本周待缴费汇总
}
private void calculateLateFee(FeeBill bill) {
if (!bill.getEnableLateFee()) {
return;
}
// 计算逾期天数
long overdueDays = ChronoUnit.DAYS.between(bill.getOverdueDate(), LocalDate.now());
if (overdueDays <= 0) {
return;
}
// 计算滞纳金
BigDecimal lateFee = bill.getAmount()
.multiply(bill.getLateFeeRate())
.multiply(BigDecimal.valueOf(overdueDays));
// 不超过上限
if (bill.getMaxLateFee() != null && lateFee.compareTo(bill.getMaxLateFee()) > 0) {
lateFee = bill.getMaxLateFee();
}
bill.setLateFee(lateFee);
bill.setPayableAmount(bill.getAmount().add(lateFee).subtract(bill.getDiscount()));
feeBillRepository.save(bill);
}
}
```
---
## 四、实现状态与差异
### 4.1 实现状态
| 功能模块 | 实现状态 | 备注 |
|---------|---------|------|
| FeeItem | 🟢 已实现 | 基础CRUD |
| FeeBill | 🟢 已实现 | 基础CRUD |
| FeePayment | 🟢 已实现 | 基础CRUD |
| 费用催缴 | 🟢 已实现 | 定时任务 |
| 滞纳金计算 | 🟢 已实现 | 自动计算 |
| 退款功能 | 🔴 未实现 | 待开发 |
| 财务报表 | 🔴 未实现 | 待开发 |
| 支付网关 | 🔴 未实现 | 待对接 |
### 4.2 与设计方案的差异
| 设计项 | 设计方案 | 现有实现 | 差异分析 |
|--------|----------|----------|----------|
| **实体归属** | ether-finance | ether-finance + ether-mdm重复 | ❌ 存在重复实体 |
| **支付网关** | 对接微信/支付宝 | 未对接 | 🔴 功能缺失 |
| **退款** | 退款流程 | 未实现 | 🔴 功能缺失 |
### 4.3 改进计划
| 优先级 | 改进项 | 说明 |
|--------|--------|------|
| P1 | 清理重复实体 | 删除ether-mdm中的Fee相关实体 |
| P2 | 实现退款功能 | 退款申请、审批、执行流程 |
| P2 | 对接支付网关 | 集成微信支付、支付宝SDK |
| P2 | 财务报表 | 收费统计、欠费分析、收入趋势 |
---
## 五、数据库表结构
```sql
-- 收费项目表
CREATE TABLE fin_fee_item (
id UUID PRIMARY KEY,
project_id UUID NOT NULL,
code VARCHAR(50) NOT NULL,
name VARCHAR(100) NOT NULL,
type VARCHAR(20) NOT NULL,
billing_method VARCHAR(20) NOT NULL,
unit_price NUMERIC(12,2),
unit VARCHAR(20),
bill_day INTEGER,
due_day INTEGER,
overdue_day INTEGER,
enable_late_fee BOOLEAN DEFAULT FALSE,
late_fee_rate NUMERIC(5,4),
max_late_fee NUMERIC(12,2),
enabled BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
UNIQUE(project_id, code)
);
-- 账单表
CREATE TABLE fin_fee_bill (
id UUID PRIMARY KEY,
project_id UUID NOT NULL,
bill_no VARCHAR(32) NOT NULL,
fee_item_id UUID NOT NULL,
space_node_id UUID,
owner_id UUID,
bill_period VARCHAR(10) NOT NULL,
bill_date DATE NOT NULL,
due_date DATE NOT NULL,
overdue_date DATE,
usage_amount NUMERIC(12,2),
usage_unit VARCHAR(20),
amount NUMERIC(12,2) NOT NULL,
late_fee NUMERIC(12,2) DEFAULT 0,
discount NUMERIC(12,2) DEFAULT 0,
payable_amount NUMERIC(12,2) NOT NULL,
paid_amount NUMERIC(12,2) DEFAULT 0,
status VARCHAR(20) NOT NULL DEFAULT 'UNPAID',
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
UNIQUE(project_id, bill_no)
);
-- 支付记录表
CREATE TABLE fin_fee_payment (
id UUID PRIMARY KEY,
bill_id UUID NOT NULL,
payment_no VARCHAR(64) NOT NULL,
amount NUMERIC(12,2) NOT NULL,
method VARCHAR(20) NOT NULL,
third_party_no VARCHAR(100),
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
fail_reason VARCHAR(500),
payment_time TIMESTAMP,
remark VARCHAR(500),
operator_id UUID,
operator_name VARCHAR(100),
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- 退款记录表
CREATE TABLE fin_fee_refund (
id UUID PRIMARY KEY,
project_id UUID NOT NULL,
bill_id UUID NOT NULL,
payment_id UUID NOT NULL,
refund_no VARCHAR(64) NOT NULL,
amount NUMERIC(12,2) NOT NULL,
reason VARCHAR(500),
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
approver_id UUID,
approver_name VARCHAR(100),
approve_time TIMESTAMP,
approve_remark VARCHAR(500),
third_party_refund_no VARCHAR(100),
refund_time TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- 创建索引
CREATE INDEX idx_fee_item_project ON fin_fee_item(project_id);
CREATE INDEX idx_fee_bill_project ON fin_fee_bill(project_id);
CREATE INDEX idx_fee_bill_status ON fin_fee_bill(status);
CREATE INDEX idx_fee_bill_owner ON fin_fee_bill(owner_id);
CREATE INDEX idx_fee_bill_due_date ON fin_fee_bill(due_date);
CREATE INDEX idx_fee_payment_bill ON fin_fee_payment(bill_id);
```
---
**文档维护**: 本领域技术方案由 ether-finance 服务负责人维护