Files
docs/财务管理/F1人工财务契约审查与门禁.md
T

126 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# F1 人工财务契约审查与门禁
> 审查日期:2026-08-11
> 审查范围:Finance Java、Finance 建库 SQL、统一增量 SQL、Admin/Client Finance API、File Java/测试、`openapi/finance-f1-manual-v1.yaml`
> 结论:F0/F1 基础代码已经实现,当前处于专项修复与契约收敛阶段;不是整体未开发。修复项完成前尚未达到完整联调验收/测试放行。
> 范围重定:领导验收改为《财务核心五页产品与接口验收契约》中的 5 页。本文 D1-D11 继续作为既有人工接口专项门禁,并新增来源链路和五页门禁。
## 1. 已确认且已对齐
| 范围 | 目标 | 当前结论 |
|---|---|---|
| 产品边界 | 人工账单、人工收款、人工成本,不做汇率、月结和自动认款 | 对齐 |
| 币种 | 只支持 CNY | Controller 校验、OpenAPI 枚举和 SQL CHECK 均为 CNY |
| 客户账期 | 每客户配置,默认 30 个自然日 | Finance 已有 GET/PUT;未配置返回 30;范围 0-3650 天 |
| 到期日 | `billDate + creditDays` | Finance 创建/修改草稿时已计算 |
| 出账冻结 | 只有 DRAFT 可直接修改,出账后走调整 | Finance 更新 SQL 限制 `status='DRAFT'`,已对齐基本门禁 |
| 收款分配 | 一笔 300 元可分配 3 张各 100 元账单 | 已校验分配合计、客户归属、状态和未收余额,并在同一事务更新三张账单 |
| 客户权限 | 客户只读且只能看本客户 | Client Controller 从认证上下文取客户范围,无客户写接口 |
| 成本类型 | 单票、整柜公共成本 | Java/OpenAPI/Admin 均统一为 `SINGLE_ORDER/CONTAINER_SHARED` |
| 权限码 | 平台 12 个按钮权限,客户 2 个按钮权限 | Controller、OpenAPI 和统一 SQL 权限种子已对齐 |
| 表结构 | 账单、修订、收款分配、成本及成本分摊 | Finance SQL 与统一增量 SQL 均存在对应表 |
当前实现包括 Finance Controller/Service、独立财务表、统一 SQL 权限种子、Admin 人工财务页面和 Client 只读账单页面。本文中的 D1-D11 是对现有实现的修复门禁,不是从零建设清单。
### 1.1 后端最终实现复验记录(2026-08-11,基线 `7c2274b`)
| 验证对象 | 命令/范围 | 结果 | 结论 |
|---|---|---|---|
| Finance | 后端历史基线复验 | 12 个测试通过 | 历史基线记录;固定 DTO、来源消费和完整幂等已由下方最新 35/35 结果覆盖 |
| Firstleg | 后端全量复验 | 527 个测试通过 | 真实柜次体积快照、客户过滤和无有效体积拒绝有直接证据 |
| File | 后端全量复验 | 114 个测试通过 | 原 JUnit 测试发现故障已解决,不再作为阻断 |
| Gateway | 后端全量复验 | 26 个测试通过 | 本次相关网关回归通过 |
| IAM | 后端全量复验 | 163 个测试通过 | 本次相关权限回归通过 |
| Admin Finance API | `pnpm test -- src/modules/finance/api/finance.test.ts` | 通过;1 个测试文件、7 个测试通过 | D8 字段适配与账单动作封装有直接证据 |
以上计数来自总控最终后端复验。尚未提供测试环境中 Finance → Firstleg/File 的真实 HTTP 联调报告,因此跨服务部署连通性仍需在联调阶段验证,但不再视为代码实现阻断。
### 1.2 五页与费用来源主链增量复验(2026-08-11)
| 验证对象 | 当前 Surefire 证据 | 结果 |
|---|---|---|
| Finance 全模块 | 10 个测试类:费用来源消费、内部 Controller、五页 DTO、Client 脱敏、异常码、File fail-closed、SQL 契约等 | **35/35,BUILD SUCCESS** |
| Finance 来源消费 | `FinanceFeeSourceEventServiceTest`、`InternalFinanceFeeSourceControllerTest` | **10/10 + 2/2 通过**;覆盖入库/柜子来源写入、调整替代、作废保留、同参重放、异参冲突、跨客户、未发布、零金额和负金额拒绝 |
| Firstleg | 全量复验 549 项(2 skip);其中事件客户端、Outbox relay、快照、柜子费用、迁移契约等 7 个测试类 17 项 | **549/549 通过,2 skip;来源定向 17/17 通过** |
| 其他直接消费者与 SQL 契约 | File、Gateway、IAM、Phase3/PDA SQL、Order SQL | **114/114、26/26、163/163、12/12、3/3,均 BUILD SUCCESS** |
| 五页固定 DTO 与脱敏 | `FinanceReadApiBehaviorTest`、`FinanceFeePoolDirectionBehaviorTest`、`FinanceExceptionHandlerTest` | **3/3 + 2/2 + 2/2 通过** |
本轮文档任务未重新执行 Maven;以上结果直接读取当前工作树已有 Surefire 报告,不把“测试源码存在”替代为“测试通过”。
## 2. 接口与权限基线
| 接口 | 权限 | 当前状态 |
|---|---|---|
| `GET /api/platform/finance/v1/bills?customerId=` | `platform:finance:bill:view` | 已实现;返回非分页数组 |
| `POST /internal/finance/v1/fee-source-events` | `X-Internal-Service-Token` | 已实现并验证;Inbox 幂等消费后写入/替代/作废 `fin_fee_source`,Finance 消费/Controller 12 项定向测试通过 |
| `GET /api/platform/finance/v1/customer-accounts/{customerId}` | `platform:finance:bill:view` | 已实现固定 `CustomerAccount` DTO;Finance DTO 行为测试通过 |
| `GET /api/platform/finance/v1/fee-pool?customerId=&direction=` | `platform:finance:bill:view` | 已实现固定 `FeePoolItem` DTO;方向和客户范围测试通过 |
| `GET /api/platform/finance/v1/statements?customerId=` | `platform:finance:bill:view` | 已实现固定 `Statement` DTO;生成/审核操作未实现 |
| `GET /api/platform/finance/v1/receivable-settlements?customerId=` | `platform:finance:bill:view` | 已实现固定 `ReceivableSettlement` DTO;生成操作未实现 |
| `GET /api/platform/finance/v1/payable-settlements?customerId=` | `platform:finance:cost:view` | 已实现固定 `PayableSettlement` DTO;审核/付款操作未实现 |
| `POST /api/platform/finance/v1/bills` | `platform:finance:bill:create` | 已实现 |
| `GET/PUT /api/platform/finance/v1/bills/{billId}` | `bill:view/update` | Java 和 Admin 均已接入;缺修订历史查询 |
| `POST .../{billId}/issue|adjust|void` | `bill:issue/adjust/void` | Java 和 Admin 均已接入;端到端闭环未放行 |
| `GET/POST /api/platform/finance/v1/receipts` | `receipt:view/create` | 已实现平台 `PlatformReceipt` 查询 DTO、多账单分配和凭证 fail-closed;Finance 测试通过 |
| `GET/POST /api/platform/finance/v1/cost-entries` | `cost:view/create` | 已实现;整柜由 Finance 调 Firstleg 真实快照,成本凭证和归属均校验 |
| `GET/PUT /api/platform/finance/v1/customers/{tenantId}/terms` | `customer-terms:view/update` | Java 已实现;Admin 尚未接入 |
| `GET /api/platform/finance/v1/bills/{billId}/revisions?customerId=` | `bill:view` | 已实现,并校验账单客户归属;Admin 尚未接入 |
| `POST /api/platform/finance/v1/vouchers?customerId=` | `receipt:create` 或 `cost:create` | 已实现图片上传,双权限兼容 |
| `GET /api/client/finance/v1/bills` | `merchant:finance:statement:list` | 已实现 |
| `GET /api/client/finance/v1/bills/{billId}` | `merchant:finance:statement:view` | 已实现 |
| `GET /api/client/finance/v1/receipts` | `merchant:finance:statement:view` | 已实现 |
`tenantId` 只保留为后端兼容路径字段,产品语义统一为客户 ID,不增加用户可见的“租户”层级。
## 3. 门禁状态(已解决与剩余)
本表保留 D1-D11 的稳定编号用于总控追踪。标为“后端完成/定向验证通过”的项目不再作为代码实现阻断;其余项目完成前不能宣称 F1 已具备完整业务闭环。
| 编号 | 阻断项 | 当前事实 | 完成标准 |
|---|---|---|---|
| D1 | 稳定响应 DTO | **后端完成。** 五页查询使用 `FinanceReadModels`、`FinanceQueryService` 和 `FinanceResponses`;平台/客户收款使用专用 DTO,成功/失败均带追踪字段;Finance 35/35 通过 | 后端门禁关闭;Admin 继续按固定 camelCase DTO 接入 |
| D2 | 列表形态统一 | **契约已冻结。** 首期维持非分页数组,Admin/Client 在适配层本地分页,OpenAPI 与 Java 一致 | 清理前端无效分页参数属于非阻断整理,不再要求后端改为分页对象 |
| D3 | 调整记录可查询 | **后端完成。** 调整前写 `fin_bill_revision`;`GET /bills/{billId}/revisions?customerId=` 已落地并校验客户归属,Finance 测试覆盖同客户和跨客户场景 | Admin 接入展示后完成产品闭环;后端门禁已通过 |
| D4 | 幂等语义 | **后端完成。** 幂等记录按客户、操作人、operation 和 key 隔离,请求哈希覆盖 method、path 和序列化 body;支持成功同参重放、异参 `FIN_409`、PROCESSING 冲突、超时 PROCESSING 抢占恢复和 FAILED 恢复 | 后端门禁关闭;保持写操作回归测试 |
| D5 | 财务凭证链路 | **后端完成。** 上传路径和双权限已对齐;File 客户端缺失返回 `FIN_503`,凭证归属/MIME/业务类型校验保持 fail-closed;Finance 35/35 通过 | 后端门禁关闭;测试环境保留一次真实 File HTTP 冒烟 |
| D6 | 整柜体积分摊 | **后端完成。** 公共请求只提交 `containerId`;Finance 调 Firstleg 真实柜次快照,按 6 位 `HALF_UP` 分摊并落 `fin_cost_allocation`;只有最后一票实际偏离常规舍入结果时才标记尾差 | Finance 12、Firstleg 527 测试通过;测试环境补一次真实跨服务冒烟即可进入联调验收 |
| D7 | 成本归属校验 | **后端完成。** Firstleg 只返回柜内属于客户且体积有效的订单;Finance 再以 `fin_fee_source` 校验单票/整柜订单属于客户 | 单票、整柜客户归属及无效快照测试通过 |
| D8 | Admin 成本字段映射 | **定向验证通过。** Admin 已兼容 `fee_item_name/expense_name` 与 `cost_amount/amount`;Finance API 测试 7/7 通过 | 保持回归测试;不得据此宣称成本创建端到端闭环通过 |
| D9 | Admin 完整操作 | **部分实现。** 已有详情、草稿修改、调整、作废、出账、凭证选择/上传和收款/成本创建;未接客户账期和 revision;整柜表单仍强制人工填写 `containerOrders/volumeCbm`,与最终后端只接 `containerId` 的契约不一致 | 删除人工体积录入,只提交 `containerId`;接客户账期和 revision;完成真实环境创建闭环 |
| D10 | 业务错误码 | **后端完成。** 通用财务异常固定为 `FIN_400/404/409/503/001`,来源事件固定为 `FIN-EVT-001` 至 `015`、`FIN-EVT-401`;未发布稳定返回 `FIN-EVT-015`,金额/币种错误返回 `FIN-EVT-010` | 后端门禁关闭;Admin/Client 按码处理,不依赖中文消息 |
| D11 | 客户状态可见性 | **后端完成。** 客户账单只见 `ISSUED/PARTIALLY_PAID/PAID`;客户收款只见 `CONFIRMED`,使用 `ClientReceipt` 专用 DTO 并去除凭证、操作人和内部字段;脱敏测试通过 | 后端门禁关闭;保留跨客户与序列化回归 |
| D12 | Finance 费用来源写入 | **后端完成。** 内部事件入口、令牌鉴权、Inbox 幂等、Firstleg 快照回查、来源写入/替代/作废均已实现;非作废事件必须 `PUBLISHED`,未发布返回 `FIN-EVT-015`;金额必须 `>0`,零/负返回 `FIN-EVT-010`;专项 10/10、Finance 全量 35/35 通过 | 后端门禁关闭;保留真实跨服务 HTTP 冒烟 |
| D13 | 柜子费用来源 | **后端完成并验证。** Firstleg 已有柜子费用模型、审核/参与结算/作废、事件、Relay 和内部快照;Firstleg 相关 17/17、Finance 柜子来源消费测试通过 | 后端门禁关闭;测试环境补真实跨服务事件冒烟 |
| D14 | 订单与客户 5 页 | **FAIL / 订单与客户 P0 未关闭。** 11 个目标 operation 契约已冻结;实现状态不再写入 OpenAPI 或主契约,G1-G4 证据矩阵当前均未放行 | G1-G4 分别完成组内全部后端直接行为测试和 Admin 真实接入,证据矩阵更新为 `PASS` 后再完成五页端到端验收 |
## 4. 非阻断但需锁定的契约说明
- `UpdateBillRequest` 与当前 Java 一致,包含 `customerId`、`billDate`、`lines`、`expectedVersion`。
- 平台账单列表的 `customerId` 必填;收款、成本列表可不传客户,供平台总览。
- 收款分配字段在请求中为 `allocations[].amount`;数据库返回联表字段为 `applied_amount`。对外 DTO 应统一一个命名,建议请求继续用 `amount`,响应用 `allocatedAmount`。
- 已收账单不得作废;当前 Java 只允许 `DRAFT/ISSUED` 且 `received_amount=0` 作废,符合首期简化原则。
- 调整后总额不得低于已收金额;这条现有服务端约束应保留。
- 客户停用才影响客户账单访问;客户子用户停用只影响该用户登录,不改变账单的客户主体归属。
## 5. 可测试门禁
进入前后端联调前至少满足:
1. D1-D8、D10-D13 已具备后端直接证据;D14 当前 FAIL,G1-G4 任一组未同时通过后端行为测试与 Admin 真实接入,均不得通过订单与客户完整联调验收。
2. D2 已固定为首期非分页数组;后续若改服务端分页须另行升级契约。
3. 幂等门禁已关闭;后续写 operation 继续复用同参重放、异参冲突和恢复语义。
4. Finance 集成测试覆盖:默认 30 天、CNY 拒绝、出账冻结、300 分 3 单、超额/跨客户/草稿拒绝、部分收款到收清、调整修订、已收不可作废。
5. 成本测试覆盖:单票归属、真实柜次快照、整柜按体积分摊、无有效体积拒绝、实际舍入尾差、跨客户对象拒绝。
6. 权限测试覆盖平台 12 个权限码和客户 2 个权限码,并验证客户无法写入及无法跨客户查询。
7. 客户查询测试证明列表、详情和收款均不会暴露 `DRAFT/VOIDED`、平台凭证 fileId 或内部备注。
## 6. 放行结论
- **当前阶段:专项修复中。** Finance、SQL、Admin 和 Client 均已有实现,不再按“未开发”管理。
- **可继续开发:是。** 11 个 P0 的路径、权限、DTO、状态、凭证、幂等、错误码和客户隔离已经冻结,不再需要新增产品决策。
- **可进行接口冒烟:有条件。** 可验证现有路径、鉴权和基础创建/查询,但结果不代表业务验收通过。
- **可进入完整联调验收:否。** Finance 来源写入等既有门禁已通过;11 个 P0 的 G1-G4 证据矩阵尚未全部 `PASS`,Admin 五页真实接入和跨服务冒烟也未完成。开发中间态不改变该结论。
本次审查只修订文档与 OpenAPI,未修改 Java、前端或 SQL。