From 767ffe8544db7e9f5129e8440f513006a91c9ffc Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Tue, 11 Aug 2026 18:06:30 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- 数据库注释维护规范.md | 96 ++ 独立任务协作登记.md | 71 +- 财务管理/F1人工财务契约审查与门禁.md | 75 +- 财务管理/F1人工账单收款成本接口契约.md | 26 +- 财务管理/Firstleg-Finance跨服务契约.md | 50 +- 财务管理/README.md | 65 +- 财务管理/openapi/finance-f1-manual-v1.yaml | 112 +- .../openapi/finance-phase1-five-pages-v1.yaml | 1170 +++++++++++++++++ 财务管理/财务一期五页产品与接口验收契约.md | 174 +++ 财务管理/财务开工门禁与F0-F1任务清单.md | 14 +- 财务管理/财务服务边界设计.md | 2 + .../财务管理产品确认版差异与剩余待确认.md | 18 +- 财务管理/财务管理产品确认表.md | 69 +- 财务管理/财务领域模型与数据字典.md | 2 + 财务管理/财务领域模型与跨服务契约.md | 2 + 16 files changed, 1765 insertions(+), 184 deletions(-) create mode 100644 数据库注释维护规范.md create mode 100644 财务管理/openapi/finance-phase1-five-pages-v1.yaml create mode 100644 财务管理/财务一期五页产品与接口验收契约.md diff --git a/README.md b/README.md index 92bda21..e18e526 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,12 @@ - [一期文档](一期/README.md):一期范围、开发进度和头程订单最小闭环。 - [二期文档](二期/README.md):预约取货、到仓收货、实测差异和指定库位闭环。 - [三期文档](三期/README.md):智能排柜、订舱、拖车、PDA 拣货、报关协同和国内出仓闭环。 -- [独立任务协作登记](独立任务协作登记.md):登记独立 Codex 任务 ID,并作为总控下发、通信和汇总的唯一寻址索引。 +- [独立任务协作登记](独立任务协作登记.md):以稳定 `routing_key + task_id` 维护长期复用通道,并保留一次性任务执行历史。 ## 跨阶段参考 - [财务管理闭环盘点与范围建议](财务管理/README.md):基于当前入库计费、财务原型和现有代码盘点财务闭环、缺口及建议的首期应收边界。 +- [数据库中文注释维护规范](数据库注释维护规范.md):数据库注释口径、职责、迁移流程、自动门禁与实值验收要求。 - [测试环境 Jenkins 部署指南](测试环境Jenkins部署指南.md):测试环境持续部署授权、Job 参数、执行边界、健康检查与失败处理。 - `悦/`:产品原型、流程图、历史架构资料和调研数据,仅作为需求参考,不直接等同于已确认范围。 - `superpowers/`:具体功能的设计与实施记录,按日期和主题维护。 diff --git a/数据库注释维护规范.md b/数据库注释维护规范.md new file mode 100644 index 0000000..512b47b --- /dev/null +++ b/数据库注释维护规范.md @@ -0,0 +1,96 @@ +# 数据库中文注释维护规范 + +## 目标与适用范围 + +本规范适用于 `baiyue-firstleg/` 各服务拥有的 MySQL 业务表。目标是让表和字段语义可以直接从数据库元数据理解,并通过自动检查防止新增无注释结构。 + +SQL 是数据库结构和注释的事实来源。数据字典应从 `information_schema` 生成,不维护 Excel、Markdown 字段清单等并行事实来源。 + +## 职责 + +- 数据库注释总控:维护本规范、统一术语、审计整体覆盖率、协调统一部署 SQL,并完成最终验收。 +- 模块负责人:依据 Controller、DTO、Service、状态机和已确认需求解释本模块字段,维护模块 SQL。 +- 业务审核人:确认状态、金额、时间、快照、审计和数据隔离字段的业务口径。 +- 发布执行人:按发布规范执行已审核的幂等迁移;未经当前对话授权不得直接修改测试、预发布或生产数据库。 + +## 必须遵守的规则 + +1. 每张业务表和每个业务字段都必须有非空中文注释。 +2. 英文缩写和枚举值可以保留,但注释必须包含中文业务含义。 +3. 模块 `src/main/resources/db/`、统一部署 SQL 和实际数据库的注释必须一致。 +4. 同一张表在基线与迁移脚本中重复定义时,每个可执行定义都必须有完整注释。 +5. 注释变更不得改变字段类型、长度、精度、字符集、默认值、可空性、索引或约束。 +6. `CREATE TABLE IF NOT EXISTS` 不会更新存量表注释;存量环境必须使用可重复执行的兼容迁移。 +7. 注释不得写密钥、令牌、客户真实数据、环境地址或其他敏感值。 + +## 中文口径 + +| 字段类型 | 注释要求 | 示例 | +|---|---|---| +| `tenant_id` | 客户数据统一说明客户与隔离语义;公共数据明确平台作用域 | `客户ID和数据隔离键`;`平台公共数据作用域,固定为0` | +| 状态 | 列出允许值及每个值的中文含义 | `状态:DRAFT草稿、ISSUED已出账、PAID已收清` | +| 金额 | 说明金额口径、币种来源,必要时说明含税口径 | `账单含税总金额;币种由currency_code指定` | +| 时间 | 说明事件语义,不只写“时间” | `财务确认实际收款时间` | +| JSON | 说明结构、元素类型、来源及敏感性 | `File服务文件ID字符串数组` | +| 快照 | 说明来源和是否随源数据回写 | `创建账单时的客户名称快照;客户档案变化不回写` | +| 布尔值 | 明确 `0` 和 `1` 的含义 | `尾差归集标记:0否、1是` | +| ID | 说明关联对象和逻辑属性 | `Firstleg入库单逻辑ID,不建立跨库外键` | +| 版本 | 说明版本用途 | `乐观锁版本号,每次更新递增` | +| 审计 | 区分业务操作人、系统创建人和链路标识 | `执行本次业务操作的平台用户ID` | + +不要把 `tenant_id` 用户可见地解释为“租户”。当前产品只有“平台、客户”两级,后端兼容字段不能扩展产品层级。 + +## 变更流程 + +1. 盘点模块 SQL 中缺失、空白或纯英文注释。 +2. 根据需求确认文档和真实代码确认字段语义;不凭字段名猜测高风险业务口径。 +3. 先更新模块 SQL,再由总控同步统一部署 SQL。 +4. 为存量环境编写幂等注释迁移。MySQL `MODIFY COLUMN` 必须完整复述原类型、默认值、可空性及其他属性。 +5. 对比迁移前后 `information_schema.columns`,除 `column_comment` 外的结构签名必须完全一致。 +6. 运行静态审计、模块测试、统一 SQL 空库与重复执行验证。 +7. 从实际数据库元数据生成数据字典和覆盖率报告。 + +## 自动审计 + +在 `baiyue-firstleg` 根目录执行: + +```powershell +.\scripts\database-comment-audit.ps1 +.\scripts\database-comment-audit.ps1 -Modules finance,order +.\scripts\database-comment-audit.ps1 -Modules finance -SummaryOnly +.\scripts\database-comment-audit.ps1 -Format Json +.\scripts\database-comment-audit.ps1 -FailOnIssues +.\scripts\database-comment-audit.ps1 -SelfTest +``` + +脚本只读取模块 `src/main/resources/db/*.sql`,不连接数据库、不启动服务、不执行 SQL。默认输出报告且退出码为 `0`;CI 使用 `-FailOnIssues`,发现缺失或纯英文注释时退出码为 `1`;配置或路径错误退出码为 `2`。 + +当前存量缺口允许通过不带 `-FailOnIssues` 的报告模式逐模块治理。模块达到 100% 后,应在其 CI 中启用强制门禁,禁止覆盖率回退。 + +## 数据库实值验收 + +部署后使用只读元数据查询验收: + +```sql +SELECT table_schema, table_name, table_comment + FROM information_schema.tables + WHERE table_schema LIKE 'baiyue_%' + AND table_type = 'BASE TABLE' + ORDER BY table_schema, table_name; + +SELECT table_schema, table_name, column_name, column_type, is_nullable, + column_default, extra, column_comment + FROM information_schema.columns + WHERE table_schema LIKE 'baiyue_%' + ORDER BY table_schema, table_name, ordinal_position; +``` + +数据字典以第二个查询为基础,可追加索引元数据;不得把生成文件改造成另一份手工契约。 + +## 验收门槛 + +- 目标模块表注释和字段中文注释覆盖率均为 100%。 +- 模块 SQL、统一部署 SQL和数据库实值一致。 +- 空库执行、存量升级和重复执行均通过。 +- 注释迁移前后,除注释外的表结构零变化。 +- 新增 SQL 在 CI 强制模式下通过。 diff --git a/独立任务协作登记.md b/独立任务协作登记.md index 7dcf5e7..13c89bf 100644 --- a/独立任务协作登记.md +++ b/独立任务协作登记.md @@ -1,25 +1,42 @@ # 独立任务协作登记 -> 本表只登记用户可独立打开和查看的 Codex 任务/对话,不登记子代理。任务 ID 是唯一寻址键,名称只是展示标签。 +> 本文件登记用户可独立打开和查看的 Codex 任务/对话,不登记子代理。长期复用通道负责创建前路由,执行历史只负责追溯;任务名称始终只是展示标签。 + +## 长期复用通道 + +总控下发独立工作前必须先查本表。`idle`、`active`、`blocked` 都表示通道可复用;只有 `archived` 表示不可继续下发。同一 `routing_key` 只能保留一个未归档任务。 + +| routing_key | task_id | display_name | durable_scope | owner | status | last_update | notes | +|---|---|---|---|---|---|---|---| +| `phase3.control.release-acceptance` | `019fdf57-2f16-7cf1-88f0-a75854ed7629` | 三期总控|发布与验收 | 三期发布、测试环境验收、阻断分类与最终汇总 | 三期总控 | idle | 2026-08-11 | 后续三期发布与验收优先向此 ID 续发;一次性复验或部署默认使用子代理 | +| `finance.control` | `019feaa3-9f10-7ab0-8023-4b121ff9662c` | 财务模块-总控 | 财务范围、跨端契约、后端/Admin/Client 协调与汇总 | 财务总控 | active | 2026-08-11 | Finance一期最终验收FAIL;正在协调11个一期P0 operation返工与独立复验 | +| `database-comments.control` | `019fef70-b644-7071-8b76-975b8940985c` | 数据库注释治理 | 数据库中文注释规范、模块分派、统一 SQL 集成与总体验收 | 数据库注释总控 | idle | 2026-08-11 | 后续数据库注释工作优先向此 ID 续发;模块盘点和修改默认使用子代理 | + +### 通道路由规则 + +- `routing_key` 按长期职责确定,格式为 `..`;不包含序号、日期、批次、提交号或本轮动作。 +- 命中未归档通道后直接使用其 `task_id` 通信;标题相同或不同均不影响寻址。通道开始新一轮工作时改为 `active`,一轮工作结束后回到 `idle`。 +- `completed` 只用于下方一次性执行历史,不是长期通道状态。长期通道不得因为一轮工作完成而失去复用资格。 +- 用户未明确要求新增独立对话时,未命中通道也不自动创建;使用当前任务或子代理完成工作。 +- 永久停用才标记 `archived`。创建替代通道时,在新旧记录备注中互相写明 ID,不覆盖或复用旧 ID。 ## 登记规则 -- 独立任务创建成功并获得平台返回的稳定 ID 后,立即登记;没有稳定 ID 不得写入临时名称或自行生成替代 ID。 -- 总控下发、追问、催办、读取、汇总和关闭任务时,一律使用 `task_id`/平台返回的任务 ID;不得根据任务名称、主题相似度或创建顺序寻址。 -- 下发前先按范围、模块和状态检索本表;已有可复用任务时继续向该 ID 通信,不重复创建独立任务。 -- 任务名称可以修改,`task_id` 不得复用、改写或转给另一项工作。任务完成后保留记录并标记 `completed`,不要删除历史 ID。 -- 跨任务共享契约由总控记录在登记表备注中;独立任务回复必须带回自身 `task_id`、状态、变更文件、验证结果和阻断项,便于按 ID 汇总。 +- 下表只保存既有一次性任务和已结束轮次,不作为新任务的复用路由;可长期复用的任务必须同时登记到上方“长期复用通道”。 +- 独立任务创建成功并获得平台返回的稳定 ID 后立即登记;没有稳定 ID 不得写入临时名称或自行生成替代 ID。 +- 任务名称可以修改,`task_id` 不得复用、改写或转给另一项工作。历史任务完成后保留记录,不删除历史 ID。 +- 跨任务共享契约由总控记录在备注中;独立任务回复必须带回自身 `task_id`、状态、变更文件、验证结果和阻断项。 -## 状态值 +## 执行历史状态值 `planned`、`active`、`blocked`、`completed`、`archived`。 -## 当前登记 +## 执行历史 | task_id | display_name | scope | owner | status | worktree/branch | last_update | notes | |---|---|---|---|---|---|---|---| | 019feb17-b5de-7d63-a0e4-5a8c2181e323 | PDA 后端作业链收口 | `baiyue-firstleg/firstleg` PDA 拣货事务、原子批量提交和相关测试 | 三期总控 | completed | 后端 `test` | 2026-08-11 | `40171b7` 已推送;根因是 operation CHECK 未允许 BATCH,统一SQL与模块SQL已补幂等迁移;定向17/17、Firstleg 524项无失败 | -| 019feb17-c56c-7e02-8f9c-0b74f960df34 | 三期验收 fixture 审计 | `baiyue-firstleg/scripts/admin-test-env-modules` | 三期总控 | active | 后端 `test` | 2026-08-11 | `fe0f166` 已完成状态映射;继续在同一职责内收口 customs 对新单余量“来源分片承载”模型的断言 | +| 019feb17-c56c-7e02-8f9c-0b74f960df34 | 三期验收 fixture 审计 | `baiyue-firstleg/scripts/admin-test-env-modules` | 三期总控 | completed | 后端 `test` | 2026-08-11 | `df0f05c`已推送:修正customs同一来源fixture配对及loading-finalize失败后误标LOADED;4/4契约测试通过,仅脚本无需部署 | | 019feb17-d3c2-7480-802a-498db11c7d3c | Admin 装柜工作流页面 | `apps/admin-web` 统一订单、智能装柜、预装清单、装柜清单、证据与完成入口 | 三期总控 | completed | Admin `release/test` | 2026-08-11 | 装柜`9b99404`、服务中心接线`17d483b`、出仓空态`af30c0c`均已部署;103 files/535 tests、typecheck/build、浏览器真实页面通过 | | 019feb2a-419b-7161-8174-5aed7e84a047 | PDA 原子批量提交契约 | PDA 后端 Controller/DTO/OpenAPI/调用方只读契约梳理 | 三期总控 | completed | 后端 `test` | 2026-08-10 | 只读阶段完成;等待 S0500 修复后进入实现 | | 019feb29-ef52-7f83-96d7-42d963d2ae34 | PDA uni-app 前端验收 | `apps/pda-uniapp` 登录、领取、广播扫码、设备草稿、批量提交、证据和真实 API 映射 | 三期总控 | completed | PDA `release/test` | 2026-08-11 | 功能提交`be776c8`已推送,远端`f83a0c4`;43/43 PASS,A0409刷新和失败回滚已验证;HBuilderX/Android真机仍为最终gap | @@ -29,18 +46,42 @@ | 019feed6-edeb-7da3-aa75-8c40aa5fb305 | Firstleg P0 测试环境发布 | Jenkins `backend-java` 的 `firstleg` 定向发布与健康检查 | 三期总控 | completed | 后端 `test` | 2026-08-11 | backend-java #101、firstleg #71 成功;部署 `9f455b6`,包含 `7aa2b50` 与 PDA 原子 `/batch`;无 SQL,已通知全量复跑 | | 019feeee-e042-73b0-ba84-c6b8e21fa6c9 | Loading-finalize 测试环境发布 | Jenkins `backend-java` 的 `firstleg` 定向发布与健康检查 | 三期总控 | completed | 后端 `test` | 2026-08-11 | backend-java #102、firstleg #72成功,部署`3f301e3`;消费者单跑缺同轮fixture,需随生产者链最终复验 | | 019feef5-59e3-7110-b87f-4aea14bb67c6 | PDA BATCH SQL 与 Firstleg 发布 | 测试库统一SQL、Jenkins `firstleg` 定向发布和健康检查 | 三期总控 | archived | 后端 `test` | 2026-08-11 | 执行前卡在工具审批,未开始备份或写库;已由新任务`019fef02-863b-7413-84f7-36593aac0c4e`替代 | -| 019fef02-863b-7413-84f7-36593aac0c4e | PDA BATCH SQL 与 Firstleg 发布(替代) | 测试库统一SQL、Jenkins `firstleg` 定向发布和健康检查 | 三期总控 | active | 后端 `test` | 2026-08-11 | 使用默认权限和已有授权继续:备份、统一SQL两次、CHECK含BATCH核验、部署Firstleg并通知最终全量 | +| 019fef02-863b-7413-84f7-36593aac0c4e | PDA BATCH SQL 与 Firstleg 发布(替代) | 测试库统一SQL、Jenkins `firstleg` 定向发布和健康检查 | 三期总控 | completed | 后端 `test` | 2026-08-11 | 统一SQL连续两次唯一PASS,CHECK含BATCH;backend-java #103、firstleg #73成功,部署`40171b7`,健康/OpenAPI通过,已通知最终全量 | +| 019fef67-b9b5-7f20-a19f-0f2bb9e91a68 | Loading complete 最终阻断修复 | `baiyue-firstleg/firstleg` 装柜完成首次提交、幂等重放与事务状态收口 | 三期总控 | completed | 后端 `test` | 2026-08-11 | `bf7c214`已推送:修复普通注解SQL误用XML实体导致MySQL语法异常;定向13/13、Firstleg 527项无失败,无SQL,仅需部署Firstleg | +| 019fef75-68c8-7d63-9f28-d2f5ac068c8b | Loading complete 修复发布 | Jenkins `firstleg` 定向发布、健康与OpenAPI核验 | 三期总控 | completed | 后端 `test` | 2026-08-11 | backend-java #104、firstleg #74成功,部署`cb308399`(包含`bf7c214`与`df0f05c`);无SQL,Gateway健康与Firstleg OpenAPI通过,已通知最终全量复跑 | +| 019fef90-4707-7c62-af0d-8b3395ef1e80 | 柜次与计划状态机复验分类 | 三期柜次push、计划推进、预装门禁及验收fixture顺序 | 三期总控 | completed | 后端 `test` | 2026-08-11 | 已确认双缺口:`cb308399`正确引入WAIT_LOADING/推送后EDITING,但错误地在首笔写库而非claim时推进LOADING;脚本仍按旧push即LOADING顺序,需后端claim推进与fixture重排后再部署复验 | +| 019fef9a-e00d-7ea2-8450-5a5930e5279d | PDA claim 推进装柜中 | `baiyue-firstleg/firstleg` 任务领取、计划LOADING推进、历史与幂等 | 三期总控 | completed | 后端 `test` | 2026-08-11 | `16a2f83`已推送:claim首次及同作业员重入幂等推进LOADING,保留pick/batch兜底且START_LOADING历史不重复;定向10/10、Firstleg 531项无失败,无SQL,仅需部署Firstleg | +| 019fef9b-046a-7062-bcfd-8069f02a4822 | 三期装柜状态验收脚本重排 | `phase3-container-loading`、`phase3-loading`及级联fixture | 三期总控 | completed | 后端 `test` | 2026-08-11 | 远端`386eae6`已收口:WAIT_LOADING→EDITING→claim后LOADING→真实pick/batch→COMPLETED,customs/finalize同轮fixture;脚本契约6/6、node check、diff check通过,仅脚本无需部署 | +| 019fefb4-ef2e-7db2-a698-a65f185df8f8 | PDA claim 状态修复发布 | Jenkins `firstleg` 定向发布、健康与OpenAPI核验 | 三期总控 | completed | 后端 `test` | 2026-08-11 | backend-java #105、firstleg #75成功,部署`a59f4634`(包含`16a2f83`与`386eae6`);无SQL,健康/OpenAPI通过,已放行完整生产者链与默认全量 | +| 019fef9f-0a38-7523-bb40-33256352505b | Firstleg claim 推进装柜状态 | `LoadingPickService.claim` 原子推进计划至LOADING并保持幂等 | 三期总控 | completed | 后端 `test` | 2026-08-11 | `16a2f83`已推送origin/test;claim成功/同作业员重入调用`startPicking`,仅首次写START_LOADING历史;LoadingPickService定向10/10、Firstleg 531项0失败2跳过、diff check通过;无SQL,仅需部署Firstleg | +| 019fef8f-8134-7380-b112-33ba490fa911 | 柜次推送状态机阻断排查 | `baiyue-firstleg/firstleg` 柜次推送、计划状态与预装确认门禁 | 三期总控 | completed | 后端 `test` | 2026-08-11 | 后端契约完整无需改动:push-preload保持EDITING,PDA task claim后才推进LOADING;失败为脚本旧顺序,已交fixture任务修正 | +| 019fef8f-994b-75c0-a3a2-0c76574567b4 | 三期最终fixture稳定性审计 | `baiyue-firstleg/scripts/admin-test-env-modules` 生产者链marker与跨模块fixture | 三期总控 | completed | 后端 `test` | 2026-08-11 | `386eae6`已推送:按push EDITING、claim EDITING、首笔PICK/BATCH启动装柜的真实顺序修正3个脚本;静态3/3、契约6/6通过 | +| 019fefae-09e8-7993-906e-95b073ffc0b7 | 预装确认与装柜启动门禁矛盾修复 | `baiyue-firstleg/firstleg` 入库预装确认、柜次启动与主状态同步 | 三期总控 | completed | 后端 `test` | 2026-08-11 | `a59f463`已推送:confirming允许INBOUNDED或LOADING,LOADING仍强制关联柜次已启动;定向4/4、Firstleg 532项无失败,无SQL | +| 019fefb7-d33d-7521-ae9d-1ef268755b9c | 预装门禁修复发布 | Jenkins `firstleg` 定向发布、健康与OpenAPI核验 | 三期总控 | completed | 后端 `test` | 2026-08-11 | backend-java #105、firstleg #75成功,部署`a59f463`;Firstleg 533项0失败2跳过,Gateway health与Firstleg OpenAPI HTTP200,未执行SQL,已通知最终全量复跑 | +| 019ff02d-924c-7290-a34e-b2c3704601b8 | 三期最终验收状态与finalize fixture修正 | `baiyue-firstleg/scripts/admin-test-env-modules` claim/CONFIRMING真实状态与订舱拖车报关到finalize柜次关联 | 三期总控 | archived | 后端 `test` | 2026-08-11 | 发布任务越权创建但未下发业务工作;已归档,范围交回现有全量任务`019fdf0a-4933-7903-8a0c-10018c379f6f`处理 | +| 019ff034-87cb-7d32-8c93-3e8aa992b082 | 三期最终脚本与全量收口 | `baiyue-firstleg/scripts/admin-test-env-*` 状态断言、finalize实际柜次关联、定向与默认全量 | 三期总控 | active | 后端 `test` | 2026-08-11 | 因原全量任务被Finance独占而建立的唯一替代任务;修复后直接跑定向和默认全量,完成即归档,不再拆分 | | 019fdf0a-4933-7903-8a0c-10018c379f6f | 后台真实全量测试 | `baiyue-firstleg/scripts/admin-test-env-*` 与测试环境真实 HTTP 验收 | 三期总控 | active | 后端 `test` | 2026-08-11 | 当前基线复跑与下一批 PDA batch/Admin 终验分两轮执行;禁止 mock、放宽断言或 gap 伪装 PASS | -| 019febc3-704f-7c72-8e82-dc317f69dc9b | Finance 后端与数据库 | `baiyue-firstleg/finance`、Finance 依赖的 `file/firstleg/gateway/iam` 契约、模块 SQL 与统一 SQL | 财务总控 | active | 共享工作区 | 2026-08-11 | 第三轮修复因会话中断待恢复;P0 为成本入口体积快照、凭证校验、订单/柜次客户归属、完整幂等 hash 和行为测试 | -| 019febc3-7c39-7ec2-8051-a4cce8d5f104 | Finance F1 接口契约 | `docs/财务管理/` 的 OpenAPI、接口契约、门禁和状态文档 | 财务总控 | active | 共享工作区 | 2026-08-11 | 第三轮修订因会话中断待恢复;只改文档,按真实实现更新 D1/D4/D6/D8/D9/D11 | -| 019febc3-89f3-7991-b7eb-6c697fd9f032 | Admin 人工财务模块 | `apps/admin-web/src/modules/finance/` 及其路由、权限和测试 | 财务总控 | active | Admin 当前工作区 | 2026-08-11 | 第三轮修复因会话中断待恢复;接入真实凭证上传、成本体积快照和 revision 查询,禁止模拟成功 | -| 019febc3-9519-7982-81dd-bc048819bf48 | Client 财务只读模块 | `apps/client-web/src/modules/finance/` 及财务权限映射和测试 | 财务总控 | completed | Client 当前工作区 | 2026-08-11 | 184/184、typecheck/build 通过;过滤 DRAFT/VOIDED,财务 list+view 权限按 AND 判断,不传 customerId/tenantId | +| 019febc3-704f-7c72-8e82-dc317f69dc9b | Finance 后端与数据库 | `baiyue-firstleg/finance`、Finance 依赖的 `file/firstleg/gateway/iam` 契约、模块 SQL 与统一 SQL | 财务总控 | active | 共享工作区 | 2026-08-11 | 最终专项判定11个一期operation缺失;已返工账户/流水、来源审核与纳入结算、对账单和应收应付结算单正式闭环 | +| 019febc3-7c39-7ec2-8051-a4cce8d5f104 | Finance F1 接口契约 | `docs/财务管理/` 的 OpenAPI、接口契约、门禁和状态文档 | 财务总控 | active | 共享工作区 | 2026-08-11 | 11个NOT_IMPLEMENTED已判定一期P0;正在冻结DTO、状态、权限、幂等、错误码和验收用例并跟踪实现 | +| 019febc3-89f3-7991-b7eb-6c697fd9f032 | Admin 人工财务模块 | `apps/admin-web/src/modules/finance/` 及其路由、权限和测试 | 财务总控 | active | Admin 当前工作区 | 2026-08-11 | 五路由已存在但后三页仍为旧能力映射;返工正式生成、详情、状态动作和来源审核/结算纳入交互 | +| 019febc3-9519-7982-81dd-bc048819bf48 | Client 财务只读模块 | `apps/client-web/src/modules/finance/` 及财务权限映射和测试 | 财务总控 | active | Client 当前工作区 | 2026-08-11 | Client交付已完成;因原全量ID被三期持续占用,复用本独立ID执行Finance一期五页最终只读专项,禁止修改文件 | +| 019fef83-0763-7930-8c00-c42d5398b0f1 | 数据库注释全库盘点 | `baiyue-firstleg/*/src/main/resources/db/` 与统一 SQL 的只读盘点 | 数据库注释总控 | completed | 共享工作区 | 2026-08-11 | 155个建表定义、约2339字段;字段注释覆盖约69%,缺失724;Finance缺164、Firstleg缺461;未修改文件 | +| 019fef83-2697-7062-a902-a0e9791d75ea | 数据库注释规范与门禁 | 数据库中文注释规范、静态审计脚本和 CI/验收门禁 | 数据库注释总控 | completed | 共享工作区 | 2026-08-11 | 规范与离线门禁完成;多行CHECK假阳性已按MySQL数据类型白名单修复并补SelfTest;Order/Finance Issues=0 | +| 019fef8b-9f95-7102-a57b-dc4960ff5ffa | Firstleg 二期数据库注释 | `firstleg` 基线、入库、测量、计费、价格及托盘相关模块 SQL | 数据库注释总控 | completed | 共享工作区 | 2026-08-11 | 7文件新增47个注释、修正1个英文状态;范围内Issues=0、结构不变、聚合构建成功;完整测试被并行Finance测试编译错误阻断 | +| 019fef8b-a844-75c1-9ed3-85dfca8a89f1 | Firstleg 三期船期数据库注释 | `firstleg` 船司查询与港口船期模块 SQL | 数据库注释总控 | completed | 共享工作区 | 2026-08-11 | 两文件补齐/中文化113字段;覆盖100%;SQL契约测试6/6、BUILD SUCCESS;仅COMMENT变更 | +| 019fef8b-afb6-7342-a707-f014f7baa785 | Order Auth IAM 数据库注释 | `order`、`auth`、`iam` 模块 SQL | 数据库注释总控 | completed | 共享工作区 | 2026-08-11 | 26表389字段覆盖100%;Auth/IAM测试通过;Order 125/126,唯一失败为统一SQL旧注释镜像待集成同步 | +| 019fef8b-b84f-70e2-b312-bebdce157ad0 | Warehouse File Audit Event 数据库注释 | `warehouse`、`file`、`audit-service`、`event-bridge-service` 模块 SQL | 数据库注释总控 | completed | 共享工作区 | 2026-08-11 | 28个表定义、433字段注释100%;四模块Maven测试BUILD SUCCESS;未改统一SQL | +| 019fef9d-ddf6-7963-b683-46bab7685bee | Firstleg 三期报关数据库注释 | `phase3-customs-schema.sql` | 数据库注释总控 | completed | 共享工作区 | 2026-08-11 | 8表107字段中文注释完成;CustomsSqlContractTest通过;仅COMMENT变更,未改统一SQL | +| 019fef9d-e7df-7bd2-9d07-ef22bbac1e6a | Firstleg 三期订舱拖车数据库注释 | `phase3-ocean-booking-schema.sql`、`phase3-truck-appointment-schema.sql` | 数据库注释总控 | completed | 共享工作区 | 2026-08-11 | 两文件Issues=0且结构不变;订舱/拖车定向测试20/20、BUILD SUCCESS;未改统一SQL | +| 019fef9d-f2f1-7e12-8f70-63e54a855851 | Firstleg 三期装柜PDA数据库注释 | 三期装柜、出仓、PDA、分柜、排柜及桥接脚本 | 数据库注释总控 | completed | 共享工作区 | 2026-08-11 | 七文件完成;Firstleg 89表1443字段覆盖100%、Issues=0;相关SQL测试33项通过、1跳过;统一SQL待同步 | +| 019fefb9-fce8-78e3-8270-dd569546ae9d | 数据库注释统一SQL集成 | `deploy/sql/头程数据库统一增量-MySQL8.sql` 与相关SQL契约测试 | 数据库注释总控 | completed | 共享工作区 | 2026-08-11 | 150唯一表2456字段;每表最多一次DDL并验签;九库断言/唯一PASS;DryRun PASS;Firstleg549、Finance35通过;未执行真实MySQL | ## 通信与汇总格式 总控下发消息至少包含: ```text +routing_key: <长期职责键> task_id: <已登记 ID> 目标: <单一交付目标> 范围: <目录/模块> @@ -48,4 +89,4 @@ task_id: <已登记 ID> 回传: <状态、变更文件、验证命令/结果、阻断项> ``` -汇总时按 `task_id` 聚合,不按 display_name 聚合;同一 ID 的最新状态覆盖旧状态,但保留重要阻断和验证记录。 +汇总时按 `routing_key + task_id` 聚合,不按 display_name 聚合;同一 ID 的最新状态覆盖旧状态,但保留重要阻断和验证记录。 diff --git a/财务管理/F1人工财务契约审查与门禁.md b/财务管理/F1人工财务契约审查与门禁.md index 160b5ab..132deee 100644 --- a/财务管理/F1人工财务契约审查与门禁.md +++ b/财务管理/F1人工财务契约审查与门禁.md @@ -4,6 +4,8 @@ > 审查范围:Finance Java、Finance 建库 SQL、统一增量 SQL、Admin/Client Finance API、File Java/测试、`openapi/finance-f1-manual-v1.yaml` > 结论:F0/F1 基础代码已经实现,当前处于专项修复与契约收敛阶段;不是整体未开发。修复项完成前尚未达到完整联调验收/测试放行。 +> 范围重定:领导验收改为《财务一期五页产品与接口验收契约》中的 5 页。本文 D1-D11 继续作为既有人工接口专项门禁,并新增来源链路和五页门禁。 + ## 1. 已确认且已对齐 | 范围 | 目标 | 当前结论 | @@ -21,51 +23,76 @@ 当前实现包括 Finance Controller/Service、独立财务表、统一 SQL 权限种子、Admin 人工财务页面和 Client 只读账单页面。本文中的 D1-D11 是对现有实现的修复门禁,不是从零建设清单。 -### 1.1 第三轮复验记录(2026-08-11) +### 1.1 后端最终实现复验记录(2026-08-11,基线 `7c2274b`) | 验证对象 | 命令/范围 | 结果 | 结论 | |---|---|---|---| -| Finance | `mvn -pl finance -am test` | 通过;Finance 7 个测试通过(1 个源码契约检查、6 个校验单测) | 可证明现有校验和 1:2:3 体积分摊单测通过;不能证明固定 DTO、完整幂等或跨服务闭环 | +| 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 字段适配与账单动作封装有直接证据 | -| 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 创建收款/成本的真实凭证端到端闭环继续阻断。 +以上计数来自总控最终后端复验。尚未提供测试环境中 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` | 已实现 | -| `GET/POST /api/platform/finance/v1/cost-entries` | `cost:view/create` | 登记及分摊落库已实现;真实体积快照、归属和成本凭证校验未完成 | +| `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 尚未接入 | -| `POST /api/platform/finance/v1/vouchers?customerId=` | `receipt:create` | 已实现图片上传;用途区分、成本上传权限和关联状态未实现 | +| `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. 开发门禁阻断项 +## 3. 门禁状态(已解决与剩余) -以下项目未完成前,可以继续编码,但不能宣称 F1 已具备完整业务闭环。 +本表保留 D1-D11 的稳定编号用于总控追踪。标为“后端完成/定向验证通过”的项目不再作为代码实现阻断;其余项目完成前不能宣称 F1 已具备完整业务闭环。 | 编号 | 阻断项 | 当前事实 | 完成标准 | |---|---|---|---| -| D1 | 稳定响应 DTO | **未完成。** Finance 仍返回 JDBC `Map`/`List` 和 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` 被直接复制到各分摊行,未验证订单、柜次及入柜关系 | 单票订单存在且属于客户;整柜存在且订单实际在柜内,并全部属于该客户;任何不一致整单拒绝 | +| 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 完整操作 | **部分实现。** 已有详情、草稿修改、调整、作废、出账、凭证选择/上传和收款/成本创建;未接客户账期和修订历史,整柜体积仍人工填写,成本凭证后端不校验,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` | +| 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 页 | **后端查询契约完成,页面开发中。** 五页固定 DTO 路径、Client 脱敏和错误码已经验证;11 个目标写/明细 operation 仍真实标记 `NOT_IMPLEMENTED`,Admin 正在接入 | 完成 Admin 五页矩阵和剩余 11 个 operation;删除费用分析、利润表、充值及假入口并完成端到端验收 | ## 4. 非阻断但需锁定的契约说明 @@ -80,11 +107,11 @@ File 失败转储:`baiyue-firstleg/file/target/surefire-reports/2026-08-11T12- 进入前后端联调前至少满足: -1. D1、D3、D5、D6、D7、D8、D9、D10、D11 已完成并有直接测试。 -2. D2 选定“首期非分页”或“服务端分页”之一,OpenAPI、Admin、Client 三方一致。 -3. D4 至少完成账单创建、出账和收款登记的同键重放测试。 +1. D1-D8、D10-D13 已具备后端直接证据;剩余以 D9 和 D14 的 11 个 operation/Admin 验收为准。 +2. D2 已固定为首期非分页数组;后续若改服务端分页须另行升级契约。 +3. 幂等门禁已关闭;后续写 operation 继续复用同参重放、异参冲突和恢复语义。 4. Finance 集成测试覆盖:默认 30 天、CNY 拒绝、出账冻结、300 分 3 单、超额/跨客户/草稿拒绝、部分收款到收清、调整修订、已收不可作废。 -5. 成本测试覆盖:单票归属、整柜按体积分摊、零体积人工处理、舍入尾差、跨客户对象拒绝。 +5. 成本测试覆盖:单票归属、真实柜次快照、整柜按体积分摊、无有效体积拒绝、实际舍入尾差、跨客户对象拒绝。 6. 权限测试覆盖平台 12 个权限码和客户 2 个权限码,并验证客户无法写入及无法跨客户查询。 7. 客户查询测试证明列表、详情和收款均不会暴露 `DRAFT/VOIDED`、平台凭证 fileId 或内部备注。 @@ -93,6 +120,6 @@ File 失败转储:`baiyue-firstleg/file/target/surefire-reports/2026-08-11T12- - **当前阶段:专项修复中。** Finance、SQL、Admin 和 Client 均已有实现,不再按“未开发”管理。 - **可继续开发:是。** 路径、权限、固定 DTO、凭证、分摊、幂等和客户可见性已经冻结,不再需要新增产品决策。 - **可进行接口冒烟:有条件。** 可验证现有路径、鉴权和基础创建/查询,但结果不代表业务验收通过。 -- **可进入完整联调验收:否。** 主要阻断为真实体积快照与归属校验、收款/成本凭证 fail-closed 校验、File 全量测试故障、Admin 创建闭环、稳定 DTO/错误码、完整幂等和客户收款 DTO。 +- **可进入完整联调验收:否。** Finance 来源写入及 `PUBLISHED + amount>0`、五页 DTO、完整幂等、File fail-closed、客户脱敏、稳定错误码和柜子来源已经通过后端测试,不再是阻断;剩余阻断是 11 个目标 operation、Admin 五页接入和真实跨服务冒烟。 本次审查只修订文档与 OpenAPI,未修改 Java、前端或 SQL。 diff --git a/财务管理/F1人工账单收款成本接口契约.md b/财务管理/F1人工账单收款成本接口契约.md index ab816b4..accff49 100644 --- a/财务管理/F1人工账单收款成本接口契约.md +++ b/财务管理/F1人工账单收款成本接口契约.md @@ -5,8 +5,13 @@ > 状态:目标契约;以《F1人工财务契约审查与门禁》为开发/联调准入依据 > OpenAPI:`openapi/finance-f1-manual-v1.yaml` +> 一期 5 页产品与新增接口以《财务一期五页产品与接口验收契约》和 `openapi/finance-phase1-five-pages-v1.yaml` 为准;本文保留现有人工账单、收款和成本能力的细化契约。 + ## 1. 首期边界 +- 领导验收页面固定为客户账户、客户应收应付、客户对账单、结算单应收、结算单应付。 +- 费用分析、利润表、发票、钱包、预收和充值不做,不保留按钮、菜单或路由。 +- 客户账户只读聚合账单、收款和应收应付来源,不存在可直接修改的账户余额。 - 只支持 RMB/CNY,不做汇率、月结、钱包、发票、银行流水和自动认款。 - 平台财务人工填写账单、收款、成本表单并上传凭证。 - 客户只读查看本客户账单、收款记录和未收余额。 @@ -14,6 +19,7 @@ - 客户账期按客户配置,默认 30 个自然日;账单日期和到期日由服务端按 `billDate + creditDays` 计算。 - 一笔收款可分配到多张账单;财务逐张填写分配金额。首期不处理预收、多收、手续费、退款。 - 单票成本归属单票;整柜清关、拖车等公共成本按柜内有效票体积分摊。 +- 账单/对账单不得接收 Admin 自造来源:只有已审核、已发布、参与结算、有效且为 CNY 的入库单/柜子费用版本进入 `fin_fee_source` 后才能选入。 ## 2. 统一接口约定 @@ -61,9 +67,9 @@ 整柜分摊固定规则: -1. 放行后的 Finance 公开入口仅接收 `containerId`,并由服务端向 Firstleg 读取成本创建时的真实柜次/订单有效体积快照;当前公开请求中的 `containerOrders[].volumeCbm` 是过渡实现,不可信且不得作为放行后的权威输入。快照单位 CBM、精度 6 位;有效票按 `orderId` 升序形成确定顺序。 +1. Finance 公开入口仅接收 `containerId`,由服务端调用 `GET /internal/firstleg/v1/finance-cost-snapshots/customers/{customerId}/containers/{containerId}` 读取真实柜次/订单有效体积快照。浏览器不得提交 `containerOrders` 或 `volumeCbm`。快照单位 CBM、精度 6 位;有效票按 Firstleg 柜次明细形成确定输入。 2. `ratio = orderVolume / totalVolume`;除最后一票外,`allocatedAmount = costAmount × ratio`,按 6 位小数 `HALF_UP`。 -3. 最后一票金额为 `costAmount - 前序分摊金额合计`。仅当该金额不等于 `costAmount × tailRatio` 按 6 位 `HALF_UP` 的常规舍入结果时,`roundingAdjustment=true`,并保存非零 `roundingDelta=tailAmount-roundedTailAmount`;没有实际尾差时必须为 `false/0`。 +3. 最后一票金额为 `costAmount - 前序分摊金额合计`。仅当该金额不等于 `costAmount × tailRatio` 按 6 位 `HALF_UP` 的常规舍入结果时,`roundingAdjustment=true`;没有实际尾差时必须为 `false`。分摊金额合计严格等于成本金额。 4. 单票体积为零时该票自动分摊为 0;柜内有效票总体积为零时返回 `FIN-COST-VOLUME-SNAPSHOT-INVALID`。首期不支持请求端 `MANUAL_ZERO_VOLUME` 人工分摊。 5. 已落库的体积、比例、分摊金额和尾差均为不可变快照;来源体积后续变化不追改历史成本。 @@ -71,9 +77,9 @@ 1. 当前公开上传入口为 Finance 的 `POST /api/platform/finance/v1/vouchers?customerId={customerId}`,multipart 字段为 `file`;当前只接受图片。OpenAPI 描述该真实入口,不再声明不存在的 `/platform/v1/files/finance-vouchers`。 2. Finance 当前通过 File 的既有 `POST /internal/file/v1/business-files/{tenantId}/FINANCE_VOUCHER/{customerId}` 上传,并以 `GET /internal/file/v1/business-files/{tenantId}/FINANCE_VOUCHER/{customerId}/{fileId}` 查询校验。仓库当前没有财务专用 `validate/associate` Controller,OpenAPI 不得将其写成已实现路径。 -3. 放行目标要求收款和成本在落库前均 fail-closed 校验:File 客户与表单客户一致、业务类型为 `FINANCE_VOUCHER`、文件存在且有效、未删除、MIME 为允许的图片类型。File 客户端不可用或超时时返回 `FIN-FILE-VALIDATION-UNAVAILABLE`,不得跳过校验。 +3. 收款和成本现已在落库前调用 File 元数据校验:File 客户与表单客户一致、业务类型为 `FINANCE_VOUCHER`、文件存在且 MIME 为允许的图片类型。当前遗留是 `fileClient == null` 时仍跳过校验;放行前必须改为 fail-closed,并返回 `FIN-FILE-VALIDATION-UNAVAILABLE`。 4. 当前 File 模型没有用途区分和关联状态;首期可由 Finance 的 `voucherFileIds` 与财务资源事务性绑定作为关联事实,但必须防止跨客户引用。若后续增加 File 关联接口,需另行更新契约,不能在本版预先承诺 Outbox 行为。 -5. 上传权限必须同时覆盖收款和成本创建人员;当前入口只有 `platform:finance:receipt:create`,因此成本凭证上传仍是门禁阻断。 +5. 上传入口使用 `hasAnyAuthority('platform:finance:receipt:create','platform:finance:cost:create')`,收款和成本创建人员均可上传。 6. 客户侧收款 DTO 不返回 `voucherFileIds`、`voucherNo`、内部状态、内部备注或下载地址;首期客户不能查看平台收款/成本凭证。 ## 4. 接口清单 @@ -89,6 +95,7 @@ | POST | `/api/platform/finance/v1/bills/{billId}/issue` | 出账并冻结 | | POST | `/api/platform/finance/v1/bills/{billId}/adjust` | 出账后调整/生成新版本 | | POST | `/api/platform/finance/v1/bills/{billId}/void` | 作废账单 | +| GET | `/api/platform/finance/v1/bills/{billId}/revisions?customerId=` | 查询账单修订历史并校验客户归属 | | POST | `/api/platform/finance/v1/receipts` | 人工登记收款并分配账单 | | GET | `/api/platform/finance/v1/receipts` | 查询收款记录 | | POST | `/api/platform/finance/v1/cost-entries` | 人工登记单票/整柜成本 | @@ -139,7 +146,7 @@ ## 6. 验收用例 1. 平台财务创建 CNY 草稿账单,保存后可继续编辑。 -2. 账单出账生成账单号和到期日(出账日+客户账期,默认 30 天)。 +2. 账单创建时按账单日期生成到期日(`billDate + creditDays`,默认 30 天),出账后冻结。 3. 出账后修改金额被拒绝;通过调整或作废重开保留原版本。 4. 登记 300 元收款,上传一张凭证并分配到 3 张 100 元账单,三张账单均更新余额。 5. 一张账单分两次收款后显示 `PARTIALLY_PAID`,余额归零后显示 `PAID`。 @@ -149,9 +156,10 @@ 9. 登记单票成本前校验订单存在且属于客户;整柜公共成本仅使用服务端真实快照分摊,零总体积直接拒绝。 10. 重复 `Idempotency-Key` 且同参返回原结果;同键不同请求返回 `FIN-IDEMPOTENCY-CONFLICT`。 11. 同键同参处于 `PROCESSING` 时返回 409、`FIN-IDEMPOTENCY-PROCESSING` 和 `Retry-After`,不会生成第二张单。 -12. File 凭证客户、业务类型、状态或 MIME 任一不匹配,或 File 校验不可用时,收款/成本和分配均不落库。 -13. 整柜自动分摊按 6 位 `HALF_UP` 计算,合计严格等于成本金额;只有实际发生尾差时最后一票才标记 `roundingAdjustment=true` 并保存非零 `roundingDelta`。 -14. 客户列表、详情和收款记录均看不到 `DRAFT/VOIDED` 账单,也看不到平台凭证 fileId 和内部备注。 +12. File 凭证客户、业务类型或 MIME 任一不匹配时,收款/成本和分配均不落库;File 客户端缺失或不可用场景仍需补 fail-closed 验收。 +13. 整柜自动分摊按 Firstleg 真实体积快照和 6 位 `HALF_UP` 计算,合计严格等于成本金额;只有实际发生尾差时最后一票才标记 `roundingAdjustment=true`。 +14. 平台按 `customerId + billId` 查询 revision 成功;跨客户账单拒绝且不返回修订快照。 +15. 客户列表、详情和收款记录均看不到 `DRAFT/VOIDED` 账单,也看不到平台凭证 fileId 和内部备注。 ## 7. 权限矩阵 @@ -164,4 +172,4 @@ - Admin:平台财务路由和三个写表单。 - Client:账单/收款只读查询。 -本契约记录开发目标与当前过渡接口;是否可测试以门禁审查结论为准。当前 Java 返回的 JDBC `snake_case Map`、公开请求体积和非分页数组均属于过渡实现,不能据此放行。 +本契约记录开发目标与当前实现;是否可测试以门禁审查结论为准。当前 Java 返回的 JDBC `snake_case Map` 和非分页数组仍属于过渡实现。整柜成本的浏览器体积输入已从后端契约移除,但 Admin 消费端尚需同步。 diff --git a/财务管理/Firstleg-Finance跨服务契约.md b/财务管理/Firstleg-Finance跨服务契约.md index ab07c95..715d593 100644 --- a/财务管理/Firstleg-Finance跨服务契约.md +++ b/财务管理/Firstleg-Finance跨服务契约.md @@ -1,24 +1,24 @@ # Firstleg-Finance 跨服务契约(技术草案) -> 版本:V0.1(开发前冻结草案) -> 日期:2026-08-10 -> 状态:技术边界已确认;业务口径以《财务管理产品确认表》为准,未将产品待确认项擅自固化。 +> 版本:V0.3(一期五页来源链路实现对齐) +> 日期:2026-08-11 +> 状态:技术边界和一期产品口径已确认;Firstleg 事件/Relay/快照与 Finance Inbox/来源写入已实现并通过定向测试。 -本文是 `firstleg-service` 与规划中的 `finance-service` 之间的技术契约草案。它只规定服务边界、传输可靠性和来源快照,不替代财务账单、收款、核销、汇率或收入确认规则。 +本文是 `firstleg-service` 与已落地 `finance-service` 之间的费用来源技术契约。产品范围以《财务一期五页产品与接口验收契约》为准。 ## 1. 事实依据与当前实现差异 -当前 Firstleg 已有 `fl_inbound_outbox`(与入库事务同库写入)和发布状态/重试字段;`FbaPricingService` 已发布 `FIRSTLEG_FEE_SENT`,调整已发布费用时发布 `FIRSTLEG_FEE_ADJUSTED`。现有 payload 主要只有 `inboundId`、`feeId`、`feeVersion`,Finance 不得把它当作完整财务凭证,必须再调用受保护的来源快照接口。 +当前 Firstleg 使用 `fl_inbound_outbox` 与业务事务同库记录统一事件信封,Outbox Relay 至少一次投递到 `POST /internal/finance/v1/fee-source-events`;事件只携带入库/柜子 ID、费用 ID 和版本,Finance 必须再调用受保护的来源快照接口,不能把短 payload 当作财务凭证。 -当前代码没有独立 Finance 消费者、`FIRSTLEG_FEE_VOIDED` 事件或 Finance 数据库;下列事件版本、信封、消费表和接口均为实施时新增/补齐的契约,不代表已上线。 +Finance 已实现内部事件 Controller、令牌鉴权、Inbox 同事件哈希幂等、Firstleg 快照回查,以及 `fin_fee_source` 新版本写入、旧版本替代和作废保留。非作废事件显式要求 `publicationStatus=PUBLISHED`,否则返回 `FIN-EVT-015`;金额必须 `>0`,零/负值返回 `FIN-EVT-010`。Finance 来源专项 10/10、全量 35/35 BUILD SUCCESS,Firstleg 事件/Relay/快照/柜子费用相关测试 17/17 通过。 ## 2. 事件目录 | 事件名 | 生产时机 | Finance 处理 | 备注 | |---|---|---|---| -| `FIRSTLEG_FEE_PUBLISHED` | 费用确认并发送、来源明细发布成功 | 建立/更新来源池候选,待规则校验 | 首版可由现有 `FIRSTLEG_FEE_SENT` 兼容映射;新契约统一使用 PUBLISHED | -| `FIRSTLEG_FEE_ADJUSTED` | 已发布费用被调整,新版本替代旧版本 | 原来源保留,新增调整来源;不得覆盖已出账明细 | 只有来源版本递增且哈希改变才接受 | -| `FIRSTLEG_FEE_VOIDED` | 费用来源作废 | 标记来源作废;若已入账,生成冲销/补差任务,不自动删除应收 | 需要 Firstleg 补充明确作废动作及原因 | +| `FIRSTLEG_FEE_PUBLISHED` | 入库或柜子费用审核通过、参与结算并发布;由 `aggregateType/payload` 区分来源 | 写入不可变来源版本 | 已实现并验证 | +| `FIRSTLEG_FEE_ADJUSTED` | 入库或柜子费用调整产生新版本 | 追加新版本并将旧 `AVAILABLE` 标为 `SUPERSEDED` | 已实现并验证 | +| `FIRSTLEG_FEE_VOIDED` | 入库或柜子费用作废 | 将同版本来源标为 `VOIDED`,不物理删除 | 已实现并验证 | 事件不是“已生成应收”通知;Finance 仍按产品确认的入池条件(例如 `settlementIncluded`、审核状态和作废状态)决定是否进入可出账池。 @@ -26,11 +26,11 @@ ```json { - "eventId": "fl-01J...", "eventType": "FIRSTLEG_FEE_PUBLISHED", + "eventId": "FIRSTLEG_FEE:INBOUND:456:789:2:FIRSTLEG_FEE_PUBLISHED", "eventType": "FIRSTLEG_FEE_PUBLISHED", "eventVersion": 1, "producer": "firstleg-service", "occurredAt": "2026-08-10T10:20:30.123+08:00", "tenantId": "123", "aggregateType": "INBOUND", "aggregateId": "456", - "sequence": 3, "idempotencyKey": "FIRSTLEG_FEE:456:789:2", - "payloadHash": "sha256...", "traceId": "...", "payload": { "inboundId": "456", "feeId": "789", "feeVersion": 2 } + "sequence": 2, "idempotencyKey": "FIRSTLEG_FEE:INBOUND:456:789:2:FIRSTLEG_FEE_PUBLISHED", + "payloadHash": "sha256...", "payload": { "inboundId": "456", "containerId": null, "feeId": "789", "feeVersion": 2 } } ``` @@ -40,24 +40,25 @@ Finance 使用服务间令牌调用 Firstleg(不经过客户端权限,不共享 Entity): -`GET /internal/firstleg/v1/inbounds/{inboundId}/fees/{feeId}/source-snapshot` +目标:`GET /internal/firstleg/v1/inbounds/{inboundId}/fees/{feeId}/source-snapshot` -请求头:`X-Service-Name: finance-service`、`X-Service-Token`、`X-Request-Id`。令牌由环境密钥管理,按服务名、路径和方法授予最小权限;拒绝客户端 JWT 伪造内部调用。 +入库和柜子来源快照接口均已实现并通过 Firstleg 定向测试;Finance Feign Client 使用相同路径和内部令牌。既有 `/internal/firstleg/v1/finance-cost-snapshots/customers/{customerId}/containers/{containerId}` 仍只是体积分摊接口,不能作为费用来源快照使用。 + +请求头:`X-Internal-Service-Token`。令牌由环境密钥管理;Firstleg Controller 使用恒定时间比较校验,拒绝客户端 JWT 伪造内部调用。 响应 DTO 至少包含: ```text -tenantId, inboundId, inboundCode, customerId, customerName, -feeId, feeNo, feeVersion, sourceType, direction, businessDate, -currency, subtotalAmount, additionalAmount, totalAmount, -feeStatus, publicationStatus, settlementIncluded, publishedAt, -chargeUnit, chargeableQuantity, actualGrossWeightKg, actualVolumeCbm, -originWarehouseId/name, channelCode/name, destinationCode/name, -items[{feeItemId, feeCode, name, quantity, unit, unitPrice, amount}], +tenantId, sourceType, feeId, feeVersion, +inboundId, inboundCode, orderId, containerId, containerBookingId, containerNo, +customerId, customerName, feeItemId, feeItemCode, feeItemName, +direction, businessDate, currency, totalAmount, +reviewStatus, publicationStatus, settlementIncluded, publishedAt, +chargeableQuantity, chargeUnit, actualGrossWeightKg, actualVolumeCbm, sourceHash, snapshotVersion ``` -响应必须是当前 Firstleg 已发布版本的不可变业务快照;Finance 将完整 DTO 和 `sourceHash` 存入自有来源池。若上游只返回 `SUPERSEDED`/`VOID` 版本,Finance 不得将其作为新应收来源。 +Finance 将完整 DTO 和 `sourceHash` 存入自有来源池。非 `FIRSTLEG_FEE_VOIDED` 事件只接受 `publicationStatus=PUBLISHED`;未发布返回 `FIN-EVT-015`。作废事件允许读取对应作废版本,但只能把既有来源标为 `VOIDED`,不得创建新来源。 建议附加只读校验接口:`GET /internal/firstleg/v1/inbounds/{inboundId}/fees/{feeId}/publication`,用于消费重试时快速判断发布状态;该接口不能返回跨客户数据。 @@ -116,7 +117,6 @@ Firstleg Outbox 发布采用至少一次投递:指数退避(建议 10s、30s 技术已确认:独立服务/数据库、DTO/事件通信、Outbox 至少一次、Finance Inbox 幂等、不可变来源快照、无跨库 Join、服务令牌、错误隔离和审计要求。 -仍依赖产品确认:何时 `settlementIncluded`、何种来源进入应收;客户/供应商账单聚合和确认节点;调整/作废对已出账单的补差或冲销策略;汇率、财务期间和收入/成本确认口径;客户账户是否抵扣应收。上述未确认项不得由事件消费者自行推断。 - -主要风险:现有 `FIRSTLEG_FEE_SENT` payload 过小且 Outbox 未带 `eventVersion/payloadHash/sequence`;当前没有明确的作废 API;`fl_consumed_event` 属 Firstleg 内部消费表,不能直接复用为 Finance Inbox。实施时需新增兼容字段/映射和回放工具,并在切换前验证历史费用版本的唯一性与哈希完整性。 +产品口径已冻结:只有 `APPROVED + PUBLISHED + settlementIncluded=true + ACTIVE + CNY` 来源可入池;一期按客户/供应商形成五页单据,出账后冻结,调整/作废重开保留版本和原因;不做汇率、月结、钱包抵扣、费用分析和利润表。事件消费者不得从历史原型推断这些已排除能力。剩余问题是来源模型、接口、事件和消费者实现,不是产品冲突。 +主要剩余风险:测试环境尚未完成 Firstleg Relay → Finance Inbox → `fin_fee_source` 的真实 HTTP 冒烟。产品已确认无历史财务数据,不需要历史事件回放方案。 diff --git a/财务管理/README.md b/财务管理/README.md index 3af95d2..d2ca1d7 100644 --- a/财务管理/README.md +++ b/财务管理/README.md @@ -1,7 +1,7 @@ # 财务管理闭环盘点与范围建议 > 盘点日期:2026-08-10 -> 当前状态:F0/F1 已有 Finance 服务、数据库、权限、Admin/Client 页面和基础接口实现;专项契约与闭环缺口修复中 +> 当前状态:F0/F1 已有 Finance 服务、数据库、权限、Admin/Client 页面和基础接口;费用来源主链、固定 DTO、脱敏、凭证 fail-closed 和稳定错误码已通过后端验证,剩余为五页页面与未实现 operation 收口 > 盘点范围:二期入库计费现状、平台后台财务原型、Admin/Client/Firstleg 当前实现 > 结论口径:本文区分“代码已实现”“原型展示”和“建议范围”;原型功能不自动成为开发范围。 @@ -13,6 +13,19 @@ 开发前总控门禁与 F0/F1 任务清单:[财务开工门禁与F0-F1任务清单](财务开工门禁与F0-F1任务清单.md)。 +一期领导验收唯一范围入口:[财务一期五页产品与接口验收契约](财务一期五页产品与接口验收契约.md)。 + +文档优先级:五页验收契约和本轮产品确认 > 五页目标 OpenAPI > 既有人工 F1 契约/门禁 > 领域模型、历史全量规划和迁移文档。旧文档用于追溯,不得据此把钱包、充值、发票、费用分析或利润表重新纳入一期。 + +旧文档降级规则: + +| 文档/章节 | 当前用途 | 一期实施约束 | +|---|---|---| +| 本文第六节起的“历史全量规划” | 保存早期盘点和演进背景 | 与五页契约冲突时全部失效,不作为菜单、路由、字段或接口依据 | +| `财务领域模型与跨服务契约.md` | 历史合并稿 | 已被数据字典和跨服务契约替代 | +| `财务服务边界设计.md`、`财务领域模型与数据字典.md` | 服务边界和长期模型参考 | 其中钱包、充值、发票、费用分析、利润模型不属于一期 | +| `财务历史迁移与上线验收方案.md`、`财务F0实施任务拆分.md` | 历史迁移、底座任务追溯 | 不得覆盖五页范围和当前实现状态 | + 详细设计材料: - [财务来源接入现状](财务来源接入现状.md) @@ -20,6 +33,8 @@ - [Firstleg-Finance跨服务契约](Firstleg-Finance跨服务契约.md) - [F1 人工账单、收款、成本接口契约](F1人工账单收款成本接口契约.md) - [F1 人工财务契约审查与门禁](F1人工财务契约审查与门禁.md) +- [财务一期五页产品与接口验收契约](财务一期五页产品与接口验收契约.md) +- [财务一期五页 OpenAPI](openapi/finance-phase1-five-pages-v1.yaml) - [财务历史迁移与上线验收方案](财务历史迁移与上线验收方案.md) - [财务F0实施任务拆分](财务F0实施任务拆分.md) @@ -54,9 +69,9 @@ -> 客户只读查询 ``` -当前仍需修复:固定响应 DTO、收款/成本凭证 fail-closed 校验、Firstleg 真实体积快照与客户归属校验、完整幂等请求哈希与恢复、客户收款专用 DTO,以及 Admin 客户账期和修订历史查询。完成《F1人工财务契约审查与门禁》中的测试门禁后,才能宣称 F1 人工财务闭环通过验收。 +后端当前已完成 Firstleg 真实柜次体积快照、客户归属、成本凭证校验、revision、双权限上传、费用来源事件/Relay/快照、Finance `fin_fee_source` 消费写入和完整幂等;Finance 35/35、Firstleg 549/549(2 skip)、File 114/114、Gateway 26/26、IAM 163/163、Phase3/PDA SQL 12/12、Order SQL 3/3,均 BUILD SUCCESS。当前剩余是 Admin 客户账期/revision 与五页页面动作,以及 11 个目标 operation;完成《F1人工财务契约审查与门禁》剩余门禁后才能宣称一期页面闭环通过验收。 -本轮确认“全部都需要做”后,目标范围升级为覆盖原型财务菜单的完整财务域:客户账户、客户应收应付、客户对账单、结算单应收、结算单应付、收付款与核销、费用分析、发票和利润报表。仍建议按依赖关系分阶段交付,避免在应收、应付、钱包和利润各自造一套金额口径。 +一期范围已重新冻结为原型 5 页:客户账户、客户应收应付、客户对账单、结算单应收、结算单应付。费用分析、利润表、发票和钱包不做,也不保留菜单、路由或假按钮。客户账户改为账单、收款和应收应付来源派生的财务往来总览,不做充值/预收。 ## 二、事实来源与边界 @@ -118,38 +133,40 @@ Firstleg 已有 `fl_inbound_fee_detail`,包含: | 原型模块 | 原型展示的主要能力 | 当前代码状态 | 是否能直接纳入首期 | |---|---|---|---| -| 客户账户 | 余额、冻结金额、可用余额、充值、扣款流水 | 未实现 | 否,这是预存款/钱包模型,需单独确认资金合规和扣款规则 | +| 客户账户 | 原型为余额、冻结、可用余额和充值流水 | 未实现 | 一期保留汇总/详情/流水结构,但改为未收、待出账、本期已收等派生指标;删除充值/钱包能力 | | 客户应收应付 | 费用明细、审核、参与结算、按截止时间或勾选生成账单 | 仅有入库费用明细后端基础;无正式财务页面 | 可裁剪为应收费用池 | | 客户对账单 | 账期、汇率、应收、已收、结清、明细和支付历史 | 未实现 | 可作为应收闭环核心,但汇率和开票先暂缓 | | 结算单应收 | 应收、已收、余额、到期日、审批、部分收款 | 未实现 | 可合并进账单/应收单,避免首期重复建模 | -| 结算单应付 | 供应商应付、已付、余额、审批、部分付款、利润 | 未实现;上游成本来源不完整 | 否,建议后续独立阶段 | -| 费用分析 | 内外部费用对比、趋势和明细 | 未实现 | 否,依赖稳定应收/应付口径 | -| 利润表 | 按客户、货柜、单票、月份统计收入、成本、利润率 | 未实现 | 否,依赖应收、成本、汇率及分摊规则 | +| 结算单应付 | 供应商应付、已付、余额、审批、部分付款、利润 | 未实现;上游成本来源不完整 | 一期纳入,但删除利润字段/按钮;必须先补真实供应商应付来源和付款凭证链路 | +| 费用分析 | 内外部费用对比、趋势和明细 | 未实现 | 一期明确不做,不进菜单/路由 | +| 利润表 | 按客户、货柜、单票、月份统计收入、成本、利润率 | 未实现 | 一期明确不做,不进菜单/路由 | 原型还出现了发票抬头、开票日期、汇率、支付方式、收付款凭证号等字段。它们只说明未来可能需要,不代表当前产品已经确认税务、外币折算、资金或发票边界。 -当前 Admin 已有正式财务路由、账单详情/草稿修改/出账/调整/作废、人工收款和成本页面及真实凭证上传调用,Client 已有正式只读账单页面;但 Admin 未接客户账期和修订历史,成本仍允许人工填写柜内体积,File 全量测试在发现阶段失败,后端成本凭证校验也未接通,因此只能视为修复中的实现,不能视为已验收交付。 +当前 Admin 已有正式财务路由、账单详情/草稿修改/出账/调整/作废、人工收款和成本页面及真实凭证上传调用,Client 已有正式只读账单页面。后端已改为根据 `containerId` 调 Firstleg 体积快照并完成成本凭证校验,但 Admin 仍强制人工填写并提交柜内订单体积,且未接客户账期和 revision,因此前端创建闭环尚未与最终契约对齐。 ## 五、当前修复中缺口 | 序号 | 断点 | 直接影响 | |---|---|---| -| 1 | Finance 仍直接返回 JDBC `Map/List` | 字段和响应结构未形成稳定 DTO,前端依赖临时适配 | -| 2 | 凭证已可上传,但成本未校验,收款校验存在 fail-open,且 File 全量测试故障 | Admin 收款/成本真实创建闭环不能放行 | -| 3 | 整柜分摊已落库,但体积由请求人工填写且归属未向 Firstleg 校验 | 分摊结果不可证明来自真实柜次快照 | +| 1 | 五页读接口、平台/客户固定 DTO 和追踪响应已实现 | Finance 35/35 BUILD SUCCESS;本轮使用现有 Surefire 报告核对,不重新执行 Maven | +| 2 | 收款和成本均已校验凭证,但 `fileClient == null` 时仍 fail-open | File 客户端缺失时可能绕过凭证校验 | +| 3 | 后端已使用 Firstleg 真实体积快照,Admin 仍要求人工填写并提交体积 | 页面与最终请求契约不一致,产生无效输入和误导 | | 4 | 幂等已有 SHA-256 与同参重放,但未覆盖完整规范化请求,缺 PROCESSING 恢复测试 | 仍可能误判同参或无法恢复中间态 | | 5 | 客户账单已过滤可见状态,收款列表仍暴露平台字段且未按可见账单过滤 | 客户只读边界仍不完整 | -| 6 | Admin 已接账单动作,但未接客户账期、修订历史,整柜仍人工填体积 | 平台财务页面仍非完整闭环 | +| 6 | Admin 已接账单动作,但未接客户账期、revision,整柜仍人工填体积 | 平台财务页面仍非完整闭环 | | 7 | 业务错误码仍以通用错误和中文消息为主 | 前端无法稳定区分版本、幂等、凭证和归属错误 | -| 8 | 修订快照已有表和写入逻辑,但缺少查询展示 | 调整历史尚不能由产品和测试人员核对 | +| 8 | revision 后端查询已落地,Admin 尚未接展示 | 调整历史仍不能在产品页面核对 | ## 六、全量财务规划与交付顺序 -### 6.1 全量目标域 +### 6.1 历史全量规划(不作为一期范围) + +以下内容只保留为历史分析。若与《财务一期五页产品与接口验收契约》冲突,以五页契约为准。 | 目标域 | 业务结果 | 主要来源 | 不能提前假定的口径 | |---|---|---|---| -| 客户账户 | 预存、冻结、扣款、退款、账户流水 | 客户充值、业务扣款 | 是否采用钱包作为应收结算方式、资金归属和退款规则 | +| 客户账户 | 客户财务往来派生汇总、详情和流水 | 账单、收款和应收应付来源 | 一期不建设钱包、充值、预收或自动扣款 | | 客户应收 | 客户欠款、到期、逾期、结清 | 已发布对客费用、补差/冲销 | 收入确认节点和账期规则 | | 客户应付 | 客户代垫或返还类应付款(如确有业务) | 业务费用或人工单据 | 产品是否真的存在“平台向客户付款”的场景 | | 客户对账单 | 期间内费用汇总、明细、平台出账和客户只读查看 | 应收/应付费用池 | 一张账单是否允许混合方向、混合币种 | @@ -157,7 +174,7 @@ Firstleg 已有 `fl_inbound_fee_detail`,包含: | 结算单应付 | 供应商成本、付款、余额和结清 | 供应商费用、运力/仓储/报关成本 | 成本确认、供应商账期、审批和付款方式 | | 收付款与核销 | 到账/付款凭证,部分结算、反核销 | 银行流水或人工登记 | 是否首期接银行流水,是否允许一笔多单核销 | | 发票 | 开票申请、开具、红冲、作废和关联 | 已确认应收/收款 | 税率、含税/未税、开票主体、外部平台 | -| 费用分析 | 按费用项目、客户、供应商、期间分析 | 应收应付台账 | 统计按发生制还是结算制 | +| 费用分析 | 历史规划,一期不做 | 应收应付台账 | 不进入一期菜单、路由和验收 | | 利润 | 客户/单票/柜次/月度收入、成本、利润率 | 应收收入 + 应付成本 + 分摊 | 汇率、成本分摊、收入成本确认时间 | ### 6.2 分阶段交付顺序 @@ -169,8 +186,8 @@ Firstleg 已有 `fl_inbound_fee_detail`,包含: | F0 财务底座 | 独立 Finance 服务/数据库、财务主体、客户/供应商往来单位、币种、费用科目、期间、权限、审计、幂等、金额精度 | 所有后续对象使用同一基础口径 | 服务边界已技术确认;产品确认主体、币种、权限和编码 | | F1 应收与客户对账 | 应收费用池、平台出账、客户只读查看、结算单应收、收款登记、核销、反核销 | 费用 → 账单 → 应收 → 收款 → 结清 | 当前已发布对客费用来源 | | F2 应付与供应商结算 | 成本费用池、供应商账单、结算单应付、付款登记、付款核销、应付调整 | 成本 → 供应商账单 → 应付 → 付款 → 结清 | 供应商主数据和真实成本来源 | -| F3 客户账户与发票 | 充值、冻结、扣款、退款、账户流水;开票申请、开票登记、红冲/作废 | 账户余额与应收核销可选联动;应收与发票可追溯 | 资金/税务主体、开票平台和合规口径 | -| F4 分析与利润 | 费用分析、收入成本汇总、客户/单票/柜次/月度利润、利润率、导出 | 应收与应付同源统计,结果可追溯 | F1/F2 结算数据、汇率和分摊规则 | +| F3 客户账户与发票 | 一期只做派生客户账户总览;发票暂缓 | 账户只读聚合,不维护资金余额 | 发票不属于一期 | +| F4 分析与利润 | 一期不做 | 不建设菜单、路由或假入口 | 后续重新立项 | 阶段之间可以并行做页面原型和契约设计,但生产数据链路必须按 F0 → F1/F2 → F3 → F4 验收。F4 不能用原型静态数字先行替代真实台账。 @@ -199,9 +216,9 @@ F1 建议页面: 4. 核销记录:每笔收款对应哪些应收、核销金额、剩余余额、反核销历史。 5. 客户侧账单:只读明细、查看收款与未结余额;异议通过线下渠道反馈。 -F1 暂不包含(在 F2-F4 交付): +以下历史排除清单已被 2026-08-11 的五页范围取代。结算单应付现在属于一期;其余未确认能力继续排除: -- 供应商应付与付款; +- 供应商在线确认和自动付款(应付结算单与人工付款属于一期); - 客户预充值、余额冻结和自动扣款; - 发票申请、开票和税务接口; - 多币种折算与汇兑损益; @@ -213,20 +230,20 @@ F1 暂不包含(在 F2-F4 交付): | 范围 | 开工门禁 | |---|---| -| 客户账户 | 明确充值是否真实入账、冻结/解冻、退款、自动扣款和资金对账责任;不得直接拿账户余额抵消应收而不留核销记录 | +| 客户账户 | 已确认只做派生财务往来总览;禁止充值、冻结/解冻、退款、自动扣款和可写余额 | | 客户应收应付 | 明确客户作为付款方还是收款方;没有真实业务场景的方向不建菜单和数据 | | 供应商应付 | 必须有供应商主数据、成本来源凭证、审批人、付款方式和付款责任主体 | | 收付款核销 | 明确人工登记与银行流水的边界;收款、付款、核销、反核销分开授权 | | 发票 | 明确开票主体、税率、含税/未税、红冲/作废和外部开票平台;未确认前只保留接口扩展位 | | 费用分析/利润 | 先冻结汇率、收入成本确认日、成本分摊和跨币种折算;报表必须能钻取到原始费用和结算单 | -## 七、必须先确认的产品问题 +## 七、已确认的产品结论(历史问题关闭结果) -以下结论没有确认前,只能做可逆的模型和页面设计,不能进入完整开发: +下表原为产品问题清单,现按本轮确认结果保留。不得再把这些条目解释为待产品确认;实现仍受真实费用来源链路和专项技术门禁约束。 | 编号 | 待确认问题 | 建议默认值 | |---|---|---| -| F1 | 全量范围是否按 F0 → F1 → F2 → F3 → F4 分阶段交付 | 是,目标范围全部保留;先完成 F0,再按依赖验收,不因先做应收而删除应付、账户、发票和利润 | +| F1 | 一期领导验收范围 | 固定为客户账户、客户应收应付、客户对账单、结算单应收、结算单应付;费用分析和利润表不做 | | F2 | 账单生成维度 | 客户 + 币种 + 账期;不同币种不合并 | | F3 | 哪些费用可以进入账单 | `APPROVED + PUBLISHED + settlementIncluded=true` 且未作废、未出账 | | F4 | 当前已发布头程费用是否自动进入待对账池 | 是,但仍由财务选择并生成账单 | diff --git a/财务管理/openapi/finance-f1-manual-v1.yaml b/财务管理/openapi/finance-f1-manual-v1.yaml index 8f12f50..ef35c68 100644 --- a/财务管理/openapi/finance-f1-manual-v1.yaml +++ b/财务管理/openapi/finance-f1-manual-v1.yaml @@ -4,8 +4,9 @@ info: version: 1.2.0 description: >- F1 人工账单、收款和成本目标契约。仅 CNY;平台财务可写;客户只读且仅可见 - ISSUED、PARTIALLY_PAID、PAID 账单。固定 DTO、完整追踪字段、真实体积快照和 - File fail-closed 校验属于放行目标;当前实现差异见契约门禁文档。 + ISSUED、PARTIALLY_PAID、PAID 账单。整柜成本由 Finance 根据 containerId 调用 + Firstleg 真实快照,浏览器不提交订单体积。固定 DTO、完整追踪字段和 File fail-closed + 校验仍属于放行目标;当前实现差异见契约门禁文档。 servers: - url: /api paths: @@ -120,6 +121,21 @@ paths: $ref: '#/components/responses/BillDetailResponse' '409': $ref: '#/components/responses/IdempotencyConflict' + /platform/finance/v1/bills/{billId}/revisions: + get: + summary: 查询账单修订历史 + description: 只返回指定客户所属账单的调整前快照,跨客户或不存在账单统一拒绝。 + x-permission: platform:finance:bill:view + security: + - platformFinance: [] + parameters: + - $ref: '#/components/parameters/BillId' + - $ref: '#/components/parameters/RequiredCustomerId' + responses: + '200': + $ref: '#/components/responses/BillRevisionListResponse' + '404': + $ref: '#/components/responses/BusinessError' /platform/finance/v1/bills/{billId}/adjust: post: summary: 调整已出账账单并保留修订历史 @@ -204,9 +220,9 @@ paths: post: summary: 人工登记单票或整柜成本 description: >- - SINGLE_ORDER 直接归属经服务端验证的同客户订单;CONTAINER_SHARED 当前请求包含 - containerOrders 过渡字段,但放行前必须改由服务端从 Firstleg 取得不可篡改体积快照。 - 总体积为零时拒绝,首期不支持人工分摊。 + SINGLE_ORDER 直接归属经服务端验证的同客户订单;CONTAINER_SHARED 只提交 containerId, + Finance 从 Firstleg 取得属于该客户的柜内有效订单体积快照。快照为空或总体积无效时拒绝, + 首期不支持浏览器提交体积或人工分摊。 x-permission: platform:finance:cost:create security: - platformFinance: [] @@ -228,8 +244,8 @@ paths: summary: 上传财务凭证图片 description: >- 当前 Finance Controller 的真实入口,query 参数为 customerId,multipart 字段为 file。 - 当前 File 仅校验图片 MIME,尚无用途区分和关联状态;收款/成本落库前的 fail-closed - 校验仍是门禁阻断。 + 收款与成本创建均按客户、FINANCE_VOUCHER 业务类型和图片 MIME 校验元数据; + File 客户端缺失时仍须改为 fail-closed,详见门禁文档。 x-permission-any-of: - platform:finance:receipt:create - platform:finance:cost:create @@ -337,10 +353,6 @@ components: type: string pattern: '^(0|[1-9][0-9]*)(\.[0-9]{1,6})?$' example: '300.000000' - SignedMoney: - type: string - pattern: '^-?(0|[1-9][0-9]*)(\.[0-9]{1,6})?$' - example: '-0.000001' ResponseMeta: type: object required: @@ -592,7 +604,6 @@ components: required: - costType - containerId - - containerOrders properties: costType: type: string @@ -600,30 +611,6 @@ components: - CONTAINER_SHARED containerId: $ref: '#/components/schemas/Id' - containerOrders: - type: array - minItems: 1 - items: - $ref: '#/components/schemas/ContainerOrderVolumeInput' - description: >- - 当前 Java 过渡请求字段。调用方不得把人工填写体积视为可信快照;放行前应由 - Finance 根据 containerId 从 Firstleg 获取并校验,届时公开请求应移除此字段。 - ContainerOrderVolumeInput: - type: object - required: - - orderId - - inboundId - - containerId - - volumeCbm - properties: - orderId: - $ref: '#/components/schemas/Id' - inboundId: - $ref: '#/components/schemas/Id' - containerId: - $ref: '#/components/schemas/Id' - volumeCbm: - $ref: '#/components/schemas/Money' FinanceVoucherUploadRequest: type: object required: @@ -733,6 +720,40 @@ components: type: array items: $ref: '#/components/schemas/BillLine' + BillRevision: + type: object + required: + - revisionId + - billId + - version + - reason + - billSnapshot + - lineSnapshot + - createdBy + - createdAt + properties: + revisionId: + $ref: '#/components/schemas/Id' + billId: + $ref: '#/components/schemas/Id' + version: + type: integer + minimum: 0 + reason: + type: string + billSnapshot: + type: object + additionalProperties: true + lineSnapshot: + type: array + items: + type: object + additionalProperties: true + createdBy: + $ref: '#/components/schemas/Id' + createdAt: + type: string + format: date-time ClientBillDetail: allOf: - $ref: '#/components/schemas/ClientBill' @@ -841,7 +862,6 @@ components: - ratio - allocatedAmount - roundingAdjustment - - roundingDelta properties: orderId: $ref: '#/components/schemas/Id' @@ -857,8 +877,6 @@ components: roundingAdjustment: type: boolean description: 仅实际尾差非零时为 true。 - roundingDelta: - $ref: '#/components/schemas/SignedMoney' Cost: type: object required: @@ -1033,6 +1051,17 @@ components: code: {type: string, example: '200'} message: {type: string} data: {$ref: '#/components/schemas/BillDetail'} + BillRevisionListEnvelope: + allOf: + - $ref: '#/components/schemas/ResponseMeta' + - type: object + required: [code, message, data] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: + type: array + items: {$ref: '#/components/schemas/BillRevision'} ClientBillDetailEnvelope: allOf: - $ref: '#/components/schemas/ResponseMeta' @@ -1127,6 +1156,11 @@ components: content: application/json: schema: {$ref: '#/components/schemas/BillDetailEnvelope'} + BillRevisionListResponse: + description: 账单修订历史列表。 + content: + application/json: + schema: {$ref: '#/components/schemas/BillRevisionListEnvelope'} ClientBillDetailResponse: description: 客户可见账单详情。 content: diff --git a/财务管理/openapi/finance-phase1-five-pages-v1.yaml b/财务管理/openapi/finance-phase1-five-pages-v1.yaml new file mode 100644 index 0000000..050543c --- /dev/null +++ b/财务管理/openapi/finance-phase1-five-pages-v1.yaml @@ -0,0 +1,1170 @@ +openapi: 3.0.3 +info: + title: Finance Phase 1 Five-page Acceptance API + version: 1.0.0-draft + description: >- + 一期领导验收 5 页目标契约:客户账户、客户应收应付、客户对账单、结算单应收、 + 结算单应付。只支持 CNY。费用分析、利润表、钱包、预收和充值不属于本契约。 + 每个 operation 通过 x-implementation-status 标记当前 Java 实现事实;NOT_IMPLEMENTED + 仅代表目标契约尚未落地,不得生成按钮或模拟成功入口。 +servers: + - url: /api +x-product-scope: + included: + - CUSTOMER_ACCOUNT_DERIVED_VIEW + - CUSTOMER_ARAP_FEE_SOURCE_POOL + - CUSTOMER_RECONCILIATION_STATEMENT + - RECEIVABLE_SETTLEMENT + - PAYABLE_SETTLEMENT + excluded: + - WALLET + - PREPAYMENT + - MANUAL_RECHARGE + - COST_ANALYSIS + - PROFIT_REPORT + - FOREIGN_CURRENCY + - EXCHANGE_RATE +x-source-gate: + eligible: APPROVED + PUBLISHED + settlementIncluded=true + ACTIVE + CNY + currentStatus: Finance 已显式校验非作废事件 PUBLISHED、APPROVED、参与结算、CNY、amount>0、客户/版本/方向/来源归属;Inbox 幂等及 fin_fee_source 写入/替代/作废均已验证。 +x-current-verification: + checkedAt: '2026-08-11' + source: 当前 baiyue-firstleg 工作树中的 Controller、DTO、Service、Client 和现有 Surefire 报告。 + testStatus: Finance 35/35、Firstleg 549/549(2 skip)、File 114/114、Gateway 26/26、IAM 163/163、Phase3/PDA SQL 12/12、Order SQL 3/3,均 BUILD SUCCESS;来源消费专项 10/10。 +paths: + /internal/finance/v1/fee-source-events: + post: + servers: + - url: / + operationId: consumeFirstlegFeeSourceEvent + summary: 消费 Firstleg 不可变费用来源事件 + description: >- + 使用内部服务令牌鉴权;按 eventId 和完整事件请求哈希幂等消费,回查 Firstleg + 入库或柜子费用快照,校验客户、版本、审核、参与结算、CNY、方向和来源类型后, + 写入 fin_inbox_event 与 fin_fee_source。调整新增版本并替代旧 AVAILABLE 版本,作废只改状态不删除历史。 + x-implementation-status: IMPLEMENTED + x-test-evidence: FinanceFeeSourceEventServiceTest 10/10、InternalFinanceFeeSourceControllerTest 2/2、Finance 全量 35/35 BUILD SUCCESS;Firstleg 相关事件/Relay/快照定向测试 17/17 + x-authentication: X-Internal-Service-Token + x-error-codes: + - FIN-EVT-001 + - FIN-EVT-002 + - FIN-EVT-003 + - FIN-EVT-004 + - FIN-EVT-005 + - FIN-EVT-006 + - FIN-EVT-007 + - FIN-EVT-008 + - FIN-EVT-009 + - FIN-EVT-010 + - FIN-EVT-011 + - FIN-EVT-012 + - FIN-EVT-013 + - FIN-EVT-014 + - FIN-EVT-015 + - FIN-EVT-401 + parameters: + - $ref: '#/components/parameters/InternalServiceToken' + requestBody: + required: true + content: + application/json: + schema: {$ref: '#/components/schemas/FinanceFeeSourceEvent'} + responses: + '200': + description: 成功消费、同参重放结果或稳定业务错误。 + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/FeeSourceEventResultEnvelope' + - $ref: '#/components/schemas/ErrorEnvelope' + /platform/finance/v1/accounts: + get: + operationId: listDerivedCustomerAccounts + summary: 查询客户财务账户派生汇总 + description: 只读聚合,不返回钱包余额,不提供充值或余额调整动作。 + x-implementation-status: NOT_IMPLEMENTED + x-permission: platform:finance:account:view + parameters: + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PageSize' + - name: keyword + in: query + schema: {type: string} + - name: status + in: query + schema: {type: string, enum: [ACTIVE, DISABLED]} + responses: + '200': {$ref: '#/components/responses/AccountPageResponse'} + /platform/finance/v1/customer-accounts/{customerId}: + get: + operationId: getDerivedCustomerAccount + summary: 查询客户财务账户详情 + x-implementation-status: IMPLEMENTED + x-test-evidence: FinanceReadApiBehaviorTest 3/3 通过;fivePlatformPageMethodsUseTypedQueryFacade 覆盖该委托 + x-permission: platform:finance:bill:view + parameters: + - $ref: '#/components/parameters/CustomerId' + responses: + '200': {$ref: '#/components/responses/CustomerAccountReadResponse'} + '404': {$ref: '#/components/responses/BusinessError'} + /platform/finance/v1/accounts/{customerId}/ledger: + get: + operationId: listDerivedCustomerAccountLedger + summary: 查询客户财务派生流水 + x-implementation-status: NOT_IMPLEMENTED + x-permission: platform:finance:account:view + parameters: + - $ref: '#/components/parameters/CustomerId' + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PageSize' + - name: businessType + in: query + schema: {$ref: '#/components/schemas/LedgerBusinessType'} + - name: startDate + in: query + schema: {type: string, format: date} + - name: endDate + in: query + schema: {type: string, format: date} + responses: + '200': {$ref: '#/components/responses/LedgerPageResponse'} + /platform/finance/v1/fee-pool: + get: + operationId: listFinanceFeeSources + summary: 查询客户应收应付费用来源池 + x-implementation-status: IMPLEMENTED + x-test-evidence: FinanceReadApiBehaviorTest 3/3、FinanceFeePoolDirectionBehaviorTest 2/2 通过 + x-permission: platform:finance:bill:view + parameters: + - name: customerId + in: query + required: true + schema: {$ref: '#/components/schemas/Id'} + - name: direction + in: query + schema: {$ref: '#/components/schemas/FeeDirection'} + responses: + '200': {$ref: '#/components/responses/FeePoolReadResponse'} + /platform/finance/v1/fee-sources/{feeSourceId}: + get: + operationId: getFinanceFeeSource + summary: 查询费用来源详情、业务关联和操作记录 + x-implementation-status: NOT_IMPLEMENTED + x-permission: platform:finance:fee-source:view + parameters: + - $ref: '#/components/parameters/FeeSourceId' + responses: + '200': {$ref: '#/components/responses/FeeSourceDetailResponse'} + '404': {$ref: '#/components/responses/BusinessError'} + /platform/finance/v1/fee-sources/{feeSourceId}/review: + post: + operationId: reviewFinanceFeeSource + summary: 审核费用来源 + x-implementation-status: NOT_IMPLEMENTED + x-permission: platform:finance:fee-source:review + parameters: + - $ref: '#/components/parameters/FeeSourceId' + - $ref: '#/components/parameters/IdempotencyKey' + requestBody: + required: true + content: + application/json: + schema: {$ref: '#/components/schemas/ReviewRequest'} + responses: + '200': {$ref: '#/components/responses/FeeSourceDetailResponse'} + '409': {$ref: '#/components/responses/BusinessError'} + /platform/finance/v1/fee-sources/{feeSourceId}/settlement-inclusion: + put: + operationId: updateFeeSourceSettlementInclusion + summary: 设置参与结算或禁止结算 + x-implementation-status: NOT_IMPLEMENTED + x-permission: platform:finance:fee-source:settlement + parameters: + - $ref: '#/components/parameters/FeeSourceId' + - $ref: '#/components/parameters/IdempotencyKey' + requestBody: + required: true + content: + application/json: + schema: {$ref: '#/components/schemas/SettlementInclusionRequest'} + responses: + '200': {$ref: '#/components/responses/FeeSourceDetailResponse'} + '409': {$ref: '#/components/responses/BusinessError'} + /platform/finance/v1/statements: + get: + operationId: listReconciliationStatements + summary: 查询客户对账单 + x-implementation-status: IMPLEMENTED + x-test-evidence: FinanceReadApiBehaviorTest 3/3 通过;fivePlatformPageMethodsUseTypedQueryFacade 覆盖该委托 + x-permission: platform:finance:bill:view + parameters: + - name: customerId + in: query + required: true + schema: {$ref: '#/components/schemas/Id'} + responses: + '200': {$ref: '#/components/responses/StatementListReadResponse'} + post: + operationId: createReconciliationStatement + summary: 按截止时间或勾选来源生成客户对账单草稿 + x-implementation-status: NOT_IMPLEMENTED + x-permission: platform:finance:reconciliation:create + parameters: + - $ref: '#/components/parameters/IdempotencyKey' + requestBody: + required: true + content: + application/json: + schema: {$ref: '#/components/schemas/CreateReconciliationRequest'} + responses: + '200': {$ref: '#/components/responses/ReconciliationDetailResponse'} + '409': {$ref: '#/components/responses/BusinessError'} + /platform/finance/v1/bills/{billId}: + get: + operationId: getReconciliationStatement + summary: 查询客户对账单详情、费用明细和支付历史 + x-implementation-status: IMPLEMENTED + x-test-evidence: FinanceReadApiBehaviorTest 3/3 通过;客户详情隔离与固定 DTO 行为有直接覆盖 + x-permission: platform:finance:bill:view + parameters: + - $ref: '#/components/parameters/BillId' + responses: + '200': {$ref: '#/components/responses/StatementDetailReadResponse'} + '404': {$ref: '#/components/responses/BusinessError'} + /platform/finance/v1/reconciliation-statements/{statementId}/actions: + post: + operationId: actionReconciliationStatement + summary: 提交审核、通过、拒绝或作废客户对账单 + x-implementation-status: NOT_IMPLEMENTED + x-permission: platform:finance:reconciliation:review + parameters: + - $ref: '#/components/parameters/StatementId' + - $ref: '#/components/parameters/IdempotencyKey' + requestBody: + required: true + content: + application/json: + schema: {$ref: '#/components/schemas/DocumentActionRequest'} + responses: + '200': {$ref: '#/components/responses/ReconciliationDetailResponse'} + '409': {$ref: '#/components/responses/BusinessError'} + /platform/finance/v1/receivable-settlements: + get: + operationId: listReceivableSettlements + summary: 查询结算单应收 + x-implementation-status: IMPLEMENTED + x-test-evidence: FinanceReadApiBehaviorTest 3/3 通过;fivePlatformPageMethodsUseTypedQueryFacade 覆盖该委托 + x-permission: platform:finance:bill:view + parameters: + - name: customerId + in: query + required: true + schema: {$ref: '#/components/schemas/Id'} + responses: + '200': {$ref: '#/components/responses/ReceivableSettlementListReadResponse'} + post: + operationId: createReceivableSettlement + summary: 从审核通过的客户对账单生成应收结算单 + x-implementation-status: NOT_IMPLEMENTED + x-permission: platform:finance:settlement-ar:create + parameters: + - $ref: '#/components/parameters/IdempotencyKey' + requestBody: + required: true + content: + application/json: + schema: {$ref: '#/components/schemas/CreateReceivableSettlementRequest'} + responses: + '200': {$ref: '#/components/responses/SettlementDetailResponse'} + '409': {$ref: '#/components/responses/BusinessError'} + /platform/finance/v1/payable-settlements: + get: + operationId: listPayableSettlements + summary: 查询结算单应付 + x-implementation-status: IMPLEMENTED + x-test-evidence: FinanceReadApiBehaviorTest 3/3 通过;fivePlatformPageMethodsUseTypedQueryFacade 覆盖该委托 + x-permission: platform:finance:cost:view + parameters: + - name: customerId + in: query + required: true + schema: {$ref: '#/components/schemas/Id'} + responses: + '200': {$ref: '#/components/responses/PayableSettlementListReadResponse'} + post: + operationId: createPayableSettlement + summary: 从审核通过且参与结算的应付来源生成应付结算单 + x-implementation-status: NOT_IMPLEMENTED + x-permission: platform:finance:settlement-ap:create + parameters: + - $ref: '#/components/parameters/IdempotencyKey' + requestBody: + required: true + content: + application/json: + schema: {$ref: '#/components/schemas/CreatePayableSettlementRequest'} + responses: + '200': {$ref: '#/components/responses/SettlementDetailResponse'} + '409': {$ref: '#/components/responses/BusinessError'} + /platform/finance/v1/receipts: + get: + operationId: listPlatformReceipts + summary: 平台按客户查询人工收款记录 + x-implementation-status: IMPLEMENTED + x-test-evidence: FinanceReadApiBehaviorTest 3/3 通过;PlatformReceipt 固定 DTO 与客户范围委托已验证 + x-permission: platform:finance:receipt:view + parameters: + - name: customerId + in: query + required: true + schema: {$ref: '#/components/schemas/Id'} + responses: + '200': {$ref: '#/components/responses/PlatformReceiptListReadResponse'} + post: + operationId: createReceiptWithAllocations + summary: 人工登记一笔收款并分配到一张或多张应收结算单 + description: >- + 收款总额必须等于 allocations 金额之和;所有结算单必须属于同一客户且为 CNY, + 每笔分配不得超过对应未收余额。示例:收款 300 元可分配到 3 张结算单各 100 元, + 共用同一组收款凭证图片。File 校验失败或不可用时必须 fail-closed,不得落库。 + x-implementation-status: IMPLEMENTED + x-test-evidence: FinanceServiceValidationTest 10/10、PlatformFinanceControllerBehaviorTest 2/2 通过;覆盖多账单分配和凭证 fail-closed + x-permission: platform:finance:receipt:create + parameters: + - $ref: '#/components/parameters/IdempotencyKey' + requestBody: + required: true + content: + application/json: + schema: {$ref: '#/components/schemas/CreateReceiptRequest'} + responses: + '200': {$ref: '#/components/responses/ResourceResultResponse'} + '409': {$ref: '#/components/responses/BusinessError'} + /client/finance/v1/receipts: + get: + operationId: listClientVisibleReceipts + summary: 客户只读查询已确认收款和账单分配 + description: 使用登录客户身份隔离,不接收 customerId;不返回凭证、操作人、内部备注或 tenantId。 + x-implementation-status: IMPLEMENTED + x-test-evidence: FinanceReadApiBehaviorTest 3/3 通过;ClientReceipt 脱敏序列化和登录客户隔离有直接覆盖 + x-permission: merchant:finance:statement:view + responses: + '200': {$ref: '#/components/responses/ClientReceiptListReadResponse'} + /platform/finance/v1/settlements/{settlementId}: + get: + operationId: getSettlement + summary: 查询应收或应付结算单详情 + x-implementation-status: NOT_IMPLEMENTED + x-permission-any-of: + - platform:finance:settlement-ar:view + - platform:finance:settlement-ap:view + parameters: + - $ref: '#/components/parameters/SettlementId' + responses: + '200': {$ref: '#/components/responses/SettlementDetailResponse'} + '404': {$ref: '#/components/responses/BusinessError'} + /platform/finance/v1/settlements/{settlementId}/transactions: + post: + operationId: createSettlementPayment + summary: 人工登记应付结算单付款并上传凭证 + x-implementation-status: NOT_IMPLEMENTED + x-permission: platform:finance:payment:create + parameters: + - $ref: '#/components/parameters/SettlementId' + - $ref: '#/components/parameters/IdempotencyKey' + requestBody: + required: true + content: + application/json: + schema: {$ref: '#/components/schemas/CreateSettlementPaymentRequest'} + responses: + '200': {$ref: '#/components/responses/SettlementDetailResponse'} + '409': {$ref: '#/components/responses/BusinessError'} +components: + parameters: + InternalServiceToken: + name: X-Internal-Service-Token + in: header + required: true + schema: {type: string, minLength: 1} + CustomerId: + name: customerId + in: path + required: true + schema: {$ref: '#/components/schemas/Id'} + FeeSourceId: + name: feeSourceId + in: path + required: true + schema: {$ref: '#/components/schemas/Id'} + StatementId: + name: statementId + in: path + required: true + schema: {$ref: '#/components/schemas/Id'} + BillId: + name: billId + in: path + required: true + schema: {$ref: '#/components/schemas/Id'} + SettlementId: + name: settlementId + in: path + required: true + schema: {$ref: '#/components/schemas/Id'} + Page: + name: page + in: query + required: false + schema: {type: integer, minimum: 1, default: 1} + PageSize: + name: pageSize + in: query + required: false + schema: {type: integer, minimum: 1, maximum: 200, default: 20} + IdempotencyKey: + name: Idempotency-Key + in: header + required: true + schema: {type: string, minLength: 1, maxLength: 128} + description: 完整规范化 method/path/customer/body 哈希;同参重放,异参冲突。 + schemas: + Id: + type: string + pattern: '^[1-9][0-9]*$' + FinanceFeeSourceEvent: + type: object + required: [eventId, eventType, eventVersion, producer, occurredAt, tenantId, aggregateType, aggregateId, sequence, idempotencyKey, payloadHash, payload] + properties: + eventId: {type: string, minLength: 1} + eventType: + type: string + enum: [FIRSTLEG_FEE_PUBLISHED, FIRSTLEG_FEE_ADJUSTED, FIRSTLEG_FEE_VOIDED] + eventVersion: {type: integer, minimum: 1} + producer: {type: string, enum: [firstleg-service]} + occurredAt: {type: string, format: date-time} + tenantId: {$ref: '#/components/schemas/Id'} + aggregateType: {type: string, enum: [INBOUND, CONTAINER]} + aggregateId: {$ref: '#/components/schemas/Id'} + sequence: {type: integer, minimum: 1} + idempotencyKey: {type: string, minLength: 1} + payloadHash: {type: string, pattern: '^[a-f0-9]{64}$'} + payload: {$ref: '#/components/schemas/FinanceFeeSourceEventPayload'} + FinanceFeeSourceEventPayload: + type: object + required: [feeId, feeVersion] + properties: + inboundId: {$ref: '#/components/schemas/Id'} + containerId: {$ref: '#/components/schemas/Id'} + feeId: {$ref: '#/components/schemas/Id'} + feeVersion: {type: integer, minimum: 1} + oneOf: + - required: [inboundId] + not: {required: [containerId]} + - required: [containerId] + not: {required: [inboundId]} + FeeSourceEventResult: + type: object + required: [feeSourceId] + properties: + feeSourceId: {type: integer, format: int64, minimum: 1} + Money: + type: string + pattern: '^(0|[1-9][0-9]*)(\.[0-9]{1,6})?$' + FeeDirection: + type: string + enum: [RECEIVABLE, PAYABLE] + FeeSourceType: + type: string + enum: [INBOUND_FEE, CONTAINER_FEE] + ReviewStatus: + type: string + enum: [PENDING, APPROVED, REJECTED] + ReconciliationStatus: + type: string + enum: [DRAFT, PENDING_REVIEW, APPROVED, REJECTED, VOIDED] + SettlementStatus: + type: string + enum: [DRAFT, PENDING_REVIEW, ISSUED, PARTIALLY_PAID, PAID, REJECTED, VOIDED] + PaymentStatus: + type: string + enum: [UNPAID, PARTIALLY_PAID, PAID, OVERDUE] + LedgerBusinessType: + type: string + enum: [FEE_INCLUDED, FEE_EXCLUDED, RECONCILIATION_CREATED, SETTLEMENT_ISSUED, RECEIPT, PAYMENT, ADJUSTMENT, VOID] + TraceMeta: + type: object + required: [requestId, traceId, timestamp] + properties: + requestId: {type: string, minLength: 1} + traceId: {type: string, minLength: 1} + timestamp: {type: string, format: date-time} + PageMeta: + type: object + required: [page, pageSize, total] + properties: + page: {type: integer, minimum: 1} + pageSize: {type: integer, minimum: 1} + total: {type: integer, format: int64, minimum: 0} + AccountSummary: + type: object + required: [customerId, customerCode, customerName, outstandingReceivable, pendingBilling, receivedInPeriod, outstandingPayable, status, lastChangedAt] + properties: + customerId: {$ref: '#/components/schemas/Id'} + customerCode: {type: string} + customerName: {type: string} + contactName: {type: string} + contactPhone: {type: string} + outstandingReceivable: {$ref: '#/components/schemas/Money'} + pendingBilling: {$ref: '#/components/schemas/Money'} + receivedInPeriod: {$ref: '#/components/schemas/Money'} + outstandingPayable: {$ref: '#/components/schemas/Money'} + status: {type: string, enum: [ACTIVE, DISABLED]} + lastChangedAt: {type: string, format: date-time} + AccountDetail: + allOf: + - $ref: '#/components/schemas/AccountSummary' + - type: object + properties: + email: {type: string} + creditDays: {type: integer, minimum: 0} + CustomerAccountRead: + type: object + required: [customerId, currency, accountMode, totalReceivable, totalReceived, outstandingAmount, entries] + properties: + customerId: {$ref: '#/components/schemas/Id'} + currency: {type: string, enum: [CNY]} + accountMode: {type: string, enum: [MANUAL_RECEIVABLE_LEDGER]} + totalReceivable: {$ref: '#/components/schemas/Money'} + totalReceived: {$ref: '#/components/schemas/Money'} + outstandingAmount: {$ref: '#/components/schemas/Money'} + entries: + type: array + items: {$ref: '#/components/schemas/AccountEntryRead'} + AccountEntryRead: + type: object + required: [entryType, resourceId, resourceNo, occurredAt, debitAmount, creditAmount, currency, status] + properties: + entryType: {type: string, enum: [RECEIVABLE, RECEIPT]} + resourceId: {$ref: '#/components/schemas/Id'} + resourceNo: {type: string} + occurredAt: {type: string, format: date-time} + debitAmount: {$ref: '#/components/schemas/Money'} + creditAmount: {$ref: '#/components/schemas/Money'} + currency: {type: string, enum: [CNY]} + status: {type: string} + LedgerEntry: + type: object + required: [ledgerId, occurredAt, businessType, documentType, documentId, receivableChange, payableChange, outstandingReceivableAfter, operatorName] + properties: + ledgerId: {$ref: '#/components/schemas/Id'} + occurredAt: {type: string, format: date-time} + businessType: {$ref: '#/components/schemas/LedgerBusinessType'} + documentType: {type: string} + documentId: {$ref: '#/components/schemas/Id'} + documentNo: {type: string} + receivableChange: {$ref: '#/components/schemas/Money'} + payableChange: {$ref: '#/components/schemas/Money'} + outstandingReceivableAfter: {$ref: '#/components/schemas/Money'} + operatorName: {type: string} + remark: {type: string} + FeeSource: + type: object + required: [feeSourceId, sourceType, sourceFeeId, sourceVersion, direction, customerId, businessDate, feeItemName, amount, currency, reviewStatus, settlementIncluded, sourceStatus, statementGenerated] + properties: + feeSourceId: {$ref: '#/components/schemas/Id'} + sourceType: {$ref: '#/components/schemas/FeeSourceType'} + sourceFeeId: {$ref: '#/components/schemas/Id'} + sourceVersion: {type: integer, minimum: 1} + direction: {$ref: '#/components/schemas/FeeDirection'} + customerId: {$ref: '#/components/schemas/Id'} + customerName: {type: string} + supplierId: {$ref: '#/components/schemas/Id'} + supplierName: {type: string} + inboundId: {$ref: '#/components/schemas/Id'} + inboundCode: {type: string} + containerId: {$ref: '#/components/schemas/Id'} + containerNo: {type: string} + businessDate: {type: string, format: date} + feeItemName: {type: string} + quantity: {$ref: '#/components/schemas/Money'} + unitPrice: {$ref: '#/components/schemas/Money'} + amount: {$ref: '#/components/schemas/Money'} + currency: {type: string, enum: [CNY]} + reviewStatus: {$ref: '#/components/schemas/ReviewStatus'} + settlementIncluded: {type: boolean} + sourceStatus: {type: string, enum: [AVAILABLE, LOCKED, BILLED, VOIDED, SUPERSEDED]} + statementGenerated: {type: boolean} + FeeSourceDetail: + allOf: + - $ref: '#/components/schemas/FeeSource' + - type: object + properties: + reviewReason: {type: string} + settlementReason: {type: string} + sourceHash: {type: string} + operationLogs: + type: array + items: {$ref: '#/components/schemas/OperationLog'} + FeePoolItemRead: + type: object + required: [poolItemId, direction, sourceType, sourceId, sourceVersion, feeItemName, amount, currency, status, createdAt] + properties: + poolItemId: {type: string} + direction: {$ref: '#/components/schemas/FeeDirection'} + sourceType: {type: string, enum: [FIRSTLEG_FEE, MANUAL_COST]} + sourceId: {$ref: '#/components/schemas/Id'} + sourceVersion: {type: integer, minimum: 1} + orderId: {$ref: '#/components/schemas/Id'} + inboundId: {$ref: '#/components/schemas/Id'} + containerId: {$ref: '#/components/schemas/Id'} + feeItemName: {type: string} + amount: {$ref: '#/components/schemas/Money'} + currency: {type: string, enum: [CNY]} + status: {type: string} + createdAt: {type: string, format: date-time} + OperationLog: + type: object + required: [occurredAt, action, operatorName, result] + properties: + occurredAt: {type: string, format: date-time} + action: {type: string} + operatorName: {type: string} + result: {type: string} + remark: {type: string} + ReviewRequest: + type: object + required: [action, expectedVersion] + properties: + action: {type: string, enum: [APPROVE, REJECT]} + reason: {type: string, maxLength: 500} + expectedVersion: {type: integer, minimum: 0} + SettlementInclusionRequest: + type: object + required: [included, reason, expectedVersion] + properties: + included: {type: boolean} + reason: {type: string, minLength: 1, maxLength: 500} + expectedVersion: {type: integer, minimum: 0} + ReconciliationSummary: + type: object + required: [statementId, statementNo, customerId, customerName, periodStart, periodEnd, creditDays, currency, totalReceivable, receivedAmount, outstandingAmount, settled, status, version, createdAt, updatedAt] + properties: + statementId: {$ref: '#/components/schemas/Id'} + statementNo: {type: string} + customerId: {$ref: '#/components/schemas/Id'} + customerName: {type: string} + periodStart: {type: string, format: date} + periodEnd: {type: string, format: date} + creditDays: {type: integer, minimum: 0} + currency: {type: string, enum: [CNY]} + totalReceivable: {$ref: '#/components/schemas/Money'} + receivedAmount: {$ref: '#/components/schemas/Money'} + outstandingAmount: {$ref: '#/components/schemas/Money'} + lastReceiptAt: {type: string, format: date-time} + settled: {type: boolean} + status: {$ref: '#/components/schemas/ReconciliationStatus'} + version: {type: integer, minimum: 0} + createdAt: {type: string, format: date-time} + updatedAt: {type: string, format: date-time} + ReconciliationDetail: + allOf: + - $ref: '#/components/schemas/ReconciliationSummary' + - type: object + required: [lines, paymentHistory] + properties: + remark: {type: string} + lines: + type: array + items: {$ref: '#/components/schemas/FeeSource'} + paymentHistory: + type: array + items: {$ref: '#/components/schemas/SettlementTransaction'} + StatementRead: + type: object + required: [id, customerId, billNo, billDate, dueDate, totalAmount, receivedAmount, outstandingAmount, currency, status, version] + properties: + id: {$ref: '#/components/schemas/Id'} + customerId: {$ref: '#/components/schemas/Id'} + billNo: {type: string} + customerName: {type: string} + billDate: {type: string, format: date} + dueDate: {type: string, format: date} + totalAmount: {$ref: '#/components/schemas/Money'} + receivedAmount: {$ref: '#/components/schemas/Money'} + outstandingAmount: {$ref: '#/components/schemas/Money'} + currency: {type: string, enum: [CNY]} + status: {type: string, enum: [DRAFT, ISSUED, PARTIALLY_PAID, PAID, VOIDED]} + version: {type: integer, minimum: 0} + StatementLineRead: + type: object + required: [id, sourceType, sourceId, sourceVersion, feeItemName, amount, currency] + properties: + id: {$ref: '#/components/schemas/Id'} + sourceType: {type: string} + sourceId: {$ref: '#/components/schemas/Id'} + sourceVersion: {type: integer, minimum: 1} + orderId: {$ref: '#/components/schemas/Id'} + inboundId: {$ref: '#/components/schemas/Id'} + feeItemCode: {type: string} + feeItemName: {type: string} + amount: {$ref: '#/components/schemas/Money'} + currency: {type: string, enum: [CNY]} + StatementDetailRead: + type: object + required: [statement, lines] + properties: + statement: {$ref: '#/components/schemas/StatementRead'} + lines: + type: array + items: {$ref: '#/components/schemas/StatementLineRead'} + ReceivableSettlementRead: + type: object + required: [id, customerId, settlementNo, billDate, dueDate, receivableAmount, receivedAmount, outstandingAmount, currency, status, version] + properties: + id: {$ref: '#/components/schemas/Id'} + customerId: {$ref: '#/components/schemas/Id'} + settlementNo: {type: string} + billDate: {type: string, format: date} + dueDate: {type: string, format: date} + receivableAmount: {$ref: '#/components/schemas/Money'} + receivedAmount: {$ref: '#/components/schemas/Money'} + outstandingAmount: {$ref: '#/components/schemas/Money'} + currency: {type: string, enum: [CNY]} + status: {type: string} + version: {type: integer, minimum: 0} + PayableSettlementRead: + type: object + required: [id, customerId, settlementNo, costType, feeItemName, payableAmount, paidAmount, outstandingAmount, currency, status, createdAt, version] + properties: + id: {$ref: '#/components/schemas/Id'} + customerId: {$ref: '#/components/schemas/Id'} + settlementNo: {type: string} + costType: {type: string, enum: [SINGLE_ORDER, CONTAINER_SHARED]} + orderId: {$ref: '#/components/schemas/Id'} + inboundId: {$ref: '#/components/schemas/Id'} + containerId: {$ref: '#/components/schemas/Id'} + supplierName: {type: string} + feeItemName: {type: string} + payableAmount: {$ref: '#/components/schemas/Money'} + paidAmount: {$ref: '#/components/schemas/Money'} + outstandingAmount: {$ref: '#/components/schemas/Money'} + currency: {type: string, enum: [CNY]} + status: {type: string} + createdAt: {type: string, format: date-time} + version: {type: integer, minimum: 0} + CreateReconciliationRequest: + type: object + required: [customerId, generationMode, periodStart, periodEnd] + properties: + customerId: {$ref: '#/components/schemas/Id'} + generationMode: {type: string, enum: [BY_CUTOFF, SELECTED]} + feeSourceIds: + type: array + uniqueItems: true + items: {$ref: '#/components/schemas/Id'} + periodStart: {type: string, format: date} + periodEnd: {type: string, format: date} + remark: {type: string, maxLength: 500} + DocumentActionRequest: + type: object + required: [action, reason, expectedVersion] + properties: + action: {type: string, enum: [SUBMIT, APPROVE, REJECT, VOID]} + reason: {type: string, minLength: 1, maxLength: 500} + expectedVersion: {type: integer, minimum: 0} + SettlementSummary: + type: object + required: [settlementId, settlementNo, direction, currency, totalAmount, paidAmount, outstandingAmount, generatedDate, dueDate, paymentStatus, status, version] + properties: + settlementId: {$ref: '#/components/schemas/Id'} + settlementNo: {type: string} + direction: {$ref: '#/components/schemas/FeeDirection'} + customerId: {$ref: '#/components/schemas/Id'} + customerName: {type: string} + supplierId: {$ref: '#/components/schemas/Id'} + supplierName: {type: string} + reconciliationStatementId: {$ref: '#/components/schemas/Id'} + businessPeriod: {type: string} + currency: {type: string, enum: [CNY]} + totalAmount: {$ref: '#/components/schemas/Money'} + paidAmount: {$ref: '#/components/schemas/Money'} + outstandingAmount: {$ref: '#/components/schemas/Money'} + generatedDate: {type: string, format: date} + dueDate: {type: string, format: date} + paymentStatus: {$ref: '#/components/schemas/PaymentStatus'} + status: {$ref: '#/components/schemas/SettlementStatus'} + version: {type: integer, minimum: 0} + SettlementDetail: + allOf: + - $ref: '#/components/schemas/SettlementSummary' + - type: object + required: [lines, transactions] + properties: + lines: + type: array + items: {$ref: '#/components/schemas/FeeSource'} + transactions: + type: array + items: {$ref: '#/components/schemas/SettlementTransaction'} + CreateReceivableSettlementRequest: + type: object + required: [reconciliationStatementId] + properties: + reconciliationStatementId: {$ref: '#/components/schemas/Id'} + CreatePayableSettlementRequest: + type: object + required: [supplierId, feeSourceIds, dueDate] + properties: + supplierId: {$ref: '#/components/schemas/Id'} + feeSourceIds: + type: array + minItems: 1 + uniqueItems: true + items: {$ref: '#/components/schemas/Id'} + dueDate: {type: string, format: date} + SettlementTransaction: + type: object + required: [transactionId, transactionType, transactionDate, amount, method, createdBy, createdAt] + properties: + transactionId: {$ref: '#/components/schemas/Id'} + transactionType: {type: string, enum: [RECEIPT, PAYMENT]} + transactionDate: {type: string, format: date} + amount: {$ref: '#/components/schemas/Money'} + method: {type: string, enum: [BANK_TRANSFER, CASH, OTHER]} + referenceNo: {type: string} + createdBy: {$ref: '#/components/schemas/Id'} + createdAt: {type: string, format: date-time} + remark: {type: string} + ReceiptAllocationRequest: + type: object + required: [settlementId, amount] + properties: + settlementId: {$ref: '#/components/schemas/Id'} + amount: {$ref: '#/components/schemas/Money'} + CreateReceiptRequest: + type: object + required: [customerId, receivedDate, amount, paymentMethod, voucherFileIds, allocations] + properties: + customerId: {$ref: '#/components/schemas/Id'} + receivedDate: {type: string, format: date} + amount: {$ref: '#/components/schemas/Money'} + currency: {type: string, enum: [CNY], default: CNY} + paymentMethod: {type: string, enum: [BANK_TRANSFER, CASH, OTHER]} + voucherFileIds: + type: array + minItems: 1 + uniqueItems: true + items: {$ref: '#/components/schemas/Id'} + allocations: + type: array + minItems: 1 + items: {$ref: '#/components/schemas/ReceiptAllocationRequest'} + remark: {type: string, maxLength: 500} + ClientReceiptRead: + type: object + required: [id, receiptNo, receivedAmount, receivedAt, paymentMethod, currency, status, allocations] + properties: + id: {$ref: '#/components/schemas/Id'} + receiptNo: {type: string} + receivedAmount: {$ref: '#/components/schemas/Money'} + receivedAt: {type: string, format: date-time} + paymentMethod: {type: string, enum: [BANK_TRANSFER, CASH, OTHER]} + currency: {type: string, enum: [CNY]} + status: {type: string, enum: [CONFIRMED]} + allocations: + type: array + items: {$ref: '#/components/schemas/ClientReceiptAllocationRead'} + PlatformReceiptRead: + type: object + required: [id, customerId, receiptNo, receivedAmount, receivedAt, paymentMethod, voucherFileIds, currency, status, version, allocations] + properties: + id: {$ref: '#/components/schemas/Id'} + customerId: {$ref: '#/components/schemas/Id'} + receiptNo: {type: string} + receivedAmount: {$ref: '#/components/schemas/Money'} + receivedAt: {type: string, format: date-time} + paymentMethod: {type: string, enum: [BANK_TRANSFER, CASH, OTHER]} + voucherNo: {type: string} + voucherFileIds: + type: array + items: {type: string} + currency: {type: string, enum: [CNY]} + status: {type: string} + remark: {type: string} + version: {type: integer, minimum: 0} + allocations: + type: array + items: {$ref: '#/components/schemas/ClientReceiptAllocationRead'} + ClientReceiptAllocationRead: + type: object + required: [billId, billNo, appliedAmount] + properties: + billId: {$ref: '#/components/schemas/Id'} + billNo: {type: string} + appliedAmount: {$ref: '#/components/schemas/Money'} + ResourceResult: + type: object + required: [id, resourceNo, status] + properties: + id: {$ref: '#/components/schemas/Id'} + resourceNo: {type: string} + status: {type: string} + version: {type: integer, minimum: 0} + CreateSettlementPaymentRequest: + type: object + required: [transactionDate, amount, method, voucherFileIds] + properties: + transactionDate: {type: string, format: date} + amount: {$ref: '#/components/schemas/Money'} + method: {type: string, enum: [BANK_TRANSFER, CASH, OTHER]} + referenceNo: {type: string} + voucherFileIds: + type: array + minItems: 1 + uniqueItems: true + items: {$ref: '#/components/schemas/Id'} + remark: {type: string, maxLength: 500} + ErrorEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message] + properties: + code: {type: string} + message: {type: string} + FeeSourceEventResultEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message, data] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: {$ref: '#/components/schemas/FeeSourceEventResult'} + AccountPageEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message, data, page] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: {type: array, items: {$ref: '#/components/schemas/AccountSummary'}} + page: {$ref: '#/components/schemas/PageMeta'} + AccountDetailEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message, data] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: {$ref: '#/components/schemas/AccountDetail'} + LedgerPageEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message, data, page] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: {type: array, items: {$ref: '#/components/schemas/LedgerEntry'}} + page: {$ref: '#/components/schemas/PageMeta'} + FeeSourcePageEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message, data, page] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: {type: array, items: {$ref: '#/components/schemas/FeeSource'}} + page: {$ref: '#/components/schemas/PageMeta'} + FeeSourceDetailEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message, data] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: {$ref: '#/components/schemas/FeeSourceDetail'} + ReconciliationPageEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message, data, page] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: {type: array, items: {$ref: '#/components/schemas/ReconciliationSummary'}} + page: {$ref: '#/components/schemas/PageMeta'} + ReconciliationDetailEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message, data] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: {$ref: '#/components/schemas/ReconciliationDetail'} + SettlementPageEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message, data, page] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: {type: array, items: {$ref: '#/components/schemas/SettlementSummary'}} + page: {$ref: '#/components/schemas/PageMeta'} + SettlementDetailEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message, data] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: {$ref: '#/components/schemas/SettlementDetail'} + CustomerAccountReadEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message, data] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: {$ref: '#/components/schemas/CustomerAccountRead'} + FeePoolReadEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message, data] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: {type: array, items: {$ref: '#/components/schemas/FeePoolItemRead'}} + StatementListReadEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message, data] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: {type: array, items: {$ref: '#/components/schemas/StatementRead'}} + StatementDetailReadEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message, data] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: {$ref: '#/components/schemas/StatementDetailRead'} + ReceivableSettlementListReadEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message, data] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: {type: array, items: {$ref: '#/components/schemas/ReceivableSettlementRead'}} + PayableSettlementListReadEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message, data] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: {type: array, items: {$ref: '#/components/schemas/PayableSettlementRead'}} + ClientReceiptListReadEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message, data] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: {type: array, items: {$ref: '#/components/schemas/ClientReceiptRead'}} + PlatformReceiptListReadEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message, data] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: {type: array, items: {$ref: '#/components/schemas/PlatformReceiptRead'}} + ResourceResultEnvelope: + allOf: + - $ref: '#/components/schemas/TraceMeta' + - type: object + required: [code, message, data] + properties: + code: {type: string, example: '200'} + message: {type: string} + data: {$ref: '#/components/schemas/ResourceResult'} + responses: + AccountPageResponse: + description: 客户账户派生汇总分页。 + content: {application/json: {schema: {$ref: '#/components/schemas/AccountPageEnvelope'}}} + AccountDetailResponse: + description: 客户账户派生详情。 + content: {application/json: {schema: {$ref: '#/components/schemas/AccountDetailEnvelope'}}} + LedgerPageResponse: + description: 客户财务派生流水分页。 + content: {application/json: {schema: {$ref: '#/components/schemas/LedgerPageEnvelope'}}} + FeeSourcePageResponse: + description: 费用来源分页。 + content: {application/json: {schema: {$ref: '#/components/schemas/FeeSourcePageEnvelope'}}} + FeeSourceDetailResponse: + description: 费用来源详情。 + content: {application/json: {schema: {$ref: '#/components/schemas/FeeSourceDetailEnvelope'}}} + ReconciliationPageResponse: + description: 客户对账单分页。 + content: {application/json: {schema: {$ref: '#/components/schemas/ReconciliationPageEnvelope'}}} + ReconciliationDetailResponse: + description: 客户对账单详情。 + content: {application/json: {schema: {$ref: '#/components/schemas/ReconciliationDetailEnvelope'}}} + SettlementPageResponse: + description: 结算单分页。 + content: {application/json: {schema: {$ref: '#/components/schemas/SettlementPageEnvelope'}}} + SettlementDetailResponse: + description: 结算单详情。 + content: {application/json: {schema: {$ref: '#/components/schemas/SettlementDetailEnvelope'}}} + CustomerAccountReadResponse: + description: 当前固定 DTO 客户账户及流水。 + content: {application/json: {schema: {$ref: '#/components/schemas/CustomerAccountReadEnvelope'}}} + FeePoolReadResponse: + description: 当前固定 DTO 客户应收应付费用池。 + content: {application/json: {schema: {$ref: '#/components/schemas/FeePoolReadEnvelope'}}} + StatementListReadResponse: + description: 当前固定 DTO 客户对账单列表。 + content: {application/json: {schema: {$ref: '#/components/schemas/StatementListReadEnvelope'}}} + StatementDetailReadResponse: + description: 当前固定 DTO 客户对账单详情。 + content: {application/json: {schema: {$ref: '#/components/schemas/StatementDetailReadEnvelope'}}} + ReceivableSettlementListReadResponse: + description: 当前固定 DTO 应收结算单列表。 + content: {application/json: {schema: {$ref: '#/components/schemas/ReceivableSettlementListReadEnvelope'}}} + PayableSettlementListReadResponse: + description: 当前固定 DTO 应付结算单列表。 + content: {application/json: {schema: {$ref: '#/components/schemas/PayableSettlementListReadEnvelope'}}} + ClientReceiptListReadResponse: + description: 客户专用收款 DTO 列表,不含凭证和内部字段。 + content: {application/json: {schema: {$ref: '#/components/schemas/ClientReceiptListReadEnvelope'}}} + PlatformReceiptListReadResponse: + description: 平台专用收款 DTO 列表,包含凭证引用和账单分配。 + content: {application/json: {schema: {$ref: '#/components/schemas/PlatformReceiptListReadEnvelope'}}} + ResourceResultResponse: + description: 写操作资源结果。 + content: {application/json: {schema: {$ref: '#/components/schemas/ResourceResultEnvelope'}}} + BusinessError: + description: 稳定业务错误。 + content: {application/json: {schema: {$ref: '#/components/schemas/ErrorEnvelope'}}} diff --git a/财务管理/财务一期五页产品与接口验收契约.md b/财务管理/财务一期五页产品与接口验收契约.md new file mode 100644 index 0000000..004aab5 --- /dev/null +++ b/财务管理/财务一期五页产品与接口验收契约.md @@ -0,0 +1,174 @@ +# 财务一期五页产品与接口验收契约 + +> 日期:2026-08-11 +> 状态:产品范围已确认;Finance 来源链与五页查询契约已实现并验证,剩余 11 个目标 operation、Admin 五页接入及跨服务 HTTP 冒烟待完成 +> 领导验收范围:客户账户、客户应收应付、客户对账单、结算单应收、结算单应付 +> 原型依据:`docs/悦/货代SAAS平台后台/货代SaaS平台原型.html` + +> 目标 OpenAPI:`openapi/finance-phase1-five-pages-v1.yaml`;当前人工接口继续参考 `openapi/finance-f1-manual-v1.yaml`。 + +## 1. 一期范围结论 + +一期只交付原型中的以下 5 页,并实现页面结构、字段、筛选、详情和状态: + +1. 客户账户。 +2. 客户应收应付。 +3. 客户对账单。 +4. 结算单应收。 +5. 结算单应付。 + +明确不做:费用分析、利润表、钱包、预收、手动充值、自动扣款余额、汇率、外币、发票、月结、银行自动对账。未实现能力不得保留按钮、菜单、路由或模拟成功入口。 + +所有金额只使用 CNY。原型中的 USD、汇率、开票日期、利润审批、利润金额仅为历史原型字段,一期删除。 + +## 2. 客户账户冲突处理(已确认) + +原型客户账户是“预充值余额 + 冻结 + 可用余额 + 手动充值”的钱包模型,与此前确认的“不做钱包/预收”冲突。产品已确认按以下方案处理,不再等待额外产品结论: + +- 页面保留“客户账户”名称和客户汇总/详情/流水结构,但定位为客户财务往来总览,不是资金账户。 +- 删除“手动充值”按钮、充值弹窗、充值方式和充值凭证号;不设计充值 API。 +- 不显示可消费额度,不允许业务系统按余额自动扣款。 +- 原型钱包指标改为财务派生指标:`未收余额`、`待出账金额`、`本期已收金额`;列表和详情使用相同口径。 +- 流水只来自费用来源纳入/排除、对账单生成/调整/作废、结算单生效和人工收付款登记,不允许直接修改余额。 + +派生公式: + +| 指标 | 公式 | +|---|---| +| 未收余额 | 生效应收结算单应收金额合计 - 已收金额合计 | +| 待出账金额 | `AVAILABLE + APPROVED + settlementIncluded=true` 且未进入有效对账单的应收费用来源金额合计 | +| 本期已收金额 | 查询期间内有效收款分配金额合计 | +| 应付余额 | 生效应付结算单应付金额合计 - 已付金额合计 | + +## 3. 五页验收矩阵 + +| 页面 | 原型结构和筛选 | 一期字段 | 一期操作 | 状态与验收 | +|---|---|---|---|---| +| 客户账户 | 汇总卡、客户列表、账户详情、流水详情;客户名称/编号、账户状态、余额条件、流水类型和日期 | 客户基本信息、客户编号、联系人、电话、未收余额、待出账金额、本期已收金额、应付余额、账户状态、最后变动时间;流水号、时间、业务类型、关联单据、应收/应付变动、变动后未收余额、操作人、备注 | 详情、查询、重置、导出;**无充值、扣款或余额调整** | 客户主体正常/停用只影响访问和新业务;所有金额可由来源、单据和收付款逐笔追溯,禁止直接写余额 | +| 客户应收应付 | 工具栏、组合查询、选中汇总、费用列表、费用详情、审核进度、关联业务、操作记录 | 客户、费用单号、是否已生成对账单、参与结算、审核状态、应收/应付、入仓码/柜次号、业务状态、费用类型、金额、业务发生日、来源类型、来源版本 | 详情、审核通过、审核拒绝、编辑未审核人工项、参与结算/禁止结算、按截止时间或勾选生成;已发布来源不可删除,只能作废/调整 | `PENDING -> APPROVED/REJECTED`;只有 `APPROVED + INCLUDED + AVAILABLE + CNY` 可生成;同一来源版本不能重复进入有效单据 | +| 客户对账单 | 查询区、主表、账单明细、支付历史;审核状态、账单编号、客户、柜次、结算起止日、是否结清、创建日期、最后收款时间 | 对账单号、客户、期间、账期、CNY、总应收、已收、未收、最后收款时间、是否结清、备注、创建/更新人时间;明细保留入仓单/柜次、业务日、计费量、费用项、金额、来源版本 | 生成草稿、详情、导出、审核通过、审核拒绝、作废;不提供发票和汇率操作 | `DRAFT -> PENDING_REVIEW -> APPROVED/REJECTED/VOIDED`;审核通过后来源冻结,调整必须留 revision;支付历史只读展示有效收款 | +| 结算单应收 | 汇总卡、筛选、列表、详情、费用明细、收款记录;收款状态、客户、结算单号 | 结算单号、客户、CNY、应收、已收、余额、关联对账单、业务期间、生成日、到期日、收款状态、制单人、状态 | 从审核通过对账单生成、详情、部分收款、导出、作废/重开;不保留高级查询假入口 | `DRAFT -> ISSUED -> PARTIALLY_PAID -> PAID`,未收款可作废;出账后冻结,调整保留 revision;收款人工表单和图片沿用 F1 契约 | +| 结算单应付 | 汇总卡、供应商筛选、列表、详情、费用明细、付款记录 | 结算单号、供应商、费用类型、CNY、应付、已付、余额、来源期间、生成日、到期日、付款状态、制单人、审核人、状态 | 从审核通过且参与结算的应付来源生成、详情、审核、部分付款、导出、作废/重开;删除利润分析和利润审批按钮 | `DRAFT -> PENDING_REVIEW -> ISSUED -> PARTIALLY_PAID -> PAID`,拒绝/作废保留记录;付款为人工登记和凭证图片 | + +表格“操作”列必须位于第一列。原型中未形成真实闭环的“新增、生成账单、高级查询、快捷导出”等按钮,只有在对应接口和权限完成后才显示。 + +### 3.1 五页现状/目标差异 + +| 页面 | 当前仓库事实 | 一期目标 | 差异等级 | +|---|---|---|---| +| 客户账户 | 后端已新增 `/customer-accounts/{customerId}` 和固定读 DTO;无列表接口 | 派生财务往来汇总、详情和流水,无任何充值/余额写入口 | IMPLEMENTED(单客户查询);列表/筛选仍缺,Finance DTO 测试通过 | +| 客户应收应付 | `/fee-pool` 固定 DTO、Firstleg 事件/快照和 `fin_fee_source` 写入均已实现并验证 | 真实入库/柜子费用来源池,支持审核、参与结算、详情和生成对账单 | 查询与来源入池已实现;详情/页面操作仍是 P0/P1 | +| 客户对账单 | 后端已新增 `/statements` 和 `/bills/{billId}` 固定读 DTO 路径;独立生成/审核模型仍缺失 | 按截止时间或勾选合格来源生成,支持审核、明细、支付历史和 revision | IN_PROGRESS/P0 | +| 结算单应收 | 后端已新增 `/receivable-settlements` 固定读 DTO 路径;`fin_bill + fin_receipt` 已有人工状态能力 | 关联审核通过的客户对账单,按原型结构形成应收结算页 | IN_PROGRESS/P0 适配 | +| 结算单应付 | 后端已新增 `/payable-settlements` 固定读 DTO 路径并读取人工成本;付款单和完整供应商来源池仍缺失 | 从真实 PAYABLE 来源生成,审核后人工登记付款与凭证 | IN_PROGRESS/P0 | + +Finance 来源写入不再是阻断:内部事件入口、Inbox 幂等、Firstleg 入库/柜子快照、`fin_fee_source` 新版本写入/替代/作废、`PUBLISHED + amount>0` 入池校验、五页固定 DTO、完整幂等、File fail-closed、Client 脱敏和稳定错误码均已实现并通过测试。剩余主要阻断转为 11 个目标 operation、Admin 五页接入和测试环境跨服务冒烟。 + +## 4. 费用来源到结算全链路 + +```mermaid +flowchart LR + I["入库单费用项"] --> R["审核"] + C["柜子费用项"] --> R + R --> S["参与结算/禁止结算"] + S --> P["发布不可变来源版本"] + P --> X["Finance Inbox"] + X --> F["fin_fee_source"] + F --> A["客户应收应付费用池"] + A --> B["客户对账单"] + B --> AR["结算单应收"] + A --> AP["结算单应付"] + AR --> CR["人工收款与分配"] + AP --> CP["人工付款与凭证"] + CR --> CA["客户账户派生汇总/流水"] + CP --> CA +``` + +### 4.1 入库单费用项 + +现有 `fl_inbound_fee_detail` 已具备客户、入库单、方向、业务日、费用项快照、计费量、单价、金额、审核、发布和 `settlement_included` 字段;已有列表、新增、编辑、删除、审核通过和驳回接口。 + +仍缺: + +- 通用费用的“参与结算/禁止结算”接口和原因、操作人、版本控制。 +- 非 FBA 头程运费的统一发布动作;当前发布主要由 FBA 费用发送流程触发。 +- 费用调整/作废事件及旧版本替代规则的完整实现。 +- Finance 消费者和来源快照写入。 + +### 4.2 柜子费用项 + +当前工作树已新增柜子费用来源表/实体、Controller、审核/参与结算/作废服务、发布事件和内部快照代码;这些文件尚未形成通过测试的稳定基线。既有 Firstleg 柜次体积快照仍只服务 Finance 人工整柜成本分摊,不能替代新来源模型的审核和不可变发布链路。 + +一期至少需要业务侧模型: + +- `containerFeeId/version`、`containerId/containerNo`、费用方向、费用项目、业务日、CNY 金额。 +- 应付来源必须有供应商;应收来源必须有客户分摊结果。 +- 审核状态、参与结算状态、发布状态、作废/调整原因和操作审计。 +- 柜内跨客户时,业务侧按真实体积形成不可变客户分摊行;Finance 只接收客户级来源,不自行猜测客户归属。 + +建议新增 `fl_container_fee_detail + fl_container_fee_allocation`,或建立统一 `fl_business_fee_source`;这是技术选型,不改变上述产品字段和验收结果。 + +### 4.3 Finance 来源池 + +当前 `FinanceFeeSourceEventService` 已通过 `POST /internal/finance/v1/fee-source-events` 消费 Firstleg 事件,回查不可变快照,并在本地事务中写 `fin_inbox_event` 与 `fin_fee_source`。相同事件和内容返回原来源;同事件异内容冲突;调整写新版本并把旧 `AVAILABLE` 版本标为 `SUPERSEDED`;作废只标 `VOIDED`,不删除历史。现有人工账单入口仍允许 Admin 提交来源 ID/版本/金额,因此生成对账单/结算单时还必须改为只选择本地合格来源并由服务端取金额。 + +目标写入条件: + +```text +reviewStatus = APPROVED +publicationStatus = PUBLISHED +settlementIncluded = true +sourceStatus = ACTIVE +currency = CNY +amount > 0 +customer/supplier/business reference complete +``` + +Finance 必须通过 Inbox 幂等消费来源版本,或通过受保护的快照接口拉取后落库;禁止 Admin 直接构造来源金额。来源调整新增版本,已进入有效单据的旧版本不覆盖。 + +## 5. 目标接口矩阵 + +| 页面/链路 | 目标接口 | 当前状态 | +|---|---|---| +| 入库费用审核 | `POST /api/platform/v1/inbounds/{inboundId}/fee-details/{detailId}/approve|reject` | 已有 | +| 入库费用参与结算 | `PUT /api/platform/v1/inbounds/{inboundId}/fee-details/{detailId}/settlement-inclusion` | 缺失 | +| 入库费用发布 | `POST /api/platform/v1/inbounds/{inboundId}/fee-details/{detailId}/publish` | 通用入口缺失 | +| 柜子费用 | `/api/platform/v1/loading-plans/{loadingPlanId}/fee-details/**` | 模型、审核/调整/作废、事件、Relay 和快照已实现;Firstleg 相关 17/17 定向测试通过 | +| Finance 来源接入 | `POST /internal/finance/v1/fee-source-events` | 已实现并验证;来源专项 10/10、Finance 全量 35/35、Firstleg 相关 17/17 通过 | +| 客户账户 | `GET /api/platform/finance/v1/customer-accounts/{customerId}` | 已实现固定 DTO 单客户查询并通过测试;列表接口仍缺失 | +| 客户应收应付 | `GET /api/platform/finance/v1/fee-pool?customerId=&direction=` | 已实现固定 DTO 查询并通过测试;详情、审核和参与结算操作仍缺失 | +| 客户对账单 | `GET /api/platform/finance/v1/statements?customerId=`、`GET /api/platform/finance/v1/bills/{billId}` | 已实现固定 DTO 查询并通过测试;生成/审核操作仍缺失 | +| 结算单应收 | `GET /api/platform/finance/v1/receivable-settlements?customerId=` | 已实现固定 DTO 查询并通过测试;生成与页面动作仍缺失 | +| 应收人工收款 | `GET/POST /api/platform/finance/v1/receipts` | 已实现平台固定 DTO、多账单分配、合计校验、共用凭证和 File fail-closed;测试通过 | +| 结算单应付 | `GET /api/platform/finance/v1/payable-settlements?customerId=` | 已实现固定 DTO 查询并通过测试;应付生成、审核和付款仍缺失 | + +## 6. 最小数据模型 + +| 模型 | 必要字段 | 当前缺口 | +|---|---|---| +| `fin_fee_source` | 来源系统/类型/ID/版本/hash、方向、客户、供应商、订单/入库/柜次、业务日、费用项、数量/单价/金额、审核/参与结算快照、状态、占用单据 | 已有写入、类型、方向、柜次、费用编码、hash、完整 JSON 快照和版本状态;供应商顶层字段及对账单占用关系仍待后续模型落地 | +| `fin_reconciliation_statement` | 对账单号、客户、期间、账期、CNY 总额、状态、审核、版本、备注 | 缺失 | +| `fin_reconciliation_line` | 对账单、feeSourceId/version/hash、业务与计费快照、金额 | 缺失 | +| `fin_settlement` | AR/AP、客户/供应商、来源对账单、期间、到期日、总额、已收付、余额、状态、版本 | 当前 `fin_bill` 仅覆盖部分 AR | +| `fin_settlement_line` | 结算单、对账单/来源行、费用项和金额快照 | 当前账单行只覆盖部分 AR | +| `fin_payment`/分配 | 收/付款、对象、日期、方式、凭证、金额、分配、操作人 | 收款已有;付款缺失 | +| 账户视图 | 客户、未收、待出账、本期已收、应付、最后变动 | 应由上述表聚合,不新增可写余额表 | + +## 7. 专项优先级 + +### P0:不开工或不联调就无法闭环 + +1. 入库费用独立的参与结算/禁止结算和通用发布动作;柜子费用来源模型已完成,不再列为阻断。 +2. 客户对账单、应收结算单、应付结算单的数据模型、状态机和剩余 11 个 operation。 + +### P1:领导验收前必须完成 + +1. 5 页 Admin 菜单、路由、字段、筛选、详情、状态和权限。 +2. 删除费用分析、利润表、手动充值、利润审批、汇率/外币/发票及所有假入口。 +3. 来源金额与对账单/结算单金额逐层勾稽报告。 +4. 跨客户、跨供应商、重复来源、已占用来源、调整/作废和并发版本测试。 +5. Finance → Firstleg/File 真实 HTTP 冒烟和 5 页端到端验收。 + +## 8. 当前确认项 + +客户账户冲突已由产品确认:采用派生财务往来总览,不做钱包/预收/充值。当前没有遗留的核心产品冲突;剩余均为接口、数据模型和实现门禁。 diff --git a/财务管理/财务开工门禁与F0-F1任务清单.md b/财务管理/财务开工门禁与F0-F1任务清单.md index dc0c82d..9515438 100644 --- a/财务管理/财务开工门禁与F0-F1任务清单.md +++ b/财务管理/财务开工门禁与F0-F1任务清单.md @@ -7,9 +7,9 @@ ## 一、总控结论 -Finance 的服务拆分、独立数据库、基础表、权限、人工账单/收款/成本接口及 Admin/Client 基础页面已经落地。当前不是等待开工,而是按专项测试退回项修复固定 DTO、凭证/File 链路、体积分摊、幂等和客户可见状态。 +Finance 的服务拆分、独立数据库、基础表、权限、人工账单/收款/成本接口及 Admin/Client 基础页面已经落地。后端真实体积快照、归属、成本凭证、revision、双权限上传、费用来源事件/Relay/快照、固定 DTO、完整幂等、File fail-closed、客户收款 DTO 和稳定错误码已经完成并通过专项测试;当前继续收口 Admin 最终契约适配和五页剩余 operation。 -F0 的平台主体、首期仅 RMB/CNY、自定义账期、编号、费用冻结、无历史迁移、客户角色和首期简化审批已经确认。当前采用人工简化方案:账单、收款、成本均由平台财务填写表单并上传凭证;客户账户、发票、供应商在线应付、银行接口、复杂审批和月结首期不做。原表状态已归一为 25 项已确认、7 项暂缓。详见[产品确认版差异与剩余待确认](财务管理产品确认版差异与剩余待确认.md)。 +F0 的平台主体、首期仅 RMB/CNY、自定义账期、编号、费用冻结、无历史迁移、客户角色和首期简化审批已经确认。一期领导验收固定为 5 页;客户账户为派生财务往来总览,不做钱包/充值,费用分析和利润表不做。详见[财务一期五页产品与接口验收契约](财务一期五页产品与接口验收契约.md)。 ## 二、文档交付状态 @@ -54,9 +54,9 @@ G0 允许:完善 API/OpenAPI、数据库迁移草案、契约测试样例、 - 预收、多收、手续费、退款、反核销的处理方式冻结; - 至少准备一组真实或脱敏费用来源样本,能够完成费用→账单→应收→收款→核销回归。 -### G3/G4:F2-F4 门禁 +### G3/G4:一期应付与排除项门禁 -供应商应付、客户账户、发票、费用分析和利润必须分别满足产品确认表中的阶段门禁。没有供应商成本凭证、资金性质、税务主体、汇率和分摊口径时,只能保留预留模型和页面草案。 +结算单应付纳入一期,但必须有供应商和真实 PAYABLE 来源。客户账户只做派生查询。发票、费用分析、利润、汇率和月结不属于一期,禁止保留假入口。 ## 四、F0/F1 任务清单 @@ -82,12 +82,12 @@ G0 允许:完善 API/OpenAPI、数据库迁移草案、契约测试样例、 - 完善 F0/F1 数据字典、状态转移表和权限矩阵; - 设计 Finance Admin/Client 页面和空态、错误态、线下异议录入态; - 准备脱敏历史费用样本与迁移校验脚本设计; -- 按 OpenAPI 1.2.0 修复服务端 DTO、凭证链路、真实体积快照与归属、幂等和客户可见状态,并补齐契约测试。 +- 按 OpenAPI 1.2.0 完成剩余服务端 DTO、File fail-closed、幂等和客户可见 DTO,并让 Admin 移除人工体积、接入账期与 revision。 ### 本轮不再等待产品确认 -- F1 首期只做 CNY 人工账单、人工收款、人工成本和客户只读,产品口径已经冻结。 -- 客户账户、发票、供应商在线应付、汇率、月结和利润不属于本轮,不阻塞 F1 修复。 +- 一期只支持 CNY,验收 5 页及其真实费用来源、对账、应收/应付和人工收付款链路。 +- 客户账户是派生总览;发票、费用分析、利润、汇率、月结不属于本轮。 - 首发不迁移历史,不阻塞 F1 修复。 ## 六、总控放行结论 diff --git a/财务管理/财务服务边界设计.md b/财务管理/财务服务边界设计.md index 290be1e..b8ff9fa 100644 --- a/财务管理/财务服务边界设计.md +++ b/财务管理/财务服务边界设计.md @@ -1,5 +1,7 @@ # 财务服务边界设计 +> **范围说明**:本文只作为 Finance 独立服务/独立数据库的技术边界依据。文中的钱包、充值、发票、费用分析和利润属于长期模型,不属于一期;一期唯一产品范围以《财务一期五页产品与接口验收契约》为准。 + > 决策日期:2026-08-10 > 决策状态:技术已确认 > 决策结论:Finance 采用独立服务和独立数据库 diff --git a/财务管理/财务管理产品确认版差异与剩余待确认.md b/财务管理/财务管理产品确认版差异与剩余待确认.md index b929054..4e18fd8 100644 --- a/财务管理/财务管理产品确认版差异与剩余待确认.md +++ b/财务管理/财务管理产品确认版差异与剩余待确认.md @@ -4,17 +4,19 @@ > 复核日期:2026-08-10 > 用途:把产品回填内容转成可开发口径;不替代产品原始回传表。 +> **降级说明(2026-08-11)**:本文保留首次回填复核过程,不再作为一期范围入口。当前范围和冲突处理以《财务一期五页产品与接口验收契约》为准;原文中的“仍待确认”“F3 整体暂缓”“成本/利润统计”均不得覆盖已确认的五页范围。 + ## 一、回传表复核结论 回传表共 32 个事项。当前状态列实际为:16 项“已确认”、2 项“待确认”、14 项状态为空但填写了最终结论。因此,表内“填写说明”显示的 16 已确认 / 16 待确认不能直接作为放行依据;状态为空的 14 项需要产品补填“已确认”或“暂缓”。 其中,以下内容可直接纳入正式文档: -- 客户应付、客户账户、发票和外部接口在头程首期不做,线下处理;不得因此创建半成品财务流程。 +- 一期页面固定为客户账户、客户应收应付、客户对账单、结算单应收、结算单应付;客户账户只是派生财务往来总览,不做钱包/预收;发票和外部接口不做。 - 供应商付款为线下付款并上传凭证;单票成本直接归属单票,整柜清关、拖车等公共成本按柜内体积分摊。 - 首期账单只支持 RMB/CNY;每个客户维护自己的账期配置,默认 30 天。 - 收款全部由财务人工填写表单并上传收款图片;一笔 300 元可由财务分别填入 3 张 100 元账单,关联同一张凭证。 -- 首期只统计 CNY 账单,不做汇率、不做月结,只按账单统计。 +- 首期所有页面和单据只统计 CNY,不做汇率、不做月结;费用分析和利润表不建设。 - 客户财务权限按菜单分权限;与“客户子用户只读、平台财务执行写操作”的既有结论一致。 ## 二、已确认但需要写入契约的细节 @@ -26,7 +28,7 @@ | F1-04 | “人工填写表单,上传收款图” | 已足够支撑首期人工收款;仍需把表单字段写入接口契约 | 收款金额、到账日、方式、凭证号、凭证图片、登记人由平台财务人工填写 | | F1-05 | “客户给 300,3 个账单都是 100,直接 3 个单” | 已明确首期按账单逐笔分配;不再扩展预收、多收、手续费、退款 | 财务分别在 3 张账单填写 100 元,并关联同一张 300 元收款凭证 | | F2-02/F4-01 | “单票成本;整柜清关、拖车按体积分摊” | 已明确业务归属;只需在接口中固定人工录入和凭证字段 | 单票成本归属单票;整柜公共成本按柜内体积分摊;平台财务人工登记并上传凭证;收入按 CNY 账单统计 | -| F4-03 | “不做汇率和月结,只做账单,只统计 CNY” | 已解决首期范围;仅需在权限和报表验收中冻结为 CNY | 首期利润/分析只统计 CNY 账单,不做汇率、汇兑损益和月结快照 | +| F4-03 | “不做汇率和月结,只做账单,只统计 CNY” | 已解决首期范围;在五页接口和验收中冻结为 CNY | 五页单据和汇总只统计 CNY,不做汇率、汇兑损益、月结快照、费用分析或利润表 | ## 三、可以按“首期不做/暂缓”处理的事项 @@ -34,17 +36,17 @@ | 事项 | 暂缓口径 | |---|---| -| F2-01、F2-03、F2-04 | 当前不存在客户应付或供应商应付在线闭环,线下处理;不开放对应菜单和状态流转 | -| F3-01 至 F3-04 | 头程首期没有客户账户/钱包,线下处理;不建设余额、冻结、扣款、退款对象 | +| F2-01、F2-03、F2-04 | 供应商在线确认、自动付款等能力暂缓;一期仍须交付结算单应付页、真实 PAYABLE 来源和人工付款凭证链路 | +| F3-01 至 F3-04 | 一期建设客户账户派生总览页,但不建设钱包余额、充值、冻结、自动扣款或退款对象 | | F3-05 至 F3-07 | 发票和外部开票/银行接口首期线下处理;只保留未来扩展位 | -| F4-04 | 按菜单分权限;平台财务可见完整利润和成本,客户侧不开放供应商成本和内部利润 | +| F4-04 | 费用分析和利润表一期不进菜单/路由;后续重新立项时再定义权限 | ## 四、当前放行判断 - F0 技术方案和客户主体/成员边界已经冻结,可以继续做 Finance 服务骨架、数据库迁移草案、事件契约和权限矩阵设计。 - F1 客户应收的核心产品口径已补齐;账期日期算法、账单单据粒度和收款字段可按本文默认值直接写入契约。 - F2/F4 的产品口径已补齐;开发前只需把人工成本/收款表单字段、凭证关联和体积分摊公式写入接口与验收契约。 -- F3 可整体标记暂缓,不阻断 F0/F1。 +- F3 钱包/充值/发票能力可暂缓;客户账户派生总览属于一期五页,不得整体暂缓。 因此,本轮已没有新的核心产品问题;剩余工作是把确认版 XLSX 的状态归一化,并在接口/验收契约中补充账期日期算法、收款表单字段、收款凭证与账单分配关系、成本来源字段和体积分摊公式。 @@ -59,4 +61,4 @@ 5. **客户侧**:客户只读查看账单、收款记录和余额;金额有误时线下反馈,由平台财务修改或作废重开。 6. **凭证与审计**:所有人工表单保留填写人、填写时间、修改原因、凭证图片和原始版本;已出账记录不覆盖,修改使用调整或作废重开。 -按此方案,开发重点是三个简单表单(账单、收款、成本)和三类查询(客户账单、收款记录、成本/利润统计),不是建设完整总账、钱包或自动结算系统。 +按此方案,开发重点是五页查询与详情、账单/收付款人工表单及真实费用来源链路,不建设成本/利润分析、完整总账、钱包或自动结算系统。 diff --git a/财务管理/财务管理产品确认表.md b/财务管理/财务管理产品确认表.md index f99edf9..1011007 100644 --- a/财务管理/财务管理产品确认表.md +++ b/财务管理/财务管理产品确认表.md @@ -1,14 +1,16 @@ # 财务管理产品确认表 -> 版本:V1.0 +> 版本:V1.1 > 创建日期:2026-08-10 -> 状态:首期人工简化财务口径已确认;后续 F2-F4 待确认项不阻塞本轮 F1 修复 -> 适用范围:客户账户、客户应收应付、客户对账单、结算单应收/应付、收付款核销、发票、费用分析和利润 +> 状态:一期 5 页范围已确认 +> 适用范围:客户账户、客户应收应付、客户对账单、结算单应收、结算单应付 > 技术方案说明:服务边界、跨服务访问、事件幂等、金额存储、状态留痕和正式模块发布方式已按当前仓库架构直接确认,不再要求产品确认;以 XLSX 的“技术结论”工作表为准。 正式技术决策见:[财务服务边界设计](财务服务边界设计.md)。 +一期领导验收及接口事实以[财务一期五页产品与接口验收契约](财务一期五页产品与接口验收契约.md)为准;本文后续 F2-F4 历史问题不自动进入一期。 + 产品回传版 XLSX 的逐项复核和剩余确认门禁见:[财务管理产品确认版差异与剩余待确认](财务管理产品确认版差异与剩余待确认.md)。 ## 一、确认说明 @@ -42,19 +44,22 @@ | 实施复杂度 | 首期全部由平台财务人工填写账单、收款和成本表单并上传凭证;不建设自动银行对账、自动核销、复杂审批、钱包、发票或总账 | | 审批复杂度 | 首期不做强制双人复核和金额分级审批;保留独立权限、操作原因和完整审计 | | 成员停用 | 客户子用户停用只撤销访问权限;客户账单、应收、余额等仍归客户企业,不触发财务单据转交;历史保留操作人快照 | +| 一期页面 | 只做客户账户、客户应收应付、客户对账单、结算单应收、结算单应付;费用分析和利润表不进菜单/路由 | +| 客户账户性质 | 财务往来派生总览,不是钱包;删除手动充值、预收、可消费余额和自动扣款入口 | +| 费用来源链路 | 入库单费用和柜子费用必须审核、标记参与结算并发布到 Finance 来源池后,才能生成对账单/结算单 | ## 二、全量范围确认 | 编号 | 范围 | 建议结论 | 最终结论 | 状态 | |---|---|---|---|---| -| S1 | 客户账户 | 做预充值、冻结、解冻、扣款、退款和账户流水;账户余额与应收核销分开,任何抵扣都产生核销记录 | 待填写 | 待确认 | -| S2 | 客户应收 | 做客户费用池、对账单、应收单、到期/逾期、收款、部分核销、结清、反核销 | 待填写 | 待确认 | -| S3 | 客户应付 | 仅在确有平台向客户付款的业务场景时启用;否则不开放菜单和数据方向 | 待填写 | 待确认 | +| S1 | 客户账户 | 做预充值、冻结、解冻、扣款、退款和账户流水;账户余额与应收核销分开,任何抵扣都产生核销记录 | 只做账单、收款和应收应付来源派生的财务往来总览;不做钱包、预收、充值和自动扣款 | 已确认 | +| S2 | 客户应收 | 做客户费用池、对账单、应收单、到期/逾期、收款、部分核销、结清、反核销 | 纳入一期 5 页;平台财务操作,客户只读 | 已确认 | +| S3 | 客户应付 | 仅在确有平台向客户付款的业务场景时启用;否则不开放菜单和数据方向 | 客户应收应付页按来源方向展示;没有真实 PAYABLE 来源时为空,不造模拟数据 | 已确认 | | S4 | 客户对账单 | 按客户 + 币种 + 账期生成,支持平台内部复核、发送、关闭和财务重开;客户只读查看 | 平台财务出账,客户只读,异议线下反馈 | 已确认 | -| S5 | 供应商应付 | 做供应商成本池、供应商对账、应付单、付款、付款核销和反核销 | 待填写 | 待确认 | -| S6 | 发票 | 做发票申请、开票登记、票号/文件关联、红冲和作废;不直接假定外部开票平台 | 待填写 | 待确认 | -| S7 | 费用分析 | 按费用项目、客户、供应商、期间统计发生额、已结算额和余额,支持钻取来源单据 | 待填写 | 待确认 | -| S8 | 利润 | 支持客户、货柜、单票、月份维度;收入、成本、分摊、汇率和利润率必须可追溯 | 待填写 | 待确认 | +| S5 | 供应商应付 | 做供应商成本池、供应商对账、应付单、付款、付款核销和反核销 | 结算单应付纳入一期;只使用具有供应商和真实来源的 CNY 应付费用,付款人工登记 | 已确认 | +| S6 | 发票 | 做发票申请、开票登记、票号/文件关联、红冲和作废;不直接假定外部开票平台 | 一期不做,不设计菜单、路由或按钮 | 已确认 | +| S7 | 费用分析 | 按费用项目、客户、供应商、期间统计发生额、已结算额和余额,支持钻取来源单据 | 一期不做,不进菜单/路由 | 已确认 | +| S8 | 利润 | 支持客户、货柜、单票、月份维度;收入、成本、分摊、汇率和利润率必须可追溯 | 一期不做,不进菜单/路由;应付页删除利润字段和利润审批 | 已确认 | ## 三、F0 财务底座确认 @@ -91,37 +96,37 @@ | 编号 | 需要确认的事项 | 建议结论 | 最终结论 | 状态 | |---|---|---|---|---| | F1-01 | 账单聚合维度 | 客户 + 币种 + 账期;不同币种不合并 | 首期只支持 RMB/CNY;支持多种费用项;多笔订单分别出账 | 已确认 | -| F1-02 | 生成方式 | 支持按筛选结果人工生成;后续可增加定时生成,但不得重复出账 | 待填写 | 待确认 | +| F1-02 | 生成方式 | 支持按筛选结果人工生成;后续可增加定时生成,但不得重复出账 | 按截止时间或财务勾选合格来源生成;同一来源版本不得重复进入有效对账单 | 已确认 | | F1-03 | 账单状态 | DRAFT → ISSUED → CLOSED;客户只读,异议线下反馈后由平台财务调整或作废重开 | 客户不在线确认/争议,平台财务手工处理 | 已确认 | | F1-04 | 应收状态 | OPEN → PARTIALLY_SETTLED → SETTLED;到期后增加 OVERDUE 标记,不改变余额 | 客户账期由客户配置,默认 30 天;到期日按账单日期+客户账期计算 | 已确认 | | F1-05 | 客户确认 | 首期不做客户在线确认/争议;客户只读查看,异议线下反馈 | 客户不能操作,由平台财务处理 | 已确认 | | F1-06 | 收款登记 | 首版人工登记到账日、金额、币种、方式、凭证号、附件和登记人 | 平台财务人工填写收款表单并上传收款凭证图片 | 已确认 | | F1-07 | 核销关系 | 支持一笔收款核销多张应收、一张应收被多笔收款核销;禁止跨币种和超额 | 一笔客户付款可分配到多张账单;财务分别填写各账单对应金额并关联同一收款凭证;首期不处理预收、多收、手续费、退款 | 已确认 | -| F1-08 | 反核销/作废 | 独立高风险权限,填写原因并保留原记录;不物理删除 | 待填写 | 待确认 | +| F1-08 | 反核销/作废 | 独立高风险权限,填写原因并保留原记录;不物理删除 | 首期不做复杂反核销;未收单据可作废,已出账调整/作废重开必须留原因、版本和原记录 | 已确认 | | F1-09 | 出账后调整 | 已出账冻结;平台财务通过调整/作废重开形成新版本,保留原金额和原因 | 仅平台财务手工调整,不允许客户修改 | 已确认 | -| F1-10 | 预收/多收/手续费/退款 | 单独建模为预收、其他应付款或退款,不直接冲减应收金额 | 待填写 | 待确认 | +| F1-10 | 预收/多收/手续费/退款 | 单独建模为预收、其他应付款或退款,不直接冲减应收金额 | 一期全部不做;不设计钱包或预收入口 | 已确认 | ## 五、F2 供应商应付与结算确认 | 编号 | 需要确认的事项 | 建议结论 | 最终结论 | 状态 | |---|---|---|---|---| -| F2-01 | 成本来源 | 只有具备供应商、付款主体、成本凭证、币种金额和成本归属的来源才能进入应付池 | 待填写 | 待确认 | -| F2-02 | 成本确认节点 | 以供应商账单/业务完成/内部审核中确定一个正式节点;确认后冻结成本快照 | 待填写 | 待确认 | -| F2-03 | 供应商账单 | DRAFT → INTERNAL_REVIEW → SENT → CONFIRMED → CLOSED;差异走争议/补差/冲销 | 待填写 | 待确认 | -| F2-04 | 应付状态 | OPEN → PARTIALLY_PAID → PAID;到期增加 OVERDUE 标记 | 待填写 | 待确认 | -| F2-05 | 付款与核销 | 付款单独登记,支持多对多付款核销;禁止跨币种和超额 | 待填写 | 待确认 | -| F2-06 | 审批 | 应付确认和付款前复核使用独立权限;是否需要金额分级审批待确认 | 待填写 | 待确认 | -| F2-07 | 预付/押金/代付/退款 | 不混入普通应付余额,单独建模并保留关联单据 | 待填写 | 待确认 | -| F2-08 | 当前人工 PAYABLE 明细 | 仅作为待补充来源,不得直接视为供应商应付闭环 | 待填写 | 待确认 | +| F2-01 | 成本来源 | 只有具备供应商、付款主体、成本凭证、币种金额和成本归属的来源才能进入应付池 | 一期只接具备供应商、真实业务来源、审核、参与结算和 CNY 金额的 PAYABLE 来源 | 已确认 | +| F2-02 | 成本确认节点 | 以供应商账单/业务完成/内部审核中确定一个正式节点;确认后冻结成本快照 | 费用审核通过、参与结算并发布后进入 Finance;来源版本冻结 | 已确认 | +| F2-03 | 供应商账单 | DRAFT → INTERNAL_REVIEW → SENT → CONFIRMED → CLOSED;差异走争议/补差/冲销 | 一期不单独做供应商对账页,直接在结算单应付中生成、审核和付款 | 已确认 | +| F2-04 | 应付状态 | OPEN → PARTIALLY_PAID → PAID;到期增加 OVERDUE 标记 | DRAFT → PENDING_REVIEW → ISSUED → PARTIALLY_PAID → PAID;可拒绝/作废并留痕 | 已确认 | +| F2-05 | 付款与核销 | 付款单独登记,支持多对多付款核销;禁止跨币种和超额 | 平台财务人工登记付款表单和凭证;不得跨供应商或超额 | 已确认 | +| F2-06 | 审批 | 应付确认和付款前复核使用独立权限;是否需要金额分级审批待确认 | 使用独立审核权限;一期不做金额分级或强制双人 | 已确认 | +| F2-07 | 预付/押金/代付/退款 | 不混入普通应付余额,单独建模并保留关联单据 | 一期不做 | 已确认 | +| F2-08 | 当前人工 PAYABLE 明细 | 仅作为待补充来源,不得直接视为供应商应付闭环 | 没有供应商、审核和发布事实的明细不得进入结算单应付 | 已确认 | ## 六、F3 客户账户与发票确认 | 编号 | 需要确认的事项 | 建议结论 | 最终结论 | 状态 | |---|---|---|---|---| -| F3-01 | 账户余额性质 | 明确是客户预付款余额还是平台内部授信;充值必须有到账凭证,余额变更原子落流水 | 待填写 | 待确认 | -| F3-02 | 账户与应收关系 | 账户扣款可以核销应收,但不得直接修改应收原金额;余额、扣款、核销三条记录均保留 | 待填写 | 待确认 | -| F3-03 | 冻结与解冻 | 明确冻结触发、解冻人、原因、部分冻结和失败补偿 | 待填写 | 待确认 | -| F3-04 | 退款 | 退款必须关联原充值/收款,记录审批、到账方式、退款凭证和余额恢复规则 | 待填写 | 待确认 | +| F3-01 | 账户余额性质 | 明确是客户预付款余额还是平台内部授信;充值必须有到账凭证,余额变更原子落流水 | 不是资金余额;客户账户页只展示账单、收款和费用来源派生指标 | 已确认 | +| F3-02 | 账户与应收关系 | 账户扣款可以核销应收,但不得直接修改应收原金额;余额、扣款、核销三条记录均保留 | 不做账户扣款;收款只通过结算单分配更新未收余额 | 已确认 | +| F3-03 | 冻结与解冻 | 明确冻结触发、解冻人、原因、部分冻结和失败补偿 | 不做钱包冻结/解冻;待出账金额由合格未出账来源派生 | 已确认 | +| F3-04 | 退款 | 退款必须关联原充值/收款,记录审批、到账方式、退款凭证和余额恢复规则 | 一期不做充值和退款 | 已确认 | | F3-05 | 发票主体 | 明确开票主体、税率、含税/未税、发票类型和客户抬头校验 | 待填写 | 待确认 | | F3-06 | 开票流程 | 申请 → 审核 → 开票登记 → 票据/文件关联;红冲、作废不可删除原票 | 待填写 | 待确认 | | F3-07 | 外部平台 | 首版可人工登记,外部开票平台和银行流水接口另定接入契约 | 待填写 | 待确认 | @@ -130,13 +135,13 @@ | 编号 | 需要确认的事项 | 建议结论 | 最终结论 | 状态 | |---|---|---|---|---| -| F4-01 | 收入口径 | 按已确认/已发布应收还是已收款确认收入,二者报表分开显示 | 首期只统计 CNY 账单,不做汇率和月结,只按账单统计 | 已确认 | -| F4-02 | 成本口径 | 按已确认应付还是已付款确认成本,二者报表分开显示 | 单票成本直接归属单票;整柜公共成本进入柜次池 | 已确认 | -| F4-03 | 分摊维度 | 支持客户、订单、单票、柜次、费用项目;明确公共成本分摊算法 | 单票按单票真实成本;整柜清关、拖车等公共成本按体积分摊 | 已确认 | -| F4-04 | 汇率 | 明确交易日、账单日或月末汇率,以及本位币和汇兑损益 | 待填写 | 待确认 | -| F4-05 | 期间锁定 | 月结后报表冻结;调整通过新期间补差/冲销反映 | 首期不做月结;只按账单统计,暂不生成月结快照 | 已确认 | -| F4-06 | 利润维度 | 客户、货柜、单票、月份均支持;每个结果可钻取到收入、成本、分摊和原始单据 | 待填写 | 待确认 | -| F4-07 | 导出与权限 | 导出按财务数据权限控制,客户不可见供应商成本和内部利润 | 待填写 | 待确认 | +| F4-01 | 收入口径 | 按已确认/已发布应收还是已收款确认收入,二者报表分开显示 | 费用分析/利润表一期不做 | 已确认 | +| F4-02 | 成本口径 | 按已确认应付还是已付款确认成本,二者报表分开显示 | 费用分析/利润表一期不做;来源成本仍用于应付结算 | 已确认 | +| F4-03 | 分摊维度 | 支持客户、订单、单票、柜次、费用项目;明确公共成本分摊算法 | 一期只为来源和结算保留真实分摊,不开发利润分析页面 | 已确认 | +| F4-04 | 汇率 | 明确交易日、账单日或月末汇率,以及本位币和汇兑损益 | 一期只支持 CNY,不做汇率 | 已确认 | +| F4-05 | 期间锁定 | 月结后报表冻结;调整通过新期间补差/冲销反映 | 一期不做月结 | 已确认 | +| F4-06 | 利润维度 | 客户、货柜、单票、月份均支持;每个结果可钻取到收入、成本、分摊和原始单据 | 一期不做利润表 | 已确认 | +| F4-07 | 导出与权限 | 导出按财务数据权限控制,客户不可见供应商成本和内部利润 | 一期无利润菜单、路由或权限 | 已确认 | ## 八、全量验收主链路 diff --git a/财务管理/财务领域模型与数据字典.md b/财务管理/财务领域模型与数据字典.md index a60a0c8..9233ba7 100644 --- a/财务管理/财务领域模型与数据字典.md +++ b/财务管理/财务领域模型与数据字典.md @@ -1,5 +1,7 @@ # 财务领域模型与数据字典(F0/F1 首版) +> **范围说明**:本文包含长期数据模型候选。钱包、充值、发票、费用分析和利润表不属于一期,不得据此建菜单、路由或接口;一期数据模型以《财务一期五页产品与接口验收契约》为准。 + > 版本:V0.1(开发前设计稿) > 日期:2026-08-10 > 范围:F0 财务底座、F1 客户应收与对账;F2-F4 预留 diff --git a/财务管理/财务领域模型与跨服务契约.md b/财务管理/财务领域模型与跨服务契约.md index ae6cf3c..74ee769 100644 --- a/财务管理/财务领域模型与跨服务契约.md +++ b/财务管理/财务领域模型与跨服务契约.md @@ -1,5 +1,7 @@ # 财务领域模型与跨服务契约(历史合并稿) +> **历史文档,非一期实施依据**:本合并稿已被《财务领域模型与数据字典》《Firstleg-Finance 跨服务契约》和《财务一期五页产品与接口验收契约》替代。钱包、充值、发票、费用分析和利润模型不属于一期。 + > 文档状态:已被拆分替代,仅保留历史设计过程,不作为后续实施依据 > 替代文档:[财务领域模型与数据字典](财务领域模型与数据字典.md)、[Firstleg-Finance 跨服务契约](Firstleg-Finance跨服务契约.md) > 原版本:F0-V1.0(历史稿)