119 lines
19 KiB
Markdown
119 lines
19 KiB
Markdown
# 柏悦头程后端项目指引
|
||
|
||
## 适用范围
|
||
|
||
- 本文件适用于 `baiyue-firstleg/` 整个 Maven 多模块仓库;进入子模块后同时遵守该模块的 `AGENTS.md`。
|
||
- 普通开发先读本文件、目标模块 `AGENTS.md` 和相邻代码;项目概览或聚合构建问题再读 `README.md`、`pom.xml`,跨服务架构、端口数据库、根目录结构、启动或部署问题再读 `docs/头程项目目录结构与服务说明.md`。
|
||
- 本仓只承载国内头程业务。尾程源码、数据库、Redis、Nacos 和内部接口不得并入本仓。
|
||
|
||
## 当前主体与隔离模型(重要,所有阶段必须遵守)
|
||
|
||
- 完整目标模型原计划为“租户—平台—客户”三级,但为尽快上线,当前约一年内采用 **业务单租户、平台管理多客户、客户严格隔离** 的模式。当前没有可管理、可选择或可切换的上级租户,也没有真实的“租户—平台—客户”三级关系。
|
||
- “业务单租户”只是产品阶段约束,不表示代码中存在一个供所有数据共用的默认租户 ID。现有代码和数据库的 `tenant_id` 已经广泛承载 **客户 ID**,是当前客户隔离键;严禁把所有客户数据的 `tenant_id` 统一改成同一个值,否则会直接破坏客户隔离。
|
||
- 当前需求、一期、二期、三期及后续未明确启用租户能力的开发,统一按“平台—客户”两级实现。不得自行新增租户主表、租户管理、租户切换、租户级配置、客户所属租户选择、租户级权限或其他显式多租户能力。
|
||
|
||
当前代码中的主体标识固定按下表理解:
|
||
|
||
| 现有结构 | 当前业务含义 | 修改约束 |
|
||
|---|---|---|
|
||
| `iam_tenant.id = 0`、`tenant_type = PLATFORM` | 平台技术作用域 | 必须保留为平台账号、平台角色和平台权限范围,不是未来的默认租户 |
|
||
| `iam_tenant.id > 0`、`tenant_type = MERCHANT` | 平台直接服务的客户;`iam_tenant` 的这些记录当前直接作为客户主档 | 客户列表和客户业务只能使用 `MERCHANT` 记录,不得把平台记录当客户 |
|
||
| Auth/JWT/业务表中的 `tenant_id > 0` | 当前登录客户或数据所属客户的 ID | 商户端从认证上下文取得;平台代客操作显式携带目标客户;Repository/Mapper 必须按该值过滤 |
|
||
| RBAC 数据中的 `tenant_id = 0` | 平台权限或供客户复用的全局角色/权限模板 | 不代表真实上级租户,不能作为客户数据隔离条件 |
|
||
| Order/Firstleg 等业务中的 `customer_id` | 订单往来单位、发货客户或其他业务客户快照,具体以模块契约为准 | 不是 IAM 平台客户 ID,不能替代 `tenant_id` 做当前客户隔离 |
|
||
| `DEFAULT_TENANT_ID`、`public-tenant-id` 等配置 | 平台公共仓库和主数据的技术归属配置,当前固定为平台作用域 `0` | 不代表未来默认租户;公共资源不得再绑定普通客户 `10001`,变更时必须同步 Warehouse 种子、存量迁移及全部消费者 |
|
||
|
||
- 当前真正的数据隔离边界是上述客户 ID。服务端必须校验客户归属和访问权限;商户请求不得信任前端传入其他客户 ID,平台跨客户查询或代客操作必须同时受平台应用域、权限码和目标客户条件约束。
|
||
- 平台维护并供所有客户使用的公共仓库、往来单位、快递公司、托盘、库位等公共资源统一归属平台技术作用域 `tenant_id = 0`;客户订单、文件、费用和其他客户私有数据继续使用客户 `tenant_id > 0`。往来单位不是客户私有数据,不能按客户复制或隔离。不得再使用演示客户 `10001` 充当公共资源所有者。
|
||
- 代码、数据库和接口中已有的 `tenant`、`Tenant*`、`tenant_id`、`merchant` 等名称属于历史命名和兼容契约,当前不做大规模重命名;但新增代码和文档必须按本节解释其真实含义,禁止看到名称后推断系统已经存在显式租户层。
|
||
- 预计约一年后再以独立需求引入真实租户。届时应优先新增独立租户主表或明确的上级 `root_tenant_id`,把所有存量 `MERCHANT` 客户统一归属到一个默认租户,再迁移 Auth、JWT、跨服务 DTO 和各服务数据库;不得原地把现有 `iam_tenant.id` 或业务 `tenant_id` 从客户 ID 改义为上级租户 ID。
|
||
- 用户在后续对话明确调整本模型之前,本节是所有新模块和分期任务的事实来源;原型、历史设计或代码中的“租户”文案不得自动扩大实现范围。
|
||
|
||
## 模块快速索引
|
||
|
||
| 模块 | 职责 | 首选入口 |
|
||
|---|---|---|
|
||
| `platform-bom/` | 公共制品版本清单 | `pom.xml` 的 `dependencyManagement` |
|
||
| `common-core/` | 返回模型、错误码、基础实体、MyBatis 配置 | `ApiResponse.java`、`ResultCode.java`、`exception/`、`model/`、`mybatis/`;成功码为字符串 `"200"` |
|
||
| `common-web/` | HTTP 200 业务响应、全局异常、请求链路标识、长整型 JSON 字符串序列化 | `CommonWebAutoConfiguration.java`、`GlobalExceptionHandler.java`、`CorrelationIdFilter.java`、`LongIdJacksonConfiguration.java` |
|
||
| `common-security/` | JWT、Redis 会话校验、认证上下文、401/403 | `JwtAuthenticationFilter.java`、`RedisAccessTokenSessionValidator.java` |
|
||
| `common-openapi/` | SpringDoc 自动配置与长整型字符串契约 | `BaiyueOpenApiAutoConfiguration.java`、`OpenApiProperties.java` |
|
||
| `common-audit/` | 审计注解、切面、脱敏与日志/HTTP 发送 | `BusinessAuditAspect.java`、`SensitiveDataSanitizer.java`、`HttpAuditSink.java` |
|
||
| `common-event-contract/` | 跨区域事件信封、类型、标识、哈希与校验 | `EventEnvelope.java`、`EventTypes.java`、`EventEnvelopeValidator.java` |
|
||
| `gateway/` | 路由、CORS、入口安全、请求标识、访问域隔离 | `config/`、`filter/CorrelationIdWebFilter.java`、`src/main/resources/application*.yml` |
|
||
| `auth/` | 登录、令牌、注册、账号、密码与账号归属范围 | `InternalAccountController.java`、`AccountManagementService.java`、`AccountManagementDtos.java`、`src/main/resources/db/` |
|
||
| `iam/` | 单租户模式下的客户、客户成员、平台统一用户管理、企业资料/资质、RBAC 与授权快照;部分代码仍保留 `Tenant*` 历史命名 | `PlatformUserManagementController.java`、`PlatformTenantController.java`、`ProfileResourceController.java`、`RbacController.java` |
|
||
| `order/` | 订单主数据、客户运单数、持久化幂等、状态编排、快照、货物明细内部查询与 Outbox;客户之间按归属隔离,部分字段/接口仍保留 `tenant` 历史命名 | `PlatformFirstLegOrderController.java`、`OrderApplicationService.java`、`InternalOrderCargoController.java`、`README-ORDER.md` |
|
||
| `firstleg/` | 履约任务与分配;二期入库单、测量/OCR 结果、审核、FBA 计费和自动完成入库 | 履约:`PlatformFulfillmentAssignmentController.java`、`FulfillmentTaskService.java`;入库:`controller/PlatformInbound*.java`、`service/Inbound*.java`、`service/Fba*.java` |
|
||
| `warehouse/` | 仓库、地址、服务能力、往来单位、快递公司、海外仓阶梯费用;二期托盘、库位、预占和占用 | 主数据:`controller/`、`PlatformPartnerQueryService.java`、`PlatformOverseasStorageFeeController.java`;二期:`PlatformPalletController.java`、`PlatformLocationController.java`、`PalletLocationAllocationService.java` |
|
||
| `file/` | 企业资质文件、公共导出/下载中心;入库测量图片与重量 OCR 协作 | 资质:`QualificationFileController.java`;导出:`PlatformExportTaskController.java`、`ExportStateService.java`;测量图片:`PlatformMeasurementImageController.java`、`InternalMeasurementImageController.java`、`MeasurementImageService.java` |
|
||
| `print/` | 无数据库的无状态 PDF 渲染;固定入仓标签模板、二维码、字体嵌入和资源保护 | `InternalPdfController.java`、`InboundMarkPdfService.java`、`InboundMarkV1Renderer.java`、`src/main/resources/openapi/print-internal-v1.yaml` |
|
||
| `audit-service/` | 审计事件接收与持久化 | `AuditIngestController.java`、`AuditIngestService.java`;当前接收接口未实现内部令牌保护 |
|
||
| `integration-service/` | 重量 OCR、百度 OCR、Amazon 等第三方适配 | `controller/WeightOcrController.java`、`service/ocr/`、`provider/ocr/`、`IntegrationProperties.java` |
|
||
| `event-bridge-service/` | 跨区域事件接收、校验与 Inbox 幂等 | `EventBridgeController.java`、`EventInboxService.java` |
|
||
|
||
## 高频业务检索入口
|
||
|
||
| 搜索目标 | 先读 |
|
||
|---|---|
|
||
| 后台用户管理、平台/客户账号、跨客户调动 | `iam/.../controller/PlatformUserManagementController.java` → `service/PlatformUserManagementService.java` → `auth/.../controller/InternalAccountController.java` 的 scope 接口 |
|
||
| 企业客户档案、审核、必填资质 | `iam/.../controller/PlatformTenantController.java`、`service/TenantService.java`、`service/ProfileResourceService.java` |
|
||
| 客户列表真实运单数 | `order/.../controller/PlatformFirstLegOrderController.java` 的 `/tenant-counts`;不要在 IAM 查订单表 |
|
||
| 平台代客户上传或替换资质 | `file/.../controller/PlatformQualificationFileController.java` → `client/IamQualificationClient.java` → `iam/.../controller/InternalTenantController.java` 的 `/platform-qualifications` |
|
||
| 往来单位多类型 | `warehouse/.../service/PlatformPartnerQueryService.java`、`entity/PartnerTypeEntity.java`、`src/main/resources/db/warehouse-partner.sql`,消费者看 `order/.../client/PartnerClient.java` |
|
||
| 往来单位/快递公司批量导入 | `warehouse/.../service/PartnerImportService.java`、`ExpressCompanyImportService.java`、两个平台 Controller 的 `/import-template` 与 `/import` |
|
||
| 海外仓阶梯费用 | `warehouse/.../controller/PlatformOverseasStorageFeeController.java` → `service/OverseasStorageFeeService.java` → `repository/OverseasStorageFeeRepository.java`;表为 `wh_overseas_storage_fee_rule`、`wh_overseas_storage_fee_tier` |
|
||
| 入仓标签 PDF 生成 | 调用方先读 `print/README.md`;实现为 `print/.../controller/InternalPdfController.java` → `service/InboundMarkPdfService.java` → `renderer/InboundMarkV1Renderer.java`;机器契约见 `print/src/main/resources/openapi/print-internal-v1.yaml`,仅供携带内部令牌的服务调用,不加入 Gateway |
|
||
| HTTP 200、body code、成功码 `"200"` | `docs/统一响应约定改造方案-HTTP200.md`、`common-core/ResultCode.java`、`common-web/GlobalExceptionHandler.java` |
|
||
| 单服务 Jenkins 构建或部署 | 总控 `Jenkinsfile`、子 Job `Jenkinsfile.service`、模块/JAR 映射 `deploy/service-map.yml` |
|
||
|
||
## 二期入库快速索引
|
||
|
||
| 业务链路 | API 与编排入口 | 数据、跨服务与验证入口 |
|
||
|---|---|---|
|
||
| 平台入库单列表、页签与聚合详情 | `firstleg/.../controller/PlatformInboundOrderController.java` → `service/InboundOrderQueryService.java` | `repository/InboundOrderRepository.java`、`mapper/InboundAllocationQueryMapper.java`;测试 `service/InboundOrderQueryServiceTest.java`、`controller/InboundOrderControllerContractTest.java` |
|
||
| 平台测量草稿、提交及重量 OCR 结果 | `firstleg/.../controller/PlatformInboundMeasurementController.java` → `service/InboundMeasurementService.java`、`MeasurementSubmissionService.java` | 图片走 `client/MeasurementImageClient.java`,托盘库位走 `client/WarehousePalletLocationClient.java`;测试 `service/InboundMeasurementServiceTest.java`、`MeasurementSubmissionServiceTest.java` |
|
||
| 第三方尺寸测量回传 | `firstleg/.../controller/OpenApiInboundMeasurementController.java` → `security/MeasurementRequestAuthenticator.java` → `service/InboundMeasurementOpenApiService.java` | 订单货物校验走 `client/OrderCargoClient.java` → `order/.../controller/InternalOrderCargoController.java`;证据图片走 File 内部接口;配置 `config/MeasurementOpenApiProperties.java`,测试 `service/InboundMeasurementOpenApiServiceTest.java` |
|
||
| 测量审核、异常放行与自动完成入库 | `firstleg/.../controller/PlatformInboundMeasurementReviewController.java` → `service/MeasurementReviewService.java` → `AutomaticInboundCompletionService.java` | 费用与入库状态编排在 `FbaPricingService.java`、`InboundCompletionOrchestrator.java`;测试 `service/MeasurementReviewServiceTest.java`、`InboundCompletionOrchestratorTest.java` |
|
||
| FBA 客户规则、承运商价格与费用快照 | 主数据 `firstleg/.../controller/PlatformFbaMasterDataController.java` → `service/FbaMasterDataService.java`;计费 `PlatformFbaPricingController.java` → `service/FbaPricingService.java` | `repository/FbaPricingRepository.java`、`src/main/resources/openapi/fba-pricing-v1.yaml`、`src/main/resources/db/phase2-fba-pricing-migration.sql`;测试 `service/FbaMasterDataServiceTest.java`、`FbaPricingServiceTest.java` |
|
||
| 客户查看入库费用 | `firstleg/.../controller/MerchantInboundBillingController.java` → `service/MerchantInboundBillingService.java` | 只按已认证客户查询;`Merchant*` 及 `/api/merchant/` 为历史兼容命名,接口为 `/api/merchant/v1/first-leg/orders/{orderId}/inbound-billing` |
|
||
| 托盘、库位与预占/占用 | 平台 CRUD:`warehouse/.../controller/PlatformPalletController.java`、`PlatformLocationController.java`;内部事务接口:`InternalPalletLocationController.java` | `service/PalletApplicationService.java`、`LocationApplicationService.java`、`PalletLocationAllocationService.java`;表与迁移在 `warehouse/src/main/resources/db/phase2-*.sql` |
|
||
| 称重图片、OCR 重试与第三方测量证据 | 平台入口 `file/.../controller/PlatformMeasurementImageController.java`;Firstleg 内部入口 `InternalMeasurementImageController.java` | `service/MeasurementImageService.java`、`MeasurementImageStorage.java`、`client/WeightOcrClient.java`;重量识别服务入口 `integration-service/.../controller/WeightOcrController.java` |
|
||
| 入库状态、幂等、历史与 Outbox | `firstleg/.../domain/InboundProcessStatus.java`、`service/InboundIdempotencyService.java`、`repository/InboundWorkflowRepository.java` | 表结构与增量在 `firstleg/src/main/resources/db/phase2-*.sql`;SQL 契约测试 `repository/InboundWorkflowSqlContractTest.java` |
|
||
| 数据库部署 | 空库/存量环境唯一入口 `deploy/sql/头程数据库统一增量-MySQL8.sql` | 覆盖全部一期、二期及后续增量;可重复执行边界和末尾 `PASS` 核验见脚本头及 `deploy/sql/README.md`,历史日期脚本不得再单独执行 |
|
||
|
||
- 上表中的 `模块/.../` 从对应模块的 `src/main/java/com/baiyue/<domain>/` 起算;二期链路跨 `order`、`firstleg`、`warehouse`、`file`、`integration-service`,修改 DTO、内部令牌、状态或表结构时必须逐个检查调用方与被调用方。
|
||
- 二期入库主状态归 `firstleg`,订单货物主数据仍归 `order`,托盘/库位资源归 `warehouse`,文件对象归 `file`,OCR 供应商适配归 `integration-service`;禁止通过跨库查询绕过内部 API。
|
||
|
||
## 配置、数据库与验证入口
|
||
|
||
- 服务本地配置在各模块 `src/main/resources/application.yml`;Nacos 环境配置集中在 `config/nacos/local/` 和 `config/nacos/cn/`。
|
||
- 每个服务的文件日志和滚动策略也在自身 `application.yml`,统一通过 `LOG_PATH` 或 `LOGGING_*` 环境变量覆盖;容器宿主机挂载见 `deploy/docker-compose.yml`。
|
||
- 子模块索引中 `controller/`、`service/`、`client/` 等包内简写均从该模块的 `src/main/java/com/baiyue/<domain>/` 起算;资源路径从模块根目录起算。
|
||
- 表结构和增量脚本优先在目标模块 `src/main/resources/db/` 查找;每个服务只能访问自己的数据库,禁止跨库 Join。
|
||
- `CREATE TABLE IF NOT EXISTS` 不会升级既有表;新增字段或索引时必须同时提供可重复执行的增量迁移,并在部署清单中写明执行顺序。
|
||
- 服务间调用先找调用方 `client/`;跨区域数据只使用 `common-event-contract/` 和事件桥,不共享领域 Entity。
|
||
- 本地环境切换看 `docs/数据库环境切换说明.md`、`scripts/local-env.ps1` 与 `config/nacos/local/`;接口契约优先读目标模块 `src/main/resources/openapi/` 和 Controller,回归入口在 `scripts/`,历史资质文件迁移使用 `scripts/migrate-local-qualification-file.ps1`。
|
||
- 平台管理与头程履约补齐的本地数据库只读核验、SQL 清单和回归结果见 `docs/2026-07-31平台管理与头程履约补齐回归报告.md`。
|
||
- IAM 授角、订单主数据、File、Gateway CORS 与文件日志的本轮结果见 `docs/2026-07-31-P0-P2问题修复回归报告.md`。
|
||
- 本轮测试环境数据库增量统一从 `deploy/sql/2026-07-31一期回归测试环境增量迁移.sql` 执行,执行边界与核验方式见 `deploy/sql/README.md`。
|
||
- 二期入库新建或存量测试环境升级使用 `deploy/sql/2026-08-04-二期入库新增表字段-MySQL8一键执行.sql`;旧的 `2026-08-03-二期入库表结构-TEST环境一键执行.sql` 只保留为早期基线参考,不替代 8 月 4 日最终增量入口。
|
||
- CI 总控入口是 `Jenkinsfile`,负责按变更或勾选项并行调度 11 个独立服务 Job;单服务构建、镜像与部署逻辑在 `Jenkinsfile.service`。Job 名必须以模块名结尾,公共模块或 `deploy/` 变化会触发全部服务。
|
||
- 从本目录运行全量校验:`mvn -DskipTests validate`、`mvn test`。单模块修改优先运行 `mvn -pl <module> -am test`。
|
||
- 公共导出中心测试环境真实任务验收使用 `scripts/export-center-test-env-acceptance.ps1`;脚本只调用 Gateway,不负责部署或停止 Broker/File/IAM,故障注入由有权限的运维配合。
|
||
- `reports/`、`target/`、日志、上传文件和本地环境文件不属于源码,检索与提交时排除。
|
||
|
||
## 通用修改约束
|
||
|
||
- Controller 只做协议转换,Service 负责编排,Repository/Mapper 负责持久化;不要跨层堆放业务逻辑。
|
||
- 当前采用业务单租户、平台管理多客户模式。现有 `tenant_id` 是客户归属与隔离键,客户数据必须保留该维度,并由服务端执行平台/客户访问域、客户数据隔离、数据范围和按钮权限校验;不得把它统一替换为所谓默认租户 ID,也不得据此允许跨客户访问。
|
||
- 修改 SQL、DTO、事件或公共组件时同步检查所有生产者与消费者;优先保持兼容,不能兼容时写清迁移顺序。
|
||
- 对外 HTTP JSON 中的 Java `Long/long` 统一输出字符串,请求同时接受字符串 ID;业务模块禁止重复添加局部 ID 转换器,OpenAPI 必须同步声明字符串契约。
|
||
- 普通业务成功与失败统一使用 HTTP 200,并以响应体字符串 `code` 判断;`code == "200"` 才成功。网关/Servlet Security 的真实 401/403、协议与基础设施错误、Warehouse readiness 503、Audit 202 和 Event Bridge 内部鉴权边界除外;完整约定见 `docs/统一响应约定改造方案-HTTP200.md`。
|
||
- 密码、令牌、第三方密钥和真实客户数据只能通过环境变量或配置中心密文注入。
|
||
|
||
## 本地内部服务令牌
|
||
|
||
- `scripts/local-env.ps1` 是本地运行时的统一环境入口,Order、Warehouse、IAM 等相互调用的服务必须从同一套 `INTERNAL_SERVICE_TOKEN` 启动。
|
||
- `config/nacos/local/baiyue-common.yml` 的本地默认令牌必须与该脚本一致;变更后执行 `scripts/publish-local-nacos-config.ps1` 发布并重启相关服务。
|