feat(platform): close AI expense value loop
Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
# 财务连接器与支付对账闭环 概念文档
|
||||
|
||||
更新时间:2026-07-17
|
||||
|
||||
## 功能一句话
|
||||
|
||||
把经过租户、来源、密钥版本和请求路径共同认证的 production-mode 财务事件契约接入付款、ERP、对账与 Savings 闭环,同时让所有非生产回执严格停留在只读模拟事实层;真实外部现金仍以目标 provider 联调为准。
|
||||
|
||||
## 背景与问题
|
||||
|
||||
当前报销单可以由财务人员在平台内确认“已付款”,并能联动申请归档和 Savings 实现记录,但这只是内部业务状态,不是银行、支付平台或 ERP 的外部现金事实。若继续把单一状态当成真实回执,会留下重复付款、金额/币种错配、回执伪造、凭证缺失、对账异常未处置和虚假现金节省等风险。
|
||||
|
||||
本功能把外部财务系统接入收敛成统一、可审计的连接器契约。首期允许使用 mock adapter 验证协议和流程,但 mock 必须完整模拟签名、幂等、失败、重试、乱序、退款、凭证和对账差异,不以直接写“已付款”代替连接器事实。
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
### 目标
|
||||
|
||||
- 建立租户隔离、来源可验证、追加式的支付/银行/ERP 事件账本。
|
||||
- 支持支付批次、结算成功、支付失败、退款/冲回、ERP 入账凭证和对账结果。
|
||||
- 只有单据、金额、币种、外部引用和签名均通过校验时,才推进报销付款状态。
|
||||
- 把外部事件与 Expense Case、Business Event、Savings Evidence、归档事件用同一 correlation 链关联。
|
||||
- 对重复、冲突、乱序和不完整事件 fail-closed,并提供可人工处理的对账异常。
|
||||
- 以版本化 activate/disable/rotate 状态机管理连接器密钥,所有配置动作形成不含密钥材料的追加式审计。
|
||||
|
||||
### 非目标
|
||||
|
||||
- 不自建银行清算、企业支付网络、税务开票网络或 ERP 总账。
|
||||
- 不保存银行卡号、银行流水原文、完整付款人账号或连接器密钥明文。
|
||||
- 不允许连接器绕过报销审批、风险门禁、租户权限或财务确认。
|
||||
- 不把 mock 回执标记为生产级外部现金证据;运行环境和证据等级必须显式区分。
|
||||
|
||||
## 用户与场景
|
||||
|
||||
- 财务付款员:提交或查看付款批次,处理失败与待匹配回执。
|
||||
- 财务复核员:复核金额/币种/收款主体和对账异常,确认或拒绝处置。
|
||||
- 财务负责人/CFO:查看已匹配、待对账、失败、退款和未入账金额。
|
||||
- 平台管理员:配置连接器公钥/密钥版本、来源白名单和健康状态,但不能代替财务确认业务结果。
|
||||
- 外部连接器:按租户和来源签名推送支付、银行或 ERP 事件,安全重试并读取幂等结果。
|
||||
|
||||
## 功能能力
|
||||
|
||||
- 连接器注册与密钥版本:来源、环境、允许事件、时钟偏差、启停和轮换状态。
|
||||
- 统一事件信封:tenant、provider、event ID、event type、occurred at、payload hash、correlation、signature version。
|
||||
- 支付批次与回执:批次创建、提交、受理、成功、失败和部分成功。
|
||||
- ERP 入账:凭证号、会计期间、入账时间、受控摘要和原始内容哈希。
|
||||
- 对账:按单据、金额、币种和外部引用自动匹配;差异进入人工处置。
|
||||
- 冲回:退款、撤销和补付使用新事件,不更新或删除原事件。
|
||||
- 可观测性:最近成功时间、失败率、重试次数、积压、签名失败和对账差异。
|
||||
|
||||
## 方案设计
|
||||
|
||||
### 模块职责
|
||||
|
||||
- `financial_connector_auth`:验证来源、签名、时间戳、密钥版本和重放窗口。
|
||||
- `financial_connector_ingestion`:规范化事件、计算指纹、幂等写入、生产/模拟分流和冲突检测。
|
||||
- `financial_connector_simulation`:只读校验 test/mock/staging 回执,不创建或修改 Claim、对账、ERP、Business Event 或 Savings。
|
||||
- `financial_connector_mock_adapter`:平台管理员显式触发的非生产场景适配器;用已激活配置的真实签名链确定性生成成功、失败、乱序、重复、冲突、退款和 ERP 回执,但不提供任意 payload 注入能力。
|
||||
- `financial_connector_observability`:按租户和配置聚合最近成功、失败率、重试、积压、签名失败、冲突和对账异常;只读取最小化事实与追加式运行事件。
|
||||
- `financial_connector_payment_evidence`:从 Claim 的付款事实中提取统一证据 DTO,明确区分 production-mode 外部回执分类、非生产模拟回执和人工付款内部状态;分类本身不证明真实 provider 已接通。
|
||||
- `financial_connector_config_lifecycle`:执行带 expected version 的激活、停用和原子密钥轮换。
|
||||
- `payment_reconciliation`:匹配 Claim、金额、币种和状态,生成 matched / exception 结果。
|
||||
- `financial_connector_actions`:在可信匹配后调用现有付款动作;支付失败、退款和 ERP 入账分别旁写事件。
|
||||
- `financial_connector_projection`:为财务工作台提供脱敏列表、详情、差异和健康度。
|
||||
- 供应商 adapter 只负责供应商字段映射,不直接修改 Claim、Savings 或预算。
|
||||
|
||||
### 数据与契约
|
||||
|
||||
首期新增以下 migration-owned 表,均带 `tenant_id`:
|
||||
|
||||
- `financial_connector_configs`:provider、environment、allowed event types、secret/key version、status、last success/error;只保存密钥引用或不可逆验证材料。
|
||||
- `financial_connector_config_events`:created、activated、disabled、rotation started/replacement created 的追加式配置审计;保存 actor、request、reason、expected version 和脱敏前后状态,不保存 `secret_ref`。
|
||||
- `financial_connector_events`:方向、事件类型、外部事件 ID、请求指纹、原始内容哈希、发生/接收时间、验证等级、处理状态、关联单据/Case 和错误码;UPDATE/DELETE 禁止。
|
||||
- `payment_reconciliation_cases`:Claim、期望/实际金额与币种、匹配状态、差异、处置版本、负责人和最后事件;作为可变投影,历史动作另存事件。
|
||||
- `payment_reconciliation_events`:创建、自动匹配、人工确认、拒绝、重开、退款和关闭的追加式审计事件,保存请求指纹和首次响应。
|
||||
- `financial_connector_operational_events`(`0022`):保存 `replay / auth_failure / payload_conflict` 三类运行事实的 tenant/config/provider/environment、受控原因码、两类 HMAC 指纹、幂等键和发生时间;不保存原始 payload、签名、外部事件 ID、Claim 引用、correlation 或密钥材料。复合租户外键防止跨租户归属,PostgreSQL 触发器禁止 UPDATE/DELETE;存在运行事实时拒绝有损降级。
|
||||
|
||||
关键约束:
|
||||
|
||||
- `(tenant_id, provider, external_event_id)` 唯一。
|
||||
- 配置从 `disabled/version=1` 创建;激活和停用必须命中 expected version,轮换原子地产生新 active key version 并把旧版本置为 rotating。
|
||||
- 同幂等键不同 payload hash 返回冲突,不能覆盖首次事件。
|
||||
- 同一个运行事实 candidate 的补偿写入按 `(tenant_id, idempotency_key)` 幂等;不同 HTTP 尝试即使请求内容相同,也因可信发生时间不同而分别计数,避免把真实重放次数永久压成一次。
|
||||
- Claim、Case、配置和对账记录使用复合租户外键。
|
||||
- 退款/冲回必须引用同租户原结算事件。
|
||||
- 生产事件必须通过已激活密钥验证;test/mock/staging 即使签名和业务字段全部匹配,也只能生成 `projection_scope=simulation_only` 的连接器事实,绝不进入核心财务状态机。
|
||||
|
||||
### 接口
|
||||
|
||||
- `POST /api/v1/integrations/financial-events`:连接器签名事件入口;返回稳定接收/重放结果。
|
||||
- `POST /api/v1/financial-connectors/admin/tenants/{tenant}/configs/{id}/activate|disable|rotate`:带版本、actor、request ID 和 reason 的配置状态机。
|
||||
- `POST /api/v1/financial-connectors/admin/tenants/{tenant}/configs/{id}/simulate`:平台管理员运行确定性非生产场景;production 配置和未激活配置 fail-closed。
|
||||
- `GET /api/v1/financial-connectors/admin/tenants/{tenant}/config-events`:读取不含密钥材料的配置审计时间线。
|
||||
- `GET /api/v1/financial-connectors/admin/tenants/{tenant}/observability`:平台管理员读取目标租户脱敏运行指标;从 config/event/reconciliation/operational event 聚合指定窗口真实值。
|
||||
- `GET /api/v1/financial-connectors/observability`:财务角色读取当前租户脱敏运行指标;返回 `window_started_at / as_of / generated_at / source_revision`,以及 replay、认证失败、签名失败、payload conflict 的真实计数与最近发生时间。`0022` 起四类指标均标记 `available`,没有事实时真实返回零而不是“待采集”占位。
|
||||
- `GET /api/v1/financial-connectors/payment-evidence/{claim}`:有单据读取权限的当前租户用户读取付款证据等级,不返回原始连接器内容。
|
||||
- `GET /api/v1/financial-reconciliation/cases`:财务角色分页查看匹配与异常。
|
||||
- `GET /api/v1/financial-reconciliation/cases/{id}`:查看脱敏证据和追加式时间线。
|
||||
- `POST /api/v1/financial-reconciliation/cases/{id}/confirm`:独立财务确认差异或人工匹配。
|
||||
- `POST /api/v1/financial-reconciliation/cases/{id}/reject`:拒绝错误回执并记录原因。
|
||||
- 连接器配置管理接口仅平台管理员可用,业务确认接口仅财务角色可用,两类权限不互相继承。
|
||||
|
||||
### 匹配算法
|
||||
|
||||
1. 验证 tenant/provider/key version/timestamp/signature 和事件类型;HMAC canonical request 同时绑定固定 HTTP method/path,禁止共享密钥跨 provider 或 key version 重放。
|
||||
2. 以 canonical JSON 生成 payload hash;按外部事件 ID 与幂等键检查首次请求。
|
||||
3. 通过受控引用解析 Claim,不允许仅凭模糊姓名或备注自动匹配。
|
||||
4. 校验 Claim 已完成审批且处于待付款;比较金额、币种、外部业务引用和事件方向。
|
||||
5. 只有 `production_verified` 且完全一致时才自动 matched;非生产来源只生成模拟投影,任何生产差异进入 exception 且不改变 Claim。
|
||||
6. matched 事件在同事务调用付款动作、记录 Business Event,并把外部事件哈希作为 Savings 证据。
|
||||
7. ERP posted 只表示入账,不重复触发付款;refund/reversal 追加负向业务与 Savings 冲回候选。
|
||||
|
||||
### 权限与安全
|
||||
|
||||
- 连接器入口不使用普通用户会话,使用租户绑定的签名认证;普通 Bearer token 不能伪装连接器。
|
||||
- HMAC/签名比较使用常量时间函数,限制时间窗口并记录 nonce/外部事件 ID 防重放。
|
||||
- 激活和轮换前由服务端解析 `secret_ref` 并校验至少 128-bit HMAC 密钥;数据库和审计 DTO 均不返回引用或明文。
|
||||
- 日志、响应和 DTO 最多暴露外部引用后八位或不可逆摘要,不返回原始 payload、签名或密钥。
|
||||
- replay 随成功重放事务提交;认证失败与 payload 冲突先回滚失败业务事务,再独立提交脱敏运行事实,审计写入异常不得覆盖原始 401/409 响应。
|
||||
- 只有服务端按 tenant/provider/key version 解析出 active 配置,并成功解析该配置的服务端密钥后,才构造可归属的 operational context。缺认证头、请求 tenant 不一致、未知/未激活配置或密钥不可用均不接受客户端自报租户,只写不带租户归属的结构化警告;签名、时间窗和事件白名单失败才可安全归入已解析配置。
|
||||
- 运行表只保存 `hmac-sha256:` 请求/外部事件指纹和 `sha256:` 幂等键。HMAC 输入可以包含外部 ID 和业务引用,但这些原值不会进入表、DTO 或错误日志。
|
||||
- 不可变连接器事实的 normalized payload 不再保存完整 `claim_reference`;`0020` 受控迁移会删除历史冗余值,保留内容哈希、受控 Claim ID 与必要尾号。
|
||||
- 跨租户资源统一按 404 隐藏;配置管理员不能确认对账,付款申请人不能确认自己的异常。
|
||||
- 连接器故障、未知密钥、签名异常、金额/币种不一致和数据库异常均 fail-closed。
|
||||
|
||||
### 状态转换
|
||||
|
||||
- 连接器事件:`received → verified → processed`,失败进入 `rejected`;事实本身追加只读。
|
||||
- 连接器配置:`disabled → active → rotating → disabled`;正常启用走 `disabled → active`,轮换时旧 active 原子进入 rotating、新 key version 以 active 创建。
|
||||
- 对账记录:`pending → matched | exception → confirmed | rejected`;退款可从 confirmed 进入 `reopened`,重新处置后关闭。
|
||||
- Claim 仅在可信 `payment_settled + matched` 后从 `pending_payment` 进入 `paid`。
|
||||
- ERP 凭证从 `pending_posting` 进入 `posted | posting_failed`,不反向伪造支付成功。
|
||||
|
||||
### 降级策略
|
||||
|
||||
- 连接器离线:保留待付款,不自动标记已付;显示积压和最后成功时间。
|
||||
- 回执乱序:先保存事实,等待前置事件或进入 pending,不猜测状态。
|
||||
- 回执冲突:保留首次事实并返回 409,生成对账异常。
|
||||
- ERP 未接入:付款事实可进入已付,但“已入账/凭证号”保持待采集。
|
||||
- test/mock/staging:返回 `simulation_only`,只保留追加式 connector fact/response projection,不创建对账 Case、不修改 Claim/ERP/归档/Savings,也不进入生产现金证明。
|
||||
- 显式 mock adapter 只能选择预定义场景和当前租户 Claim;事件 ID、correlation 和受控外部引用由 tenant/config/scenario/request ID 确定性派生。重复执行同一请求只产生稳定重放,不能借模拟接口注入生产配置或任意字段。
|
||||
- 升级前若已有非生产事件且响应曾关联对账 Case,`0020` 将其标记为 `legacy_nonproduction_effect_unknown` 供审计,不伪装成新策略下的无副作用模拟事实,也不在迁移中猜测性冲回历史财务状态。
|
||||
|
||||
### 兼容策略
|
||||
|
||||
- 保留现有人工“确认已付款”作为低等级内部证据;付款证据 DTO 使用 `internal_manual_payment`,生产连接器使用 `external_cash`,非生产连接器使用 `simulated_connector`/`staging_connector`。UI 必须同时展示来源标签和可信等级,不能只显示“已付款”。
|
||||
- 新连接器路径复用现有幂等付款动作、Case 时间线和 Savings 实现服务,不复制第二套状态机。
|
||||
- 现有 `risk_flags_json` 付款摘要继续只读兼容,新连接器事实进入正式事件表。
|
||||
|
||||
## 测试方案
|
||||
|
||||
- 单元:签名、时间窗口、canonical hash、幂等重放、冲突、乱序和字段白名单。
|
||||
- 权限:跨租户、普通用户伪造、管理员越权、申请人自证和密钥停用。
|
||||
- PostgreSQL:复合外键、唯一键、append-only、并发同事件、不同 payload 冲突和安全降级。
|
||||
- 集成:申请 → 票据 → 报销 → 预审 → 审批 → 外部付款 → 对账 → ERP 入账 → 归档 → Savings 待确认。
|
||||
- 反向:支付失败不推进、金额/币种错配不推进、重复回执只写一次、退款追加冲回。
|
||||
- adapter:逐场景验证确定性结果、重复请求、跨租户 Claim 隐藏、production 拒绝,以及 Claim/对账/ERP/Business Event/Savings 零副作用。
|
||||
- 可观测性:验证租户隔离、财务/管理员权限、窗口边界、签名失败与重放计数,并断言 DTO/日志无原始 payload、签名和密钥。
|
||||
- 运行事件:验证同 candidate 并发/补偿重试单赢家、不同请求尝试分别计数、冲突回滚后独立持久化、复合租户外键、HMAC 格式检查和数据库 append-only。
|
||||
- 所有验证只在 `local-x-financial-linux` 容器内执行,每条命令最长 60 秒。
|
||||
|
||||
## 算法与公式
|
||||
|
||||
本能力不做概率预测,核心是确定性门禁:
|
||||
|
||||
```text
|
||||
canonical_effect_allowed = (
|
||||
verification_level == production_verified
|
||||
AND signature_valid
|
||||
AND tenant_provider_key_path_bound
|
||||
AND claim_amount_currency_reference_match
|
||||
)
|
||||
```
|
||||
|
||||
任一条件为假都不得产生核心财务副作用;非生产环境无论其他条件是否为真,`canonical_effect_allowed` 固定为 false。
|
||||
|
||||
运行指标使用确定性窗口聚合,不从应用日志估算:
|
||||
|
||||
```text
|
||||
operational_count(type, window) = COUNT(
|
||||
tenant_id = current_tenant
|
||||
AND event_type = type
|
||||
AND window_started_at <= occurred_at <= as_of
|
||||
)
|
||||
|
||||
operational_idempotency_key = SHA256(
|
||||
source_revision, tenant, config, type, reason,
|
||||
HMAC(request), HMAC(external_event), occurred_at
|
||||
)
|
||||
```
|
||||
|
||||
`source_revision=20260716_0022` 表示当前运行指标的数据源与聚合契约版本;它不是 provider 协议版本。发生时间属于本次接收尝试,因此同一 candidate 重试仍稳定,而新尝试会形成新的真实计数。
|
||||
|
||||
## 指标与验收
|
||||
|
||||
- 100% 外部结算事件具有租户、来源、签名版本、payload hash、外部 ID 和接收时间。
|
||||
- 重复相同事件稳定重放,冲突 payload 100% 拒绝。
|
||||
- 任何金额/币种/Claim/审批状态不一致均不会推进已付款。
|
||||
- 外部支付、ERP 凭证、对账处置、归档和 Savings 证据可由 correlation 链回放。
|
||||
- 连接器离线或异常时不出现虚假“已付款”“已入账”或现金节省。
|
||||
- observability 响应 100% 标明窗口起止和 source revision;三类运行事实按租户真实计数,并提供各类最近发生时间。
|
||||
|
||||
## 风险与开放问题
|
||||
|
||||
- 首期真实 provider、签名算法、事件字段和 SLA 需要目标客户确认。
|
||||
- 部分 ERP 只有批次级凭证,需要明确批次到单据的拆分和舍入规则。
|
||||
- 多币种付款需要锁定汇率来源和会计期间;本功能不自行猜测汇率。
|
||||
- 退款、补付、员工自担调整是否进入现金节省,仍需客户财务签字口径。
|
||||
- 对账大额阈值及双人复核需按企业策略配置。
|
||||
|
||||
## 本轮实现记录
|
||||
|
||||
- 2026-07-16:完成现有内部付款、申请归档、Business Event、Savings 实现链路盘点,并冻结统一连接器、安全认证、对账状态和完整 E2E 方案;实现与容器证据保留在同目录 TODO 中继续执行。
|
||||
- 2026-07-16:完成 `0017` 四表迁移、HMAC/密钥版本/时间窗认证、租户隔离、追加式事件、幂等冲突和对账投影;一次性 PostgreSQL 17 迁移循环与 2 项并发探针通过。
|
||||
- 2026-07-16:支付结算只在金额、币种、Claim、审批状态与来源全部匹配时复用正式付款动作;ERP、失败、退款/冲回、Savings 失效和财务处置均进入同一 correlation 审计链,连接器服务/HTTP 7 项通过。
|
||||
- 2026-07-16:真实 provider、批次拆分、汇率、会计期间和大额双人复核仍由目标客户确认;当前 mock/test 证据始终标记 simulated,不冒充生产现金事实。
|
||||
- 2026-07-16:补齐 `0020` 配置版本与追加式审计迁移;新配置只能停用创建,激活前校验服务端密钥,轮换原子切换 key version,HTTP 时间线不暴露 `secret_ref`。
|
||||
- 2026-07-16:把 test/mock/staging 六类事件收口到 `simulation_only` 只读投影;生产事件继续完成付款、ERP、归档、Savings 和冲回,生产冲回也不能引用模拟原事件。
|
||||
- 2026-07-16:normalized payload 删除完整 `claim_reference`,历史冗余值由 `0020` 受控脱敏,HMAC v2 继续以内容哈希和 tenant/provider/key version/path 证明请求边界。
|
||||
- 2026-07-16:容器内连接器/配置/费用价值链组合 16 项、PostgreSQL 并发 3 项、迁移静态 124 项及全新 PostgreSQL 17 完整升降级循环通过;独立 0019→0020 探针确认版本回填、历史脱敏和 legacy 不确定性标记符合契约。
|
||||
- 2026-07-16:新增显式非生产 adapter,以 tenant/config/claim/scenario/request ID 派生稳定签名回执,覆盖成功、失败、乱序、重复、冲突、退款和 ERP 场景;HTTP 与服务测试确认 simulation-only 且 Claim、对账、Business Event、ERP 与 Savings 零副作用。
|
||||
- 2026-07-16:新增租户级脱敏可观测性 API 与财务看板面板,现有 config/event/reconciliation 表提供最后成功、失败率、积压和对账异常真实值;retry/auth_failure 在 `0022` 运行事件落地前明确显示 unavailable,不伪造零值。
|
||||
- 2026-07-16:新增付款证据等级 DTO 和 UI 口径,通过 production-mode 签名契约的回执分类为 `external_cash`,人工确认标为低等级 `internal_manual_payment`,模拟/预发布回执明确不进入核心账;当前本地自签测试不作为真实现金或 provider 接通证据。
|
||||
- 2026-07-17:完成 `0022` 追加式运行事实迁移与服务接入,耐久记录 replay、可信归属后的 auth failure 和 payload conflict;请求与外部事件只保存 HMAC 指纹,失败事务回滚后独立补偿提交。
|
||||
- 2026-07-17:可观测性改为返回 `20260716_0022` source revision、明确窗口、真实计数和最近时间;同一 candidate 补偿重试幂等,不同 HTTP 尝试分别计数。迁移总头继续串到既有 `0023`,`0022` 不越界创建后继 AI 表。
|
||||
- 2026-07-17:全新一次性 PostgreSQL 17 完整迁移循环 51 项、连接器并发 4 项、后继盲审并发 7 项通过;验证租户复合外键、HMAC 格式、运行事实 append-only 和并发单赢家。
|
||||
@@ -0,0 +1,76 @@
|
||||
# 财务连接器与支付对账闭环 TODO
|
||||
|
||||
更新时间:2026-07-17
|
||||
|
||||
## 使用规则
|
||||
|
||||
- 每项必须回链 `CONCEPT.md`;只有代码、迁移、接口或容器验证提供证据后才能勾选。
|
||||
- 外部回执、内部付款状态、ERP 入账和财务确认必须分开,mock 不得伪装成生产现金事实。
|
||||
|
||||
## 1. 契约与安全
|
||||
|
||||
- [x] [CONCEPT: 背景与问题] 盘点内部付款、申请归档、Business Event、Savings 实现和证据边界。
|
||||
证据:`expense_claim_approval_flow.py`、`expense_claim_application_handoff.py`、`expense_cases.py`、`savings_realization.py` 只读审计。
|
||||
- [x] [CONCEPT: 目标与非目标] 冻结签名事件、幂等、对账、ERP 凭证、冲回和 mock 环境边界。
|
||||
证据:`CONCEPT.md`“目标与非目标”“数据与契约”“匹配算法”“降级策略”。
|
||||
- [x] [CONCEPT: 权限与安全] 实现连接器签名认证、密钥版本、时间窗口、来源白名单和防重放。
|
||||
证据:`financial_connector_auth.py`、`financial_connector_ingestion.py`;HMAC 使用常量时间比较,tenant/provider/key version/timestamp/method/path 均进入签名边界,共享密钥跨 provider/key version 重放被拒绝,相同事件幂等重放、冲突 payload 返回 409。
|
||||
- [x] [CONCEPT: 权限与安全] 实现平台配置权限与财务处置权限分离、跨租户 404 和申请人自证拒绝。
|
||||
证据:`financial_connectors.py`、`financial_connector_projection.py`;HTTP/服务回归覆盖普通用户、平台管理员、财务角色、跨租户隐藏与申请人自证拒绝。
|
||||
|
||||
## 2. 数据与迁移
|
||||
|
||||
- [x] [CONCEPT: 数据与契约] 新增配置、外部事件、对账投影和对账事件模型。
|
||||
证据:`models/financial_connector.py`、`schemas/financial_connector.py`。
|
||||
- [x] [CONCEPT: 数据与契约] 新增后继 Alembic 迁移、迁移所有权、复合租户外键、唯一/检查约束和 append-only 触发器。
|
||||
证据:`20260716_0017_financial_connector_reconciliation.py`、`schema_ownership.py`、`migration_preflight.py`;一次性 PostgreSQL 17 完整迁移循环通过。
|
||||
- [x] [CONCEPT: 数据与契约] 实现外部事件与退款/冲回引用、首次响应和 payload 指纹冲突。
|
||||
证据:`financial_connector_ingestion.py`、`payment_reconciliation.py`;同外部事件不同 payload 拒绝,退款/冲回必须绑定同租户已处理结算原事件。
|
||||
- [x] [CONCEPT: 数据与契约] 新增配置 version、追加式配置审计和历史 normalized payload 脱敏迁移。
|
||||
证据:`20260716_0020_financial_connector_config_lifecycle.py`、`FinancialConnectorConfigEvent`;配置审计使用 PostgreSQL append-only trigger,历史 `claim_reference` 从不可变事件的 normalized payload 中受控移除,内容哈希继续保留;全新 PostgreSQL 17 完整升降级循环 1 项通过,另一个独立库验证 0019→0020 历史数据脱敏与 legacy 标识。
|
||||
|
||||
## 3. 服务与接口
|
||||
|
||||
- [x] [CONCEPT: 模块职责] 拆分认证、ingestion、reconciliation、action 和 projection 服务,核心文件不超过 800 行。
|
||||
证据:`financial_connector_auth.py`、`financial_connector_ingestion.py`、`payment_reconciliation.py`、`financial_connector_actions.py`、`financial_connector_projection.py` 职责独立,最大核心文件低于 800 行。
|
||||
- [x] [CONCEPT: 接口] 实现统一事件入口、连接器配置、对账列表/详情、确认和拒绝接口。
|
||||
证据:`api/v1/endpoints/financial_connectors.py`。
|
||||
- [x] [CONCEPT: 接口] 实现带 expected version、actor、request ID、reason 的 activate/disable/rotate 状态机与脱敏审计查询。
|
||||
证据:`financial_connector_config_lifecycle.py`、`financial_connector_config_audit.py`、`FinancialConnectorConfigLifecycleAction`、`FinancialConnectorConfigRotateAction`;激活/轮换前解析服务端密钥并校验强度,轮换原子切换新旧 key version。
|
||||
- [x] [CONCEPT: 匹配算法] 完全匹配时复用现有幂等付款动作;任何金额、币种、单据或审批状态差异不产生付款副作用。
|
||||
证据:`FinancialConnectorActionService` 复用 `ExpenseClaimService.mark_claim_paid_from_connector()`;反向测试验证 mismatch/failure/conflict 无付款副作用。
|
||||
- [x] [CONCEPT: 证据与审计] 把外部事件哈希写入 Expense Case/Business Event/Savings 证据 correlation 链。
|
||||
证据:连接器结算动作写入脱敏内容哈希、verification/evidence classification 与 correlation;服务 E2E 可回放付款、Case、Business Event 和 Savings evidence。
|
||||
- [x] [CONCEPT: 状态转换] 实现支付失败、ERP posted/posting_failed、退款/冲回和对账重开。
|
||||
证据:`PaymentReconciliationService` 对六类生产事件分流;ERP 不重复付款,生产退款/冲回恢复 Claim 并追加 Savings 冲回事实。
|
||||
- [x] [CONCEPT: 降级策略] test/mock/staging 六类事件只写 simulation-only connector fact/response projection,不修改核心财务状态。
|
||||
证据:`financial_connector_simulation.py`、`financial_connector_ingestion.py`;`test_financial_connector_services.py` 参数化覆盖三类非生产环境和六类事件,Claim、申请归档、对账、Business Event、ERP 与 Savings 均保持不变。
|
||||
|
||||
## 4. Mock 与可观测性
|
||||
|
||||
- [x] [CONCEPT: 降级策略] 实现明确标识 test/mock 的 adapter,覆盖成功、失败、乱序、重复、冲突、退款和 ERP 回执。
|
||||
证据:`financial_connector_mock_adapter.py`、`FinancialConnectorSimulationCreate/Read` 与平台管理员 simulate API;仅 active test/mock/staging 可运行,tenant/config/claim/scenario/request ID 确定性派生事件,production、disabled 和跨租户请求 fail-closed;7 场景服务/HTTP 回归通过。
|
||||
- [x] [CONCEPT: 可观测性] 从现有事实输出最后成功时间、失败率、积压和对账异常,并在安全 DTO/UI 声明未采集指标。
|
||||
证据:`financial_connector_observability.py`、当前租户/管理员 observability API、`FinancialConnectorHealthPanel.vue`;所有查询先绑定 tenant,仅聚合 config/event/reconciliation 最小化事实,不返回原始 payload、签名和密钥。
|
||||
- [x] [CONCEPT: 可观测性] 用后继 `0022` 追加式运行事件补齐 replay、auth_failure 和 payload conflict 耐久计数。
|
||||
证据:`20260716_0022_financial_connector_operational_events.py`、`financial_connector_operational_events.py`、`financial_connector_auth.py`、`financial_connector_ingestion.py`、`financial_connector_observability.py`;只有可信配置与服务端密钥解析后才归属认证失败,表内只保存 HMAC 指纹;同 candidate 重试幂等、不同接收尝试分别计数,API 返回窗口、source revision、真实数量和最近时间。
|
||||
- [x] [CONCEPT: 兼容策略] 保留人工付款为低等级内部证据,并在 DTO/UI 区分外部回执和内部确认。
|
||||
证据:`financial_connector_payment_evidence.py`、payment-evidence API、`FinancialPaymentEvidenceRead` 与面板证据口径;人工付款=`internal_manual_payment`,通过 production-mode 契约验证的外部回执分类=`external_cash`,非生产回执=`simulated_connector|staging_connector`。真实 provider 仍待联调。
|
||||
|
||||
## 5. 测试与验收
|
||||
|
||||
- [x] [CONCEPT: 测试方案] 签名、防重放、幂等、冲突、字段白名单和错误恢复单元测试通过。
|
||||
证据:`test_financial_connector_services.py`、`test_financial_connector_endpoints.py`、`test_financial_connector_config_lifecycle.py` 与费用价值链 E2E 共 16 项通过;乱序原事件缺失保守进入 exception,不推进付款。
|
||||
- [x] [CONCEPT: 测试方案] PostgreSQL 迁移、复合租户约束、append-only、并发和安全降级验证通过。
|
||||
证据:fresh PostgreSQL 17 最终迁移总探针 62 项通过;`financial_connector_migration_assertions.py` 验证配置/事件/运行事实 trigger、租户约束、HMAC 格式和 version check;`test_financial_connector_concurrency_postgres.py` 4 项通过,覆盖同事件、冲突补偿事实、配置单版本胜者和 operational candidate 单赢家;最终 head 为 `20260717_0028`。
|
||||
- [x] [CONCEPT: 测试方案] 申请 → 票据 → 报销 → 预审 → 审批 → 外部付款 → 对账 → ERP 入账 → 归档端到端通过。
|
||||
证据:`test_expense_financial_value_chain_e2e.py` 使用测试密钥自签 production-mode HMAC 事件,覆盖申请审批、报销审批、ERP 入账、独立财务确认、Savings、商业价值与退款冲回契约;与连接器定向组合共 16 项通过,不代表真实 provider 回执或现金。
|
||||
- [x] [CONCEPT: 测试方案] 支付失败、金额/币种错配、重复回执和退款反向链路通过。
|
||||
证据:`test_financial_connector_services.py` 覆盖 production-mode 失败/错配无副作用、稳定重放、ERP、reversal/refund 追加 Savings 冲回,以及非生产回执完全不创建 Savings。
|
||||
- [x] [CONCEPT: 容器验证] 相关 pytest、Ruff、前端测试和构建均在 `local-x-financial-linux` 内通过。
|
||||
证据:历史连接器/配置/费用价值链/迁移组合 `140 passed, 1 skipped`;adapter/观测/证据 DTO 与既有连接器回归 `21 passed, 3 skipped`。2026-07-17 的 `0022` 收尾在容器内新增/定向回归 `115 passed`,全新 PostgreSQL 17 完整迁移循环 `51 passed`,连接器并发 `4 passed`,后继 `0023` 并发 `7 passed`;相关 Ruff、文件行数和全树 `git diff --check` 通过。历史连接器前端 3 项与 Vite 生产构建已通过;共享前端曾有 5 项旧路径测试失败,已单独记录且不属于本切片。
|
||||
|
||||
## 6. 客户配置待确认
|
||||
|
||||
- [ ] [CONCEPT: 风险与开放问题] 确认首个 provider、签名算法、字段映射、事件 SLA 和重试窗口。
|
||||
- [ ] [CONCEPT: 风险与开放问题] 确认批次到单据映射、多币种汇率、会计期间、大额双人复核和退款口径。
|
||||
Reference in New Issue
Block a user