14 KiB
财务领域模型与跨服务契约(历史合并稿)
文档状态:已被拆分替代,仅保留历史设计过程,不作为后续实施依据
替代文档:财务领域模型与数据字典、Firstleg-Finance 跨服务契约
原版本:F0-V1.0(历史稿)
日期:2026-08-10
适用:finance-serviceF0,以及 Firstleg → Finance 的费用来源接入
本文只定义财务 F0 的领域对象、来源快照和服务间契约,不创建服务、数据库或业务代码。范围和服务边界以财务服务边界设计为准;财务全量产品项仍以财务管理产品确认表为准。
1. 设计原则与现状基线
- Finance 是独立
finance-service,使用独立baiyue_finance数据库;不得跨库 Join、共享 Entity 或让 Firstleg 直接写 Finance。 - Firstleg 是费用事实的所有者:负责测量、计费快照、版本、发布、调整和作废;Finance 只保存来源引用、不可变快照及财务状态。
- 当前 Firstleg 已有
fl_inbound_fee_detail和fl_inbound_outbox。费用发布会产生sourceType=FBA_PRICING、sourceFeeId/sourceFeeVersion、direction=RECEIVABLE、币种和金额等字段;自动发布的settlementIncluded=true。 - 当前 Outbox 已写入
FIRSTLEG_FEE_SENT、FIRSTLEG_FEE_ADJUSTED,负载仅含inboundId/feeId(/feeVersion),仓库中尚未形成 Finance 专用发布器或内部来源查询接口。它们是迁移基础,不是本契约的完整 v1 事件。 - Firstleg 现有业务错误遵循 HTTP 200 + body
code(成功为字符串"200");认证、网关和基础设施拒绝可使用真实 401/403/503。内部接口沿用该业务响应封装,服务令牌失败使用 401/403。
2. F0 核心对象
Finance 只在自己的库保存以下对象;所有长整型 ID 在 JSON 中以字符串传输,金额以定点十进制字符串传输,币种使用 ISO 4217 三位大写码。
| 对象 | 必要字段/约束 | 生命周期与归属 |
|---|---|---|
FinancialEntity 财务主体 |
entityId、主体名称快照、主体类型、默认币种、状态、版本 |
Finance 维护;主体、账套、客户隔离键分开建模。主体是否至少平台收款/付款两个方向为产品门禁 F0-02 |
CounterpartyRef 往来单位引用 |
counterpartyType=CUSTOMER/SUPPLIER、外部 ID、名称快照、来源服务、快照时间 |
不复制 IAM/Warehouse 实体;引用变更不改历史快照。供应商主数据来源及字段待 F0-03/F2-01 确认 |
CurrencyRef 币种 |
ISO 码、精度、启用状态 | F0 固定三位码和精度;汇率来源、生效时间、版本只预留,自动折算待 F0-05/F4-04 |
AccountingPeriod 财务期间 |
periodId、起止日期、状态 OPEN/CLOSING/CLOSED、关闭人/时间、版本 |
Finance 唯一维护;CLOSED 禁止直接改账,只能补差/冲销 |
DocumentSequence 单据号 |
业务类型(AR/AP/账单/收付款/核销/发票/钱包)、期间、序号、版本 | 服务端发号;同一幂等重试返回原单号,不由客户端生成 |
FeeSource 财务费用来源 |
sourceType、sourceId=feeDetailId、sourceFeeId、sourceVersion、来源单号、客户、方向、业务日、币种、金额、字段快照、sourceHash、入池状态 |
Finance 的不可变接入记录;RECEIVABLE 已发布费用可进入来源池,PAYABLE 需供应商/主体/凭证等产品条件确认 |
FeeSourceLine 来源明细 |
费用编码/名称快照、数量、单位、单价、金额、业务维度和来源明细 ID | 与 FeeSource 同版本冻结;不能用当前 Firstleg 表实时重算历史 |
SourceInbox 事件接收记录 |
eventId、事件类型、契约版本、幂等键、payloadHash、状态 RECEIVED/PROCESSED/RETRY/CONFLICT/DLQ、重试信息、错误码 |
Finance 本地 Inbox;唯一键至少 (eventId) 和 (sourceType,sourceId,sourceVersion) |
FinanceAuditLog 财务审计 |
事件/操作编码、主体/客户范围、操作者、请求/链路 ID、前后快照、结果、错误码、时间 | 追加写、不可物理删除;敏感字段脱敏 |
F1-F4 的账单、应收、付款、钱包、发票和利润对象引用 FeeSource,但不改变 F0 来源快照。一个有效来源版本只能进入一张未作废账单明细;出账后调整产生新来源或补差/冲销记录。
3. 来源入池与快照规则
3.1 入池门禁
Finance 仅接受满足以下技术条件的来源:
- 事件声明来源版本处于 Firstleg 的有效发布版本,来源明细
reviewStatus=APPROVED、publicationStatus=PUBLISHED; settlementIncluded=true,方向为RECEIVABLE;来源未作废、未被有效账单使用;- 客户、入库单、费用明细、来源版本、币种、金额和业务日期齐全,金额非负;
sourceHash可由事件内容与查询返回的规范化快照校验;校验失败不得入账;- Finance 当前期间允许入池。期间关闭不阻止接收事件,但应转为待处理并返回期间错误。
是否允许客户应付、供应商成本、预收、税额、汇率折算及历史费用迁移,不由本技术契约推断,分别受 F0/F2/F3/F4 门禁约束。
3.2 快照内容
Firstleg 内部查询至少返回:来源 ID/版本/单号、入库单 ID/编号、客户 ID/名称/发票抬头、方向、业务日、费用项目编码/名称/分类快照、渠道和目的地、包裹数、实重/体积/计费量/计费单位、单价、币种、金额、审核/发布时间、settlementIncluded、作废/替代关系及 sourceHash。可选的计费规则、价格表和税模式字段作为追溯扩展。
Finance 持久化查询返回的完整快照及接收时的事件元数据。历史账单只引用此快照;Firstleg 后续 SUPERSEDED 不会覆盖已出账明细。
4. Firstleg → Finance 事件契约 v1
4.1 Envelope
事件使用可靠本地 Outbox 发布,v1 消息主题固定为 finance.firstleg.fee.v1,事件体为 JSON:
{
"eventId": "190000000000000001",
"eventType": "FIRSTLEG_FEE_PUBLISHED",
"schemaVersion": 1,
"occurredAt": "2026-08-10T08:00:00Z",
"producer": "firstleg-service",
"tenantId": "10001",
"customerId": "10001",
"sourceType": "INBOUND_FEE_DETAIL",
"sourceId": "200000000000000001",
"sourceFeeId": "300000000000000001",
"sourceVersion": 2,
"sourceFeeNo": "FBAF-778-2",
"inboundId": "778",
"feeDetailId": "200000000000000001",
"direction": "RECEIVABLE",
"businessDate": "2026-08-09",
"currency": "CNY",
"amount": "1280.00",
"sourceHash": "sha256:...",
"traceId": "trace-...",
"requestId": "req-..."
}
必填字段是上例中除 traceId/requestId 外的全部字段;金额必须是字符串,ID 必须是十进制字符串。事件类型固定为:
FIRSTLEG_FEE_PUBLISHED:产生/重新发布一个有效来源版本;FIRSTLEG_FEE_ADJUSTED:替代旧发布版本,必须带supersedesSourceId/supersedesSourceVersion;FIRSTLEG_FEE_VOIDED:作废来源,必须带作废原因和操作者信息。
事件只声明来源索引,不承载可被截断的完整账单字段;Finance 必须随后调用内部查询获取快照。Outbox 记录的 id 作为 eventId,事件入库与 Firstleg 费用状态变更必须在同一事务内完成。
4.2 当前事件兼容与迁移
现有 FIRSTLEG_FEE_SENT 仅允许作为迁移期别名:消费者可解析其 feeId,调用内部查询后映射为 FIRSTLEG_FEE_PUBLISHED,但不得把最小 payload 直接入财务来源。Firstleg 发布正式 v1 后停止新增 FEE_SENT;旧事件重放必须保留原 eventId,不得重新生成导致重复来源。FIRSTLEG_FEE_ADJUSTED 旧 payload 缺少完整替代关系时同样必须查询补齐,否则进入 CONFLICT/DLQ。
5. Finance → Firstleg 内部查询契约
调用方式:Finance 使用服务间令牌,禁止前端访问。v1 接口固定为:
GET /internal/firstleg/v1/finance/fee-sources/{sourceType}/{sourceId}/versions/{sourceVersion}
Authorization: Bearer <service-token>
X-Request-Id: <id>
返回统一业务封装 {code:"200", message, data:{...}};data 为 DTO,不返回 Entity。查询必须按 tenantId 服务端上下文隔离,且校验版本的客户、发布状态、作废/替代关系和 sourceHash。业务成功/失败使用 HTTP 200,由 body code 区分成功、来源不存在和版本/状态冲突;服务身份失败使用 HTTP 401/403,网关或基础设施暂时不可用可使用 HTTP 503。
Finance 不以当前查询结果覆盖已保存快照。查询超时、5xx 或网络错误可重试;明确的 404/状态冲突不可无限重试,应记录错误并进入人工处理。
6. 幂等、乱序、重试与死信
- Inbox 先以
eventId去重,再以(sourceType,sourceId,sourceVersion)防止不同事件重复入池。相同键且 payloadHash 相同,返回首次PROCESSED结果;相同键内容不同,标记CONFLICT,错误码FIN_SOURCE_IDEMPOTENCY_CONFLICT,不得覆盖。 ADJUSTED可能先于PUBLISHED到达。Finance 先保存事件为RETRY,查询完整快照成功后再按来源版本建立替代关系;不能按到达顺序直接入账。旧版本已出账时只生成待处理补差/冲销来源。- v1 默认采用 1m、5m、30m、2h、12h 的指数退避,最多 5 次且允许配置覆盖;每次保存
retryCount/nextRetryAt/lastErrorCode。超过上限进入 DLQ,支持受权限保护的重放;重放沿用原事件 ID 和幂等键。 - 只有来源快照落库、校验通过、Inbox 标记
PROCESSED后,才允许进入费用池;消费失败不得部分写入财务单据。并发消费使用行锁/版本号,避免双写。
7. 错误码与响应语义
错误码是跨服务稳定字符串,message 可本地化且不能作为程序判断依据:
| 错误码 | 语义 | 是否重试 |
|---|---|---|
FIN_SOURCE_NOT_FOUND |
来源或指定版本不存在 | 否 |
FIN_SOURCE_NOT_PUBLISHED |
来源未达到有效发布门禁 | 否 |
FIN_SOURCE_VOIDED |
来源已作废 | 否 |
FIN_SOURCE_HASH_MISMATCH |
事件与快照哈希不一致 | 否,人工处理 |
FIN_SOURCE_IDEMPOTENCY_CONFLICT |
幂等键对应不同内容 | 否,人工处理 |
FIN_SOURCE_VERSION_CONFLICT |
乱序/替代关系冲突 | 否或待人工 |
FIN_PERIOD_CLOSED |
期间已关闭,禁止直接入账/改账 | 否 |
FIN_CURRENCY_MISMATCH |
跨币种合并或核销 | 否 |
FIN_TENANT_SCOPE_DENIED |
客户范围不匹配 | 否 |
FIN_SERVICE_UNAVAILABLE |
Firstleg/消息基础设施暂不可用 | 是 |
FIN_CONSUME_RETRY_EXHAUSTED |
重试耗尽,已进入 DLQ | 仅授权重放 |
8. 权限、客户隔离与审计
Finance 服务端从登录态/服务令牌取得客户范围,绝不信任客户端传入的客户 ID。平台操作 tenantId=0 仅表示平台作用域,访问具体客户仍需目标客户权限;客户用户只能读写自身客户范围。跨客户、跨主体、跨币种和关闭期间操作必须硬拒绝。
v1 权限资源编码固定为(角色如何分配仍待 F0-10):finance:source:view、finance:source:retry、finance:period:manage、finance:document:create、finance:document:review、finance:payment:create、finance:allocation:create、finance:allocation:reverse、finance:invoice:operate、finance:report:export。反核销、付款、关账、红冲/作废和来源重放属于高风险权限,需独立授权,不得复用 Firstleg 费用编辑权限。
每次接收、重试、冲突、重放及后续财务写操作均追加审计:eventId/requestId/traceId、业务客户/主体、操作者及权限域、资源和版本、前后脱敏快照、结果/错误码、时间和服务名。审计写入失败不得回滚已完成的核心交易,但必须告警并可补偿;Token、凭证和敏感附件不得进入日志。
9. 版本与兼容规则
- 本契约当前为
schemaVersion=1。新增可选字段向后兼容;不得复用字段含义、改变枚举语义或把可选字段改为必填。 - 破坏性变更递增主版本并使用新主题/事件类型(例如
...fee.v2),Finance 至少保留 v1 消费窗口;不得静默重写历史事件。 - 事件和来源都以
(sourceType,sourceId,sourceVersion)标识业务版本;sourceHash是对内部查询完整快照计算的 SHA-256,计算时排除sourceHash本身及请求/链路元数据,JSON 字段按字典序、无空白、金额按约定精度、时间统一 UTC,结果前缀为sha256:。同版本哈希不可变。 - Firstleg 新版本替代旧版本时,旧来源仍可查询;Finance 通过新事件的替代字段建立有向版本链。已出账、已核销或已开票记录只能新增补差/冲销/红冲,不更新原金额。
- 合同测试必须覆盖:事件字段完整性、版本链、重复/冲突、乱序、重试耗尽、查询 DTO 脱敏及权限范围。
10. 产品门禁(不得由本技术文档代定)
以下事项在财务管理产品确认表仍为“待确认”,本契约只预留扩展,不视为已决定:
- F0-02 财务主体/账套的实际数量和收付款方向;
- F0-03 往来单位字段和供应商主数据责任;
- F0-05/F4-04 汇率来源、折算日、本位币和汇兑损益;
- F0-08/F2-01 供应商成本、客户应付、PAYABLE 入池条件;
- F0-10 角色分配、制单/复核是否必须分离及金额分级审批;
- F1/F2 账期、账单确认、收付款方式、核销关系和反核销流程;
- F3 钱包资金性质、冻结/退款、税务主体和外部开票平台;
- F4 收入/成本确认时点、分摊算法、利润维度和导出范围。
在上述门禁未关闭前,只能实现可逆的契约校验、来源接收和审计骨架;不得以原型静态数据或当前人工 PAYABLE 明细推导完整应付、钱包、发票或利润业务。
11. 验收清单
- 发布、调整、作废均可追溯到不可变来源版本;Finance 接收后保存完整快照。
- 重复事件不重复生成来源,内容冲突可发现;乱序可重试,失败可进入 DLQ 并授权重放。
- 查询使用服务令牌和 DTO;跨客户/越权/跨币种/关闭期间操作被拒绝。
- ID、金额、币种、哈希和版本符合本契约;所有接收与写操作有审计记录。
- 事件/查询契约冻结后,才进入 F1/F2 财务单据实现;本轮不修改任何代码或其他文档。