4.7 KiB
title, aliases, tags, audience, status, sub_feature, last_review, code_version
| title | aliases | tags | audience | status | sub_feature | last_review | code_version | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| prop-acc · deposit · 押金账户与押金流水 |
|
|
|
已发布 | deposit | 2026-05-25 | 2026-05-22 |
押金账户与押金流水
保证金模块底层是两个对象配合的:账户(DepositAccount)只记当前状态,流水(DepositTransaction)记每一笔变动。
为什么要两个对象
[!info] 类比:银行存折
- 账户 = 存折封面那一行"当前余额 ¥5,000"
- 流水 = 翻开存折每一页:几号存了多少、几号取了多少、每笔的"前余额→后余额"
如果只有账户没有流水,查到余额是 ¥3,000 你不知道钱从哪里来的、被扣了什么。审计、业户对账、纠纷复盘全都失据。
如果只有流水没有账户,每次想知道当前余额都要重新累加全部历史,慢且容易脏。
所以两个都要:账户给你"现在长什么样",流水给你"如何变成现在这样"。
字段速查
DepositAccount(账户)
| 字段 | 含义 |
|---|---|
id |
账户 ID |
community_id |
所属物业项目 |
fee_type_id |
押金种类(装修保证金 / 入驻押金 / ...) |
payer_type |
缴款人类型(详见 payer-types) |
payer_name |
缴款人姓名(冗余存,免关联 user 表) |
payer_contact |
缴款人联系方式 |
community_user_profile_id |
业户档案 ID(若缴款人是平台业户) |
asset_id |
关联房屋单元(若与具体房屋相关) |
balance |
当前余额(单一事实来源) |
status |
账户状态(详见 account-state-machine) |
opened_at |
开户时间 |
meta |
JSON 扩展字段(force_closed_* 等审计标记) |
DepositTransaction(流水)
| 字段 | 含义 |
|---|---|
id |
流水 ID |
deposit_account_id |
归属账户 |
type |
流水类型(详见 transaction-types) |
amount |
本笔金额(正数;退款/扣罚也是正数,方向由 type 表达) |
balance_before |
本笔之前账户余额 |
balance_after |
本笔之后账户余额 |
related_collection_order_id |
关联收款单(deposit / refund / forfeiture 都关联) |
memo |
备注 |
operated_by |
操作员 ID |
| 创建后不可变 | 一旦生成就只读,任何"撤销"都建新流水反向冲 |
两者的契约
账户.balance 必须等于流水按时间累加的净值。
$account->verifyBalance(); // bool,看是否一致
$account->getBalanceDifference(); // float,差额(0 才对)
$account->calculateBalanceFromTransactions(); // 现场重算
这是审计的核心校验。日常运行中两者必须一致;若出现不一致只可能是 bug 或人为绕过(已通过 account-state-machine 守护和 Policy 双重防御)。
与一次性收费的 CollectionOrder + Receipt 关系
保证金的每笔流水也会建一张 CollectionOrder 和 Receipt(详见 collection-order-and-receipt),退款 / 扣罚走红字 CollectionOrder(详见 red-receipt-design)。
flowchart LR
A[业户缴款 5000] --> B[DepositTransaction<br/>type=deposit, amount=5000]
A --> C[CollectionOrder<br/>actual=5000]
C --> D[Receipt<br/>amount=5000]
B -.关联.-> C
E[退款 5000] --> F[DepositTransaction<br/>type=refund, amount=5000]
E --> G[CollectionOrder<br/>actual=-5000 红字]
G --> H[Receipt<br/>amount=-5000 红字]
F -.关联.-> G
为什么不只用 DepositTransaction?因为业户要拿到一张可下载、可打印的收据凭证 —— 那是 Receipt 的职责。DepositTransaction 是内部台账,Receipt 是对外凭证。
业户视角(您看到什么)
业户通常不直接接触账户/流水概念。您看到的是:
- 物业前台告诉您"您还有 ¥5,000 装修保证金在账"
- 装修完了物业给您一张红字收据:"装修保证金退还 ¥-5,000"
- 微信小程序"我的账户"里能查到余额和历次变动
底下的两个对象都是后台的事。
业务人员视角
后台 → 保证金 → 账户列表,你看到的每行就是一个 DepositAccount。点开"查看"进入 ViewDepositAccount,右侧的"流水"标签就是该账户的 DepositTransaction 列表(只读、按时间倒序)。
所有写入操作(DepositAction / RefundAction / ForfeitureAction 等)都同时写账户余额 + 流水,事务内完成。任何只写一边的代码都是 bug。