This commit is contained in:
2026-08-11 13:55:19 +08:00
parent a5b49293ab
commit 863f9ca395
6 changed files with 114 additions and 252 deletions
@@ -1,7 +1,7 @@
# F1 人工财务契约审查与门禁
> 审查日期:2026-08-11
> 审查范围:Finance Java、Finance 建库 SQL、统一增量 SQL、Admin/Client Finance API、`openapi/finance-f1-manual-v1.yaml`
> 审查范围:Finance Java、Finance 建库 SQL、统一增量 SQL、Admin/Client Finance API、File Java/测试、`openapi/finance-f1-manual-v1.yaml`
> 结论:F0/F1 基础代码已经实现,当前处于专项修复与契约收敛阶段;不是整体未开发。修复项完成前尚未达到完整联调验收/测试放行。
## 1. 已确认且已对齐
@@ -19,7 +19,17 @@
| 权限码 | 平台 12 个按钮权限,客户 2 个按钮权限 | Controller、OpenAPI 和统一 SQL 权限种子已对齐 |
| 表结构 | 账单、修订、收款分配、成本及成本分摊 | Finance SQL 与统一增量 SQL 均存在对应表 |
当前实现包括 Finance Controller/Service、独立财务表、统一 SQL 权限种子、Admin 人工财务页面和 Client 只读账单页面。本文中的 D1-D10 是对现有实现的修复门禁,不是从零建设清单。
当前实现包括 Finance Controller/Service、独立财务表、统一 SQL 权限种子、Admin 人工财务页面和 Client 只读账单页面。本文中的 D1-D11 是对现有实现的修复门禁,不是从零建设清单。
### 1.1 第三轮复验记录(2026-08-11)
| 验证对象 | 命令/范围 | 结果 | 结论 |
|---|---|---|---|
| Finance | `mvn -pl finance -am test` | 通过;Finance 7 个测试通过(1 个源码契约检查、6 个校验单测) | 可证明现有校验和 1:2:3 体积分摊单测通过;不能证明固定 DTO、完整幂等或跨服务闭环 |
| Admin Finance API | `pnpm test -- src/modules/finance/api/finance.test.ts` | 通过;1 个测试文件、7 个测试通过 | D8 字段适配与账单动作封装有直接证据 |
| File | `mvn -pl file -am test` | 失败;`file-service` 运行 0 个测试,JUnit Jupiter 发现阶段失败 | `ProductImageControllerContractTest` 缺少 `AuthenticatedUser`;File 凭证链路无绿色全量测试证据 |
File 失败转储:`baiyue-firstleg/file/target/surefire-reports/2026-08-11T12-07-49_956-jvmRun1.dump`。由于 File 测试未进入执行阶段,Admin 创建收款/成本的真实凭证端到端闭环继续阻断。
## 2. 接口与权限基线
@@ -27,11 +37,12 @@
|---|---|---|
| `GET /api/platform/finance/v1/bills?customerId=` | `platform:finance:bill:view` | 已实现;返回非分页数组 |
| `POST /api/platform/finance/v1/bills` | `platform:finance:bill:create` | 已实现 |
| `GET/PUT /api/platform/finance/v1/bills/{billId}` | `bill:view/update` | 已实现;Admin 尚未接详情/修改 |
| `POST .../{billId}/issue|adjust|void` | `bill:issue/adjust/void` | Java 已实现;Admin 仅已接出账 |
| `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` | 已实现 |
| `GET/POST /api/platform/finance/v1/cost-entries` | `cost:view/create` | 登记已实现;整柜分摊未实现 |
| `GET/POST /api/platform/finance/v1/cost-entries` | `cost:view/create` | 登记及分摊落库已实现;真实体积快照、归属和成本凭证校验未完成 |
| `GET/PUT /api/platform/finance/v1/customers/{tenantId}/terms` | `customer-terms:view/update` | Java 已实现;Admin 尚未接入 |
| `POST /api/platform/finance/v1/vouchers?customerId=` | `receipt: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` | 已实现 |
@@ -44,17 +55,17 @@
| 编号 | 阻断项 | 当前事实 | 完成标准 |
|---|---|---|---|
| D1 | 稳定响应 DTO | Finance 直接返回 JDBC `Map`/`List<Map>`,字段为 snake_case | 按 OpenAPI 1.1.0 输出账单、收款、成本、账期固定 camelCase DTO;所有成功/失败响应带 `requestId/traceId/timestamp` |
| D1 | 稳定响应 DTO | **未完成。** Finance 仍返回 JDBC `Map`/`List<Map>` 和 snake_case;`ApiResponse` 已有 `requestId/traceId/timestamp`,但 Controller 调用 `success(data)` 时成功响应的 `requestId/traceId` 仍为空 | 按 OpenAPI 1.2.0 输出固定 camelCase DTO;所有成功/失败响应的三个追踪字段均为非空有效值,并有序列化契约测试 |
| D2 | 列表形态统一 | Java 为非分页数组;Admin/Client 已做本地分页适配,但仍发送分页/筛选参数 | 首期明确维持非分页并清理无效参数,或后端真正实现统一分页;两端测试与 OpenAPI 同步 |
| D3 | 调整记录可查询 | Java 调整前写 `fin_bill_revision`,再更新当前账单及替换明细,具备数据库快照;但无修订查询接口,产品无法查看调整前后 | 明确“同账单版本修订”即首期方案,并提供调整记录查询/展示;不得无痕覆盖 |
| D4 | 幂等语义 | 重复幂等键目前直接返回冲突,`request_hash` 只哈希键本身 | 同键同参成功后返回原资源;异参返回 `FIN-IDEMPOTENCY-CONFLICT`;`PROCESSING` 返回 409 + `Retry-After` 并由超时恢复任务核对 |
| D5 | 财务凭证链路 | Admin 已主动阻止空凭证保存;Finance 未校验 File,File 也没有财务专用公开上传/内部关联接口 | 实现 OpenAPI 中临时上传、批量校验、幂等关联;严格校验客户、用途、状态、类型和大小,关联失败由 Finance Outbox 补偿 |
| D6 | 整柜体积分摊 | 有 `fin_cost_allocation` 表,但 `createCost` 只写 `fin_cost`,未计算或落分摊 | 同客户有效票按体积、6 位 `HALF_UP` 分摊,最后一票承接尾差;总体积为零才允许人工分摊且合计严格相等 |
| D7 | 成本归属校验 | Java 仅校验成本类型和金额,未强制单票 `orderId`/整柜 `containerId` | 两种类型分别强制唯一归属,且校验对象存在、属于对应客户 |
| D8 | Admin 成本字段映射 | 后端列为 `fee_item_name/cost_amount`,Admin 读取 `expense_name/amount`,列表费用名和金额会为空/0 | Admin 适配真实线协议并增加回归测试 |
| D9 | Admin 完整操作 | 已有创建、列表和出账;缺账单详情/草稿修改/调整/作废、客户账期维护 | 接齐接口、权限按钮、版本冲突和错误态;调整原因、作废原因必填 |
| D4 | 幂等语义 | **部分实现。** 已使用 SHA-256(JSON(request)),支持成功同参返回原资源、异参冲突和 PROCESSING 冲突;但哈希未统一覆盖 `method + path + customer + normalized body`,`updateBill` 的 `String.valueOf(lines)` 不稳定,且缺 `Retry-After`、超时恢复、FAILED 处理和真实回归测试 | 全部写操作使用完整规范化请求哈希;固定冲突码;补同参、异参、PROCESSING、恢复和并发测试 |
| D5 | 财务凭证链路 | **未完成。** 现有上传为 `POST /api/platform/finance/v1/vouchers`,仅图片且权限为 `receipt:create`;收款调用校验但 `fileClient == null` 时 fail-open,成本未调用校验;无关联接口/状态;File 全量测试发现失败 | 收款和成本均 fail-closed 校验文件存在、客户、业务类型、MIME 和状态;成本角色可上传;建立幂等关联或明确等价持久化关系;File 全量测试通过 |
| D6 | 整柜体积分摊 | **部分实现。** 已有 `ContainerOrderVolume`、`fin_cost_allocation` 落库及 6 位 `HALF_UP` 测试;但公开请求和 Admin 允许人工填 `volumeCbm`,没有 Firstleg 真实快照;归属由请求自证;最后一票无实际尾差也标记 `roundingAdjustment=true`,且未保存真实 `roundingDelta` | 服务端从 Firstleg 取得不可篡改体积快照并验证柜次/订单/客户关系;只有 `tailAmount != round(costAmount × tailRatio, 6, HALF_UP)` 时标记尾差并保存 delta;总和严格等于成本 |
| D7 | 成本归属校验 | **未完成。** 单票只检查 `orderId` 非空;整柜请求的 `customerId` 被直接复制到各分摊行,未验证订单、柜次及入柜关系 | 单票订单存在且属于客户;整柜存在且订单实际在柜内,并全部属于该客户;任何不一致整单拒绝 |
| D8 | Admin 成本字段映射 | **定向验证通过。** Admin 已兼容 `fee_item_name/expense_name` 与 `cost_amount/amount`;Finance API 测试 7/7 通过 | 保持回归测试;不得据此宣称成本创建端到端闭环通过 |
| D9 | Admin 完整操作 | **部分实现。** 已有详情、草稿修改、调整、作废、出账、凭证选择/上传和收款/成本创建;未接客户账期和修订历史,整柜体积仍人工填写,成本凭证后端不校验,File 全量测试失败 | 接客户账期和修订历史;改为服务端真实体积快照;File/Finance 凭证校验通过并完成真实环境创建闭环 |
| D10 | 业务错误码 | 文档定义 `FIN-AR-*`/`FIN-COST-*`;Java 当前主要返回通用 BAD_REQUEST/CONFLICT + 中文消息 | 后端建立稳定业务码并由 Admin/Client 按码处理,不依赖消息文本 |
| D11 | 客户状态可见性 | Client Controller 当前按客户隔离,但账单列表/详情未过滤状态 | 客户只可见 `ISSUED/PARTIALLY_PAID/PAID`;草稿、作废、跨客户和不存在详情统一按不存在处理 |
| D11 | 客户状态可见性 | **部分实现。** 账单列表/详情 SQL 已过滤 `ISSUED/PARTIALLY_PAID/PAID`;收款列表只按客户过滤,仍可返回 `voucher_file_ids`、`voucher_no`、内部状态等平台字段;DRAFT/VOIDED 详情异常尚未固定映射 `FIN-AR-001` | 客户账单及其收款只来源于三种可见状态;使用专用只读 DTO,去除凭证和内部字段;不可见/跨客户/不存在统一 `FIN-AR-001` |
## 4. 非阻断但需锁定的契约说明
@@ -82,6 +93,6 @@
- **当前阶段:专项修复中。** Finance、SQL、Admin 和 Client 均已有实现,不再按“未开发”管理。
- **可继续开发:是。** 路径、权限、固定 DTO、凭证、分摊、幂等和客户可见性已经冻结,不再需要新增产品决策。
- **可进行接口冒烟:有条件。** 可验证现有路径、鉴权和基础创建/查询,但结果不代表业务验收通过。
- **可进入完整联调验收:否。** 主要阻断为整柜成本分摊、凭证服务端校验、Admin 完整操作、稳定 DTO/错误码和幂等重放。
- **可进入完整联调验收:否。** 主要阻断为真实体积快照与归属校验、收款/成本凭证 fail-closed 校验、File 全量测试故障、Admin 创建闭环、稳定 DTO/错误码、完整幂等和客户收款 DTO。
本次审查只修订文档与 OpenAPI,未修改 Java、前端或 SQL。