Skip to content

新财务结算体系数据模型

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_idorder_idfinance_ready_atterminal_typesource_versionlast_mile_borne_bylast_mile_borne_sourcelast_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

字段类型约束/说明
keytextPK,固定 seller_settlement_v1
cutover_attimestamptz非空,固定新账源事实边界
timezonetext非空,固定 Asia/Shanghai
calculation_versiontext非空,当前默认计算版本
created_at / updated_attimestamptz非空

3.2 rt_settlement_record

订单级财务主记录,只保存资格、判定和当前版本指针,不保存可变汇总金额。

字段类型约束/说明
iduuidPK
tenant_iduuid非空,FK rt_tenant.id
order_iduuid非空,FK rt_order.id
record_notext非空,全局唯一公开编号
fulfillment_typetext非空,首版固定 dropship
calculation_statustextawaiting_evidence/ready/blocked
source_finance_ready_attimestamptz非空,冻结订单域 finance_ready_at
settlement_perioddate非空,source_finance_ready_at 所在上海自然月首日
source_terminal_typetext非空,normal 或异常专项固定终态
source_terminal_versiontext非空,订单终态事实版本
last_mile_borne_bytext非空,检查为 buyer/seller,冻结交接时最终责任人
last_mile_borne_sourcetext非空,terminal_default/manual_override
last_mile_borne_versiontext非空,默认或人工改判事实版本
current_snapshot_iduuid可空,FK 当前快照;通过延迟 FK 建立
source_order_updated_attimestamptz非空,源订单版本水位
created_at / updated_attimestamptz非空

