From bd90ec9d70006a3b26127691e28b12efe3663fe5 Mon Sep 17 00:00:00 2001 From: hewencai <2357300448@qq.com> Date: Mon, 1 Dec 2025 23:44:32 +0800 Subject: [PATCH] =?UTF-8?q?feat(databus):=20=E5=AE=8C=E6=88=90=E9=98=B6?= =?UTF-8?q?=E6=AE=B5=E5=9B=9B-DataBus=20Server=E5=AE=8C=E6=95=B4=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 补充缺失的 API 类(DatabusMessage、DatabusBatchMessage、DatabusEventType) - 新增变更消息消费者(3个:部门、用户、岗位) - 新增数据提供者(3个:部门、用户、岗位) - 确认分发器服务(核心定向推送逻辑) - 确认全量同步与消息推送组件 - 确认管理后台 API(5个 Controller) - 确认 Service ��(4个核心服务) - 确认 DAL 层(7个 DO + Mapper) - 添加 databus-server starter 依赖到 pom.xml - 编译验证通过 Ref: docs/databus/implementation-checklist.md 任务 39-70 --- .../pom.xml | 71 ++++ .../DatabusSyncClientAutoConfiguration.java | 23 ++ .../config/DatabusSyncClientProperties.java | 113 ++++++ .../controller/SyncMessageController.java | 43 +++ .../core/idempotent/IdempotentStore.java | 19 + .../core/idempotent/RedisIdempotentStore.java | 30 ++ .../listener/DatabusConsumerRegistry.java | 148 ++++++++ .../client/core/message/SyncMessage.java | 54 +++ .../core/processor/MessageProcessor.java | 236 +++++++++++++ .../client/handler/BatchSyncEventHandler.java | 51 +++ .../client/handler/SyncEventHandler.java | 57 +++ .../client/handler/post/PostSyncService.java | 108 ++++++ .../handler/post/SystemPostFullHandler.java | 71 ++++ .../main/resources/META-INF/spring.factories | 2 + ...ot.autoconfigure.AutoConfiguration.imports | 1 + .../pom.xml | 73 ++++ .../config/DatabusMessagingConfiguration.java | 27 ++ .../DatabusServerAutoConfiguration.java | 24 ++ .../config/DatabusServerProperties.java | 60 ++++ .../DatabusSyncServerAutoConfiguration.java | 45 +++ .../config/DatabusSyncServerProperties.java | 61 ++++ .../admin/DatabusSyncClientController.java | 92 +++++ .../DatabusSyncDeadLetterController.java | 76 ++++ .../admin/DatabusSyncEventController.java | 92 +++++ .../admin/DatabusSyncFullTaskController.java | 102 ++++++ .../admin/DatabusSyncPushLogController.java | 69 ++++ .../DatabusSyncSubscriptionController.java | 100 ++++++ .../vo/client/DatabusSyncClientPageReqVO.java | 27 ++ .../vo/client/DatabusSyncClientRespVO.java | 63 ++++ .../vo/client/DatabusSyncClientSaveReqVO.java | 55 +++ .../DatabusSyncDeadLetterPageReqVO.java | 43 +++ .../DatabusSyncDeadLetterRespVO.java | 63 ++++ .../vo/event/DatabusSyncEventPageReqVO.java | 24 ++ .../vo/event/DatabusSyncEventRespVO.java | 54 +++ .../vo/event/DatabusSyncEventSaveReqVO.java | 45 +++ .../DatabusSyncFullTaskCreateReqVO.java | 18 + .../DatabusSyncFullTaskPageReqVO.java | 30 ++ .../fulltask/DatabusSyncFullTaskRespVO.java | 88 +++++ .../pushlog/DatabusSyncPushLogPageReqVO.java | 46 +++ .../vo/pushlog/DatabusSyncPushLogRespVO.java | 72 ++++ .../DatabusSyncSubscriptionPageReqVO.java | 24 ++ .../DatabusSyncSubscriptionRespVO.java | 78 +++++ .../DatabusSyncSubscriptionSaveReqVO.java | 39 +++ .../convert/DatabusSyncClientConvert.java | 25 ++ .../convert/DatabusSyncDeadLetterConvert.java | 22 ++ .../convert/DatabusSyncEventConvert.java | 25 ++ .../convert/DatabusSyncFullTaskConvert.java | 22 ++ .../convert/DatabusSyncPushLogConvert.java | 22 ++ .../DatabusSyncSubscriptionConvert.java | 25 ++ .../server/core/event/DatabusEvent.java | 71 ++++ .../server/core/message/BatchSyncMessage.java | 121 +++++++ .../server/core/message/SyncMessage.java | 54 +++ .../server/core/provider/DataProvider.java | 120 +++++++ .../core/provider/DataProviderRegistry.java | 62 ++++ .../core/publisher/DatabusEventPublisher.java | 28 ++ .../publisher/DatabusEventPublisherImpl.java | 60 ++++ .../server/core/pusher/MessagePusher.java | 50 +++ .../server/core/pusher/MessagePusherImpl.java | 103 ++++++ .../core/sync/DatabusFullSyncService.java | 51 +++ .../sync/DatabusIncrementalSyncService.java | 27 ++ .../DatabusIncrementalSyncServiceImpl.java | 246 +++++++++++++ .../server/core/sync/DatabusSyncService.java | 24 ++ .../core/sync/DatabusSyncServiceImpl.java | 237 +++++++++++++ .../dal/dataobject/DatabusSyncClientDO.java | 84 +++++ .../dataobject/DatabusSyncDeadLetterDO.java | 106 ++++++ .../dal/dataobject/DatabusSyncEventDO.java | 76 ++++ .../dataobject/DatabusSyncEventRecordDO.java | 87 +++++ .../dal/dataobject/DatabusSyncFullTaskDO.java | 126 +++++++ .../dal/dataobject/DatabusSyncLogDO.java | 126 +++++++ .../dataobject/DatabusSyncSubscriptionDO.java | 91 +++++ .../dal/mapper/DatabusSyncClientMapper.java | 38 ++ .../mapper/DatabusSyncDeadLetterMapper.java | 29 ++ .../dal/mapper/DatabusSyncEventMapper.java | 37 ++ .../mapper/DatabusSyncEventRecordMapper.java | 15 + .../dal/mapper/DatabusSyncFullTaskMapper.java | 62 ++++ .../dal/mapper/DatabusSyncLogMapper.java | 30 ++ .../mapper/DatabusSyncSubscriptionMapper.java | 41 +++ .../server/enums/DataProviderTypeEnum.java | 36 ++ .../server/enums/DeadLetterStatusEnum.java | 51 +++ .../server/enums/ErrorCodeConstants.java | 30 ++ .../server/enums/FullTaskStatusEnum.java | 75 ++++ .../databus/server/enums/SyncModeEnum.java | 46 +++ .../databus/server/enums/SyncStatusEnum.java | 56 +++ .../databus/server/enums/TimelinessEnum.java | 51 +++ .../server/enums/TransportTypeEnum.java | 46 +++ .../producer/DatabusMessageProducer.java | 195 +++++++++++ .../service/DatabusSyncClientService.java | 70 ++++ .../service/DatabusSyncDeadLetterService.java | 54 +++ .../service/DatabusSyncEventService.java | 70 ++++ .../server/service/DatabusSyncLogService.java | 37 ++ .../DatabusSyncSubscriptionService.java | 75 ++++ .../impl/DatabusFullSyncServiceImpl.java | 328 ++++++++++++++++++ .../impl/DatabusSyncClientServiceImpl.java | 115 ++++++ .../DatabusSyncDeadLetterServiceImpl.java | 99 ++++++ .../impl/DatabusSyncEventServiceImpl.java | 115 ++++++ .../impl/DatabusSyncLogServiceImpl.java | 50 +++ .../DatabusSyncSubscriptionServiceImpl.java | 129 +++++++ .../main/resources/META-INF/spring.factories | 2 + ...ot.autoconfigure.AutoConfiguration.imports | 2 + .../api/message/DatabusBatchMessage.java | 106 ++++++ .../databus/api/message/DatabusMessage.java | 73 ++++ .../databus/enums/DatabusEventType.java | 306 ++++++++++++++++ .../zt-module-databus-server/pom.xml | 7 + .../consumer/DatabusDeptChangeConsumer.java | 90 +++++ .../consumer/DatabusPostChangeConsumer.java | 73 ++++ .../consumer/DatabusUserChangeConsumer.java | 79 +++++ .../provider/DeptDataFeignProvider.java | 85 +++++ .../provider/PostDataFeignProvider.java | 81 +++++ .../provider/UserDataFeignProvider.java | 81 +++++ 109 files changed, 7673 insertions(+) create mode 100644 zt-framework/zt-spring-boot-starter-databus-client/pom.xml create mode 100644 zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/config/DatabusSyncClientAutoConfiguration.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/config/DatabusSyncClientProperties.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/controller/SyncMessageController.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/idempotent/IdempotentStore.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/idempotent/RedisIdempotentStore.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/listener/DatabusConsumerRegistry.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/message/SyncMessage.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/processor/MessageProcessor.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/handler/BatchSyncEventHandler.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/handler/SyncEventHandler.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/handler/post/PostSyncService.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/handler/post/SystemPostFullHandler.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-client/src/main/resources/META-INF/spring.factories create mode 100644 zt-framework/zt-spring-boot-starter-databus-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/pom.xml create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusMessagingConfiguration.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusServerAutoConfiguration.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusServerProperties.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusSyncServerAutoConfiguration.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusSyncServerProperties.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncClientController.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncDeadLetterController.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncEventController.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncFullTaskController.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncPushLogController.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncSubscriptionController.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/client/DatabusSyncClientPageReqVO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/client/DatabusSyncClientRespVO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/client/DatabusSyncClientSaveReqVO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/deadletter/DatabusSyncDeadLetterPageReqVO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/deadletter/DatabusSyncDeadLetterRespVO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/event/DatabusSyncEventPageReqVO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/event/DatabusSyncEventRespVO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/event/DatabusSyncEventSaveReqVO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/fulltask/DatabusSyncFullTaskCreateReqVO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/fulltask/DatabusSyncFullTaskPageReqVO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/fulltask/DatabusSyncFullTaskRespVO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/pushlog/DatabusSyncPushLogPageReqVO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/pushlog/DatabusSyncPushLogRespVO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/subscription/DatabusSyncSubscriptionPageReqVO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/subscription/DatabusSyncSubscriptionRespVO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/subscription/DatabusSyncSubscriptionSaveReqVO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncClientConvert.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncDeadLetterConvert.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncEventConvert.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncFullTaskConvert.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncPushLogConvert.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncSubscriptionConvert.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/event/DatabusEvent.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/message/BatchSyncMessage.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/message/SyncMessage.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/provider/DataProvider.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/provider/DataProviderRegistry.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/publisher/DatabusEventPublisher.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/publisher/DatabusEventPublisherImpl.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/pusher/MessagePusher.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/pusher/MessagePusherImpl.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusFullSyncService.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusIncrementalSyncService.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusIncrementalSyncServiceImpl.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusSyncService.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusSyncServiceImpl.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncClientDO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncDeadLetterDO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncEventDO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncEventRecordDO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncFullTaskDO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncLogDO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncSubscriptionDO.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncClientMapper.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncDeadLetterMapper.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncEventMapper.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncEventRecordMapper.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncFullTaskMapper.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncLogMapper.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncSubscriptionMapper.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/DataProviderTypeEnum.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/DeadLetterStatusEnum.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/ErrorCodeConstants.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/FullTaskStatusEnum.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/SyncModeEnum.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/SyncStatusEnum.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/TimelinessEnum.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/TransportTypeEnum.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/producer/DatabusMessageProducer.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncClientService.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncDeadLetterService.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncEventService.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncLogService.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncSubscriptionService.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusFullSyncServiceImpl.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncClientServiceImpl.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncDeadLetterServiceImpl.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncEventServiceImpl.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncLogServiceImpl.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncSubscriptionServiceImpl.java create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/resources/META-INF/spring.factories create mode 100644 zt-framework/zt-spring-boot-starter-databus-server/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/api/message/DatabusBatchMessage.java create mode 100644 zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/api/message/DatabusMessage.java create mode 100644 zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/enums/DatabusEventType.java create mode 100644 zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/mq/consumer/DatabusDeptChangeConsumer.java create mode 100644 zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/mq/consumer/DatabusPostChangeConsumer.java create mode 100644 zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/mq/consumer/DatabusUserChangeConsumer.java create mode 100644 zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/provider/DeptDataFeignProvider.java create mode 100644 zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/provider/PostDataFeignProvider.java create mode 100644 zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/provider/UserDataFeignProvider.java diff --git a/zt-framework/zt-spring-boot-starter-databus-client/pom.xml b/zt-framework/zt-spring-boot-starter-databus-client/pom.xml new file mode 100644 index 00000000..08e8dcc8 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-client/pom.xml @@ -0,0 +1,71 @@ + + + + com.zt.plat + zt-framework + ${revision} + + 4.0.0 + zt-spring-boot-starter-databus-client + jar + + ${project.artifactId} + DataBus 客户端组件,负责接收数据变更并同步 + https://github.com/YunaiV/ruoyi-vue-pro + + + + com.zt.plat + zt-common + + + + + com.zt.plat + zt-module-databus-api + ${revision} + + + + + com.zt.plat + zt-module-system-api + ${revision} + true + + + + cn.hutool + hutool-all + + + + + com.zt.plat + zt-spring-boot-starter-mq + + + + + com.zt.plat + zt-spring-boot-starter-redis + + + + + org.springframework.boot + spring-boot-starter-web + true + + + + + com.zt.plat + zt-spring-boot-starter-test + test + + + + diff --git a/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/config/DatabusSyncClientAutoConfiguration.java b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/config/DatabusSyncClientAutoConfiguration.java new file mode 100644 index 00000000..afa72c43 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/config/DatabusSyncClientAutoConfiguration.java @@ -0,0 +1,23 @@ +package com.zt.plat.framework.databus.client.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; + +/** + * Databus 同步客户端自动配置 + * + * @author ZT + */ +@Slf4j +@AutoConfiguration +@EnableConfigurationProperties(DatabusSyncClientProperties.class) +@ComponentScan(basePackages = "com.zt.plat.framework.databus.client") +public class DatabusSyncClientAutoConfiguration { + + public DatabusSyncClientAutoConfiguration() { + log.info("[Databus] 数据同步客户端模块已加载"); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/config/DatabusSyncClientProperties.java b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/config/DatabusSyncClientProperties.java new file mode 100644 index 00000000..f79478ef --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/config/DatabusSyncClientProperties.java @@ -0,0 +1,113 @@ +package com.zt.plat.framework.databus.client.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Databus 数据同步客户端配置属性 + * + * @author ZT + */ +@Data +@ConfigurationProperties(prefix = "zt.databus.sync.client") +public class DatabusSyncClientProperties { + + /** + * 是否启用 + */ + private Boolean enabled = true; + /** + * RocketMQ NameServer 地址 + */ + private String nameServer; + + /** + * 客户端编码(必填,用于订阅专属Topic) + * Topic格式: databus-sync-{eventType}-{clientCode} + */ + private String clientCode; + + /** + * MQ配置 + */ + private Mq mq = new Mq(); + + /** + * HTTP配置 + */ + private Http http = new Http(); + + /** + * 幂等配置 + */ + private Idempotent idempotent = new Idempotent(); + + @Data + public static class Mq { + /** + * 是否启用MQ消费 + */ + private Boolean enabled = true; + /** + * RocketMQ NameServer 地址 + */ + private String nameServer; + + /** + * Topic基础名称 + */ + private String topicBase = "databus-sync"; + + /** + * 消费者组前缀,完整格式: {consumerGroupPrefix}-{eventType} + * 默认: databus-client-{clientCode} + */ + private String consumerGroupPrefix; + + /** + * 消费线程数 + */ + private Integer consumeThreadMin = 1; + + /** + * 消费线程数 + */ + private Integer consumeThreadMax = 5; + + /** + * 最大重试次数 + */ + private Integer maxReconsumeTimes = 3; + } + + @Data + public static class Http { + /** + * 是否启用HTTP推送接收 + */ + private Boolean enabled = false; + + /** + * 接收端点路径 + */ + private String endpoint = "/databus/sync/receive"; + } + + @Data + public static class Idempotent { + /** + * 是否启用幂等检查 + */ + private Boolean enabled = true; + /** + * RocketMQ NameServer 地址 + */ + private String nameServer; + + /** + * 幂等记录过期时间(秒) + */ + private Integer expireSeconds = 86400; // 24小时 + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/controller/SyncMessageController.java b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/controller/SyncMessageController.java new file mode 100644 index 00000000..1d5ace2b --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/controller/SyncMessageController.java @@ -0,0 +1,43 @@ +package com.zt.plat.framework.databus.client.core.controller; + +import com.zt.plat.framework.databus.client.core.processor.MessageProcessor; +import com.zt.plat.module.databus.enums.DatabusEventType; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.web.bind.annotation.*; + +/** + * 同步消息HTTP接收控制器 + * + * @author ZT + */ +@Slf4j +@RestController +@RequestMapping("${zt.databus.sync.client.http.endpoint:/databus/sync/receive}") +@RequiredArgsConstructor +@ConditionalOnProperty(prefix = "zt.databus.sync.client.http", name = "enabled", havingValue = "true") +public class SyncMessageController { + + private final MessageProcessor messageProcessor; + + /** + * 接收同步消息 + * + * @param eventType 事件类型 + * @param message 消息体 + */ + @PostMapping("/{eventType}") + public void receive(@PathVariable("eventType") String eventType, @RequestBody String message) { + log.debug("[Databus Client] 接收到HTTP消息, eventType={}", eventType); + + DatabusEventType type = DatabusEventType.valueOf(eventType); + if (type == null) { + log.warn("[Databus Client] 未知的事件类型: {}", eventType); + return; + } + + messageProcessor.process(message, type); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/idempotent/IdempotentStore.java b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/idempotent/IdempotentStore.java new file mode 100644 index 00000000..14f99600 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/idempotent/IdempotentStore.java @@ -0,0 +1,19 @@ +package com.zt.plat.framework.databus.client.core.idempotent; + +/** + * 幂等存储接口 + * + * @author ZT + */ +public interface IdempotentStore { + + /** + * 检查并记录消息是否已处理 + * + * @param syncId 同步ID + * @param expireSeconds 过期时间(秒) + * @return true-未处理(可以处理), false-已处理(应该跳过) + */ + boolean checkAndMark(String syncId, int expireSeconds); + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/idempotent/RedisIdempotentStore.java b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/idempotent/RedisIdempotentStore.java new file mode 100644 index 00000000..edae4ce7 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/idempotent/RedisIdempotentStore.java @@ -0,0 +1,30 @@ +package com.zt.plat.framework.databus.client.core.idempotent; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +/** + * 基于 Redis 的幂等存储实现 + * + * @author ZT + */ +@Component +@RequiredArgsConstructor +public class RedisIdempotentStore implements IdempotentStore { + + private static final String KEY_PREFIX = "databus:sync:idempotent:"; + + private final StringRedisTemplate stringRedisTemplate; + + @Override + public boolean checkAndMark(String syncId, int expireSeconds) { + String key = KEY_PREFIX + syncId; + Boolean success = stringRedisTemplate.opsForValue() + .setIfAbsent(key, "1", expireSeconds, TimeUnit.SECONDS); + return Boolean.TRUE.equals(success); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/listener/DatabusConsumerRegistry.java b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/listener/DatabusConsumerRegistry.java new file mode 100644 index 00000000..bf1ba313 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/listener/DatabusConsumerRegistry.java @@ -0,0 +1,148 @@ +package com.zt.plat.framework.databus.client.core.listener; + +import com.zt.plat.framework.databus.client.config.DatabusSyncClientProperties; +import com.zt.plat.framework.databus.client.core.processor.MessageProcessor; +import com.zt.plat.module.databus.enums.DatabusEventType; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; +import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; +import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; +import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.common.message.MessageExt; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +/** + * Databus Consumer 注册器 + *

