# Section 同步改造后的调用链路 ## 一、目标说明 本次改造将 section 同步从“controller 直接调用专用批处理 service”的方式,收敛为“controller + 公共编排 service + 业务处理器”的统一批量同步架构。 目标收益: - 统一批次校验、分片、并发、错误归集和状态汇总逻辑 - 业务类只保留 section 专属的校验、映射和单分片下发逻辑 - 为后续 course/classroom 等同步类型复用同一套编排链路 --- ## 二、改造后的主调用链 ### 1. HTTP 入口 文件:`ruoyi-server/ruoyi-server-sync/src/main/java/org/dromara/server/sync/controller/SyncReceiveController.java` 入口方法: - `syncSections(BatchSyncRequest request)` 职责: 1. 接收 `/api/sync/receive/sections` 请求 2. 设置 `request.businessType = SyncResourceConstants.SECTION` 3. 调用 `CommonBatchSyncService.sync(request)` 4. 返回 `BatchSyncResult` --- ### 2. 公共批量编排层 文件:`ruoyi-server/ruoyi-server-sync/src/main/java/org/dromara/server/sync/business/batch/CommonBatchSyncService.java` 主方法: - `sync(BatchSyncRequest request)` 职责: 1. 校验 `request` 和 `records` 2. 根据 `businessType` 获取业务处理器 3. 创建批次记录 `SyncBatchTrackerService.start(...)` 4. 逐条校验并转换记录 5. 收集校验失败错误 6. 将有效记录按 100 条分片 7. 单片串行执行,多片按每轮最多 5 片并发执行 8. 聚合所有分片的成功数与错误明细 9. 保存错误明细并结束批次 10. 返回统一 `BatchSyncResult` 关键内部方法: - `getHandler(...)`:根据 `businessType` 获取处理器 - `partition(...)`:按固定大小切分分片 - `executeChunks(...)`:执行分片,同步或并发 - `buildResult(...)`:构造最终返回结果 - `resolveStatus(...)`:计算批次状态 --- ### 3. 业务处理器抽象 文件:`ruoyi-server/ruoyi-server-sync/src/main/java/org/dromara/server/sync/business/batch/BatchSyncBusinessHandler.java` 作用: 定义公共编排层与具体业务处理器之间的统一契约。 核心方法: - `getBusinessType()` - `getApiName()` - `getResourceType()` - `resolveTenantId(...)` - `validateRecord(...)` - `getBizKey(...)` - `toDispatchDto(...)` - `dispatchChunk(...)` --- ### 4. section 业务处理器 文件:`ruoyi-server/ruoyi-server-sync/src/main/java/org/dromara/server/sync/business/batch/SectionBatchSyncService.java` 注册方式: - `@Service(SyncResourceConstants.SECTION)` 实现接口: - `BatchSyncBusinessHandler` 职责拆分: 1. `validateRecord(...)` - 校验 section 单条记录的必填字段 2. `getBizKey(...)` - 提取 `sectionId` 作为错误定位主键 3. `toDispatchDto(...)` - 将请求对象转换为 `RemoteSectionSyncBatchDto` 4. `dispatchChunk(...)` - 执行单个分片的远端 section 同步 - 解析远端返回结果 - 转换远端错误为本地 `SyncBatchErrorRecord` 5. `syncSections(...)` - 兼容旧入口,内部委托给 `CommonBatchSyncService` --- ### 5. 远端 Dubbo 接口 接口文件:`ruoyi-api/ruoyi-api-ecs/src/main/java/org/dromara/ecs/api/RemoteSectionSyncService.java` 实现文件:`ruoyi-modules/ruoyi-ecs/src/main/java/org/dromara/ecs/dubbo/RemoteSectionSyncServiceImpl.java` 调用方法: - `syncSectionBatch(List records)` 职责: 1. 接收 server-sync 下发的 section 分片数据 2. 转换为 ECS 内部 DTO 3. 调用 `ISyncSectionService.receiveSectionBatch(...)` 4. 返回远端批量处理结果 `RemoteBatchSyncResultDto` --- ### 6. ECS 内部同步服务 文件:`ruoyi-modules/ruoyi-ecs/src/main/java/org/dromara/ecs/service/sync/SyncSectionServiceImpl.java` 核心方法: - `receiveSectionBatch(...)` - `processSection(...)` 职责: 1. 先处理 `delFlag=1` 的删除组 2. 再处理 `delFlag=0` 的新增/修改组 3. 单条处理时: - 删除组:按 `otherId` 更新删除标记 - 非删除组:按 `otherId` 判断新增或修改 --- ### 7. 持久化层 文件: - `ruoyi-modules/ruoyi-ecs/src/main/java/org/dromara/ecs/service/impl/EcsSectionServiceImpl.java` - `ruoyi-modules/ruoyi-ecs/src/main/java/org/dromara/ecs/mapper/EcsSectionMapper.java` 职责: - `queryByOtherId(...)`:查询已存在记录 - `insertByBo(...)`:新增课表 - `updateByBo(...)`:更新课表 - `updateDelFlagByOtherId(...)`:按 `otherId + tenantId` 更新删除标记 --- ## 三、改造后的完整执行时序 1. 外部系统调用 `/api/sync/receive/sections` 2. `SyncReceiveController.syncSections(...)` 3. 设置 `businessType = SECTION` 4. `CommonBatchSyncService.sync(...)` 5. `SpringUtils.getBean("SECTION", BatchSyncBusinessHandler.class)` 获取 `SectionBatchSyncService` 6. `SyncBatchTrackerService.start(...)` 创建批次 7. 公共层逐条校验 section 数据并转换为 `RemoteSectionSyncBatchDto` 8. 公共层按 100 条切分分片 9. 单分片同步执行;多分片每轮最多 5 片并发执行 10. 每个分片调用 `SectionBatchSyncService.dispatchChunk(...)` 11. `dispatchChunk(...)` 调用 `RemoteSectionSyncService.syncSectionBatch(...)` 12. `RemoteSectionSyncServiceImpl.syncSectionBatch(...)` 转内部 DTO 后调用 `SyncSectionServiceImpl.receiveSectionBatch(...)` 13. ECS 内部完成删除/新增/修改处理 14. 分片结果返回到公共层 15. 公共层聚合 success/error 16. `SyncBatchTrackerService.saveErrors(...)` 保存错误明细 17. `SyncBatchTrackerService.finish(...)` 更新批次状态 18. controller 返回 `BatchSyncResult` --- ## 四、当前分片与并发规则 在 `CommonBatchSyncService` 中约定: - `CHUNK_SIZE = 100` - `MAX_PARALLEL_CHUNKS = 5` 执行规则: - 只有 1 个分片:当前线程同步执行 - 多于 1 个分片:按窗口执行,每轮最多并发 5 个分片 - 并发线程池复用 `threadPoolTaskExecutor` --- ## 五、错误归集规则 错误来源分两类: ### 1. 校验失败 发生位置:公共编排层 处理方式: - 不进入远端调用 - 直接通过 `SyncBatchTrackerService.error(...)` 构造 `VALIDATION_ERROR` ### 2. 业务失败 / 远端失败 发生位置:section 分片处理器或远端服务 处理方式: - 转换为 `BIZ_ERROR` - 统一归集为 `SyncBatchErrorRecord` - 最后由 `SyncBatchTrackerService.saveErrors(...)` 批量保存 --- ## 六、改造后 section 相关关键类 ### server-sync 侧 - `SyncReceiveController` - `CommonBatchSyncService` - `BatchSyncBusinessHandler` - `SectionBatchSyncService` - `SyncBatchTrackerService` - `BatchSyncRequest` - `BatchSyncResult` - `BatchChunkSyncResult` ### api / ecs 侧 - `RemoteSectionSyncService` - `RemoteSectionSyncServiceImpl` - `SyncSectionServiceImpl` - `EcsSectionServiceImpl` - `EcsSectionMapper` --- ## 七、当前实现的边界说明 1. 这次只迁移了 section,同步类如 train/trainee 仍保持原有方式 2. 并发后的事务边界是“单分片”,不是“整批” 3. section 下游的删除语义没有改变,仍由 ECS 内部服务控制 4. 公共编排层只负责批次通用逻辑,不介入 section 的具体业务规则 --- ## 八、后续可复用扩展方式 如果后续要接入新的同步类型,如 `course`、`classroom`: 1. 在 `SyncResourceConstants` 中增加业务类型常量 2. 新增对应的业务处理器,实现 `BatchSyncBusinessHandler` 3. 在 controller 中设置 `businessType` 4. 直接复用 `CommonBatchSyncService.sync(...)` 这样就不需要重复实现批次校验、分片、并发、错误归集和批次跟踪逻辑。