Files
docs/财务管理/F1人工财务契约审查与门禁.md
T
2026-08-11 13:55:19 +08:00

99 lines
11 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 基础代码已经实现,当前处于专项修复与契约收敛阶段;不是整体未开发。修复项完成前尚未达到完整联调验收/测试放行。
## 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)
| 验证对象 | 命令/范围 | 结果 | 结论 |
|---|---|---|---|
| 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. 接口与权限基线
| 接口 | 权限 | 当前状态 |
|---|---|---|
| `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` | 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/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` | 已实现 |
`tenantId` 只保留为后端兼容路径字段,产品语义统一为客户 ID,不增加用户可见的“租户”层级。
## 3. 开发门禁阻断项
以下项目未完成前,可以继续编码,但不能宣称 F1 已具备完整业务闭环。
| 编号 | 阻断项 | 当前事实 | 完成标准 |
|---|---|---|---|
| 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 | 幂等语义 | **部分实现。** 已使用 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 | 客户状态可见性 | **部分实现。** 账单列表/详情 SQL 已过滤 `ISSUED/PARTIALLY_PAID/PAID`;收款列表只按客户过滤,仍可返回 `voucher_file_ids`、`voucher_no`、内部状态等平台字段;DRAFT/VOIDED 详情异常尚未固定映射 `FIN-AR-001` | 客户账单及其收款只来源于三种可见状态;使用专用只读 DTO,去除凭证和内部字段;不可见/跨客户/不存在统一 `FIN-AR-001` |
## 4. 非阻断但需锁定的契约说明
- `UpdateBillRequest` 与当前 Java 一致,包含 `customerId`、`billDate`、`lines`、`expectedVersion`。
- 平台账单列表的 `customerId` 必填;收款、成本列表可不传客户,供平台总览。
- 收款分配字段在请求中为 `allocations[].amount`;数据库返回联表字段为 `applied_amount`。对外 DTO 应统一一个命名,建议请求继续用 `amount`,响应用 `allocatedAmount`。
- 已收账单不得作废;当前 Java 只允许 `DRAFT/ISSUED` 且 `received_amount=0` 作废,符合首期简化原则。
- 调整后总额不得低于已收金额;这条现有服务端约束应保留。
- 客户停用才影响客户账单访问;客户子用户停用只影响该用户登录,不改变账单的客户主体归属。
## 5. 可测试门禁
进入前后端联调前至少满足:
1. D1、D3、D5、D6、D7、D8、D9、D10、D11 已完成并有直接测试。
2. D2 选定“首期非分页”或“服务端分页”之一,OpenAPI、Admin、Client 三方一致。
3. D4 至少完成账单创建、出账和收款登记的同键重放测试。
4. Finance 集成测试覆盖:默认 30 天、CNY 拒绝、出账冻结、300 分 3 单、超额/跨客户/草稿拒绝、部分收款到收清、调整修订、已收不可作废。
5. 成本测试覆盖:单票归属、整柜按体积分摊、零体积人工处理、舍入尾差、跨客户对象拒绝。
6. 权限测试覆盖平台 12 个权限码和客户 2 个权限码,并验证客户无法写入及无法跨客户查询。
7. 客户查询测试证明列表、详情和收款均不会暴露 `DRAFT/VOIDED`、平台凭证 fileId 或内部备注。
## 6. 放行结论
- **当前阶段:专项修复中。** Finance、SQL、Admin 和 Client 均已有实现,不再按“未开发”管理。
- **可继续开发:是。** 路径、权限、固定 DTO、凭证、分摊、幂等和客户可见性已经冻结,不再需要新增产品决策。
- **可进行接口冒烟:有条件。** 可验证现有路径、鉴权和基础创建/查询,但结果不代表业务验收通过。
- **可进入完整联调验收:否。** 主要阻断为真实体积快照与归属校验、收款/成本凭证 fail-closed 校验、File 全量测试故障、Admin 创建闭环、稳定 DTO/错误码、完整幂等和客户收款 DTO。
本次审查只修订文档与 OpenAPI,未修改 Java、前端或 SQL。