+ * 根据 DatabusEventType 枚举自动为所有事件类型创建消费者 + * Topic格式: {topicBase}-{module}-{entity}-{action}-{clientCode} + * 例如: databus-sync-system-user-create-company-a + * + * @author ZT + */ +@Slf4j +@Component +@ConditionalOnProperty(prefix = "zt.databus.sync.client.mq", name = "enabled", havingValue = "true", matchIfMissing = true) +public class DatabusConsumerRegistry { + + private final MessageProcessor messageProcessor; + private final DatabusSyncClientProperties properties; + + + /** + * 管理的消费者列表 + */ + private final List consumers = new ArrayList<>(); + + public DatabusConsumerRegistry(MessageProcessor messageProcessor, DatabusSyncClientProperties properties) { + this.messageProcessor = messageProcessor; + this.properties = properties; + } + + @PostConstruct + public void init() { + if (!Boolean.TRUE.equals(properties.getEnabled())) { + log.info("[Databus Client] 客户端未启用,跳过初始化"); + return; + } + + String nameServer = properties.getMq().getNameServer(); + if (nameServer == null || nameServer.isEmpty()) { + log.warn("[Databus Client] RocketMQ nameServer未配置,跳过MQ消费者初始化"); + return; + } + + String clientCode = properties.getClientCode(); + if (clientCode == null || clientCode.isEmpty()) { + log.error("[Databus Client] clientCode未配置,无法订阅Topic"); + return; + } + + String topicBase = properties.getMq().getTopicBase(); + String consumerGroupPrefix = properties.getMq().getConsumerGroupPrefix(); + if (consumerGroupPrefix == null || consumerGroupPrefix.isEmpty()) { + consumerGroupPrefix = "databus-client-" + clientCode; + } + + // 为每个事件类型创建独立的消费者 + for (DatabusEventType eventType : DatabusEventType.values()) { + try { + createConsumer(eventType, topicBase, clientCode, consumerGroupPrefix, nameServer); + } catch (Exception e) { + log.error("[Databus Client] 创建消费者失败, eventType={}", eventType.name(), e); + } + } + + log.info("[Databus Client] 消费者注册完成,共创建 {} 个消费者", consumers.size()); + } + + /** + * 为指定事件类型创建消费者 + */ + private void createConsumer(DatabusEventType eventType, String topicBase, String clientCode, String consumerGroupPrefix, String nameServer) throws MQClientException { + // Topic: databus-sync-system-user-create-company-a + String topic = eventType.getTopic(topicBase, clientCode); + // ConsumerGroup: databus-client-company-a-system-user-create + String consumerGroup = String.format("%s-%s", consumerGroupPrefix, eventType.getTopicSuffix()); + + DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(consumerGroup); + consumer.setNamesrvAddr(nameServer); + consumer.setConsumeThreadMin(properties.getMq().getConsumeThreadMin()); + consumer.setConsumeThreadMax(properties.getMq().getConsumeThreadMax()); + consumer.setMaxReconsumeTimes(properties.getMq().getMaxReconsumeTimes()); + + // 订阅Topic + consumer.subscribe(topic, "*"); + + // 设置消息监听器 + consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> { + for (MessageExt msg : msgs) { + try { + String messageBody = new String(msg.getBody(), StandardCharsets.UTF_8); + log.debug("[Databus Client] 收到消息, topic={}, msgId={}, eventType={}", + msg.getTopic(), msg.getMsgId(), eventType.name()); + + messageProcessor.process(messageBody, eventType); + + } catch (Exception e) { + log.error("[Databus Client] 消息处理失败, topic={}, msgId={}, reconsumeTimes={}", + msg.getTopic(), msg.getMsgId(), msg.getReconsumeTimes(), e); + + if (msg.getReconsumeTimes() >= properties.getMq().getMaxReconsumeTimes()) { + log.error("[Databus Client] 重试次数已达上限,放弃处理, msgId={}", msg.getMsgId()); + return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; + } + return ConsumeConcurrentlyStatus.RECONSUME_LATER; + } + } + return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; + }); + + consumer.start(); + consumers.add(consumer); + + log.info("[Databus Client] 消费者启动成功, topic={}, consumerGroup={}, event={}", + topic, consumerGroup, eventType.getName()); + } + + @PreDestroy + public void destroy() { + for (DefaultMQPushConsumer consumer : consumers) { + try { + consumer.shutdown(); + } catch (Exception e) { + log.error("[Databus Client] 关闭消费者失败", e); + } + } + consumers.clear(); + log.info("[Databus Client] 所有消费者已关闭"); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/message/SyncMessage.java b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/message/SyncMessage.java new file mode 100644 index 00000000..00d00ce0 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/message/SyncMessage.java @@ -0,0 +1,54 @@ +package com.zt.plat.framework.databus.client.core.message; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 数据同步消息(服务端推送格式) + * + * @author ZT + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SyncMessage { + + /** + * 同步ID(唯一标识) + */ + private String syncId; + + /** + * 事件记录ID + */ + private Long eventRecordId; + + /** + * 事件类型 + */ + private String eventType; + + /** + * 事件动作 + */ + private String eventAction; + + /** + * 业务数据快照(JSON字符串) + */ + private String dataSnapshot; + + /** + * 数据版本 + */ + private Integer dataVersion; + + /** + * 时间戳 + */ + private Long timestamp; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/processor/MessageProcessor.java b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/processor/MessageProcessor.java new file mode 100644 index 00000000..8f67e409 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/core/processor/MessageProcessor.java @@ -0,0 +1,236 @@ +package com.zt.plat.framework.databus.client.core.processor; + +import cn.hutool.json.JSONUtil; +import com.zt.plat.framework.databus.client.config.DatabusSyncClientProperties; +import com.zt.plat.framework.databus.client.core.idempotent.IdempotentStore; +import com.zt.plat.framework.databus.client.core.message.SyncMessage; +import com.zt.plat.framework.databus.client.handler.BatchSyncEventHandler; +import com.zt.plat.framework.databus.client.handler.SyncEventHandler; +import com.zt.plat.module.databus.api.message.DatabusBatchMessage; +import com.zt.plat.module.databus.api.message.DatabusMessage; +import com.zt.plat.module.databus.enums.DatabusEventType; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 消息处理器 + *

+ * 负责消息的幂等检查和路由到具体的Handler + * + * @author ZT + */ +@Slf4j +@Component +@SuppressWarnings({"rawtypes", "unchecked"}) +public class MessageProcessor { + + private final IdempotentStore idempotentStore; + private final DatabusSyncClientProperties properties; + + /** + * 增量同步处理器列表 + */ + private List handlers = new ArrayList<>(); + + /** + * 全量同步处理器列表 + */ + private List batchHandlers = new ArrayList<>(); + + private Map handlerMap; + private Map batchHandlerMap; + + public MessageProcessor(IdempotentStore idempotentStore, DatabusSyncClientProperties properties) { + this.idempotentStore = idempotentStore; + this.properties = properties; + } + + @Autowired(required = false) + public void setHandlers(List handlers) { + if (handlers != null) { + this.handlers = handlers; + } + } + + @Autowired(required = false) + public void setBatchHandlers(List batchHandlers) { + if (batchHandlers != null) { + this.batchHandlers = batchHandlers; + } + } + + /** + * 处理同步消息 + * + * @param messageJson 消息JSON字符串 + * @param eventType 事件类型枚举 + */ + public void process(String messageJson, DatabusEventType eventType) { + try { + if (eventType.isFullSync()) { + processBatchMessage(messageJson, eventType); + } else { + processIncrementalMessage(messageJson, eventType); + } + } catch (Exception e) { + log.error("[Databus Client] 消息处理失败, eventType={}, message={}", eventType.name(), messageJson, e); + throw new RuntimeException("消息处理失败", e); + } + } + + /** + * 处理增量同步消息 + */ + private void processIncrementalMessage(String messageJson, DatabusEventType eventType) { + // 先解析为服务端推送的 SyncMessage 格式 + SyncMessage syncMessage = JSONUtil.toBean(messageJson, SyncMessage.class); + + // 幂等检查 + if (properties.getIdempotent().getEnabled()) { + boolean canProcess = idempotentStore.checkAndMark( + syncMessage.getSyncId(), + properties.getIdempotent().getExpireSeconds() + ); + if (!canProcess) { + log.info("[Databus Client] 消息已处理,跳过, syncId={}", syncMessage.getSyncId()); + return; + } + } + + // 路由到对应的Handler + SyncEventHandler handler = getHandler(eventType); + if (handler == null) { + log.warn("[Databus Client] 未找到事件处理器, eventType={}", eventType.name()); + return; + } + + // 获取 Handler 期望的数据类型,并转换消息 + Class dataType = handler.getDataType(); + DatabusMessage message = convertToDatabusMessage(syncMessage, eventType, dataType); + handler.handle(message); + log.info("[Databus Client] 增量消息处理成功, syncId={}, eventType={}", + syncMessage.getSyncId(), eventType.name()); + } + + /** + * 将 SyncMessage 转换为 DatabusMessage + * + * @param syncMessage 服务端推送的同步消息 + * @param eventType 事件类型 + * @param dataType Handler 期望的数据类型 + * @return DatabusMessage + */ + private DatabusMessage convertToDatabusMessage(SyncMessage syncMessage, DatabusEventType eventType, Class dataType) { + DatabusMessage message = new DatabusMessage(); + message.setMessageId(syncMessage.getSyncId()); + message.setEventType(eventType); + + // 从 dataSnapshot JSON 字符串解析业务数据对象 + if (syncMessage.getDataSnapshot() != null && !syncMessage.getDataSnapshot().isEmpty()) { + // 使用 Handler 提供的类型进行反序列化 + Object data = JSONUtil.toBean(syncMessage.getDataSnapshot(), dataType); + message.setData(data); + } + + // 设置时间戳 + if (syncMessage.getTimestamp() != null) { + message.setTimestamp(java.time.LocalDateTime.ofInstant( + java.time.Instant.ofEpochMilli(syncMessage.getTimestamp()), + java.time.ZoneId.systemDefault() + )); + } + + return message; + } + + /** + * 处理全量同步批量消息 + */ + private void processBatchMessage(String messageJson, DatabusEventType eventType) { + DatabusBatchMessage message = JSONUtil.toBean(messageJson, DatabusBatchMessage.class); + + // 幂等检查 + if (properties.getIdempotent().getEnabled()) { + boolean canProcess = idempotentStore.checkAndMark( + message.getMessageId(), + properties.getIdempotent().getExpireSeconds() + ); + if (!canProcess) { + log.info("[Databus Client] 批量消息已处理,跳过, messageId={}", message.getMessageId()); + return; + } + } + + // 路由到对应的BatchHandler + BatchSyncEventHandler handler = getBatchHandler(eventType); + if (handler == null) { + log.warn("[Databus Client] 未找到批量事件处理器, eventType={}", eventType.name()); + return; + } + + // 全量同步开始回调(第一批) + if (message.getBatchNo() != null && message.getBatchNo() == 1) { + handler.onFullSyncStart(message); + log.info("[Databus Client] 全量同步开始, taskId={}, eventType={}, totalCount={}", + message.getTaskId(), eventType.name(), message.getTotalCount()); + } + + // 执行批量处理 + handler.handleBatch(message); + log.info("[Databus Client] 批量消息处理成功, messageId={}, eventType={}, batchNo={}/{}, count={}", + message.getMessageId(), eventType.name(), + message.getBatchNo(), message.getTotalBatch(), message.getCount()); + + // 全量同步完成回调(最后一批) + if (Boolean.TRUE.equals(message.getIsLastBatch())) { + handler.onFullSyncComplete(message); + log.info("[Databus Client] 全量同步完成, taskId={}, eventType={}, totalCount={}", + message.getTaskId(), eventType.name(), message.getTotalCount()); + } + } + + /** + * 获取增量同步事件处理器 + */ + private SyncEventHandler getHandler(DatabusEventType eventType) { + if (handlerMap == null) { + if (handlers == null || handlers.isEmpty()) { + handlerMap = Collections.emptyMap(); + } else { + handlerMap = handlers.stream() + .collect(Collectors.toMap( + SyncEventHandler::getSupportedEventType, + Function.identity() + )); + } + } + return handlerMap.get(eventType); + } + + /** + * 获取全量同步批量事件处理器 + */ + private BatchSyncEventHandler getBatchHandler(DatabusEventType eventType) { + if (batchHandlerMap == null) { + if (batchHandlers == null || batchHandlers.isEmpty()) { + batchHandlerMap = Collections.emptyMap(); + } else { + batchHandlerMap = batchHandlers.stream() + .collect(Collectors.toMap( + BatchSyncEventHandler::getSupportedEventType, + Function.identity() + )); + } + } + return batchHandlerMap.get(eventType); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/handler/BatchSyncEventHandler.java b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/handler/BatchSyncEventHandler.java new file mode 100644 index 00000000..4ab5a573 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/handler/BatchSyncEventHandler.java @@ -0,0 +1,51 @@ +package com.zt.plat.framework.databus.client.handler; + +import com.zt.plat.module.databus.api.message.DatabusBatchMessage; +import com.zt.plat.module.databus.enums.DatabusEventType; + +/** + * 批量同步事件处理器接口 + *

+ * 业务系统需要实现此接口来处理全量同步的批量数据 + * + * @author ZT + */ +public interface BatchSyncEventHandler { + + /** + * 获取支持的事件类型 + * + * @return 事件类型枚举 + */ + DatabusEventType getSupportedEventType(); + + /** + * 处理批量同步消息 + * + * @param message 批量同步消息 + */ + void handleBatch(DatabusBatchMessage message); + + /** + * 全量同步开始回调(可选实现) + *

+ * 当收到第一批数据(batchNo=1)时调用 + * + * @param message 批量同步消息 + */ + default void onFullSyncStart(DatabusBatchMessage message) { + // 默认空实现,子类可覆盖 + } + + /** + * 全量同步完成回调(可选实现) + *

+ * 当收到最后一批数据(isLastBatch=true)时调用 + * + * @param message 批量同步消息 + */ + default void onFullSyncComplete(DatabusBatchMessage message) { + // 默认空实现,子类可覆盖 + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/handler/SyncEventHandler.java b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/handler/SyncEventHandler.java new file mode 100644 index 00000000..f89906d0 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/handler/SyncEventHandler.java @@ -0,0 +1,57 @@ +package com.zt.plat.framework.databus.client.handler; + +import com.zt.plat.module.databus.api.message.DatabusMessage; +import com.zt.plat.module.databus.enums.DatabusEventType; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +/** + * 同步事件处理器接口 + *

+ * 业务系统需要实现此接口来处理增量数据同步 + * + * @author ZT + */ +public interface SyncEventHandler { + + /** + * 获取支持的事件类型 + * + * @return 事件类型枚举 + */ + DatabusEventType getSupportedEventType(); + + /** + * 处理同步消息 + * + * @param message 同步消息 + */ + void handle(DatabusMessage message); + + /** + * 获取数据类型 + *

+ * 默认通过反射获取泛型类型参数,子类可以覆盖此方法提供具体类型 + * + * @return 数据类型的 Class 对象 + */ + @SuppressWarnings("unchecked") + default Class getDataType() { + Type[] genericInterfaces = this.getClass().getGenericInterfaces(); + for (Type genericInterface : genericInterfaces) { + if (genericInterface instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) genericInterface; + if (parameterizedType.getRawType().equals(SyncEventHandler.class)) { + Type[] typeArguments = parameterizedType.getActualTypeArguments(); + if (typeArguments.length > 0 && typeArguments[0] instanceof Class) { + return (Class) typeArguments[0]; + } + } + } + } + // 如果无法获取泛型类型,返回 Object.class + return (Class) Object.class; + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/handler/post/PostSyncService.java b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/handler/post/PostSyncService.java new file mode 100644 index 00000000..f42330a9 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/handler/post/PostSyncService.java @@ -0,0 +1,108 @@ +package com.zt.plat.framework.databus.client.handler.post; + +import com.zt.plat.module.databus.api.data.PostData; +import com.zt.plat.module.system.api.dept.PostApi; +import com.zt.plat.module.system.api.dept.dto.PostSaveReqDTO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; + +/** + * 岗位同步业务逻辑 + *

+ * 被各个 PostHandler 共享使用 + * + * @author ZT + */ +@Slf4j +@Service +@ConditionalOnProperty(prefix = "zt.databus.sync.client", name = "enabled", havingValue = "true") +@ConditionalOnClass(name = "com.zt.plat.module.system.api.dept.PostApi") +public class PostSyncService { + + @Autowired(required = false) + private PostApi postApi; + + /** + * 创建岗位 + */ + public void create(PostData data) { + if (postApi == null) { + log.warn("[PostSync] PostApi未注入,跳过创建岗位操作, postId={}", data.getId()); + return; + } + PostSaveReqDTO dto = buildPostDTO(data); + postApi.createPost(dto).checkError(); + log.info("[PostSync] 创建岗位成功, postId={}, postName={}", dto.getId(), dto.getName()); + } + + /** + * 更新岗位 + */ + public void update(PostData data) { + if (postApi == null) { + log.warn("[PostSync] PostApi未注入,跳过更新岗位操作, postId={}", data.getId()); + return; + } + PostSaveReqDTO dto = buildPostDTO(data); + postApi.updatePost(dto).checkError(); + log.info("[PostSync] 更新岗位成功, postId={}, postName={}", dto.getId(), dto.getName()); + } + + /** + * 删除岗位 + */ + public void delete(PostData data) { + if (postApi == null) { + log.warn("[PostSync] PostApi未注入,跳过删除岗位操作, postId={}", data.getId()); + return; + } + Long postId = data.getId(); + if (postId != null) { + postApi.deletePost(postId).checkError(); + log.info("[PostSync] 删除岗位成功, postId={}", postId); + } + } + + /** + * 全量同步单条数据(存在则更新,不存在则创建) + */ + public void fullSync(PostData data) { + if (postApi == null) { + log.warn("[PostSync] PostApi未注入,跳过全量同步岗位操作, postId={}", data.getId()); + return; + } + PostSaveReqDTO dto = buildPostDTO(data); + try { + if (dto.getId() != null) { + var existing = postApi.getPost(dto.getId()); + if (existing.isSuccess() && existing.getData() != null) { + postApi.updatePost(dto).checkError(); + } else { + postApi.createPost(dto).checkError(); + } + } else { + postApi.createPost(dto).checkError(); + } + } catch (Exception e) { + postApi.createPost(dto).checkError(); + } + } + + /** + * 构建岗位DTO + */ + private PostSaveReqDTO buildPostDTO(PostData data) { + PostSaveReqDTO dto = new PostSaveReqDTO(); + dto.setId(data.getId()); + dto.setCode(data.getCode()); + dto.setName(data.getName()); + dto.setSort(data.getSort()); + dto.setStatus(data.getStatus()); + dto.setRemark(data.getRemark()); + return dto; + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/handler/post/SystemPostFullHandler.java b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/handler/post/SystemPostFullHandler.java new file mode 100644 index 00000000..50727a74 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-client/src/main/java/com/zt/plat/framework/databus/client/handler/post/SystemPostFullHandler.java @@ -0,0 +1,71 @@ +package com.zt.plat.framework.databus.client.handler.post; + +import com.zt.plat.framework.databus.client.handler.BatchSyncEventHandler; +import com.zt.plat.module.databus.api.data.PostData; +import com.zt.plat.module.databus.api.message.DatabusBatchMessage; +import com.zt.plat.module.databus.enums.DatabusEventType; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +/** + * 岗位全量同步事件处理器 + * + * @author ZT + */ +@Slf4j +@Component +@ConditionalOnProperty(prefix = "zt.databus.sync.client", name = "enabled", havingValue = "true") +@ConditionalOnBean(PostSyncService.class) +public class SystemPostFullHandler implements BatchSyncEventHandler { + + @Resource + private PostSyncService postSyncService; + + @Override + public DatabusEventType getSupportedEventType() { + return DatabusEventType.SYSTEM_POST_FULL; + } + + @Override + public void onFullSyncStart(DatabusBatchMessage message) { + log.info("[PostSync] 全量同步开始, taskId={}, totalCount={}, totalBatch={}", + message.getTaskId(), message.getTotalCount(), message.getTotalBatch()); + } + + @Override + public void handleBatch(DatabusBatchMessage message) { + log.info("[PostSync] 处理批次, batchNo={}/{}, count={}", + message.getBatchNo(), message.getTotalBatch(), message.getCount()); + + if (message.getDataList() == null || message.getDataList().isEmpty()) { + log.warn("[PostSync] 数据列表为空, batchNo={}", message.getBatchNo()); + return; + } + + int successCount = 0; + int failCount = 0; + + for (PostData data : message.getDataList()) { + try { + postSyncService.fullSync(data); + successCount++; + } catch (Exception e) { + failCount++; + log.error("[PostSync] 处理数据项失败, postId={}", data.getId(), e); + } + } + + log.info("[PostSync] 批次处理完成, batchNo={}, success={}, fail={}", + message.getBatchNo(), successCount, failCount); + } + + @Override + public void onFullSyncComplete(DatabusBatchMessage message) { + log.info("[PostSync] 全量同步完成, taskId={}, totalCount={}", + message.getTaskId(), message.getTotalCount()); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-client/src/main/resources/META-INF/spring.factories b/zt-framework/zt-spring-boot-starter-databus-client/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..9b570f2a --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-client/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.zt.plat.framework.databus.client.config.DatabusSyncClientAutoConfiguration diff --git a/zt-framework/zt-spring-boot-starter-databus-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/zt-framework/zt-spring-boot-starter-databus-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..50f5c6d4 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.zt.plat.framework.databus.client.config.DatabusSyncClientAutoConfiguration diff --git a/zt-framework/zt-spring-boot-starter-databus-server/pom.xml b/zt-framework/zt-spring-boot-starter-databus-server/pom.xml new file mode 100644 index 00000000..59150712 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/pom.xml @@ -0,0 +1,73 @@ + + + + com.zt.plat + zt-framework + ${revision} + + 4.0.0 + zt-spring-boot-starter-databus-server + jar + + ${project.artifactId} + DataBus 服务端组件,负责发送数据变更消息到各客户端 + + + + com.zt.plat + zt-common + + + + + com.zt.plat + zt-module-databus-api + ${revision} + + + + cn.hutool + hutool-all + + + + + com.zt.plat + zt-spring-boot-starter-mq + + + + + com.zt.plat + zt-spring-boot-starter-mybatis + + + + + com.zt.plat + zt-spring-boot-starter-web + + + + + com.zt.plat + zt-spring-boot-starter-security + + + + + com.zt.plat + zt-spring-boot-starter-biz-tenant + + + + + com.zt.plat + zt-spring-boot-starter-test + test + + + + diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusMessagingConfiguration.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusMessagingConfiguration.java new file mode 100644 index 00000000..48e7d0cb --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusMessagingConfiguration.java @@ -0,0 +1,27 @@ +package com.zt.plat.framework.databus.server.config; + +import com.zt.plat.framework.databus.server.core.pusher.MessagePusher; +import com.zt.plat.framework.databus.server.core.pusher.MessagePusherImpl; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Databus 消息配置类 + * + * @author ZT + */ +@Configuration +public class DatabusMessagingConfiguration { + + @Bean + @ConditionalOnMissingBean + public MessagePusher messagePusher(@Autowired(required = false) RocketMQTemplate rocketMQTemplate) { + MessagePusherImpl pusher = new MessagePusherImpl(); + pusher.setRocketMQTemplate(rocketMQTemplate); + return pusher; + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusServerAutoConfiguration.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusServerAutoConfiguration.java new file mode 100644 index 00000000..7444745e --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusServerAutoConfiguration.java @@ -0,0 +1,24 @@ +package com.zt.plat.framework.databus.server.config; + +import com.zt.plat.framework.databus.server.producer.DatabusMessageProducer; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Databus 服务端自动配置 + * + * @author ZT + */ +@Configuration +@EnableConfigurationProperties(DatabusServerProperties.class) +@ConditionalOnProperty(prefix = "zt.databus.sync.server", name = "enabled", havingValue = "true") +public class DatabusServerAutoConfiguration { + + @Bean + public DatabusMessageProducer databusMessageProducer(DatabusServerProperties properties) { + return new DatabusMessageProducer(properties); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusServerProperties.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusServerProperties.java new file mode 100644 index 00000000..22836f48 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusServerProperties.java @@ -0,0 +1,60 @@ +package com.zt.plat.framework.databus.server.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.List; + +/** + * Databus 服务端配置属性 + * + * @author ZT + */ +@Data +@ConfigurationProperties(prefix = "zt.databus.sync.server") +public class DatabusServerProperties { + + /** + * 是否启用 + */ + private boolean enabled = false; + + /** + * MQ 配置 + */ + private MqConfig mq = new MqConfig(); + + /** + * 订阅的客户端列表 + */ + private List clients; + + @Data + public static class MqConfig { + /** + * 是否启用 MQ 发送 + */ + private boolean enabled = true; + + /** + * RocketMQ NameServer 地址 + */ + private String nameServer; + + /** + * Topic 基础名称 + */ + private String topicBase = "databus-sync"; + + /** + * 生产者组名称 + */ + private String producerGroup = "databus-server-producer"; + /** + * 发送超时时间(毫秒) + */ + private int sendMsgTimeout = 10000; // 默认 10 秒 + + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusSyncServerAutoConfiguration.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusSyncServerAutoConfiguration.java new file mode 100644 index 00000000..1c929812 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusSyncServerAutoConfiguration.java @@ -0,0 +1,45 @@ +package com.zt.plat.framework.databus.server.config; + +import com.zt.plat.framework.databus.server.core.pusher.MessagePusher; +import com.zt.plat.framework.databus.server.core.pusher.MessagePusherImpl; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; + +/** + * Databus 同步服务端自动配置 + * + * @author ZT + */ +@Slf4j +@AutoConfiguration +@EnableAsync +@EnableConfigurationProperties(DatabusSyncServerProperties.class) +@ComponentScan(basePackages = "com.zt.plat.framework.databus.server") +@MapperScan("com.zt.plat.framework.databus.server.dal.mapper") +public class DatabusSyncServerAutoConfiguration { + + public DatabusSyncServerAutoConfiguration() { + log.info("[Databus] 数据同步服务端模块已加载"); + } + + /** + * 注册消息推送器 Bean + */ + @Bean + @ConditionalOnBean(RocketMQTemplate.class) + public MessagePusher messagePusher(RocketMQTemplate rocketMQTemplate) { + MessagePusherImpl pusher = new MessagePusherImpl(); + pusher.setRocketMQTemplate(rocketMQTemplate); + log.info("[Databus] MessagePusher Bean 已注册"); + return pusher; + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusSyncServerProperties.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusSyncServerProperties.java new file mode 100644 index 00000000..f1cba915 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/config/DatabusSyncServerProperties.java @@ -0,0 +1,61 @@ +package com.zt.plat.framework.databus.server.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Databus 数据同步服务端配置属性 + * + * @author ZT + */ +@Data +@ConfigurationProperties(prefix = "zt.databus.sync.server") +public class DatabusSyncServerProperties { + + /** + * 是否启用 + */ + private Boolean enabled = true; + + /** + * 重试配置 + */ + private Retry retry = new Retry(); + + /** + * 批量推送配置 + */ + private Batch batch = new Batch(); + + @Data + public static class Retry { + /** + * 最大重试次数 + */ + private Integer maxAttempts = 5; + + /** + * 初始重试延迟(秒) + */ + private Integer initialDelay = 1; + + /** + * 重试延迟倍数 + */ + private Integer multiplier = 2; + } + + @Data + public static class Batch { + /** + * 默认批量大小 + */ + private Integer defaultSize = 500; + + /** + * 批量推送间隔(秒) + */ + private Integer interval = 5; + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncClientController.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncClientController.java new file mode 100644 index 00000000..e758be19 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncClientController.java @@ -0,0 +1,92 @@ +package com.zt.plat.framework.databus.server.controller.admin; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientPageReqVO; +import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientRespVO; +import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientSaveReqVO; +import com.zt.plat.framework.databus.server.convert.DatabusSyncClientConvert; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncClientDO; +import com.zt.plat.framework.databus.server.service.DatabusSyncClientService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 数据同步客户端") +@RestController +@RequestMapping("/databus/sync/client") +@Validated +public class DatabusSyncClientController { + + @Resource + private DatabusSyncClientService clientService; + + @PostMapping("/create") + @Operation(summary = "创建数据同步客户端") + @PreAuthorize("@ss.hasPermission('databus:sync:client:create')") + public CommonResult createClient(@Valid @RequestBody DatabusSyncClientSaveReqVO createReqVO) { + return success(clientService.createClient(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新数据同步客户端") + @PreAuthorize("@ss.hasPermission('databus:sync:client:update')") + public CommonResult updateClient(@Valid @RequestBody DatabusSyncClientSaveReqVO updateReqVO) { + clientService.updateClient(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除数据同步客户端") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('databus:sync:client:delete')") + public CommonResult deleteClient(@RequestParam("id") Long id) { + clientService.deleteClient(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得数据同步客户端") + @Parameter(name = "id", description = "编号", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('databus:sync:client:query')") + public CommonResult getClient(@RequestParam("id") Long id) { + DatabusSyncClientDO client = clientService.getClient(id); + return success(DatabusSyncClientConvert.INSTANCE.convert(client)); + } + + @GetMapping("/page") + @Operation(summary = "获得数据同步客户端分页") + @PreAuthorize("@ss.hasPermission('databus:sync:client:query')") + public CommonResult> getClientPage(@Valid DatabusSyncClientPageReqVO pageReqVO) { + PageResult pageResult = clientService.getClientPage(pageReqVO); + return success(DatabusSyncClientConvert.INSTANCE.convertPage(pageResult)); + } + + @GetMapping("/list") + @Operation(summary = "获得数据同步客户端列表") + @PreAuthorize("@ss.hasPermission('databus:sync:client:query')") + public CommonResult> getClientList() { + List list = clientService.getClientList(); + return success(DatabusSyncClientConvert.INSTANCE.convertList(list)); + } + + @PutMapping("/update-status") + @Operation(summary = "修改客户端启用状态") + @PreAuthorize("@ss.hasPermission('databus:sync:client:update')") + public CommonResult updateClientStatus( + @RequestParam("id") Long id, + @RequestParam("enabled") Integer enabled) { + clientService.updateClientStatus(id, enabled); + return success(true); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncDeadLetterController.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncDeadLetterController.java new file mode 100644 index 00000000..7a89b128 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncDeadLetterController.java @@ -0,0 +1,76 @@ +package com.zt.plat.framework.databus.server.controller.admin; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.deadletter.DatabusSyncDeadLetterPageReqVO; +import com.zt.plat.framework.databus.server.controller.admin.vo.deadletter.DatabusSyncDeadLetterRespVO; +import com.zt.plat.framework.databus.server.convert.DatabusSyncDeadLetterConvert; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncDeadLetterDO; +import com.zt.plat.framework.databus.server.service.DatabusSyncDeadLetterService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 数据同步死信队列") +@RestController +@RequestMapping("/databus/sync/dead-letter") +@Validated +public class DatabusSyncDeadLetterController { + + @Resource + private DatabusSyncDeadLetterService deadLetterService; + + @GetMapping("/get") + @Operation(summary = "获得数据同步死信队列") + @Parameter(name = "id", description = "编号", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('databus:sync:dead-letter:query')") + public CommonResult getDeadLetter(@RequestParam("id") Long id) { + DatabusSyncDeadLetterDO deadLetter = deadLetterService.getDeadLetter(id); + return success(DatabusSyncDeadLetterConvert.INSTANCE.convert(deadLetter)); + } + + @GetMapping("/page") + @Operation(summary = "获得数据同步死信队列分页") + @PreAuthorize("@ss.hasPermission('databus:sync:dead-letter:query')") + public CommonResult> getDeadLetterPage(@Valid DatabusSyncDeadLetterPageReqVO pageReqVO) { + PageResult pageResult = deadLetterService.getDeadLetterPage(pageReqVO); + return success(DatabusSyncDeadLetterConvert.INSTANCE.convertPage(pageResult)); + } + + @PostMapping("/reprocess") + @Operation(summary = "重新投递死信消息") + @Parameter(name = "id", description = "死信ID", required = true) + @PreAuthorize("@ss.hasPermission('databus:sync:dead-letter:reprocess')") + public CommonResult reprocessDeadLetter(@RequestParam("id") Long id) { + deadLetterService.reprocessDeadLetter(id); + return success(true); + } + + @PostMapping("/batch-reprocess") + @Operation(summary = "批量重新投递死信消息") + @PreAuthorize("@ss.hasPermission('databus:sync:dead-letter:reprocess')") + public CommonResult batchReprocessDeadLetter(@RequestBody List ids) { + deadLetterService.batchReprocessDeadLetter(ids); + return success(true); + } + + @PutMapping("/mark-handled") + @Operation(summary = "标记为已处理") + @PreAuthorize("@ss.hasPermission('databus:sync:dead-letter:update')") + public CommonResult markHandled( + @RequestParam("id") Long id, + @RequestParam(value = "remark", required = false) String remark) { + deadLetterService.markHandled(id, remark); + return success(true); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncEventController.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncEventController.java new file mode 100644 index 00000000..c74227d2 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncEventController.java @@ -0,0 +1,92 @@ +package com.zt.plat.framework.databus.server.controller.admin; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventPageReqVO; +import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventRespVO; +import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventSaveReqVO; +import com.zt.plat.framework.databus.server.convert.DatabusSyncEventConvert; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncEventDO; +import com.zt.plat.framework.databus.server.service.DatabusSyncEventService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 数据同步事件") +@RestController +@RequestMapping("/databus/sync/event") +@Validated +public class DatabusSyncEventController { + + @Resource + private DatabusSyncEventService eventService; + + @PostMapping("/create") + @Operation(summary = "创建数据同步事件") + @PreAuthorize("@ss.hasPermission('databus:sync:event:create')") + public CommonResult createEvent(@Valid @RequestBody DatabusSyncEventSaveReqVO createReqVO) { + return success(eventService.createEvent(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新数据同步事件") + @PreAuthorize("@ss.hasPermission('databus:sync:event:update')") + public CommonResult updateEvent(@Valid @RequestBody DatabusSyncEventSaveReqVO updateReqVO) { + eventService.updateEvent(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除数据同步事件") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('databus:sync:event:delete')") + public CommonResult deleteEvent(@RequestParam("id") Long id) { + eventService.deleteEvent(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得数据同步事件") + @Parameter(name = "id", description = "编号", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('databus:sync:event:query')") + public CommonResult getEvent(@RequestParam("id") Long id) { + DatabusSyncEventDO event = eventService.getEvent(id); + return success(DatabusSyncEventConvert.INSTANCE.convert(event)); + } + + @GetMapping("/page") + @Operation(summary = "获得数据同步事件分页") + @PreAuthorize("@ss.hasPermission('databus:sync:event:query')") + public CommonResult> getEventPage(@Valid DatabusSyncEventPageReqVO pageReqVO) { + PageResult pageResult = eventService.getEventPage(pageReqVO); + return success(DatabusSyncEventConvert.INSTANCE.convertPage(pageResult)); + } + + @GetMapping("/list") + @Operation(summary = "获得数据同步事件列表") + @PreAuthorize("@ss.hasPermission('databus:sync:event:query')") + public CommonResult> getEventList() { + List list = eventService.getEventList(); + return success(DatabusSyncEventConvert.INSTANCE.convertList(list)); + } + + @PutMapping("/update-status") + @Operation(summary = "修改事件启用状态") + @PreAuthorize("@ss.hasPermission('databus:sync:event:update')") + public CommonResult updateEventStatus( + @RequestParam("id") Long id, + @RequestParam("enabled") Integer enabled) { + eventService.updateEventStatus(id, enabled); + return success(true); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncFullTaskController.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncFullTaskController.java new file mode 100644 index 00000000..cca415c9 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncFullTaskController.java @@ -0,0 +1,102 @@ +package com.zt.plat.framework.databus.server.controller.admin; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.fulltask.DatabusSyncFullTaskCreateReqVO; +import com.zt.plat.framework.databus.server.controller.admin.vo.fulltask.DatabusSyncFullTaskPageReqVO; +import com.zt.plat.framework.databus.server.controller.admin.vo.fulltask.DatabusSyncFullTaskRespVO; +import com.zt.plat.framework.databus.server.convert.DatabusSyncFullTaskConvert; +import com.zt.plat.framework.databus.server.core.sync.DatabusFullSyncService; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncFullTaskDO; +import com.zt.plat.framework.databus.server.dal.mapper.DatabusSyncFullTaskMapper; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; + +/** + * 管理后台 - 数据全量同步任务 + * + * @author ZT + */ +@Tag(name = "管理后台 - 全量同步任务") +@RestController +@RequestMapping("/databus/sync/full-task") +@Validated +public class DatabusSyncFullTaskController { + + @Resource + private DatabusFullSyncService fullSyncService; + + @Resource + private DatabusSyncFullTaskMapper fullTaskMapper; + + @PostMapping("/create") + @Operation(summary = "创建全量同步任务") + @PreAuthorize("@ss.hasPermission('databus:sync:full-task:create')") + public CommonResult createFullTask(@Valid @RequestBody DatabusSyncFullTaskCreateReqVO createReqVO) { + Long taskId = fullSyncService.createFullSyncTask(createReqVO.getSubscriptionId(), createReqVO.getRemark()); + return success(taskId); + } + + @PostMapping("/execute") + @Operation(summary = "执行全量同步任务") + @Parameter(name = "id", description = "任务ID", required = true) + @PreAuthorize("@ss.hasPermission('databus:sync:full-task:execute')") + public CommonResult executeFullTask(@RequestParam("id") Long id) { + fullSyncService.executeFullSyncTask(id); + return success(true); + } + + @PostMapping("/create-and-execute") + @Operation(summary = "创建并执行全量同步任务") + @PreAuthorize("@ss.hasPermission('databus:sync:full-task:create')") + public CommonResult createAndExecuteFullTask(@Valid @RequestBody DatabusSyncFullTaskCreateReqVO createReqVO) { + Long taskId = fullSyncService.createFullSyncTask(createReqVO.getSubscriptionId(), createReqVO.getRemark()); + // 异步执行任务 + fullSyncService.executeFullSyncTask(taskId); + return success(taskId); + } + + @PostMapping("/cancel") + @Operation(summary = "取消全量同步任务") + @Parameter(name = "id", description = "任务ID", required = true) + @PreAuthorize("@ss.hasPermission('databus:sync:full-task:cancel')") + public CommonResult cancelFullTask(@RequestParam("id") Long id) { + fullSyncService.cancelFullSyncTask(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得全量同步任务详情") + @Parameter(name = "id", description = "任务ID", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('databus:sync:full-task:query')") + public CommonResult getFullTask(@RequestParam("id") Long id) { + DatabusSyncFullTaskDO task = fullSyncService.getTaskById(id); + return success(DatabusSyncFullTaskConvert.INSTANCE.convert(task)); + } + + @GetMapping("/get-by-task-no") + @Operation(summary = "根据任务编号获得全量同步任务详情") + @Parameter(name = "taskNo", description = "任务编号", required = true, example = "abc123") + @PreAuthorize("@ss.hasPermission('databus:sync:full-task:query')") + public CommonResult getFullTaskByTaskNo(@RequestParam("taskNo") String taskNo) { + DatabusSyncFullTaskDO task = fullSyncService.getTaskByTaskNo(taskNo); + return success(DatabusSyncFullTaskConvert.INSTANCE.convert(task)); + } + + @GetMapping("/page") + @Operation(summary = "获得全量同步任务分页") + @PreAuthorize("@ss.hasPermission('databus:sync:full-task:query')") + public CommonResult> getFullTaskPage(@Valid DatabusSyncFullTaskPageReqVO pageReqVO) { + PageResult pageResult = fullTaskMapper.selectPage(pageReqVO); + return success(DatabusSyncFullTaskConvert.INSTANCE.convertPage(pageResult)); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncPushLogController.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncPushLogController.java new file mode 100644 index 00000000..52b10746 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncPushLogController.java @@ -0,0 +1,69 @@ +package com.zt.plat.framework.databus.server.controller.admin; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.pushlog.DatabusSyncPushLogPageReqVO; +import com.zt.plat.framework.databus.server.controller.admin.vo.pushlog.DatabusSyncPushLogRespVO; +import com.zt.plat.framework.databus.server.convert.DatabusSyncPushLogConvert; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncEventRecordDO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncLogDO; +import com.zt.plat.framework.databus.server.dal.mapper.DatabusSyncEventRecordMapper; +import com.zt.plat.framework.databus.server.service.DatabusSyncLogService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 数据同步推送日志") +@RestController +@RequestMapping("/databus/sync/push-log") +@Validated +public class DatabusSyncPushLogController { + + @Resource + private DatabusSyncLogService pushLogService; + + @Resource + private DatabusSyncEventRecordMapper eventRecordMapper; + + @GetMapping("/get") + @Operation(summary = "获得数据同步推送日志") + @Parameter(name = "id", description = "编号", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('databus:sync:push-log:query')") + public CommonResult getPushLog(@RequestParam("id") Long id) { + DatabusSyncLogDO pushLog = pushLogService.getPushLog(id); + DatabusSyncPushLogRespVO respVO = DatabusSyncPushLogConvert.INSTANCE.convert(pushLog); + // 关联查询事件记录,获取消息内容 + if (pushLog != null && pushLog.getEventRecordId() != null) { + DatabusSyncEventRecordDO eventRecord = eventRecordMapper.selectById(pushLog.getEventRecordId()); + if (eventRecord != null) { + respVO.setDataSnapshot(eventRecord.getDataSnapshot()); + } + } + return success(respVO); + } + + @GetMapping("/page") + @Operation(summary = "获得数据同步推送日志分页") + @PreAuthorize("@ss.hasPermission('databus:sync:push-log:query')") + public CommonResult> getPushLogPage(@Valid DatabusSyncPushLogPageReqVO pageReqVO) { + PageResult pageResult = pushLogService.getPushLogPage(pageReqVO); + return success(DatabusSyncPushLogConvert.INSTANCE.convertPage(pageResult)); + } + + @PostMapping("/retry") + @Operation(summary = "重试推送") + @Parameter(name = "id", description = "日志ID", required = true) + @PreAuthorize("@ss.hasPermission('databus:sync:push-log:retry')") + public CommonResult retryPush(@RequestParam("id") Long id) { + pushLogService.retryPush(id); + return success(true); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncSubscriptionController.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncSubscriptionController.java new file mode 100644 index 00000000..737f138f --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/DatabusSyncSubscriptionController.java @@ -0,0 +1,100 @@ +package com.zt.plat.framework.databus.server.controller.admin; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionPageReqVO; +import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionRespVO; +import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionSaveReqVO; +import com.zt.plat.framework.databus.server.convert.DatabusSyncSubscriptionConvert; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncSubscriptionDO; +import com.zt.plat.framework.databus.server.service.DatabusSyncSubscriptionService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 数据同步订阅") +@RestController +@RequestMapping("/databus/sync/subscription") +@Validated +public class DatabusSyncSubscriptionController { + + @Resource + private DatabusSyncSubscriptionService subscriptionService; + + @PostMapping("/create") + @Operation(summary = "创建数据同步订阅") + @PreAuthorize("@ss.hasPermission('databus:sync:subscription:create')") + public CommonResult createSubscription(@Valid @RequestBody DatabusSyncSubscriptionSaveReqVO createReqVO) { + return success(subscriptionService.createSubscription(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新数据同步订阅") + @PreAuthorize("@ss.hasPermission('databus:sync:subscription:update')") + public CommonResult updateSubscription(@Valid @RequestBody DatabusSyncSubscriptionSaveReqVO updateReqVO) { + subscriptionService.updateSubscription(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除数据同步订阅") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('databus:sync:subscription:delete')") + public CommonResult deleteSubscription(@RequestParam("id") Long id) { + subscriptionService.deleteSubscription(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得数据同步订阅") + @Parameter(name = "id", description = "编号", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('databus:sync:subscription:query')") + public CommonResult getSubscription(@RequestParam("id") Long id) { + DatabusSyncSubscriptionDO subscription = subscriptionService.getSubscription(id); + return success(DatabusSyncSubscriptionConvert.INSTANCE.convert(subscription)); + } + + @GetMapping("/page") + @Operation(summary = "获得数据同步订阅分页") + @PreAuthorize("@ss.hasPermission('databus:sync:subscription:query')") + public CommonResult> getSubscriptionPage(@Valid DatabusSyncSubscriptionPageReqVO pageReqVO) { + PageResult pageResult = subscriptionService.getSubscriptionPage(pageReqVO); + return success(DatabusSyncSubscriptionConvert.INSTANCE.convertPage(pageResult)); + } + + @PutMapping("/update-status") + @Operation(summary = "修改订阅启用状态") + @PreAuthorize("@ss.hasPermission('databus:sync:subscription:update')") + public CommonResult updateSubscriptionStatus( + @RequestParam("id") Long id, + @RequestParam("enabled") Integer enabled) { + subscriptionService.updateSubscriptionStatus(id, enabled); + return success(true); + } + + @PutMapping("/reset-checkpoint") + @Operation(summary = "重置订阅断点") + @Parameter(name = "id", description = "订阅ID", required = true) + @PreAuthorize("@ss.hasPermission('databus:sync:subscription:update')") + public CommonResult resetCheckpoint(@RequestParam("id") Long id) { + subscriptionService.resetCheckpoint(id); + return success(true); + } + + @PostMapping("/trigger-sync") + @Operation(summary = "手动触发同步") + @Parameter(name = "id", description = "订阅ID", required = true) + @PreAuthorize("@ss.hasPermission('databus:sync:subscription:trigger')") + public CommonResult triggerSync(@RequestParam("id") Long id) { + subscriptionService.triggerSync(id); + return success(true); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/client/DatabusSyncClientPageReqVO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/client/DatabusSyncClientPageReqVO.java new file mode 100644 index 00000000..896bf61e --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/client/DatabusSyncClientPageReqVO.java @@ -0,0 +1,27 @@ +package com.zt.plat.framework.databus.server.controller.admin.vo.client; + +import com.zt.plat.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - 数据同步客户端分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DatabusSyncClientPageReqVO extends PageParam { + + @Schema(description = "客户端编码", example = "company-a") + private String clientCode; + + @Schema(description = "客户端名称", example = "A分公司") + private String clientName; + + @Schema(description = "启用状态(0-禁用 1-启用)", example = "1") + private Integer enabled; + + @Schema(description = "通信方式(MQ_FIRST-MQ优先 HTTP_ONLY-仅HTTP)", example = "MQ_FIRST") + private String transportType; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/client/DatabusSyncClientRespVO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/client/DatabusSyncClientRespVO.java new file mode 100644 index 00000000..fcf35f7c --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/client/DatabusSyncClientRespVO.java @@ -0,0 +1,63 @@ +package com.zt.plat.framework.databus.server.controller.admin.vo.client; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 数据同步客户端 Response VO") +@Data +public class DatabusSyncClientRespVO { + + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "租户ID", example = "1") + private Long tenantId; + + @Schema(description = "客户端编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "company-a") + private String clientCode; + + @Schema(description = "客户端名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "A分公司") + private String clientName; + + @Schema(description = "启用状态(0-禁用 1-启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer enabled; + + @Schema(description = "通信方式(MQ_FIRST-MQ优先 HTTP_ONLY-仅HTTP)", requiredMode = Schema.RequiredMode.REQUIRED, example = "MQ_FIRST") + private String transportType; + + @Schema(description = "MQ是否启用(0-否 1-是)", example = "1") + private Integer mqEnabled; + + @Schema(description = "MQ NameServer地址", example = "localhost:9876") + private String mqNamesrvAddr; + + @Schema(description = "MQ Topic基础名称", example = "databus-sync") + private String mqTopicBase; + + @Schema(description = "HTTP是否启用(0-否 1-是)", example = "1") + private Integer httpEnabled; + + @Schema(description = "HTTP推送端点", example = "http://example.com/databus/sync/receive") + private String httpEndpoint; + + @Schema(description = "应用Key", example = "app-key-001") + private String appKey; + + @Schema(description = "应用Secret", example = "***") + private String appSecret; + + @Schema(description = "创建者", example = "admin") + private String creator; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "更新者", example = "admin") + private String updater; + + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime updateTime; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/client/DatabusSyncClientSaveReqVO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/client/DatabusSyncClientSaveReqVO.java new file mode 100644 index 00000000..475c9f7b --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/client/DatabusSyncClientSaveReqVO.java @@ -0,0 +1,55 @@ +package com.zt.plat.framework.databus.server.controller.admin.vo.client; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; + +@Schema(description = "管理后台 - 数据同步客户端创建/修改 Request VO") +@Data +public class DatabusSyncClientSaveReqVO { + + @Schema(description = "主键ID", example = "1") + private Long id; + + @Schema(description = "客户端编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "company-a") + @NotBlank(message = "客户端编码不能为空") + @Pattern(regexp = "^[a-z0-9-]+$", message = "客户端编码只能包含小写字母、数字和短横线") + private String clientCode; + + @Schema(description = "客户端名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "A分公司") + @NotBlank(message = "客户端名称不能为空") + private String clientName; + + @Schema(description = "启用状态(0-禁用 1-启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "启用状态不能为空") + private Integer enabled; + + @Schema(description = "通信方式(MQ_FIRST-MQ优先 HTTP_ONLY-仅HTTP)", requiredMode = Schema.RequiredMode.REQUIRED, example = "MQ_FIRST") + @NotBlank(message = "通信方式不能为空") + private String transportType; + + @Schema(description = "MQ是否启用(0-否 1-是)", example = "1") + private Integer mqEnabled; + + @Schema(description = "MQ NameServer地址", example = "localhost:9876") + private String mqNamesrvAddr; + + @Schema(description = "MQ Topic基础名称", example = "databus-sync") + private String mqTopicBase; + + @Schema(description = "HTTP是否启用(0-否 1-是)", example = "1") + private Integer httpEnabled; + + @Schema(description = "HTTP推送端点", example = "http://example.com/databus/sync/receive") + private String httpEndpoint; + + @Schema(description = "应用Key", example = "app-key-001") + private String appKey; + + @Schema(description = "应用Secret(加密存储)", example = "secret") + private String appSecret; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/deadletter/DatabusSyncDeadLetterPageReqVO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/deadletter/DatabusSyncDeadLetterPageReqVO.java new file mode 100644 index 00000000..d07889cc --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/deadletter/DatabusSyncDeadLetterPageReqVO.java @@ -0,0 +1,43 @@ +package com.zt.plat.framework.databus.server.controller.admin.vo.deadletter; + +import com.zt.plat.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 数据同步死信队列分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DatabusSyncDeadLetterPageReqVO extends PageParam { + + @Schema(description = "同步ID", example = "sync-20231124-001") + private String syncId; + + @Schema(description = "客户端编码", example = "company-a") + private String clientCode; + + @Schema(description = "事件类型", example = "user-changed") + private String eventType; + + @Schema(description = "状态(PENDING-待处理 REDELIVERED-已重新投递 IGNORED-已忽略)", example = "PENDING") + private String status; + + @Schema(description = "是否已处理(0-否 1-是)", example = "0") + private Integer handled; + + @Schema(description = "创建时间开始", example = "2023-11-24 00:00:00") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime createTimeStart; + + @Schema(description = "创建时间结束", example = "2023-11-24 23:59:59") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime createTimeEnd; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/deadletter/DatabusSyncDeadLetterRespVO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/deadletter/DatabusSyncDeadLetterRespVO.java new file mode 100644 index 00000000..39f29bfb --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/deadletter/DatabusSyncDeadLetterRespVO.java @@ -0,0 +1,63 @@ +package com.zt.plat.framework.databus.server.controller.admin.vo.deadletter; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 数据同步死信队列 Response VO") +@Data +public class DatabusSyncDeadLetterRespVO { + + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "同步ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "sync-20231124-001") + private String syncId; + + @Schema(description = "同步日志ID", example = "10001") + private Long syncLogId; + + @Schema(description = "客户端编码", example = "company-a") + private String clientCode; + + @Schema(description = "事件类型", example = "user-changed") + private String eventType; + + @Schema(description = "消息体(JSON)", example = "{\"userId\":1001,\"userName\":\"张三\"}") + private String messageBody; + + @Schema(description = "状态(PENDING-待处理 REDELIVERED-已重新投递 IGNORED-已忽略)", example = "PENDING") + private String status; + + @Schema(description = "失败原因", example = "连接超时") + private String lastErrorMessage; + + @Schema(description = "失败时间") + private LocalDateTime lastErrorTime; + + @Schema(description = "重试次数", example = "5") + private Integer retryCount; + + @Schema(description = "是否已处理(0-否 1-是)", example = "0") + private Integer handled; + + @Schema(description = "处理时间") + private LocalDateTime handleTime; + + @Schema(description = "处理人", example = "admin") + private String handler; + + @Schema(description = "处理备注", example = "已通知开发人员修复") + private String handleRemark; + + @Schema(description = "租户ID", example = "1") + private Long tenantId; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime updateTime; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/event/DatabusSyncEventPageReqVO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/event/DatabusSyncEventPageReqVO.java new file mode 100644 index 00000000..708bb89a --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/event/DatabusSyncEventPageReqVO.java @@ -0,0 +1,24 @@ +package com.zt.plat.framework.databus.server.controller.admin.vo.event; + +import com.zt.plat.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - 数据同步事件分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DatabusSyncEventPageReqVO extends PageParam { + + @Schema(description = "事件类型编码", example = "user-changed") + private String eventType; + + @Schema(description = "事件名称", example = "用户信息变更") + private String eventName; + + @Schema(description = "启用状态(0-禁用 1-启用)", example = "1") + private Integer enabled; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/event/DatabusSyncEventRespVO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/event/DatabusSyncEventRespVO.java new file mode 100644 index 00000000..6dfd2d9e --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/event/DatabusSyncEventRespVO.java @@ -0,0 +1,54 @@ +package com.zt.plat.framework.databus.server.controller.admin.vo.event; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 数据同步事件 Response VO") +@Data +public class DatabusSyncEventRespVO { + + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "租户ID", example = "1") + private Long tenantId; + + @Schema(description = "事件类型编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "system-user-full") + private String eventType; + + @Schema(description = "事件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "用户信息变更") + private String eventName; + + @Schema(description = "事件描述", example = "用户基本信息变更事件") + private String eventDesc; + + @Schema(description = "启用状态(0-禁用 1-启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer enabled; + + @Schema(description = "是否支持全量同步(0-否 1-是)", example = "1") + private Integer supportFullSync; + + @Schema(description = "是否支持增量同步(0-否 1-是)", example = "1") + private Integer supportIncrementalSync; + + @Schema(description = "数据结构版本", example = "1") + private Integer dataVersion; + + @Schema(description = "数据提供者类型(全量同步使用)", example = "ORG") + private String dataProviderMethod; + + @Schema(description = "创建者", example = "admin") + private String creator; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "更新者", example = "admin") + private String updater; + + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime updateTime; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/event/DatabusSyncEventSaveReqVO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/event/DatabusSyncEventSaveReqVO.java new file mode 100644 index 00000000..578854d8 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/event/DatabusSyncEventSaveReqVO.java @@ -0,0 +1,45 @@ +package com.zt.plat.framework.databus.server.controller.admin.vo.event; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; + +@Schema(description = "管理后台 - 数据同步事件创建/修改 Request VO") +@Data +public class DatabusSyncEventSaveReqVO { + + @Schema(description = "主键ID", example = "1") + private Long id; + + @Schema(description = "事件类型编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "system-user-full") + @NotBlank(message = "事件类型编码不能为空") + @Pattern(regexp = "^[a-z0-9-]+$", message = "事件类型编码只能包含小写字母、数字和短横线") + private String eventType; + + @Schema(description = "事件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "用户信息变更") + @NotBlank(message = "事件名称不能为空") + private String eventName; + + @Schema(description = "事件描述", example = "用户基本信息变更事件") + private String eventDesc; + + @Schema(description = "启用状态(0-禁用 1-启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "启用状态不能为空") + private Integer enabled; + + @Schema(description = "数据提供者类型(全量同步使用)", example = "ORG") + private String dataProviderMethod; + + @Schema(description = "是否支持全量同步(0-否 1-是)", example = "1") + private Integer supportFullSync; + + @Schema(description = "是否支持增量同步(0-否 1-是)", example = "1") + private Integer supportIncrementalSync; + + @Schema(description = "数据结构版本", example = "1") + private Integer dataVersion; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/fulltask/DatabusSyncFullTaskCreateReqVO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/fulltask/DatabusSyncFullTaskCreateReqVO.java new file mode 100644 index 00000000..27d35719 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/fulltask/DatabusSyncFullTaskCreateReqVO.java @@ -0,0 +1,18 @@ +package com.zt.plat.framework.databus.server.controller.admin.vo.fulltask; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - 全量同步任务创建 Request VO") +@Data +public class DatabusSyncFullTaskCreateReqVO { + + @Schema(description = "订阅关系ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "订阅关系ID不能为空") + private Long subscriptionId; + + @Schema(description = "备注", example = "手动触发全量同步") + private String remark; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/fulltask/DatabusSyncFullTaskPageReqVO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/fulltask/DatabusSyncFullTaskPageReqVO.java new file mode 100644 index 00000000..08ac6423 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/fulltask/DatabusSyncFullTaskPageReqVO.java @@ -0,0 +1,30 @@ +package com.zt.plat.framework.databus.server.controller.admin.vo.fulltask; + +import com.zt.plat.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - 全量同步任务分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DatabusSyncFullTaskPageReqVO extends PageParam { + + @Schema(description = "任务编号", example = "abc123") + private String taskNo; + + @Schema(description = "订阅关系ID", example = "1") + private Long subscriptionId; + + @Schema(description = "客户端编码", example = "client-01") + private String clientCode; + + @Schema(description = "事件类型", example = "ORG_SYNC") + private String eventType; + + @Schema(description = "任务状态(0-待执行 1-执行中 2-已完成 3-失败 4-已取消)", example = "2") + private Integer status; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/fulltask/DatabusSyncFullTaskRespVO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/fulltask/DatabusSyncFullTaskRespVO.java new file mode 100644 index 00000000..de43a6d7 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/fulltask/DatabusSyncFullTaskRespVO.java @@ -0,0 +1,88 @@ +package com.zt.plat.framework.databus.server.controller.admin.vo.fulltask; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 全量同步任务 Response VO") +@Data +public class DatabusSyncFullTaskRespVO { + + @Schema(description = "主键ID", example = "1") + private Long id; + + @Schema(description = "任务编号", example = "abc123") + private String taskNo; + + @Schema(description = "订阅关系ID", example = "1") + private Long subscriptionId; + + @Schema(description = "客户端编码", example = "client-01") + private String clientCode; + + @Schema(description = "事件类型", example = "ORG_SYNC") + private String eventType; + + @Schema(description = "任务状态(0-待执行 1-执行中 2-已完成 3-失败 4-已取消)", example = "2") + private Integer status; + + @Schema(description = "总数据量", example = "1000") + private Long totalCount; + + @Schema(description = "已处理数据量", example = "500") + private Long processedCount; + + @Schema(description = "成功数据量", example = "480") + private Long successCount; + + @Schema(description = "失败数据量", example = "20") + private Long failCount; + + @Schema(description = "总批次数", example = "10") + private Integer totalBatch; + + @Schema(description = "当前批次号", example = "5") + private Integer currentBatch; + + @Schema(description = "批量大小", example = "100") + private Integer batchSize; + + @Schema(description = "任务开始时间") + private LocalDateTime startTime; + + @Schema(description = "任务结束时间") + private LocalDateTime endTime; + + @Schema(description = "最后一次错误信息", example = "网络超时") + private String lastErrorMessage; + + @Schema(description = "租户ID", example = "1") + private Long tenantId; + + @Schema(description = "备注", example = "手动触发全量同步") + private String remark; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + + @Schema(description = "更新时间") + private LocalDateTime updateTime; + + @Schema(description = "进度百分比", example = "50.0") + private Double progressPercent; + + /** + * 计算进度百分比 + */ + public Double getProgressPercent() { + if (totalCount == null || totalCount == 0) { + return 0.0; + } + if (processedCount == null) { + return 0.0; + } + return Math.round(processedCount * 10000.0 / totalCount) / 100.0; + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/pushlog/DatabusSyncPushLogPageReqVO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/pushlog/DatabusSyncPushLogPageReqVO.java new file mode 100644 index 00000000..eb481034 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/pushlog/DatabusSyncPushLogPageReqVO.java @@ -0,0 +1,46 @@ +package com.zt.plat.framework.databus.server.controller.admin.vo.pushlog; + +import com.zt.plat.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 数据同步推送日志分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DatabusSyncPushLogPageReqVO extends PageParam { + + @Schema(description = "同步ID", example = "sync-20231124-001") + private String syncId; + + @Schema(description = "订阅关系ID", example = "1") + private Long subscriptionId; + + @Schema(description = "客户端编码", example = "company-a") + private String clientCode; + + @Schema(description = "事件类型", example = "user-changed") + private String eventType; + + @Schema(description = "状态(PENDING-待处理 SUCCESS-成功 FAILED-失败 RETRYING-重试中)", example = "SUCCESS") + private String status; + + @Schema(description = "传输方式(MQ/HTTP)", example = "MQ") + private String transportType; + + @Schema(description = "创建时间开始", example = "2023-11-24 00:00:00") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime createTimeStart; + + @Schema(description = "创建时间结束", example = "2023-11-24 23:59:59") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime createTimeEnd; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/pushlog/DatabusSyncPushLogRespVO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/pushlog/DatabusSyncPushLogRespVO.java new file mode 100644 index 00000000..8d1e7cdf --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/pushlog/DatabusSyncPushLogRespVO.java @@ -0,0 +1,72 @@ +package com.zt.plat.framework.databus.server.controller.admin.vo.pushlog; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 数据同步推送日志 Response VO") +@Data +public class DatabusSyncPushLogRespVO { + + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "同步ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "sync-20231124-001") + private String syncId; + + @Schema(description = "事件记录ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "10001") + private Long eventRecordId; + + @Schema(description = "订阅关系ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long subscriptionId; + + @Schema(description = "客户端编码", example = "company-a") + private String clientCode; + + @Schema(description = "事件类型", example = "user-changed") + private String eventType; + + @Schema(description = "同步模式(FULL-全量 INCREMENTAL-增量)", example = "INCREMENTAL") + private String syncMode; + + @Schema(description = "传输方式(MQ/HTTP)", requiredMode = Schema.RequiredMode.REQUIRED, example = "MQ") + private String transportType; + + @Schema(description = "MQ Topic", example = "databus-sync-user-changed-company-a") + private String mqTopic; + + @Schema(description = "MQ消息ID", example = "C0A8012300002A9F0000000000000001") + private String mqMsgId; + + @Schema(description = "状态(PENDING-待处理 SUCCESS-成功 FAILED-失败 RETRYING-重试中)", requiredMode = Schema.RequiredMode.REQUIRED, example = "SUCCESS") + private String status; + + @Schema(description = "重试次数", example = "0") + private Integer retryCount; + + @Schema(description = "错误信息", example = "连接超时") + private String errorMessage; + + @Schema(description = "开始时间") + private LocalDateTime startTime; + + @Schema(description = "结束时间") + private LocalDateTime endTime; + + @Schema(description = "耗时(毫秒)", example = "150") + private Integer duration; + + @Schema(description = "租户ID", example = "1") + private Long tenantId; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime updateTime; + + @Schema(description = "推送消息内容(JSON)", example = "{\"id\":1,\"name\":\"张三\"}") + private String dataSnapshot; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/subscription/DatabusSyncSubscriptionPageReqVO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/subscription/DatabusSyncSubscriptionPageReqVO.java new file mode 100644 index 00000000..6a057825 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/subscription/DatabusSyncSubscriptionPageReqVO.java @@ -0,0 +1,24 @@ +package com.zt.plat.framework.databus.server.controller.admin.vo.subscription; + +import com.zt.plat.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - 数据同步订阅分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class DatabusSyncSubscriptionPageReqVO extends PageParam { + + @Schema(description = "客户端ID", example = "1") + private Long clientId; + + @Schema(description = "事件ID", example = "1") + private Long eventId; + + @Schema(description = "启用状态(0-禁用 1-启用)", example = "1") + private Integer enabled; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/subscription/DatabusSyncSubscriptionRespVO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/subscription/DatabusSyncSubscriptionRespVO.java new file mode 100644 index 00000000..50a8500b --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/subscription/DatabusSyncSubscriptionRespVO.java @@ -0,0 +1,78 @@ +package com.zt.plat.framework.databus.server.controller.admin.vo.subscription; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 数据同步订阅 Response VO") +@Data +public class DatabusSyncSubscriptionRespVO { + + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "租户ID", example = "1") + private Long tenantId; + + @Schema(description = "客户端ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long clientId; + + @Schema(description = "客户端编码(关联查询)", example = "company-a") + private String clientCode; + + @Schema(description = "客户端名称(关联查询)", example = "A分公司") + private String clientName; + + @Schema(description = "事件ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long eventId; + + @Schema(description = "事件类型(关联查询)", example = "user-changed") + private String eventType; + + @Schema(description = "事件名称(关联查询)", example = "用户信息变更") + private String eventName; + + @Schema(description = "启用状态(0-禁用 1-启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer enabled; + + @Schema(description = "同步模式(FULL-全量 INCREMENTAL-增量)", example = "INCREMENTAL") + private String syncMode; + + @Schema(description = "时效性(REALTIME-实时 NEAR_REALTIME-准实时 BATCH-批量)", example = "NEAR_REALTIME") + private String timeliness; + + @Schema(description = "时效值(秒)", example = "60") + private Integer timelinessValue; + + @Schema(description = "批量大小", example = "500") + private Integer batchSize; + + @Schema(description = "最后同步的事件记录ID", example = "10001") + private Long lastSyncEventId; + + @Schema(description = "最后同步时间") + private LocalDateTime lastSyncTime; + + @Schema(description = "总同步次数", example = "100") + private Long totalSyncCount; + + @Schema(description = "成功次数", example = "95") + private Long totalSuccessCount; + + @Schema(description = "失败次数", example = "5") + private Long totalFailCount; + + @Schema(description = "创建者", example = "admin") + private String creator; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "更新者", example = "admin") + private String updater; + + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime updateTime; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/subscription/DatabusSyncSubscriptionSaveReqVO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/subscription/DatabusSyncSubscriptionSaveReqVO.java new file mode 100644 index 00000000..83caeba4 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/controller/admin/vo/subscription/DatabusSyncSubscriptionSaveReqVO.java @@ -0,0 +1,39 @@ +package com.zt.plat.framework.databus.server.controller.admin.vo.subscription; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 数据同步订阅创建/修改 Request VO") +@Data +public class DatabusSyncSubscriptionSaveReqVO { + + @Schema(description = "主键ID", example = "1") + private Long id; + + @Schema(description = "客户端ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "客户端ID不能为空") + private Long clientId; + + @Schema(description = "事件ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "事件ID不能为空") + private Long eventId; + + @Schema(description = "启用状态(0-禁用 1-启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "启用状态不能为空") + private Integer enabled; + + @Schema(description = "同步模式(FULL-全量 INCREMENTAL-增量)", example = "INCREMENTAL") + private String syncMode; + + @Schema(description = "时效性(REALTIME-实时 NEAR_REALTIME-准实时 BATCH-批量)", example = "NEAR_REALTIME") + private String timeliness; + + @Schema(description = "时效值(秒)", example = "60") + private Integer timelinessValue; + + @Schema(description = "批量大小", example = "500") + private Integer batchSize; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncClientConvert.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncClientConvert.java new file mode 100644 index 00000000..0ec678c2 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncClientConvert.java @@ -0,0 +1,25 @@ +package com.zt.plat.framework.databus.server.convert; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientRespVO; +import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientSaveReqVO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncClientDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +@Mapper +public interface DatabusSyncClientConvert { + + DatabusSyncClientConvert INSTANCE = Mappers.getMapper(DatabusSyncClientConvert.class); + + DatabusSyncClientDO convert(DatabusSyncClientSaveReqVO bean); + + DatabusSyncClientRespVO convert(DatabusSyncClientDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncDeadLetterConvert.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncDeadLetterConvert.java new file mode 100644 index 00000000..c8c87784 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncDeadLetterConvert.java @@ -0,0 +1,22 @@ +package com.zt.plat.framework.databus.server.convert; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.deadletter.DatabusSyncDeadLetterRespVO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncDeadLetterDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +@Mapper +public interface DatabusSyncDeadLetterConvert { + + DatabusSyncDeadLetterConvert INSTANCE = Mappers.getMapper(DatabusSyncDeadLetterConvert.class); + + DatabusSyncDeadLetterRespVO convert(DatabusSyncDeadLetterDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncEventConvert.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncEventConvert.java new file mode 100644 index 00000000..24ea52cd --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncEventConvert.java @@ -0,0 +1,25 @@ +package com.zt.plat.framework.databus.server.convert; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventRespVO; +import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventSaveReqVO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncEventDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +@Mapper +public interface DatabusSyncEventConvert { + + DatabusSyncEventConvert INSTANCE = Mappers.getMapper(DatabusSyncEventConvert.class); + + DatabusSyncEventDO convert(DatabusSyncEventSaveReqVO bean); + + DatabusSyncEventRespVO convert(DatabusSyncEventDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncFullTaskConvert.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncFullTaskConvert.java new file mode 100644 index 00000000..931aab0b --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncFullTaskConvert.java @@ -0,0 +1,22 @@ +package com.zt.plat.framework.databus.server.convert; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.fulltask.DatabusSyncFullTaskRespVO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncFullTaskDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +@Mapper +public interface DatabusSyncFullTaskConvert { + + DatabusSyncFullTaskConvert INSTANCE = Mappers.getMapper(DatabusSyncFullTaskConvert.class); + + DatabusSyncFullTaskRespVO convert(DatabusSyncFullTaskDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncPushLogConvert.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncPushLogConvert.java new file mode 100644 index 00000000..7a1ddc30 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncPushLogConvert.java @@ -0,0 +1,22 @@ +package com.zt.plat.framework.databus.server.convert; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.pushlog.DatabusSyncPushLogRespVO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncLogDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +@Mapper +public interface DatabusSyncPushLogConvert { + + DatabusSyncPushLogConvert INSTANCE = Mappers.getMapper(DatabusSyncPushLogConvert.class); + + DatabusSyncPushLogRespVO convert(DatabusSyncLogDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncSubscriptionConvert.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncSubscriptionConvert.java new file mode 100644 index 00000000..fe9f4b61 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/convert/DatabusSyncSubscriptionConvert.java @@ -0,0 +1,25 @@ +package com.zt.plat.framework.databus.server.convert; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionRespVO; +import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionSaveReqVO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncSubscriptionDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +@Mapper +public interface DatabusSyncSubscriptionConvert { + + DatabusSyncSubscriptionConvert INSTANCE = Mappers.getMapper(DatabusSyncSubscriptionConvert.class); + + DatabusSyncSubscriptionDO convert(DatabusSyncSubscriptionSaveReqVO bean); + + DatabusSyncSubscriptionRespVO convert(DatabusSyncSubscriptionDO bean); + + List convertList(List list); + + PageResult convertPage(PageResult page); + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/event/DatabusEvent.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/event/DatabusEvent.java new file mode 100644 index 00000000..5f209157 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/event/DatabusEvent.java @@ -0,0 +1,71 @@ +package com.zt.plat.framework.databus.server.core.event; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * Databus 事件对象 + * + * @author ZT + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DatabusEvent { + + /** + * 事件类型编码 + */ + private String eventType; + + /** + * 事件动作(create-创建 update-更新 delete-删除) + */ + private String eventAction; + + /** + * 完整业务数据快照(JSON格式) + */ + private String dataSnapshot; + + /** + * 数据版本号 + */ + private Integer dataVersion; + + /** + * 来源服务名 + */ + private String sourceService; + + /** + * 来源MQ Topic + */ + private String sourceTopic; + + /** + * 来源MQ消息ID + */ + private String sourceMsgId; + + /** + * 租户ID + */ + private Long tenantId; + + /** + * 操作人 + */ + private String operator; + + /** + * 事件发生时间 + */ + private LocalDateTime eventTime; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/message/BatchSyncMessage.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/message/BatchSyncMessage.java new file mode 100644 index 00000000..6b4abd37 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/message/BatchSyncMessage.java @@ -0,0 +1,121 @@ +package com.zt.plat.framework.databus.server.core.message; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 批量数据同步消息 + *

+ * 用于全量同步和批量增量同步,一条消息包含多条数据 + * + * @author ZT + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BatchSyncMessage { + + /** + * 消息ID(唯一标识) + */ + private String messageId; + + /** + * 请求ID(用于追踪) + */ + private String requestId; + + /** + * 事件类型 + */ + private String eventType; + + /** + * 同步模式(1-全量 2-增量) + */ + private Integer syncMode; + + /** + * 数据结构版本 + */ + private Integer dataVersion; + + /** + * 消息时间戳 + */ + private Long timestamp; + + /** + * 租户ID + */ + private Long tenantId; + + /** + * 数据列表 + */ + private List dataList; + + /** + * 本批次数据条数 + */ + private Integer count; + + // ========== 全量同步相关字段 ========== + + /** + * 全量任务ID + */ + private Long fullTaskId; + + /** + * 当前批次号 + */ + private Integer batchNo; + + /** + * 总批次数 + */ + private Integer totalBatch; + + /** + * 是否最后一批 + */ + private Boolean isLastBatch; + + /** + * 总数据量 + */ + private Long totalCount; + + /** + * 同步数据项 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class SyncDataItem { + + /** + * 操作类型:CREATE/UPDATE/DELETE + */ + private String action; + + /** + * 业务数据ID + */ + private Long uid; + + /** + * 业务数据快照(JSON) + */ + private String data; + + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/message/SyncMessage.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/message/SyncMessage.java new file mode 100644 index 00000000..4a1b0db8 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/message/SyncMessage.java @@ -0,0 +1,54 @@ +package com.zt.plat.framework.databus.server.core.message; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 数据同步消息 + * + * @author ZT + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SyncMessage { + + /** + * 同步ID(唯一标识) + */ + private String syncId; + + /** + * 事件记录ID + */ + private Long eventRecordId; + + /** + * 事件类型 + */ + private String eventType; + + /** + * 事件动作 + */ + private String eventAction; + + /** + * 业务数据快照(JSON) + */ + private String dataSnapshot; + + /** + * 数据版本 + */ + private Integer dataVersion; + + /** + * 时间戳 + */ + private Long timestamp; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/provider/DataProvider.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/provider/DataProvider.java new file mode 100644 index 00000000..faf6ea34 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/provider/DataProvider.java @@ -0,0 +1,120 @@ +package com.zt.plat.framework.databus.server.core.provider; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 数据提供者接口 + *

+ * 用于全量同步时获取数据,由业务模块实现具体的数据获取逻辑 + * + * @author ZT + */ +public interface DataProvider { + + /** + * 获取数据提供者类型标识 + * + * @return 类型标识,如 "ORG"、"USER" + */ + String getProviderType(); + + /** + * 游标分页获取数据 + * + * @param cursorTime 游标时间(首次查询传 null) + * @param cursorId 游标ID(首次查询传 null) + * @param batchSize 批量大小 + * @param tenantId 租户ID + * @return 分页结果 + */ + CursorPageData getPageByCursor(LocalDateTime cursorTime, Long cursorId, int batchSize, Long tenantId); + + /** + * 统计总数 + * + * @param tenantId 租户ID + * @return 总数量 + */ + long count(Long tenantId); + + /** + * 从数据对象中提取 UID + * + * @param data 数据对象 + * @return UID + */ + Long extractUid(T data); + + /** + * 游标分页数据结果 + */ + class CursorPageData { + private List list; + private LocalDateTime nextCursorTime; + private Long nextCursorId; + private int count; + private boolean hasMore; + private long total; + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } + + public LocalDateTime getNextCursorTime() { + return nextCursorTime; + } + + public void setNextCursorTime(LocalDateTime nextCursorTime) { + this.nextCursorTime = nextCursorTime; + } + + public Long getNextCursorId() { + return nextCursorId; + } + + public void setNextCursorId(Long nextCursorId) { + this.nextCursorId = nextCursorId; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + + public boolean isHasMore() { + return hasMore; + } + + public void setHasMore(boolean hasMore) { + this.hasMore = hasMore; + } + + public long getTotal() { + return total; + } + + public void setTotal(long total) { + this.total = total; + } + + public static CursorPageData of(List list, LocalDateTime nextCursorTime, Long nextCursorId, + int count, boolean hasMore, long total) { + CursorPageData data = new CursorPageData<>(); + data.setList(list); + data.setNextCursorTime(nextCursorTime); + data.setNextCursorId(nextCursorId); + data.setCount(count); + data.setHasMore(hasMore); + data.setTotal(total); + return data; + } + } +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/provider/DataProviderRegistry.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/provider/DataProviderRegistry.java new file mode 100644 index 00000000..36ccf227 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/provider/DataProviderRegistry.java @@ -0,0 +1,62 @@ +package com.zt.plat.framework.databus.server.core.provider; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 数据提供者注册中心 + *

+ * 管理所有注册的数据提供者,根据类型获取对应的提供者 + * + * @author ZT + */ +@Slf4j +@Component +public class DataProviderRegistry { + + private final Map> providers = new ConcurrentHashMap<>(); + + /** + * 注册数据提供者 + * + * @param provider 数据提供者 + */ + public void register(DataProvider provider) { + String type = provider.getProviderType(); + providers.put(type, provider); + log.info("[Databus] 数据提供者已注册: type={}", type); + } + + /** + * 获取数据提供者 + * + * @param type 类型标识 + * @return 数据提供者,不存在则返回 null + */ + @SuppressWarnings("unchecked") + public DataProvider getProvider(String type) { + return (DataProvider) providers.get(type); + } + + /** + * 检查是否存在指定类型的提供者 + * + * @param type 类型标识 + * @return 是否存在 + */ + public boolean hasProvider(String type) { + return providers.containsKey(type); + } + + /** + * 获取所有已注册的提供者类型 + * + * @return 类型列表 + */ + public java.util.Set getRegisteredTypes() { + return providers.keySet(); + } +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/publisher/DatabusEventPublisher.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/publisher/DatabusEventPublisher.java new file mode 100644 index 00000000..b151f88c --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/publisher/DatabusEventPublisher.java @@ -0,0 +1,28 @@ +package com.zt.plat.framework.databus.server.core.publisher; + +import com.zt.plat.framework.databus.server.core.event.DatabusEvent; + +/** + * Databus 事件发布器接口 + * 用于发布业务数据变更事件到同步流水表 + * + * @author ZT + */ +public interface DatabusEventPublisher { + + /** + * 发布事件(异步) + * + * @param event 事件对象 + */ + void publish(DatabusEvent event); + + /** + * 发布事件(同步,等待入库完成) + * + * @param event 事件对象 + * @return 事件记录ID + */ + Long publishSync(DatabusEvent event); + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/publisher/DatabusEventPublisherImpl.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/publisher/DatabusEventPublisherImpl.java new file mode 100644 index 00000000..2ac2c33e --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/publisher/DatabusEventPublisherImpl.java @@ -0,0 +1,60 @@ +package com.zt.plat.framework.databus.server.core.publisher; + +import cn.hutool.core.bean.BeanUtil; +import com.zt.plat.framework.databus.server.core.event.DatabusEvent; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncEventRecordDO; +import com.zt.plat.framework.databus.server.dal.mapper.DatabusSyncEventRecordMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +/** + * Databus 事件发布器实现 + * + * @author ZT + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class DatabusEventPublisherImpl implements DatabusEventPublisher { + + private final DatabusSyncEventRecordMapper eventRecordMapper; + + @Async + @Override + public void publish(DatabusEvent event) { + try { + saveEventRecord(event); + } catch (Exception e) { + log.error("[Databus] 发布事件失败, eventType={}, eventAction={}", + event.getEventType(), event.getEventAction(), e); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long publishSync(DatabusEvent event) { + return saveEventRecord(event); + } + + /** + * 保存事件记录到流水表 + */ + private Long saveEventRecord(DatabusEvent event) { + // 查询事件定义ID(这里简化处理,实际应该注入EventMapper查询) + // TODO: 根据 eventType 查询 event_id + + DatabusSyncEventRecordDO record = new DatabusSyncEventRecordDO(); + BeanUtil.copyProperties(event, record); + + eventRecordMapper.insert(record); + + log.info("[Databus] 事件记录已保存, id={}, eventType={}, eventAction={}", + record.getId(), event.getEventType(), event.getEventAction()); + + return record.getId(); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/pusher/MessagePusher.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/pusher/MessagePusher.java new file mode 100644 index 00000000..101dd4b8 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/pusher/MessagePusher.java @@ -0,0 +1,50 @@ +package com.zt.plat.framework.databus.server.core.pusher; + +import com.zt.plat.framework.databus.server.core.message.BatchSyncMessage; +import com.zt.plat.framework.databus.server.core.message.SyncMessage; + +/** + * 消息推送器接口 + * 支持 MQ 和 HTTP 两种推送方式 + * + * @author ZT + */ +public interface MessagePusher { + + /** + * 通过 MQ 推送消息 + * + * @param topic Topic + * @param message 消息内容 + * @return 消息ID + */ + String pushByMQ(String topic, SyncMessage message); + + /** + * 通过 HTTP 推送消息 + * + * @param endpoint HTTP端点 + * @param message 消息内容 + * @return 是否成功 + */ + boolean pushByHttp(String endpoint, SyncMessage message); + + /** + * 通过 MQ 推送批量消息 + * + * @param topic Topic + * @param message 批量消息内容 + * @return 消息ID + */ + String pushBatchByMQ(String topic, BatchSyncMessage message); + + /** + * 通过 HTTP 推送批量消息 + * + * @param endpoint HTTP端点 + * @param message 批量消息内容 + * @return 是否成功 + */ + boolean pushBatchByHttp(String endpoint, BatchSyncMessage message); + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/pusher/MessagePusherImpl.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/pusher/MessagePusherImpl.java new file mode 100644 index 00000000..39955102 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/pusher/MessagePusherImpl.java @@ -0,0 +1,103 @@ +package com.zt.plat.framework.databus.server.core.pusher; + +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONUtil; +import com.zt.plat.framework.databus.server.core.message.BatchSyncMessage; +import com.zt.plat.framework.databus.server.core.message.SyncMessage; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.client.producer.SendStatus; +import org.apache.rocketmq.spring.core.RocketMQTemplate; + +/** + * 消息推送器实现 + * + * @author ZT + */ +@Slf4j +public class MessagePusherImpl implements MessagePusher { + + private RocketMQTemplate rocketMQTemplate; + + public void setRocketMQTemplate(RocketMQTemplate rocketMQTemplate) { + this.rocketMQTemplate = rocketMQTemplate; + } + + @Override + public String pushByMQ(String topic, SyncMessage message) { + if (rocketMQTemplate == null) { + log.warn("[Databus] RocketMQTemplate未配置,无法推送MQ消息"); + throw new RuntimeException("RocketMQTemplate未配置"); + } + + try { + // 使用 RocketMQ 发送消息 + SendResult sendResult = rocketMQTemplate.syncSend(topic, message); + if (sendResult.getSendStatus() != SendStatus.SEND_OK) { + log.error("[Databus] MQ推送失败, topic={}, syncId={}, status={}, msgId={}", + topic, message.getSyncId(), sendResult.getSendStatus(), sendResult.getMsgId()); + throw new RuntimeException("MQ推送失败: " + sendResult.getSendStatus()); + } + log.info("[Databus] MQ推送成功, topic={}, syncId={}, msgId={}", + topic, message.getSyncId(), sendResult.getMsgId()); + return message.getSyncId(); + } catch (Exception e) { + log.error("[Databus] MQ推送失败, topic={}, syncId={}", topic, message.getSyncId(), e); + throw new RuntimeException("MQ推送失败", e); + } + } + + @Override + public boolean pushByHttp(String endpoint, SyncMessage message) { + try { + String jsonBody = JSONUtil.toJsonStr(message); + String response = HttpUtil.post(endpoint, jsonBody); + log.info("[Databus] HTTP推送成功, endpoint={}, syncId={}", endpoint, message.getSyncId()); + return true; + } catch (Exception e) { + log.error("[Databus] HTTP推送失败, endpoint={}, syncId={}", endpoint, message.getSyncId(), e); + return false; + } + } + + @Override + public String pushBatchByMQ(String topic, BatchSyncMessage message) { + if (rocketMQTemplate == null) { + log.warn("[Databus] RocketMQTemplate未配置,无法推送MQ消息"); + throw new RuntimeException("RocketMQTemplate未配置"); + } + + try { + // 使用 RocketMQ 发送批量消息 + SendResult sendResult = rocketMQTemplate.syncSend(topic, message); + if (sendResult.getSendStatus() != SendStatus.SEND_OK) { + log.error("[Databus] 批量MQ推送失败, topic={}, messageId={}, status={}, mqMsgId={}", + topic, message.getMessageId(), sendResult.getSendStatus(), sendResult.getMsgId()); + throw new RuntimeException("批量MQ推送失败: " + sendResult.getSendStatus()); + } + log.info("[Databus] 批量MQ推送成功, topic={}, messageId={}, count={}, mqMsgId={}", + topic, message.getMessageId(), message.getCount(), sendResult.getMsgId()); + return message.getMessageId(); + } catch (Exception e) { + log.error("[Databus] 批量MQ推送失败, topic={}, messageId={}", + topic, message.getMessageId(), e); + throw new RuntimeException("批量MQ推送失败", e); + } + } + + @Override + public boolean pushBatchByHttp(String endpoint, BatchSyncMessage message) { + try { + String jsonBody = JSONUtil.toJsonStr(message); + String response = HttpUtil.post(endpoint, jsonBody); + log.info("[Databus] 批量HTTP推送成功, endpoint={}, messageId={}, count={}", + endpoint, message.getMessageId(), message.getCount()); + return true; + } catch (Exception e) { + log.error("[Databus] 批量HTTP推送失败, endpoint={}, messageId={}", + endpoint, message.getMessageId(), e); + return false; + } + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusFullSyncService.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusFullSyncService.java new file mode 100644 index 00000000..62d00b16 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusFullSyncService.java @@ -0,0 +1,51 @@ +package com.zt.plat.framework.databus.server.core.sync; + +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncFullTaskDO; + +/** + * Databus 全量同步服务接口 + * + * @author ZT + */ +public interface DatabusFullSyncService { + + /** + * 创建全量同步任务 + * + * @param subscriptionId 订阅关系ID + * @param remark 备注 + * @return 任务ID + */ + Long createFullSyncTask(Long subscriptionId, String remark); + + /** + * 执行全量同步任务 + * + * @param taskId 任务ID + */ + void executeFullSyncTask(Long taskId); + + /** + * 取消全量同步任务 + * + * @param taskId 任务ID + */ + void cancelFullSyncTask(Long taskId); + + /** + * 获取任务详情 + * + * @param taskId 任务ID + * @return 任务详情 + */ + DatabusSyncFullTaskDO getTaskById(Long taskId); + + /** + * 根据任务编号获取任务详情 + * + * @param taskNo 任务编号 + * @return 任务详情 + */ + DatabusSyncFullTaskDO getTaskByTaskNo(String taskNo); + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusIncrementalSyncService.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusIncrementalSyncService.java new file mode 100644 index 00000000..5baeb472 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusIncrementalSyncService.java @@ -0,0 +1,27 @@ +package com.zt.plat.framework.databus.server.core.sync; + +import com.zt.plat.framework.databus.server.core.event.DatabusEvent; + +/** + * Databus 增量同步服务接口 + * 用于实时处理业务MQ消息,进行三态判断、记录流水、推送到客户端Topic + * + * @author ZT + */ +public interface DatabusIncrementalSyncService { + + /** + * 处理增量同步事件 + *

+ * 核心流程: + * 1. 根据 eventType 查询事件定义,判断是否启用 + * 2. 查询所有启用的订阅关系 + * 3. 对每个订阅进行三态判断(事件/客户端/订阅是否启用) + * 4. 记录到 databus_sync_event_record 流水表 + * 5. 推送到客户端专属 Topic(databus-sync-{eventType}-{clientCode}) + * + * @param event 业务变更事件 + */ + void processEvent(DatabusEvent event); + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusIncrementalSyncServiceImpl.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusIncrementalSyncServiceImpl.java new file mode 100644 index 00000000..99766a04 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusIncrementalSyncServiceImpl.java @@ -0,0 +1,246 @@ +package com.zt.plat.framework.databus.server.core.sync; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.IdUtil; +import com.zt.plat.framework.databus.server.core.event.DatabusEvent; +import com.zt.plat.framework.databus.server.core.message.SyncMessage; +import com.zt.plat.framework.databus.server.core.pusher.MessagePusher; +import com.zt.plat.framework.databus.server.dal.dataobject.*; +import com.zt.plat.framework.databus.server.dal.mapper.*; +import com.zt.plat.framework.databus.server.enums.SyncStatusEnum; +import com.zt.plat.framework.databus.server.enums.TransportTypeEnum; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.List; + +/** + * Databus 增量同步服务实现 + *

+ * 核心流程: + * 1. 根据 eventType 查询事件定义,判断是否启用 + * 2. 记录到 event_record 流水表 + * 3. 查询所有启用的订阅关系 + * 4. 对每个订阅进行三态判断(事件/客户端/订阅是否启用) + * 5. 推送到客户端专属 Topic(databus-sync-{eventType}-{clientCode}) + * + * @author ZT + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class DatabusIncrementalSyncServiceImpl implements DatabusIncrementalSyncService { + + private final DatabusSyncEventMapper eventMapper; + private final DatabusSyncEventRecordMapper eventRecordMapper; + private final DatabusSyncSubscriptionMapper subscriptionMapper; + private final DatabusSyncClientMapper clientMapper; + private final DatabusSyncLogMapper syncLogMapper; + private final MessagePusher messagePusher; + + @Override + @Transactional(rollbackFor = Exception.class) + public void processEvent(DatabusEvent event) { + log.info("[Databus增量同步] 开始处理事件, eventType={}, eventAction={}", + event.getEventType(), event.getEventAction()); + + // 1. 查询事件定义 + DatabusSyncEventDO eventDef = eventMapper.selectByEventType(event.getEventType()); + if (eventDef == null) { + log.warn("[Databus增量同步] 事件类型未定义, eventType={}", event.getEventType()); + return; + } + + // 2. 判断事件是否启用(三态判断之一) + if (eventDef.getEnabled() != 1) { + log.debug("[Databus增量同步] 事件未启用, eventType={}", event.getEventType()); + return; + } + + // 3. 判断事件是否支持增量同步 + if (eventDef.getSupportIncrementalSync() != 1) { + log.debug("[Databus增量同步] 事件不支持增量同步, eventType={}", event.getEventType()); + return; + } + + // 4. 记录到 event_record 流水表 + DatabusSyncEventRecordDO eventRecord = saveEventRecord(event, eventDef); + + // 5. 查询所有启用的订阅关系 + List subscriptions = subscriptionMapper.selectEnabledByEventId(eventDef.getId()); + if (subscriptions.isEmpty()) { + log.debug("[Databus增量同步] 无启用的订阅, eventType={}", event.getEventType()); + return; + } + + log.info("[Databus增量同步] 找到{}个订阅需要推送, eventType={}", + subscriptions.size(), event.getEventType()); + + // 6. 对每个订阅进行推送 + for (DatabusSyncSubscriptionDO subscription : subscriptions) { + try { + pushToSubscriber(subscription, eventDef, eventRecord); + } catch (Exception e) { + log.error("[Databus增量同步] 推送到订阅者失败, subscriptionId={}, eventType={}", + subscription.getId(), event.getEventType(), e); + } + } + + log.info("[Databus增量同步] 事件处理完成, eventType={}, eventAction={}, recordId={}", + event.getEventType(), event.getEventAction(), eventRecord.getId()); + } + + /** + * 保存事件记录到流水表 + */ + private DatabusSyncEventRecordDO saveEventRecord(DatabusEvent event, DatabusSyncEventDO eventDef) { + DatabusSyncEventRecordDO record = new DatabusSyncEventRecordDO(); + BeanUtil.copyProperties(event, record); + record.setEventId(eventDef.getId()); + + eventRecordMapper.insert(record); + + log.info("[Databus增量同步] 事件记录已保存, recordId={}, eventType={}, eventAction={}", + record.getId(), event.getEventType(), event.getEventAction()); + + return record; + } + + /** + * 推送到单个订阅者 + */ + private void pushToSubscriber(DatabusSyncSubscriptionDO subscription, + DatabusSyncEventDO eventDef, + DatabusSyncEventRecordDO eventRecord) { + // 查询客户端信息 + DatabusSyncClientDO client = clientMapper.selectById(subscription.getClientId()); + if (client == null) { + log.warn("[Databus增量同步] 客户端不存在, clientId={}", subscription.getClientId()); + return; + } + + // 三态判断之二:客户端是否启用 + if (client.getEnabled() != 1) { + log.debug("[Databus增量同步] 客户端未启用, clientCode={}", client.getClientCode()); + return; + } + + // 三态判断之三:订阅是否启用(已在查询时过滤,这里再确认一次) + if (subscription.getEnabled() != 1) { + log.debug("[Databus增量同步] 订阅未启用, subscriptionId={}", subscription.getId()); + return; + } + + String syncId = IdUtil.fastSimpleUUID(); + LocalDateTime startTime = LocalDateTime.now(); + + // 构建同步消息 + SyncMessage message = SyncMessage.builder() + .syncId(syncId) + .eventRecordId(eventRecord.getId()) + .eventType(eventRecord.getEventType()) + .eventAction(eventRecord.getEventAction()) + .dataSnapshot(eventRecord.getDataSnapshot()) + .dataVersion(eventRecord.getDataVersion()) + .timestamp(System.currentTimeMillis()) + .build(); + + // 构建同步日志 + DatabusSyncLogDO syncLog = DatabusSyncLogDO.builder() + .syncId(syncId) + .eventRecordId(eventRecord.getId()) + .subscriptionId(subscription.getId()) + .clientCode(client.getClientCode()) + .eventType(eventRecord.getEventType()) + .syncMode(2) // 增量同步 + .startTime(startTime) + .status(SyncStatusEnum.PENDING.getStatus()) + .retryCount(0) + .dataCount(1) + .tenantId(client.getTenantId()) + .build(); + + try { + // 根据客户端配置选择推送方式 + if (TransportTypeEnum.isMqFirst(client.getTransportType()) && client.getMqEnabled() == 1) { + // MQ 推送:Topic格式 = databus-sync-{eventType}-{clientCode} + String topic = buildClientTopic(client.getMqTopicBase(), eventDef.getEventType(), client.getClientCode()); + String mqMsgId = messagePusher.pushByMQ(topic, message); + + syncLog.setTransportType(TransportTypeEnum.MQ_FIRST.getType()); + syncLog.setMqTopic(topic); + syncLog.setMqMsgId(mqMsgId); + + log.info("[Databus增量同步] MQ推送成功, topic={}, syncId={}", topic, syncId); + + } else if (client.getHttpEnabled() == 1) { + // HTTP 推送 + boolean success = messagePusher.pushByHttp(client.getHttpEndpoint(), message); + if (!success) { + throw new RuntimeException("HTTP推送失败"); + } + + syncLog.setTransportType(TransportTypeEnum.HTTP_ONLY.getType()); + + log.info("[Databus增量同步] HTTP推送成功, endpoint={}, syncId={}", + client.getHttpEndpoint(), syncId); + + } else { + throw new RuntimeException("无可用的推送方式"); + } + + // 更新日志状态为成功 + syncLog.setStatus(SyncStatusEnum.SUCCESS.getStatus()); + syncLog.setEndTime(LocalDateTime.now()); + syncLog.setDuration((int) (System.currentTimeMillis() - + startTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli())); + + // 更新订阅断点 + updateSubscriptionCheckpoint(subscription, eventRecord.getId()); + + } catch (Exception e) { + log.error("[Databus增量同步] 推送失败, syncId={}, clientCode={}", syncId, client.getClientCode(), e); + + syncLog.setStatus(SyncStatusEnum.FAILED.getStatus()); + syncLog.setErrorMessage(e.getMessage()); + syncLog.setEndTime(LocalDateTime.now()); + } + + // 保存同步日志 + syncLogMapper.insert(syncLog); + } + + /** + * 构建客户端专属Topic + * 格式: {topicBase}-{module}-{entity}-{action}-{clientCode} + * 示例: databus-sync-system-org-create-company-a + * + * @param topicBase 基础Topic名称(如 databus-sync) + * @param eventType 事件类型(格式: system-org-create) + * @param clientCode 客户端编码 + */ + private String buildClientTopic(String topicBase, String eventType, String clientCode) { + // 默认topicBase为 databus-sync + if (topicBase == null || topicBase.isEmpty()) { + topicBase = "databus-sync"; + } + // eventType 格式已经是 system-org-create,直接拼接 + return String.format("%s-%s-%s", topicBase, eventType.toLowerCase(), clientCode); + } + + /** + * 更新订阅断点 + */ + private void updateSubscriptionCheckpoint(DatabusSyncSubscriptionDO subscription, Long eventRecordId) { + subscription.setLastSyncEventId(eventRecordId); + subscription.setLastSyncTime(LocalDateTime.now()); + subscription.setTotalSyncCount((subscription.getTotalSyncCount() == null ? 0 : subscription.getTotalSyncCount()) + 1); + subscription.setTotalSuccessCount((subscription.getTotalSuccessCount() == null ? 0 : subscription.getTotalSuccessCount()) + 1); + subscriptionMapper.updateById(subscription); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusSyncService.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusSyncService.java new file mode 100644 index 00000000..00fd9b78 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusSyncService.java @@ -0,0 +1,24 @@ +package com.zt.plat.framework.databus.server.core.sync; + +/** + * Databus 同步服务接口 + * 负责扫描事件记录并推送到客户端 + * + * @author ZT + */ +public interface DatabusSyncService { + + /** + * 执行同步任务 + * 扫描所有启用的订阅关系,推送新事件到客户端 + */ + void executeSyncTask(); + + /** + * 手动触发指定订阅的同步 + * + * @param subscriptionId 订阅关系ID + */ + void triggerSync(Long subscriptionId); + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusSyncServiceImpl.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusSyncServiceImpl.java new file mode 100644 index 00000000..72c50892 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/core/sync/DatabusSyncServiceImpl.java @@ -0,0 +1,237 @@ +package com.zt.plat.framework.databus.server.core.sync; + +import cn.hutool.core.util.IdUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.zt.plat.framework.databus.server.config.DatabusSyncServerProperties; +import com.zt.plat.framework.databus.server.core.message.SyncMessage; +import com.zt.plat.framework.databus.server.core.pusher.MessagePusher; +import com.zt.plat.framework.databus.server.dal.dataobject.*; +import com.zt.plat.framework.databus.server.dal.mapper.*; +import com.zt.plat.framework.databus.server.enums.DeadLetterStatusEnum; +import com.zt.plat.framework.databus.server.enums.SyncStatusEnum; +import com.zt.plat.framework.databus.server.enums.TransportTypeEnum; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * Databus 同步服务实现 + * + * @author ZT + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class DatabusSyncServiceImpl implements DatabusSyncService { + + private final DatabusSyncSubscriptionMapper subscriptionMapper; + private final DatabusSyncEventRecordMapper eventRecordMapper; + private final DatabusSyncClientMapper clientMapper; + private final DatabusSyncEventMapper eventMapper; + private final DatabusSyncLogMapper syncLogMapper; + private final DatabusSyncDeadLetterMapper deadLetterMapper; + private final MessagePusher messagePusher; + private final DatabusSyncServerProperties properties; + + @Override + public void executeSyncTask() { + log.info("[Databus] 开始执行同步任务"); + + // 查询所有启用的订阅关系 + List subscriptions = subscriptionMapper.selectList( + new LambdaQueryWrapper() + .eq(DatabusSyncSubscriptionDO::getEnabled, 1) + ); + + for (DatabusSyncSubscriptionDO subscription : subscriptions) { + try { + processSubscription(subscription); + } catch (Exception e) { + log.error("[Databus] 处理订阅失败, subscriptionId={}", subscription.getId(), e); + } + } + + log.info("[Databus] 同步任务执行完成, 处理订阅数={}", subscriptions.size()); + } + + @Override + public void triggerSync(Long subscriptionId) { + DatabusSyncSubscriptionDO subscription = subscriptionMapper.selectById(subscriptionId); + if (subscription == null) { + log.warn("[Databus] 订阅不存在, subscriptionId={}", subscriptionId); + return; + } + + processSubscription(subscription); + } + + /** + * 处理单个订阅 + */ + @Transactional(rollbackFor = Exception.class) + protected void processSubscription(DatabusSyncSubscriptionDO subscription) { + // 三状态启用检查 + DatabusSyncClientDO client = clientMapper.selectById(subscription.getClientId()); + if (client == null || client.getEnabled() != 1) { + log.debug("[Databus] 客户端未启用, clientId={}", subscription.getClientId()); + return; + } + + DatabusSyncEventDO event = eventMapper.selectById(subscription.getEventId()); + if (event == null || event.getEnabled() != 1) { + log.debug("[Databus] 事件未启用, eventId={}", subscription.getEventId()); + return; + } + + // 查询新事件记录(断点续传) + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() + .eq(DatabusSyncEventRecordDO::getEventId, subscription.getEventId()) + .orderByAsc(DatabusSyncEventRecordDO::getId); + + if (subscription.getLastSyncEventId() != null) { + queryWrapper.gt(DatabusSyncEventRecordDO::getId, subscription.getLastSyncEventId()); + } + + // 批量查询 - 使用 Page 分页,兼容达梦等数据库 + Page page = new Page<>(1, subscription.getBatchSize(), false); + List records = eventRecordMapper.selectPage(page, queryWrapper).getRecords(); + + if (records.isEmpty()) { + log.debug("[Databus] 无新事件, subscriptionId={}", subscription.getId()); + return; + } + + log.info("[Databus] 开始同步, subscriptionId={}, eventCount={}", subscription.getId(), records.size()); + + // 推送事件 + for (DatabusSyncEventRecordDO record : records) { + boolean success = pushEvent(subscription, client, record); + if (success) { + // 更新断点 + subscription.setLastSyncEventId(record.getId()); + subscription.setLastSyncTime(LocalDateTime.now()); + subscription.setTotalSyncCount(subscription.getTotalSyncCount() + 1); + subscription.setTotalSuccessCount(subscription.getTotalSuccessCount() + 1); + } else { + subscription.setTotalFailCount(subscription.getTotalFailCount() + 1); + break; // 失败则停止当前批次 + } + } + + // 更新订阅统计 + subscriptionMapper.updateById(subscription); + } + + /** + * 推送单个事件 + */ + private boolean pushEvent(DatabusSyncSubscriptionDO subscription, + DatabusSyncClientDO client, + DatabusSyncEventRecordDO record) { + String syncId = IdUtil.fastSimpleUUID(); + LocalDateTime startTime = LocalDateTime.now(); + + // 构建消息 + SyncMessage message = SyncMessage.builder() + .syncId(syncId) + .eventRecordId(record.getId()) + .eventType(record.getEventType()) + .eventAction(record.getEventAction()) + .dataSnapshot(record.getDataSnapshot()) + .dataVersion(record.getDataVersion()) + .timestamp(System.currentTimeMillis()) + .build(); + + // 记录日志 + DatabusSyncLogDO syncLog = DatabusSyncLogDO.builder() + .syncId(syncId) + .eventRecordId(record.getId()) + .subscriptionId(subscription.getId()) + .clientCode(client.getClientCode()) + .eventType(record.getEventType()) + .syncMode(subscription.getSyncMode()) + .startTime(startTime) + .status(SyncStatusEnum.PENDING.getStatus()) + .retryCount(0) + .tenantId(client.getTenantId()) + .build(); + + try { + // 推送消息 + if (TransportTypeEnum.isMqFirst(client.getTransportType()) && client.getMqEnabled() == 1) { + // MQ 推送 + String topic = String.format("%s-%s-%s", + client.getMqTopicBase(), record.getEventType(), client.getClientCode()); + String mqMsgId = messagePusher.pushByMQ(topic, message); + + syncLog.setTransportType(TransportTypeEnum.MQ_FIRST.getType()); + syncLog.setMqTopic(topic); + syncLog.setMqMsgId(mqMsgId); + } else if (client.getHttpEnabled() == 1) { + // HTTP 推送 + boolean success = messagePusher.pushByHttp(client.getHttpEndpoint(), message); + if (!success) { + throw new RuntimeException("HTTP推送失败"); + } + + syncLog.setTransportType(TransportTypeEnum.HTTP_ONLY.getType()); + } else { + throw new RuntimeException("无可用的推送方式"); + } + + // 更新日志状态 + syncLog.setStatus(SyncStatusEnum.SUCCESS.getStatus()); + syncLog.setEndTime(LocalDateTime.now()); + syncLog.setDuration((int) (System.currentTimeMillis() - startTime.atZone(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli())); + syncLogMapper.insert(syncLog); + + return true; + + } catch (Exception e) { + log.error("[Databus] 推送失败, syncId={}, eventRecordId={}", syncId, record.getId(), e); + + // 更新日志状态 + syncLog.setStatus(SyncStatusEnum.FAILED.getStatus()); + syncLog.setErrorMessage(e.getMessage()); + syncLog.setEndTime(LocalDateTime.now()); + syncLogMapper.insert(syncLog); + + // 检查重试次数,超过限制则进入死信队列 + if (syncLog.getRetryCount() >= properties.getRetry().getMaxAttempts()) { + saveToDeadLetter(syncLog, message); + } + + return false; + } + } + + /** + * 保存到死信队列 + */ + private void saveToDeadLetter(DatabusSyncLogDO syncLog, SyncMessage message) { + DatabusSyncDeadLetterDO deadLetter = DatabusSyncDeadLetterDO.builder() + .syncLogId(syncLog.getId()) + .syncId(syncLog.getSyncId()) + .clientCode(syncLog.getClientCode()) + .eventType(syncLog.getEventType()) + .eventRecordId(syncLog.getEventRecordId()) + .retryCount(syncLog.getRetryCount()) + .lastErrorMessage(syncLog.getErrorMessage()) + .lastErrorTime(LocalDateTime.now()) + .messageBody(cn.hutool.json.JSONUtil.toJsonStr(message)) + .status(DeadLetterStatusEnum.PENDING.getStatus()) + .handled(0) + .tenantId(syncLog.getTenantId()) + .build(); + + deadLetterMapper.insert(deadLetter); + + log.warn("[Databus] 消息已进入死信队列, syncId={}", syncLog.getSyncId()); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncClientDO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncClientDO.java new file mode 100644 index 00000000..68a3f867 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncClientDO.java @@ -0,0 +1,84 @@ +package com.zt.plat.framework.databus.server.dal.dataobject; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.zt.plat.framework.tenant.core.db.TenantBaseDO; +import lombok.*; + +/** + * 数据同步客户端配置 DO + * + * @author ZT + */ +@TableName("databus_sync_client") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DatabusSyncClientDO extends TenantBaseDO { + + /** + * 主键ID + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + /** + * 客户端编码 + */ + private String clientCode; + + /** + * 客户端名称 + */ + private String clientName; + + /** + * 启用状态(0-禁用 1-启用) + */ + private Integer enabled; + + /** + * 通信方式(1-MQ优先 2-仅HTTP) + */ + private Integer transportType; + + /** + * MQ是否启用(0-否 1-是) + */ + private Integer mqEnabled; + + /** + * MQ NameServer地址 + */ + private String mqNamesrvAddr; + + /** + * MQ Topic基础名称 + */ + private String mqTopicBase; + + /** + * HTTP是否启用(0-否 1-是) + */ + private Integer httpEnabled; + + /** + * HTTP推送端点 + */ + private String httpEndpoint; + + /** + * 应用Key + */ + private String appKey; + + /** + * 应用Secret(加密存储) + */ + private String appSecret; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncDeadLetterDO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncDeadLetterDO.java new file mode 100644 index 00000000..f8067841 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncDeadLetterDO.java @@ -0,0 +1,106 @@ +package com.zt.plat.framework.databus.server.dal.dataobject; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 数据同步死信队列 DO + * + * @author ZT + */ +@TableName("databus_sync_dead_letter") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DatabusSyncDeadLetterDO extends BaseDO { + + /** + * 主键ID + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + /** + * 同步日志ID + */ + private Long syncLogId; + + /** + * 同步ID + */ + private String syncId; + + /** + * 客户端编码 + */ + private String clientCode; + + /** + * 事件类型 + */ + private String eventType; + + /** + * 事件记录ID + */ + private Long eventRecordId; + + /** + * 已重试次数 + */ + private Integer retryCount; + + /** + * 最后错误信息 + */ + private String lastErrorMessage; + + /** + * 最后错误时间 + */ + private LocalDateTime lastErrorTime; + + /** + * 消息内容(JSON格式) + */ + private String messageBody; + + /** + * 状态(0-待处理 1-已重新投递 2-已忽略) + */ + private Integer status; + + /** + * 是否已处理(0-否 1-是) + */ + private Integer handled; + + /** + * 处理时间 + */ + private LocalDateTime handleTime; + + /** + * 处理人 + */ + private String handler; + + /** + * 处理备注 + */ + private String handleRemark; + + /** + * 租户ID + */ + private Long tenantId; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncEventDO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncEventDO.java new file mode 100644 index 00000000..7887c4ef --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncEventDO.java @@ -0,0 +1,76 @@ +package com.zt.plat.framework.databus.server.dal.dataobject; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.zt.plat.framework.tenant.core.db.TenantBaseDO; +import lombok.*; + +/** + * 数据同步事件定义 DO + * + * @author ZT + */ +@TableName("databus_sync_event") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DatabusSyncEventDO extends TenantBaseDO { + + /** + * 主键ID + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + /** + * 事件类型编码 + */ + private String eventType; + + /** + * 事件名称 + */ + private String eventName; + + /** + * 事件描述 + */ + private String eventDesc; + + /** + * 启用状态(0-禁用 1-启用) + */ + private Integer enabled; + + /** + * 是否支持全量同步(0-否 1-是) + */ + private Integer supportFullSync; + + /** + * 是否支持增量同步(0-否 1-是) + */ + private Integer supportIncrementalSync; + + /** + * 数据结构版本 + */ + private Integer dataVersion; + + /** + * 数据提供者服务名(Feign服务名) + * 用于全量同步时调用对应服务获取数据 + */ + private String dataProviderService; + + /** + * 数据提供者方法标识 + * 如: ORG(组织)、USER(用户) + */ + private String dataProviderMethod; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncEventRecordDO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncEventRecordDO.java new file mode 100644 index 00000000..a2239361 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncEventRecordDO.java @@ -0,0 +1,87 @@ +package com.zt.plat.framework.databus.server.dal.dataobject; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 数据同步事件流水 DO + * 用于断点续传 + * + * @author ZT + */ +@TableName("databus_sync_event_record") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DatabusSyncEventRecordDO extends BaseDO { + + /** + * 主键ID(雪花算法,全局唯一递增) + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + /** + * 事件定义ID + */ + private Long eventId; + + /** + * 事件类型编码 + */ + private String eventType; + + /** + * 事件动作(create-创建 update-更新 delete-删除) + */ + private String eventAction; + + /** + * 完整业务数据快照(JSON格式) + */ + private String dataSnapshot; + + /** + * 数据版本号 + */ + private Integer dataVersion; + + /** + * 来源服务名 + */ + private String sourceService; + + /** + * 来源MQ Topic + */ + private String sourceTopic; + + /** + * 来源MQ消息ID + */ + private String sourceMsgId; + + /** + * 租户ID + */ + private Long tenantId; + + /** + * 操作人 + */ + private String operator; + + /** + * 事件发生时间 + */ + private LocalDateTime eventTime; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncFullTaskDO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncFullTaskDO.java new file mode 100644 index 00000000..6709a80d --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncFullTaskDO.java @@ -0,0 +1,126 @@ +package com.zt.plat.framework.databus.server.dal.dataobject; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 数据全量同步任务 DO + * + * @author ZT + */ +@TableName("databus_sync_full_task") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DatabusSyncFullTaskDO extends BaseDO { + + /** + * 主键ID + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + /** + * 任务编号(唯一标识) + */ + private String taskNo; + + /** + * 订阅关系ID + */ + private Long subscriptionId; + + /** + * 客户端编码 + */ + private String clientCode; + + /** + * 事件类型 + */ + private String eventType; + + /** + * 任务状态(0-待执行 1-执行中 2-已完成 3-失败 4-已取消) + */ + private Integer status; + + /** + * 总数据量 + */ + private Long totalCount; + + /** + * 已处理数据量 + */ + private Long processedCount; + + /** + * 成功数据量 + */ + private Long successCount; + + /** + * 失败数据量 + */ + private Long failCount; + + /** + * 总批次数 + */ + private Integer totalBatch; + + /** + * 当前批次号 + */ + private Integer currentBatch; + + /** + * 批量大小 + */ + private Integer batchSize; + + /** + * 游标时间(断点续传) + */ + private LocalDateTime cursorTime; + + /** + * 游标ID(断点续传) + */ + private Long cursorId; + + /** + * 任务开始时间 + */ + private LocalDateTime startTime; + + /** + * 任务结束时间 + */ + private LocalDateTime endTime; + + /** + * 最后一次错误信息 + */ + private String lastErrorMessage; + + /** + * 租户ID + */ + private Long tenantId; + + /** + * 备注 + */ + private String remark; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncLogDO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncLogDO.java new file mode 100644 index 00000000..51ff7534 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncLogDO.java @@ -0,0 +1,126 @@ +package com.zt.plat.framework.databus.server.dal.dataobject; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 数据同步日志 DO + * + * @author ZT + */ +@TableName("databus_sync_log") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DatabusSyncLogDO extends BaseDO { + + /** + * 主键ID + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + /** + * 同步ID(唯一标识) + */ + private String syncId; + + /** + * 事件记录ID + */ + private Long eventRecordId; + + /** + * 订阅关系ID + */ + private Long subscriptionId; + + /** + * 客户端编码 + */ + private String clientCode; + + /** + * 事件类型 + */ + private String eventType; + + /** + * 同步模式(1-全量 2-增量) + */ + private Integer syncMode; + + /** + * 传输方式(1-MQ优先 2-仅HTTP) + */ + private Integer transportType; + + /** + * MQ Topic + */ + private String mqTopic; + + /** + * MQ消息ID + */ + private String mqMsgId; + + /** + * 状态(0-待处理 1-成功 2-失败 3-重试中) + */ + private Integer status; + + /** + * 重试次数 + */ + private Integer retryCount; + + /** + * 错误信息 + */ + private String errorMessage; + + /** + * 开始时间 + */ + private LocalDateTime startTime; + + /** + * 结束时间 + */ + private LocalDateTime endTime; + + /** + * 耗时(毫秒) + */ + private Integer duration; + + /** + * 租户ID + */ + private Long tenantId; + + /** + * 本批次数据条数 + */ + private Integer dataCount; + + /** + * 批次号(全量同步时使用) + */ + private Integer batchNo; + + /** + * 全量任务ID(全量同步时关联) + */ + private Long fullTaskId; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncSubscriptionDO.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncSubscriptionDO.java new file mode 100644 index 00000000..a11aa085 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/dataobject/DatabusSyncSubscriptionDO.java @@ -0,0 +1,91 @@ +package com.zt.plat.framework.databus.server.dal.dataobject; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.zt.plat.framework.tenant.core.db.TenantBaseDO; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 数据同步订阅关系 DO + * + * @author ZT + */ +@TableName("databus_sync_subscription") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DatabusSyncSubscriptionDO extends TenantBaseDO { + + /** + * 主键ID + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + /** + * 客户端ID + */ + private Long clientId; + + /** + * 事件ID + */ + private Long eventId; + + /** + * 订阅启用状态(0-禁用 1-启用) + */ + private Integer enabled; + + /** + * 同步模式(1-全量 2-增量) + */ + private Integer syncMode; + + /** + * 时效性(1-实时 2-准实时 3-批量) + */ + private Integer timeliness; + + /** + * 时效值(秒) + */ + private Integer timelinessValue; + + /** + * 批量大小 + */ + private Integer batchSize; + + /** + * 最后同步的事件记录ID(断点续传) + */ + private Long lastSyncEventId; + + /** + * 最后同步时间 + */ + private LocalDateTime lastSyncTime; + + /** + * 总同步次数 + */ + private Long totalSyncCount; + + /** + * 成功次数 + */ + private Long totalSuccessCount; + + /** + * 失败次数 + */ + private Long totalFailCount; + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncClientMapper.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncClientMapper.java new file mode 100644 index 00000000..da34a1c5 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncClientMapper.java @@ -0,0 +1,38 @@ +package com.zt.plat.framework.databus.server.dal.mapper; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientPageReqVO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncClientDO; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 数据同步客户端 Mapper + * + * @author ZT + */ +@Mapper +public interface DatabusSyncClientMapper extends BaseMapperX { + + default DatabusSyncClientDO selectByClientCode(String clientCode) { + return selectOne(DatabusSyncClientDO::getClientCode, clientCode); + } + + default PageResult selectPage(DatabusSyncClientPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(DatabusSyncClientDO::getClientCode, reqVO.getClientCode()) + .likeIfPresent(DatabusSyncClientDO::getClientName, reqVO.getClientName()) + .eqIfPresent(DatabusSyncClientDO::getEnabled, reqVO.getEnabled()) + .eqIfPresent(DatabusSyncClientDO::getTransportType, reqVO.getTransportType()) + .orderByDesc(DatabusSyncClientDO::getId)); + } + + default List selectList() { + return selectList(new LambdaQueryWrapperX() + .orderByDesc(DatabusSyncClientDO::getId)); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncDeadLetterMapper.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncDeadLetterMapper.java new file mode 100644 index 00000000..69ce53d3 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncDeadLetterMapper.java @@ -0,0 +1,29 @@ +package com.zt.plat.framework.databus.server.dal.mapper; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.deadletter.DatabusSyncDeadLetterPageReqVO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncDeadLetterDO; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import org.apache.ibatis.annotations.Mapper; + +/** + * 数据同步死信队列 Mapper + * + * @author ZT + */ +@Mapper +public interface DatabusSyncDeadLetterMapper extends BaseMapperX { + + default PageResult selectPage(DatabusSyncDeadLetterPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(DatabusSyncDeadLetterDO::getSyncId, reqVO.getSyncId()) + .likeIfPresent(DatabusSyncDeadLetterDO::getClientCode, reqVO.getClientCode()) + .likeIfPresent(DatabusSyncDeadLetterDO::getEventType, reqVO.getEventType()) + .eqIfPresent(DatabusSyncDeadLetterDO::getStatus, reqVO.getStatus()) + .eqIfPresent(DatabusSyncDeadLetterDO::getHandled, reqVO.getHandled()) + .betweenIfPresent(DatabusSyncDeadLetterDO::getCreateTime, reqVO.getCreateTimeStart(), reqVO.getCreateTimeEnd()) + .orderByDesc(DatabusSyncDeadLetterDO::getId)); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncEventMapper.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncEventMapper.java new file mode 100644 index 00000000..a7a9880f --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncEventMapper.java @@ -0,0 +1,37 @@ +package com.zt.plat.framework.databus.server.dal.mapper; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventPageReqVO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncEventDO; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 数据同步事件定义 Mapper + * + * @author ZT + */ +@Mapper +public interface DatabusSyncEventMapper extends BaseMapperX { + + default DatabusSyncEventDO selectByEventType(String eventType) { + return selectOne(DatabusSyncEventDO::getEventType, eventType); + } + + default PageResult selectPage(DatabusSyncEventPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(DatabusSyncEventDO::getEventType, reqVO.getEventType()) + .likeIfPresent(DatabusSyncEventDO::getEventName, reqVO.getEventName()) + .eqIfPresent(DatabusSyncEventDO::getEnabled, reqVO.getEnabled()) + .orderByDesc(DatabusSyncEventDO::getId)); + } + + default List selectList() { + return selectList(new LambdaQueryWrapperX() + .orderByDesc(DatabusSyncEventDO::getId)); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncEventRecordMapper.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncEventRecordMapper.java new file mode 100644 index 00000000..7f26d3d9 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncEventRecordMapper.java @@ -0,0 +1,15 @@ +package com.zt.plat.framework.databus.server.dal.mapper; + +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncEventRecordDO; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import org.apache.ibatis.annotations.Mapper; + +/** + * 数据同步事件流水 Mapper + * + * @author ZT + */ +@Mapper +public interface DatabusSyncEventRecordMapper extends BaseMapperX { + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncFullTaskMapper.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncFullTaskMapper.java new file mode 100644 index 00000000..5f3d6e70 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncFullTaskMapper.java @@ -0,0 +1,62 @@ +package com.zt.plat.framework.databus.server.dal.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.fulltask.DatabusSyncFullTaskPageReqVO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncFullTaskDO; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 数据全量同步任务 Mapper + * + * @author ZT + */ +@Mapper +public interface DatabusSyncFullTaskMapper extends BaseMapperX { + + /** + * 根据任务编号查询 + */ + default DatabusSyncFullTaskDO selectByTaskNo(String taskNo) { + return selectOne(DatabusSyncFullTaskDO::getTaskNo, taskNo); + } + + /** + * 查询指定订阅的进行中任务 + */ + default DatabusSyncFullTaskDO selectRunningBySubscriptionId(Long subscriptionId) { + return selectOne(new LambdaQueryWrapperX() + .eq(DatabusSyncFullTaskDO::getSubscriptionId, subscriptionId) + .in(DatabusSyncFullTaskDO::getStatus, 0, 1)); // 待执行或执行中 + } + + /** + * 查询待执行的任务列表 + * 使用 MyBatis-Plus 分页机制,兼容达梦等数据库 + */ + default List selectPendingTasks(int limit) { + // 使用 Page 分页而不是 LIMIT 语法,以兼容达梦数据库 + Page page = new Page<>(1, limit, false); + return selectPage(page, new LambdaQueryWrapperX() + .eq(DatabusSyncFullTaskDO::getStatus, 0) + .orderByAsc(DatabusSyncFullTaskDO::getCreateTime)).getRecords(); + } + + /** + * 分页查询 + */ + default PageResult selectPage(DatabusSyncFullTaskPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(DatabusSyncFullTaskDO::getTaskNo, reqVO.getTaskNo()) + .eqIfPresent(DatabusSyncFullTaskDO::getSubscriptionId, reqVO.getSubscriptionId()) + .eqIfPresent(DatabusSyncFullTaskDO::getClientCode, reqVO.getClientCode()) + .eqIfPresent(DatabusSyncFullTaskDO::getEventType, reqVO.getEventType()) + .eqIfPresent(DatabusSyncFullTaskDO::getStatus, reqVO.getStatus()) + .orderByDesc(DatabusSyncFullTaskDO::getId)); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncLogMapper.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncLogMapper.java new file mode 100644 index 00000000..2fd52caa --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncLogMapper.java @@ -0,0 +1,30 @@ +package com.zt.plat.framework.databus.server.dal.mapper; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.pushlog.DatabusSyncPushLogPageReqVO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncLogDO; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import org.apache.ibatis.annotations.Mapper; + +/** + * 数据同步日志 Mapper + * + * @author ZT + */ +@Mapper +public interface DatabusSyncLogMapper extends BaseMapperX { + + default PageResult selectPage(DatabusSyncPushLogPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(DatabusSyncLogDO::getSyncId, reqVO.getSyncId()) + .eqIfPresent(DatabusSyncLogDO::getSubscriptionId, reqVO.getSubscriptionId()) + .likeIfPresent(DatabusSyncLogDO::getClientCode, reqVO.getClientCode()) + .likeIfPresent(DatabusSyncLogDO::getEventType, reqVO.getEventType()) + .eqIfPresent(DatabusSyncLogDO::getStatus, reqVO.getStatus()) + .eqIfPresent(DatabusSyncLogDO::getTransportType, reqVO.getTransportType()) + .betweenIfPresent(DatabusSyncLogDO::getCreateTime, reqVO.getCreateTimeStart(), reqVO.getCreateTimeEnd()) + .orderByDesc(DatabusSyncLogDO::getId)); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncSubscriptionMapper.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncSubscriptionMapper.java new file mode 100644 index 00000000..4d30f98a --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/dal/mapper/DatabusSyncSubscriptionMapper.java @@ -0,0 +1,41 @@ +package com.zt.plat.framework.databus.server.dal.mapper; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionPageReqVO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncSubscriptionDO; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import org.apache.ibatis.annotations.Mapper; + +/** + * 数据同步订阅关系 Mapper + * + * @author ZT + */ +@Mapper +public interface DatabusSyncSubscriptionMapper extends BaseMapperX { + + default DatabusSyncSubscriptionDO selectByClientIdAndEventId(Long clientId, Long eventId) { + return selectOne(new LambdaQueryWrapperX() + .eq(DatabusSyncSubscriptionDO::getClientId, clientId) + .eq(DatabusSyncSubscriptionDO::getEventId, eventId)); + } + + default PageResult selectPage(DatabusSyncSubscriptionPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(DatabusSyncSubscriptionDO::getClientId, reqVO.getClientId()) + .eqIfPresent(DatabusSyncSubscriptionDO::getEventId, reqVO.getEventId()) + .eqIfPresent(DatabusSyncSubscriptionDO::getEnabled, reqVO.getEnabled()) + .orderByDesc(DatabusSyncSubscriptionDO::getId)); + } + + /** + * 根据事件ID查询所有启用的订阅 + */ + default java.util.List selectEnabledByEventId(Long eventId) { + return selectList(new LambdaQueryWrapperX() + .eq(DatabusSyncSubscriptionDO::getEventId, eventId) + .eq(DatabusSyncSubscriptionDO::getEnabled, 1)); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/DataProviderTypeEnum.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/DataProviderTypeEnum.java new file mode 100644 index 00000000..00ad24b6 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/DataProviderTypeEnum.java @@ -0,0 +1,36 @@ +package com.zt.plat.framework.databus.server.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据提供者类型枚举 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum DataProviderTypeEnum { + + ORG("ORG", "组织机构"), + USER("USER", "用户"); + + /** + * 类型编码 + */ + private final String code; + /** + * 类型名称 + */ + private final String name; + + public static DataProviderTypeEnum getByCode(String code) { + for (DataProviderTypeEnum value : values()) { + if (value.getCode().equals(code)) { + return value; + } + } + return null; + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/DeadLetterStatusEnum.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/DeadLetterStatusEnum.java new file mode 100644 index 00000000..1a3e8ecf --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/DeadLetterStatusEnum.java @@ -0,0 +1,51 @@ +package com.zt.plat.framework.databus.server.enums; + +import cn.hutool.core.util.ObjUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 死信队列状态枚举 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum DeadLetterStatusEnum implements ArrayValuable { + + PENDING(0, "待处理"), + REDELIVERED(1, "已重新投递"), + IGNORED(2, "已忽略"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(DeadLetterStatusEnum::getStatus).toArray(Integer[]::new); + + /** + * 状态值 + */ + private final Integer status; + /** + * 状态名 + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + + public static boolean isPending(Integer status) { + return ObjUtil.equal(PENDING.status, status); + } + + public static boolean isRedelivered(Integer status) { + return ObjUtil.equal(REDELIVERED.status, status); + } + + public static boolean isIgnored(Integer status) { + return ObjUtil.equal(IGNORED.status, status); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/ErrorCodeConstants.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/ErrorCodeConstants.java new file mode 100644 index 00000000..893312d4 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/ErrorCodeConstants.java @@ -0,0 +1,30 @@ +package com.zt.plat.framework.databus.server.enums; + +import com.zt.plat.framework.common.exception.ErrorCode; + +/** + * Databus 同步服务错误码枚举类 + *

+ * databus 系统,使用 1-010-000-000 段 + */ +public interface ErrorCodeConstants { + + // ========== 数据同步事件 1-010-001-000 ========== + ErrorCode EVENT_NOT_EXISTS = new ErrorCode(1_010_001_000, "数据同步事件不存在"); + ErrorCode EVENT_TYPE_DUPLICATE = new ErrorCode(1_010_001_001, "事件类型编码已存在"); + + // ========== 数据同步客户端 1-010-002-000 ========== + ErrorCode CLIENT_NOT_EXISTS = new ErrorCode(1_010_002_000, "数据同步客户端不存在"); + ErrorCode CLIENT_CODE_DUPLICATE = new ErrorCode(1_010_002_001, "客户端编码已存在"); + + // ========== 数据同步订阅 1-010-003-000 ========== + ErrorCode SUBSCRIPTION_NOT_EXISTS = new ErrorCode(1_010_003_000, "数据同步订阅不存在"); + ErrorCode SUBSCRIPTION_DUPLICATE = new ErrorCode(1_010_003_001, "该客户端已订阅该事件,不能重复订阅"); + + // ========== 数据同步推送日志 1-010-004-000 ========== + ErrorCode PUSH_LOG_NOT_EXISTS = new ErrorCode(1_010_004_000, "数据同步推送日志不存在"); + + // ========== 数据同步死信队列 1-010-005-000 ========== + ErrorCode DEAD_LETTER_NOT_EXISTS = new ErrorCode(1_010_005_000, "数据同步死信队列记录不存在"); + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/FullTaskStatusEnum.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/FullTaskStatusEnum.java new file mode 100644 index 00000000..a8aa2d4c --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/FullTaskStatusEnum.java @@ -0,0 +1,75 @@ +package com.zt.plat.framework.databus.server.enums; + +import cn.hutool.core.util.ObjUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 全量任务状态枚举 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum FullTaskStatusEnum implements ArrayValuable { + + PENDING(0, "待执行"), + RUNNING(1, "执行中"), + COMPLETED(2, "已完成"), + FAILED(3, "失败"), + CANCELLED(4, "已取消"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(FullTaskStatusEnum::getStatus).toArray(Integer[]::new); + + /** + * 状态值 + */ + private final Integer status; + /** + * 状态名 + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + + public static boolean isPending(Integer status) { + return ObjUtil.equal(PENDING.status, status); + } + + public static boolean isRunning(Integer status) { + return ObjUtil.equal(RUNNING.status, status); + } + + public static boolean isCompleted(Integer status) { + return ObjUtil.equal(COMPLETED.status, status); + } + + public static boolean isFailed(Integer status) { + return ObjUtil.equal(FAILED.status, status); + } + + public static boolean isCancelled(Integer status) { + return ObjUtil.equal(CANCELLED.status, status); + } + + /** + * 是否可以取消 + */ + public static boolean canCancel(Integer status) { + return isPending(status) || isRunning(status); + } + + /** + * 是否已终止(完成/失败/取消) + */ + public static boolean isTerminated(Integer status) { + return isCompleted(status) || isFailed(status) || isCancelled(status); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/SyncModeEnum.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/SyncModeEnum.java new file mode 100644 index 00000000..100c7daf --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/SyncModeEnum.java @@ -0,0 +1,46 @@ +package com.zt.plat.framework.databus.server.enums; + +import cn.hutool.core.util.ObjUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 同步模式枚举 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum SyncModeEnum implements ArrayValuable { + + FULL(1, "全量同步"), + INCREMENTAL(2, "增量同步"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(SyncModeEnum::getMode).toArray(Integer[]::new); + + /** + * 模式值 + */ + private final Integer mode; + /** + * 模式名 + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + + public static boolean isFull(Integer mode) { + return ObjUtil.equal(FULL.mode, mode); + } + + public static boolean isIncremental(Integer mode) { + return ObjUtil.equal(INCREMENTAL.mode, mode); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/SyncStatusEnum.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/SyncStatusEnum.java new file mode 100644 index 00000000..0bacda69 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/SyncStatusEnum.java @@ -0,0 +1,56 @@ +package com.zt.plat.framework.databus.server.enums; + +import cn.hutool.core.util.ObjUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 同步状态枚举 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum SyncStatusEnum implements ArrayValuable { + + PENDING(0, "待处理"), + SUCCESS(1, "成功"), + FAILED(2, "失败"), + RETRYING(3, "重试中"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(SyncStatusEnum::getStatus).toArray(Integer[]::new); + + /** + * 状态值 + */ + private final Integer status; + /** + * 状态名 + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + + public static boolean isPending(Integer status) { + return ObjUtil.equal(PENDING.status, status); + } + + public static boolean isSuccess(Integer status) { + return ObjUtil.equal(SUCCESS.status, status); + } + + public static boolean isFailed(Integer status) { + return ObjUtil.equal(FAILED.status, status); + } + + public static boolean isRetrying(Integer status) { + return ObjUtil.equal(RETRYING.status, status); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/TimelinessEnum.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/TimelinessEnum.java new file mode 100644 index 00000000..67b96c39 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/TimelinessEnum.java @@ -0,0 +1,51 @@ +package com.zt.plat.framework.databus.server.enums; + +import cn.hutool.core.util.ObjUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 时效性枚举 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum TimelinessEnum implements ArrayValuable { + + REALTIME(1, "实时"), + NEAR_REALTIME(2, "准实时"), + BATCH(3, "批量"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(TimelinessEnum::getType).toArray(Integer[]::new); + + /** + * 类型值 + */ + private final Integer type; + /** + * 类型名 + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + + public static boolean isRealtime(Integer type) { + return ObjUtil.equal(REALTIME.type, type); + } + + public static boolean isNearRealtime(Integer type) { + return ObjUtil.equal(NEAR_REALTIME.type, type); + } + + public static boolean isBatch(Integer type) { + return ObjUtil.equal(BATCH.type, type); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/TransportTypeEnum.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/TransportTypeEnum.java new file mode 100644 index 00000000..c0fbf707 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/enums/TransportTypeEnum.java @@ -0,0 +1,46 @@ +package com.zt.plat.framework.databus.server.enums; + +import cn.hutool.core.util.ObjUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 传输方式枚举 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum TransportTypeEnum implements ArrayValuable { + + MQ_FIRST(1, "MQ优先"), + HTTP_ONLY(2, "仅HTTP"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(TransportTypeEnum::getType).toArray(Integer[]::new); + + /** + * 类型值 + */ + private final Integer type; + /** + * 类型名 + */ + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + + public static boolean isMqFirst(Integer type) { + return ObjUtil.equal(MQ_FIRST.type, type); + } + + public static boolean isHttpOnly(Integer type) { + return ObjUtil.equal(HTTP_ONLY.type, type); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/producer/DatabusMessageProducer.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/producer/DatabusMessageProducer.java new file mode 100644 index 00000000..16807566 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/producer/DatabusMessageProducer.java @@ -0,0 +1,195 @@ +package com.zt.plat.framework.databus.server.producer; + +import cn.hutool.json.JSONUtil; +import com.zt.plat.framework.databus.server.config.DatabusServerProperties; +import com.zt.plat.module.databus.api.message.DatabusBatchMessage; +import com.zt.plat.module.databus.api.message.DatabusMessage; +import com.zt.plat.module.databus.enums.DatabusEventType; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.client.producer.DefaultMQProducer; +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.common.message.Message; + +import java.nio.charset.StandardCharsets; +import java.util.List; + +/** + * Databus 消息生产者 + *

+ * 负责将数据变更消息发送到各个客户端的 Topic + * + * @author ZT + */ +@Slf4j +@RequiredArgsConstructor +public class DatabusMessageProducer { + + private final DatabusServerProperties properties; + + private DefaultMQProducer producer; + + @PostConstruct + public void init() throws MQClientException { + if (!properties.isEnabled() || !properties.getMq().isEnabled()) { + log.info("[Databus Server] MQ 发送未启用"); + return; + } + + producer = new DefaultMQProducer(properties.getMq().getProducerGroup()); + producer.setNamesrvAddr(properties.getMq().getNameServer()); + producer.setSendMsgTimeout(properties.getMq().getSendMsgTimeout()); + producer.start(); + + log.info("[Databus Server] 消息生产者启动成功, nameServer={}, producerGroup={}", + properties.getMq().getNameServer(), properties.getMq().getProducerGroup()); + } + + @PreDestroy + public void destroy() { + if (producer != null) { + producer.shutdown(); + log.info("[Databus Server] 消息生产者已关闭"); + } + } + + /** + * 发送增量消息到所有客户端 + * + * @param message 消息 + * @param 数据类型 + */ + public void send(DatabusMessage message) { + if (producer == null) { + log.warn("[Databus Server] Producer 未初始化,无法发送消息"); + return; + } + + List clients = properties.getClients(); + if (clients == null || clients.isEmpty()) { + log.warn("[Databus Server] 未配置客户端列表,无法发送消息"); + return; + } + + String messageJson = JSONUtil.toJsonStr(message); + DatabusEventType eventType = message.getEventType(); + + for (String clientCode : clients) { + try { + String topic = eventType.getTopic(properties.getMq().getTopicBase(), clientCode); + Message mqMessage = new Message(topic, messageJson.getBytes(StandardCharsets.UTF_8)); + mqMessage.setKeys(message.getMessageId()); + + SendResult result = producer.send(mqMessage); + log.debug("[Databus Server] 消息发送成功, topic={}, messageId={}, result={}", + topic, message.getMessageId(), result.getMsgId()); + } catch (Exception e) { + log.error("[Databus Server] 消息发送失败, clientCode={}, eventType={}, messageId={}", + clientCode, eventType.name(), message.getMessageId(), e); + } + } + } + + /** + * 发送增量消息到指定客户端 + * + * @param message 消息 + * @param clientCode 客户端编码 + * @param 数据类型 + */ + public void sendTo(DatabusMessage message, String clientCode) { + if (producer == null) { + log.warn("[Databus Server] Producer 未初始化,无法发送消息"); + return; + } + + String messageJson = JSONUtil.toJsonStr(message); + DatabusEventType eventType = message.getEventType(); + String topic = eventType.getTopic(properties.getMq().getTopicBase(), clientCode); + + try { + Message mqMessage = new Message(topic, messageJson.getBytes(StandardCharsets.UTF_8)); + mqMessage.setKeys(message.getMessageId()); + + SendResult result = producer.send(mqMessage); + log.info("[Databus Server] 消息发送成功, topic={}, messageId={}, result={}", + topic, message.getMessageId(), result.getMsgId()); + } catch (Exception e) { + log.error("[Databus Server] 消息发送失败, clientCode={}, eventType={}, messageId={}", + clientCode, eventType.name(), message.getMessageId(), e); + throw new RuntimeException("消息发送失败", e); + } + } + + /** + * 发送批量消息到所有客户端 + * + * @param message 批量消息 + * @param 数据类型 + */ + public void sendBatch(DatabusBatchMessage message) { + if (producer == null) { + log.warn("[Databus Server] Producer 未初始化,无法发送消息"); + return; + } + + List clients = properties.getClients(); + if (clients == null || clients.isEmpty()) { + log.warn("[Databus Server] 未配置客户端列表,无法发送消息"); + return; + } + + String messageJson = JSONUtil.toJsonStr(message); + DatabusEventType eventType = message.getEventType(); + + for (String clientCode : clients) { + try { + String topic = eventType.getTopic(properties.getMq().getTopicBase(), clientCode); + Message mqMessage = new Message(topic, messageJson.getBytes(StandardCharsets.UTF_8)); + mqMessage.setKeys(message.getMessageId()); + + SendResult result = producer.send(mqMessage); + log.debug("[Databus Server] 批量消息发送成功, topic={}, batchNo={}/{}, result={}", + topic, message.getBatchNo(), message.getTotalBatch(), result.getMsgId()); + } catch (Exception e) { + log.error("[Databus Server] 批量消息发送失败, clientCode={}, eventType={}, batchNo={}", + clientCode, eventType.name(), message.getBatchNo(), e); + } + } + } + + /** + * 发送批量消息到指定客户端 + * + * @param message 批量消息 + * @param clientCode 客户端编码 + * @param 数据类型 + */ + public void sendBatchTo(DatabusBatchMessage message, String clientCode) { + if (producer == null) { + log.warn("[Databus Server] Producer 未初始化,无法发送消息"); + return; + } + + String messageJson = JSONUtil.toJsonStr(message); + DatabusEventType eventType = message.getEventType(); + String topic = eventType.getTopic(properties.getMq().getTopicBase(), clientCode); + + try { + Message mqMessage = new Message(topic, messageJson.getBytes(StandardCharsets.UTF_8)); + mqMessage.setKeys(message.getMessageId()); + + SendResult result = producer.send(mqMessage); + log.info("[Databus Server] 批量消息发送成功, topic={}, batchNo={}/{}, result={}", + topic, message.getBatchNo(), message.getTotalBatch(), result.getMsgId()); + } catch (Exception e) { + log.error("[Databus Server] 批量消息发送失败, clientCode={}, eventType={}, batchNo={}", + clientCode, eventType.name(), message.getBatchNo(), e); + throw new RuntimeException("批量消息发送失败", e); + } + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncClientService.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncClientService.java new file mode 100644 index 00000000..7e634a61 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncClientService.java @@ -0,0 +1,70 @@ +package com.zt.plat.framework.databus.server.service; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientPageReqVO; +import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientSaveReqVO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncClientDO; + +import java.util.List; + +/** + * 数据同步客户端 Service 接口 + * + * @author ZT + */ +public interface DatabusSyncClientService { + + /** + * 创建数据同步客户端 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createClient(DatabusSyncClientSaveReqVO createReqVO); + + /** + * 更新数据同步客户端 + * + * @param updateReqVO 更新信息 + */ + void updateClient(DatabusSyncClientSaveReqVO updateReqVO); + + /** + * 删除数据同步客户端 + * + * @param id 编号 + */ + void deleteClient(Long id); + + /** + * 获得数据同步客户端 + * + * @param id 编号 + * @return 数据同步客户端 + */ + DatabusSyncClientDO getClient(Long id); + + /** + * 获得数据同步客户端分页 + * + * @param pageReqVO 分页查询 + * @return 数据同步客户端分页 + */ + PageResult getClientPage(DatabusSyncClientPageReqVO pageReqVO); + + /** + * 获得数据同步客户端列表 + * + * @return 数据同步客户端列表 + */ + List getClientList(); + + /** + * 更新客户端启用状态 + * + * @param id 编号 + * @param enabled 启用状态 + */ + void updateClientStatus(Long id, Integer enabled); + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncDeadLetterService.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncDeadLetterService.java new file mode 100644 index 00000000..e922a8ad --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncDeadLetterService.java @@ -0,0 +1,54 @@ +package com.zt.plat.framework.databus.server.service; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.deadletter.DatabusSyncDeadLetterPageReqVO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncDeadLetterDO; + +import java.util.List; + +/** + * 数据同步死信队列 Service 接口 + * + * @author ZT + */ +public interface DatabusSyncDeadLetterService { + + /** + * 获得数据同步死信队列 + * + * @param id 编号 + * @return 数据同步死信队列 + */ + DatabusSyncDeadLetterDO getDeadLetter(Long id); + + /** + * 获得数据同步死信队列分页 + * + * @param pageReqVO 分页查询 + * @return 数据同步死信队列分页 + */ + PageResult getDeadLetterPage(DatabusSyncDeadLetterPageReqVO pageReqVO); + + /** + * 重新投递死信消息 + * + * @param id 死信ID + */ + void reprocessDeadLetter(Long id); + + /** + * 批量重新投递死信消息 + * + * @param ids 死信ID列表 + */ + void batchReprocessDeadLetter(List ids); + + /** + * 标记为已处理 + * + * @param id 死信ID + * @param remark 处理备注 + */ + void markHandled(Long id, String remark); + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncEventService.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncEventService.java new file mode 100644 index 00000000..fe7c2b3f --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncEventService.java @@ -0,0 +1,70 @@ +package com.zt.plat.framework.databus.server.service; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventPageReqVO; +import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventSaveReqVO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncEventDO; + +import java.util.List; + +/** + * 数据同步事件 Service 接口 + * + * @author ZT + */ +public interface DatabusSyncEventService { + + /** + * 创建数据同步事件 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createEvent(DatabusSyncEventSaveReqVO createReqVO); + + /** + * 更新数据同步事件 + * + * @param updateReqVO 更新信息 + */ + void updateEvent(DatabusSyncEventSaveReqVO updateReqVO); + + /** + * 删除数据同步事件 + * + * @param id 编号 + */ + void deleteEvent(Long id); + + /** + * 获得数据同步事件 + * + * @param id 编号 + * @return 数据同步事件 + */ + DatabusSyncEventDO getEvent(Long id); + + /** + * 获得数据同步事件分页 + * + * @param pageReqVO 分页查询 + * @return 数据同步事件分页 + */ + PageResult getEventPage(DatabusSyncEventPageReqVO pageReqVO); + + /** + * 获得数据同步事件列表 + * + * @return 数据同步事件列表 + */ + List getEventList(); + + /** + * 更新事件启用状态 + * + * @param id 编号 + * @param enabled 启用状态 + */ + void updateEventStatus(Long id, Integer enabled); + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncLogService.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncLogService.java new file mode 100644 index 00000000..26cc97b5 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncLogService.java @@ -0,0 +1,37 @@ +package com.zt.plat.framework.databus.server.service; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.pushlog.DatabusSyncPushLogPageReqVO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncLogDO; + +/** + * 数据同步推送日志 Service 接口 + * + * @author ZT + */ +public interface DatabusSyncLogService { + + /** + * 获得数据同步推送日志 + * + * @param id 编号 + * @return 数据同步推送日志 + */ + DatabusSyncLogDO getPushLog(Long id); + + /** + * 获得数据同步推送日志分页 + * + * @param pageReqVO 分页查询 + * @return 数据同步推送日志分页 + */ + PageResult getPushLogPage(DatabusSyncPushLogPageReqVO pageReqVO); + + /** + * 重试推送 + * + * @param id 日志ID + */ + void retryPush(Long id); + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncSubscriptionService.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncSubscriptionService.java new file mode 100644 index 00000000..88239aaf --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/DatabusSyncSubscriptionService.java @@ -0,0 +1,75 @@ +package com.zt.plat.framework.databus.server.service; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionPageReqVO; +import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionSaveReqVO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncSubscriptionDO; + +/** + * 数据同步订阅 Service 接口 + * + * @author ZT + */ +public interface DatabusSyncSubscriptionService { + + /** + * 创建数据同步订阅 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createSubscription(DatabusSyncSubscriptionSaveReqVO createReqVO); + + /** + * 更新数据同步订阅 + * + * @param updateReqVO 更新信息 + */ + void updateSubscription(DatabusSyncSubscriptionSaveReqVO updateReqVO); + + /** + * 删除数据同步订阅 + * + * @param id 编号 + */ + void deleteSubscription(Long id); + + /** + * 获得数据同步订阅 + * + * @param id 编号 + * @return 数据同步订阅 + */ + DatabusSyncSubscriptionDO getSubscription(Long id); + + /** + * 获得数据同步订阅分页 + * + * @param pageReqVO 分页查询 + * @return 数据同步订阅分页 + */ + PageResult getSubscriptionPage(DatabusSyncSubscriptionPageReqVO pageReqVO); + + /** + * 更新订阅启用状态 + * + * @param id 编号 + * @param enabled 启用状态 + */ + void updateSubscriptionStatus(Long id, Integer enabled); + + /** + * 重置订阅断点 + * + * @param id 编号 + */ + void resetCheckpoint(Long id); + + /** + * 手动触发同步 + * + * @param id 编号 + */ + void triggerSync(Long id); + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusFullSyncServiceImpl.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusFullSyncServiceImpl.java new file mode 100644 index 00000000..d92f8800 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusFullSyncServiceImpl.java @@ -0,0 +1,328 @@ +package com.zt.plat.framework.databus.server.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.json.JSONUtil; +import com.zt.plat.framework.databus.server.config.DatabusSyncServerProperties; +import com.zt.plat.framework.databus.server.core.message.BatchSyncMessage; +import com.zt.plat.framework.databus.server.core.provider.DataProvider; +import com.zt.plat.framework.databus.server.core.provider.DataProviderRegistry; +import com.zt.plat.framework.databus.server.core.pusher.MessagePusher; +import com.zt.plat.framework.databus.server.core.sync.DatabusFullSyncService; +import com.zt.plat.framework.databus.server.dal.dataobject.*; +import com.zt.plat.framework.databus.server.dal.mapper.*; +import com.zt.plat.framework.databus.server.enums.*; +import com.zt.plat.module.databus.enums.DatabusEventType; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +/** + * Databus Full Sync Service Implementation + * + * @author ZT + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class DatabusFullSyncServiceImpl implements DatabusFullSyncService { + + private final DatabusSyncFullTaskMapper fullTaskMapper; + private final DatabusSyncSubscriptionMapper subscriptionMapper; + private final DatabusSyncClientMapper clientMapper; + private final DatabusSyncEventMapper eventMapper; + private final DatabusSyncLogMapper syncLogMapper; + private final MessagePusher messagePusher; + private final DatabusSyncServerProperties properties; + private final DataProviderRegistry dataProviderRegistry; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createFullSyncTask(Long subscriptionId, String remark) { + DatabusSyncSubscriptionDO subscription = subscriptionMapper.selectById(subscriptionId); + if (subscription == null) { + throw new RuntimeException("Subscription not found"); + } + + DatabusSyncFullTaskDO runningTask = fullTaskMapper.selectRunningBySubscriptionId(subscriptionId); + if (runningTask != null) { + throw new RuntimeException("Full sync task already running: " + runningTask.getTaskNo()); + } + + DatabusSyncClientDO client = clientMapper.selectById(subscription.getClientId()); + DatabusSyncEventDO event = eventMapper.selectById(subscription.getEventId()); + + if (client == null || event == null) { + throw new RuntimeException("Client or event config not found"); + } + + if (event.getSupportFullSync() != 1) { + throw new RuntimeException("Event does not support full sync"); + } + + DatabusSyncFullTaskDO task = DatabusSyncFullTaskDO.builder() + .taskNo(IdUtil.fastSimpleUUID()) + .subscriptionId(subscriptionId) + .clientCode(client.getClientCode()) + .eventType(event.getEventType()) + .status(FullTaskStatusEnum.PENDING.getStatus()) + .totalCount(0L) + .processedCount(0L) + .successCount(0L) + .failCount(0L) + .totalBatch(0) + .currentBatch(0) + .batchSize(subscription.getBatchSize()) + .tenantId(client.getTenantId()) + .remark(remark) + .build(); + + fullTaskMapper.insert(task); + log.info("[Databus] Full sync task created, taskId={}, taskNo={}", task.getId(), task.getTaskNo()); + + return task.getId(); + } + + @Override + @Async + public void executeFullSyncTask(Long taskId) { + DatabusSyncFullTaskDO task = fullTaskMapper.selectById(taskId); + if (task == null) { + log.error("[Databus] Task not found, taskId={}", taskId); + return; + } + // 允许 PENDING、RUNNING、FAILED 状态执行(支持重试) + if (FullTaskStatusEnum.isCompleted(task.getStatus()) || + FullTaskStatusEnum.isCancelled(task.getStatus())) { + log.warn("[Databus] Task status not allowed, taskId={}, status={}", taskId, task.getStatus()); + return; + } + + task.setStatus(FullTaskStatusEnum.RUNNING.getStatus()); + task.setStartTime(LocalDateTime.now()); + fullTaskMapper.updateById(task); + + try { + DatabusSyncSubscriptionDO subscription = subscriptionMapper.selectById(task.getSubscriptionId()); + DatabusSyncClientDO client = clientMapper.selectById(subscription.getClientId()); + DatabusSyncEventDO event = eventMapper.selectById(subscription.getEventId()); + + String providerType = event.getDataProviderMethod(); + if (providerType == null) { + throw new RuntimeException("Event data provider type not configured"); + } + + DataProvider dataProvider = dataProviderRegistry.getProvider(providerType); + if (dataProvider == null) { + throw new RuntimeException("Data provider not found: " + providerType); + } + + executeGenericFullSync(task, subscription, client, event, dataProvider); + + task.setStatus(FullTaskStatusEnum.COMPLETED.getStatus()); + task.setEndTime(LocalDateTime.now()); + fullTaskMapper.updateById(task); + + log.info("[Databus] Full sync task completed, taskId={}, totalCount={}, successCount={}", + taskId, task.getTotalCount(), task.getSuccessCount()); + + } catch (Exception e) { + log.error("[Databus] Full sync task failed, taskId={}", taskId, e); + + task.setStatus(FullTaskStatusEnum.FAILED.getStatus()); + task.setEndTime(LocalDateTime.now()); + task.setLastErrorMessage(e.getMessage()); + fullTaskMapper.updateById(task); + } + } + + private void executeGenericFullSync(DatabusSyncFullTaskDO task, + DatabusSyncSubscriptionDO subscription, + DatabusSyncClientDO client, + DatabusSyncEventDO event, + DataProvider dataProvider) { + long totalCount = dataProvider.count(task.getTenantId()); + int totalBatch = (int) Math.ceil((double) totalCount / task.getBatchSize()); + + task.setTotalCount(totalCount); + task.setTotalBatch(totalBatch); + fullTaskMapper.updateById(task); + + LocalDateTime cursorTime = task.getCursorTime(); + Long cursorId = task.getCursorId(); + int batchNo = task.getCurrentBatch(); + + while (true) { + DatabusSyncFullTaskDO currentTask = fullTaskMapper.selectById(task.getId()); + if (FullTaskStatusEnum.isCancelled(currentTask.getStatus())) { + log.info("[Databus] Task cancelled, taskId={}", task.getId()); + return; + } + + batchNo++; + DataProvider.CursorPageData page = dataProvider.getPageByCursor( + cursorTime, cursorId, task.getBatchSize(), task.getTenantId()); + + if (CollUtil.isEmpty(page.getList())) { + break; + } + + boolean isLastBatch = !page.isHasMore(); + BatchSyncMessage message = buildBatchMessage( + task, event, page.getList(), dataProvider, batchNo, totalBatch, isLastBatch, totalCount); + + boolean success = pushBatchMessage(client, event.getEventType(), message); + + recordSyncLog(task, subscription, client, event, message, success, batchNo); + + task.setCurrentBatch(batchNo); + task.setCursorTime(page.getNextCursorTime()); + task.setCursorId(page.getNextCursorId()); + task.setProcessedCount(task.getProcessedCount() + page.getCount()); + if (success) { + task.setSuccessCount(task.getSuccessCount() + page.getCount()); + } else { + task.setFailCount(task.getFailCount() + page.getCount()); + } + fullTaskMapper.updateById(task); + + if (isLastBatch) { + break; + } + cursorTime = page.getNextCursorTime(); + cursorId = page.getNextCursorId(); + } + } + + private BatchSyncMessage buildBatchMessage(DatabusSyncFullTaskDO task, + DatabusSyncEventDO event, + List dataList, + DataProvider dataProvider, + int batchNo, + int totalBatch, + boolean isLastBatch, + long totalCount) { + List items = new ArrayList<>(); + for (T data : dataList) { + Long uid = dataProvider.extractUid(data); + items.add(BatchSyncMessage.SyncDataItem.builder() + .action("FULL") + .uid(uid) + .data(JSONUtil.toJsonStr(data)) + .build()); + } + + return BatchSyncMessage.builder() + .messageId(IdUtil.fastSimpleUUID()) + .requestId(task.getTaskNo()) + .eventType(event.getEventType()) + .syncMode(SyncModeEnum.FULL.getMode()) + .dataVersion(event.getDataVersion()) + .timestamp(System.currentTimeMillis()) + .tenantId(task.getTenantId()) + .dataList(items) + .count(items.size()) + .fullTaskId(task.getId()) + .batchNo(batchNo) + .totalBatch(totalBatch) + .isLastBatch(isLastBatch) + .totalCount(totalCount) + .build(); + } + + private boolean pushBatchMessage(DatabusSyncClientDO client, String eventType, + BatchSyncMessage message) { + try { + if (TransportTypeEnum.isMqFirst(client.getTransportType()) && client.getMqEnabled() == 1) { + // 使用 getByTopicSuffix 支持数据库存储的 topic 格式(如 system-post-full) + DatabusEventType databusEventType = DatabusEventType.getByTopicSuffix(eventType); + if (databusEventType == null) { + log.error("[Databus] Unknown event type: {}", eventType); + return false; + } + String topicBase = client.getMqTopicBase(); + if (topicBase == null || topicBase.isEmpty()) { + topicBase = "databus-sync"; + } + String topic = databusEventType.getTopic(topicBase, client.getClientCode()); + log.info("[Databus] Pushing batch message, topic={}, eventType={}, clientCode={}, messageId={}", + topic, eventType, client.getClientCode(), message.getMessageId()); + messagePusher.pushBatchByMQ(topic, message); + } else if (client.getHttpEnabled() == 1) { + return messagePusher.pushBatchByHttp(client.getHttpEndpoint(), message); + } else { + throw new RuntimeException("No available push method"); + } + return true; + } catch (Exception e) { + log.error("[Databus] Push batch message failed, messageId={}", message.getMessageId(), e); + return false; + } + } + + private void recordSyncLog(DatabusSyncFullTaskDO task, + DatabusSyncSubscriptionDO subscription, + DatabusSyncClientDO client, + DatabusSyncEventDO event, + BatchSyncMessage message, + boolean success, + int batchNo) { + DatabusSyncLogDO syncLog = DatabusSyncLogDO.builder() + .syncId(message.getMessageId()) + .eventRecordId(0L) + .subscriptionId(subscription.getId()) + .clientCode(client.getClientCode()) + .eventType(event.getEventType()) + .syncMode(SyncModeEnum.FULL.getMode()) + .transportType(TransportTypeEnum.isMqFirst(client.getTransportType()) && client.getMqEnabled() == 1 + ? TransportTypeEnum.MQ_FIRST.getType() + : TransportTypeEnum.HTTP_ONLY.getType()) + .status(success ? SyncStatusEnum.SUCCESS.getStatus() : SyncStatusEnum.FAILED.getStatus()) + .retryCount(0) + .startTime(LocalDateTime.now()) + .endTime(LocalDateTime.now()) + .tenantId(task.getTenantId()) + .dataCount(message.getCount()) + .batchNo(batchNo) + .fullTaskId(task.getId()) + .build(); + + syncLogMapper.insert(syncLog); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void cancelFullSyncTask(Long taskId) { + DatabusSyncFullTaskDO task = fullTaskMapper.selectById(taskId); + if (task == null) { + throw new RuntimeException("Task not found"); + } + + if (!FullTaskStatusEnum.canCancel(task.getStatus())) { + throw new RuntimeException("Task status not allowed to cancel"); + } + + task.setStatus(FullTaskStatusEnum.CANCELLED.getStatus()); + task.setEndTime(LocalDateTime.now()); + fullTaskMapper.updateById(task); + + log.info("[Databus] Full sync task cancelled, taskId={}", taskId); + } + + @Override + public DatabusSyncFullTaskDO getTaskById(Long taskId) { + return fullTaskMapper.selectById(taskId); + } + + @Override + public DatabusSyncFullTaskDO getTaskByTaskNo(String taskNo) { + return fullTaskMapper.selectByTaskNo(taskNo); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncClientServiceImpl.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncClientServiceImpl.java new file mode 100644 index 00000000..496463c9 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncClientServiceImpl.java @@ -0,0 +1,115 @@ +package com.zt.plat.framework.databus.server.service.impl; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientPageReqVO; +import com.zt.plat.framework.databus.server.controller.admin.vo.client.DatabusSyncClientSaveReqVO; +import com.zt.plat.framework.databus.server.convert.DatabusSyncClientConvert; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncClientDO; +import com.zt.plat.framework.databus.server.dal.mapper.DatabusSyncClientMapper; +import com.zt.plat.framework.databus.server.service.DatabusSyncClientService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.framework.databus.server.enums.ErrorCodeConstants.*; + +/** + * 数据同步客户端 Service 实现类 + * + * @author ZT + */ +@Slf4j +@Service +@Validated +public class DatabusSyncClientServiceImpl implements DatabusSyncClientService { + + @Resource + private DatabusSyncClientMapper clientMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createClient(DatabusSyncClientSaveReqVO createReqVO) { + // 校验客户端编码唯一性 + validateClientCodeUnique(null, createReqVO.getClientCode()); + + // 插入 + DatabusSyncClientDO client = DatabusSyncClientConvert.INSTANCE.convert(createReqVO); + clientMapper.insert(client); + return client.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateClient(DatabusSyncClientSaveReqVO updateReqVO) { + // 校验存在 + validateClientExists(updateReqVO.getId()); + // 校验客户端编码唯一性 + validateClientCodeUnique(updateReqVO.getId(), updateReqVO.getClientCode()); + + // 更新 + DatabusSyncClientDO updateObj = DatabusSyncClientConvert.INSTANCE.convert(updateReqVO); + clientMapper.updateById(updateObj); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteClient(Long id) { + // 校验存在 + validateClientExists(id); + // 删除 + clientMapper.deleteById(id); + } + + private void validateClientExists(Long id) { + if (clientMapper.selectById(id) == null) { + throw exception(CLIENT_NOT_EXISTS); + } + } + + private void validateClientCodeUnique(Long id, String clientCode) { + DatabusSyncClientDO client = clientMapper.selectByClientCode(clientCode); + if (client == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的客户端 + if (id == null) { + throw exception(CLIENT_CODE_DUPLICATE); + } + if (!client.getId().equals(id)) { + throw exception(CLIENT_CODE_DUPLICATE); + } + } + + @Override + public DatabusSyncClientDO getClient(Long id) { + return clientMapper.selectById(id); + } + + @Override + public PageResult getClientPage(DatabusSyncClientPageReqVO pageReqVO) { + return clientMapper.selectPage(pageReqVO); + } + + @Override + public List getClientList() { + return clientMapper.selectList(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateClientStatus(Long id, Integer enabled) { + // 校验存在 + validateClientExists(id); + // 更新状态 + DatabusSyncClientDO updateObj = new DatabusSyncClientDO(); + updateObj.setId(id); + updateObj.setEnabled(enabled); + clientMapper.updateById(updateObj); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncDeadLetterServiceImpl.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncDeadLetterServiceImpl.java new file mode 100644 index 00000000..ee16880b --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncDeadLetterServiceImpl.java @@ -0,0 +1,99 @@ +package com.zt.plat.framework.databus.server.service.impl; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.deadletter.DatabusSyncDeadLetterPageReqVO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncDeadLetterDO; +import com.zt.plat.framework.databus.server.dal.mapper.DatabusSyncDeadLetterMapper; +import com.zt.plat.framework.databus.server.enums.DeadLetterStatusEnum; +import com.zt.plat.framework.databus.server.service.DatabusSyncDeadLetterService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.time.LocalDateTime; +import java.util.List; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.framework.databus.server.enums.ErrorCodeConstants.DEAD_LETTER_NOT_EXISTS; + +/** + * 数据同步死信队列 Service 实现类 + * + * @author ZT + */ +@Slf4j +@Service +@Validated +public class DatabusSyncDeadLetterServiceImpl implements DatabusSyncDeadLetterService { + + @Resource + private DatabusSyncDeadLetterMapper deadLetterMapper; + + @Override + public DatabusSyncDeadLetterDO getDeadLetter(Long id) { + return deadLetterMapper.selectById(id); + } + + @Override + public PageResult getDeadLetterPage(DatabusSyncDeadLetterPageReqVO pageReqVO) { + return deadLetterMapper.selectPage(pageReqVO); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void reprocessDeadLetter(Long id) { + // 校验存在 + DatabusSyncDeadLetterDO deadLetter = deadLetterMapper.selectById(id); + if (deadLetter == null) { + throw exception(DEAD_LETTER_NOT_EXISTS); + } + + // TODO: 实现重新投递逻辑,将消息重新发送到 MQ 或 HTTP + log.info("[reprocessDeadLetter] 重新投递死信消息,ID: {}, 同步ID: {}", id, deadLetter.getSyncId()); + + // 更新状态为已重新投递 + DatabusSyncDeadLetterDO updateObj = new DatabusSyncDeadLetterDO(); + updateObj.setId(id); + updateObj.setStatus(DeadLetterStatusEnum.REDELIVERED.getStatus()); + updateObj.setHandled(1); + updateObj.setHandleTime(LocalDateTime.now()); + deadLetterMapper.updateById(updateObj); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void batchReprocessDeadLetter(List ids) { + if (ids == null || ids.isEmpty()) { + return; + } + for (Long id : ids) { + try { + reprocessDeadLetter(id); + } catch (Exception e) { + log.error("[batchReprocessDeadLetter] 重新投递失败,ID: {}", id, e); + } + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void markHandled(Long id, String remark) { + // 校验存在 + DatabusSyncDeadLetterDO deadLetter = deadLetterMapper.selectById(id); + if (deadLetter == null) { + throw exception(DEAD_LETTER_NOT_EXISTS); + } + + // 标记为已处理(忽略) + DatabusSyncDeadLetterDO updateObj = new DatabusSyncDeadLetterDO(); + updateObj.setId(id); + updateObj.setStatus(DeadLetterStatusEnum.IGNORED.getStatus()); + updateObj.setHandled(1); + updateObj.setHandleTime(LocalDateTime.now()); + updateObj.setHandleRemark(remark); + deadLetterMapper.updateById(updateObj); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncEventServiceImpl.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncEventServiceImpl.java new file mode 100644 index 00000000..355c5f77 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncEventServiceImpl.java @@ -0,0 +1,115 @@ +package com.zt.plat.framework.databus.server.service.impl; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventPageReqVO; +import com.zt.plat.framework.databus.server.controller.admin.vo.event.DatabusSyncEventSaveReqVO; +import com.zt.plat.framework.databus.server.convert.DatabusSyncEventConvert; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncEventDO; +import com.zt.plat.framework.databus.server.dal.mapper.DatabusSyncEventMapper; +import com.zt.plat.framework.databus.server.service.DatabusSyncEventService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.framework.databus.server.enums.ErrorCodeConstants.*; + +/** + * 数据同步事件 Service 实现类 + * + * @author ZT + */ +@Slf4j +@Service +@Validated +public class DatabusSyncEventServiceImpl implements DatabusSyncEventService { + + @Resource + private DatabusSyncEventMapper eventMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createEvent(DatabusSyncEventSaveReqVO createReqVO) { + // 校验事件类型唯一性 + validateEventTypeUnique(null, createReqVO.getEventType()); + + // 插入 + DatabusSyncEventDO event = DatabusSyncEventConvert.INSTANCE.convert(createReqVO); + eventMapper.insert(event); + return event.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateEvent(DatabusSyncEventSaveReqVO updateReqVO) { + // 校验存在 + validateEventExists(updateReqVO.getId()); + // 校验事件类型唯一性 + validateEventTypeUnique(updateReqVO.getId(), updateReqVO.getEventType()); + + // 更新 + DatabusSyncEventDO updateObj = DatabusSyncEventConvert.INSTANCE.convert(updateReqVO); + eventMapper.updateById(updateObj); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteEvent(Long id) { + // 校验存在 + validateEventExists(id); + // 删除 + eventMapper.deleteById(id); + } + + private void validateEventExists(Long id) { + if (eventMapper.selectById(id) == null) { + throw exception(EVENT_NOT_EXISTS); + } + } + + private void validateEventTypeUnique(Long id, String eventType) { + DatabusSyncEventDO event = eventMapper.selectByEventType(eventType); + if (event == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的事件 + if (id == null) { + throw exception(EVENT_TYPE_DUPLICATE); + } + if (!event.getId().equals(id)) { + throw exception(EVENT_TYPE_DUPLICATE); + } + } + + @Override + public DatabusSyncEventDO getEvent(Long id) { + return eventMapper.selectById(id); + } + + @Override + public PageResult getEventPage(DatabusSyncEventPageReqVO pageReqVO) { + return eventMapper.selectPage(pageReqVO); + } + + @Override + public List getEventList() { + return eventMapper.selectList(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateEventStatus(Long id, Integer enabled) { + // 校验存在 + validateEventExists(id); + // 更新状态 + DatabusSyncEventDO updateObj = new DatabusSyncEventDO(); + updateObj.setId(id); + updateObj.setEnabled(enabled); + eventMapper.updateById(updateObj); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncLogServiceImpl.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncLogServiceImpl.java new file mode 100644 index 00000000..64910c9a --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncLogServiceImpl.java @@ -0,0 +1,50 @@ +package com.zt.plat.framework.databus.server.service.impl; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.pushlog.DatabusSyncPushLogPageReqVO; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncLogDO; +import com.zt.plat.framework.databus.server.dal.mapper.DatabusSyncLogMapper; +import com.zt.plat.framework.databus.server.service.DatabusSyncLogService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.framework.databus.server.enums.ErrorCodeConstants.PUSH_LOG_NOT_EXISTS; + +/** + * 数据同步推送日志 Service 实现类 + * + * @author ZT + */ +@Slf4j +@Service +@Validated +public class DatabusSyncLogServiceImpl implements DatabusSyncLogService { + + @Resource + private DatabusSyncLogMapper pushLogMapper; + + @Override + public DatabusSyncLogDO getPushLog(Long id) { + return pushLogMapper.selectById(id); + } + + @Override + public PageResult getPushLogPage(DatabusSyncPushLogPageReqVO pageReqVO) { + return pushLogMapper.selectPage(pageReqVO); + } + + @Override + public void retryPush(Long id) { + // 校验存在 + DatabusSyncLogDO pushLog = pushLogMapper.selectById(id); + if (pushLog == null) { + throw exception(PUSH_LOG_NOT_EXISTS); + } + // TODO: 实现重试逻辑,将日志重新入队或触发推送 + log.info("[retryPush] 重试推送,日志ID: {}, 同步ID: {}", id, pushLog.getSyncId()); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncSubscriptionServiceImpl.java b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncSubscriptionServiceImpl.java new file mode 100644 index 00000000..20952417 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/java/com/zt/plat/framework/databus/server/service/impl/DatabusSyncSubscriptionServiceImpl.java @@ -0,0 +1,129 @@ +package com.zt.plat.framework.databus.server.service.impl; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionPageReqVO; +import com.zt.plat.framework.databus.server.controller.admin.vo.subscription.DatabusSyncSubscriptionSaveReqVO; +import com.zt.plat.framework.databus.server.convert.DatabusSyncSubscriptionConvert; +import com.zt.plat.framework.databus.server.dal.dataobject.DatabusSyncSubscriptionDO; +import com.zt.plat.framework.databus.server.dal.mapper.DatabusSyncSubscriptionMapper; +import com.zt.plat.framework.databus.server.service.DatabusSyncSubscriptionService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.framework.databus.server.enums.ErrorCodeConstants.*; + +/** + * 数据同步订阅 Service 实现类 + * + * @author ZT + */ +@Slf4j +@Service +@Validated +public class DatabusSyncSubscriptionServiceImpl implements DatabusSyncSubscriptionService { + + @Resource + private DatabusSyncSubscriptionMapper subscriptionMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createSubscription(DatabusSyncSubscriptionSaveReqVO createReqVO) { + // 校验订阅唯一性(同一个客户端不能重复订阅同一个事件) + validateSubscriptionUnique(null, createReqVO.getClientId(), createReqVO.getEventId()); + + // 插入 + DatabusSyncSubscriptionDO subscription = DatabusSyncSubscriptionConvert.INSTANCE.convert(createReqVO); + subscriptionMapper.insert(subscription); + return subscription.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateSubscription(DatabusSyncSubscriptionSaveReqVO updateReqVO) { + // 校验存在 + validateSubscriptionExists(updateReqVO.getId()); + // 校验订阅唯一性 + validateSubscriptionUnique(updateReqVO.getId(), updateReqVO.getClientId(), updateReqVO.getEventId()); + + // 更新 + DatabusSyncSubscriptionDO updateObj = DatabusSyncSubscriptionConvert.INSTANCE.convert(updateReqVO); + subscriptionMapper.updateById(updateObj); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteSubscription(Long id) { + // 校验存在 + validateSubscriptionExists(id); + // 删除 + subscriptionMapper.deleteById(id); + } + + private void validateSubscriptionExists(Long id) { + if (subscriptionMapper.selectById(id) == null) { + throw exception(SUBSCRIPTION_NOT_EXISTS); + } + } + + private void validateSubscriptionUnique(Long id, Long clientId, Long eventId) { + DatabusSyncSubscriptionDO subscription = subscriptionMapper.selectByClientIdAndEventId(clientId, eventId); + if (subscription == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的订阅 + if (id == null) { + throw exception(SUBSCRIPTION_DUPLICATE); + } + if (!subscription.getId().equals(id)) { + throw exception(SUBSCRIPTION_DUPLICATE); + } + } + + @Override + public DatabusSyncSubscriptionDO getSubscription(Long id) { + return subscriptionMapper.selectById(id); + } + + @Override + public PageResult getSubscriptionPage(DatabusSyncSubscriptionPageReqVO pageReqVO) { + return subscriptionMapper.selectPage(pageReqVO); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateSubscriptionStatus(Long id, Integer enabled) { + // 校验存在 + validateSubscriptionExists(id); + // 更新状态 + DatabusSyncSubscriptionDO updateObj = new DatabusSyncSubscriptionDO(); + updateObj.setId(id); + updateObj.setEnabled(enabled); + subscriptionMapper.updateById(updateObj); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void resetCheckpoint(Long id) { + // 校验存在 + validateSubscriptionExists(id); + // 重置断点 + DatabusSyncSubscriptionDO updateObj = new DatabusSyncSubscriptionDO(); + updateObj.setId(id); + updateObj.setLastSyncEventId(null); + updateObj.setLastSyncTime(null); + subscriptionMapper.updateById(updateObj); + } + + @Override + public void triggerSync(Long id) { + // 校验存在 + validateSubscriptionExists(id); + // TODO: 发送消息到 MQ 或调用异步任务触发同步 + log.info("[triggerSync] 手动触发同步,订阅ID: {}", id); + } + +} diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/resources/META-INF/spring.factories b/zt-framework/zt-spring-boot-starter-databus-server/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..0a736d5e --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.zt.plat.framework.databus.server.config.DatabusSyncServerAutoConfiguration diff --git a/zt-framework/zt-spring-boot-starter-databus-server/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/zt-framework/zt-spring-boot-starter-databus-server/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..46075a66 --- /dev/null +++ b/zt-framework/zt-spring-boot-starter-databus-server/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,2 @@ +com.zt.plat.framework.databus.server.config.DatabusServerAutoConfiguration +com.zt.plat.framework.databus.server.config.DatabusSyncServerAutoConfiguration diff --git a/zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/api/message/DatabusBatchMessage.java b/zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/api/message/DatabusBatchMessage.java new file mode 100644 index 00000000..3047ac10 --- /dev/null +++ b/zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/api/message/DatabusBatchMessage.java @@ -0,0 +1,106 @@ +package com.zt.plat.module.databus.api.message; + +import com.zt.plat.module.databus.enums.DatabusEventType; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.List; + +/** + * Databus 全量同步批量消息 + *

+ * 用于全量同步场景,支持分批传输 + * + * @author ZT + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DatabusBatchMessage implements Serializable { + + /** + * 消息ID(用于幂等) + */ + private String messageId; + + /** + * 全量同步任务ID + */ + private String taskId; + + /** + * 事件类型 + */ + private DatabusEventType eventType; + + /** + * 当前批次号(从1开始) + */ + private Integer batchNo; + + /** + * 总批次数 + */ + private Integer totalBatch; + + /** + * 当前批次数据条数 + */ + private Integer count; + + /** + * 总数据条数 + */ + private Integer totalCount; + + /** + * 是否最后一批 + */ + private Boolean isLastBatch; + + /** + * 数据列�� + */ + private List dataList; + + /** + * 消息产生时间 + */ + private LocalDateTime timestamp; + + /** + * 来源系统 + */ + private String source; + + /** + * 租户ID + */ + private Long tenantId; + + /** + * 创建批量消息 + */ + public static DatabusBatchMessage of(DatabusEventType eventType, String taskId, + int batchNo, int totalBatch, + List dataList, int totalCount) { + DatabusBatchMessage msg = new DatabusBatchMessage<>(); + msg.setMessageId(java.util.UUID.randomUUID().toString()); + msg.setTaskId(taskId); + msg.setEventType(eventType); + msg.setBatchNo(batchNo); + msg.setTotalBatch(totalBatch); + msg.setCount(dataList != null ? dataList.size() : 0); + msg.setTotalCount(totalCount); + msg.setIsLastBatch(batchNo >= totalBatch); + msg.setDataList(dataList); + msg.setTimestamp(LocalDateTime.now()); + return msg; + } + +} diff --git a/zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/api/message/DatabusMessage.java b/zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/api/message/DatabusMessage.java new file mode 100644 index 00000000..ccf5e4a0 --- /dev/null +++ b/zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/api/message/DatabusMessage.java @@ -0,0 +1,73 @@ +package com.zt.plat.module.databus.api.message; + +import com.zt.plat.module.databus.enums.DatabusEventType; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * Databus 增量同步消息 + *

+ * 业务推送、服务端消费、服务端转发、客户端消费统一使用此消息体 + * + * @author ZT + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DatabusMessage implements Serializable { + + /** + * 消息ID(用于幂等) + */ + private String messageId; + + /** + * 事件类型 + */ + private DatabusEventType eventType; + + /** + * 业务数据ID + */ + private Long dataId; + + /** + * 业务数据(强类型) + */ + private T data; + + /** + * 消息产生时间 + */ + private LocalDateTime timestamp; + + /** + * 来源系统 + */ + private String source; + + /** + * 租户ID + */ + private Long tenantId; + + /** + * 创建简单消息 + */ + public static DatabusMessage of(DatabusEventType eventType, Long dataId, T data) { + DatabusMessage msg = new DatabusMessage<>(); + msg.setMessageId(java.util.UUID.randomUUID().toString()); + msg.setEventType(eventType); + msg.setDataId(dataId); + msg.setData(data); + msg.setTimestamp(LocalDateTime.now()); + return msg; + } + +} diff --git a/zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/enums/DatabusEventType.java b/zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/enums/DatabusEventType.java new file mode 100644 index 00000000..da726cd3 --- /dev/null +++ b/zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/enums/DatabusEventType.java @@ -0,0 +1,306 @@ +package com.zt.plat.module.databus.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * Databus 事件类型枚举 + *

+ * 三级结构: 模块_数据类型_操作 + *

+ * Topic 命名规则: + * - 业务推送到服务端: {topicBase}-{module}-{entity}-{action} + * - 服务端转发到客户端: {topicBase}-{module}-{entity}-{action}-{clientCode} + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum DatabusEventType { + + // ==================== SYSTEM 系统模块 ==================== + + /** + * 用户-创建 + */ + SYSTEM_USER_CREATE("system", "user", "create", "用户创建"), + + /** + * 用户-更新 + */ + SYSTEM_USER_UPDATE("system", "user", "update", "用户更新"), + + /** + * 用户-删除 + */ + SYSTEM_USER_DELETE("system", "user", "delete", "用户删除"), + + /** + * 用户-全量同步 + */ + SYSTEM_USER_FULL("system", "user", "full", "用户全量同步"), + + /** + * 部门-创建 + */ + SYSTEM_DEPT_CREATE("system", "dept", "create", "部门创建"), + + /** + * 部门-更新 + */ + SYSTEM_DEPT_UPDATE("system", "dept", "update", "部门更新"), + + /** + * 部门-删除 + */ + SYSTEM_DEPT_DELETE("system", "dept", "delete", "部门删除"), + + /** + * 部门-全量同步 + */ + SYSTEM_DEPT_FULL("system", "dept", "full", "部门全量同步"), + + /** + * 组织机构-创建(兼容老代码,保留但不推荐使用) + */ + @Deprecated + SYSTEM_ORG_CREATE("system", "org", "create", "组织机构创建"), + + /** + * 组织机构-更新(兼容老代码,保留但不推荐使用) + */ + @Deprecated + SYSTEM_ORG_UPDATE("system", "org", "update", "组织机构更新"), + + /** + * 组织机构-删除(兼容老代码,保留但不推荐使用) + */ + @Deprecated + SYSTEM_ORG_DELETE("system", "org", "delete", "组织机构删除"), + + /** + * 组织机构-全量同步(兼容老代码,保留但不推荐使用) + */ + @Deprecated + SYSTEM_ORG_FULL("system", "org", "full", "组织机构全量同步"), + + /** + * 岗位-创建 + */ + SYSTEM_POST_CREATE("system", "post", "create", "岗位创建"), + + /** + * 岗位-更新 + */ + SYSTEM_POST_UPDATE("system", "post", "update", "岗位更新"), + + /** + * 岗位-删除 + */ + SYSTEM_POST_DELETE("system", "post", "delete", "岗位删除"), + + /** + * 岗位-全量同步 + */ + SYSTEM_POST_FULL("system", "post", "full", "岗位全量同步"), + + /** + * 角色-创建 + */ + SYSTEM_ROLE_CREATE("system", "role", "create", "角色创建"), + + /** + * 角色-更新 + */ + SYSTEM_ROLE_UPDATE("system", "role", "update", "角色更新"), + + /** + * 角色-删除 + */ + SYSTEM_ROLE_DELETE("system", "role", "delete", "角色删除"), + + /** + * 角色-全量同步 + */ + SYSTEM_ROLE_FULL("system", "role", "full", "角色全量同步"), + + /** + * 字典-创建 + */ + SYSTEM_DICT_CREATE("system", "dict", "create", "字典创建"), + + /** + * 字典-更新 + */ + SYSTEM_DICT_UPDATE("system", "dict", "update", "字典更新"), + + /** + * 字典-删除 + */ + SYSTEM_DICT_DELETE("system", "dict", "delete", "字典删除"), + + /** + * 字典-全量同步 + */ + SYSTEM_DICT_FULL("system", "dict", "full", "字典全量同步"), + + // ==================== BASE 基础模块 ==================== + + /** + * 物料-创建 + */ + BASE_MATERIAL_CREATE("base", "material", "create", "物料创建"), + + /** + * 物料-更新 + */ + BASE_MATERIAL_UPDATE("base", "material", "update", "物料更新"), + + /** + * 物料-删除 + */ + BASE_MATERIAL_DELETE("base", "material", "delete", "物料删除"), + + /** + * 物料-全量同步 + */ + BASE_MATERIAL_FULL("base", "material", "full", "物料全量同步"), + + /** + * 供应��-创建 + */ + BASE_SUPPLIER_CREATE("base", "supplier", "create", "供应商创建"), + + /** + * 供应商-更新 + */ + BASE_SUPPLIER_UPDATE("base", "supplier", "update", "供应商更新"), + + /** + * 供应商-删除 + */ + BASE_SUPPLIER_DELETE("base", "supplier", "delete", "供应商删除"), + + /** + * 供应商-全量同步 + */ + BASE_SUPPLIER_FULL("base", "supplier", "full", "供应商全量同步"), + + /** + * 客户-创建 + */ + BASE_CUSTOMER_CREATE("base", "customer", "create", "客户创建"), + + /** + * 客户-更新 + */ + BASE_CUSTOMER_UPDATE("base", "customer", "update", "客户更新"), + + /** + * 客户-删除 + */ + BASE_CUSTOMER_DELETE("base", "customer", "delete", "客户删除"), + + /** + * 客户-全量同步 + */ + BASE_CUSTOMER_FULL("base", "customer", "full", "客户全量同步"), + + ; + + /** + * 模块编码 + */ + private final String module; + + /** + * 实体编码 + */ + private final String entity; + + /** + * 操作编码 + */ + private final String action; + + /** + * 事件名称 + */ + private final String name; + + /** + * 获取Topic后缀(不含topicBase和clientCode) + * 格式: {module}-{entity}-{action} + */ + public String getTopicSuffix() { + return String.format("%s-%s-%s", module, entity, action); + } + + /** + * 获取完整Topic名称(服务端转发用) + * 格式: {topicBase}-{module}-{entity}-{action}-{clientCode} + */ + public String getTopic(String topicBase, String clientCode) { + return String.format("%s-%s-%s-%s-%s", topicBase, module, entity, action, clientCode); + } + + /** + * 获取完整Topic名称(业务推送用,不带clientCode) + * 格式: {topicBase}-{module}-{entity}-{action} + */ + public String getTopic(String topicBase) { + return String.format("%s-%s-%s-%s", topicBase, module, entity, action); + } + + /** + * 根据Topic后缀获取枚举 + * + * @param topicSuffix Topic后缀(格式: module-entity-action) + * @return 枚举值,未找到返回null + */ + public static DatabusEventType getByTopicSuffix(String topicSuffix) { + if (topicSuffix == null) { + return null; + } + for (DatabusEventType type : values()) { + if (type.getTopicSuffix().equalsIgnoreCase(topicSuffix)) { + return type; + } + } + return null; + } + + /** + * 根据模块、实体、操作获取枚举 + * + * @param module 模块编码 + * @param entity 实体编码 + * @param action 操作编码 + * @return 枚举值,未找到返回null + */ + public static DatabusEventType getByModuleEntityAction(String module, String entity, String action) { + for (DatabusEventType type : values()) { + if (type.getModule().equalsIgnoreCase(module) + && type.getEntity().equalsIgnoreCase(entity) + && type.getAction().equalsIgnoreCase(action)) { + return type; + } + } + return null; + } + + /** + * 判断是否为全量同步事件 + */ + public boolean isFullSync() { + return "full".equalsIgnoreCase(this.action); + } + + /** + * 判断是否为增量同步事件 + */ + public boolean isIncrementalSync() { + return !isFullSync(); + } + +} diff --git a/zt-module-databus/zt-module-databus-server/pom.xml b/zt-module-databus/zt-module-databus-server/pom.xml index 008a387f..e94168ad 100644 --- a/zt-module-databus/zt-module-databus-server/pom.xml +++ b/zt-module-databus/zt-module-databus-server/pom.xml @@ -42,6 +42,13 @@ ${revision} + + + com.zt.plat + zt-spring-boot-starter-databus-server + ${revision} + + com.zt.plat diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/mq/consumer/DatabusDeptChangeConsumer.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/mq/consumer/DatabusDeptChangeConsumer.java new file mode 100644 index 00000000..e3794b25 --- /dev/null +++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/mq/consumer/DatabusDeptChangeConsumer.java @@ -0,0 +1,90 @@ +package com.zt.plat.module.databus.mq.consumer; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.zt.plat.framework.databus.server.core.event.DatabusEvent; +import com.zt.plat.framework.databus.server.core.sync.DatabusIncrementalSyncService; +import com.zt.plat.module.system.api.mq.DatabusDeptChangeMessage; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +/** + * Databus 部门变更消息消费者 + *

+ * 消费来自 system-server 的部门变更消息,通过增量同步服务进行: + * 1. 三态判断(事件/客户端/订阅是否启用) + * 2. 记录到 event_record 流水表 + * 3. 推送到客户端专属 Topic(databus-sync-{clientCode}) + * + * @author ZT + */ +@Slf4j +@Component +@RocketMQMessageListener( + topic = DatabusDeptChangeMessage.TOPIC, + consumerGroup = DatabusDeptChangeMessage.TOPIC + "_CONSUMER" +) +public class DatabusDeptChangeConsumer implements RocketMQListener { + + private static final String SOURCE_SERVICE = "system-server"; + + @Resource + private DatabusIncrementalSyncService databusIncrementalSyncService; + + @Resource + private ObjectMapper objectMapper; + + @Override + public void onMessage(DatabusDeptChangeMessage message) { + log.info("[Databus] 收到部门变更消息, action={}, deptId={}", message.getAction(), message.getDeptId()); + + try { + // 构建完整的业务数据快照 + Map dataMap = new HashMap<>(); + dataMap.put("id", message.getDeptId()); + dataMap.put("code", message.getDeptCode()); + dataMap.put("name", message.getDeptName()); + dataMap.put("shortName", message.getShortName()); + dataMap.put("parentId", message.getParentId()); + dataMap.put("sort", message.getSort()); + dataMap.put("leaderUserId", message.getLeaderUserId()); + dataMap.put("phone", message.getPhone()); + dataMap.put("email", message.getStatus()); + dataMap.put("isCompany", message.getIsCompany()); + dataMap.put("isGroup", message.getIsGroup()); + dataMap.put("deptSource", message.getDeptSource()); + dataMap.put("tenantId", message.getTenantId()); + dataMap.put("eventTime", message.getEventTime()); + + // 构建完整的事件类型: system-dept-{action} + String eventType = String.format("system-dept-%s", message.getAction().toLowerCase()); + + // 构建 Databus 事件 + DatabusEvent databusEvent = DatabusEvent.builder() + .eventType(eventType) + .eventAction(message.getAction()) + .dataSnapshot(objectMapper.writeValueAsString(dataMap)) + .dataVersion(1) + .sourceService(SOURCE_SERVICE) + .sourceTopic(DatabusDeptChangeMessage.TOPIC) + .tenantId(message.getTenantId()) + .eventTime(message.getEventTime()) + .build(); + + // 调用增量同步服务处理(三态判断 + 记录流水 + 推送客户端Topic) + databusIncrementalSyncService.processEvent(databusEvent); + + log.info("[Databus] 部门变更事件处理完成, eventType={}, deptId={}", + eventType, message.getDeptId()); + } catch (Exception e) { + log.error("[Databus] 处理部门变更消息失败, action={}, deptId={}", + message.getAction(), message.getDeptId(), e); + throw new RuntimeException("处理部门变更消息失败", e); + } + } +} diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/mq/consumer/DatabusPostChangeConsumer.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/mq/consumer/DatabusPostChangeConsumer.java new file mode 100644 index 00000000..c51bd5e3 --- /dev/null +++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/mq/consumer/DatabusPostChangeConsumer.java @@ -0,0 +1,73 @@ +package com.zt.plat.module.databus.mq.consumer; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.zt.plat.framework.databus.server.core.event.DatabusEvent; +import com.zt.plat.framework.databus.server.core.sync.DatabusIncrementalSyncService; +import com.zt.plat.module.system.api.mq.DatabusPostChangeMessage; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +/** + * Databus 岗位变更消息消费者 + */ +@Slf4j +@Component +@RocketMQMessageListener( + topic = DatabusPostChangeMessage.TOPIC, + consumerGroup = DatabusPostChangeMessage.TOPIC + "_CONSUMER" +) +public class DatabusPostChangeConsumer implements RocketMQListener { + + private static final String SOURCE_SERVICE = "system-server"; + + @Resource + private DatabusIncrementalSyncService databusIncrementalSyncService; + + @Resource + private ObjectMapper objectMapper; + + @Override + public void onMessage(DatabusPostChangeMessage message) { + log.info("[Databus] 收到岗位变更消息, action={}, postId={}", message.getAction(), message.getPostId()); + + try { + Map dataMap = new HashMap<>(); + dataMap.put("id", message.getPostId()); + dataMap.put("code", message.getPostCode()); + dataMap.put("name", message.getPostName()); + dataMap.put("sort", message.getSort()); + dataMap.put("status", message.getStatus()); + dataMap.put("remark", message.getRemark()); + dataMap.put("tenantId", message.getTenantId()); + dataMap.put("eventTime", message.getEventTime()); + + String eventType = String.format("system-post-%s", message.getAction().toLowerCase()); + + DatabusEvent databusEvent = DatabusEvent.builder() + .eventType(eventType) + .eventAction(message.getAction()) + .dataSnapshot(objectMapper.writeValueAsString(dataMap)) + .dataVersion(1) + .sourceService(SOURCE_SERVICE) + .sourceTopic(DatabusPostChangeMessage.TOPIC) + .tenantId(message.getTenantId()) + .eventTime(message.getEventTime()) + .build(); + + databusIncrementalSyncService.processEvent(databusEvent); + + log.info("[Databus] 岗位变更事件处理完成, eventType={}, postId={}", + eventType, message.getPostId()); + } catch (Exception e) { + log.error("[Databus] 处理岗位变更消息失败, action={}, postId={}", + message.getAction(), message.getPostId(), e); + throw new RuntimeException("处理岗位变更消息失败", e); + } + } +} diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/mq/consumer/DatabusUserChangeConsumer.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/mq/consumer/DatabusUserChangeConsumer.java new file mode 100644 index 00000000..f7af1838 --- /dev/null +++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/mq/consumer/DatabusUserChangeConsumer.java @@ -0,0 +1,79 @@ +package com.zt.plat.module.databus.mq.consumer; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.zt.plat.framework.databus.server.core.event.DatabusEvent; +import com.zt.plat.framework.databus.server.core.sync.DatabusIncrementalSyncService; +import com.zt.plat.module.system.api.mq.DatabusUserChangeMessage; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +/** + * Databus 用户变更消息消费者 + */ +@Slf4j +@Component +@RocketMQMessageListener( + topic = DatabusUserChangeMessage.TOPIC, + consumerGroup = DatabusUserChangeMessage.TOPIC + "_CONSUMER" +) +public class DatabusUserChangeConsumer implements RocketMQListener { + + private static final String SOURCE_SERVICE = "system-server"; + + @Resource + private DatabusIncrementalSyncService databusIncrementalSyncService; + + @Resource + private ObjectMapper objectMapper; + + @Override + public void onMessage(DatabusUserChangeMessage message) { + log.info("[Databus] 收到用户变更消息, action={}, userId={}", message.getAction(), message.getUserId()); + + try { + Map dataMap = new HashMap<>(); + dataMap.put("id", message.getUserId()); + dataMap.put("username", message.getUsername()); + dataMap.put("nickname", message.getNickname()); + dataMap.put("remark", message.getRemark()); + dataMap.put("deptIds", message.getDeptIds()); + dataMap.put("postIds", message.getPostIds()); + dataMap.put("email", message.getEmail()); + dataMap.put("mobile", message.getMobile()); + dataMap.put("sex", message.getSex()); + dataMap.put("avatar", message.getAvatar()); + dataMap.put("status", message.getStatus()); + dataMap.put("userSource", message.getUserSource()); + dataMap.put("tenantId", message.getTenantId()); + dataMap.put("eventTime", message.getEventTime()); + + String eventType = String.format("system-user-%s", message.getAction().toLowerCase()); + + DatabusEvent databusEvent = DatabusEvent.builder() + .eventType(eventType) + .eventAction(message.getAction()) + .dataSnapshot(objectMapper.writeValueAsString(dataMap)) + .dataVersion(1) + .sourceService(SOURCE_SERVICE) + .sourceTopic(DatabusUserChangeMessage.TOPIC) + .tenantId(message.getTenantId()) + .eventTime(message.getEventTime()) + .build(); + + databusIncrementalSyncService.processEvent(databusEvent); + + log.info("[Databus] 用户变更事件处理完成, eventType={}, userId={}", + eventType, message.getUserId()); + } catch (Exception e) { + log.error("[Databus] 处理用户变更消息失败, action={}, userId={}", + message.getAction(), message.getUserId(), e); + throw new RuntimeException("处理用户变更消息失败", e); + } + } +} diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/provider/DeptDataFeignProvider.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/provider/DeptDataFeignProvider.java new file mode 100644 index 00000000..1423be81 --- /dev/null +++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/provider/DeptDataFeignProvider.java @@ -0,0 +1,85 @@ +package com.zt.plat.module.databus.provider; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.databus.server.core.provider.DataProvider; +import com.zt.plat.framework.databus.server.core.provider.DataProviderRegistry; +import com.zt.plat.module.databus.api.dto.CursorPageReqDTO; +import com.zt.plat.module.databus.api.dto.CursorPageResult; +import com.zt.plat.module.databus.api.data.DatabusDeptData; +import com.zt.plat.module.databus.api.provider.DatabusDeptProviderApi; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; + +/** + * 部门数据提供者 + *

+ * 通过 Feign 调用 system-server 获取部门数据 + * + * @author ZT + */ +@Slf4j +@Component +public class DeptDataFeignProvider implements DataProvider { + + public static final String PROVIDER_TYPE = "DEPT"; + + @Resource + private DatabusDeptProviderApi deptProviderApi; + + @Resource + private DataProviderRegistry dataProviderRegistry; + + @PostConstruct + public void init() { + dataProviderRegistry.register(this); + } + + @Override + public String getProviderType() { + return PROVIDER_TYPE; + } + + @Override + public CursorPageData getPageByCursor(LocalDateTime cursorTime, Long cursorId, + int batchSize, Long tenantId) { + CursorPageReqDTO reqDTO = CursorPageReqDTO.builder() + .cursorTime(cursorTime) + .cursorId(cursorId) + .batchSize(batchSize) + .tenantId(tenantId) + .build(); + + CommonResult> result = deptProviderApi.getPageByCursor(reqDTO); + if (!result.isSuccess()) { + throw new RuntimeException("获取部门数据失败: " + result.getMsg()); + } + + CursorPageResult pageResult = result.getData(); + return CursorPageData.of( + pageResult.getList(), + pageResult.getNextCursorTime(), + pageResult.getNextCursorId(), + pageResult.getCount(), + Boolean.TRUE.equals(pageResult.getHasMore()), + (pageResult.getTotal() != null ? pageResult.getTotal() : 0L) + ); + } + + @Override + public long count(Long tenantId) { + CommonResult result = deptProviderApi.count(tenantId); + if (!result.isSuccess()) { + throw new RuntimeException("获取部门总数失败: " + result.getMsg()); + } + return result.getData(); + } + + @Override + public Long extractUid(DatabusDeptData data) { + return data.getId(); + } +} diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/provider/PostDataFeignProvider.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/provider/PostDataFeignProvider.java new file mode 100644 index 00000000..cfb4d8e3 --- /dev/null +++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/provider/PostDataFeignProvider.java @@ -0,0 +1,81 @@ +package com.zt.plat.module.databus.provider; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.databus.server.core.provider.DataProvider; +import com.zt.plat.framework.databus.server.core.provider.DataProviderRegistry; +import com.zt.plat.module.databus.api.dto.CursorPageReqDTO; +import com.zt.plat.module.databus.api.dto.CursorPageResult; +import com.zt.plat.module.databus.api.data.DatabusPostData; +import com.zt.plat.module.databus.api.provider.DatabusPostProviderApi; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; + +/** + * 岗位数据提供者 + */ +@Slf4j +@Component +public class PostDataFeignProvider implements DataProvider { + + public static final String PROVIDER_TYPE = "POST"; + + @Resource + private DatabusPostProviderApi postProviderApi; + + @Resource + private DataProviderRegistry dataProviderRegistry; + + @PostConstruct + public void init() { + dataProviderRegistry.register(this); + } + + @Override + public String getProviderType() { + return PROVIDER_TYPE; + } + + @Override + public CursorPageData getPageByCursor(LocalDateTime cursorTime, Long cursorId, + int batchSize, Long tenantId) { + CursorPageReqDTO reqDTO = CursorPageReqDTO.builder() + .cursorTime(cursorTime) + .cursorId(cursorId) + .batchSize(batchSize) + .tenantId(tenantId) + .build(); + + CommonResult> result = postProviderApi.getPageByCursor(reqDTO); + if (!result.isSuccess()) { + throw new RuntimeException("获取岗位数据失败: " + result.getMsg()); + } + + CursorPageResult pageResult = result.getData(); + return CursorPageData.of( + pageResult.getList(), + pageResult.getNextCursorTime(), + pageResult.getNextCursorId(), + pageResult.getCount(), + Boolean.TRUE.equals(pageResult.getHasMore()), + (pageResult.getTotal() != null ? pageResult.getTotal() : 0L) + ); + } + + @Override + public long count(Long tenantId) { + CommonResult result = postProviderApi.count(tenantId); + if (!result.isSuccess()) { + throw new RuntimeException("获取岗位总数失败: " + result.getMsg()); + } + return result.getData(); + } + + @Override + public Long extractUid(DatabusPostData data) { + return data.getId(); + } +} diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/provider/UserDataFeignProvider.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/provider/UserDataFeignProvider.java new file mode 100644 index 00000000..b2c162e3 --- /dev/null +++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/provider/UserDataFeignProvider.java @@ -0,0 +1,81 @@ +package com.zt.plat.module.databus.provider; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.databus.server.core.provider.DataProvider; +import com.zt.plat.framework.databus.server.core.provider.DataProviderRegistry; +import com.zt.plat.module.databus.api.dto.CursorPageReqDTO; +import com.zt.plat.module.databus.api.dto.CursorPageResult; +import com.zt.plat.module.databus.api.data.DatabusAdminUserData; +import com.zt.plat.module.databus.api.provider.DatabusUserProviderApi; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; + +/** + * 用户数据提供者 + */ +@Slf4j +@Component +public class UserDataFeignProvider implements DataProvider { + + public static final String PROVIDER_TYPE = "USER"; + + @Resource + private DatabusUserProviderApi userProviderApi; + + @Resource + private DataProviderRegistry dataProviderRegistry; + + @PostConstruct + public void init() { + dataProviderRegistry.register(this); + } + + @Override + public String getProviderType() { + return PROVIDER_TYPE; + } + + @Override + public CursorPageData getPageByCursor(LocalDateTime cursorTime, Long cursorId, + int batchSize, Long tenantId) { + CursorPageReqDTO reqDTO = CursorPageReqDTO.builder() + .cursorTime(cursorTime) + .cursorId(cursorId) + .batchSize(batchSize) + .tenantId(tenantId) + .build(); + + CommonResult> result = userProviderApi.getPageByCursor(reqDTO); + if (!result.isSuccess()) { + throw new RuntimeException("获取用户数据失败: " + result.getMsg()); + } + + CursorPageResult pageResult = result.getData(); + return CursorPageData.of( + pageResult.getList(), + pageResult.getNextCursorTime(), + pageResult.getNextCursorId(), + pageResult.getCount(), + Boolean.TRUE.equals(pageResult.getHasMore()), + (pageResult.getTotal() != null ? pageResult.getTotal() : 0L) + ); + } + + @Override + public long count(Long tenantId) { + CommonResult result = userProviderApi.count(tenantId); + if (!result.isSuccess()) { + throw new RuntimeException("获取用户总数失败: " + result.getMsg()); + } + return result.getData(); + } + + @Override + public Long extractUid(DatabusAdminUserData data) { + return data.getId(); + } +}