Appearance
新财务结算体系数据模型
1. 设计裁决
本设计直接建立新体系,不修改旧 Finance V2 表的业务语义,也不迁移旧财务派生数据。 新表统一使用 rt_settlement_* 前缀,与旧 rt_finance_*、rt_monthly_statement*、 rt_payout_* 隔离。
新体系允许读取以下业务源事实:订单、订单项、履约、物流费用、税务单据、退款、汇率、 费率配置、租户与用户。旧 Finance V2 的记录、快照、月结单、事件和发款任务不是新体系 的输入。未来首期账只接收 finance_ready_at 不早于 2026-08-01 00:00:00 Asia/Shanghai 的订单终态事实;本轮不运行真实交接,这也不是旧账迁移。
新体系的数据主体始终是卖家(tenant_id)。物流费用、运单和赔付只作为卖家订单的 来源证据或账单科目,不引入 logistics_provider_id 维度的账单、应付、确认、付款、 余额或结算状态。物流方结算不得复用 rt_settlement_statement 及其状态机;如未来需要, 应另立领域模型和任务。
金额统一为 TWD 主单位,落账金额使用 numeric(20,2);原币证据使用 numeric(20,6),汇率使用 numeric(24,12)。API 返回两位小数字符串,计算使用 BigNumber。所有业务时间使用 timestamptz,结算期使用 date 保存月份首日,禁止用 无约束的 text YYYY-MM 代替日期。
2. 分层与关系
text
业务源事实
└─ rt_order.finance_ready_at(订单域完成后的唯一财务交接时间)
└─ rt_settlement_record(每个卖家订单一条财务主记录)
├─ rt_settlement_blocker(可同时存在多个暂停原因)
└─ rt_settlement_snapshot(追加式计算版本)
└─ rt_settlement_snapshot_line(逐科目、逐证据金额行)
rt_settlement_statement(卖家 × 结算期冻结账单)
├─ rt_settlement_statement_order(绑定 record + snapshot)
├─ rt_settlement_statement_adjustment(绑定追加调整)
└─ rt_settlement_statement_event(生成、确认、平台结算、导出审计)
rt_settlement_adjustment(处罚、补贴、人工差异、冲正)
└─ original_adjustment_id(追加式冲正链)rt_settlement_statement 只表示平台对卖家的账单。物流费行即使带有物流商来源标识, 也只用于来源追溯,不构成物流方账户或应付记录。本期不建设发款表或发款状态机。
订单快照与账单快照是两层不同冻结:订单快照回答“该订单当时怎么算”;账单绑定表回答 “本期最终采用了哪个订单快照”。列表、详情、导出和商店街汇总只能读取账单及其绑定行。
3. 表设计
3.0 订单域交接字段与契约
订单域未来在 rt_order 新增 finance_ready_at timestamptz null,作为允许进入新财务体系的 唯一时间标志。正常订单必须写入与 order.completed_at 完全相同的值;异常订单必须写入 权威异常处理完成时间。空值表示订单尚未完成,不得进入财务。
15 天保护期、退款/取消/赔付等异常是否终结以及异常完成时间均由订单域负责。当前运行时 派生的 resolution/case_status 没有持久化完成时间,不能作为交接依据;订单域补齐正式 终态事实前,异常订单保持 finance_ready_at=null。财务不得读取签收时间重算 15 天, 也不得使用 updated_at、页面状态或当前时间推断该字段。
交接载荷至少包含 tenant_id、order_id、finance_ready_at、terminal_type、 source_version、last_mile_borne_by、last_mile_borne_source、 last_mile_borne_version、商品收入、平台赔付、物流费、平台费、税费、币种和各金额来源证据。 交接幂等键为 (order_id, finance_ready_at, source_version)。首版只定义此契约,不修改 rt_order、不扫描或消费真实订单;实际字段与生产交接属于后续订单域任务。 finance_ready_at 一旦被财务成功消费不得原地修改;初始交接成功即冻结订单快照,异常 终态不得转换,同一订单不得再次执行初始交接。终态或金额纠正只能通过受控调整/冲销 保留完整历史。
异常 terminal_type 固定为 cancelled_unshipped/cancelled_shipped/logistics_claimed/refunded,详细进入时间和金额规则以 ABNORMAL_ORDER_FINANCE_HANDOFF.md 为准。已发货取消 允许先交接已有事实,后到物流费用通过自动调整补齐;这不改变原 finance_ready_at。
末端责任默认规则固定为正常终态 buyer、异常终态 seller。订单域允许退款等人工处理 流程在异常交接前有审计地改判,改判必须保存操作人、时间、原因、前后值和版本;财务只 冻结最终值,不自行裁决。交接后责任纠正只使用追加调整或冲正,不创建替代初始快照, 也不覆盖已冻结事实。
3.1 rt_settlement_system_config
新体系单例配置,不读取旧 rt_finance_system_setting。
| 字段 | 类型 | 约束/说明 |
|---|---|---|
key | text | PK,固定 seller_settlement_v1 |
cutover_at | timestamptz | 非空,固定新账源事实边界 |
timezone | text | 非空,固定 Asia/Shanghai |
calculation_version | text | 非空,当前默认计算版本 |
created_at / updated_at | timestamptz | 非空 |
3.2 rt_settlement_record
订单级财务主记录,只保存资格、判定和当前版本指针,不保存可变汇总金额。
| 字段 | 类型 | 约束/说明 |
|---|---|---|
id | uuid | PK |
tenant_id | uuid | 非空,FK rt_tenant.id |
order_id | uuid | 非空,FK rt_order.id |
record_no | text | 非空,全局唯一公开编号 |
fulfillment_type | text | 非空,首版固定 dropship |
calculation_status | text | awaiting_evidence/ready/blocked |
source_finance_ready_at | timestamptz | 非空,冻结订单域 finance_ready_at |
settlement_period | date | 非空,source_finance_ready_at 所在上海自然月首日 |
source_terminal_type | text | 非空,normal 或异常专项固定终态 |
source_terminal_version | text | 非空,订单终态事实版本 |
last_mile_borne_by | text | 非空,检查为 buyer/seller,冻结交接时最终责任人 |
last_mile_borne_source | text | 非空,terminal_default/manual_override |
last_mile_borne_version | text | 非空,默认或人工改判事实版本 |
current_snapshot_id | uuid | 可空,FK 当前快照;通过延迟 FK 建立 |
source_order_updated_at | timestamptz | 非空,源订单版本水位 |
created_at / updated_at | timestamptz | 非空 |
唯一约束为 (tenant_id, order_id) 和 record_no,另以 (order_id, source_finance_ready_at, source_terminal_version) 保证交接幂等。索引覆盖 (tenant_id, calculation_status, settlement_period) 和 source_finance_ready_at。
3.3 rt_settlement_blocker
一个订单可同时缺税务、重量、汇率和责任版本证据,因此不能继续使用单个 blocking_reason text。
| 字段 | 类型 | 约束/说明 |
|---|---|---|
id | uuid | PK |
record_id / tenant_id | uuid | 非空,FK 主记录/租户 |
code | text | 类型化原因,如 missing_terminal_evidence、missing_tax_result |
status | text | open/resolved |
evidence_type / evidence_id | text | 可空,解决阻断的正式证据键 |
detail | jsonb | 仅放诊断上下文,不保存权威金额 |
detected_at / resolved_at | timestamptz | resolved 时后者必填 |
部分唯一索引保证同一 record_id + code 最多一条 open 记录。只有所有 blocker 关闭且 快照恒等校验通过,主记录才可进入 ready。
3.4 rt_settlement_snapshot
追加式订单计算快照;账单确认后相关版本永久不可更新或删除。
| 字段 | 类型 | 约束/说明 |
|---|---|---|
id / record_id / tenant_id / order_id | uuid | PK 与必要 FK |
version | integer | 从 1 递增,(record_id, version) 唯一 |
goods_income | numeric(20,2) | 商品收入 |
compensation_income | numeric(20,2) | 平台对卖家赔付收入 |
cross_border_shipping_fee | numeric(20,2) | 跨境重量运费 TWD |
actual_shipping_weight_grams | integer | 可空,冻结物流正式出库事实提供的订单实际总重量(g) |
billable_weight_kg | numeric(12,3) | 可空,按合同规则得到的订单计费重量(kg) |
tax_amount | numeric(20,2) | 税费 TWD |
exception_last_mile_fee | numeric(20,2) | 卖家承担异常末端费用 |
platform_fee | numeric(20,2) | 商品与赔付分项舍入后之和 |
total_income / total_expense / net_amount | numeric(20,2) | 冻结合计 |
currency | char(3) | 首版检查为 TWD |
fee_rate | numeric(10,6) | 订单费率快照 |
fee_schedule_id | uuid | 可空,已验证配置来源 |
fee_business_date / fee_timezone | date / text | 费率命中边界证据 |
calculation_version | text | 非空 |
snapshot_hash | text | 非空;规范化输入与行项目哈希 |
calculated_at / created_at | timestamptz | 非空 |
恒等式固定为:total_income = goods_income + compensation_income; total_expense = cross_border_shipping_fee + tax_amount + exception_last_mile_fee + platform_fee; net_amount = total_income - total_expense。数据库检查约束只校验字段符号与基本关系,完整 金额恒等式由事务内 BigNumber 校验和测试保证,避免依赖数据库隐式舍入。
3.5 rt_settlement_snapshot_line
逐科目、逐来源事实的不可变金额证据。汇总字段必须由这些行求和产生。
| 字段 | 类型 | 约束/说明 |
|---|---|---|
id / snapshot_id / record_id / tenant_id | uuid | PK 与 FK |
category | text | goods/compensation/cross_border_shipping/tax/last_mile/platform_fee/rounding |
direction | text | income/expense |
source_type / source_id / source_version | text | 非空,正式事实的稳定幂等键 |
source_amount | numeric(20,6) | 非空,原币金额 |
source_currency | char(3) | 非空 |
exchange_rate_id | uuid | TWD 原币时可空,外币时必填 |
exchange_rate | numeric(24,12) | 外币时必填 |
exchange_rate_effective_at | timestamptz | 外币时必填 |
amount_twd | numeric(20,2) | 非空,方向不通过正负号表达 |
actual_weight_grams | integer | 可空,跨境物流行冻结对应正式包裹实际重量(g) |
parcel_id | uuid | 可空,跨境物流重量行必须关联正式包裹 |
rule_version | text | 非空,计费/税务/费率规则版本 |
occurred_at / effective_at | timestamptz | 事实发生与财务生效时间 |
evidence | jsonb | 可审计副本;不能替代上述类型化字段 |
created_at | timestamptz | 非空 |
唯一约束 (snapshot_id, category, source_type, source_id, source_version)。外币字段完整性用 检查约束保证。初始快照及其 line 在交接后不可修改;纠错使用 adjustment/reversal。 actual_shipping_weight_grams 必须由所绑定跨境物流行的 actual_weight_grams 求和产生, 来源固定为正式 SHIP_PACK_OUT 包裹事实,不得使用商品预估重量或入库重量替代。无物流段、 未发货取消或正式重量尚未产生时保持 null,不得用 0 混淆“没有重量”与“零重量”。 category=cross_border_shipping 的初始快照行必须同时填写 parcel_id 和正整数 actual_weight_grams;同一正式包裹只能在同一订单快照中计入一次。
3.6 rt_settlement_statement
卖家月结账单头。三条状态轴完全独立。
| 字段 | 类型 | 约束/说明 |
|---|---|---|
id / tenant_id | uuid | PK,租户 FK |
statement_no | text | 全局唯一公开编号 |
period | date | 月份首日,(tenant_id, period) 唯一 |
period_start_at / period_end_at | timestamptz | 左闭右开冻结边界 |
timezone | text | 固定 Asia/Shanghai |
generation_status | text | generating/ready/failed |
seller_confirmation_status | text | unconfirmed/confirmed |
platform_settlement_status | text | pending/settled |
order_count | integer | 非负 |
order_amount / compensation_amount | numeric(20,2) | 收入汇总 |
logistics_fee / platform_fee / tax_amount | numeric(20,2) | 支出汇总 |
prior_refund_amount | numeric(20,2) | 首版固定 0.00,为待接入页面/模板保留 |
penalty_amount | numeric(20,2) | 非负扣减汇总 |
other_adjustment_amount | numeric(20,2) | 有符号调整汇总,增加为正、扣减为负 |
final_settlement_amount | numeric(20,2) | 允许为负,不钳位为零 |
currency | char(3) | 固定 TWD |
calculation_version / snapshot_hash | text | 非空 |
generation_run_id | uuid | 非空,同一次生成追踪键 |
failure_code / failure_detail | text / jsonb | 仅失败时填写 |
generated_at | timestamptz | ready 时必填 |
seller_confirmed_by / seller_confirmed_at | uuid / timestamptz | confirmed 时必填 |
platform_settled_by / platform_settled_at | uuid / timestamptz | settled 时必填 |
platform_settlement_note | text | 可空,平台线下结算人工确认备注 |
created_at / updated_at | timestamptz | 非空 |
最终金额:`order_amount + compensation_amount - logistics_fee - platform_fee - tax_amount
- penalty_amount + other_adjustment_amount
。prior_refund_amount首版固定为0.00`,不参与 实际业务扣减;其他费用展示汇总为非负绝对值。确认后,账单头金额、绑定行和 hash 禁止更新。
3.7 rt_settlement_statement_order
账单采用的订单快照绑定表,同时保存导出所需的冻结编号,避免读实时订单后字段漂移。
| 字段 | 类型 | 约束/说明 |
|---|---|---|
id / statement_id / record_id / snapshot_id / tenant_id | uuid | PK 与 FK |
order_id | uuid | FK 源订单 |
order_no / record_no | text | 冻结展示编号 |
finance_ready_at | timestamptz | 非空,从 record 冗余冻结 |
| 各金额字段 | numeric(20,2) | 从绑定 snapshot 冗余冻结,便于恒等与导出 |
created_at | timestamptz | 非空 |
唯一约束 (statement_id, record_id)、snapshot_id。单个 snapshot 只能进入一张账单;同一 record 的后期差异必须走 adjustment,不能重复绑定另一张订单账单。
3.8 rt_settlement_adjustment
独立、追加式调整台账。处罚、补贴、人工差异和冲正共用会计结构,但保留明确类型。
| 字段 | 类型 | 约束/说明 |
|---|---|---|
id / tenant_id | uuid | PK 与租户 FK |
adjustment_no | text | 全局唯一公开编号 |
type | text | penalty/subsidy/manual_difference/late_logistics/reversal;首版无 prior_refund/carry_forward |
direction | text | increase/decrease 卖家应结金额 |
source_type / source_id / source_version | text | 非空,来源幂等键 |
order_id / record_id / original_statement_id | uuid | 可空,存在时必须 FK |
original_adjustment_id | uuid | reversal 必填,FK 原调整 |
effective_period | date | 目标月份首日 |
amount | numeric(20,2) | 严格大于 0,方向单独表达 |
currency | char(3) | 固定 TWD;外币先按证据折算 |
reason | text | 非空 |
status | text | pending/bound/reversed |
created_by_type / created_by | text / uuid | 非空 |
effective_at / created_at | timestamptz | 非空 |
唯一约束 (tenant_id, source_type, source_id, source_version, type)。禁止删除和原地改金额; 撤销必须新建反方向、同金额的 reversal,且一个原调整最多一条有效冲正。订单级调整的 order_id + record_id 必填;店铺级处罚、补贴和人工差异允许为空;账单级调整必须填写 original_statement_id。reversal 必须继承原调整的 tenant、订单和原账单关系。
late_logistics 必须绑定订单、record、物流履约/物流段和费用来源。原账单未确认时可绑定 原账单并重算账单汇总,但不得修改初始订单快照;原账单已确认时只能进入之后最早的 未确认期。来源幂等键至少包含 provider + fulfillment + segment + fee_type + source_version。
3.9 rt_settlement_statement_adjustment
账单与调整的不可变绑定,避免把 effective_statement_id 写回调整事实造成历史归属被改写。 字段为 id、statement_id、adjustment_id、tenant_id、type、direction、amount、 currency、created_at。adjustment_id 全局唯一;金额与方向从调整冗余冻结并参与账单 恒等校验。
3.10 rt_settlement_statement_event
字段为 id、statement_id、tenant_id、event_type、axis、from_status、 to_status、actor_type、actor_id、batch_id、reason、request_id、metadata、 created_at。事件类型覆盖生成开始/成功/失败、卖家确认、平台结算、导出和批量操作; 平台结算事件可在 reason 保存本次可选备注,不存在平台结算退回事件。事件只追加不更新; 索引 (statement_id, created_at)、(batch_id, created_at)。
4. 外键、租户与不可变规则
- 新体系内部及对
rt_tenant、rt_order的稳定 UUID 关系使用数据库 FK;删除策略一律RESTRICT,不使用级联删除财务历史。 - 物流费用、税务文档、退款等跨域且可能多类型的来源,使用
source_type/source_id/source_version类型化证据键;生成时验证真实来源,不能假装存在 多态 FK。 - 所有子表冗余
tenant_id,写入事务必须同时校验父子租户一致;关键绑定可使用复合唯一 键与复合 FK 防止跨租户串账。 - 已确认 statement、已绑定 snapshot/line/adjustment、所有 event 只读。更正只能追加新 snapshot、adjustment 或 reversal。
jsonb只保存诊断、原始证据副本和非检索扩展信息。金额、币种、状态、来源键、版本、 汇率、业务时间与承担方不得只存在 JSON 中。
5. 生成事务与幂等
- 以
(tenant_id, period)获取事务级 advisory lock。 - 创建或锁定
generating账单;同一租户同一期唯一。 - 选择
settlement_period命中且无 open blocker 的已交接 record,固定其 current snapshot。 - 绑定订单快照与
pendingadjustment;每个 snapshot/adjustment 的唯一约束防重复。 - 从绑定行聚合账单头,执行逐科目与最终金额恒等校验。
- 生成规范化
snapshot_hash,写事件并切换为ready后提交。 - 任一步失败整事务回滚;另行记录失败运行结果,再创建/更新
failed账单及失败事件。
卖家确认使用条件更新 ready + unconfirmed -> confirmed。平台结算使用条件更新 confirmed + pending -> settled 并同事务写事件;settled 不可退回。批量平台结算全成 或全败,批量记录共用本次可选备注。settled 表示平台财务已完成线下结算并人工确认。 账单允许为负,本系统不创建发款或自动结转记录。
6. 不迁移与切换边界
- 建表迁移只创建
rt_settlement_*表、约束和索引,不ALTER、DELETE、TRUNCATE、INSERT ... SELECT旧 Finance V2 表。 - 不提供旧财务数据 backfill。首期生成任务只从业务源事实计算 2026 年 8 月及以后新账。
- 首版进一步不运行真实订单交接或账单生成;订单域补齐
finance_ready_at与异常终态时间、 完成当前completed_at写入审计后,才能另立任务启用增量交接。 - 新旧 API 在验收期必须使用不同路由,防止误把旧 ID 传入新体系。
- 切换流量、旧体系停写、历史查询保留期限与最终删表属于后续任务,均需要单独授权。
7. 实施前仍需裁决的阻断项
以下问题未确定时,对应订单保留 open blocker,不能通过默认值生成账单:
- 现有
completed_at是否全部满足“正常订单完成并可进入财务”的语义; - 异常订单正式终态与权威异常处理完成时间的持久化来源;
- 计费重和 0.5kg 阶梯的物流合同输出;
- 零税/免税正式终态;
- 末端物流段方向与确认状态,以及终态默认责任和人工改判的正式版本;
- 平台对卖家物流赔付的正式事实表;赔付币种首版固定为 TWD。
多订单或跨卖家合包不做包裹费用分摊:每张订单使用自身正式计费重量独立套用完整公式, 包括各自的固定费用。平台费按商品收入与赔付收入两个分项分别四舍五入至两位后求和, 不按合并基数重算,也不做尾差再分摊。负余额保持线下处理,不属于实施阻断项。
8. B1 验收
- Drizzle schema 与建表 SQL 只出现新
rt_settlement_*对象,不写旧财务表。 - 所有状态、币种、金额精度、日期和外币字段完整性具备检查约束。
- FK、唯一约束、部分索引与跨租户防护测试通过。
- 重复生成、重复来源事实、重复 adjustment、重复 reversal 均被数据库约束拒绝。
- 已确认账单与已绑定快照的不可变性由 service 条件更新和集成测试共同证明。
- 首版 schema/代码不得接入真实订单;文档契约验证覆盖正常、异常、空值与重复交接场景。