diff --git a/AGENTS.md b/AGENTS.md index 896e86841..6653f9074 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -99,7 +99,7 @@ - 跨服务修改先检查调用方 `client/`、被调用方 Controller/DTO、`src/main/resources/openapi/` 和双方契约测试;禁止共享领域 Entity。 - 修改 Java 方法、构造器、Controller、Client、DTO 或 Mapper 参数签名时,先用 `rg` 定向查找符号及旧参数形态,并同步更新 `src/main/`、`src/test/`、Mock、Fixture、契约测试和脚本中的全部调用点。新增可选参数也必须补齐旧测试调用,不能因生产调用已更新就视为完成。 -- 所有生产 MyBatis Mapper 接口必须显式标记 `@Mapper`;每个持久化服务只允许在启动类使用一次服务根包 `@MapperScan(..., annotationClass = Mapper.class)`,不得新增子域包白名单或分散的 `*MapperConfiguration`。新增或移动 Mapper 后先运行 `node scripts/mapper-scan-contract.mjs`,全量后端回归也会在 Maven 前执行该门禁。 +- 所有生产 MyBatis Mapper 接口必须显式标记 `@Mapper`;每个持久化服务只允许在启动类使用一次服务根包 `@MapperScan(..., annotationClass = Mapper.class)`,不得新增子域包白名单或分散的 `*MapperConfiguration`。新增、移动 Mapper 或修改扫描配置后运行 `node scripts/mapper-scan-contract.mjs`;全量后端回归已在 Maven 前执行该门禁时复用结果,输入未变不重复执行。 - 平台后台存在正式列表页时,`*:list` 同时授权列表、详情及只读辅助接口;`create`、`edit`、`delete`、`status`、`approve`、`review`、`export`、`upload`、`complete` 等操作保持独立。当前未上线,不兼容旧独立 `*:view`,Controller 不得使用 `hasAnyAuthority(list, view)`,OpenAPI 和角色初始化只保留 `*:list`。 - 供应商查询接口固定只使用 `platform:supplier:list`;IAM 中 `platform:supplier:list` 是正式菜单资源,`platform:supplier:view` 必须停用/移除,不得重新提升或兼容。 - 权限语义变更必须同步 Controller `@PreAuthorize`、OpenAPI、模块权限 SQL、IAM 菜单资源、统一增量脚本和契约测试。并行模块任务不得覆盖菜单与权限总控已写入共享 SQL 或权限契约的改动。 @@ -107,7 +107,7 @@ - 后端同样遵守根指引中的“开发数据和上传文件可丢弃”边界:不得为现有开发库孤儿文件、旧文件归属或历史部分失败新增删除补偿接口、兼容复用、回填或清理链路;只修复当前正式上传与持久化路径,既有开发文件由人工重新上传。 - 通用配置在 `config/nacos/{local,cn}/`;服务日志、上传文件、`target/`、`reports/` 不属于源码。 - Java 签名变更的最低编译门禁是 `mvn -pl -am test-compile`,它必须在部署前完成并覆盖测试源码;有行为变化时继续运行 `mvn -pl -am test`。`-DskipTests` 可跳过测试执行但仍须保留测试编译,`-Dmaven.test.skip=true` 会跳过测试编译,不得作为签名变更的交付验证。全量验证仍使用 `mvn -DskipTests validate`、`mvn test`;不要在本地启动 Java/Nacos/数据库,除非用户明确要求。 -- `AGENTS.md` 索引维护完成后运行 `./scripts/agents-index-contract.ps1`;脚本只读源码目录和指定指引,验证根快速表唯一性、部署单元、Client active 路由、海外仓业务范围漂移、仓库主档唯一权威、订舱关联候选与柜量语义、整柜出仓与船节点单调性、海外 Gateway 覆盖、PDA 运行配置/独立更新/语言预留、人工分段验收链、Lastleg 海外安全投影、同步 Trace、模块覆盖、关键入口与排除目录,不访问 HTTP、数据库或部署环境。 +- 同一批 `AGENTS.md` 索引修改完成后从后端根统一运行一次 `./scripts/agents-index-contract.ps1`;脚本只读源码目录和指定指引,不访问 HTTP、数据库或部署环境。 - Mapper 注册静态合同为 `scripts/mapper-scan-contract.mjs`、`mapper-scan-contract.test.mjs`:自动发现聚合模块、生产 `*Mapper` 接口、服务启动类和分散扫描配置,不启动 Spring、不连接数据库;遗漏注解、扫描根漂移或重新引入子域扫描时直接失败并输出类与文件路径。 - 单服务镜像构建/部署从 `Jenkinsfile.service`、`deploy/docker/Dockerfile`、`deploy/scripts/deploy-built-service.sh` 进入;新镜像启动失败或健康检查失败时只允许回滚到同服务先前镜像,并保持任务失败,离线合同见 `scripts/deploy-built-service-rollback.test.mjs`。实际部署仍须当前对话明确授权。 @@ -120,7 +120,7 @@ - Admin 总入口默认按模块列表覆盖平台全链路;需要定位单个业务时使用 `--modules `,模块名称和静态合同先查 `scripts/admin-test-env-modules/README.md`。 - 订舱与拖车跨服务完整状态链由 Admin 注册模块 `service-center-flow` 验证,必须复用本轮已放舱订舱并由总入口展开依赖,不得直接执行模块文件或借历史数据。 - 尾程公网控制由 `phase4-tail-order-controls` 在海外 PDA 前验证整柜接柜仓变更与恢复、单/批目的仓变更、单/批合托标记、CSV 导出、订单费用、暂扣/解除、八个人工节点和逐票人工计划;目的港码头拖车由 `phase5-terminal-drayage` 在卸柜后验证选车队、预约提柜、实际提柜和实际还柜,`phase4-carrier-nodes` 只读核验最终船节点终态。 -- 这些脚本会创建、更新或最终停用测试数据,必须在当前对话明确授权测试环境验收后执行,并保存 JSON 报告;未授权不得请求 Gateway。 +- 这些脚本会创建、更新或最终停用测试数据;指定测试环境和服务后,本轮非破坏性写入复用一次授权,删除、重置、SQL、部署或发布仍逐项授权,并保存 JSON 报告;未授权不得请求 Gateway。 - 后端 Maven、前端 `pnpm test/typecheck`、PDA `node --test` 和数据库 Dry-run 都是本地质量检查,不属于全量 HTTP 验收。 - Java 模块、OpenAPI 覆盖、JaCoCo 与 SpotBugs 聚合摘要使用 `node scripts/java-quality-summary.mjs`;默认仅报告存量缺口,是否阻断由显式 `--require-*` 参数决定,输出位于 `target/` 时不得作为业务事实或提交物。 - HTTP 全量通过后,才进入 Admin/Client 真实页面调用、浏览器样式和 PDA 页面/设备验收;自动化 HTTP 通过不代表视觉验收完成。 diff --git a/deploy/sql/README.md b/deploy/sql/README.md index aadb21f39..e848b4494 100644 --- a/deploy/sql/README.md +++ b/deploy/sql/README.md @@ -77,6 +77,8 @@ 完整操作请先读 [数据库迁移操作手册](数据库迁移操作手册.md)。离线入口为 `scripts/db-migrations.mjs`: +编号统一采用每个 schema 独立递增的 `V1、V2、V3……`,有 SQL 变更才由工具新增,成功执行后不改内容和编号。功能分支从最新 `main` 创建,进入测试前同步 main 并解决草稿编号冲突。生产发布另记 `release-1、release-2……`;该发布编号及分支门禁尚待流水线实现,完整流程见操作手册“编号与功能分支规则”。 + ```powershell # 自动取得该 schema 的下一个整数序号并创建草稿;不连接数据库 node deploy/sql/scripts/db-migrations.mjs new --region CN --schema baiyue_auth --description add_login_audit diff --git a/deploy/sql/数据库迁移操作手册.md b/deploy/sql/数据库迁移操作手册.md index 99b306ee9..bb31bca76 100644 --- a/deploy/sql/数据库迁移操作手册.md +++ b/deploy/sql/数据库迁移操作手册.md @@ -11,6 +11,22 @@ 当前测试分支只完成了版本迁移的离线工具和目录骨架。没有冻结基线、没有具体业务变更时,不要复制初始化 SQL 生成 `V1`,也不要连接测试或生产数据库试跑。 +## 编号与功能分支规则(2026-09-22 确认) + +SQL 编号统一保持 **`V1、V2、V3、V4、V5……`**,不使用时间戳。每个数据库独立计数,由工具生成下一个整数;数字变长不需要重置或重新编号。 + +- 有数据库逻辑变更才新增迁移;普通提交、重复测试和重复部署不增加 SQL 编号。 +- 同一未冻结、未在保留历史环境执行的变更可以反复编辑;成功执行后不改内容、不改编号,修复新增迁移。失败候选的受控修订仍按方案第 13.2.1 节处理。 +- 功能分支从最新 `main` 创建;进入共享测试前先拉取并合并最新 `main`,解决代码和同库编号冲突。只允许调整尚未冻结、未在保留历史环境执行的草稿。 +- 两人并行开发,共享测试、合并和发布依次进行。前一功能完成发布或处理失败后,下一功能同步 main 再进入测试;若前一功能留下未合并 SQL 或未决失败,先处理测试起点,不能直接继续验收。 +- 测试后 main 又变化,必须重新同步和验证。合并 main 后构建正式候选并再次自动验证,生产使用这份正式候选,不重新构建或修改 SQL。 + +**生产发布版本另外记为 `release-1、release-2……`,不与 SQL 编号绑定。** 例如 `release-12` 中 Auth 是 `V3`、Order 是 `V7`;只有代码变化也可以发布 `release-13`,SQL 编号保持不变。 + +目标流程由 Jenkins 为新的生产候选自动分配发布编号;成功后更新当前生产版本并记录 Git 标签、完整提交、镜像 digest 和 SQL 清单。失败编号保留且不复用,同一制品重试沿用原编号;修改制品后分配新编号并重新验证。main 是已合并代码,生产实际版本以成功发布记录为准;发布失败时还需核对各库、各服务的实际状态,不能假定全部未变。 + +当前工具只依据当前分支的迁移目录生成整数编号;跨分支冲突预检、main 同步门禁、`release-N` 分配和标签归档尚待接入,不因写入本手册就视为已自动执行。 + ## 日常开发:新建和修改迁移 以下命令均从 `baiyue-firstleg` 根目录执行,PowerShell 和 Bash 都可以使用 Node 命令。 @@ -136,6 +152,7 @@ node deploy/sql/scripts/db-migrations.mjs run ` - 生产或测试数据库基线登记; - Flyway 实际执行和历史表校验; - Jenkins 的数据库阶段、环境锁和构建/部署解耦; +- 功能分支同步检查、合并后候选验证、生产 `release-N` 自动编号及发布标签归档; - 真实测试环境部署与生产晋级。 因此现在可以开始编写和评审迁移文件,但不能把离线检查通过当作测试或生产发布通过。 diff --git a/finance/AGENTS.md b/finance/AGENTS.md index 87820bedc..f1dad374a 100644 --- a/finance/AGENTS.md +++ b/finance/AGENTS.md @@ -3,7 +3,7 @@ ## 模块定位 - 本模块是独立财务服务,拥有客户结算条款、财务费用来源、CNY 账单与明细、客户资金账户及充值申请、应收款月度统计、客户对账确认、收款及分配、成本及分摊、账单修订、单据编号、幂等记录和财务审计状态。 -- 产品范围先读 `../../docs/财务管理/财务管理产品确认表.md` 与 `财务核心五页产品与接口验收契约.md`,跨服务契约再读 `Firstleg-Finance跨服务契约.md`;专题 README、证据矩阵和测试环境报告只用于状态或验收问题,不作为普通开发首读。 +- 产品范围先读 `../../docs/财务管理/财务管理产品确认表.md` 与 `财务核心五页产品与接口验收契约.md`,跨服务契约再读 `Firstleg-Finance跨服务契约.md`;只有海外仓费用阶段 0A—0E 的设计或实现任务再读取 `../../docs/财务管理/海外仓费用统一接入阶段0技术契约.md`,并明确区分其目标 V2 协议与当前 V1 代码事实。专题 README、证据矩阵和测试环境报告只用于状态或验收问题,不作为普通开发首读。 - Firstleg 拥有入库/柜次业务费用和履约事实,File 拥有凭证文件;Finance 通过内部接口读取或消费快照,不跨库查询,也不复制上游业务状态机。 ## 快速查询 diff --git a/lastleg-service/docs/business-index.md b/lastleg-service/docs/business-index.md index a962c5162..7ea635ea1 100644 --- a/lastleg-service/docs/business-index.md +++ b/lastleg-service/docs/business-index.md @@ -58,7 +58,7 @@ | 异常订单与派送退回入仓 | `exceptionorder/controller/ExceptionOrderController.java`、`exceptionorder/service/ExceptionOrderService.java`、`exceptionorder/gateway/ExceptionEvidenceFileGateway.java`、`inventory/command/service/InventoryCommandService.java`;证据固定按客户、异常单 ID 和 `OVERSEAS_EXCEPTION_EVIDENCE` 校验。退回候选按原出库拣货事实冻结,不接受自由 SKU 或数量;异常按 `PENDING→PROCESSING→COMPLETED`(待处理可取消)、版本与幂等键流转。退回海外仓须把实收按原出库来源恢复到服务端校验的真实托盘/库位并写 `RETURN_STOCK_IN` 流水,库存恢复、异常完成与派送 `RETURNED` 在同一事务完成 | | 海外采购、客户下单、采购审核、客户/采购仓候选与操作流水 | `procurement/controller/`、`procurement/service/OverseasProcurementService.java`;客户入口按 `merchant:cross-border:procurement:list/create` 拆分且只取 Token 客户范围;OpenAPI `src/main/resources/openapi/overseas-business-v1.yaml` | | 海外业务资金指令与结果 | `wallet/WalletCommandOutboxService.java`、`WalletCommandCompletedConsumer.java`、`WalletResultHandler.java`;采购和换标通过 `WALLET_COMMAND_REQUESTED` 请求国内 Finance 冻结、结算或释放,并以 `WALLET_COMMAND_COMPLETED` 幂等回写业务资金状态;不得跨区同步调用 Finance 钱包或在 Lastleg 自建余额账本 | -| 海外业务费用来源 | `financefee/controller/PlatformOverseasFeeSourceController.java`、`financefee/service/OverseasFeeSourceService.java`、`OverseasFeeBusinessGuard.java`、`OverseasFeeOutboxService.java`、`OverseasFeeOutboxRelay.java`;海外入仓、采购、美国卡车、本土散板和海外仓服务费必须匹配当前客户的真实业务,入仓记录和入仓码按事实选填;审核通过、调整或作废后以版本化完整快照经 Outbox/Event Bridge 进入独立 Finance,OpenAPI `src/main/resources/openapi/overseas-finance-fees-v1.yaml` | +| 海外业务费用来源 | `financefee/controller/PlatformOverseasFeeSourceController.java`、`financefee/service/OverseasFeeSourceService.java`、`OverseasFeeBusinessGuard.java`、`OverseasFeeOutboxService.java`、`OverseasFeeOutboxRelay.java`;海外入仓、采购、美国卡车、本土散板和海外仓服务费必须匹配当前客户的真实业务,入仓记录和入仓码按事实选填;当前 V1 审核通过、调整或作废后以版本化完整快照经 Outbox/Event Bridge 进入独立 Finance,OpenAPI `src/main/resources/openapi/overseas-finance-fees-v1.yaml`。阶段 0A—0E 开发目标另读 `../../docs/财务管理/海外仓费用统一接入阶段0技术契约.md`,收费身份、候选版本、Finance 结果事件与资金计划 V2 尚须以实际代码落地验证,不能因文档存在宣称已实现 | | 美国/本土卡车订单 | `trucking/controller/PlatformUsTruckOrderController.java`、`MerchantUsTruckOrderController.java` 及同子域 Service/DTO;客户入口按 `merchant:cross-border:domestic-trucking:list/create/edit` 拆分 | | 本土散板订单 | `looseboard/controller/PlatformLocalLooseboardController.java`、`MerchantLocalLooseboardController.java` 及同子域 Service/DTO/Entity/Mapper;客户入口按 `merchant:cross-border:pallet-shipping:list/create/edit` 拆分;OpenAPI `src/main/resources/openapi/overseas-business-v1.yaml` | | 海外仓报价、月度汇率、车辆主数据 | 报价 `masterdata/quote/`、汇率 `masterdata/exchange/`;车辆入口 `masterdata/vehicle/adapter/in/web/PlatformVehicleController.java`,应用服务 `application/VehicleApplicationService.java`,持久化端口与适配器 `application/port/out/VehicleRepository.java`、`adapter/out/persistence/`;OpenAPI `src/main/resources/openapi/overseas-master-data-v1.yaml`,车辆档案不含派送履约历史 | diff --git a/scripts/AGENTS.md b/scripts/AGENTS.md index 2d27e0132..0fb99aaa6 100644 --- a/scripts/AGENTS.md +++ b/scripts/AGENTS.md @@ -48,4 +48,4 @@ - 修改 Admin 模块时运行直接对应的 `*contract.test.mjs`;修改造数链时同时运行 `admin-test-env-modules/it-test-wait-split-bulk-contract.test.mjs` 等受影响合同。 - 宣称脚本“全量补齐”前,必须运行 `scripts/` 根级离线合同与 `admin-test-env-modules/*contract.test.mjs`,并分别报告测试文件数、断言通过/失败数和精确失败文件;历史真实 HTTP 报告全绿不能覆盖当前离线合同失败。 - 新增、移动或删除脚本后运行 `node --test scripts/script-documentation-contract.test.mjs`,确认目录已登记且所有直接入口都有说明。 -- 修改任一 `AGENTS.md` 后从后端根运行 `./scripts/agents-index-contract.ps1`;真实 HTTP、数据库、Nacos、Jenkins 和部署验证仍须当前轮明确授权。 +- `AGENTS.md` 修改统一遵循 `../AGENTS.md` 的索引维护门禁,复用同一批次结果;环境操作授权遵循外层根指引的确认边界。 diff --git a/scripts/README.md b/scripts/README.md index ad44819c0..bccbcc913 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -158,7 +158,7 @@ Client 全量、Client 入库测量与计费、待审核订单和批量待分柜 | 迁移辅助 | `migrate-local-qualification-file.ps1` | 本地资质文件迁移辅助,会修改本地文件/数据;运行前核对脚本参数和备份策略 | | Jenkins 管理 | `provision-development-service-jobs.groovy` | Jenkins Script Console 批量准备开发服务 Job;默认 `applyChanges=false` 仅预览,改为 true 会创建 Job | -部署目录的入口另有:`deploy/scripts/deploy-built-service.sh`(单服务健康检查与失败回滚)、`deploy/scripts/deploy-development-service.sh`(经 SSH 部署开发服务)、`deploy/scripts/deploy-services.sh`(按服务列表编排部署)、`deploy/scripts/initialize-test-databases.sh`(初始化测试基础设施数据库)和 `deploy/rocketmq/render-broker-config.sh`(容器内渲染并启动 RocketMQ broker)。这些脚本都会改变运行环境,不能作为普通本地检查执行。 +部署目录的入口另有:`deploy/scripts/deploy-built-service.sh`(单服务健康检查与失败回滚)、`deploy/scripts/deploy-development-service.sh`(经 SSH 部署开发服务)、`deploy/scripts/deploy-services.sh`(按服务列表编排部署)、`deploy/scripts/initialize-test-databases.sh`(初始化测试基础设施数据库)、`deploy/scripts/check-lingxing-browser.sh`(只读检查 Lingxing 浏览器容器及健康状态)和 `deploy/rocketmq/render-broker-config.sh`(容器内渲染并启动 RocketMQ broker)。这些脚本都会改变运行环境或依赖受控部署上下文,不能作为普通本地检查执行。 数据库迁移离线入口为 `deploy/sql/scripts/db-migrations.mjs`:`new` 自动生成单 schema 的下一个版本草稿,`check` 做范围/文件/跨 schema 静态检查,`plan` 生成选定服务的不可变清单,`run --dry-run` 只输出执行计划。当前不连接数据库;真实 Flyway 执行须等迁移阶段完成并获得对应环境授权。用途、参数和边界见 `deploy/sql/README.md`。 diff --git a/scripts/admin-test-env-acceptance.mjs b/scripts/admin-test-env-acceptance.mjs index df080bd58..e9a598f84 100644 --- a/scripts/admin-test-env-acceptance.mjs +++ b/scripts/admin-test-env-acceptance.mjs @@ -1112,7 +1112,7 @@ export class AcceptanceRun { async runCustomers() { const module = 'customers'; const phone = await this.findAvailablePhone(module); const suffix = this.marker.slice(-12) const payload = { - tenantName: `后台客户验收-${suffix}`, legalName: `后台客户验收有限公司-${suffix}`, + tenantName: `后台客户验收-${suffix}`, customerNickname: `后台客户-${suffix}`, legalName: `后台客户验收有限公司-${suffix}`, legalRepresentative: '接口验收法人', contactName: '接口验收联系人', contactPhone: phone, contactEmail: `admin-api-${suffix.toLowerCase()}@example.com`, servicePackage: 'API验收套餐', contactWechat: `api_${suffix.toLowerCase()}`, salesOwnerName: '接口验收销售', paymentTermDays: 30, @@ -1166,7 +1166,7 @@ export class AcceptanceRun { const detailPath = `/api/platform/v1/iam/tenants/${tenantId}` const detailResponse = await this.request('GET', detailPath); const detail = this.apiSuccess(module, '查询客户详情', detailResponse) this.assert(module, '客户详情基本信息正确', detailResponse, detail && detail.tenantName === payload.tenantName && detail.contactPhone === phone && detail.internalRemark === payload.internalRemark, `tenantId=${tenantId}`) - const editedPayload = { ...payload, tenantName: `${payload.tenantName}-已编辑`, contactName: '接口验收联系人-已编辑', contactEmail: `edited-${payload.contactEmail}`, paymentTermDays: 45, internalRemark: `${this.marker}-EDITED` } + const editedPayload = { ...payload, tenantName: `${payload.tenantName}-已编辑`, customerNickname: `${payload.customerNickname}-已编辑`, contactName: '接口验收联系人-已编辑', contactEmail: `edited-${payload.contactEmail}`, paymentTermDays: 45, internalRemark: `${this.marker}-EDITED` } const editResponse = await this.request('PUT', detailPath, editedPayload); const edited = this.apiSuccess(module, '编辑客户', editResponse) this.assert(module, '客户编辑正确回显', editResponse, edited?.tenantName === editedPayload.tenantName && edited?.contactEmail === editedPayload.contactEmail && Number(edited?.paymentTermDays) === 45, `tenantId=${tenantId}`) const persistedResponse = await this.request('GET', detailPath); const persisted = this.apiSuccess(module, '回查客户编辑结果', persistedResponse) diff --git a/scripts/admin-test-env-modules/manual-state-loading-pda.mjs b/scripts/admin-test-env-modules/manual-state-loading-pda.mjs index 80742c422..767c3d0f2 100644 --- a/scripts/admin-test-env-modules/manual-state-loading-pda.mjs +++ b/scripts/admin-test-env-modules/manual-state-loading-pda.mjs @@ -112,7 +112,8 @@ async function createConfirmedContainer(context, source, label) { if (!agent) throw new Error(`${label} 未取得当前船公司关联的 ACTIVE 订舱代理`) const draft = { customerId: source.tenantId, customerName: summary.customerName || 'IT 测试客户', - channelCode: summary.channelCode, warehouseId: source.warehouseId, + // Ocean booking 当前契约接收稳定的渠道分组码;入库摘要仍保留真实渠道码用于候选匹配。 + channelCode: channelGroup(summary.channelCode), warehouseId: source.warehouseId, billOfLadingNo: `BL-${value}`.slice(0, 128), shippingCompanyId: id(carrier.id), shippingCompanyName: carrier.name, carrierCode: carrier.carrierCode, bookingAgentId: id(agent.id), bookingAgentName: agent.name, departurePortCode: 'CNSZX', diff --git a/scripts/admin-test-env-modules/order-inbound-status-fixtures.mjs b/scripts/admin-test-env-modules/order-inbound-status-fixtures.mjs index 585dc0e69..b90f9ab9f 100644 --- a/scripts/admin-test-env-modules/order-inbound-status-fixtures.mjs +++ b/scripts/admin-test-env-modules/order-inbound-status-fixtures.mjs @@ -137,16 +137,18 @@ async function readOrder(context, fixture, orderId) { return { response, detail: context.apiSuccess(MODULE, '回读订单状态样本', response) } } -async function waitForInbound(context, fixture, orderId) { +async function waitForInbound(context, fixture, orderId, allowCompletedOrder = false) { const path = `/api/platform/v1/orders/${query(orderId)}?tenantId=${query(fixture.tenantId)}` - for (let attempt = 0; attempt < 20; attempt += 1) { + // 审核/接受/完成会经过跨服务履约事件;测试环境高负载时可能超过10秒才落入库单。 + // 保持轮询只读并延长观察窗口,避免把异步延迟误判为“未生成入库单”。 + for (let attempt = 0; attempt < 240; attempt += 1) { const response = await context.request('GET', path) const summary = data(response)?.summary if (ok(response) && summary?.inboundId - && ['WAIT_MEASURE', 'MEASURING', 'MEASURE_PENDING', 'MEASURE_REJECTED', 'WAIT_SPLIT'].includes(summary.mainStatus)) { + && (allowCompletedOrder || ['WAIT_MEASURE', 'MEASURING', 'MEASURE_PENDING', 'MEASURE_REJECTED', 'WAIT_SPLIT'].includes(summary.mainStatus))) { return { response, summary } } - if (attempt < 19) await wait(500) + if (attempt < 239) await wait(500) } return { response: null, summary: null } } @@ -248,7 +250,7 @@ export async function run(context, matrix = context.createdResources.manualState } if (actions.includes('approve')) { - const inbound = await waitForInbound(context, fixture, orderId) + const inbound = await waitForInbound(context, fixture, orderId, target === 'COMPLETED') const inboundId = id(inbound.summary?.inboundId) context.assert(MODULE, `${target} 订单生成待测量入库单`, inbound.response, Boolean(inboundId), `orderId=${orderId}, inboundId=${inboundId || 'missing'}`) diff --git a/scripts/agents-index-contract.ps1 b/scripts/agents-index-contract.ps1 index 0962b711d..d1038eb6a 100644 --- a/scripts/agents-index-contract.ps1 +++ b/scripts/agents-index-contract.ps1 @@ -846,11 +846,17 @@ $eventTypes = Read-RequiredText (Join-Path $backendRoot 'common-event-contract/s $overseasDispatcher = Read-RequiredText (Join-Path $backendRoot 'event-bridge-service/src/main/java/com/baiyue/eventbridge/service/OverseasEventDispatcher.java') $financeEventConsumer = Read-RequiredText (Join-Path $backendRoot 'finance/src/main/java/com/baiyue/finance/service/BusinessFeeSourceEventService.java') $financePhase1Service = Read-RequiredText (Join-Path $backendRoot 'finance/src/main/java/com/baiyue/finance/service/FinancePhase1OperationService.java') +$overseasFinancePhase0ContractPath = Join-Path $workspaceRoot 'docs/财务管理/海外仓费用统一接入阶段0技术契约.md' +$overseasFinancePhase0Contract = Read-RequiredText $overseasFinancePhase0ContractPath Assert-Contains $businessIndex '财务管理与柜级公共费用分摊' '根柜费分摊快速路由' Assert-Contains $businessIndex '海外业务费用来源与客户财务' '根海外费用快速路由' Assert-Contains $adminAgents '财务、客户资金账户与柜级公共费用分摊' 'Admin 柜费分摊快速路由' Assert-Contains $backendAgents '柜级公共费用跨客户分摊' '后端柜费分摊快速路由' Assert-Contains $backendAgents '海外业务费用来源跨区入池' '后端海外费用快速路由' +Assert-Contains $businessIndex '海外仓费用统一接入阶段0技术契约.md' '根海外费用阶段 0 按需入口' +Assert-Contains $financeAgents '海外仓费用统一接入阶段0技术契约.md' 'Finance 海外费用阶段 0 按需入口' +Assert-Contains $lastlegAgents '海外仓费用统一接入阶段0技术契约.md' 'Lastleg 海外费用阶段 0 按需入口' +Assert-Contains $lastlegAgents '不能因文档存在宣称已实现' '阶段 0 设计与代码事实边界' Assert-Contains $firstlegAgents '单行入口拒绝分摊组' 'Firstleg 分摊组原子边界' Assert-Contains $adminFinanceAgents '`allocationGroupId` 整组审核、驳回、调整或作废' 'Admin 分摊组操作边界' Assert-Contains $containerFeeOpenApi '/fee-details/allocation-groups:' '柜费分摊组创建 OpenAPI' @@ -916,6 +922,7 @@ foreach ($module in $pom.project.modules.module) { # 对本轮高变化链路做精确文件门禁,防止改名或删除后留下悬空索引。 $criticalPaths = @( 'apps/admin-web/src/modules/overseas-warehouse/api/inboundOrders.ts', + 'docs/财务管理/海外仓费用统一接入阶段0技术契约.md', 'apps/admin-web/src/modules/overseas-warehouse/api/lastMileOrders.ts', 'apps/pda-overseas-uniapp/src/api/pallet.js', 'apps/pda-overseas-uniapp/src/pages/pallet/pallet.vue', diff --git a/scripts/script-documentation-contract.test.mjs b/scripts/script-documentation-contract.test.mjs index 33b66b68b..44ab27cfe 100644 --- a/scripts/script-documentation-contract.test.mjs +++ b/scripts/script-documentation-contract.test.mjs @@ -13,6 +13,7 @@ const excludedDirectoryNames = new Set([ ]) const allowedDirectoryPrefixes = [ 'scripts/', + 'deploy/docker/', 'deploy/scripts/', 'deploy/sql/scripts/', 'deploy/rocketmq/',