唯一约束为 (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

字段类型约束/说明
iduuidPK
record_id / tenant_iduuid非空,FK 主记录/租户
codetext类型化原因,如 missing_terminal_evidencemissing_tax_result
statustextopen/resolved
evidence_type / evidence_idtext可空,解决阻断的正式证据键
detailjsonb仅放诊断上下文,不保存权威金额
detected_at / resolved_attimestamptzresolved 时后者必填

部分唯一索引保证同一 record_id + code 最多一条 open 记录。只有所有 blocker 关闭且 快照恒等校验通过,主记录才可进入 ready

3.4 rt_settlement_snapshot

追加式订单计算快照;账单确认后相关版本永久不可更新或删除。

字段类型约束/说明
id / record_id / tenant_id / order_iduuidPK 与必要 FK
versioninteger从 1 递增,(record_id, version) 唯一
goods_incomenumeric(20,2)商品收入
compensation_incomenumeric(20,2)平台对卖家赔付收入
cross_border_shipping_feenumeric(20,2)跨境重量运费 TWD
actual_shipping_weight_gramsinteger可空,冻结物流正式出库事实提供的订单实际总重量(g)
billable_weight_kgnumeric(12,3)可空,按合同规则得到的订单计费重量(kg)
tax_amountnumeric(20,2)税费 TWD
exception_last_mile_feenumeric(20,2)卖家承担异常末端费用
platform_feenumeric(20,2)商品与赔付分项舍入后之和
total_income / total_expense / net_amountnumeric(20,2)冻结合计
currencychar(3)首版检查为 TWD
fee_ratenumeric(10,6)订单费率快照
fee_schedule_iduuid可空,已验证配置来源
fee_business_date / fee_timezonedate / text费率命中边界证据
calculation_versiontext非空
snapshot_hashtext非空;规范化输入与行项目哈希
calculated_at / created_attimestamptz非空

恒等式固定为:total_income = goods_income + compensation_incometotal_expense = cross_border_shipping_fee + tax_amount + exception_last_mile_fee + platform_feenet_amount = total_income - total_expense。数据库检查约束只校验字段符号与基本关系,完整 金额恒等式由事务内 BigNumber 校验和测试保证,避免依赖数据库隐式舍入。

3.5 rt_settlement_snapshot_line

逐科目、逐来源事实的不可变金额证据。汇总字段必须由这些行求和产生。

字段类型约束/说明
id / snapshot_id / record_id / tenant_iduuidPK 与 FK
categorytextgoods/compensation/cross_border_shipping/tax/last_mile/platform_fee/rounding
directiontextincome/expense
source_type / source_id / source_versiontext非空,正式事实的稳定幂等键
source_amountnumeric(20,6)非空,原币金额
source_currencychar(3)非空
exchange_rate_iduuidTWD 原币时可空,外币时必填
exchange_ratenumeric(24,12)外币时必填
exchange_rate_effective_attimestamptz外币时必填
amount_twdnumeric(20,2)非空,方向不通过正负号表达
actual_weight_gramsinteger可空,跨境物流行冻结对应正式包裹实际重量(g)
parcel_iduuid可空,跨境物流重量行必须关联正式包裹
rule_versiontext非空,计费/税务/费率规则版本
occurred_at / effective_attimestamptz事实发生与财务生效时间
evidencejsonb可审计副本;不能替代上述类型化字段
created_attimestamptz非空

唯一约束 (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_iduuidPK,租户 FK
statement_notext全局唯一公开编号
perioddate月份首日,(tenant_id, period) 唯一
period_start_at / period_end_attimestamptz左闭右开冻结边界
timezonetext固定 Asia/Shanghai
generation_statustextgenerating/ready/failed
seller_confirmation_statustextunconfirmed/confirmed
platform_settlement_statustextpending/settled
order_countinteger非负
order_amount / compensation_amountnumeric(20,2)收入汇总
logistics_fee / platform_fee / tax_amountnumeric(20,2)支出汇总
prior_refund_amountnumeric(20,2)首版固定 0.00,为待接入页面/模板保留
penalty_amountnumeric(20,2)非负扣减汇总
other_adjustment_amountnumeric(20,2)有符号调整汇总,增加为正、扣减为负
final_settlement_amountnumeric(20,2)允许为负,不钳位为零
currencychar(3)固定 TWD
calculation_version / snapshot_hashtext非空
generation_run_iduuid非空,同一次生成追踪键
failure_code / failure_detailtext / jsonb仅失败时填写
generated_attimestamptzready 时必填
seller_confirmed_by / seller_confirmed_atuuid / timestamptzconfirmed 时必填
platform_settled_by / platform_settled_atuuid / timestamptzsettled 时必填
platform_settlement_notetext可空,平台线下结算人工确认备注
created_at / updated_attimestamptz非空

最终金额:`order_amount + compensation_amount - logistics_fee - platform_fee - tax_amount

  • penalty_amount + other_adjustment_amountprior_refund_amount首版固定为0.00`,不参与 实际业务扣减;其他费用展示汇总为非负绝对值。确认后,账单头金额、绑定行和 hash 禁止更新。

3.7 rt_settlement_statement_order

账单采用的订单快照绑定表,同时保存导出所需的冻结编号,避免读实时订单后字段漂移。

字段类型约束/说明
id / statement_id / record_id / snapshot_id / tenant_iduuidPK 与 FK
order_iduuidFK 源订单
order_no / record_notext冻结展示编号
finance_ready_attimestamptz非空,从 record 冗余冻结
各金额字段numeric(20,2)从绑定 snapshot 冗余冻结,便于恒等与导出
created_attimestamptz非空

唯一约束 (statement_id, record_id)snapshot_id。单个 snapshot 只能进入一张账单;同一 record 的后期差异必须走 adjustment,不能重复绑定另一张订单账单。

3.8 rt_settlement_adjustment

独立、追加式调整台账。处罚、补贴、人工差异和冲正共用会计结构,但保留明确类型。

字段类型约束/说明
id / tenant_iduuidPK 与租户 FK
adjustment_notext全局唯一公开编号
typetextpenalty/subsidy/manual_difference/late_logistics/reversal;首版无 prior_refund/carry_forward
directiontextincrease/decrease 卖家应结金额
source_type / source_id / source_versiontext非空,来源幂等键
order_id / record_id / original_statement_iduuid可空,存在时必须 FK
original_adjustment_iduuidreversal 必填,FK 原调整
effective_perioddate目标月份首日
amountnumeric(20,2)严格大于 0,方向单独表达
currencychar(3)固定 TWD;外币先按证据折算
reasontext非空
statustextpending/bound/reversed
created_by_type / created_bytext / uuid非空
effective_at / created_attimestamptz非空

唯一约束 (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 写回调整事实造成历史归属被改写。 字段为 idstatement_idadjustment_idtenant_idtypedirectionamountcurrencycreated_atadjustment_id 全局唯一;金额与方向从调整冗余冻结并参与账单 恒等校验。

3.10 rt_settlement_statement_event

字段为 idstatement_idtenant_idevent_typeaxisfrom_statusto_statusactor_typeactor_idbatch_idreasonrequest_idmetadatacreated_at。事件类型覆盖生成开始/成功/失败、卖家确认、平台结算、导出和批量操作; 平台结算事件可在 reason 保存本次可选备注,不存在平台结算退回事件。事件只追加不更新; 索引 (statement_id, created_at)(batch_id, created_at)

4. 外键、租户与不可变规则

  • 新体系内部及对 rt_tenantrt_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. 生成事务与幂等

  1. (tenant_id, period) 获取事务级 advisory lock。
  2. 创建或锁定 generating 账单;同一租户同一期唯一。
  3. 选择 settlement_period 命中且无 open blocker 的已交接 record,固定其 current snapshot。
  4. 绑定订单快照与 pending adjustment;每个 snapshot/adjustment 的唯一约束防重复。
  5. 从绑定行聚合账单头,执行逐科目与最终金额恒等校验。
  6. 生成规范化 snapshot_hash,写事件并切换为 ready 后提交。
  7. 任一步失败整事务回滚;另行记录失败运行结果,再创建/更新 failed 账单及失败事件。

卖家确认使用条件更新 ready + unconfirmed -> confirmed。平台结算使用条件更新 confirmed + pending -> settled 并同事务写事件;settled 不可退回。批量平台结算全成 或全败,批量记录共用本次可选备注。settled 表示平台财务已完成线下结算并人工确认。 账单允许为负,本系统不创建发款或自动结转记录。

6. 不迁移与切换边界

  • 建表迁移只创建 rt_settlement_* 表、约束和索引,不 ALTERDELETETRUNCATEINSERT ... 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/代码不得接入真实订单;文档契约验证覆盖正常、异常、空值与重复交接场景。