409 lines
25 KiB
Markdown
409 lines
25 KiB
Markdown
# 财务来源接入现状
|
||||
|
|
|
|||
|
|
> 盘点日期:2026-08-10
|
|||
|
|
> 盘点范围:`baiyue-firstleg/`、`apps/admin-web/`
|
|||
|
|
> 盘点方式:只读核对 Controller、DTO、Service、Entity、SQL、OpenAPI、前端 API/类型/页面和测试
|
|||
|
|
> 用途:为 Firstleg → Finance F0 的来源接入设计提供代码现状基线,不代表 Finance 已实现
|
|||
|
|
|
|||
|
|
## 一、结论摘要
|
|||
|
|
|
|||
|
|
当前仓库已经具备可作为 Finance 上游的入库费用事实,但尚未形成可直接接入 Finance 的完整生产链路。
|
|||
|
|
|
|||
|
|
已经具备:
|
|||
|
|
|
|||
|
|
- 入库费用从试算、确认、发布到发布后调价的版本化快照;
|
|||
|
|
- 自动发布的对客应收入库运费明细,以及人工应收/应付费用明细的录入与审核基础;
|
|||
|
|
- 平台公共费用项目主数据;
|
|||
|
|
- 费用写操作幂等、业务审计和 Firstleg 本地 Outbox 落库;
|
|||
|
|
- Admin 的费用项目、入库费用快照、已发布费用调整和统一订单费用明细查询;
|
|||
|
|
- Order 的可运行 Outbox 调度、重试模式,以及 Event Bridge 的事件信封和 Inbox 幂等模式,可作为实现参考。
|
|||
|
|
|
|||
|
|
尚未具备:
|
|||
|
|
|
|||
|
|
- `finance-service`、Finance 数据库、Finance Inbox 和财务来源池;
|
|||
|
|
- Firstleg 面向 Finance 的冻结事件契约和内部来源快照查询接口;
|
|||
|
|
- Firstleg Outbox 的实际扫描、投递、重试、失败/死信处理;
|
|||
|
|
- 费用作废事件,以及已出账后补差/冲销语义;
|
|||
|
|
- 跨入库单的待结算费用池、账单占用/冻结和财务状态回执;
|
|||
|
|
- Finance 独立权限、财务单据、收付款及核销能力。
|
|||
|
|
|
|||
|
|
因此,F0 可以复用当前费用快照、费用明细和本地事务 Outbox 的写入位置,但不能直接把 `fl_inbound_fee_detail` 当作 Finance 台账,也不能把现有 Event Bridge 当作 Firstleg → Finance 的现成消息通道。
|
|||
|
|
|
|||
|
|
## 二、现有费用来源模型
|
|||
|
|
|
|||
|
|
### 2.1 入库计费快照
|
|||
|
|
|
|||
|
|
`fl_inbound_fee` 保存版本化费用头,`fl_inbound_fee_item` 保存费用组成。核心能力如下:
|
|||
|
|
|
|||
|
|
| 能力 | 当前实现 | 可复用价值 | 现有限制 |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| 试算 | `POST /api/platform/v1/inbounds/{inboundId}/fees/calculate` | 保存计费输入、规则/价格版本、计费量和金额快照 | 只面向单个入库单,不是财务来源查询接口 |
|
|||
|
|
| 确认 | `POST .../fees/confirm` | `CALCULATED -> CONFIRMED`,写操作带 `Idempotency-Key` | 确认是业务计费状态,不等于财务审核 |
|
|||
|
|
| 发布 | `POST .../fees/send` | `CONFIRMED -> SENT`,生成对客费用明细并写 Outbox | 方法和事件仍使用 `send/SENT` 旧命名;事件负载过少 |
|
|||
|
|
| 查询最新快照 | `GET .../fees/latest` | Admin 可读取最新业务快照 | 只返回最新版本,不适合 Finance 核验指定历史版本 |
|
|||
|
|
| 发布后调价 | `PUT .../fees/published` | 旧 `SENT` 版本转 `SUPERSEDED`,生成新 `SENT` 版本,保留调整原因并写 Outbox | 没有已出账判断;Finance 上线后不能继续无条件覆盖来源有效版本 |
|
|||
|
|
|
|||
|
|
`FeeSnapshotResponse` 已包含可复用的主要快照字段:
|
|||
|
|
|
|||
|
|
- 标识:`feeId`、`feeNo`、`feeVersion`、`inboundId`、`inboundCode`;
|
|||
|
|
- 状态:`feeStatus`、`nextProcessStatus`;
|
|||
|
|
- 客户与业务:`customerId`、`customerName`、`businessType`;
|
|||
|
|
- 规则与价格:规则/价格 ID、编码、版本、价格表版本及有效期;
|
|||
|
|
- 路由与目的地:起运仓、渠道、承运商、目的地类型/ID/名称/编码;
|
|||
|
|
- 计费依据:实际重量、实际体积、换算量、计费量、单位、换算/舍入/税模式;
|
|||
|
|
- 金额:单价、小计、附加费、总额、币种;
|
|||
|
|
- 时间:试算、确认、发布时间。
|
|||
|
|
|
|||
|
|
费用快照的数据库约束已经提供 `(tenant_id, inbound_id, fee_version)` 唯一键,金额使用定点小数,币种为三位码。这些可继续作为 Firstleg 来源版本的事实基础。
|
|||
|
|
|
|||
|
|
### 2.2 已发布费用明细
|
|||
|
|
|
|||
|
|
`fl_inbound_fee_detail` 是当前最接近财务来源的业务表。自动发布链路会把 `SENT` 费用快照转换为一条入库应收运费明细:
|
|||
|
|
|
|||
|
|
| 字段组 | 已有字段/状态 |
|
|||
|
|
|---|---|
|
|||
|
|
| 来源定位 | `sourceType=FBA_PRICING`、`sourceFeeId`、`sourceFeeVersion`、`sourceFeeNo` |
|
|||
|
|
| 业务定位 | `tenantId`、`inboundId`、`inboundCode`、`businessDate` |
|
|||
|
|
| 客户快照 | `customerId`、`customerName`、`invoiceTitle` |
|
|||
|
|
| 费用快照 | 费用分类、贸易范围、费用名称、方向、币种、单价、金额 |
|
|||
|
|
| 计费依据 | 箱数、重量、体积、体积重、计费量、计费单位 |
|
|||
|
|
| 审核 | `PENDING / APPROVED / REJECTED`,审核人、时间、原因 |
|
|||
|
|
| 发布 | `DRAFT / PUBLISHED / SUPERSEDED`,发布人、时间 |
|
|||
|
|
| 结算候选标识 | `settlementIncluded` |
|
|||
|
|
|
|||
|
|
自动发布的 FBA 头程运费固定为:
|
|||
|
|
|
|||
|
|
- `direction=RECEIVABLE`;
|
|||
|
|
- `reviewStatus=APPROVED`;
|
|||
|
|
- `publicationStatus=PUBLISHED`;
|
|||
|
|
- `settlementIncluded=true`;
|
|||
|
|
- 来源费用项目编码 `FBA_HEAD_FREIGHT`,不存在时由服务端以平台公共费用项目自动创建。
|
|||
|
|
|
|||
|
|
发布新费用版本时,旧的已发布运费明细会被标记为 `SUPERSEDED`,而不是物理删除。`(tenant_id, source_type, source_fee_id)` 的来源唯一性和并发重复键兜底,已经能支撑来源版本幂等的第一层防护。
|
|||
|
|
|
|||
|
|
人工费用明细支持 `RECEIVABLE | PAYABLE`,但创建后为 `PENDING + DRAFT + settlementIncluded=false`。现有审核通过只改变审核状态,没有发布/纳入结算动作,因此人工明细尚不能按统一规则自动进入 Finance。
|
|||
|
|
|
|||
|
|
### 2.3 费用项目
|
|||
|
|
|
|||
|
|
平台费用项目接口已完整实现:
|
|||
|
|
|
|||
|
|
| 操作 | 接口 | 权限 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| 分页查询 | `GET /api/platform/v1/fee-items` | `firstleg:fee-item:view` |
|
|||
|
|
| 详情 | `GET /api/platform/v1/fee-items/{id}` | `firstleg:fee-item:view` |
|
|||
|
|
| 新增 | `POST /api/platform/v1/fee-items` | `firstleg:fee-item:create` |
|
|||
|
|
| 编辑 | `PUT /api/platform/v1/fee-items/{id}` | `firstleg:fee-item:edit` |
|
|||
|
|
| 启停 | `PATCH /api/platform/v1/fee-items/{id}/status` | `firstleg:fee-item:edit` |
|
|||
|
|
| 删除 | `DELETE /api/platform/v1/fee-items/{id}` | `firstleg:fee-item:delete` |
|
|||
|
|
|
|||
|
|
现有 DTO/实体字段包括 `feeCode`、分类、`DOMESTIC | INTERNATIONAL`、提货/仓库应收应付适用性、是否分摊费、说明和 `ACTIVE | DISABLED`。
|
|||
|
|
|
|||
|
|
F0 可引用稳定 `feeCode` 并保存名称/分类快照,但不应直接把 Firstleg 费用项目表作为 Finance 自有会计科目表。Finance 费用科目与业务费用项目需要显式映射和独立版本管理。
|
|||
|
|
|
|||
|
|
## 三、现有接口、DTO、权限与状态
|
|||
|
|
|
|||
|
|
### 3.1 可复用平台接口
|
|||
|
|
|
|||
|
|
| 范围 | 接口 | DTO/返回 | 权限 | F0 复用判断 |
|
|||
|
|
|---|---|---|---|---|
|
|||
|
|
| 费用快照 | `GET .../inbounds/{inboundId}/fees/latest` | `FeeSnapshotResponse` | `firstleg:fba-pricing:view` | 可供 Admin 查看,不满足 Finance 按来源版本核验 |
|
|||
|
|
| 费用调整 | `PUT .../inbounds/{inboundId}/fees/published` | `AdjustFeeRequest` → `FeeSnapshotResponse` | `firstleg:fba-pricing:adjust` | 保留业务入口,但需增加已出账约束或改走补差/冲销 |
|
|||
|
|
| 费用明细查询 | `GET .../inbounds/{inboundId}/fee-details?accountId=` | `List<InboundFeeDetailEntity>` | `firstleg:inbound:fee-detail:view` | 仅单入库单平台查询,不作为跨服务契约 |
|
|||
|
|
| 人工费用新增/编辑 | `POST/PUT .../fee-details` | `SaveRequest` → `InboundFeeDetailEntity` | `firstleg:inbound:fee-detail:edit` | 可继续维护业务费用,不能代替财务制单 |
|
|||
|
|
| 人工费用删除 | `DELETE .../fee-details/{detailId}` | 空 | `firstleg:inbound:fee-detail:delete` | 仅未审核明细可删;Finance 入池后必须禁止物理语义 |
|
|||
|
|
| 人工费用审核 | `POST .../{detailId}/approve|reject` | `RejectRequest` | `firstleg:inbound:fee-detail:review` | 可作为业务来源前置条件 |
|
|||
|
|
| 客户已发布费用 | `GET /api/merchant/v1/first-leg/orders/{orderId}/inbound-billing` | `MerchantInboundBillingResponse` | `merchant:order:view` | 客户只读展示,不是 Finance 内部来源接口 |
|
|||
|
|
|
|||
|
|
费用试算、确认、发布还分别使用:
|
|||
|
|
|
|||
|
|
- `firstleg:fba-pricing:calculate`;
|
|||
|
|
- `firstleg:fba-pricing:confirm`;
|
|||
|
|
- `firstleg:inbound:send-fee`。
|
|||
|
|
|
|||
|
|
上述权限都是 Firstleg 业务权限。F0 需要新增 Finance 独立权限资源,不能复用它们授权财务入池、冲突处理、来源重放或来源作废。
|
|||
|
|
|
|||
|
|
### 3.2 建议冻结的来源状态映射
|
|||
|
|
|
|||
|
|
| Firstleg 状态组合 | 当前含义 | F0 建议 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `feeStatus=CALCULATED` | 已试算 | 不入池 |
|
|||
|
|
| `feeStatus=CONFIRMED` | 业务已确认 | 不入池 |
|
|||
|
|
| `feeStatus=SENT` + 明细 `APPROVED/PUBLISHED/settlementIncluded=true` | 当前有效对客费用 | 允许生成 Finance 来源 |
|
|||
|
|
| 旧费用 `SUPERSEDED` + 旧明细 `SUPERSEDED` | 已有更新版本 | Finance 已消费时保留原来源,新增调整来源,不覆盖历史 |
|
|||
|
|
| 人工明细 `PENDING/DRAFT/false` | 待业务审核 | 不入池 |
|
|||
|
|
| 人工明细 `APPROVED/DRAFT/false` | 已审核但未发布 | 当前仍不入池;先补发布动作与事件 |
|
|||
|
|
| `REJECTED` | 业务驳回 | 不入池 |
|
|||
|
|
|
|||
|
|
`SENT` 是当前代码中的“已发布给客户”状态,F0 契约宜对外统一为业务语义清晰的 `PUBLISHED`,同时保留内部兼容映射,避免直接修改存量状态造成迁移风险。
|
|||
|
|
|
|||
|
|
## 四、Outbox 与事件能力现状
|
|||
|
|
|
|||
|
|
### 4.1 Firstleg 本地 Outbox
|
|||
|
|
|
|||
|
|
`fl_inbound_outbox` 已包含:事件 ID、客户隔离键、聚合类型/ID、事件类型、JSON 负载、发布状态、重试次数、下次重试时间、发布时间和创建时间。费用发布/调整与业务表在同一本地事务内写入 Outbox,写入位置可复用。
|
|||
|
|
|
|||
|
|
当前实际费用事件只有:
|
|||
|
|
|
|||
|
|
| 事件 | 触发点 | 当前负载 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `FIRSTLEG_FEE_SENT` | 已确认费用发布 | `inboundId`、`feeId` |
|
|||
|
|
| `FIRSTLEG_FEE_ADJUSTED` | 已发布费用生成新版本 | `inboundId`、`feeId`、`feeVersion` |
|
|||
|
|
|
|||
|
|
当前 Firstleg 模块没有 `@Scheduled` Outbox 扫描器、消息生产者、发布成功/重试更新逻辑,也没有费用作废事件。因此 `PENDING` 记录目前只落库,不会自动送达 Finance。
|
|||
|
|
|
|||
|
|
### 4.2 可参考的 Order Outbox
|
|||
|
|
|
|||
|
|
Order 已有可运行的 `OrderInboundOutboxPublisher`:
|
|||
|
|
|
|||
|
|
- 定时分页扫描待发布事件;
|
|||
|
|
- 校验聚合类型、事件类型、所有权和负载完整性;
|
|||
|
|
- 通过受保护内部客户端投递 Firstleg;
|
|||
|
|
- 成功标记已发布,失败设置下次重试,不支持的事件永久失败;
|
|||
|
|
- 对应测试覆盖成功、无效事件、负载错误、所有权冲突和重试。
|
|||
|
|
|
|||
|
|
F0 可以复用这一可靠投递结构和测试思路,但费用事件应使用专门发布器/消费者和冻结的事件信封,不应扩展 `OrderInboundOutboxPublisher` 承载 Finance 逻辑。
|
|||
|
|
|
|||
|
|
### 4.3 Event Bridge 与公共事件契约
|
|||
|
|
|
|||
|
|
`common-event-contract` 已提供版本化 `EventEnvelope`、区域、事件 ID、负载规范化、SHA-256 哈希和基础校验;ID 使用字符串,适合跨语言和长整型安全传输。
|
|||
|
|
|
|||
|
|
`event-bridge-service` 已提供:
|
|||
|
|
|
|||
|
|
- `POST /internal/event-bridge/events`,使用 `X-Internal-Service-Token`;
|
|||
|
|
- 目标区域和 `payloadHash` 校验;
|
|||
|
|
- 以 `eventId` 幂等入库;
|
|||
|
|
- 相同事件 ID 但负载哈希、客户或信封身份不同则拒绝覆盖;
|
|||
|
|
- Inbox 状态初始为 `RECEIVED`。
|
|||
|
|
|
|||
|
|
但它明确是跨区域接收骨架:状态接口返回发送能力未就绪,尚无真实跨区发送、重试调度、死信补投和领域消费分发。公共 `EventTypes` 也没有费用事件常量。Firstleg → Finance 是同国内业务域内的服务协作,F0 应优先建立独立的服务内事件投递/Inbox 契约;只有架构明确要求统一事件总线时,才将公共信封的通用字段复用进该链路。
|
|||
|
|
|
|||
|
|
### 4.4 审计 Outbox 不是领域 Outbox
|
|||
|
|
|
|||
|
|
Firstleg 还包含通用 `sys_audit_outbox`,用于 `@BusinessAudit` 业务操作审计。它与 `fl_inbound_outbox` 的领域事件用途不同,不能把审计事件当作 Finance 来源事件。F0 需要同时保留“来源领域事件”和“财务操作审计”两条记录。
|
|||
|
|
|
|||
|
|
## 五、Admin 已有能力
|
|||
|
|
|
|||
|
|
### 5.1 正式页面与接口
|
|||
|
|
|
|||
|
|
| 页面/能力 | 当前状态 | 使用契约 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| 费用项目 | 正式路由 `/master-data/fee-items`,支持查询、新增、编辑、启停、删除和权限态 | `/platform/v1/fee-items` |
|
|||
|
|
| 船期价格表 | 正式路由 `/first-leg-pricing/carrier-rates`,支持价格表上传、查询和发布 | FBA pricing price-book API |
|
|||
|
|
| 入库费用快照 | 入库详情工作区已嵌入 `InboundFeePanel`,展示计算依据、金额、规则/价格版本和时间线 | `GET .../fees/latest` |
|
|||
|
|
| 已发布费用调整 | 入库费用面板支持填写小计、附加费和原因,带客户端幂等键 | `PUT .../fees/published` |
|
|||
|
|
| 统一订单费用明细 | 订单详情费用页签按入库单加载应收/应付明细并展示审核、计费、结算标识 | `GET .../fee-details?accountId=` |
|
|||
|
|
|
|||
|
|
Admin 当前没有:
|
|||
|
|
|
|||
|
|
- 财务来源池页面;
|
|||
|
|
- 跨客户/跨入库单费用筛选、入池状态、冲突和重放操作;
|
|||
|
|
- 账单占用、冻结、补差/冲销、作废展示;
|
|||
|
|
- Finance 权限和正式 Finance API 适配。
|
|||
|
|
|
|||
|
|
财务管理导航仍整体 `hidden: true`,指向 `/prototype/finance/*`。这些原型页不属于正式生产能力,不应作为 F0 接入基础。
|
|||
|
|
|
|||
|
|
### 5.2 前后端契约注意点
|
|||
|
|
|
|||
|
|
- Admin 对费用快照和入库 ID 使用字符串类型,符合长整型传输要求;
|
|||
|
|
- 费用项目资源兼容 `number | string`,但新增/编辑请求没有暴露后端可选 `feeCode`,F0 不应从页面名称反推稳定来源科目;
|
|||
|
|
- 统一订单费用明细前端预留了 `billNo`、结算起止日等字段,后端当前实体并未提供这些财务字段;这些 UI 预留不能视为已实现账单能力;
|
|||
|
|
- `InboundFeePanel` 只接入查询和发布后调整,没有接入试算、确认、发布按钮,相关动作仍由后端流程/其他编排承担。
|
|||
|
|
|
|||
|
|
## 六、F0 缺口清单
|
|||
|
|
|
|||
|
|
| 优先级 | 缺口 | 风险/影响 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| P0 | 缺少 Finance 服务、数据库、Inbox 和来源表 | 无法建立财务所有权和独立事务边界 |
|
|||
|
|
| P0 | Firstleg 无费用 Outbox 发布器 | 事件只停留在 `PENDING`,Finance 无法接收 |
|
|||
|
|
| P0 | 费用事件名称、版本和负载未冻结 | 当前 `FIRSTLEG_FEE_SENT` 与目标 `PUBLISHED` 语义不一致,负载无法独立幂等核验 |
|
|||
|
|
| P0 | 缺少按 `sourceFeeId + sourceFeeVersion` 查询的内部快照 API | Finance 只能依赖事件少量字段或平台 API,均不合适 |
|
|||
|
|
| P0 | 缺少来源哈希 | 无法检测同一来源版本内容被覆盖或事件冲突 |
|
|||
|
|
| P0 | 缺少作废事件和原因 | 错误费用无法以不可变方式退出财务来源池 |
|
|||
|
|
| P0 | 发布后调价未校验财务占用 | 已出账后仍可能直接把旧来源标记为 `SUPERSEDED` |
|
|||
|
|
| P1 | 人工费用审核后无发布动作 | 人工应收/应付无法可靠进入统一来源链路 |
|
|||
|
|
| P1 | 无跨单费用池查询和游标/时间增量接口 | 补偿扫描、历史迁移和运营核对困难 |
|
|||
|
|
| P1 | 无 Outbox 失败查询、人工重放、退避和死信策略 | 故障不可运营化处理 |
|
|||
|
|
| P1 | 无 Finance 独立权限与审计事件 | 来源冲突、重放、作废等高风险操作无法分权 |
|
|||
|
|
| P1 | 应付来源字段不足 | 人工 `PAYABLE` 缺供应商、付款主体、凭证和成本归属,不能直接进入 F2 |
|
|||
|
|
| P2 | OpenAPI 未提供完整 `FeeSnapshotResponse` schema | 跨服务调用方难以通过契约测试防止字段漂移 |
|
|||
|
|
| P2 | Admin 无来源接入状态和错误可观测性 | 上游已发布但 Finance 未入池时无法定位 |
|
|||
|
|
|
|||
|
|
## 七、Firstleg → Finance F0 接入建议
|
|||
|
|
|
|||
|
|
### 7.1 接入主链路
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Firstleg 费用发布/调整/作废事务
|
|||
|
|
-> 更新 Firstleg 业务费用与不可变版本
|
|||
|
|
-> 同事务写 fl_inbound_outbox
|
|||
|
|
-> Firstleg Outbox Publisher 投递版本化事件
|
|||
|
|
-> Finance Inbox 以 eventId 去重并校验来源身份
|
|||
|
|
-> Finance 用内部令牌查询指定来源版本完整快照
|
|||
|
|
-> 校验 sourceHash
|
|||
|
|
-> Finance 保存不可变来源快照和处理结果
|
|||
|
|
-> 后续 F1/F2 从 Finance 来源池制单
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Finance 不跨库读取 Firstleg,不共享 Entity,不以 Admin 平台接口作为服务间接口。
|
|||
|
|
|
|||
|
|
### 7.2 建议冻结的事件
|
|||
|
|
|
|||
|
|
建议对外统一:
|
|||
|
|
|
|||
|
|
- `FIRSTLEG_FEE_PUBLISHED`;
|
|||
|
|
- `FIRSTLEG_FEE_ADJUSTED`;
|
|||
|
|
- `FIRSTLEG_FEE_VOIDED`。
|
|||
|
|
|
|||
|
|
兼容策略:存量 `FIRSTLEG_FEE_SENT` 可由发布器映射为 `FIRSTLEG_FEE_PUBLISHED` V1,或通过一次受控迁移重建待发布事件;不要让 Finance 同时长期理解两套同义事件。
|
|||
|
|
|
|||
|
|
事件信封至少包含:
|
|||
|
|
|
|||
|
|
- `eventId`、`eventType`、`eventVersion`、`occurredAt`;
|
|||
|
|
- `customerId`(技术字段仍可映射现有 `tenantId`);
|
|||
|
|
- `aggregateType=FIRSTLEG_FEE`、`aggregateId=sourceFeeId`、`aggregateVersion=sourceFeeVersion`;
|
|||
|
|
- `inboundId`、`sourceFeeId`、`sourceFeeVersion`、`sourceFeeNo`;
|
|||
|
|
- `feeDetailId`、`direction`、`businessDate`、`currency`、`amount`;
|
|||
|
|
- `reviewStatus`、`publicationStatus`、`settlementIncluded`;
|
|||
|
|
- `sourceHash`、`traceId`、`correlationId`、`causationId`;
|
|||
|
|
- 调整/作废事件的 `previousSourceFeeId`、`reason`。
|
|||
|
|
|
|||
|
|
事件只用于通知和快速路由,Finance 仍应通过内部接口读取完整快照。
|
|||
|
|
|
|||
|
|
### 7.3 建议新增的 Firstleg 内部接口
|
|||
|
|
|
|||
|
|
建议提供受内部服务令牌保护的版本查询,而不是复用 `latest`:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
GET /internal/firstleg/v1/finance/fee-sources/{sourceFeeId}?version={sourceFeeVersion}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
返回专用 `FirstlegFeeSourceSnapshot`,至少覆盖:来源身份、客户、入库业务键、费用项目快照、方向、业务日、币种金额、计费依据、审核/发布/结算候选状态、发布时间、调整链和 `sourceHash`。不存在、版本不符、未达到入池状态时返回明确业务错误码。
|
|||
|
|
|
|||
|
|
可补充一个只用于补偿和迁移的分页接口,按 `publishedAt + sourceFeeId` 稳定游标扫描;它不能替代实时事件。
|
|||
|
|
|
|||
|
|
### 7.4 幂等与乱序
|
|||
|
|
|
|||
|
|
- Outbox 事件 ID 全局唯一;
|
|||
|
|
- Finance Inbox 先按 `eventId` 去重;
|
|||
|
|
- 财务来源再以 `sourceType + sourceId + sourceVersion` 唯一;
|
|||
|
|
- 同一唯一键但 `sourceHash` 不同必须进入冲突状态,禁止覆盖;
|
|||
|
|
- 调整 V2 早于发布 V1 到达时,先保存待依赖状态并重试核验,不能用 V2 覆盖 V1;
|
|||
|
|
- Finance 已出账的 V1 永久保留,V2 形成补差/冲销候选,不修改原账单明细;
|
|||
|
|
- 发布器成功、重试、永久失败和人工重放均记录时间、次数、错误摘要和审计人。
|
|||
|
|
|
|||
|
|
### 7.5 Firstleg 调整门禁
|
|||
|
|
|
|||
|
|
F0 上线前应冻结以下规则:
|
|||
|
|
|
|||
|
|
1. 未进入 Finance 或仍在待对账池的来源,可由新版本替代,但 Finance 保留版本历史;
|
|||
|
|
2. 已被账单占用的来源不能直接调整原金额,必须发布补差或冲销来源;
|
|||
|
|
3. 作废不物理删除原费用和事件;
|
|||
|
|
4. Firstleg 不保存财务余额或账单状态,只保存必要的只读占用/处理回执或通过 Finance 内部校验门禁;
|
|||
|
|
5. Finance 不回写 `fl_inbound_fee_detail` 伪造 `billNo`,账单关系归 Finance 自有表。
|
|||
|
|
|
|||
|
|
### 7.6 权限建议
|
|||
|
|
|
|||
|
|
保留现有 Firstleg 业务权限,并在 IAM 中为 Finance 新建独立权限,例如:
|
|||
|
|
|
|||
|
|
- `finance:source:view`;
|
|||
|
|
- `finance:source:retry`;
|
|||
|
|
- `finance:source:resolve-conflict`;
|
|||
|
|
- `finance:source:void`;
|
|||
|
|
- `finance:source:export`。
|
|||
|
|
|
|||
|
|
内部事件投递和快照查询使用服务身份,不使用平台员工权限。人工重放、冲突处理和作废属于高风险操作,必须填写原因并记录业务审计。
|
|||
|
|
|
|||
|
|
## 八、建议实施顺序与验收
|
|||
|
|
|
|||
|
|
### 8.1 实施顺序
|
|||
|
|
|
|||
|
|
1. 冻结 V1 事件、内部快照 DTO、来源哈希算法和状态映射;
|
|||
|
|
2. 在 Firstleg 增加指定版本内部查询及契约测试;
|
|||
|
|
3. 增加 Firstleg Outbox 发布器、重试/失败状态和运维查询;
|
|||
|
|
4. 建立 Finance Inbox、来源表、唯一约束和冲突状态;
|
|||
|
|
5. 联通发布、重复、乱序、失败重试、调整和作废链路;
|
|||
|
|
6. 增加 Admin 来源池和接入状态页面;
|
|||
|
|
7. 最后迁移满足条件的历史已发布费用。
|
|||
|
|
|
|||
|
|
### 8.2 F0 最小验收案例
|
|||
|
|
|
|||
|
|
- 一条 `APPROVED + PUBLISHED + settlementIncluded=true` 的应收费用只生成一个 Finance 来源;
|
|||
|
|
- 同一事件重复投递不重复入池;
|
|||
|
|
- 同一来源版本不同哈希被拒绝并可运营处理;
|
|||
|
|
- Finance 暂停后恢复,Firstleg Outbox 能重试并最终成功;
|
|||
|
|
- V2 调整不会覆盖 Finance 中 V1 来源;
|
|||
|
|
- V2 先于 V1 到达不会破坏版本顺序;
|
|||
|
|
- 已出账 V1 的后续调整形成补差/冲销候选;
|
|||
|
|
- 作废保留原来源和审计链;
|
|||
|
|
- 跨客户查询、事件或快照调用被服务端拒绝;
|
|||
|
|
- 长整型 ID 按字符串传输,金额/币种精度不丢失;
|
|||
|
|
- Admin 能看到待处理、成功、重试、冲突状态以及 `requestId/traceId`。
|
|||
|
|
|
|||
|
|
## 九、证据路径
|
|||
|
|
|
|||
|
|
### 9.1 Firstleg 费用与计费
|
|||
|
|
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformFbaPricingController.java`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/dto/FbaPricingDtos.java`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/service/FbaPricingService.java`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/entity/InboundFeeEntity.java`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/entity/InboundFeeItemEntity.java`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/resources/openapi/fba-pricing-v1.yaml`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/resources/db/phase2-inbound-schema.sql`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/test/java/com/baiyue/firstleg/service/FbaPricingServiceTest.java`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/test/java/com/baiyue/firstleg/controller/FbaPricingContractTest.java`
|
|||
|
|
|
|||
|
|
### 9.2 费用明细与费用项目
|
|||
|
|
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformInboundFeeDetailController.java`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/service/InboundFeeDetailService.java`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/dto/InboundFeeDetailDtos.java`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/entity/InboundFeeDetailEntity.java`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformFeeItemController.java`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/dto/FeeItemDtos.java`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/entity/FeeItemEntity.java`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/resources/openapi/fee-item-v1.yaml`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/resources/db/phase2-fee-item-and-inbound-detail.sql`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/resources/db/phase2-published-freight-detail-migration.sql`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/test/java/com/baiyue/firstleg/service/FeeManagementServiceTest.java`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/test/java/com/baiyue/firstleg/controller/FeeManagementContractTest.java`
|
|||
|
|
|
|||
|
|
### 9.3 客户费用查询
|
|||
|
|
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/controller/MerchantInboundBillingController.java`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/service/MerchantInboundBillingService.java`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/dto/MerchantInboundBillingResponse.java`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/test/java/com/baiyue/firstleg/controller/MerchantInboundBillingControllerContractTest.java`
|
|||
|
|
|
|||
|
|
### 9.4 Outbox、事件与权限
|
|||
|
|
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/entity/InboundOutboxEntity.java`
|
|||
|
|
- `baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/mapper/InboundOutboxMapper.java`
|
|||
|
|
- `baiyue-firstleg/order/src/main/java/com/baiyue/order/entity/OutboxEventEntity.java`
|
|||
|
|
- `baiyue-firstleg/order/src/main/java/com/baiyue/order/service/OrderInboundOutboxPublisher.java`
|
|||
|
|
- `baiyue-firstleg/order/src/test/java/com/baiyue/order/service/OrderInboundOutboxPublisherTest.java`
|
|||
|
|
- `baiyue-firstleg/common-event-contract/src/main/java/com/baiyue/common/event/EventEnvelope.java`
|
|||
|
|
- `baiyue-firstleg/common-event-contract/src/main/java/com/baiyue/common/event/EventTypes.java`
|
|||
|
|
- `baiyue-firstleg/event-bridge-service/src/main/java/com/baiyue/eventbridge/controller/EventBridgeController.java`
|
|||
|
|
- `baiyue-firstleg/event-bridge-service/src/main/java/com/baiyue/eventbridge/service/EventInboxService.java`
|
|||
|
|
- `baiyue-firstleg/event-bridge-service/src/test/java/com/baiyue/eventbridge/service/EventInboxServiceTest.java`
|
|||
|
|
- `baiyue-firstleg/iam/src/main/resources/db/phase2-fba-pricing-permissions.sql`
|
|||
|
|
- `baiyue-firstleg/iam/src/main/resources/db/phase2-fee-item-permissions.sql`
|
|||
|
|
- `baiyue-firstleg/deploy/sql/头程数据库统一增量-MySQL8.sql`
|
|||
|
|
|
|||
|
|
### 9.5 Admin
|
|||
|
|
|
|||
|
|
- `apps/admin-web/src/modules/inbounds/fee/api.ts`
|
|||
|
|
- `apps/admin-web/src/modules/inbounds/fee/types.ts`
|
|||
|
|
- `apps/admin-web/src/modules/inbounds/fee/InboundFeePanel.vue`
|
|||
|
|
- `apps/admin-web/src/modules/inbounds/components/InboundWorkflowWorkspace.vue`
|
|||
|
|
- `apps/admin-web/src/modules/fee-items/api/platformFeeItems.ts`
|
|||
|
|
- `apps/admin-web/src/modules/fee-items/pages/FeeItemsView.vue`
|
|||
|
|
- `apps/admin-web/src/modules/unified-orders/api/unifiedOrders.ts`
|
|||
|
|
- `apps/admin-web/src/modules/unified-orders/pages/UnifiedOrdersView.vue`
|
|||
|
|
- `apps/admin-web/src/core/navigation/adminNavigation.ts`
|
|||
|
|
|
|||
|
|
## 十、盘点边界
|
|||
|
|
|
|||
|
|
本文只证明当前代码已经存在的能力和 F0 接入缺口。没有修改业务代码、数据库脚本或既有文档,也没有验证测试环境消息中间件、数据库实值或实际事件投递。Finance 的财务主体、期间、账套、币种、权限和编号等产品口径仍以财务管理产品确认结果为准。
|