yumeng пре 1 недеља
родитељ
комит
44503a96c8
34 измењених фајлова са 4065 додато и 141 уклоњено
  1. 902 0
      logs/tencent-baidu-tracking.log
  2. 4 0
      pom.xml
  3. 118 9
      src/main/java/com/adx/tencent/AppConfiguration.java
  4. 200 0
      src/main/java/com/adx/tencent/config/AppProperties.java
  5. 526 129
      src/main/java/com/adx/tencent/conversionsync/ConversionSyncService.java
  6. 119 0
      src/main/java/com/adx/tencent/honor/HonorBackgroundTasks.java
  7. 191 0
      src/main/java/com/adx/tencent/honor/HonorClient.java
  8. 205 0
      src/main/java/com/adx/tencent/honor/HonorColdStore.java
  9. 67 0
      src/main/java/com/adx/tencent/honor/HonorColdWorker.java
  10. 35 0
      src/main/java/com/adx/tencent/honor/HonorConversionSyncRunner.java
  11. 180 0
      src/main/java/com/adx/tencent/honor/HonorConversionSyncService.java
  12. 246 0
      src/main/java/com/adx/tencent/honor/HonorHotStore.java
  13. 113 0
      src/main/java/com/adx/tencent/honor/HonorPlacement.java
  14. 70 0
      src/main/java/com/adx/tencent/honor/HonorRetryService.java
  15. 75 0
      src/main/java/com/adx/tencent/honor/HonorTagEventResolver.java
  16. 71 0
      src/main/java/com/adx/tencent/honor/HonorTagEventSyncService.java
  17. 288 0
      src/main/java/com/adx/tencent/honor/HonorTrackingController.java
  18. 14 0
      src/main/java/com/adx/tencent/honor/mapper/HonorBidEventMapper.java
  19. 12 0
      src/main/java/com/adx/tencent/honor/mapper/HonorConversionMapper.java
  20. 14 0
      src/main/java/com/adx/tencent/honor/mapper/HonorMediaCallbackMapper.java
  21. 14 0
      src/main/java/com/adx/tencent/honor/mapper/HonorTagEventMapper.java
  22. 9 0
      src/main/java/com/adx/tencent/honor/mapper/HonorTrackingReportMapper.java
  23. 117 0
      src/main/java/com/adx/tencent/honor/model/HonorBidRecord.java
  24. 53 0
      src/main/java/com/adx/tencent/honor/model/HonorConversionRecord.java
  25. 50 0
      src/main/java/com/adx/tencent/honor/model/HonorMediaCallbackRecord.java
  26. 14 0
      src/main/java/com/adx/tencent/honor/model/HonorTagEventRecord.java
  27. 29 0
      src/main/java/com/adx/tencent/honor/model/HonorTrackingRecord.java
  28. 18 3
      src/main/java/com/adx/tencent/storage/TiDBColdStore.java
  29. 84 0
      src/main/resources/mapper/HonorBidEventMapper.xml
  30. 32 0
      src/main/resources/mapper/HonorConversionMapper.xml
  31. 65 0
      src/main/resources/mapper/HonorMediaCallbackMapper.xml
  32. 21 0
      src/main/resources/mapper/HonorTagEventMapper.xml
  33. 9 0
      src/main/resources/mapper/HonorTrackingReportMapper.xml
  34. 100 0
      src/main/resources/schema-honor.sql

+ 902 - 0
logs/tencent-baidu-tracking.log

@@ -0,0 +1,902 @@
+2026-07-21 10:24:47.521 [main] INFO  com.adx.tencent.Application - Starting Application using Java 17.0.14 with PID 99261 (/Users/yumeng/IdeaProjects/tencent-baidu-tracking/target/classes started by yumeng in /Users/yumeng/IdeaProjects/tencent-baidu-tracking)
+2026-07-21 10:24:47.523 [main] INFO  com.adx.tencent.Application - The following 1 profile is active: "dev"
+2026-07-21 10:24:47.767 [main] INFO  o.s.d.r.c.RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
+2026-07-21 10:24:47.768 [main] INFO  o.s.d.r.c.RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
+2026-07-21 10:24:47.783 [main] INFO  o.s.d.r.c.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 7 ms. Found 0 Redis repository interfaces.
+2026-07-21 10:24:47.940 [main] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat initialized with port 8788 (http)
+2026-07-21 10:24:47.944 [main] INFO  o.a.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8788"]
+2026-07-21 10:24:47.945 [main] INFO  o.a.catalina.core.StandardService - Starting service [Tomcat]
+2026-07-21 10:24:47.945 [main] INFO  o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.20]
+2026-07-21 10:24:47.961 [main] INFO  o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
+2026-07-21 10:24:47.961 [main] INFO  o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 422 ms
+2026-07-21 10:24:48.132 [main] INFO  com.zaxxer.hikari.HikariDataSource - adx-tidb - Starting...
+2026-07-21 10:24:48.681 [main] INFO  com.zaxxer.hikari.pool.HikariPool - adx-tidb - Added connection com.mysql.cj.jdbc.ConnectionImpl@5822ecda
+2026-07-21 10:24:48.683 [main] INFO  com.zaxxer.hikari.HikariDataSource - adx-tidb - Start completed.
+2026-07-21 10:24:51.853 [main] INFO  com.adx.tencent.AppConfiguration - TiDB cold store initialized and migrated
+2026-07-21 10:24:51.948 [main] INFO  com.zaxxer.hikari.HikariDataSource - adx-tidb - Starting...
+2026-07-21 10:24:52.495 [main] INFO  com.zaxxer.hikari.pool.HikariPool - adx-tidb - Added connection com.mysql.cj.jdbc.ConnectionImpl@9bf63d2
+2026-07-21 10:24:52.495 [main] INFO  com.zaxxer.hikari.HikariDataSource - adx-tidb - Start completed.
+2026-07-21 10:24:55.755 [main] INFO  c.a.t.b.c.BaiduClueStoreConfiguration - Baidu clue cold store initialized and migrated
+2026-07-21 10:24:55.880 [main] INFO  o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8788"]
+2026-07-21 10:24:55.884 [main] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port 8788 (http) with context path ''
+2026-07-21 10:24:55.888 [main] INFO  com.adx.tencent.Application - Started Application in 8.552 seconds (process running for 9.155)
+2026-07-21 10:24:55.889 [main] INFO  c.a.t.AppConfiguration$BackgroundTasks - Cold worker started
+2026-07-21 10:24:55.889 [main] INFO  c.a.t.AppConfiguration$BackgroundTasks - Conversion sync started (skip leader election)
+2026-07-21 10:24:55.889 [Thread-3] INFO  c.a.t.AppConfiguration$BackgroundTasks - [ConversionSync] task started, interval=30000ms
+2026-07-21 10:24:55.889 [main] INFO  c.a.t.AppConfiguration$BackgroundTasks - Callback retry started (skip leader election)
+2026-07-21 10:24:55.889 [Thread-3] INFO  c.a.t.AppConfiguration$BackgroundTasks - [ConversionSync] executing...
+2026-07-21 10:24:55.890 [main] INFO  c.a.t.AppConfiguration$BackgroundTasks - Tag event sync started (skip leader election)
+2026-07-21 10:24:55.890 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=1, mode=REALTIME
+2026-07-21 10:24:55.890 [Thread-4] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] task started, interval=300000ms, limit=100
+2026-07-21 10:24:55.890 [Thread-4] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] executing...
+2026-07-21 10:24:55.890 [Thread-5] INFO  c.a.t.AppConfiguration$BackgroundTasks - [TagEventSync] task started, interval=300000ms
+2026-07-21 10:24:56.033 [Thread-4] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] done: fetched=0 sent=0 failed=0
+2026-07-21 10:24:56.034 [Thread-4] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] sleeping 300000ms until next run
+2026-07-21 10:24:56.595 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=1 returned 1000 records, responsePageSize=26, mode=REALTIME
+2026-07-21 10:25:26.072 [Thread-5] ERROR c.a.t.AppConfiguration$BackgroundTasks - [TagEventSync] error: Unable to connect to Redis
+org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis
+	at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.translateException(LettuceConnectionFactory.java:1795)
+	at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.getConnection(LettuceConnectionFactory.java:1726)
+	at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$SharedConnection.getNativeConnection(LettuceConnectionFactory.java:1528)
+	at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$SharedConnection.lambda$getConnection$0(LettuceConnectionFactory.java:1508)
+	at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.doInLock(LettuceConnectionFactory.java:1469)
+	at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$SharedConnection.getConnection(LettuceConnectionFactory.java:1505)
+	at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getSharedConnection(LettuceConnectionFactory.java:1191)
+	at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getConnection(LettuceConnectionFactory.java:997)
+	at org.springframework.data.redis.core.RedisConnectionUtils.fetchConnection(RedisConnectionUtils.java:195)
+	at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:144)
+	at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:105)
+	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:383)
+	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:363)
+	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:350)
+	at org.springframework.data.redis.core.RedisTemplate.executePipelined(RedisTemplate.java:471)
+	at org.springframework.data.redis.core.RedisTemplate.executePipelined(RedisTemplate.java:465)
+	at com.adx.tencent.tagsync.TagEventSyncService.syncOnce(TagEventSyncService.java:51)
+	at com.adx.tencent.AppConfiguration$BackgroundTasks.runTagEventSync(AppConfiguration.java:552)
+	at com.adx.tencent.AppConfiguration$BackgroundTasks.lambda$run$13(AppConfiguration.java:451)
+	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
+	at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)
+	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java)
+	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
+	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
+	at java.base/java.lang.Thread.run(Thread.java:842)
+Caused by: io.lettuce.core.RedisConnectionException: Unable to connect to cq-crs-47p6pmwo.sql.tencentcdb.com/<unresolved>:28036
+	at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:78)
+	at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:56)
+	at io.lettuce.core.AbstractRedisClient.getConnection(AbstractRedisClient.java:350)
+	at io.lettuce.core.RedisClient.connect(RedisClient.java:215)
+	at org.springframework.data.redis.connection.lettuce.StandaloneConnectionProvider.lambda$getConnection$1(StandaloneConnectionProvider.java:112)
+	at java.base/java.util.Optional.orElseGet(Optional.java:364)
+	at org.springframework.data.redis.connection.lettuce.StandaloneConnectionProvider.getConnection(StandaloneConnectionProvider.java:112)
+	at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.getConnection(LettuceConnectionFactory.java:1724)
+	... 23 common frames omitted
+Caused by: io.netty.channel.ConnectTimeoutException: connection timed out after 10000 ms: cq-crs-47p6pmwo.sql.tencentcdb.com/162.14.252.220:28036
+	at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe$1.run(AbstractNioChannel.java:263)
+	at io.netty.util.concurrent.PromiseTask.runTask(PromiseTask.java:98)
+	at io.netty.util.concurrent.ScheduledFutureTask.run(ScheduledFutureTask.java:153)
+	at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:173)
+	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute$$$capture(AbstractEventExecutor.java:166)
+	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java)
+	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470)
+	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569)
+	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
+	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
+	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
+	... 1 common frames omitted
+2026-07-21 10:25:46.103 [main] INFO  com.adx.tencent.Application - Starting Application using Java 17.0.14 with PID 99482 (/Users/yumeng/IdeaProjects/tencent-baidu-tracking/target/classes started by yumeng in /Users/yumeng/IdeaProjects/tencent-baidu-tracking)
+2026-07-21 10:25:46.103 [main] INFO  com.adx.tencent.Application - The following 1 profile is active: "dev"
+2026-07-21 10:25:46.362 [main] INFO  o.s.d.r.c.RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
+2026-07-21 10:25:46.363 [main] INFO  o.s.d.r.c.RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
+2026-07-21 10:25:46.380 [main] INFO  o.s.d.r.c.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 7 ms. Found 0 Redis repository interfaces.
+2026-07-21 10:25:46.589 [main] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat initialized with port 8788 (http)
+2026-07-21 10:25:46.594 [main] INFO  o.a.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8788"]
+2026-07-21 10:25:46.595 [main] INFO  o.a.catalina.core.StandardService - Starting service [Tomcat]
+2026-07-21 10:25:46.595 [main] INFO  o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.20]
+2026-07-21 10:25:46.611 [main] INFO  o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
+2026-07-21 10:25:46.611 [main] INFO  o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 488 ms
+2026-07-21 10:25:46.780 [main] INFO  com.zaxxer.hikari.HikariDataSource - adx-tidb - Starting...
+2026-07-21 10:25:47.639 [main] INFO  com.zaxxer.hikari.pool.HikariPool - adx-tidb - Added connection com.mysql.cj.jdbc.ConnectionImpl@1736c1e4
+2026-07-21 10:25:47.641 [main] INFO  com.zaxxer.hikari.HikariDataSource - adx-tidb - Start completed.
+2026-07-21 10:25:51.058 [main] INFO  com.adx.tencent.AppConfiguration - TiDB cold store initialized and migrated
+2026-07-21 10:25:51.116 [main] INFO  com.zaxxer.hikari.HikariDataSource - adx-tidb - Starting...
+2026-07-21 10:25:51.894 [main] INFO  com.zaxxer.hikari.pool.HikariPool - adx-tidb - Added connection com.mysql.cj.jdbc.ConnectionImpl@1bdafb01
+2026-07-21 10:25:51.894 [main] INFO  com.zaxxer.hikari.HikariDataSource - adx-tidb - Start completed.
+2026-07-21 10:25:53.368 [main] INFO  c.a.t.b.c.BaiduClueStoreConfiguration - Baidu clue cold store initialized and migrated
+2026-07-21 10:25:53.502 [main] INFO  o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8788"]
+2026-07-21 10:25:53.506 [main] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port 8788 (http) with context path ''
+2026-07-21 10:25:53.510 [main] INFO  com.adx.tencent.Application - Started Application in 7.58 seconds (process running for 9.148)
+2026-07-21 10:25:53.511 [main] INFO  c.a.t.AppConfiguration$BackgroundTasks - Cold worker started
+2026-07-21 10:25:53.511 [main] INFO  c.a.t.AppConfiguration$BackgroundTasks - Conversion sync started (skip leader election)
+2026-07-21 10:25:53.512 [Thread-2] INFO  c.a.t.AppConfiguration$BackgroundTasks - [ConversionSync] task started, interval=30000ms
+2026-07-21 10:25:53.512 [Thread-2] INFO  c.a.t.AppConfiguration$BackgroundTasks - [ConversionSync] executing...
+2026-07-21 10:25:53.512 [main] INFO  c.a.t.AppConfiguration$BackgroundTasks - Callback retry started (skip leader election)
+2026-07-21 10:25:53.512 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=1, mode=REALTIME
+2026-07-21 10:25:53.512 [Thread-3] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] task started, interval=300000ms, limit=100
+2026-07-21 10:25:53.512 [Thread-3] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] executing...
+2026-07-21 10:25:53.512 [main] INFO  c.a.t.AppConfiguration$BackgroundTasks - Tag event sync started (skip leader election)
+2026-07-21 10:25:53.512 [Thread-4] INFO  c.a.t.AppConfiguration$BackgroundTasks - [TagEventSync] task started, interval=300000ms
+2026-07-21 10:25:53.639 [Thread-3] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] done: fetched=0 sent=0 failed=0
+2026-07-21 10:25:53.640 [Thread-3] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] sleeping 300000ms until next run
+2026-07-21 10:25:54.241 [Thread-4] INFO  c.a.t.tagsync.TagEventSyncService - [TagEventSync] 同步完成,共 6 条广告位回传方式写入 Redis
+2026-07-21 10:25:54.241 [Thread-4] INFO  c.a.t.AppConfiguration$BackgroundTasks - [TagEventSync] done: synced=6
+2026-07-21 10:25:54.261 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=1 returned 1000 records, responsePageSize=26, mode=REALTIME
+2026-07-21 10:26:25.831 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=37aab33afb313017 | act=3 | deviceId=1ffdfffd-59fd-4e2b-b14c-737dffe7e9f4 | date=20260721 | payment=0.0
+2026-07-21 10:26:25.870 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=121565b46b60733a | act=3 | deviceId=7d142c06-8538-447a-ad29-52e2278ddcb8 | date=20260721 | payment=0.0
+2026-07-21 10:26:25.909 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=c1fa332a9d60fc1f | act=3 | deviceId=0e03f29b-3600-4205-b29c-c4764b299526 | date=20260721 | payment=0.0
+2026-07-21 10:26:25.946 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=faab0ed80066f658 | act=3 | deviceId=daa8f9bc-dd0e-4f72-a353-ff7640e9e990 | date=20260721 | payment=0.0
+2026-07-21 10:26:26.157 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=faab0ed80066f658 | act=2 | deviceId=daa8f9bc-dd0e-4f72-a353-ff7640e9e990 | date=20260721 | payment=0.0
+2026-07-21 10:26:26.195 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=a81b958958f41f1e | act=3 | deviceId=680D30751B794EEFB18ABF0C1BA2B54A7a200d4f8fc7eb8051d126411efcf8a9 | date=20260721 | payment=0.0
+2026-07-21 10:26:26.235 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=b01a4f636dfd1148 | act=3 | deviceId=a552f89479cde0d0 | date=20260721 | payment=0.0
+2026-07-21 10:26:26.273 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=0b314ae933639839 | act=3 | deviceId=9AD9DE9360CA4FBEA30F8AACDC0F010E5cf4e143e1e5a381a2e780b4facf135f | date=20260721 | payment=0.0
+2026-07-21 10:26:26.329 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=a147f4e1bdf10c67 | act=3 | deviceId=C03A3BAF124547C989525D6FF10F04FEfd9acaa0cb972b1710b1e1b188805839 | date=20260721 | payment=0.0
+2026-07-21 10:26:26.370 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=4a753510383ace1a | act=3 | deviceId=BC265E609EE74971A32FD338F8B7DF387d624819d95182c6fe5e7f19f91d0dc9 | date=20260721 | payment=0.0
+2026-07-21 10:26:26.416 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=a67f973e1402ee7f | act=3 | deviceId=5619fa43277062a7 | date=20260721 | payment=0.0
+2026-07-21 10:26:26.457 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=a08af84b68c06a12 | act=3 | deviceId=DC038EDCD50E473DA58236C4F996F131df0a4fe3196d071daacd0a5d81b8c205 | date=20260721 | payment=0.0
+2026-07-21 10:26:26.495 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=1c20aa2db8d4f878 | act=3 | deviceId=e8b2125f98b2510fed3e4716eaa3052f655c1fbd156a49241cd80e7182678453 | date=20260721 | payment=0.0
+2026-07-21 10:26:26.535 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=71df167efe094149 | act=3 | deviceId=6724db526e8b8bd4 | date=20260721 | payment=0.0
+2026-07-21 10:26:26.575 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=8b8bf31c35c60226 | act=3 | deviceId=E892456E39844236AA8BBA7ED037E28B0a2578a98d253dbf8ffa0914099e7464 | date=20260721 | payment=0.0
+2026-07-21 10:26:26.619 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=8e8d5c582b4d6961 | act=3 | deviceId=da78c6e4-f25e-49fa-9872-48b7af8b36df | date=20260721 | payment=0.0
+2026-07-21 10:26:26.658 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=601b618ffc28137f | act=3 | deviceId=de771b6c-5fb6-47b6-9954-c06e9408646e | date=20260721 | payment=0.0
+2026-07-21 10:26:26.696 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=19f5056b90a5b027 | act=3 | deviceId=60ED24F3112240A7A006AAEB82F16ED65336e81c2617070789c6e8347989dfd4 | date=20260721 | payment=0.0
+2026-07-21 10:26:26.735 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=2a18351b77360442 | act=3 | deviceId=3f100f74-87ca-45fa-b0ef-75dcd2766418 | date=20260721 | payment=0.0
+2026-07-21 10:26:26.775 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=ec6f2105e0aeff24 | act=3 | deviceId=268d1f452e586dab | date=20260721 | payment=0.0
+2026-07-21 10:26:26.816 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=b4066c1c469db51c | act=3 | deviceId=CAF8FBBA7D0F4D72961D0CD5D7DDF38921273be334236c784e59f3fc15ca7702 | date=20260721 | payment=0.0
+2026-07-21 10:26:26.898 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=72cf2ffbbd4a2761 | act=3 | deviceId=aafbf0e8-a6ab-40c6-b45d-a16d95f3534b | date=20260721 | payment=0.0
+2026-07-21 10:26:26.986 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=9b9ec2fe30600237 | act=3 | deviceId=1E8F833B66114B71BAA344B289FF663188a9fc4d884b3afa1e029398dc01e263 | date=20260721 | payment=0.0
+2026-07-21 10:27:41.332 [adx-tidb housekeeper] WARN  com.zaxxer.hikari.pool.HikariPool - adx-tidb - Thread starvation or clock leap detected (housekeeper delta=1m22s171ms).
+2026-07-21 10:27:41.332 [adx-tidb housekeeper] WARN  com.zaxxer.hikari.pool.HikariPool - adx-tidb - Thread starvation or clock leap detected (housekeeper delta=1m16s973ms).
+2026-07-21 10:27:49.690 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=1 processed, cost=112496ms, totalFetched=1000, matched=23, sent=0, failed=0, skipped=977, alreadySent=23, mode=REALTIME
+2026-07-21 10:27:50.860 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=2, mode=REALTIME
+2026-07-21 10:27:51.404 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=2 returned 1000 records, responsePageSize=26, mode=REALTIME
+2026-07-21 10:27:55.424 [Thread-1] WARN  com.zaxxer.hikari.pool.PoolBase - adx-tidb - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@1736c1e4 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
+2026-07-21 10:27:56.550 [Thread-2] WARN  com.zaxxer.hikari.pool.PoolBase - adx-tidb - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@1bdafb01 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
+2026-07-21 10:27:56.552 [Thread-2] WARN  com.zaxxer.hikari.pool.PoolBase - adx-tidb - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@70092563 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
+2026-07-21 10:27:56.553 [Thread-2] WARN  com.zaxxer.hikari.pool.PoolBase - adx-tidb - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@49299dc6 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
+2026-07-21 10:27:57.418 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=de74634339ccf91b | act=3 | deviceId=a7a57c3b-6949-4a8f-ab6f-968c29f9f27c | date=20260721 | payment=0.0
+2026-07-21 10:28:00.951 [Thread-1] WARN  com.zaxxer.hikari.pool.PoolBase - adx-tidb - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@45b2d305 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
+2026-07-21 10:28:03.306 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=2 processed, cost=12446ms, totalFetched=2000, matched=24, sent=0, failed=0, skipped=1976, alreadySent=24, mode=REALTIME
+2026-07-21 10:28:03.307 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=3, mode=REALTIME
+2026-07-21 10:28:03.834 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=3 returned 1000 records, responsePageSize=26, mode=REALTIME
+2026-07-21 10:28:05.681 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=9c8d6d09940d9250 | act=3 | deviceId=aea64fdc652f618e38c82b76f3e0d540f401513e60e8f5374618b2b223b166e5 | date=20260721 | payment=0.0
+2026-07-21 10:28:05.755 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=9767b62a2aec3d1c | act=3 | deviceId=56A4F29FF0DE4D78AE79B71193856CAD2ad4406010d481fe8813702c1fabe15f | date=20260721 | payment=0.0
+2026-07-21 10:28:05.855 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=8805805927790c50 | act=2 | deviceId=6c956281-03b9-43c8-a8ba-392925c01591 | date=20260721 | payment=0.0
+2026-07-21 10:28:06.095 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=8805805927790c50 | act=3 | deviceId=6c956281-03b9-43c8-a8ba-392925c01591 | date=20260721 | payment=0.0
+2026-07-21 10:28:11.734 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=3 processed, cost=8426ms, totalFetched=3000, matched=28, sent=0, failed=0, skipped=2972, alreadySent=28, mode=REALTIME
+2026-07-21 10:28:11.735 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=4, mode=REALTIME
+2026-07-21 10:28:12.189 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=4 returned 1000 records, responsePageSize=26, mode=REALTIME
+2026-07-21 10:28:13.958 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=0ceaf34009cd4c29 | act=3 | deviceId=7a7284a136b9d94b65b084600faf20d97ecf114e1ade93ffb5618886404cd232 | date=20260721 | payment=0.0
+2026-07-21 10:28:14.120 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=6f8d93c2c4d0034d | act=3 | deviceId=7d92f9ef07d3c32c | date=20260721 | payment=0.0
+2026-07-21 10:28:14.289 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=ceade02954d2f73f | act=3 | deviceId=a6523593-6c19-47c9-b8a5-871b62e200e1 | date=20260721 | payment=0.0
+2026-07-21 10:28:19.878 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=4 processed, cost=8142ms, totalFetched=4000, matched=31, sent=0, failed=0, skipped=3969, alreadySent=31, mode=REALTIME
+2026-07-21 10:28:19.879 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=5, mode=REALTIME
+2026-07-21 10:28:20.304 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=5 returned 1000 records, responsePageSize=26, mode=REALTIME
+2026-07-21 10:28:27.378 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=5 processed, cost=7498ms, totalFetched=5000, matched=31, sent=0, failed=0, skipped=4969, alreadySent=31, mode=REALTIME
+2026-07-21 10:28:27.381 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=6, mode=REALTIME
+2026-07-21 10:28:27.998 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=6 returned 1000 records, responsePageSize=26, mode=REALTIME
+2026-07-21 10:28:29.564 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=3c3e6c755dd31b43 | act=3 | deviceId=3E445FB97BE94951AEAEA5A5CFDB395A6ab5cb3b6b422c9de568f8845e8e5ee6 | date=20260721 | payment=0.0
+2026-07-21 10:28:29.616 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=7a39636cb50e5130 | act=3 | deviceId=fd55b029-d5ad-4c5c-a43e-13a50188ad0a | date=20260721 | payment=0.0
+2026-07-21 10:28:35.109 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=6 processed, cost=7728ms, totalFetched=6000, matched=33, sent=0, failed=0, skipped=5967, alreadySent=33, mode=REALTIME
+2026-07-21 10:28:35.110 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=7, mode=REALTIME
+2026-07-21 10:28:35.587 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=7 returned 1000 records, responsePageSize=26, mode=REALTIME
+2026-07-21 10:28:42.675 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=7 processed, cost=7564ms, totalFetched=7000, matched=33, sent=0, failed=0, skipped=6967, alreadySent=33, mode=REALTIME
+2026-07-21 10:28:42.677 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=8, mode=REALTIME
+2026-07-21 10:28:43.114 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=8 returned 1000 records, responsePageSize=26, mode=REALTIME
+2026-07-21 10:28:44.602 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=36c5fdb07db89479 | act=3 | deviceId=9312a168292fd60ef1cda4773bb1344733aeb448a19e8d27f98ec5c5177b2063 | date=20260721 | payment=0.0
+2026-07-21 10:28:44.681 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=d23259588687ae31 | act=3 | deviceId=962a4683d61f34da | date=20260721 | payment=0.0
+2026-07-21 10:28:50.099 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=8 processed, cost=7422ms, totalFetched=8000, matched=35, sent=0, failed=0, skipped=7965, alreadySent=35, mode=REALTIME
+2026-07-21 10:28:50.100 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=9, mode=REALTIME
+2026-07-21 10:28:50.555 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=9 returned 1000 records, responsePageSize=26, mode=REALTIME
+2026-07-21 10:28:57.500 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=9 processed, cost=7400ms, totalFetched=9000, matched=35, sent=0, failed=0, skipped=8965, alreadySent=35, mode=REALTIME
+2026-07-21 10:28:57.502 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=10, mode=REALTIME
+2026-07-21 10:28:58.004 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=10 returned 1000 records, responsePageSize=26, mode=REALTIME
+2026-07-21 10:28:59.517 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=73f069e5d782a825 | act=3 | deviceId=7fc0cf2d8d2766f5e8c838f0a1a5dd4dae09d7dcebe32fc74836bb0dcb59f6be | date=20260721 | payment=0.0
+2026-07-21 10:28:59.554 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=754f70c88d5dc378 | act=2 | deviceId=f7a1ff7e-bfff-404d-ffee-76fba3fbb33d | date=20260721 | payment=0.0
+2026-07-21 10:28:59.596 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=754f70c88d5dc378 | act=3 | deviceId=f7a1ff7e-bfff-404d-ffee-76fba3fbb33d | date=20260721 | payment=0.0
+2026-07-21 10:29:05.102 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=10 processed, cost=7545ms, totalFetched=10000, matched=38, sent=0, failed=0, skipped=9962, alreadySent=38, mode=REALTIME
+2026-07-21 10:29:05.103 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=11, mode=REALTIME
+2026-07-21 10:29:05.564 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=11 returned 1000 records, responsePageSize=26, mode=REALTIME
+2026-07-21 10:29:13.010 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=11 processed, cost=7907ms, totalFetched=11000, matched=38, sent=0, failed=0, skipped=10962, alreadySent=38, mode=REALTIME
+2026-07-21 10:29:13.013 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=12, mode=REALTIME
+2026-07-21 10:29:13.422 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=12 returned 1000 records, responsePageSize=26, mode=REALTIME
+2026-07-21 10:29:21.825 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=12 processed, cost=8812ms, totalFetched=12000, matched=38, sent=0, failed=0, skipped=11962, alreadySent=38, mode=REALTIME
+2026-07-21 10:29:21.827 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=13, mode=REALTIME
+2026-07-21 10:29:22.290 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=13 returned 1000 records, responsePageSize=26, mode=REALTIME
+2026-07-21 10:29:24.828 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=54141b84e905ef79 | act=3 | deviceId=BCBDEB1EF76A4C588EDC4D42999F6DECbd042f9bab2b107c76de5240549922e1 | date=20260721 | payment=0.0
+2026-07-21 10:29:25.116 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=1e1aa5e180296f76 | act=3 | deviceId=c1054fb0b5d89cbdaef1bdb36327845ffd7dc5d5ee89c0a2d1a3fac4f71b15d0 | date=20260721 | payment=0.0
+2026-07-21 10:29:25.204 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=23da7f152ab4f843 | act=3 | deviceId=69C5D4FA003A4F5D912C68E35CD8F2D586038c184bfa911b43365f207cca922c | date=20260721 | payment=0.0
+2026-07-21 10:29:25.285 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=a5c82d0e1fb3f04b | act=3 | deviceId=7149b03377553805706ffa0196e8b538cd21f575966dc75401ba2fc8eb001b0c | date=20260721 | payment=0.0
+2026-07-21 10:29:31.076 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=13 processed, cost=9248ms, totalFetched=13000, matched=42, sent=0, failed=0, skipped=12958, alreadySent=42, mode=REALTIME
+2026-07-21 10:29:31.077 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=14, mode=REALTIME
+2026-07-21 10:29:31.536 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=14 returned 1000 records, responsePageSize=26, mode=REALTIME
+2026-07-21 10:29:33.825 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=2a120206b2ddbd68 | act=3 | deviceId=c0c79f10ad487ee7 | date=20260721 | payment=0.0
+2026-07-21 10:29:33.884 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=2810c3e2f8ab3c6e | act=3 | deviceId=4B58E1E38C424F0CA461C23FA47D584C21bb0817d70d9c99888aacf89c16957e | date=20260721 | payment=0.0
+2026-07-21 10:29:33.940 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=fd88c48912335734 | act=3 | deviceId=CF136BFC16504EFF8E8D1F143F907EDE09e2b42025bb450c5d9ae573bd1cd8a3 | date=20260721 | payment=0.0
+2026-07-21 10:29:39.818 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=14 processed, cost=8741ms, totalFetched=14000, matched=45, sent=0, failed=0, skipped=13955, alreadySent=45, mode=REALTIME
+2026-07-21 10:29:39.819 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=15, mode=REALTIME
+2026-07-21 10:29:40.462 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=15 returned 1000 records, responsePageSize=26, mode=REALTIME
+2026-07-21 10:29:41.961 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=4b83d9e2a363bb6c | act=3 | deviceId=627a6998-7d0d-4152-a95a-eaa84de4a5f1 | date=20260721 | payment=0.0
+2026-07-21 10:29:42.031 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=1f73e1ae93faaf3e | act=3 | deviceId=a1210ad58bdb1b9d | date=20260721 | payment=0.0
+2026-07-21 10:29:42.117 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=3f120a4a5c46e64f | act=3 | deviceId=289b40819ecd1532dcc265ddf7360e1959941582ea818250a8c99212b72ff81d | date=20260721 | payment=0.0
+2026-07-21 10:29:47.735 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=15 processed, cost=7915ms, totalFetched=15000, matched=48, sent=0, failed=0, skipped=14952, alreadySent=48, mode=REALTIME
+2026-07-21 10:29:47.736 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=16, mode=REALTIME
+2026-07-21 10:29:48.292 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=16 returned 1000 records, responsePageSize=26, mode=REALTIME
+2026-07-21 10:29:55.561 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=16 processed, cost=7825ms, totalFetched=16000, matched=48, sent=0, failed=0, skipped=15952, alreadySent=48, mode=REALTIME
+2026-07-21 10:29:55.564 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=17, mode=REALTIME
+2026-07-21 10:29:55.990 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=17 returned 1000 records, responsePageSize=26, mode=REALTIME
+2026-07-21 10:29:58.115 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=42337b604824ab42 | act=3 | deviceId=F04EC33EFF6A492E8ACCFB1919F98A4Dfc7f6dacc9c47b02816c58fb90d6351a | date=20260721 | payment=0.0
+2026-07-21 10:29:58.210 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=36f3f89a2eb4bc17 | act=3 | deviceId=5C18EFC5CAF343549070478925AB52F222957606c86158f024d4b65ec5f86085 | date=20260721 | payment=0.0
+2026-07-21 10:29:58.421 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=95d548b635cf065d | act=3 | deviceId=1a02dbea98189d3308e890a04600f3b319bc1848196500f1ae1d64135aee9f13 | date=20260721 | payment=0.0
+2026-07-21 10:30:05.183 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=17 processed, cost=9619ms, totalFetched=17000, matched=51, sent=0, failed=0, skipped=16949, alreadySent=51, mode=REALTIME
+2026-07-21 10:30:05.187 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=18, mode=REALTIME
+2026-07-21 10:30:05.759 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=18 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:30:14.114 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=c19e632be019ef15 | act=3 | deviceId=daed56dead51c6d81b8d1b360c0c5a4dcb7ed95a976d3d8bfc8f3587bfb6c18f | date=20260721 | payment=0.0
+2026-07-21 10:30:14.362 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=4ba5807a8affd16d | act=3 | deviceId=3ade9218-7957-475c-b758-948f5582592f | date=20260721 | payment=0.0
+2026-07-21 10:30:14.598 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=5c02a0d7dbe14f73 | act=3 | deviceId=fb31d3edf73b7ace | date=20260721 | payment=0.0
+2026-07-21 10:30:55.231 [Thread-3] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] executing...
+2026-07-21 10:30:55.238 [Thread-3] WARN  com.zaxxer.hikari.pool.PoolBase - adx-tidb - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@73a7e2b7 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
+2026-07-21 10:30:55.379 [Thread-3] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] done: fetched=0 sent=0 failed=0
+2026-07-21 10:30:55.379 [Thread-3] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] sleeping 300000ms until next run
+2026-07-21 10:30:55.433 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=18 processed, cost=50245ms, totalFetched=18000, matched=54, sent=0, failed=0, skipped=17946, alreadySent=54, mode=REALTIME
+2026-07-21 10:30:55.434 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=19, mode=REALTIME
+2026-07-21 10:30:55.578 [Thread-4] INFO  c.a.t.tagsync.TagEventSyncService - [TagEventSync] 同步完成,共 6 条广告位回传方式写入 Redis
+2026-07-21 10:30:55.578 [Thread-4] INFO  c.a.t.AppConfiguration$BackgroundTasks - [TagEventSync] done: synced=6
+2026-07-21 10:30:56.006 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=19 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:31:03.393 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=b9af460ae324e02e | act=3 | deviceId=a6c58fca50c606a5 | date=20260721 | payment=0.0
+2026-07-21 10:31:03.433 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=0ceaf34009cd4c29 | act=2 | deviceId=7a7284a136b9d94b65b084600faf20d97ecf114e1ade93ffb5618886404cd232 | date=20260721 | payment=0.0
+2026-07-21 10:31:23.077 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=19 processed, cost=26740ms, totalFetched=19000, matched=56, sent=0, failed=0, skipped=18944, alreadySent=56, mode=REALTIME
+2026-07-21 10:31:23.077 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=20, mode=REALTIME
+2026-07-21 10:31:23.535 [Thread-2] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=20 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:31:24.824 [Thread-5] WARN  io.lettuce.core.RedisChannelHandler - Connection is already closed
+2026-07-21 10:31:24.824 [Thread-2] ERROR c.a.t.AppConfiguration$BackgroundTasks - [ConversionSync] error: Redis exception
+org.springframework.data.redis.RedisSystemException: Redis exception
+	at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:72)
+	at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:41)
+	at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:40)
+	at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:38)
+	at org.springframework.data.redis.connection.lettuce.LettuceConnection.convertLettuceAccessException(LettuceConnection.java:308)
+	at org.springframework.data.redis.connection.lettuce.LettuceConnection.await(LettuceConnection.java:1026)
+	at org.springframework.data.redis.connection.lettuce.LettuceConnection.lambda$doInvoke$3(LettuceConnection.java:445)
+	at org.springframework.data.redis.connection.lettuce.LettuceInvoker$Synchronizer.invoke(LettuceInvoker.java:673)
+	at org.springframework.data.redis.connection.lettuce.LettuceInvoker$DefaultManyInvocationSpec.toList(LettuceInvoker.java:618)
+	at org.springframework.data.redis.connection.lettuce.LettuceStringCommands.mGet(LettuceStringCommands.java:92)
+	at org.springframework.data.redis.connection.DefaultedRedisConnection.mGet(DefaultedRedisConnection.java:312)
+	at org.springframework.data.redis.connection.DefaultStringRedisConnection.mGet(DefaultStringRedisConnection.java:640)
+	at org.springframework.data.redis.core.DefaultValueOperations.lambda$multiGet$7(DefaultValueOperations.java:193)
+	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:396)
+	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:363)
+	at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:97)
+	at org.springframework.data.redis.core.DefaultValueOperations.multiGet(DefaultValueOperations.java:193)
+	at com.adx.tencent.honor.HonorHotStore.findBidsByQks(HonorHotStore.java:134)
+	at com.adx.tencent.conversionsync.ConversionSyncService.loadBidLookup(ConversionSyncService.java:186)
+	at com.adx.tencent.conversionsync.ConversionSyncService.processPayments(ConversionSyncService.java:148)
+	at com.adx.tencent.conversionsync.ConversionSyncService.syncTencentConversions(ConversionSyncService.java:120)
+	at com.adx.tencent.conversionsync.ConversionSyncRunner.runForDate(ConversionSyncRunner.java:107)
+	at com.adx.tencent.conversionsync.ConversionSyncRunner.runForOffsetsParallel(ConversionSyncRunner.java:76)
+	at com.adx.tencent.conversionsync.ConversionSyncRunner.runForOffsets(ConversionSyncRunner.java:46)
+	at com.adx.tencent.conversionsync.ConversionSyncRunner.runOnce(ConversionSyncRunner.java:42)
+	at com.adx.tencent.AppConfiguration$BackgroundTasks.runConversionSync(AppConfiguration.java:477)
+	at com.adx.tencent.AppConfiguration$BackgroundTasks.lambda$run$3(AppConfiguration.java:407)
+	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
+	at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)
+	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java)
+	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
+	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
+	at java.base/java.lang.Thread.run(Thread.java:842)
+Caused by: io.lettuce.core.RedisException: Connection closed
+	at io.lettuce.core.protocol.DefaultEndpoint.lambda$notifyDrainQueuedCommands$7(DefaultEndpoint.java:673)
+	at io.lettuce.core.protocol.DefaultEndpoint$Lazy.getNullable(DefaultEndpoint.java:1182)
+	at io.lettuce.core.protocol.DefaultEndpoint$Lazy.get(DefaultEndpoint.java:1167)
+	at io.lettuce.core.protocol.DefaultEndpoint.lambda$notifyDrainQueuedCommands$8(DefaultEndpoint.java:674)
+	at io.lettuce.core.protocol.DefaultEndpoint.cancelCommands(DefaultEndpoint.java:797)
+	at io.lettuce.core.protocol.DefaultEndpoint.notifyDrainQueuedCommands(DefaultEndpoint.java:674)
+	at io.lettuce.core.protocol.CommandHandler.channelInactive(CommandHandler.java:358)
+	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:303)
+	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:281)
+	at io.netty.channel.AbstractChannelHandlerContext.fireChannelInactive(AbstractChannelHandlerContext.java:274)
+	at io.netty.channel.ChannelInboundHandlerAdapter.channelInactive(ChannelInboundHandlerAdapter.java:81)
+	at io.lettuce.core.protocol.RedisHandshakeHandler.channelInactive(RedisHandshakeHandler.java:94)
+	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:303)
+	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:281)
+	at io.netty.channel.AbstractChannelHandlerContext.fireChannelInactive(AbstractChannelHandlerContext.java:274)
+	at io.netty.channel.ChannelInboundHandlerAdapter.channelInactive(ChannelInboundHandlerAdapter.java:81)
+	at io.lettuce.core.ChannelGroupListener.channelInactive(ChannelGroupListener.java:69)
+	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:303)
+	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:281)
+	at io.netty.channel.AbstractChannelHandlerContext.fireChannelInactive(AbstractChannelHandlerContext.java:274)
+	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelInactive(DefaultChannelPipeline.java:1405)
+	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:301)
+	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:281)
+	at io.netty.channel.DefaultChannelPipeline.fireChannelInactive(DefaultChannelPipeline.java:901)
+	at io.netty.channel.AbstractChannel$AbstractUnsafe$7.run(AbstractChannel.java:813)
+	at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:173)
+	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute$$$capture(AbstractEventExecutor.java:166)
+	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java)
+	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470)
+	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569)
+	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
+	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
+	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
+	... 1 common frames omitted
+2026-07-21 10:31:24.825 [Thread-2] INFO  c.a.t.AppConfiguration$BackgroundTasks - [ConversionSync] sleeping 30000ms until next run
+2026-07-21 10:31:24.825 [Thread-5] ERROR c.a.t.honor.HonorBackgroundTasks - honor cold worker: read honor stream failed
+java.lang.RuntimeException: read honor stream failed
+	at com.adx.tencent.honor.HonorHotStore.read(HonorHotStore.java:179)
+	at com.adx.tencent.honor.HonorColdWorker.runOnce(HonorColdWorker.java:40)
+	at com.adx.tencent.honor.HonorBackgroundTasks.lambda$run$1(HonorBackgroundTasks.java:40)
+	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
+	at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)
+	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java)
+	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
+	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
+	at java.base/java.lang.Thread.run(Thread.java:842)
+Caused by: org.springframework.data.redis.RedisSystemException: Redis exception
+	at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:72)
+	at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:41)
+	at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:40)
+	at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:38)
+	at org.springframework.data.redis.connection.lettuce.LettuceConnection.convertLettuceAccessException(LettuceConnection.java:308)
+	at org.springframework.data.redis.connection.lettuce.LettuceConnection.await(LettuceConnection.java:1026)
+	at org.springframework.data.redis.connection.lettuce.LettuceConnection.lambda$doInvoke$3(LettuceConnection.java:445)
+	at org.springframework.data.redis.connection.lettuce.LettuceInvoker$Synchronizer.invoke(LettuceInvoker.java:673)
+	at org.springframework.data.redis.connection.lettuce.LettuceInvoker$DefaultManyInvocationSpec.toList(LettuceInvoker.java:618)
+	at org.springframework.data.redis.connection.lettuce.LettuceStreamCommands.xReadGroup(LettuceStreamCommands.java:282)
+	at org.springframework.data.redis.connection.DefaultedRedisConnection.xReadGroup(DefaultedRedisConnection.java:624)
+	at org.springframework.data.redis.connection.DefaultStringRedisConnection.xReadGroup(DefaultStringRedisConnection.java:3000)
+	at org.springframework.data.redis.core.DefaultStreamOperations$5.inRedis(DefaultStreamOperations.java:263)
+	at org.springframework.data.redis.core.DefaultStreamOperations$RecordDeserializingRedisCallback.doInRedis(DefaultStreamOperations.java:336)
+	at org.springframework.data.redis.core.DefaultStreamOperations$RecordDeserializingRedisCallback.doInRedis(DefaultStreamOperations.java:331)
+	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:396)
+	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:363)
+	at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:97)
+	at org.springframework.data.redis.core.DefaultStreamOperations.read(DefaultStreamOperations.java:258)
+	at com.adx.tencent.honor.HonorHotStore.read(HonorHotStore.java:171)
+	... 8 common frames omitted
+Caused by: io.lettuce.core.RedisException: Connection closed
+	at io.lettuce.core.protocol.DefaultEndpoint.lambda$notifyDrainQueuedCommands$7(DefaultEndpoint.java:673)
+	at io.lettuce.core.protocol.DefaultEndpoint$Lazy.getNullable(DefaultEndpoint.java:1182)
+	at io.lettuce.core.protocol.DefaultEndpoint$Lazy.get(DefaultEndpoint.java:1167)
+	at io.lettuce.core.protocol.DefaultEndpoint.lambda$notifyDrainQueuedCommands$8(DefaultEndpoint.java:674)
+	at io.lettuce.core.protocol.DefaultEndpoint.cancelCommands(DefaultEndpoint.java:797)
+	at io.lettuce.core.protocol.DefaultEndpoint.notifyDrainQueuedCommands(DefaultEndpoint.java:674)
+	at io.lettuce.core.protocol.CommandHandler.channelInactive(CommandHandler.java:358)
+	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:303)
+	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:281)
+	at io.netty.channel.AbstractChannelHandlerContext.fireChannelInactive(AbstractChannelHandlerContext.java:274)
+	at io.netty.channel.ChannelInboundHandlerAdapter.channelInactive(ChannelInboundHandlerAdapter.java:81)
+	at io.lettuce.core.protocol.RedisHandshakeHandler.channelInactive(RedisHandshakeHandler.java:94)
+	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:303)
+	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:281)
+	at io.netty.channel.AbstractChannelHandlerContext.fireChannelInactive(AbstractChannelHandlerContext.java:274)
+	at io.netty.channel.ChannelInboundHandlerAdapter.channelInactive(ChannelInboundHandlerAdapter.java:81)
+	at io.lettuce.core.ChannelGroupListener.channelInactive(ChannelGroupListener.java:69)
+	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:303)
+	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:281)
+	at io.netty.channel.AbstractChannelHandlerContext.fireChannelInactive(AbstractChannelHandlerContext.java:274)
+	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelInactive(DefaultChannelPipeline.java:1405)
+	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:301)
+	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:281)
+	at io.netty.channel.DefaultChannelPipeline.fireChannelInactive(DefaultChannelPipeline.java:901)
+	at io.netty.channel.AbstractChannel$AbstractUnsafe$7.run(AbstractChannel.java:813)
+	at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:173)
+	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute$$$capture(AbstractEventExecutor.java:166)
+	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java)
+	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470)
+	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569)
+	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
+	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
+	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
+	... 1 common frames omitted
+2026-07-21 10:31:24.875 [Thread-1] ERROR c.a.t.AppConfiguration$BackgroundTasks - cold worker: LettuceConnectionFactory is STOPPING
+java.lang.IllegalStateException: LettuceConnectionFactory is STOPPING
+	at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.assertStarted(LettuceConnectionFactory.java:1394)
+	at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getConnection(LettuceConnectionFactory.java:991)
+	at org.springframework.data.redis.core.RedisConnectionUtils.fetchConnection(RedisConnectionUtils.java:195)
+	at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:144)
+	at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:105)
+	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:383)
+	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:363)
+	at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:97)
+	at org.springframework.data.redis.core.DefaultStreamOperations.acknowledge(DefaultStreamOperations.java:122)
+	at com.adx.tencent.storage.RedisHotStore.ack(RedisHotStore.java:304)
+	at com.adx.tencent.worker.ColdWorker.runOnce(ColdWorker.java:91)
+	at com.adx.tencent.AppConfiguration$BackgroundTasks.lambda$run$1(AppConfiguration.java:394)
+	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
+	at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)
+	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java)
+	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
+	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
+	at java.base/java.lang.Thread.run(Thread.java:842)
+2026-07-21 10:36:38.200 [main] INFO  com.adx.tencent.Application - Starting Application using Java 17.0.14 with PID 1183 (/Users/yumeng/IdeaProjects/tencent-baidu-tracking/target/classes started by yumeng in /Users/yumeng/IdeaProjects/tencent-baidu-tracking)
+2026-07-21 10:36:38.201 [main] INFO  com.adx.tencent.Application - The following 1 profile is active: "dev"
+2026-07-21 10:36:38.432 [main] INFO  o.s.d.r.c.RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
+2026-07-21 10:36:38.433 [main] INFO  o.s.d.r.c.RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
+2026-07-21 10:36:38.450 [main] INFO  o.s.d.r.c.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 7 ms. Found 0 Redis repository interfaces.
+2026-07-21 10:36:38.601 [main] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat initialized with port 8788 (http)
+2026-07-21 10:36:38.604 [main] INFO  o.a.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8788"]
+2026-07-21 10:36:38.605 [main] INFO  o.a.catalina.core.StandardService - Starting service [Tomcat]
+2026-07-21 10:36:38.605 [main] INFO  o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.20]
+2026-07-21 10:36:38.621 [main] INFO  o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
+2026-07-21 10:36:38.622 [main] INFO  o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 404 ms
+2026-07-21 10:36:38.788 [main] INFO  com.zaxxer.hikari.HikariDataSource - adx-tidb - Starting...
+2026-07-21 10:36:39.545 [main] INFO  com.zaxxer.hikari.pool.HikariPool - adx-tidb - Added connection com.mysql.cj.jdbc.ConnectionImpl@46b6701e
+2026-07-21 10:36:39.548 [main] INFO  com.zaxxer.hikari.HikariDataSource - adx-tidb - Start completed.
+2026-07-21 10:36:56.242 [main] INFO  com.adx.tencent.Application - Starting Application using Java 17.0.14 with PID 1405 (/Users/yumeng/IdeaProjects/tencent-baidu-tracking/target/classes started by yumeng in /Users/yumeng/IdeaProjects/tencent-baidu-tracking)
+2026-07-21 10:36:56.243 [main] INFO  com.adx.tencent.Application - The following 1 profile is active: "dev"
+2026-07-21 10:36:56.471 [main] INFO  o.s.d.r.c.RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
+2026-07-21 10:36:56.472 [main] INFO  o.s.d.r.c.RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
+2026-07-21 10:36:56.489 [main] INFO  o.s.d.r.c.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 7 ms. Found 0 Redis repository interfaces.
+2026-07-21 10:36:56.637 [main] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat initialized with port 8788 (http)
+2026-07-21 10:36:56.641 [main] INFO  o.a.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8788"]
+2026-07-21 10:36:56.641 [main] INFO  o.a.catalina.core.StandardService - Starting service [Tomcat]
+2026-07-21 10:36:56.641 [main] INFO  o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.20]
+2026-07-21 10:36:56.657 [main] INFO  o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
+2026-07-21 10:36:56.657 [main] INFO  o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 397 ms
+2026-07-21 10:36:56.815 [main] INFO  com.zaxxer.hikari.HikariDataSource - adx-tidb - Starting...
+2026-07-21 10:36:57.197 [main] INFO  com.zaxxer.hikari.pool.HikariPool - adx-tidb - Added connection com.mysql.cj.jdbc.ConnectionImpl@4228f269
+2026-07-21 10:36:57.200 [main] INFO  com.zaxxer.hikari.HikariDataSource - adx-tidb - Start completed.
+2026-07-21 10:36:59.361 [main] INFO  com.adx.tencent.AppConfiguration - TiDB cold store initialized and migrated
+2026-07-21 10:36:59.438 [main] INFO  com.zaxxer.hikari.HikariDataSource - adx-tidb - Starting...
+2026-07-21 10:36:59.801 [main] INFO  com.zaxxer.hikari.pool.HikariPool - adx-tidb - Added connection com.mysql.cj.jdbc.ConnectionImpl@5d800a56
+2026-07-21 10:36:59.801 [main] INFO  com.zaxxer.hikari.HikariDataSource - adx-tidb - Start completed.
+2026-07-21 10:37:00.642 [main] INFO  c.a.t.b.c.BaiduClueStoreConfiguration - Baidu clue cold store initialized and migrated
+2026-07-21 10:37:00.765 [main] INFO  o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8788"]
+2026-07-21 10:37:00.768 [main] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port 8788 (http) with context path ''
+2026-07-21 10:37:00.772 [main] INFO  com.adx.tencent.Application - Started Application in 4.713 seconds (process running for 5.235)
+2026-07-21 10:37:00.773 [main] INFO  c.a.t.AppConfiguration$BackgroundTasks - Cold worker started
+2026-07-21 10:37:00.774 [main] INFO  c.a.t.AppConfiguration$BackgroundTasks - Conversion sync started (skip leader election)
+2026-07-21 10:37:00.774 [Thread-3] INFO  c.a.t.AppConfiguration$BackgroundTasks - [ConversionSync] task started, interval=30000ms
+2026-07-21 10:37:00.774 [Thread-3] INFO  c.a.t.AppConfiguration$BackgroundTasks - [ConversionSync] executing...
+2026-07-21 10:37:00.774 [main] INFO  c.a.t.AppConfiguration$BackgroundTasks - Callback retry started (skip leader election)
+2026-07-21 10:37:00.774 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=1, mode=REALTIME
+2026-07-21 10:37:00.774 [Thread-4] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] task started, interval=300000ms, limit=100
+2026-07-21 10:37:00.774 [Thread-4] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] executing...
+2026-07-21 10:37:00.774 [main] INFO  c.a.t.AppConfiguration$BackgroundTasks - Tag event sync started (skip leader election)
+2026-07-21 10:37:00.774 [Thread-5] INFO  c.a.t.AppConfiguration$BackgroundTasks - [TagEventSync] task started, interval=300000ms
+2026-07-21 10:37:00.878 [Thread-4] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] done: fetched=0 sent=0 failed=0
+2026-07-21 10:37:00.878 [Thread-4] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] sleeping 300000ms until next run
+2026-07-21 10:37:01.383 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=1 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:37:01.395 [Thread-5] INFO  c.a.t.tagsync.TagEventSyncService - [TagEventSync] 同步完成,共 6 条广告位回传方式写入 Redis
+2026-07-21 10:37:01.395 [Thread-5] INFO  c.a.t.AppConfiguration$BackgroundTasks - [TagEventSync] done: synced=6
+2026-07-21 10:37:08.414 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=37aab33afb313017 | act=3 | deviceId=1ffdfffd-59fd-4e2b-b14c-737dffe7e9f4 | date=20260721 | payment=0.0
+2026-07-21 10:37:08.486 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=121565b46b60733a | act=3 | deviceId=7d142c06-8538-447a-ad29-52e2278ddcb8 | date=20260721 | payment=0.0
+2026-07-21 10:37:08.572 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=c1fa332a9d60fc1f | act=3 | deviceId=0e03f29b-3600-4205-b29c-c4764b299526 | date=20260721 | payment=0.0
+2026-07-21 10:37:08.613 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=faab0ed80066f658 | act=3 | deviceId=daa8f9bc-dd0e-4f72-a353-ff7640e9e990 | date=20260721 | payment=0.0
+2026-07-21 10:37:08.677 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=faab0ed80066f658 | act=2 | deviceId=daa8f9bc-dd0e-4f72-a353-ff7640e9e990 | date=20260721 | payment=0.0
+2026-07-21 10:37:08.712 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=a81b958958f41f1e | act=3 | deviceId=680D30751B794EEFB18ABF0C1BA2B54A7a200d4f8fc7eb8051d126411efcf8a9 | date=20260721 | payment=0.0
+2026-07-21 10:37:08.756 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=b01a4f636dfd1148 | act=3 | deviceId=a552f89479cde0d0 | date=20260721 | payment=0.0
+2026-07-21 10:37:08.791 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=0b314ae933639839 | act=3 | deviceId=9AD9DE9360CA4FBEA30F8AACDC0F010E5cf4e143e1e5a381a2e780b4facf135f | date=20260721 | payment=0.0
+2026-07-21 10:37:08.992 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=a147f4e1bdf10c67 | act=3 | deviceId=C03A3BAF124547C989525D6FF10F04FEfd9acaa0cb972b1710b1e1b188805839 | date=20260721 | payment=0.0
+2026-07-21 10:37:09.040 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=4a753510383ace1a | act=3 | deviceId=BC265E609EE74971A32FD338F8B7DF387d624819d95182c6fe5e7f19f91d0dc9 | date=20260721 | payment=0.0
+2026-07-21 10:37:09.078 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=a67f973e1402ee7f | act=3 | deviceId=5619fa43277062a7 | date=20260721 | payment=0.0
+2026-07-21 10:37:09.121 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=a08af84b68c06a12 | act=3 | deviceId=DC038EDCD50E473DA58236C4F996F131df0a4fe3196d071daacd0a5d81b8c205 | date=20260721 | payment=0.0
+2026-07-21 10:37:09.222 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=1c20aa2db8d4f878 | act=3 | deviceId=e8b2125f98b2510fed3e4716eaa3052f655c1fbd156a49241cd80e7182678453 | date=20260721 | payment=0.0
+2026-07-21 10:37:09.265 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=71df167efe094149 | act=3 | deviceId=6724db526e8b8bd4 | date=20260721 | payment=0.0
+2026-07-21 10:37:09.308 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=8b8bf31c35c60226 | act=3 | deviceId=E892456E39844236AA8BBA7ED037E28B0a2578a98d253dbf8ffa0914099e7464 | date=20260721 | payment=0.0
+2026-07-21 10:37:09.349 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=8e8d5c582b4d6961 | act=3 | deviceId=da78c6e4-f25e-49fa-9872-48b7af8b36df | date=20260721 | payment=0.0
+2026-07-21 10:37:09.506 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=601b618ffc28137f | act=3 | deviceId=de771b6c-5fb6-47b6-9954-c06e9408646e | date=20260721 | payment=0.0
+2026-07-21 10:37:09.548 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=19f5056b90a5b027 | act=3 | deviceId=60ED24F3112240A7A006AAEB82F16ED65336e81c2617070789c6e8347989dfd4 | date=20260721 | payment=0.0
+2026-07-21 10:37:09.586 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=2a18351b77360442 | act=3 | deviceId=3f100f74-87ca-45fa-b0ef-75dcd2766418 | date=20260721 | payment=0.0
+2026-07-21 10:37:09.633 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=ec6f2105e0aeff24 | act=3 | deviceId=268d1f452e586dab | date=20260721 | payment=0.0
+2026-07-21 10:37:09.733 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=b4066c1c469db51c | act=3 | deviceId=CAF8FBBA7D0F4D72961D0CD5D7DDF38921273be334236c784e59f3fc15ca7702 | date=20260721 | payment=0.0
+2026-07-21 10:37:09.961 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=72cf2ffbbd4a2761 | act=3 | deviceId=aafbf0e8-a6ab-40c6-b45d-a16d95f3534b | date=20260721 | payment=0.0
+2026-07-21 10:37:10.069 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=9b9ec2fe30600237 | act=3 | deviceId=1E8F833B66114B71BAA344B289FF663188a9fc4d884b3afa1e029398dc01e263 | date=20260721 | payment=0.0
+2026-07-21 10:38:10.616 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=5, batchSize=200, cost=59781ms
+2026-07-21 10:39:21.493 [adx-tidb housekeeper] WARN  com.zaxxer.hikari.pool.HikariPool - adx-tidb - Thread starvation or clock leap detected (housekeeper delta=1m21s582ms).
+2026-07-21 10:39:21.495 [adx-tidb housekeeper] WARN  com.zaxxer.hikari.pool.HikariPool - adx-tidb - Thread starvation or clock leap detected (housekeeper delta=1m24s180ms).
+2026-07-21 10:39:21.502 [Thread-2] WARN  com.zaxxer.hikari.pool.PoolBase - adx-tidb - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@4228f269 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
+2026-07-21 10:39:21.503 [Thread-2] WARN  com.zaxxer.hikari.pool.PoolBase - adx-tidb - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@77de65fd (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
+2026-07-21 10:39:21.503 [Thread-2] WARN  com.zaxxer.hikari.pool.PoolBase - adx-tidb - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@3519097 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
+2026-07-21 10:39:21.504 [Thread-2] WARN  com.zaxxer.hikari.pool.PoolBase - adx-tidb - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@769550f2 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
+2026-07-21 10:39:21.553 [Thread-1] ERROR c.a.t.honor.HonorBackgroundTasks - honor cold worker: ensure honor group failed
+java.lang.RuntimeException: ensure honor group failed
+	at com.adx.tencent.honor.HonorHotStore.ensureGroup(HonorHotStore.java:164)
+	at com.adx.tencent.honor.HonorColdWorker.runOnce(HonorColdWorker.java:38)
+	at com.adx.tencent.honor.HonorBackgroundTasks.lambda$run$0(HonorBackgroundTasks.java:40)
+	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
+	at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)
+	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java)
+	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
+	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
+	at java.base/java.lang.Thread.run(Thread.java:842)
+Caused by: java.lang.IllegalStateException: LettuceConnectionFactory is STOPPING
+	at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.assertStarted(LettuceConnectionFactory.java:1394)
+	at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getConnection(LettuceConnectionFactory.java:991)
+	at org.springframework.data.redis.core.RedisConnectionUtils.fetchConnection(RedisConnectionUtils.java:195)
+	at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:144)
+	at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:105)
+	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:383)
+	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:363)
+	at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:97)
+	at org.springframework.data.redis.core.DefaultStreamOperations.add(DefaultStreamOperations.java:136)
+	at org.springframework.data.redis.core.StreamOperations.add(StreamOperations.java:122)
+	at com.adx.tencent.honor.HonorHotStore.ensureGroup(HonorHotStore.java:159)
+	... 8 common frames omitted
+2026-07-21 10:42:47.119 [main] INFO  com.adx.tencent.Application - Starting Application using Java 17.0.14 with PID 1980 (/Users/yumeng/IdeaProjects/tencent-baidu-tracking/target/classes started by yumeng in /Users/yumeng/IdeaProjects/tencent-baidu-tracking)
+2026-07-21 10:42:47.120 [main] INFO  com.adx.tencent.Application - The following 1 profile is active: "dev"
+2026-07-21 10:42:47.367 [main] INFO  o.s.d.r.c.RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
+2026-07-21 10:42:47.368 [main] INFO  o.s.d.r.c.RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
+2026-07-21 10:42:47.384 [main] INFO  o.s.d.r.c.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 9 ms. Found 0 Redis repository interfaces.
+2026-07-21 10:42:47.534 [main] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat initialized with port 8788 (http)
+2026-07-21 10:42:47.537 [main] INFO  o.a.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8788"]
+2026-07-21 10:42:47.538 [main] INFO  o.a.catalina.core.StandardService - Starting service [Tomcat]
+2026-07-21 10:42:47.538 [main] INFO  o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.20]
+2026-07-21 10:42:47.555 [main] INFO  o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
+2026-07-21 10:42:47.555 [main] INFO  o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 421 ms
+2026-07-21 10:42:47.732 [main] INFO  com.zaxxer.hikari.HikariDataSource - adx-tidb - Starting...
+2026-07-21 10:42:49.342 [main] INFO  com.zaxxer.hikari.pool.HikariPool - adx-tidb - Added connection com.mysql.cj.jdbc.ConnectionImpl@2ae3235e
+2026-07-21 10:42:49.345 [main] INFO  com.zaxxer.hikari.HikariDataSource - adx-tidb - Start completed.
+2026-07-21 10:42:52.316 [main] INFO  com.adx.tencent.AppConfiguration - TiDB cold store initialized and migrated
+2026-07-21 10:42:52.393 [main] INFO  com.zaxxer.hikari.HikariDataSource - adx-tidb - Starting...
+2026-07-21 10:42:52.911 [main] INFO  com.zaxxer.hikari.pool.HikariPool - adx-tidb - Added connection com.mysql.cj.jdbc.ConnectionImpl@248d3a
+2026-07-21 10:42:52.912 [main] INFO  com.zaxxer.hikari.HikariDataSource - adx-tidb - Start completed.
+2026-07-21 10:42:53.766 [main] INFO  c.a.t.b.c.BaiduClueStoreConfiguration - Baidu clue cold store initialized and migrated
+2026-07-21 10:42:53.890 [main] INFO  o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8788"]
+2026-07-21 10:42:53.893 [main] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port 8788 (http) with context path ''
+2026-07-21 10:42:53.897 [main] INFO  com.adx.tencent.Application - Started Application in 6.953 seconds (process running for 7.413)
+2026-07-21 10:42:53.898 [main] INFO  c.a.t.AppConfiguration$BackgroundTasks - Cold worker started
+2026-07-21 10:42:53.898 [main] INFO  c.a.t.AppConfiguration$BackgroundTasks - Conversion sync started (skip leader election)
+2026-07-21 10:42:53.898 [Thread-3] INFO  c.a.t.AppConfiguration$BackgroundTasks - [ConversionSync] task started, interval=30000ms
+2026-07-21 10:42:53.898 [Thread-3] INFO  c.a.t.AppConfiguration$BackgroundTasks - [ConversionSync] executing...
+2026-07-21 10:42:53.898 [main] INFO  c.a.t.AppConfiguration$BackgroundTasks - Callback retry started (skip leader election)
+2026-07-21 10:42:53.898 [main] INFO  c.a.t.AppConfiguration$BackgroundTasks - Tag event sync started (skip leader election)
+2026-07-21 10:42:53.898 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=1, mode=REALTIME
+2026-07-21 10:42:53.898 [Thread-5] INFO  c.a.t.AppConfiguration$BackgroundTasks - [TagEventSync] task started, interval=300000ms
+2026-07-21 10:42:53.898 [Thread-4] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] task started, interval=300000ms, limit=100
+2026-07-21 10:42:53.898 [Thread-4] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] executing...
+2026-07-21 10:42:54.057 [Thread-4] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] done: fetched=0 sent=0 failed=0
+2026-07-21 10:42:54.057 [Thread-4] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] sleeping 300000ms until next run
+2026-07-21 10:42:54.394 [Thread-5] INFO  c.a.t.tagsync.TagEventSyncService - [TagEventSync] 同步完成,共 6 条广告位回传方式写入 Redis
+2026-07-21 10:42:54.395 [Thread-5] INFO  c.a.t.AppConfiguration$BackgroundTasks - [TagEventSync] done: synced=6
+2026-07-21 10:42:54.428 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=1 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:42:58.842 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=37aab33afb313017 | act=3 | deviceId=1ffdfffd-59fd-4e2b-b14c-737dffe7e9f4 | date=20260721 | payment=0.0
+2026-07-21 10:42:58.878 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=121565b46b60733a | act=3 | deviceId=7d142c06-8538-447a-ad29-52e2278ddcb8 | date=20260721 | payment=0.0
+2026-07-21 10:42:58.915 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=c1fa332a9d60fc1f | act=3 | deviceId=0e03f29b-3600-4205-b29c-c4764b299526 | date=20260721 | payment=0.0
+2026-07-21 10:42:58.953 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=faab0ed80066f658 | act=3 | deviceId=daa8f9bc-dd0e-4f72-a353-ff7640e9e990 | date=20260721 | payment=0.0
+2026-07-21 10:42:58.990 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=faab0ed80066f658 | act=2 | deviceId=daa8f9bc-dd0e-4f72-a353-ff7640e9e990 | date=20260721 | payment=0.0
+2026-07-21 10:42:59.028 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=a81b958958f41f1e | act=3 | deviceId=680D30751B794EEFB18ABF0C1BA2B54A7a200d4f8fc7eb8051d126411efcf8a9 | date=20260721 | payment=0.0
+2026-07-21 10:42:59.065 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=b01a4f636dfd1148 | act=3 | deviceId=a552f89479cde0d0 | date=20260721 | payment=0.0
+2026-07-21 10:42:59.102 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=0b314ae933639839 | act=3 | deviceId=9AD9DE9360CA4FBEA30F8AACDC0F010E5cf4e143e1e5a381a2e780b4facf135f | date=20260721 | payment=0.0
+2026-07-21 10:42:59.139 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=a147f4e1bdf10c67 | act=3 | deviceId=C03A3BAF124547C989525D6FF10F04FEfd9acaa0cb972b1710b1e1b188805839 | date=20260721 | payment=0.0
+2026-07-21 10:42:59.175 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=4a753510383ace1a | act=3 | deviceId=BC265E609EE74971A32FD338F8B7DF387d624819d95182c6fe5e7f19f91d0dc9 | date=20260721 | payment=0.0
+2026-07-21 10:42:59.211 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=a67f973e1402ee7f | act=3 | deviceId=5619fa43277062a7 | date=20260721 | payment=0.0
+2026-07-21 10:42:59.246 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=a08af84b68c06a12 | act=3 | deviceId=DC038EDCD50E473DA58236C4F996F131df0a4fe3196d071daacd0a5d81b8c205 | date=20260721 | payment=0.0
+2026-07-21 10:42:59.282 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=1c20aa2db8d4f878 | act=3 | deviceId=e8b2125f98b2510fed3e4716eaa3052f655c1fbd156a49241cd80e7182678453 | date=20260721 | payment=0.0
+2026-07-21 10:42:59.319 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=71df167efe094149 | act=3 | deviceId=6724db526e8b8bd4 | date=20260721 | payment=0.0
+2026-07-21 10:42:59.527 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=8b8bf31c35c60226 | act=3 | deviceId=E892456E39844236AA8BBA7ED037E28B0a2578a98d253dbf8ffa0914099e7464 | date=20260721 | payment=0.0
+2026-07-21 10:42:59.567 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=8e8d5c582b4d6961 | act=3 | deviceId=da78c6e4-f25e-49fa-9872-48b7af8b36df | date=20260721 | payment=0.0
+2026-07-21 10:42:59.608 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=601b618ffc28137f | act=3 | deviceId=de771b6c-5fb6-47b6-9954-c06e9408646e | date=20260721 | payment=0.0
+2026-07-21 10:42:59.648 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=19f5056b90a5b027 | act=3 | deviceId=60ED24F3112240A7A006AAEB82F16ED65336e81c2617070789c6e8347989dfd4 | date=20260721 | payment=0.0
+2026-07-21 10:42:59.689 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=2a18351b77360442 | act=3 | deviceId=3f100f74-87ca-45fa-b0ef-75dcd2766418 | date=20260721 | payment=0.0
+2026-07-21 10:42:59.729 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=ec6f2105e0aeff24 | act=3 | deviceId=268d1f452e586dab | date=20260721 | payment=0.0
+2026-07-21 10:42:59.772 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=b4066c1c469db51c | act=3 | deviceId=CAF8FBBA7D0F4D72961D0CD5D7DDF38921273be334236c784e59f3fc15ca7702 | date=20260721 | payment=0.0
+2026-07-21 10:42:59.867 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=72cf2ffbbd4a2761 | act=3 | deviceId=aafbf0e8-a6ab-40c6-b45d-a16d95f3534b | date=20260721 | payment=0.0
+2026-07-21 10:42:59.957 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=9b9ec2fe30600237 | act=3 | deviceId=1E8F833B66114B71BAA344B289FF663188a9fc4d884b3afa1e029398dc01e263 | date=20260721 | payment=0.0
+2026-07-21 10:43:07.075 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=6153ms
+2026-07-21 10:43:27.842 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=1 processed, cost=33943ms, totalFetched=1000, matched=23, sent=0, failed=0, skipped=977, alreadySent=23, mode=REALTIME
+2026-07-21 10:43:27.845 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=2, mode=REALTIME
+2026-07-21 10:43:28.247 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=2 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:43:30.741 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=de74634339ccf91b | act=3 | deviceId=a7a57c3b-6949-4a8f-ab6f-968c29f9f27c | date=20260721 | payment=0.0
+2026-07-21 10:43:32.553 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=1812ms
+2026-07-21 10:43:32.590 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=2 processed, cost=4745ms, totalFetched=2000, matched=24, sent=0, failed=0, skipped=1976, alreadySent=24, mode=REALTIME
+2026-07-21 10:43:32.590 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=3, mode=REALTIME
+2026-07-21 10:43:32.902 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=3 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:43:34.466 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=9c8d6d09940d9250 | act=3 | deviceId=aea64fdc652f618e38c82b76f3e0d540f401513e60e8f5374618b2b223b166e5 | date=20260721 | payment=0.0
+2026-07-21 10:43:34.504 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=9767b62a2aec3d1c | act=3 | deviceId=56A4F29FF0DE4D78AE79B71193856CAD2ad4406010d481fe8813702c1fabe15f | date=20260721 | payment=0.0
+2026-07-21 10:43:34.540 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=8805805927790c50 | act=2 | deviceId=6c956281-03b9-43c8-a8ba-392925c01591 | date=20260721 | payment=0.0
+2026-07-21 10:43:34.578 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=8805805927790c50 | act=3 | deviceId=6c956281-03b9-43c8-a8ba-392925c01591 | date=20260721 | payment=0.0
+2026-07-21 10:43:36.488 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=1909ms
+2026-07-21 10:43:36.525 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=3 processed, cost=3934ms, totalFetched=3000, matched=28, sent=0, failed=0, skipped=2972, alreadySent=28, mode=REALTIME
+2026-07-21 10:43:36.525 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=4, mode=REALTIME
+2026-07-21 10:43:36.973 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=4 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:43:38.308 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=0ceaf34009cd4c29 | act=3 | deviceId=7a7284a136b9d94b65b084600faf20d97ecf114e1ade93ffb5618886404cd232 | date=20260721 | payment=0.0
+2026-07-21 10:43:38.345 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=6f8d93c2c4d0034d | act=3 | deviceId=7d92f9ef07d3c32c | date=20260721 | payment=0.0
+2026-07-21 10:43:38.560 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=ceade02954d2f73f | act=3 | deviceId=a6523593-6c19-47c9-b8a5-871b62e200e1 | date=20260721 | payment=0.0
+2026-07-21 10:43:40.573 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=2013ms
+2026-07-21 10:43:40.611 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=4 processed, cost=4085ms, totalFetched=4000, matched=31, sent=0, failed=0, skipped=3969, alreadySent=31, mode=REALTIME
+2026-07-21 10:43:40.611 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=5, mode=REALTIME
+2026-07-21 10:43:40.987 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=5 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:43:44.278 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=1943ms
+2026-07-21 10:43:44.278 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=5 processed, cost=3667ms, totalFetched=5000, matched=31, sent=0, failed=0, skipped=4969, alreadySent=31, mode=REALTIME
+2026-07-21 10:43:44.279 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=6, mode=REALTIME
+2026-07-21 10:43:44.638 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=6 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:43:46.086 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=3c3e6c755dd31b43 | act=3 | deviceId=3E445FB97BE94951AEAEA5A5CFDB395A6ab5cb3b6b422c9de568f8845e8e5ee6 | date=20260721 | payment=0.0
+2026-07-21 10:43:46.885 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=7a39636cb50e5130 | act=3 | deviceId=fd55b029-d5ad-4c5c-a43e-13a50188ad0a | date=20260721 | payment=0.0
+2026-07-21 10:43:49.776 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=2890ms
+2026-07-21 10:43:49.813 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=6 processed, cost=5534ms, totalFetched=6000, matched=33, sent=0, failed=0, skipped=5967, alreadySent=33, mode=REALTIME
+2026-07-21 10:43:49.814 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=7, mode=REALTIME
+2026-07-21 10:43:50.160 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=7 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:43:53.570 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=1952ms
+2026-07-21 10:43:53.573 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=7 processed, cost=3759ms, totalFetched=7000, matched=33, sent=0, failed=0, skipped=6967, alreadySent=33, mode=REALTIME
+2026-07-21 10:43:53.573 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=8, mode=REALTIME
+2026-07-21 10:43:53.938 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=8 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:43:54.874 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=36c5fdb07db89479 | act=3 | deviceId=9312a168292fd60ef1cda4773bb1344733aeb448a19e8d27f98ec5c5177b2063 | date=20260721 | payment=0.0
+2026-07-21 10:43:54.928 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=d23259588687ae31 | act=3 | deviceId=962a4683d61f34da | date=20260721 | payment=0.0
+2026-07-21 10:43:55.639 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=710ms
+2026-07-21 10:43:55.678 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=8 processed, cost=2104ms, totalFetched=8000, matched=35, sent=0, failed=0, skipped=7965, alreadySent=35, mode=REALTIME
+2026-07-21 10:43:55.678 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=9, mode=REALTIME
+2026-07-21 10:43:56.025 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=9 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:43:57.111 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=529ms
+2026-07-21 10:43:57.112 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=9 processed, cost=1433ms, totalFetched=9000, matched=35, sent=0, failed=0, skipped=8965, alreadySent=35, mode=REALTIME
+2026-07-21 10:43:57.112 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=10, mode=REALTIME
+2026-07-21 10:43:57.468 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=10 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:43:58.293 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=73f069e5d782a825 | act=3 | deviceId=7fc0cf2d8d2766f5e8c838f0a1a5dd4dae09d7dcebe32fc74836bb0dcb59f6be | date=20260721 | payment=0.0
+2026-07-21 10:43:58.330 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=754f70c88d5dc378 | act=2 | deviceId=f7a1ff7e-bfff-404d-ffee-76fba3fbb33d | date=20260721 | payment=0.0
+2026-07-21 10:43:58.367 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=754f70c88d5dc378 | act=3 | deviceId=f7a1ff7e-bfff-404d-ffee-76fba3fbb33d | date=20260721 | payment=0.0
+2026-07-21 10:43:58.994 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=625ms
+2026-07-21 10:43:59.030 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=10 processed, cost=1917ms, totalFetched=10000, matched=38, sent=0, failed=0, skipped=9962, alreadySent=38, mode=REALTIME
+2026-07-21 10:43:59.030 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=11, mode=REALTIME
+2026-07-21 10:43:59.362 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=11 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:44:01.178 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=766ms
+2026-07-21 10:44:01.179 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=11 processed, cost=2148ms, totalFetched=11000, matched=38, sent=0, failed=0, skipped=10962, alreadySent=38, mode=REALTIME
+2026-07-21 10:44:01.180 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=12, mode=REALTIME
+2026-07-21 10:44:01.534 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=12 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:44:03.062 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=1027ms
+2026-07-21 10:44:03.062 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=12 processed, cost=1882ms, totalFetched=12000, matched=38, sent=0, failed=0, skipped=11962, alreadySent=38, mode=REALTIME
+2026-07-21 10:44:03.062 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=13, mode=REALTIME
+2026-07-21 10:44:03.398 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=13 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:44:04.260 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=54141b84e905ef79 | act=3 | deviceId=BCBDEB1EF76A4C588EDC4D42999F6DECbd042f9bab2b107c76de5240549922e1 | date=20260721 | payment=0.0
+2026-07-21 10:44:04.297 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=1e1aa5e180296f76 | act=3 | deviceId=c1054fb0b5d89cbdaef1bdb36327845ffd7dc5d5ee89c0a2d1a3fac4f71b15d0 | date=20260721 | payment=0.0
+2026-07-21 10:44:04.334 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=23da7f152ab4f843 | act=3 | deviceId=69C5D4FA003A4F5D912C68E35CD8F2D586038c184bfa911b43365f207cca922c | date=20260721 | payment=0.0
+2026-07-21 10:44:04.371 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=a5c82d0e1fb3f04b | act=3 | deviceId=7149b03377553805706ffa0196e8b538cd21f575966dc75401ba2fc8eb001b0c | date=20260721 | payment=0.0
+2026-07-21 10:44:05.752 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=1380ms
+2026-07-21 10:44:05.833 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=13 processed, cost=2771ms, totalFetched=13000, matched=42, sent=0, failed=0, skipped=12958, alreadySent=42, mode=REALTIME
+2026-07-21 10:44:05.833 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=14, mode=REALTIME
+2026-07-21 10:44:06.174 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=14 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:44:06.735 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=2a120206b2ddbd68 | act=3 | deviceId=c0c79f10ad487ee7 | date=20260721 | payment=0.0
+2026-07-21 10:44:06.772 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=2810c3e2f8ab3c6e | act=3 | deviceId=4B58E1E38C424F0CA461C23FA47D584C21bb0817d70d9c99888aacf89c16957e | date=20260721 | payment=0.0
+2026-07-21 10:44:06.808 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=fd88c48912335734 | act=3 | deviceId=CF136BFC16504EFF8E8D1F143F907EDE09e2b42025bb450c5d9ae573bd1cd8a3 | date=20260721 | payment=0.0
+2026-07-21 10:44:07.814 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=1006ms
+2026-07-21 10:44:07.858 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=14 processed, cost=2024ms, totalFetched=14000, matched=45, sent=0, failed=0, skipped=13955, alreadySent=45, mode=REALTIME
+2026-07-21 10:44:07.858 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=15, mode=REALTIME
+2026-07-21 10:44:08.213 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=15 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:44:08.986 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=4b83d9e2a363bb6c | act=3 | deviceId=627a6998-7d0d-4152-a95a-eaa84de4a5f1 | date=20260721 | payment=0.0
+2026-07-21 10:44:09.023 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=1f73e1ae93faaf3e | act=3 | deviceId=a1210ad58bdb1b9d | date=20260721 | payment=0.0
+2026-07-21 10:44:09.059 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=3f120a4a5c46e64f | act=3 | deviceId=289b40819ecd1532dcc265ddf7360e1959941582ea818250a8c99212b72ff81d | date=20260721 | payment=0.0
+2026-07-21 10:44:10.063 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=1003ms
+2026-07-21 10:44:10.099 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=15 processed, cost=2241ms, totalFetched=15000, matched=48, sent=0, failed=0, skipped=14952, alreadySent=48, mode=REALTIME
+2026-07-21 10:44:10.099 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=16, mode=REALTIME
+2026-07-21 10:44:10.555 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=16 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:44:13.203 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=1660ms
+2026-07-21 10:44:13.204 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=16 processed, cost=3104ms, totalFetched=16000, matched=48, sent=0, failed=0, skipped=15952, alreadySent=48, mode=REALTIME
+2026-07-21 10:44:13.204 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=17, mode=REALTIME
+2026-07-21 10:44:13.568 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=17 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:44:14.985 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=42337b604824ab42 | act=3 | deviceId=F04EC33EFF6A492E8ACCFB1919F98A4Dfc7f6dacc9c47b02816c58fb90d6351a | date=20260721 | payment=0.0
+2026-07-21 10:44:15.025 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=36f3f89a2eb4bc17 | act=3 | deviceId=5C18EFC5CAF343549070478925AB52F222957606c86158f024d4b65ec5f86085 | date=20260721 | payment=0.0
+2026-07-21 10:44:15.065 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=95d548b635cf065d | act=3 | deviceId=1a02dbea98189d3308e890a04600f3b319bc1848196500f1ae1d64135aee9f13 | date=20260721 | payment=0.0
+2026-07-21 10:44:16.668 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=1602ms
+2026-07-21 10:44:16.707 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=17 processed, cost=3503ms, totalFetched=17000, matched=51, sent=0, failed=0, skipped=16949, alreadySent=51, mode=REALTIME
+2026-07-21 10:44:16.707 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=18, mode=REALTIME
+2026-07-21 10:44:17.045 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=18 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:44:18.809 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=c19e632be019ef15 | act=3 | deviceId=daed56dead51c6d81b8d1b360c0c5a4dcb7ed95a976d3d8bfc8f3587bfb6c18f | date=20260721 | payment=0.0
+2026-07-21 10:44:18.851 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=4ba5807a8affd16d | act=3 | deviceId=3ade9218-7957-475c-b758-948f5582592f | date=20260721 | payment=0.0
+2026-07-21 10:44:18.888 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=5c02a0d7dbe14f73 | act=3 | deviceId=fb31d3edf73b7ace | date=20260721 | payment=0.0
+2026-07-21 10:44:21.320 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=2431ms
+2026-07-21 10:44:21.360 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=18 processed, cost=4652ms, totalFetched=18000, matched=54, sent=0, failed=0, skipped=17946, alreadySent=54, mode=REALTIME
+2026-07-21 10:44:21.360 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=19, mode=REALTIME
+2026-07-21 10:44:21.714 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=19 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:44:23.295 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=b9af460ae324e02e | act=3 | deviceId=a6c58fca50c606a5 | date=20260721 | payment=0.0
+2026-07-21 10:44:23.348 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=0ceaf34009cd4c29 | act=2 | deviceId=7a7284a136b9d94b65b084600faf20d97ecf114e1ade93ffb5618886404cd232 | date=20260721 | payment=0.0
+2026-07-21 10:44:25.111 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=1760ms
+2026-07-21 10:44:25.147 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=19 processed, cost=3786ms, totalFetched=19000, matched=56, sent=0, failed=0, skipped=18944, alreadySent=56, mode=REALTIME
+2026-07-21 10:44:25.147 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=20, mode=REALTIME
+2026-07-21 10:44:25.496 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=20 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:44:27.294 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=f1268ff0d135804d | act=3 | deviceId=B432AC8353144A4EB081FEF44183D8657a5978402ef108bee5d3753a2b1253dc | date=20260721 | payment=0.0
+2026-07-21 10:44:27.331 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=33f59714fb35c437 | act=3 | deviceId=f9a1344c-c2b1-498c-acda-302be708e5bc | date=20260721 | payment=0.0
+2026-07-21 10:44:27.365 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=d7ee5a5898a96e65 | act=3 | deviceId=6FF0D3F706594817B2A4CE1E62DE7191d5b0aabd200a64fd23af07dfbba7f46e | date=20260721 | payment=0.0
+2026-07-21 10:44:27.401 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=574c00394520692d | act=3 | deviceId=A9EC38E9CEEB4975972FD0BF368CDCA2e38974f5c69eb5544027e86348126ef4 | date=20260721 | payment=0.0
+2026-07-21 10:44:28.645 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=1244ms
+2026-07-21 10:44:28.685 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=20 processed, cost=3537ms, totalFetched=20000, matched=60, sent=0, failed=0, skipped=19940, alreadySent=60, mode=REALTIME
+2026-07-21 10:44:28.685 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=21, mode=REALTIME
+2026-07-21 10:44:29.031 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=21 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:44:29.742 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=925d9f244e20be7d | act=3 | deviceId=0c426e6ea374c67ad9e7b6286a1e452d29240fcfeafae7f4fd9b6c9173258a9d | date=20260721 | payment=0.0
+2026-07-21 10:44:29.779 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=de5cac6216ebbe4f | act=3 | deviceId=8421D64B6BAF4E6CA2B35C7BEF68277Ad75dbeb9087938a0057f3a518975d7c7 | date=20260721 | payment=0.0
+2026-07-21 10:44:30.367 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=587ms
+2026-07-21 10:44:30.406 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=21 processed, cost=1721ms, totalFetched=21000, matched=62, sent=0, failed=0, skipped=20938, alreadySent=62, mode=REALTIME
+2026-07-21 10:44:30.407 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=22, mode=REALTIME
+2026-07-21 10:44:30.798 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=22 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:44:31.232 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=96a03efd8e1fd260 | act=3 | deviceId=9b7e71e1-f1fc-4412-b67e-daa2124e1d16 | date=20260721 | payment=0.0
+2026-07-21 10:44:31.269 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=239b11106bf14e7b | act=3 | deviceId=3c0d5e6f19efd69c | date=20260721 | payment=0.0
+2026-07-21 10:44:31.306 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=0d7c6be036bb7e57 | act=3 | deviceId=7219B42077224C3FBF2A5965626A7A20750e0075d460bc50975866ef0993b56a | date=20260721 | payment=0.0
+2026-07-21 10:44:31.343 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=59a38e7f1090cf64 | act=3 | deviceId=4574ad13-e2f9-415a-8300-481a8ad1d0c4 | date=20260721 | payment=0.0
+2026-07-21 10:44:31.378 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=b5cdb5dda9bb3f5a | act=3 | deviceId=c62670aa05ea7fc84d94cd79165b99375eb222a8dceb1dc9f39ebbfc0120146d | date=20260721 | payment=0.0
+2026-07-21 10:44:31.414 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=5058d2330cdb8130 | act=3 | deviceId=9CB31F636B7A4B4C85DDC820B0B9CE9Acc663ed1eec5ba4b06fa7a7197572c8b | date=20260721 | payment=0.0
+2026-07-21 10:44:31.449 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=b0f2c0fa31bdd358 | act=3 | deviceId=96f5409c-db40-489e-9543-b8592fcca23f | date=20260721 | payment=0.0
+2026-07-21 10:44:31.946 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=496ms
+2026-07-21 10:44:31.985 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=22 processed, cost=1578ms, totalFetched=22000, matched=69, sent=0, failed=0, skipped=21931, alreadySent=69, mode=REALTIME
+2026-07-21 10:44:31.985 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=23, mode=REALTIME
+2026-07-21 10:44:32.328 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=23 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:44:32.730 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=3dd78eb546436439 | act=3 | deviceId=61600c4eec7bdaa2dc92aa8bd4b182d2a6b3434479e227060c9366079593e5c1 | date=20260721 | payment=0.0
+2026-07-21 10:44:32.765 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=5058d2330cdb8130 | act=2 | deviceId=9CB31F636B7A4B4C85DDC820B0B9CE9Acc663ed1eec5ba4b06fa7a7197572c8b | date=20260721 | payment=0.0
+2026-07-21 10:44:32.800 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=43ee44c0b3c06223 | act=2 | deviceId=AE6E5913C1F94B72BBBE1FE45ABCC391562a7ccbf3f6d4ed9df959366a96ae8e | date=20260721 | payment=0.0
+2026-07-21 10:44:32.835 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=43ee44c0b3c06223 | act=3 | deviceId=AE6E5913C1F94B72BBBE1FE45ABCC391562a7ccbf3f6d4ed9df959366a96ae8e | date=20260721 | payment=0.0
+2026-07-21 10:44:32.871 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=103478fc5b478e14 | act=3 | deviceId=a845d9f4-43b1-4935-b9e0-e95df0d90217 | date=20260721 | payment=0.0
+2026-07-21 10:44:32.906 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=b168ca3a325d143a | act=3 | deviceId=f847395c77445724e8b793cd6c855a4f000cf79d894b464baf320c5cc9f1f7a3 | date=20260721 | payment=0.0
+2026-07-21 10:44:33.328 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=422ms
+2026-07-21 10:44:33.365 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=23 processed, cost=1379ms, totalFetched=23000, matched=75, sent=0, failed=0, skipped=22925, alreadySent=75, mode=REALTIME
+2026-07-21 10:44:33.365 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=24, mode=REALTIME
+2026-07-21 10:44:33.733 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=24 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:44:34.116 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=da5c865e88db3d74 | act=3 | deviceId=3947d60f-1618-4025-bd17-9b0c12bd2fea | date=20260721 | payment=0.0
+2026-07-21 10:44:34.155 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=e041aa89a474265d | act=3 | deviceId=6efbca3e-4787-4d1f-b075-3ee16da43f98 | date=20260721 | payment=0.0
+2026-07-21 10:44:34.194 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=c988dded7b9e161a | act=3 | deviceId=0d5b16d2-d405-4718-a877-304d4c2f5fba | date=20260721 | payment=0.0
+2026-07-21 10:44:34.231 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=9d501ee87328194d | act=3 | deviceId=dc4b3360-e6bc-4444-856a-6144944876fb | date=20260721 | payment=0.0
+2026-07-21 10:44:34.661 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=429ms
+2026-07-21 10:44:34.700 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=24 processed, cost=1334ms, totalFetched=24000, matched=79, sent=0, failed=0, skipped=23921, alreadySent=79, mode=REALTIME
+2026-07-21 10:44:34.700 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=25, mode=REALTIME
+2026-07-21 10:44:35.052 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=25 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:44:35.371 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=c0c40acd0bcefc6b | act=3 | deviceId=ff4ab597f7b061ab | date=20260721 | payment=0.0
+2026-07-21 10:44:35.410 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=e0ecc647a3cffa35 | act=3 | deviceId=31f79c1e-08dd-4085-b616-593874c90517 | date=20260721 | payment=0.0
+2026-07-21 10:44:35.448 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=feed32ea77290342 | act=3 | deviceId=14F57C0BAC8B4E9D9B14CF71E91DDB0F8cb66fa55a45c9a451fa954052455b6b | date=20260721 | payment=0.0
+2026-07-21 10:44:35.554 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=feed32ea77290342 | act=2 | deviceId=14F57C0BAC8B4E9D9B14CF71E91DDB0F8cb66fa55a45c9a451fa954052455b6b | date=20260721 | payment=0.0
+2026-07-21 10:44:36.074 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=520ms
+2026-07-21 10:44:36.438 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=25 processed, cost=1737ms, totalFetched=25000, matched=83, sent=0, failed=0, skipped=24917, alreadySent=83, mode=REALTIME
+2026-07-21 10:44:36.438 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=26, mode=REALTIME
+2026-07-21 10:44:36.767 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=26 returned 1000 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:44:37.700 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=24efa17dfa406912 | act=3 | deviceId=D352E673A474473CBA01E79DCE12163A612c63b089aeae604cd8160634fac37c | date=20260721 | payment=0.0
+2026-07-21 10:44:37.736 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=4505e6a2a1776650 | act=3 | deviceId=c49a4472-cdde-4011-a5c4-b4a0d4a0a722 | date=20260721 | payment=0.0
+2026-07-21 10:44:37.777 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=b4b3b6c43657a260 | act=3 | deviceId=1b4e41b2-5b07-48fa-868d-b7fad320c158 | date=20260721 | payment=0.0
+2026-07-21 10:44:37.814 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=e0822e73216f1c6a | act=3 | deviceId=3CCB0338EE114D9BB0629E28A393ACA69d4d19fc39a91c4c8fc30e3256410b4e | date=20260721 | payment=0.0
+2026-07-21 10:44:38.824 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=1010ms
+2026-07-21 10:44:38.863 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=26 processed, cost=2424ms, totalFetched=26000, matched=87, sent=0, failed=0, skipped=25913, alreadySent=87, mode=REALTIME
+2026-07-21 10:44:38.863 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=27, mode=REALTIME
+2026-07-21 10:44:39.410 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=27 returned 739 records, responsePageSize=27, mode=REALTIME
+2026-07-21 10:44:40.702 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=4fdfaf87983b0e36 | act=3 | deviceId=385b4d0d01f2ce0029a66b14e22a74ca257876637514be6750ecd198e2620efb | date=20260721 | payment=0.0
+2026-07-21 10:44:40.739 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=e151aed9e6623f5c | act=3 | deviceId=879d6fb0cff35e70 | date=20260721 | payment=0.0
+2026-07-21 10:44:40.776 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=7aaacf28a48af32d | act=3 | deviceId=b7e0103365c7a925 | date=20260721 | payment=0.0
+2026-07-21 10:44:41.739 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=739, batches=1, batchSize=1000, cost=962ms
+2026-07-21 10:44:41.776 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=27 processed, cost=2912ms, totalFetched=26739, matched=90, sent=0, failed=0, skipped=26649, alreadySent=90, mode=REALTIME
+2026-07-21 10:44:41.776 [Thread-3] INFO  c.a.t.AppConfiguration$BackgroundTasks - [ConversionSync] done: fetched=26739 sent=0 failed=0
+2026-07-21 10:44:41.776 [Thread-3] INFO  c.a.t.AppConfiguration$BackgroundTasks - [ConversionSync] sleeping 30000ms until next run
+2026-07-21 10:45:11.781 [Thread-3] INFO  c.a.t.AppConfiguration$BackgroundTasks - [ConversionSync] executing...
+2026-07-21 10:45:11.784 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=1, mode=REALTIME
+2026-07-21 10:45:12.128 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=1 returned 1000 records, responsePageSize=28, mode=REALTIME
+2026-07-21 10:45:13.169 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=37aab33afb313017 | act=3 | deviceId=1ffdfffd-59fd-4e2b-b14c-737dffe7e9f4 | date=20260721 | payment=0.0
+2026-07-21 10:45:13.211 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=121565b46b60733a | act=3 | deviceId=7d142c06-8538-447a-ad29-52e2278ddcb8 | date=20260721 | payment=0.0
+2026-07-21 10:45:13.246 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=c1fa332a9d60fc1f | act=3 | deviceId=0e03f29b-3600-4205-b29c-c4764b299526 | date=20260721 | payment=0.0
+2026-07-21 10:45:13.458 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=faab0ed80066f658 | act=3 | deviceId=daa8f9bc-dd0e-4f72-a353-ff7640e9e990 | date=20260721 | payment=0.0
+2026-07-21 10:45:13.494 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=faab0ed80066f658 | act=2 | deviceId=daa8f9bc-dd0e-4f72-a353-ff7640e9e990 | date=20260721 | payment=0.0
+2026-07-21 10:45:13.529 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=a81b958958f41f1e | act=3 | deviceId=680D30751B794EEFB18ABF0C1BA2B54A7a200d4f8fc7eb8051d126411efcf8a9 | date=20260721 | payment=0.0
+2026-07-21 10:45:13.565 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=b01a4f636dfd1148 | act=3 | deviceId=a552f89479cde0d0 | date=20260721 | payment=0.0
+2026-07-21 10:45:13.600 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=0b314ae933639839 | act=3 | deviceId=9AD9DE9360CA4FBEA30F8AACDC0F010E5cf4e143e1e5a381a2e780b4facf135f | date=20260721 | payment=0.0
+2026-07-21 10:45:13.705 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=a147f4e1bdf10c67 | act=3 | deviceId=C03A3BAF124547C989525D6FF10F04FEfd9acaa0cb972b1710b1e1b188805839 | date=20260721 | payment=0.0
+2026-07-21 10:45:13.740 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=4a753510383ace1a | act=3 | deviceId=BC265E609EE74971A32FD338F8B7DF387d624819d95182c6fe5e7f19f91d0dc9 | date=20260721 | payment=0.0
+2026-07-21 10:45:13.775 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=a67f973e1402ee7f | act=3 | deviceId=5619fa43277062a7 | date=20260721 | payment=0.0
+2026-07-21 10:45:13.811 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=a08af84b68c06a12 | act=3 | deviceId=DC038EDCD50E473DA58236C4F996F131df0a4fe3196d071daacd0a5d81b8c205 | date=20260721 | payment=0.0
+2026-07-21 10:45:13.846 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=1c20aa2db8d4f878 | act=3 | deviceId=e8b2125f98b2510fed3e4716eaa3052f655c1fbd156a49241cd80e7182678453 | date=20260721 | payment=0.0
+2026-07-21 10:45:13.881 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=71df167efe094149 | act=3 | deviceId=6724db526e8b8bd4 | date=20260721 | payment=0.0
+2026-07-21 10:45:13.916 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=8b8bf31c35c60226 | act=3 | deviceId=E892456E39844236AA8BBA7ED037E28B0a2578a98d253dbf8ffa0914099e7464 | date=20260721 | payment=0.0
+2026-07-21 10:45:13.956 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=8e8d5c582b4d6961 | act=3 | deviceId=da78c6e4-f25e-49fa-9872-48b7af8b36df | date=20260721 | payment=0.0
+2026-07-21 10:45:13.992 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=601b618ffc28137f | act=3 | deviceId=de771b6c-5fb6-47b6-9954-c06e9408646e | date=20260721 | payment=0.0
+2026-07-21 10:45:14.028 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=19f5056b90a5b027 | act=3 | deviceId=60ED24F3112240A7A006AAEB82F16ED65336e81c2617070789c6e8347989dfd4 | date=20260721 | payment=0.0
+2026-07-21 10:45:14.063 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=2a18351b77360442 | act=3 | deviceId=3f100f74-87ca-45fa-b0ef-75dcd2766418 | date=20260721 | payment=0.0
+2026-07-21 10:45:14.098 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=ec6f2105e0aeff24 | act=3 | deviceId=268d1f452e586dab | date=20260721 | payment=0.0
+2026-07-21 10:45:14.133 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=b4066c1c469db51c | act=3 | deviceId=CAF8FBBA7D0F4D72961D0CD5D7DDF38921273be334236c784e59f3fc15ca7702 | date=20260721 | payment=0.0
+2026-07-21 10:45:14.169 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=72cf2ffbbd4a2761 | act=3 | deviceId=aafbf0e8-a6ab-40c6-b45d-a16d95f3534b | date=20260721 | payment=0.0
+2026-07-21 10:45:14.379 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=9b9ec2fe30600237 | act=3 | deviceId=1E8F833B66114B71BAA344B289FF663188a9fc4d884b3afa1e029398dc01e263 | date=20260721 | payment=0.0
+2026-07-21 10:45:15.872 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=1492ms
+2026-07-21 10:45:15.982 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=1 processed, cost=4198ms, totalFetched=1000, matched=23, sent=0, failed=0, skipped=977, alreadySent=23, mode=REALTIME
+2026-07-21 10:45:15.982 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=2, mode=REALTIME
+2026-07-21 10:45:16.285 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=2 returned 1000 records, responsePageSize=28, mode=REALTIME
+2026-07-21 10:45:17.560 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=de74634339ccf91b | act=3 | deviceId=a7a57c3b-6949-4a8f-ab6f-968c29f9f27c | date=20260721 | payment=0.0
+2026-07-21 10:45:19.566 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=2005ms
+2026-07-21 10:45:19.601 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=2 processed, cost=3618ms, totalFetched=2000, matched=24, sent=0, failed=0, skipped=1976, alreadySent=24, mode=REALTIME
+2026-07-21 10:45:19.601 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=3, mode=REALTIME
+2026-07-21 10:45:19.874 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=3 returned 1000 records, responsePageSize=28, mode=REALTIME
+2026-07-21 10:45:21.244 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=9c8d6d09940d9250 | act=3 | deviceId=aea64fdc652f618e38c82b76f3e0d540f401513e60e8f5374618b2b223b166e5 | date=20260721 | payment=0.0
+2026-07-21 10:45:21.279 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=9767b62a2aec3d1c | act=3 | deviceId=56A4F29FF0DE4D78AE79B71193856CAD2ad4406010d481fe8813702c1fabe15f | date=20260721 | payment=0.0
+2026-07-21 10:45:21.315 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=8805805927790c50 | act=2 | deviceId=6c956281-03b9-43c8-a8ba-392925c01591 | date=20260721 | payment=0.0
+2026-07-21 10:45:21.352 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=8805805927790c50 | act=3 | deviceId=6c956281-03b9-43c8-a8ba-392925c01591 | date=20260721 | payment=0.0
+2026-07-21 10:45:22.518 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=1166ms
+2026-07-21 10:45:22.554 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=3 processed, cost=2952ms, totalFetched=3000, matched=28, sent=0, failed=0, skipped=2972, alreadySent=28, mode=REALTIME
+2026-07-21 10:45:22.554 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=4, mode=REALTIME
+2026-07-21 10:45:22.852 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=4 returned 1000 records, responsePageSize=28, mode=REALTIME
+2026-07-21 10:45:23.824 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=0ceaf34009cd4c29 | act=3 | deviceId=7a7284a136b9d94b65b084600faf20d97ecf114e1ade93ffb5618886404cd232 | date=20260721 | payment=0.0
+2026-07-21 10:45:23.859 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=6f8d93c2c4d0034d | act=3 | deviceId=7d92f9ef07d3c32c | date=20260721 | payment=0.0
+2026-07-21 10:45:23.895 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=ceade02954d2f73f | act=3 | deviceId=a6523593-6c19-47c9-b8a5-871b62e200e1 | date=20260721 | payment=0.0
+2026-07-21 10:45:24.716 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=820ms
+2026-07-21 10:45:24.755 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=4 processed, cost=2201ms, totalFetched=4000, matched=31, sent=0, failed=0, skipped=3969, alreadySent=31, mode=REALTIME
+2026-07-21 10:45:24.755 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=5, mode=REALTIME
+2026-07-21 10:45:25.073 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=5 returned 1000 records, responsePageSize=28, mode=REALTIME
+2026-07-21 10:45:26.091 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=518ms
+2026-07-21 10:45:26.091 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=5 processed, cost=1336ms, totalFetched=5000, matched=31, sent=0, failed=0, skipped=4969, alreadySent=31, mode=REALTIME
+2026-07-21 10:45:26.091 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=6, mode=REALTIME
+2026-07-21 10:45:26.377 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=6 returned 1000 records, responsePageSize=28, mode=REALTIME
+2026-07-21 10:45:27.300 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=3c3e6c755dd31b43 | act=3 | deviceId=3E445FB97BE94951AEAEA5A5CFDB395A6ab5cb3b6b422c9de568f8845e8e5ee6 | date=20260721 | payment=0.0
+2026-07-21 10:45:27.336 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=7a39636cb50e5130 | act=3 | deviceId=fd55b029-d5ad-4c5c-a43e-13a50188ad0a | date=20260721 | payment=0.0
+2026-07-21 10:45:27.841 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=505ms
+2026-07-21 10:45:27.878 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=6 processed, cost=1786ms, totalFetched=6000, matched=33, sent=0, failed=0, skipped=5967, alreadySent=33, mode=REALTIME
+2026-07-21 10:45:27.878 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=7, mode=REALTIME
+2026-07-21 10:45:28.199 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=7 returned 1000 records, responsePageSize=28, mode=REALTIME
+2026-07-21 10:45:28.982 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=466ms
+2026-07-21 10:45:28.983 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=7 processed, cost=1104ms, totalFetched=7000, matched=33, sent=0, failed=0, skipped=6967, alreadySent=33, mode=REALTIME
+2026-07-21 10:45:28.983 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=8, mode=REALTIME
+2026-07-21 10:45:29.276 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=8 returned 1000 records, responsePageSize=28, mode=REALTIME
+2026-07-21 10:45:29.663 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=36c5fdb07db89479 | act=3 | deviceId=9312a168292fd60ef1cda4773bb1344733aeb448a19e8d27f98ec5c5177b2063 | date=20260721 | payment=0.0
+2026-07-21 10:45:29.700 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=d23259588687ae31 | act=3 | deviceId=962a4683d61f34da | date=20260721 | payment=0.0
+2026-07-21 10:45:30.154 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=454ms
+2026-07-21 10:45:30.191 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=8 processed, cost=1208ms, totalFetched=8000, matched=35, sent=0, failed=0, skipped=7965, alreadySent=35, mode=REALTIME
+2026-07-21 10:45:30.191 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=9, mode=REALTIME
+2026-07-21 10:45:30.455 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=9 returned 1000 records, responsePageSize=28, mode=REALTIME
+2026-07-21 10:45:31.144 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=382ms
+2026-07-21 10:45:31.144 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=9 processed, cost=953ms, totalFetched=9000, matched=35, sent=0, failed=0, skipped=8965, alreadySent=35, mode=REALTIME
+2026-07-21 10:45:31.144 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=10, mode=REALTIME
+2026-07-21 10:45:31.447 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=10 returned 1000 records, responsePageSize=28, mode=REALTIME
+2026-07-21 10:45:31.783 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=73f069e5d782a825 | act=3 | deviceId=7fc0cf2d8d2766f5e8c838f0a1a5dd4dae09d7dcebe32fc74836bb0dcb59f6be | date=20260721 | payment=0.0
+2026-07-21 10:45:31.819 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=754f70c88d5dc378 | act=2 | deviceId=f7a1ff7e-bfff-404d-ffee-76fba3fbb33d | date=20260721 | payment=0.0
+2026-07-21 10:45:31.854 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=754f70c88d5dc378 | act=3 | deviceId=f7a1ff7e-bfff-404d-ffee-76fba3fbb33d | date=20260721 | payment=0.0
+2026-07-21 10:45:32.581 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=724ms
+2026-07-21 10:45:32.617 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=10 processed, cost=1472ms, totalFetched=10000, matched=38, sent=0, failed=0, skipped=9962, alreadySent=38, mode=REALTIME
+2026-07-21 10:45:32.617 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=11, mode=REALTIME
+2026-07-21 10:45:32.909 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=11 returned 1000 records, responsePageSize=28, mode=REALTIME
+2026-07-21 10:45:33.737 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=430ms
+2026-07-21 10:45:33.738 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=11 processed, cost=1120ms, totalFetched=11000, matched=38, sent=0, failed=0, skipped=10962, alreadySent=38, mode=REALTIME
+2026-07-21 10:45:33.738 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=12, mode=REALTIME
+2026-07-21 10:45:33.997 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=12 returned 1000 records, responsePageSize=28, mode=REALTIME
+2026-07-21 10:45:34.928 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=526ms
+2026-07-21 10:45:34.928 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=12 processed, cost=1190ms, totalFetched=12000, matched=38, sent=0, failed=0, skipped=11962, alreadySent=38, mode=REALTIME
+2026-07-21 10:45:34.928 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=13, mode=REALTIME
+2026-07-21 10:45:35.243 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=13 returned 1000 records, responsePageSize=28, mode=REALTIME
+2026-07-21 10:45:35.609 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=54141b84e905ef79 | act=3 | deviceId=BCBDEB1EF76A4C588EDC4D42999F6DECbd042f9bab2b107c76de5240549922e1 | date=20260721 | payment=0.0
+2026-07-21 10:45:35.646 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=1e1aa5e180296f76 | act=3 | deviceId=c1054fb0b5d89cbdaef1bdb36327845ffd7dc5d5ee89c0a2d1a3fac4f71b15d0 | date=20260721 | payment=0.0
+2026-07-21 10:45:35.683 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=23da7f152ab4f843 | act=3 | deviceId=69C5D4FA003A4F5D912C68E35CD8F2D586038c184bfa911b43365f207cca922c | date=20260721 | payment=0.0
+2026-07-21 10:45:35.720 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=a5c82d0e1fb3f04b | act=3 | deviceId=7149b03377553805706ffa0196e8b538cd21f575966dc75401ba2fc8eb001b0c | date=20260721 | payment=0.0
+2026-07-21 10:45:36.341 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=620ms
+2026-07-21 10:45:36.378 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=13 processed, cost=1449ms, totalFetched=13000, matched=42, sent=0, failed=0, skipped=12958, alreadySent=42, mode=REALTIME
+2026-07-21 10:45:36.378 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=14, mode=REALTIME
+2026-07-21 10:45:36.667 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=14 returned 1000 records, responsePageSize=28, mode=REALTIME
+2026-07-21 10:45:37.025 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=2a120206b2ddbd68 | act=3 | deviceId=c0c79f10ad487ee7 | date=20260721 | payment=0.0
+2026-07-21 10:45:37.060 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=2810c3e2f8ab3c6e | act=3 | deviceId=4B58E1E38C424F0CA461C23FA47D584C21bb0817d70d9c99888aacf89c16957e | date=20260721 | payment=0.0
+2026-07-21 10:45:37.096 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=fd88c48912335734 | act=3 | deviceId=CF136BFC16504EFF8E8D1F143F907EDE09e2b42025bb450c5d9ae573bd1cd8a3 | date=20260721 | payment=0.0
+2026-07-21 10:45:37.591 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=495ms
+2026-07-21 10:45:37.627 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=14 processed, cost=1249ms, totalFetched=14000, matched=45, sent=0, failed=0, skipped=13955, alreadySent=45, mode=REALTIME
+2026-07-21 10:45:37.628 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=15, mode=REALTIME
+2026-07-21 10:45:37.916 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=15 returned 1000 records, responsePageSize=28, mode=REALTIME
+2026-07-21 10:45:38.268 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=4b83d9e2a363bb6c | act=3 | deviceId=627a6998-7d0d-4152-a95a-eaa84de4a5f1 | date=20260721 | payment=0.0
+2026-07-21 10:45:38.304 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=1f73e1ae93faaf3e | act=3 | deviceId=a1210ad58bdb1b9d | date=20260721 | payment=0.0
+2026-07-21 10:45:38.342 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=3f120a4a5c46e64f | act=3 | deviceId=289b40819ecd1532dcc265ddf7360e1959941582ea818250a8c99212b72ff81d | date=20260721 | payment=0.0
+2026-07-21 10:45:38.782 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=439ms
+2026-07-21 10:45:38.818 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=15 processed, cost=1190ms, totalFetched=15000, matched=48, sent=0, failed=0, skipped=14952, alreadySent=48, mode=REALTIME
+2026-07-21 10:45:38.818 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=16, mode=REALTIME
+2026-07-21 10:45:39.148 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=16 returned 1000 records, responsePageSize=28, mode=REALTIME
+2026-07-21 10:46:18.238 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=5306ms
+2026-07-21 10:46:19.397 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=16 processed, cost=40578ms, totalFetched=16000, matched=48, sent=0, failed=0, skipped=15952, alreadySent=48, mode=REALTIME
+2026-07-21 10:46:19.397 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=17, mode=REALTIME
+2026-07-21 10:46:19.718 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=17 returned 1000 records, responsePageSize=28, mode=REALTIME
+2026-07-21 10:46:21.016 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=42337b604824ab42 | act=3 | deviceId=F04EC33EFF6A492E8ACCFB1919F98A4Dfc7f6dacc9c47b02816c58fb90d6351a | date=20260721 | payment=0.0
+2026-07-21 10:46:21.053 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=36f3f89a2eb4bc17 | act=3 | deviceId=5C18EFC5CAF343549070478925AB52F222957606c86158f024d4b65ec5f86085 | date=20260721 | payment=0.0
+2026-07-21 10:46:21.089 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=95d548b635cf065d | act=3 | deviceId=1a02dbea98189d3308e890a04600f3b319bc1848196500f1ae1d64135aee9f13 | date=20260721 | payment=0.0
+2026-07-21 10:46:21.362 [Thread-3] INFO  c.adx.tencent.storage.TiDBColdStore - [TiDBColdStore] saveConversions done: size=1000, batches=1, batchSize=1000, cost=273ms
+2026-07-21 10:46:21.397 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=17 processed, cost=2000ms, totalFetched=17000, matched=51, sent=0, failed=0, skipped=16949, alreadySent=51, mode=REALTIME
+2026-07-21 10:46:21.397 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] fetching date=20260721, page=18, mode=REALTIME
+2026-07-21 10:46:21.698 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] date=20260721, page=18 returned 1000 records, responsePageSize=28, mode=REALTIME
+2026-07-21 10:46:38.219 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=c19e632be019ef15 | act=3 | deviceId=daed56dead51c6d81b8d1b360c0c5a4dcb7ed95a976d3d8bfc8f3587bfb6c18f | date=20260721 | payment=0.0
+2026-07-21 10:46:38.285 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=4ba5807a8affd16d | act=3 | deviceId=3ade9218-7957-475c-b758-948f5582592f | date=20260721 | payment=0.0
+2026-07-21 10:46:38.336 [Thread-3] INFO  c.a.t.c.ConversionSyncService - [ConversionSync] 匹配到腾讯转化 | qk=5c02a0d7dbe14f73 | act=3 | deviceId=fb31d3edf73b7ace | date=20260721 | payment=0.0
+2026-07-21 10:48:25.673 [Thread-4] INFO  c.a.t.AppConfiguration$BackgroundTasks - [CallbackRetry] executing...
+2026-07-21 10:48:25.673 [adx-tidb housekeeper] WARN  com.zaxxer.hikari.pool.HikariPool - adx-tidb - Thread starvation or clock leap detected (housekeeper delta=1m49s146ms).
+2026-07-21 10:48:25.673 [adx-tidb housekeeper] WARN  com.zaxxer.hikari.pool.HikariPool - adx-tidb - Thread starvation or clock leap detected (housekeeper delta=1m49s146ms).
+2026-07-21 10:48:25.683 [Thread-5] WARN  com.zaxxer.hikari.pool.PoolBase - adx-tidb - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@6049c27d (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
+2026-07-21 10:48:25.683 [Thread-2] WARN  com.zaxxer.hikari.pool.PoolBase - adx-tidb - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@4494a5ad (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
+2026-07-21 10:48:25.683 [Thread-4] WARN  com.zaxxer.hikari.pool.PoolBase - adx-tidb - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@2ae3235e (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
+2026-07-21 10:48:25.740 [Thread-1] ERROR c.a.t.honor.HonorBackgroundTasks - honor cold worker: read honor stream failed
+java.lang.RuntimeException: read honor stream failed
+	at com.adx.tencent.honor.HonorHotStore.read(HonorHotStore.java:179)
+	at com.adx.tencent.honor.HonorColdWorker.runOnce(HonorColdWorker.java:40)
+	at com.adx.tencent.honor.HonorBackgroundTasks.lambda$run$0(HonorBackgroundTasks.java:40)
+	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
+	at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)
+	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java)
+	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
+	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
+	at java.base/java.lang.Thread.run(Thread.java:842)
+Caused by: java.lang.IllegalStateException: LettuceConnectionFactory is STOPPING
+	at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.assertStarted(LettuceConnectionFactory.java:1394)
+	at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getConnection(LettuceConnectionFactory.java:991)
+	at org.springframework.data.redis.core.RedisConnectionUtils.fetchConnection(RedisConnectionUtils.java:195)
+	at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:144)
+	at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:105)
+	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:383)
+	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:363)
+	at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:97)
+	at org.springframework.data.redis.core.DefaultStreamOperations.read(DefaultStreamOperations.java:258)
+	at com.adx.tencent.honor.HonorHotStore.read(HonorHotStore.java:171)
+	... 8 common frames omitted

+ 4 - 0
pom.xml

@@ -61,6 +61,10 @@
             <version>3.0.3</version>
         </dependency>
         <dependency>
+            <groupId>com.zaxxer</groupId>
+            <artifactId>HikariCP</artifactId>
+        </dependency>
+        <dependency>
             <groupId>com.mysql</groupId>
             <artifactId>mysql-connector-j</artifactId>
             <scope>runtime</scope>

+ 118 - 9
src/main/java/com/adx/tencent/AppConfiguration.java

@@ -9,6 +9,14 @@ import com.adx.tencent.conversionsync.ConversionSyncService;
 import com.adx.tencent.conversionsync.ConversionBackfillJobService;
 import com.adx.tencent.conversionsync.RetryService;
 import com.adx.tencent.httpapi.MediaPlacement;
+import com.adx.tencent.honor.HonorClient;
+import com.adx.tencent.honor.HonorColdStore;
+import com.adx.tencent.honor.HonorColdWorker;
+import com.adx.tencent.honor.HonorHotStore;
+import com.adx.tencent.honor.HonorPlacement;
+import com.adx.tencent.honor.HonorRetryService;
+import com.adx.tencent.honor.HonorTagEventResolver;
+import com.adx.tencent.honor.HonorTagEventSyncService;
 import com.adx.tencent.leader.LeaderElection;
 import com.adx.tencent.tagsync.TagEventResolver;
 import com.adx.tencent.tagsync.TagEventSyncService;
@@ -18,6 +26,8 @@ import com.adx.tencent.storage.RedisLock;
 import com.adx.tencent.storage.TiDBColdStore;
 import com.adx.tencent.worker.ColdWorker;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
 import org.apache.ibatis.session.SqlSessionFactory;
 import org.mybatis.spring.SqlSessionFactoryBean;
 import org.slf4j.Logger;
@@ -132,13 +142,19 @@ public class AppConfiguration {
     }
 
     private DataSource createDataSource(String jdbcUrl, String username, String password) {
-        org.springframework.jdbc.datasource.DriverManagerDataSource ds =
-                new org.springframework.jdbc.datasource.DriverManagerDataSource();
-        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
-        ds.setUrl(jdbcUrl);
-        if (username != null && !username.isBlank()) ds.setUsername(username);
-        if (password != null && !password.isBlank()) ds.setPassword(password);
-        return ds;
+        HikariConfig config = new HikariConfig();
+        config.setDriverClassName("com.mysql.cj.jdbc.Driver");
+        config.setJdbcUrl(jdbcUrl);
+        if (username != null && !username.isBlank()) config.setUsername(username);
+        if (password != null && !password.isBlank()) config.setPassword(password);
+        config.setPoolName("adx-tidb");
+        config.setMinimumIdle(2);
+        config.setMaximumPoolSize(10);
+        config.setConnectionTimeout(Duration.ofSeconds(10).toMillis());
+        config.setValidationTimeout(Duration.ofSeconds(5).toMillis());
+        config.setIdleTimeout(Duration.ofMinutes(10).toMillis());
+        config.setMaxLifetime(Duration.ofMinutes(30).toMillis());
+        return new HikariDataSource(config);
     }
 
     // ─── Baidu 客户端 ────────────────────────────────────────────────────────
@@ -165,6 +181,78 @@ public class AppConfiguration {
                 objectMapper);
     }
 
+    @Bean
+    public HonorColdStore honorColdStore(ObjectMapper objectMapper) {
+        String url = props.getTidbUrl();
+        if (url == null || url.isBlank()) return null;
+        try {
+            DataSource ds = createDataSource(url, props.getTidbUsername(), props.getTidbPassword());
+            SqlSessionFactory sqlSessionFactory = createSqlSessionFactory(ds);
+            HonorColdStore store = new HonorColdStore(sqlSessionFactory, objectMapper);
+            store.migrate();
+            return store;
+        } catch (Exception e) {
+            log.warn("Failed to open Honor TiDB: {}", e.getMessage(), e);
+            return null;
+        }
+    }
+
+    @Bean
+    public HonorHotStore honorHotStore(StringRedisTemplate redis,
+                                       ObjectMapper objectMapper,
+                                       @Nullable HonorColdStore honorColdStore) {
+        if (honorColdStore == null) return null;
+        return new HonorHotStore(redis, objectMapper, honorColdStore,
+                "adx:honor:", props.getHonorRedisStream(), props.getBidTtl());
+    }
+
+    @Bean
+    public HonorPlacement honorPlacement() {
+        HonorPlacement p = new HonorPlacement();
+        p.setBaiduMediaId(props.getHonorBaiduMediaId());
+        p.setBaiduAppId(props.getHonorBaiduAppId());
+        p.setBaiduTagId(props.getHonorBaiduTagId());
+        p.setBidFloor(props.getHonorBaiduBidFloor());
+        p.setActionTypes(List.of(0, 1, 2));
+
+        Map<String, HonorPlacement.HonorAppPlacement> platforms = new HashMap<>();
+        if (props.getHonorBaiduAndroidAppId() != null && !props.getHonorBaiduAndroidAppId().isBlank()) {
+            HonorPlacement.HonorAppPlacement android = new HonorPlacement.HonorAppPlacement();
+            android.setAppId(props.getHonorBaiduAndroidAppId());
+            android.setTagIds(props.getHonorBaiduAndroidTagIds());
+            platforms.put("android", android);
+        }
+        if (props.getHonorBaiduIosAppId() != null && !props.getHonorBaiduIosAppId().isBlank()) {
+            HonorPlacement.HonorAppPlacement ios = new HonorPlacement.HonorAppPlacement();
+            ios.setAppId(props.getHonorBaiduIosAppId());
+            ios.setTagIds(props.getHonorBaiduIosTagIds());
+            platforms.put("ios", ios);
+        }
+        if (!platforms.isEmpty()) p.setPlatforms(platforms);
+        return p;
+    }
+
+    @Bean
+    public HonorClient honorClient(ObjectMapper objectMapper) {
+        return new HonorClient(props.getHonorConversionBaseUrl(), props.getHonorChannelId(), objectMapper);
+    }
+
+    @Bean
+    public HonorRetryService honorRetryService(@Nullable HonorColdStore honorColdStore, HonorClient honorClient) {
+        if (honorColdStore == null) return null;
+        return new HonorRetryService(honorColdStore, honorClient, props.getHonorCallbackRetryLimit());
+    }
+
+    @Bean
+    public HonorColdWorker honorColdWorker(@Nullable HonorHotStore honorHotStore,
+                                           @Nullable HonorColdStore honorColdStore,
+                                           ObjectMapper objectMapper) {
+        if (honorHotStore == null || honorColdStore == null) return null;
+        return new HonorColdWorker(honorHotStore, honorColdStore,
+                "honor-cold-writers", props.getWorkerConsumer() + "-honor", props.getWorkerBatch(),
+                null, props.getRedisStreamMaxLen(), objectMapper);
+    }
+
     // ─── Tencent 客户端 + Sync 服务 ──────────────────────────────────────────
 
     @Bean
@@ -193,13 +281,25 @@ public class AppConfiguration {
     }
 
     @Bean
+    public HonorTagEventResolver honorTagEventResolver(@Nullable HonorColdStore honorColdStore,
+                                                       StringRedisTemplate redisTemplate) {
+        if (honorColdStore == null) return null;
+        return new HonorTagEventResolver(redisTemplate, honorColdStore, props.getHonorTagEventSyncRedisPrefix());
+    }
+
+    @Bean
     public ConversionSyncService conversionSyncService(ConversionClient baiduClient,
                                                          RedisHotStore hotStore,
                                                          TencentClient tencentClient,
                                                          @Nullable TiDBColdStore coldStore,
-                                                         @Nullable TagEventResolver tagEventResolver) {
+                                                         @Nullable TagEventResolver tagEventResolver,
+                                                         @Nullable HonorTagEventResolver honorTagEventResolver,
+                                                         @Nullable HonorHotStore honorHotStore,
+                                                         @Nullable HonorColdStore honorColdStore,
+                                                         HonorClient honorClient) {
         if (coldStore == null) return null;
-        return new ConversionSyncService(baiduClient, hotStore, tencentClient, coldStore, tagEventResolver);
+        return new ConversionSyncService(baiduClient, hotStore, tencentClient, coldStore, tagEventResolver,
+                honorTagEventResolver, honorHotStore, honorColdStore, honorClient);
     }
 
     @Bean
@@ -233,6 +333,15 @@ public class AppConfiguration {
                 props.getTagEventSyncRedisPrefix(), ttl);
     }
 
+    @Bean
+    public HonorTagEventSyncService honorTagEventSyncService(@Nullable HonorColdStore honorColdStore,
+                                                             StringRedisTemplate redisTemplate) {
+        if (honorColdStore == null) return null;
+        Duration ttl = props.getHonorTagEventSyncInterval().multipliedBy(3);
+        return new HonorTagEventSyncService(honorColdStore, redisTemplate,
+                props.getHonorTagEventSyncRedisPrefix(), ttl);
+    }
+
     // ─── ColdWorker ─────────────────────────────────────────────────────────
 
     @Bean

+ 200 - 0
src/main/java/com/adx/tencent/config/AppProperties.java

@@ -38,6 +38,30 @@ public class AppProperties {
     private long baiduBidFloor = 0;
     private int baiduActionType = 1;
 
+    // Honor Placement / Callback
+    private long honorBaiduMediaId = 0;
+    private String honorBaiduAppId;
+    private String honorBaiduTagId;
+    private String honorBaiduAndroidAppId;
+    private List<String> honorBaiduAndroidTagIds = new ArrayList<>();
+    private String honorBaiduIosAppId;
+    private List<String> honorBaiduIosTagIds = new ArrayList<>();
+    private long honorBaiduBidFloor = 0;
+    private String honorConversionBaseUrl = "https://ads-drcn.platform.hihonorcloud.com";
+    private String honorRedisStream = "adx:honor:events";
+    private String honorChannelId = "9";
+    private boolean honorConversionSyncEnabled = false;
+    private Duration honorConversionSyncInterval = Duration.ofSeconds(300);
+    private int honorConversionSyncDateOffsetDays = 0;
+    private int honorConversionSyncPageSize = 1;
+    private List<Integer> honorConversionSyncActs = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 2001);
+    private boolean honorCallbackRetryEnabled = false;
+    private Duration honorCallbackRetryInterval = Duration.ofSeconds(300);
+    private int honorCallbackRetryLimit = 100;
+    private boolean honorTagEventSyncEnabled = false;
+    private Duration honorTagEventSyncInterval = Duration.ofMinutes(5);
+    private String honorTagEventSyncRedisPrefix = "";
+
     // Tencent API
     private String tencentApiBaseUrl = "https://api.e.qq.com/v3.0";
     private String tencentAccessToken;
@@ -237,6 +261,182 @@ public class AppProperties {
         this.baiduActionType = v;
     }
 
+    public long getHonorBaiduMediaId() {
+        return honorBaiduMediaId;
+    }
+
+    public void setHonorBaiduMediaId(long v) {
+        this.honorBaiduMediaId = v;
+    }
+
+    public String getHonorBaiduAppId() {
+        return honorBaiduAppId;
+    }
+
+    public void setHonorBaiduAppId(String v) {
+        this.honorBaiduAppId = v;
+    }
+
+    public String getHonorBaiduTagId() {
+        return honorBaiduTagId;
+    }
+
+    public void setHonorBaiduTagId(String v) {
+        this.honorBaiduTagId = v;
+    }
+
+    public String getHonorBaiduAndroidAppId() {
+        return honorBaiduAndroidAppId;
+    }
+
+    public void setHonorBaiduAndroidAppId(String v) {
+        this.honorBaiduAndroidAppId = v;
+    }
+
+    public List<String> getHonorBaiduAndroidTagIds() {
+        return honorBaiduAndroidTagIds;
+    }
+
+    public void setHonorBaiduAndroidTagIds(List<String> v) {
+        this.honorBaiduAndroidTagIds = v;
+    }
+
+    public String getHonorBaiduIosAppId() {
+        return honorBaiduIosAppId;
+    }
+
+    public void setHonorBaiduIosAppId(String v) {
+        this.honorBaiduIosAppId = v;
+    }
+
+    public List<String> getHonorBaiduIosTagIds() {
+        return honorBaiduIosTagIds;
+    }
+
+    public void setHonorBaiduIosTagIds(List<String> v) {
+        this.honorBaiduIosTagIds = v;
+    }
+
+    public long getHonorBaiduBidFloor() {
+        return honorBaiduBidFloor;
+    }
+
+    public void setHonorBaiduBidFloor(long v) {
+        this.honorBaiduBidFloor = v;
+    }
+
+    public String getHonorConversionBaseUrl() {
+        return honorConversionBaseUrl;
+    }
+
+    public void setHonorConversionBaseUrl(String v) {
+        this.honorConversionBaseUrl = v;
+    }
+
+    public String getHonorRedisStream() {
+        return honorRedisStream;
+    }
+
+    public void setHonorRedisStream(String v) {
+        this.honorRedisStream = v;
+    }
+
+    public String getHonorChannelId() {
+        return honorChannelId;
+    }
+
+    public void setHonorChannelId(String v) {
+        this.honorChannelId = v;
+    }
+
+    public boolean isHonorConversionSyncEnabled() {
+        return honorConversionSyncEnabled;
+    }
+
+    public void setHonorConversionSyncEnabled(boolean v) {
+        this.honorConversionSyncEnabled = v;
+    }
+
+    public Duration getHonorConversionSyncInterval() {
+        return honorConversionSyncInterval;
+    }
+
+    public void setHonorConversionSyncInterval(Duration v) {
+        this.honorConversionSyncInterval = v;
+    }
+
+    public int getHonorConversionSyncDateOffsetDays() {
+        return honorConversionSyncDateOffsetDays;
+    }
+
+    public void setHonorConversionSyncDateOffsetDays(int v) {
+        this.honorConversionSyncDateOffsetDays = v;
+    }
+
+    public int getHonorConversionSyncPageSize() {
+        return honorConversionSyncPageSize;
+    }
+
+    public void setHonorConversionSyncPageSize(int v) {
+        this.honorConversionSyncPageSize = v;
+    }
+
+    public List<Integer> getHonorConversionSyncActs() {
+        return honorConversionSyncActs;
+    }
+
+    public void setHonorConversionSyncActs(List<Integer> v) {
+        this.honorConversionSyncActs = v;
+    }
+
+    public boolean isHonorCallbackRetryEnabled() {
+        return honorCallbackRetryEnabled;
+    }
+
+    public void setHonorCallbackRetryEnabled(boolean v) {
+        this.honorCallbackRetryEnabled = v;
+    }
+
+    public Duration getHonorCallbackRetryInterval() {
+        return honorCallbackRetryInterval;
+    }
+
+    public void setHonorCallbackRetryInterval(Duration v) {
+        this.honorCallbackRetryInterval = v;
+    }
+
+    public int getHonorCallbackRetryLimit() {
+        return honorCallbackRetryLimit;
+    }
+
+    public void setHonorCallbackRetryLimit(int v) {
+        this.honorCallbackRetryLimit = v;
+    }
+
+    public boolean isHonorTagEventSyncEnabled() {
+        return honorTagEventSyncEnabled;
+    }
+
+    public void setHonorTagEventSyncEnabled(boolean v) {
+        this.honorTagEventSyncEnabled = v;
+    }
+
+    public Duration getHonorTagEventSyncInterval() {
+        return honorTagEventSyncInterval;
+    }
+
+    public void setHonorTagEventSyncInterval(Duration v) {
+        this.honorTagEventSyncInterval = v;
+    }
+
+    public String getHonorTagEventSyncRedisPrefix() {
+        return honorTagEventSyncRedisPrefix;
+    }
+
+    public void setHonorTagEventSyncRedisPrefix(String v) {
+        this.honorTagEventSyncRedisPrefix = v;
+    }
+
     public String getTencentApiBaseUrl() {
         return tencentApiBaseUrl;
     }

+ 526 - 129
src/main/java/com/adx/tencent/conversionsync/ConversionSyncService.java

@@ -4,11 +4,19 @@ import com.adx.tencent.baidu.ConversionClient;
 import com.adx.tencent.baidu.model.ConversionQuery;
 import com.adx.tencent.baidu.model.ConversionResponse;
 import com.adx.tencent.baidu.model.PaymentInfo;
-import com.adx.tencent.tencent.TencentClient;
+import com.adx.tencent.honor.HonorClient;
+import com.adx.tencent.honor.HonorColdStore;
+import com.adx.tencent.honor.HonorHotStore;
+import com.adx.tencent.honor.HonorTagEventResolver;
+import com.adx.tencent.honor.model.HonorBidRecord;
+import com.adx.tencent.honor.model.HonorMediaCallbackRecord;
 import com.adx.tencent.storage.RedisHotStore;
 import com.adx.tencent.storage.TiDBColdStore;
-import com.adx.tencent.storage.model.*;
+import com.adx.tencent.storage.model.BidRecord;
+import com.adx.tencent.storage.model.ConversionRecord;
+import com.adx.tencent.storage.model.MediaCallbackRecord;
 import com.adx.tencent.tagsync.TagEventResolver;
+import com.adx.tencent.tencent.TencentClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -19,12 +27,18 @@ import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
- * 转化同步核心逻辑。
- * 定时从百度拉取转化数据,匹配腾讯 bid,通过 callback URL 回传腾讯。
+ * 统一转化同步:
+ * 1. 只从百度拉一次转化
+ * 2. 按 qk 匹配腾讯/荣耀 bid
+ * 3. 百度转化明细统一落 tencent_baidu_conversions
+ * 4. 媒体回传结果分别落各自表
  */
 public class ConversionSyncService {
 
@@ -34,9 +48,13 @@ public class ConversionSyncService {
 
     private final ConversionClient baiduClient;
     private final RedisHotStore hotStore;
+    private final HonorHotStore honorHotStore;
     private final TencentClient tencentClient;
     private final TiDBColdStore coldStore;
+    private final HonorColdStore honorColdStore;
+    private final HonorClient honorClient;
     private final TagEventResolver tagEventResolver;
+    private final HonorTagEventResolver honorTagEventResolver;
     private final ExecutorService realtimeExecutor;
     private final ExecutorService backfillExecutor;
 
@@ -45,50 +63,75 @@ public class ConversionSyncService {
         BACKFILL
     }
 
-    public ConversionSyncService(ConversionClient baiduClient, RedisHotStore hotStore,
-                                  TencentClient tencentClient, TiDBColdStore coldStore,
-                                  TagEventResolver tagEventResolver) {
+    public ConversionSyncService(ConversionClient baiduClient,
+                                 RedisHotStore hotStore,
+                                 TencentClient tencentClient,
+                                 TiDBColdStore coldStore,
+                                 TagEventResolver tagEventResolver,
+                                 HonorTagEventResolver honorTagEventResolver,
+                                 HonorHotStore honorHotStore,
+                                 HonorColdStore honorColdStore,
+                                 HonorClient honorClient) {
         this.baiduClient = baiduClient;
         this.hotStore = hotStore;
         this.tencentClient = tencentClient;
         this.coldStore = coldStore;
         this.tagEventResolver = tagEventResolver;
-        this.realtimeExecutor = Executors.newFixedThreadPool(REALTIME_CONCURRENCY,
-                r -> { Thread t = new Thread(r, "conv-sync-worker"); t.setDaemon(true); return t; });
-        this.backfillExecutor = Executors.newFixedThreadPool(BACKFILL_CONCURRENCY,
-                r -> { Thread t = new Thread(r, "conv-backfill-worker"); t.setDaemon(true); return t; });
+        this.honorTagEventResolver = honorTagEventResolver;
+        this.honorHotStore = honorHotStore;
+        this.honorColdStore = honorColdStore;
+        this.honorClient = honorClient;
+        this.realtimeExecutor = Executors.newFixedThreadPool(
+                REALTIME_CONCURRENCY,
+                r -> {
+                    Thread t = new Thread(r, "conv-sync-worker");
+                    t.setDaemon(true);
+                    return t;
+                });
+        this.backfillExecutor = Executors.newFixedThreadPool(
+                BACKFILL_CONCURRENCY,
+                r -> {
+                    Thread t = new Thread(r, "conv-backfill-worker");
+                    t.setDaemon(true);
+                    return t;
+                });
     }
 
-    /**
-     * 分页拉取百度转化数据并回传腾讯。
-     */
     public SyncResult syncTencentConversions(ConversionQuery query) throws Exception {
         return syncTencentConversions(query, SyncMode.REALTIME);
     }
 
     public SyncResult syncTencentConversions(ConversionQuery query, SyncMode mode) throws Exception {
-        if (query.getPageSize() <= 0) query.setPageSize(1);
+        if (query.getPageSize() <= 0) {
+            query.setPageSize(1);
+        }
 
         SyncResult result = new SyncResult();
-        int maxPages = 100;
+        int maxPages = 10000;
         for (int page = query.getPageSize(); ; page++) {
             query.setPageSize(page);
             long pageStartNs = System.nanoTime();
             log.info("[ConversionSync] fetching date={}, page={}, mode={}", query.getDate(), page, mode);
+
             ConversionResponse response = baiduClient.queryPayments(query);
             List<PaymentInfo> data = response.getData();
             int dataSize = data != null ? data.size() : 0;
             log.info("[ConversionSync] date={}, page={} returned {} records, responsePageSize={}, mode={}",
                     query.getDate(), page, dataSize, response.getPageSize(), mode);
+
             if (data != null) {
                 processPayments(data, result, mode);
             }
+
             long pageCostMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - pageStartNs);
             log.info("[ConversionSync] date={}, page={} processed, cost={}ms, totalFetched={}, matched={}, sent={}, failed={}, skipped={}, alreadySent={}, mode={}",
                     query.getDate(), page, pageCostMs,
                     result.fetched.get(), result.matched.get(), result.sent.get(), result.failed.get(),
                     result.skipped.get(), result.alreadySent.get(), mode);
-            if (response.getPageSize() <= page) break;
+
+            if (response.getPageSize() <= page) {
+                break;
+            }
             if (page >= maxPages) {
                 log.warn("[ConversionSync] reached max page limit ({}), stopping", maxPages);
                 break;
@@ -97,12 +140,41 @@ public class ConversionSyncService {
         return result;
     }
 
-    private void processPayments(List<PaymentInfo> payments, SyncResult result, SyncMode mode) throws Exception {
+    private void processPayments(List<PaymentInfo> payments, SyncResult result, SyncMode mode) {
         if (payments == null || payments.isEmpty()) {
             return;
         }
 
         ExecutorService executor = mode == SyncMode.BACKFILL ? backfillExecutor : realtimeExecutor;
+
+        // 1. 批量查这一页转化对应的腾讯/荣耀 bid;Redis miss 时会自动回落到冷库。
+        BidLookup lookup = loadBidLookup(payments, result);
+
+        // 2. 统一整理要落库的百度转化明细,以及要回传给媒体的任务。
+        List<ConversionRecord> conversionRecords = new ArrayList<>();
+        List<CallbackTask> callbackTasks = new ArrayList<>();
+        buildRecordsAndTasks(payments, lookup, conversionRecords, callbackTasks, result);
+
+        // 3. 百度转化明细统一落 tencent_baidu_conversions。
+        saveConversions(conversionRecords);
+        if (callbackTasks.isEmpty()) {
+            return;
+        }
+
+        // 4. 回传前先做去重,只发送未成功回传过的任务。
+        List<CallbackTask> pendingTasks = filterPendingCallbacks(callbackTasks, result);
+        if (pendingTasks.isEmpty()) {
+            return;
+        }
+
+        // 5. 并发回传媒体,并把回传结果分别落到腾讯/荣耀回调表。
+        List<CallbackDispatchResult> dispatchResults = dispatchCallbacks(pendingTasks, executor, result);
+        saveCallbackResults(dispatchResults);
+    }
+
+    // ─── Step 1: 加载 bid ───────────────────────────────────────────────────
+
+    private BidLookup loadBidLookup(List<PaymentInfo> payments, SyncResult result) {
         Set<String> qks = new LinkedHashSet<>();
         for (PaymentInfo payment : payments) {
             result.fetched.incrementAndGet();
@@ -111,191 +183,516 @@ public class ConversionSyncService {
             }
         }
 
-        Map<String, BidRecord> bidMap = hotStore.findBidsByQks(new ArrayList<>(qks));
-        List<ConversionRecord> conversionRecords = new ArrayList<>();
-        List<CallbackWork> callbackWorks = new ArrayList<>();
+        List<String> qkList = new ArrayList<>(qks);
+        Map<String, BidRecord> tencentBids = hotStore.findBidsByQks(qkList);
+        Map<String, HonorBidRecord> honorBids = honorHotStore != null
+                ? honorHotStore.findBidsByQks(qkList)
+                : Map.of();
+        return new BidLookup(tencentBids, honorBids);
+    }
 
+    // ─── Step 2: 组装转化记录与回传任务 ───────────────────────────────────────
+
+    private void buildRecordsAndTasks(List<PaymentInfo> payments,
+                                      BidLookup lookup,
+                                      List<ConversionRecord> conversionRecords,
+                                      List<CallbackTask> callbackTasks,
+                                      SyncResult result) {
         for (PaymentInfo payment : payments) {
-            BidRecord bid = bidMap.get(payment.getQk());
-            if (bid == null) {
-                conversionRecords.add(conversionRecord(payment, null, ""));
+            BidRecord tencentBid = lookup.tencentBids.get(payment.getQk());
+            HonorBidRecord honorBid = lookup.honorBids.get(payment.getQk());
+
+            if (tencentBid == null && honorBid == null) {
+                conversionRecords.add(toConversionRecord(payment, null, ""));
                 result.skipped.incrementAndGet();
                 continue;
             }
 
-            if (!"tencent".equals(bid.getMedia())) {
-                conversionRecords.add(conversionRecord(payment, bid, bid.getMedia()));
+            boolean hasTencentBid = tencentBid != null;
+            boolean hasHonorBid = honorBid != null;
+            boolean validTencent = hasTencentBid && "tencent".equals(tencentBid.getMedia());
+            boolean validHonor = hasHonorBid && "honor".equals(honorBid.getMedia());
+
+            if (hasTencentBid && !validTencent) {
+                log.warn("[ConversionSync] 腾讯 bid media 非法 | qk={} | media={}", payment.getQk(), tencentBid.getMedia());
+            }
+
+            if (hasHonorBid && !validHonor) {
+                log.warn("[ConversionSync] 荣耀 bid media 非法 | qk={} | media={}", payment.getQk(), honorBid.getMedia());
+            }
+
+            if (validTencent && validHonor) {
+                log.warn("[ConversionSync] qk 同时命中腾讯和荣耀,跳过分发 | qk={} | act={} | date={}",
+                        payment.getQk(), payment.getAct(), payment.getDate());
                 result.skipped.incrementAndGet();
                 continue;
             }
 
+            if (validTencent) {
+                conversionRecords.add(toConversionRecord(payment, tencentBid, "tencent"));
+                callbackTasks.add(buildTencentCallbackTask(payment, tencentBid));
+                result.matched.incrementAndGet();
+                log.info("[ConversionSync] 匹配到腾讯转化 | qk={} | act={} | deviceId={} | date={} | payment={}",
+                        payment.getQk(), payment.getAct(), payment.getDeviceId(), payment.getDate(), payment.getPayment());
+                continue;
+            }
+
+            if (!validHonor) {
+                conversionRecords.add(toConversionRecord(payment, hasTencentBid ? tencentBid : null, hasTencentBid ? tencentBid.getMedia() : ""));
+                result.skipped.incrementAndGet();
+                continue;
+            }
+
+            conversionRecords.add(toHonorConversionRecord(payment, honorBid));
             result.matched.incrementAndGet();
-            log.info("[ConversionSync] 匹配到腾讯转化 | qk={} | act={} | deviceId={} | date={} | payment={}",
+            log.info("[ConversionSync] 匹配到荣耀转化 | qk={} | act={} | deviceId={} | date={} | payment={}",
                     payment.getQk(), payment.getAct(), payment.getDeviceId(), payment.getDate(), payment.getPayment());
-            conversionRecords.add(conversionRecord(payment, bid, bid.getMedia()));
 
-            String actionType = null;
-            if (tagEventResolver != null) {
-                actionType = tagEventResolver.resolveActionType(bid.getTagId(), payment.getAct());
-            }
-            if (actionType == null || actionType.isBlank()) {
-                actionType = tencentClient.conversionActionType(payment.getAct());
-                log.warn("[ConversionSync] 广告位回传方式未配置, 回退act-map | qk={} | tagId={} | act={} | actionType={}",
-                        payment.getQk(), bid.getTagId(), payment.getAct(), actionType);
+            CallbackTask honorTask = buildHonorCallbackTask(payment, honorBid);
+            if (honorTask != null) {
+                callbackTasks.add(honorTask);
             }
+        }
+    }
 
-            callbackWorks.add(new CallbackWork(payment, bid, actionType, callbackDedupeKey(payment, bid, actionType)));
+    private CallbackTask buildTencentCallbackTask(PaymentInfo payment, BidRecord bid) {
+        String actionType = null;
+        if (tagEventResolver != null) {
+            actionType = tagEventResolver.resolveActionType(bid.getTagId(), payment.getAct());
+        }
+        if (actionType == null || actionType.isBlank()) {
+            actionType = tencentClient.conversionActionType(payment.getAct());
+            log.warn("[ConversionSync] 广告位回传方式未配置, 回退act-map | qk={} | tagId={} | act={} | actionType={}",
+                    payment.getQk(), bid.getTagId(), payment.getAct(), actionType);
         }
 
-        if (coldStore != null && !conversionRecords.isEmpty()) {
-            try {
-                coldStore.saveConversions(conversionRecords);
-            } catch (Exception e) {
-                log.error("[ConversionSync] batch save conversions failed: size={}, {}", conversionRecords.size(), e.getMessage(), e);
-            }
+        return CallbackTask.tencent(
+                payment,
+                bid,
+                callbackDedupeKey(payment, bid, actionType),
+                actionType
+        );
+    }
+
+    private CallbackTask buildHonorCallbackTask(PaymentInfo payment, HonorBidRecord bid) {
+        if (honorColdStore == null && honorTagEventResolver == null) {
+            return null;
+        }
+
+        Integer conversionId = honorTagEventResolver != null
+                ? honorTagEventResolver.resolveConversionId(bid.getTagId(), payment.getAct())
+                : resolveHonorConversionIdFromDb(bid.getTagId(), payment.getAct());
+        if (conversionId == null) {
+            log.warn("[ConversionSync] 荣耀 conversionId 未配置 | qk={} | tagId={} | act={}",
+                    payment.getQk(), bid.getTagId(), payment.getAct());
+            return null;
         }
 
-        if (callbackWorks.isEmpty()) {
+        return CallbackTask.honor(
+                payment,
+                bid,
+                honorCallbackDedupeKey(payment, bid, conversionId),
+                conversionId
+        );
+    }
+
+    private Integer resolveHonorConversionIdFromDb(String tagId, int baiduAct) {
+        if (honorColdStore == null) {
+            return null;
+        }
+        com.adx.tencent.honor.model.HonorTagEventRecord exact = honorColdStore.getTagEvent(tagId, baiduAct);
+        if (exact != null) {
+            return exact.getConversionId();
+        }
+        com.adx.tencent.honor.model.HonorTagEventRecord fallback = honorColdStore.getTagEvent(tagId, 0);
+        return fallback != null ? fallback.getConversionId() : null;
+    }
+
+    // ─── Step 3: 统一落百度转化明细 ───────────────────────────────────────────
+
+    private void saveConversions(List<ConversionRecord> records) {
+        if (coldStore == null || records.isEmpty()) {
             return;
         }
+        try {
+            coldStore.saveConversions(records);
+        } catch (Exception e) {
+            log.error("[ConversionSync] batch save conversions failed: size={}, {}", records.size(), e.getMessage(), e);
+        }
+    }
 
+    // ─── Step 4: 回传前去重 ─────────────────────────────────────────────────
+
+    private List<CallbackTask> filterPendingCallbacks(List<CallbackTask> callbackTasks, SyncResult result) {
         Set<String> existing = new HashSet<>();
-        if (coldStore != null) {
-            try {
-                List<String> callbackKeys = new ArrayList<>(callbackWorks.size());
-                List<String> legacyKeys = new ArrayList<>(callbackWorks.size());
-                List<Integer> eventTypes = new ArrayList<>(callbackWorks.size());
-                for (CallbackWork work : callbackWorks) {
-                    callbackKeys.add(work.callbackKey);
-                    legacyKeys.add(legacyCallbackDedupeKey(work.payment, work.bid.getMedia()));
-                    eventTypes.add(work.payment.getAct());
-                }
-                existing.addAll(coldStore.successfulCallbackKeysWithLegacy(callbackKeys, legacyKeys, eventTypes));
-            } catch (Exception e) {
-                log.error("[ConversionSync] batch dedup check error: size={}, {}", callbackWorks.size(), e.getMessage(), e);
-            }
-        }
+        existing.addAll(collectTencentSuccessfulCallbackKeys(callbackTasks));
+        existing.addAll(collectHonorSuccessfulCallbackKeys(callbackTasks));
 
-        List<CallbackWork> toSend = new ArrayList<>(callbackWorks.size());
-        for (CallbackWork work : callbackWorks) {
-            if (existing.contains(work.callbackKey)) {
+        List<CallbackTask> pending = new ArrayList<>(callbackTasks.size());
+        for (CallbackTask task : callbackTasks) {
+            if (existing.contains(task.callbackKey)) {
                 result.alreadySent.incrementAndGet();
             } else {
-                toSend.add(work);
+                pending.add(task);
             }
         }
+        return pending;
+    }
 
-        List<CompletableFuture<MediaCallbackRecord>> futures = new ArrayList<>(toSend.size());
-        for (CallbackWork work : toSend) {
-            futures.add(CompletableFuture.supplyAsync(() -> sendConversionEvent(work), executor));
+    private Set<String> collectTencentSuccessfulCallbackKeys(List<CallbackTask> callbackTasks) {
+        if (coldStore == null) {
+            return Set.of();
         }
 
-        List<MediaCallbackRecord> callbackRecords = new ArrayList<>(toSend.size());
-        for (CompletableFuture<MediaCallbackRecord> future : futures) {
-            MediaCallbackRecord callbackRecord = future.join();
-            if (callbackRecord == null) {
+        List<String> callbackKeys = new ArrayList<>();
+        List<String> legacyKeys = new ArrayList<>();
+        List<Integer> eventTypes = new ArrayList<>();
+        for (CallbackTask task : callbackTasks) {
+            if (!task.isTencent()) {
                 continue;
             }
-            if (callbackRecord.getStatus() == 0 && callbackRecord.getErrorMessage() != null) {
+            callbackKeys.add(task.callbackKey);
+            legacyKeys.add(legacyCallbackDedupeKey(task.payment, "tencent"));
+            eventTypes.add(task.payment.getAct());
+        }
+
+        if (callbackKeys.isEmpty()) {
+            return Set.of();
+        }
+
+        try {
+            return new HashSet<>(coldStore.successfulCallbackKeysWithLegacy(callbackKeys, legacyKeys, eventTypes));
+        } catch (Exception e) {
+            log.error("[ConversionSync] batch dedup check error: size={}, {}", callbackKeys.size(), e.getMessage(), e);
+            return Set.of();
+        }
+    }
+
+    private Set<String> collectHonorSuccessfulCallbackKeys(List<CallbackTask> callbackTasks) {
+        if (honorColdStore == null) {
+            return Set.of();
+        }
+
+        Set<String> keys = new HashSet<>();
+        for (CallbackTask task : callbackTasks) {
+            if (task.isHonor()) {
+                keys.add(task.callbackKey);
+            }
+        }
+
+        if (keys.isEmpty()) {
+            return Set.of();
+        }
+
+        try {
+            return honorColdStore.successfulCallbackKeys(keys);
+        } catch (Exception e) {
+            log.error("[ConversionSync] honor dedup check error: size={}, {}", keys.size(), e.getMessage(), e);
+            return Set.of();
+        }
+    }
+
+    // ─── Step 5: 并发回传媒体 ───────────────────────────────────────────────
+
+    private List<CallbackDispatchResult> dispatchCallbacks(List<CallbackTask> tasks,
+                                                           ExecutorService executor,
+                                                           SyncResult result) {
+        List<CompletableFuture<CallbackDispatchResult>> futures = new ArrayList<>(tasks.size());
+        for (CallbackTask task : tasks) {
+            futures.add(CompletableFuture.supplyAsync(() -> sendCallback(task), executor));
+        }
+
+        List<CallbackDispatchResult> results = new ArrayList<>(tasks.size());
+        for (CompletableFuture<CallbackDispatchResult> future : futures) {
+            CallbackDispatchResult dispatchResult = future.join();
+            if (dispatchResult == null || dispatchResult.record == null) {
+                continue;
+            }
+            if (dispatchResult.record.isOk()) {
+                result.sent.incrementAndGet();
+            } else {
                 result.failed.incrementAndGet();
+            }
+            results.add(dispatchResult);
+        }
+        return results;
+    }
+
+    private CallbackDispatchResult sendCallback(CallbackTask task) {
+        if (task.isTencent()) {
+            String platform = resolvePlatform(task.tencentBid);
+            MediaCallbackRecord callbackRecord = tencentClient.sendConversionEvent(
+                    task.tencentBid, task.payment, platform, task.tencentActionType);
+            callbackRecord.setDedupeKey(task.callbackKey);
+            return CallbackDispatchResult.tencent(task, callbackRecord);
+        }
+
+        if (task.isHonor()) {
+            HonorMediaCallbackRecord honorRecord = honorClient.sendConversion(
+                    task.honorBid, task.payment, task.honorConversionId);
+            return CallbackDispatchResult.honor(task, toCallbackRecord(task, honorRecord));
+        }
+
+        return null;
+    }
+
+    // ─── Step 6: 落媒体回传结果 ─────────────────────────────────────────────
+
+    private void saveCallbackResults(List<CallbackDispatchResult> results) {
+        if (results.isEmpty()) {
+            return;
+        }
+
+        List<MediaCallbackRecord> tencentRecords = new ArrayList<>();
+        List<HonorMediaCallbackRecord> honorRecords = new ArrayList<>();
+        for (CallbackDispatchResult result : results) {
+            if (result.isHonor()) {
+                honorRecords.add(toHonorCallbackRecord(result.task, result.record));
             } else {
-                result.sent.incrementAndGet();
+                tencentRecords.add(result.record);
             }
-            callbackRecords.add(callbackRecord);
         }
 
-        if (coldStore != null && !callbackRecords.isEmpty()) {
+        if (coldStore != null && !tencentRecords.isEmpty()) {
             try {
-                coldStore.saveMediaCallbacks(callbackRecords);
+                coldStore.saveMediaCallbacks(tencentRecords);
             } catch (Exception e) {
-                log.error("[ConversionSync] batch save callbacks failed: size={}, {}", callbackRecords.size(), e.getMessage(), e);
+                log.error("[ConversionSync] batch save callbacks failed: size={}, {}", tencentRecords.size(), e.getMessage(), e);
             }
         }
-    }
 
-    private MediaCallbackRecord sendConversionEvent(CallbackWork work) {
-        String platform = resolvePlatform(work.bid);
-        MediaCallbackRecord callbackRecord = tencentClient.sendConversionEvent(
-                work.bid, work.payment, platform, work.actionType);
-        callbackRecord.setDedupeKey(work.callbackKey);
-        return callbackRecord;
+        if (honorColdStore != null && !honorRecords.isEmpty()) {
+            try {
+                honorColdStore.saveMediaCallbacks(honorRecords);
+            } catch (Exception e) {
+                log.error("[ConversionSync] batch save honor callbacks failed: size={}, {}", honorRecords.size(), e.getMessage(), e);
+            }
+        }
     }
 
-    // ─── 辅助方法 ─────────────────────────────────────────────────────────────
+    // ─── 工具方法 ────────────────────────────────────────────────────────────
 
-    /**
-     * 从 BidRecord 中提取平台信息。
-     * 优先读取 platform 字段,其次从 mediaParams 中提取,最后根据设备标识推断。
-     */
     private static String resolvePlatform(BidRecord bid) {
-        // 优先使用 BidRecord 的 platform 字段
         if (bid.getPlatform() != null && !bid.getPlatform().isBlank()) {
             return bid.getPlatform().toLowerCase();
         }
-        if (bid.getMediaParams() == null) return "android";
+        if (bid.getMediaParams() == null) {
+            return "android";
+        }
         String platform = bid.getMediaParams().get("baidu_platform");
-        if (platform != null && !platform.isBlank()) return platform.toLowerCase();
-        // 有 idfa 则为 ios
+        if (platform != null && !platform.isBlank()) {
+            return platform.toLowerCase();
+        }
         String idfa = bid.getMediaParams().get("idfa");
-        if (idfa != null && !idfa.isBlank()) return "ios";
+        if (idfa != null && !idfa.isBlank()) {
+            return "ios";
+        }
         return "android";
     }
 
-    private static ConversionRecord conversionRecord(PaymentInfo p, BidRecord bid, String media) {
-        ConversionRecord r = new ConversionRecord();
-        r.setDedupeKey(conversionDedupeKey(p));
-        r.setQk(p.getQk());
-        r.setMedia(media);
-        r.setTagId(bid != null ? bid.getTagId() : null);
-        r.setDate(p.getDate());
-        r.setAppSid(p.getAppSid());
-        r.setCustomerName(p.getCustomerName());
-        r.setDeviceId(p.getDeviceId());
-        r.setConv(p.getConv());
-        r.setPayment(p.getPayment());
-        r.setGmv(p.getGmv());
-        r.setAct(p.getAct());
-        r.setTu(p.getTu());
-        r.setClkTime(p.getClkTime());
-        r.setCreatedAt(Instant.now());
-        return r;
+    private static ConversionRecord toConversionRecord(PaymentInfo payment, BidRecord bid, String media) {
+        ConversionRecord record = new ConversionRecord();
+        record.setDedupeKey(conversionDedupeKey(payment));
+        record.setQk(payment.getQk());
+        record.setMedia(media);
+        record.setTagId(bid != null ? bid.getTagId() : null);
+        record.setDate(payment.getDate());
+        record.setAppSid(payment.getAppSid());
+        record.setCustomerName(payment.getCustomerName());
+        record.setDeviceId(payment.getDeviceId());
+        record.setConv(payment.getConv());
+        record.setPayment(payment.getPayment());
+        record.setGmv(payment.getGmv());
+        record.setAct(payment.getAct());
+        record.setTu(payment.getTu());
+        record.setClkTime(payment.getClkTime());
+        record.setCreatedAt(Instant.now());
+        return record;
+    }
+
+    private static ConversionRecord toHonorConversionRecord(PaymentInfo payment, HonorBidRecord bid) {
+        ConversionRecord record = new ConversionRecord();
+        record.setDedupeKey(conversionDedupeKey(payment));
+        record.setQk(payment.getQk());
+        record.setMedia("honor");
+        record.setTagId(bid != null ? bid.getTagId() : null);
+        record.setDate(payment.getDate());
+        record.setAppSid(payment.getAppSid());
+        record.setCustomerName(payment.getCustomerName());
+        record.setDeviceId(payment.getDeviceId());
+        record.setConv(payment.getConv());
+        record.setPayment(payment.getPayment());
+        record.setGmv(payment.getGmv());
+        record.setAct(payment.getAct());
+        record.setTu(payment.getTu());
+        record.setClkTime(payment.getClkTime());
+        record.setCreatedAt(Instant.now());
+        return record;
     }
 
-    static String conversionDedupeKey(PaymentInfo p) {
-        return String.format("%s:%s:%s:%d", p.getQk(), p.getDate(), p.getDeviceId(), p.getAct());
+    static String conversionDedupeKey(PaymentInfo payment) {
+        return String.format("%s:%s:%s:%d",
+                payment.getQk(),
+                payment.getDate(),
+                payment.getDeviceId(),
+                payment.getAct());
     }
 
-    static String callbackDedupeKey(PaymentInfo p, BidRecord bid, String actionType) {
+    static String callbackDedupeKey(PaymentInfo payment, BidRecord bid, String actionType) {
         return String.format("%s:%s:%s:%s:%s:%d:%s",
                 bid.getMedia(),
-                p.getQk(),
-                p.getDate(),
-                p.getDeviceId(),
+                payment.getQk(),
+                payment.getDate(),
+                payment.getDeviceId(),
                 bid.getTagId(),
-                p.getAct(),
+                payment.getAct(),
                 actionType);
     }
 
-    static String legacyCallbackDedupeKey(PaymentInfo p, String media) {
+    static String legacyCallbackDedupeKey(PaymentInfo payment, String media) {
         return String.format("%s:%s:%s:%s:%d:%d",
-                media, p.getQk(), p.getDate(), p.getDeviceId(), p.getAct(), p.getAct());
+                media,
+                payment.getQk(),
+                payment.getDate(),
+                payment.getDeviceId(),
+                payment.getAct(),
+                payment.getAct());
+    }
+
+    static String honorCallbackDedupeKey(PaymentInfo payment, HonorBidRecord bid, int conversionId) {
+        return String.format("honor:%s:%s:%s:%s:%d:%d",
+                payment.getQk(),
+                payment.getDate(),
+                payment.getDeviceId(),
+                bid.getTagId(),
+                payment.getAct(),
+                conversionId);
+    }
+
+    private static HonorMediaCallbackRecord toHonorCallbackRecord(CallbackTask task, MediaCallbackRecord record) {
+        HonorMediaCallbackRecord callback = new HonorMediaCallbackRecord();
+        callback.setDedupeKey(record.getDedupeKey());
+        callback.setQk(record.getQk());
+        callback.setTagId(task.tagId);
+        callback.setCallbackUrl(record.getCallbackUrl());
+        callback.setEventType(record.getEventType());
+        callback.setEventTimeMs(record.getEventTimeMs());
+        callback.setPurchase(record.getPurchase());
+        callback.setStatus(record.getStatus());
+        callback.setOk(record.isOk());
+        callback.setAttempt(record.getAttempt());
+        callback.setResponseBody(record.getResponseBody());
+        callback.setErrorMessage(record.getErrorMessage());
+        callback.setRequestBody(record.getRequestBody());
+        callback.setCreatedAt(record.getCreatedAt());
+        return callback;
+    }
+
+    private static MediaCallbackRecord toCallbackRecord(CallbackTask task, HonorMediaCallbackRecord honorRecord) {
+        MediaCallbackRecord callbackRecord = new MediaCallbackRecord();
+        callbackRecord.setDedupeKey(task.callbackKey);
+        callbackRecord.setMedia("honor");
+        callbackRecord.setQk(honorRecord.getQk());
+        callbackRecord.setCallbackUrl(honorRecord.getCallbackUrl());
+        callbackRecord.setEventType(honorRecord.getEventType());
+        callbackRecord.setEventTimeMs(honorRecord.getEventTimeMs());
+        callbackRecord.setPurchase(honorRecord.getPurchase());
+        callbackRecord.setStatus(honorRecord.getStatus());
+        callbackRecord.setOk(honorRecord.isOk());
+        callbackRecord.setAttempt(honorRecord.getAttempt());
+        callbackRecord.setResponseBody(honorRecord.getResponseBody());
+        callbackRecord.setErrorMessage(honorRecord.getErrorMessage());
+        callbackRecord.setRequestBody(honorRecord.getRequestBody());
+        callbackRecord.setCreatedAt(honorRecord.getCreatedAt());
+        return callbackRecord;
+    }
+
+    // ─── 内部数据结构 ────────────────────────────────────────────────────────
+
+    private static class BidLookup {
+        private final Map<String, BidRecord> tencentBids;
+        private final Map<String, HonorBidRecord> honorBids;
+
+        private BidLookup(Map<String, BidRecord> tencentBids, Map<String, HonorBidRecord> honorBids) {
+            this.tencentBids = tencentBids;
+            this.honorBids = honorBids;
+        }
     }
 
-    private static class CallbackWork {
+    private static class CallbackTask {
+        private final String media;
         private final PaymentInfo payment;
-        private final BidRecord bid;
-        private final String actionType;
+        private final String tagId;
         private final String callbackKey;
-
-        private CallbackWork(PaymentInfo payment, BidRecord bid, String actionType, String callbackKey) {
+        private final BidRecord tencentBid;
+        private final String tencentActionType;
+        private final HonorBidRecord honorBid;
+        private final Integer honorConversionId;
+
+        private CallbackTask(String media,
+                             PaymentInfo payment,
+                             String tagId,
+                             String callbackKey,
+                             BidRecord tencentBid,
+                             String tencentActionType,
+                             HonorBidRecord honorBid,
+                             Integer honorConversionId) {
+            this.media = media;
             this.payment = payment;
-            this.bid = bid;
-            this.actionType = actionType;
+            this.tagId = tagId;
             this.callbackKey = callbackKey;
+            this.tencentBid = tencentBid;
+            this.tencentActionType = tencentActionType;
+            this.honorBid = honorBid;
+            this.honorConversionId = honorConversionId;
+        }
+
+        private static CallbackTask tencent(PaymentInfo payment,
+                                            BidRecord bid,
+                                            String callbackKey,
+                                            String actionType) {
+            return new CallbackTask("tencent", payment, bid.getTagId(), callbackKey, bid, actionType, null, null);
+        }
+
+        private static CallbackTask honor(PaymentInfo payment,
+                                          HonorBidRecord bid,
+                                          String callbackKey,
+                                          Integer conversionId) {
+            return new CallbackTask("honor", payment, bid.getTagId(), callbackKey, null, null, bid, conversionId);
+        }
+
+        private boolean isTencent() {
+            return "tencent".equals(media);
+        }
+
+        private boolean isHonor() {
+            return "honor".equals(media);
         }
     }
 
-    // ─── Result DTO ─────────────────────────────────────────────────────────
+    private static class CallbackDispatchResult {
+        private final String media;
+        private final CallbackTask task;
+        private final MediaCallbackRecord record;
+
+        private CallbackDispatchResult(String media, CallbackTask task, MediaCallbackRecord record) {
+            this.media = media;
+            this.task = task;
+            this.record = record;
+        }
+
+        private static CallbackDispatchResult tencent(CallbackTask task, MediaCallbackRecord record) {
+            return new CallbackDispatchResult("tencent", task, record);
+        }
+
+        private static CallbackDispatchResult honor(CallbackTask task, MediaCallbackRecord record) {
+            return new CallbackDispatchResult("honor", task, record);
+        }
+
+        private boolean isHonor() {
+            return "honor".equals(media);
+        }
+    }
 
     public static class SyncResult {
         public final AtomicInteger fetched = new AtomicInteger();

+ 119 - 0
src/main/java/com/adx/tencent/honor/HonorBackgroundTasks.java

@@ -0,0 +1,119 @@
+package com.adx.tencent.honor;
+
+import com.adx.tencent.config.AppProperties;
+import com.adx.tencent.leader.LeaderElection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.lang.Nullable;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+@Component
+public class HonorBackgroundTasks implements ApplicationRunner {
+
+    private static final Logger log = LoggerFactory.getLogger(HonorBackgroundTasks.class);
+
+    @Autowired private AppProperties props;
+    @Autowired(required = false) private HonorColdWorker honorColdWorker;
+    @Autowired(required = false) private HonorRetryService honorRetryService;
+    @Autowired(required = false) private HonorTagEventSyncService honorTagEventSyncService;
+    @Autowired(required = false) @Nullable private LeaderElection leaderElection;
+
+    private final AtomicBoolean stopped = new AtomicBoolean(false);
+    private final ExecutorService executor = Executors.newCachedThreadPool(r -> {
+        Thread t = new Thread(r);
+        t.setDaemon(true);
+        return t;
+    });
+
+    @Override
+    public void run(ApplicationArguments args) {
+        if (honorColdWorker != null) {
+            executor.submit(() -> {
+                while (!stopped.get()) {
+                    try {
+                        honorColdWorker.runOnce();
+                    } catch (Exception e) {
+                        log.error("honor cold worker: {}", e.getMessage(), e);
+                        sleep(1000);
+                    }
+                }
+            });
+        }
+
+        if (honorRetryService != null && props.isHonorCallbackRetryEnabled()) {
+            if (props.isSkipLeaderElection() || leaderElection == null) {
+                executor.submit(() -> runRetry(() -> stopped.get()));
+            } else {
+                executor.submit(() -> leaderElection.run(
+                        "adx:lock:honor:callback-retry",
+                        props.getTaskLockTtl(), props.getTaskLockRenewInterval(), props.getTaskLockRetryInterval(),
+                        stopped::get,
+                        this::runRetry,
+                        e -> log.error("honor callback retry leader: {}", e.getMessage(), e)
+                ));
+            }
+        }
+
+        if (honorTagEventSyncService != null && props.isHonorTagEventSyncEnabled()) {
+            if (props.isSkipLeaderElection() || leaderElection == null) {
+                executor.submit(() -> runTagEventSync(() -> stopped.get()));
+            } else {
+                executor.submit(() -> leaderElection.run(
+                        "adx:lock:honor:tag-event-sync",
+                        props.getTaskLockTtl(), props.getTaskLockRenewInterval(), props.getTaskLockRetryInterval(),
+                        stopped::get,
+                        this::runTagEventSync,
+                        e -> log.error("honor tag event sync leader: {}", e.getMessage(), e)
+                ));
+            }
+        }
+    }
+
+    private void runRetry(LeaderElection.StopSignal stopSignal) {
+        long intervalMs = props.getHonorCallbackRetryInterval().toMillis();
+        while (!stopSignal.isStopped()) {
+            try {
+                honorRetryService.retryCallbacks(props.getHonorCallbackRetryLimit());
+            } catch (Exception e) {
+                log.error("honor callback retry: {}", e.getMessage(), e);
+            }
+            sleepResponsive(intervalMs, stopSignal);
+        }
+    }
+
+    private void runTagEventSync(LeaderElection.StopSignal stopSignal) {
+        long intervalMs = props.getHonorTagEventSyncInterval().toMillis();
+        while (!stopSignal.isStopped()) {
+            try {
+                honorTagEventSyncService.syncOnce();
+            } catch (Exception e) {
+                log.error("honor tag event sync: {}", e.getMessage(), e);
+            }
+            sleepResponsive(intervalMs, stopSignal);
+        }
+    }
+
+    private void sleepResponsive(long millis, LeaderElection.StopSignal stopSignal) {
+        long remaining = millis;
+        while (remaining > 0 && !stopSignal.isStopped()) {
+            long chunk = Math.min(remaining, 1000);
+            sleep(chunk);
+            remaining -= chunk;
+        }
+    }
+
+    private static void sleep(long millis) {
+        try {
+            Thread.sleep(millis);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
+    }
+}

+ 191 - 0
src/main/java/com/adx/tencent/honor/HonorClient.java

@@ -0,0 +1,191 @@
+package com.adx.tencent.honor;
+
+import com.adx.tencent.baidu.model.PaymentInfo;
+import com.adx.tencent.honor.model.HonorBidRecord;
+import com.adx.tencent.honor.model.HonorMediaCallbackRecord;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.URI;
+import java.net.URLEncoder;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+
+public class HonorClient {
+
+    private static final Logger log = LoggerFactory.getLogger(HonorClient.class);
+    private static final String FIXED_CHANNEL_ID = "9";
+
+    private final String baseUrl;
+    private final ObjectMapper objectMapper;
+    private final HttpClient httpClient;
+
+    public HonorClient(String baseUrl, String channelId, ObjectMapper objectMapper) {
+        this.baseUrl = (baseUrl == null || baseUrl.isBlank())
+                ? "https://ads-drcn.platform.hihonorcloud.com"
+                : baseUrl.replaceAll("/+$", "");
+        this.objectMapper = objectMapper;
+        this.httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
+    }
+
+    public HonorMediaCallbackRecord sendConversion(HonorBidRecord bid, PaymentInfo payment, int conversionId) {
+        HonorMediaCallbackRecord record = new HonorMediaCallbackRecord();
+        record.setQk(payment.getQk());
+        record.setTagId(bid.getTagId());
+        record.setEventType(payment.getAct());
+        record.setPurchase(payment.getPayment());
+        record.setAttempt(1);
+        record.setCreatedAt(Instant.now());
+
+        Map<String, String> params = buildParams(bid, payment, conversionId);
+        String callbackUrl = baseUrl + "/api/ad-tracking/v1/conversion?" + toQuery(params);
+        record.setCallbackUrl(callbackUrl);
+        record.setRequestBody(toJson(params));
+        record.setEventTimeMs(Long.parseLong(params.get("conversionTime")));
+
+        try {
+            HttpRequest request = HttpRequest.newBuilder()
+                    .uri(URI.create(callbackUrl))
+                    .GET()
+                    .timeout(Duration.ofSeconds(15))
+                    .build();
+            HttpResponse<byte[]> response = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray());
+            String body = new String(response.body(), StandardCharsets.UTF_8);
+            record.setStatus(response.statusCode());
+            record.setResponseBody(body);
+            record.setOk(response.statusCode() >= 200 && response.statusCode() < 300);
+            if (!record.isOk()) {
+                record.setErrorMessage("HTTP status: " + response.statusCode());
+            }
+            log.info("[Honor] callback结果: qk={}, tagId={}, conversionId={}, status={}, ok={}, response={}",
+                    payment.getQk(), bid.getTagId(), conversionId, response.statusCode(), record.isOk(), body);
+        } catch (Exception e) {
+            record.setOk(false);
+            record.setErrorMessage(e.getMessage());
+            log.warn("[Honor] callback失败: qk={}, tagId={}, conversionId={}, error={}",
+                    payment.getQk(), bid.getTagId(), conversionId, e.getMessage());
+        }
+        return record;
+    }
+
+    public HonorMediaCallbackRecord retryCallback(HonorMediaCallbackRecord original) throws Exception {
+        HonorMediaCallbackRecord retry = copy(original);
+        retry.setAttempt(original.getAttempt() + 1);
+        retry.setCreatedAt(Instant.now());
+        HttpRequest request = HttpRequest.newBuilder()
+                .uri(URI.create(original.getCallbackUrl()))
+                .GET()
+                .timeout(Duration.ofSeconds(15))
+                .build();
+        HttpResponse<byte[]> response = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray());
+        retry.setStatus(response.statusCode());
+        retry.setResponseBody(new String(response.body(), StandardCharsets.UTF_8));
+        retry.setOk(response.statusCode() >= 200 && response.statusCode() < 300);
+        if (!retry.isOk()) retry.setErrorMessage("HTTP status: " + response.statusCode());
+        return retry;
+    }
+
+    private Map<String, String> buildParams(HonorBidRecord bid, PaymentInfo payment, int conversionId) {
+        Map<String, String> mediaParams = bid.getMediaParams();
+        long monitorTime = parseLong(mediaParams, "eventTime");
+        long conversionTime = Math.max(Instant.now().toEpochMilli(), monitorTime + 1);
+
+        Map<String, String> params = new LinkedHashMap<>();
+        put(params, "trackId", value(mediaParams, "trackId"));
+        params.put("conversionId", String.valueOf(conversionId));
+        params.put("conversionTime", String.valueOf(conversionTime));
+        put(params, "advertiserId", value(mediaParams, "advertiserId"));
+        put(params, "oaid", value(mediaParams, "oaid"));
+        params.put("id_type", "0");
+        params.put("channelId", FIXED_CHANNEL_ID);
+        put(params, "pkgName", firstNonBlank(value(mediaParams, "pkgName"), bid.getPackageName()));
+        put(params, "extra", urlEncode("{\"tagId\":\"" + bid.getTagId() + "\"}"));
+        if (conversionId == 10004) {
+            params.put("payAmount", String.format(Locale.ROOT, "%.2f",
+                    payment.getGmv() > 0 ? payment.getGmv() : payment.getPayment()));
+            put(params, "orderId", buildOrderId(payment));
+        }
+        return params;
+    }
+
+    private static HonorMediaCallbackRecord copy(HonorMediaCallbackRecord src) {
+        HonorMediaCallbackRecord copy = new HonorMediaCallbackRecord();
+        copy.setDedupeKey(src.getDedupeKey());
+        copy.setQk(src.getQk());
+        copy.setTagId(src.getTagId());
+        copy.setCallbackUrl(src.getCallbackUrl());
+        copy.setEventType(src.getEventType());
+        copy.setEventTimeMs(src.getEventTimeMs());
+        copy.setPurchase(src.getPurchase());
+        copy.setStatus(src.getStatus());
+        copy.setOk(src.isOk());
+        copy.setAttempt(src.getAttempt());
+        copy.setResponseBody(src.getResponseBody());
+        copy.setErrorMessage(src.getErrorMessage());
+        copy.setRequestBody(src.getRequestBody());
+        copy.setCreatedAt(src.getCreatedAt());
+        return copy;
+    }
+
+    private String toJson(Object value) {
+        try {
+            return objectMapper.writeValueAsString(value);
+        } catch (Exception e) {
+            return String.valueOf(value);
+        }
+    }
+
+    private static String toQuery(Map<String, String> params) {
+        StringBuilder sb = new StringBuilder();
+        boolean first = true;
+        for (Map.Entry<String, String> entry : params.entrySet()) {
+            if (entry.getValue() == null || entry.getValue().isBlank()) continue;
+            if (!first) sb.append('&');
+            first = false;
+            sb.append(urlEncode(entry.getKey())).append('=').append(urlEncode(entry.getValue()));
+        }
+        return sb.toString();
+    }
+
+    private static String urlEncode(String value) {
+        return URLEncoder.encode(value, StandardCharsets.UTF_8);
+    }
+
+    private static void put(Map<String, String> params, String key, String value) {
+        if (value != null && !value.isBlank()) params.put(key, value);
+    }
+
+    private static String value(Map<String, String> params, String key) {
+        return params != null ? params.get(key) : null;
+    }
+
+    private static long parseLong(Map<String, String> params, String key) {
+        try {
+            return Long.parseLong(value(params, key));
+        } catch (Exception e) {
+            return 0L;
+        }
+    }
+
+    private static String firstNonBlank(String... values) {
+        for (String value : values) {
+            if (value != null && !value.isBlank()) return value;
+        }
+        return null;
+    }
+
+    private static String buildOrderId(PaymentInfo payment) {
+        String raw = (payment.getTu() != null ? payment.getTu() : "")
+                + "_" + (payment.getQk() != null ? payment.getQk() : "")
+                + "_" + payment.getAct();
+        return raw.length() <= 64 ? raw : raw.substring(0, 64);
+    }
+}

+ 205 - 0
src/main/java/com/adx/tencent/honor/HonorColdStore.java

@@ -0,0 +1,205 @@
+package com.adx.tencent.honor;
+
+import com.adx.tencent.honor.mapper.HonorBidEventMapper;
+import com.adx.tencent.honor.mapper.HonorConversionMapper;
+import com.adx.tencent.honor.mapper.HonorMediaCallbackMapper;
+import com.adx.tencent.honor.mapper.HonorTagEventMapper;
+import com.adx.tencent.honor.mapper.HonorTrackingReportMapper;
+import com.adx.tencent.honor.model.HonorBidRecord;
+import com.adx.tencent.honor.model.HonorConversionRecord;
+import com.adx.tencent.honor.model.HonorMediaCallbackRecord;
+import com.adx.tencent.honor.model.HonorTagEventRecord;
+import com.adx.tencent.honor.model.HonorTrackingRecord;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.ibatis.session.ExecutorType;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+
+import java.sql.Statement;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class HonorColdStore {
+
+    private final SqlSessionFactory sqlSessionFactory;
+
+    public HonorColdStore(SqlSessionFactory sqlSessionFactory, ObjectMapper objectMapper) {
+        this.sqlSessionFactory = sqlSessionFactory;
+    }
+
+    public void migrate() {
+        String[] ddls = {
+                """
+                CREATE TABLE IF NOT EXISTS honor_ad_bid_events (
+                    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+                    qk VARCHAR(128) NOT NULL,
+                    media VARCHAR(64) NOT NULL,
+                    media_trace_id VARCHAR(255),
+                    platform VARCHAR(16),
+                    ad_id VARCHAR(128),
+                    account_id VARCHAR(128),
+                    event_type VARCHAR(32),
+                    req_id VARCHAR(512),
+                    bid_id VARCHAR(128),
+                    creative_id VARCHAR(128),
+                    imp_id VARCHAR(128),
+                    tag_id VARCHAR(128) NOT NULL,
+                    price BIGINT UNSIGNED,
+                    show_urls JSON,
+                    click_urls JSON,
+                    landing_page TEXT,
+                    app_store_link TEXT,
+                    package_name VARCHAR(255),
+                    media_params JSON,
+                    created_at TIMESTAMP(3) NOT NULL,
+                    UNIQUE KEY uk_honor_ad_bid_events_qk (qk),
+                    KEY idx_honor_ad_bid_events_media_trace (media, media_trace_id),
+                    KEY idx_honor_ad_bid_events_platform_tag (platform, tag_id),
+                    KEY idx_honor_ad_bid_events_created_at (created_at)
+                )
+                """,
+                """
+                CREATE TABLE IF NOT EXISTS honor_tracking_reports (
+                    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+                    qk VARCHAR(128),
+                    tag_id VARCHAR(128) NOT NULL,
+                    kind VARCHAR(32) NOT NULL,
+                    tracking_url TEXT NOT NULL,
+                    status INT NOT NULL,
+                    ok TINYINT(1) NOT NULL,
+                    created_at TIMESTAMP(3) NOT NULL,
+                    KEY idx_honor_tracking_reports_qk_kind_created_at (qk, kind, created_at),
+                    KEY idx_honor_tracking_reports_tag_created_at (tag_id, created_at)
+                )
+                """,
+                """
+                CREATE TABLE IF NOT EXISTS honor_media_callbacks (
+                    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+                    dedupe_key VARCHAR(255) NOT NULL,
+                    qk VARCHAR(128),
+                    tag_id VARCHAR(128) NOT NULL,
+                    callback_url TEXT NOT NULL,
+                    event_type INT NOT NULL,
+                    event_time_ms BIGINT NOT NULL,
+                    purchase DOUBLE,
+                    status INT NOT NULL,
+                    ok TINYINT(1) NOT NULL,
+                    attempt INT NOT NULL DEFAULT 1,
+                    response_body TEXT,
+                    error_message TEXT,
+                    request_body TEXT,
+                    created_at TIMESTAMP(3) NOT NULL,
+                    KEY idx_honor_media_callbacks_dedupe_ok_created_at (dedupe_key, ok, created_at),
+                    KEY idx_honor_media_callbacks_qk_created_at (qk, created_at),
+                    KEY idx_honor_media_callbacks_tag_created_at (tag_id, created_at)
+                )
+                """,
+                """
+                CREATE TABLE IF NOT EXISTS honor_tag_event (
+                    tag_id VARCHAR(128) NOT NULL,
+                    baidu_act INT NOT NULL,
+                    conversion_id INT NOT NULL,
+                    PRIMARY KEY (tag_id, baidu_act)
+                )
+                """
+        };
+        try (SqlSession session = sqlSessionFactory.openSession(true)) {
+            Statement stmt = session.getConnection().createStatement();
+            for (String ddl : ddls) {
+                stmt.execute(ddl);
+            }
+            stmt.close();
+        } catch (Exception e) {
+            throw new RuntimeException("honor migrate failed", e);
+        }
+    }
+
+    public void saveBid(HonorBidRecord record) {
+        if (record.getCreatedAt() == null) record.setCreatedAt(Instant.now());
+        try (SqlSession session = sqlSessionFactory.openSession(true)) {
+            HonorBidEventMapper mapper = session.getMapper(HonorBidEventMapper.class);
+            mapper.insertOrUpdate(record);
+        }
+    }
+
+    public HonorBidRecord getBidByQk(String qk) {
+        if (qk == null || qk.isBlank()) return null;
+        try (SqlSession session = sqlSessionFactory.openSession(true)) {
+            HonorBidEventMapper mapper = session.getMapper(HonorBidEventMapper.class);
+            return mapper.selectByQk(qk);
+        }
+    }
+
+    public List<HonorBidRecord> getBidsByQks(List<String> qks) {
+        if (qks == null || qks.isEmpty()) return List.of();
+        try (SqlSession session = sqlSessionFactory.openSession(true)) {
+            HonorBidEventMapper mapper = session.getMapper(HonorBidEventMapper.class);
+            return mapper.selectByQks(qks);
+        }
+    }
+
+    public void saveTracking(HonorTrackingRecord record) {
+        if (record.getCreatedAt() == null) record.setCreatedAt(Instant.now());
+        try (SqlSession session = sqlSessionFactory.openSession(true)) {
+            HonorTrackingReportMapper mapper = session.getMapper(HonorTrackingReportMapper.class);
+            mapper.insert(record);
+        }
+    }
+
+    public void saveConversions(List<HonorConversionRecord> records) {
+        if (records == null || records.isEmpty()) return;
+        for (HonorConversionRecord record : records) {
+            if (record.getCreatedAt() == null) record.setCreatedAt(Instant.now());
+        }
+        try (SqlSession session = sqlSessionFactory.openSession(true)) {
+            HonorConversionMapper mapper = session.getMapper(HonorConversionMapper.class);
+            mapper.batchInsertOrUpdate(records);
+        }
+    }
+
+    public void saveMediaCallbacks(List<HonorMediaCallbackRecord> records) {
+        if (records == null || records.isEmpty()) return;
+        for (HonorMediaCallbackRecord record : records) {
+            if (record.getCreatedAt() == null) record.setCreatedAt(Instant.now());
+            if (record.getAttempt() <= 0) record.setAttempt(1);
+        }
+        try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH, false)) {
+            HonorMediaCallbackMapper mapper = session.getMapper(HonorMediaCallbackMapper.class);
+            mapper.batchInsert(records);
+            session.commit();
+        }
+    }
+
+    public Set<String> successfulCallbackKeys(Set<String> dedupeKeys) {
+        if (dedupeKeys == null || dedupeKeys.isEmpty()) return Set.of();
+        try (SqlSession session = sqlSessionFactory.openSession(true)) {
+            HonorMediaCallbackMapper mapper = session.getMapper(HonorMediaCallbackMapper.class);
+            return Set.copyOf(mapper.selectSuccessfulDedupeKeys(new ArrayList<>(dedupeKeys)));
+        }
+    }
+
+    public List<HonorMediaCallbackRecord> pendingMediaCallbacks(int limit) {
+        if (limit <= 0) limit = 100;
+        try (SqlSession session = sqlSessionFactory.openSession(true)) {
+            HonorMediaCallbackMapper mapper = session.getMapper(HonorMediaCallbackMapper.class);
+            return mapper.selectPendingCallbacks(limit);
+        }
+    }
+
+    public HonorTagEventRecord getTagEvent(String tagId, int baiduAct) {
+        if (tagId == null || tagId.isBlank()) return null;
+        try (SqlSession session = sqlSessionFactory.openSession(true)) {
+            HonorTagEventMapper mapper = session.getMapper(HonorTagEventMapper.class);
+            return mapper.selectByTagIdAndAct(tagId, baiduAct);
+        }
+    }
+
+    public List<HonorTagEventRecord> listAllTagEvents() {
+        try (SqlSession session = sqlSessionFactory.openSession(true)) {
+            HonorTagEventMapper mapper = session.getMapper(HonorTagEventMapper.class);
+            return mapper.selectAll();
+        }
+    }
+}

+ 67 - 0
src/main/java/com/adx/tencent/honor/HonorColdWorker.java

@@ -0,0 +1,67 @@
+package com.adx.tencent.honor;
+
+import com.adx.tencent.honor.model.HonorBidRecord;
+import com.adx.tencent.honor.model.HonorTrackingRecord;
+import com.adx.tencent.storage.model.QueuedEvent;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+
+public class HonorColdWorker {
+
+    private final HonorHotStore hotStore;
+    private final HonorColdStore coldStore;
+    private final String group;
+    private final String consumer;
+    private final int batch;
+    private final Duration pendingIdle;
+    private final long streamMaxLen;
+    private final ObjectMapper objectMapper;
+
+    public HonorColdWorker(HonorHotStore hotStore, HonorColdStore coldStore,
+                           String group, String consumer, int batch,
+                           Duration pendingIdle, long streamMaxLen,
+                           ObjectMapper objectMapper) {
+        this.hotStore = hotStore;
+        this.coldStore = coldStore;
+        this.group = group;
+        this.consumer = consumer;
+        this.batch = batch <= 0 ? 100 : batch;
+        this.pendingIdle = pendingIdle == null || pendingIdle.isZero() ? Duration.ofMinutes(2) : pendingIdle;
+        this.streamMaxLen = streamMaxLen;
+        this.objectMapper = objectMapper;
+    }
+
+    public int runOnce() throws Exception {
+        hotStore.ensureGroup(group);
+        List<QueuedEvent> events = hotStore.claimStale(group, consumer, pendingIdle, batch);
+        if (events.isEmpty()) events = hotStore.read(group, consumer, batch);
+        if (events.isEmpty()) return 0;
+
+        List<String> acked = new ArrayList<>();
+        Exception failed = null;
+        for (QueuedEvent event : events) {
+            try {
+                handleEvent(event);
+            } catch (Exception e) {
+                failed = e;
+                break;
+            }
+            acked.add(event.getId());
+        }
+        if (failed != null) throw failed;
+        hotStore.ack(group, acked);
+        if (streamMaxLen > 0) hotStore.trim(streamMaxLen);
+        return events.size();
+    }
+
+    private void handleEvent(QueuedEvent event) throws Exception {
+        switch (event.getType()) {
+            case "bid" -> coldStore.saveBid(objectMapper.readValue(event.getPayload(), HonorBidRecord.class));
+            case "tracking" -> coldStore.saveTracking(objectMapper.readValue(event.getPayload(), HonorTrackingRecord.class));
+            default -> throw new IllegalArgumentException("unknown honor event type: " + event.getType());
+        }
+    }
+}

+ 35 - 0
src/main/java/com/adx/tencent/honor/HonorConversionSyncRunner.java

@@ -0,0 +1,35 @@
+package com.adx.tencent.honor;
+
+import com.adx.tencent.baidu.model.ConversionQuery;
+
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+public class HonorConversionSyncRunner {
+
+    private final HonorConversionSyncService syncService;
+    private final int dateOffsetDays;
+    private final int pageSize;
+    private final List<Integer> acts;
+
+    public HonorConversionSyncRunner(HonorConversionSyncService syncService, int dateOffsetDays, int pageSize, List<Integer> acts) {
+        this.syncService = syncService;
+        this.dateOffsetDays = dateOffsetDays;
+        this.pageSize = pageSize;
+        this.acts = acts;
+    }
+
+    public HonorConversionSyncService.SyncResult runOnce() throws Exception {
+        return runForDate(LocalDate.now(ZoneId.of("Asia/Shanghai")).plusDays(dateOffsetDays));
+    }
+
+    public HonorConversionSyncService.SyncResult runForDate(LocalDate date) throws Exception {
+        ConversionQuery query = new ConversionQuery();
+        query.setDate(date.format(DateTimeFormatter.BASIC_ISO_DATE));
+        query.setPageSize(pageSize);
+        query.setActs(acts);
+        return syncService.syncConversions(query);
+    }
+}

+ 180 - 0
src/main/java/com/adx/tencent/honor/HonorConversionSyncService.java

@@ -0,0 +1,180 @@
+package com.adx.tencent.honor;
+
+import com.adx.tencent.baidu.ConversionClient;
+import com.adx.tencent.baidu.model.ConversionQuery;
+import com.adx.tencent.baidu.model.ConversionResponse;
+import com.adx.tencent.baidu.model.PaymentInfo;
+import com.adx.tencent.honor.model.HonorBidRecord;
+import com.adx.tencent.honor.model.HonorConversionRecord;
+import com.adx.tencent.honor.model.HonorMediaCallbackRecord;
+import com.adx.tencent.honor.model.HonorTagEventRecord;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class HonorConversionSyncService {
+
+    private static final Logger log = LoggerFactory.getLogger(HonorConversionSyncService.class);
+
+    private final ConversionClient baiduClient;
+    private final HonorHotStore hotStore;
+    private final HonorColdStore coldStore;
+    private final HonorClient honorClient;
+    private final ExecutorService executor = Executors.newFixedThreadPool(10, r -> {
+        Thread t = new Thread(r, "honor-conv-sync-worker");
+        t.setDaemon(true);
+        return t;
+    });
+
+    public HonorConversionSyncService(ConversionClient baiduClient,
+                                      HonorHotStore hotStore,
+                                      HonorColdStore coldStore,
+                                      HonorClient honorClient) {
+        this.baiduClient = baiduClient;
+        this.hotStore = hotStore;
+        this.coldStore = coldStore;
+        this.honorClient = honorClient;
+    }
+
+    public SyncResult syncConversions(ConversionQuery query) throws Exception {
+        if (query.getPageSize() <= 0) query.setPageSize(1);
+        SyncResult result = new SyncResult();
+        int maxPages = 100;
+        for (int page = query.getPageSize(); ; page++) {
+            query.setPageSize(page);
+            ConversionResponse response = baiduClient.queryPayments(query);
+            List<PaymentInfo> data = response.getData();
+            if (data != null && !data.isEmpty()) processPayments(data, result);
+            if (response.getPageSize() <= page || page >= maxPages) break;
+        }
+        return result;
+    }
+
+    private void processPayments(List<PaymentInfo> payments, SyncResult result) {
+        Set<String> qks = new LinkedHashSet<>();
+        for (PaymentInfo payment : payments) {
+            result.fetched.incrementAndGet();
+            if (payment.getQk() != null && !payment.getQk().isBlank()) qks.add(payment.getQk());
+        }
+        Map<String, HonorBidRecord> bidMap = hotStore.findBidsByQks(new ArrayList<>(qks));
+        List<HonorConversionRecord> conversionRecords = new ArrayList<>();
+        List<CallbackWork> callbackWorks = new ArrayList<>();
+
+        for (PaymentInfo payment : payments) {
+            HonorBidRecord bid = bidMap.get(payment.getQk());
+            if (bid == null) {
+                result.skipped.incrementAndGet();
+                continue;
+            }
+            result.matched.incrementAndGet();
+            conversionRecords.add(toConversionRecord(payment, bid));
+            HonorTagEventRecord tagEvent = coldStore.getTagEvent(bid.getTagId(), payment.getAct());
+            if (tagEvent == null) {
+                result.skipped.incrementAndGet();
+                log.warn("[HonorConversionSync] missing conversionId mapping | qk={} | tagId={} | act={}",
+                        payment.getQk(), bid.getTagId(), payment.getAct());
+                continue;
+            }
+            String dedupeKey = callbackDedupeKey(payment, bid, tagEvent.getConversionId());
+            callbackWorks.add(new CallbackWork(payment, bid, tagEvent.getConversionId(), dedupeKey));
+        }
+
+        if (!conversionRecords.isEmpty()) {
+            coldStore.saveConversions(conversionRecords);
+        }
+
+        if (callbackWorks.isEmpty()) return;
+
+        Set<String> dedupeKeys = new LinkedHashSet<>();
+        for (CallbackWork work : callbackWorks) dedupeKeys.add(work.dedupeKey);
+        Set<String> existing = coldStore.successfulCallbackKeys(dedupeKeys);
+
+        List<CallbackWork> toSend = new ArrayList<>();
+        for (CallbackWork work : callbackWorks) {
+            if (existing.contains(work.dedupeKey)) {
+                result.alreadySent.incrementAndGet();
+            } else {
+                toSend.add(work);
+            }
+        }
+
+        List<CompletableFuture<HonorMediaCallbackRecord>> futures = new ArrayList<>();
+        for (CallbackWork work : toSend) {
+            futures.add(CompletableFuture.supplyAsync(() -> honorClient.sendConversion(work.bid, work.payment, work.conversionId), executor));
+        }
+
+        List<HonorMediaCallbackRecord> callbackRecords = new ArrayList<>();
+        for (int i = 0; i < futures.size(); i++) {
+            HonorMediaCallbackRecord record = futures.get(i).join();
+            record.setDedupeKey(toSend.get(i).dedupeKey);
+            callbackRecords.add(record);
+            if (record.isOk()) result.sent.incrementAndGet();
+            else result.failed.incrementAndGet();
+        }
+        if (!callbackRecords.isEmpty()) {
+            coldStore.saveMediaCallbacks(callbackRecords);
+        }
+    }
+
+    private HonorConversionRecord toConversionRecord(PaymentInfo payment, HonorBidRecord bid) {
+        HonorConversionRecord record = new HonorConversionRecord();
+        record.setDedupeKey(conversionDedupeKey(payment));
+        record.setQk(payment.getQk());
+        record.setMedia("honor");
+        record.setTagId(bid.getTagId());
+        record.setDate(payment.getDate());
+        record.setAppSid(payment.getAppSid());
+        record.setCustomerName(payment.getCustomerName());
+        record.setDeviceId(payment.getDeviceId());
+        record.setConv(payment.getConv());
+        record.setPayment(payment.getPayment());
+        record.setGmv(payment.getGmv());
+        record.setAct(payment.getAct());
+        record.setTu(payment.getTu());
+        record.setClkTime(payment.getClkTime());
+        record.setCreatedAt(Instant.now());
+        return record;
+    }
+
+    static String conversionDedupeKey(PaymentInfo p) {
+        return String.format("%s:%s:%s:%d", p.getQk(), p.getDate(), p.getDeviceId(), p.getAct());
+    }
+
+    static String callbackDedupeKey(PaymentInfo p, HonorBidRecord bid, int conversionId) {
+        return String.format("honor:%s:%s:%s:%s:%d:%d",
+                p.getQk(), p.getDate(), p.getDeviceId(), bid.getTagId(), p.getAct(), conversionId);
+    }
+
+    private static class CallbackWork {
+        private final PaymentInfo payment;
+        private final HonorBidRecord bid;
+        private final int conversionId;
+        private final String dedupeKey;
+
+        private CallbackWork(PaymentInfo payment, HonorBidRecord bid, int conversionId, String dedupeKey) {
+            this.payment = payment;
+            this.bid = bid;
+            this.conversionId = conversionId;
+            this.dedupeKey = dedupeKey;
+        }
+    }
+
+    public static class SyncResult {
+        public final AtomicInteger fetched = new AtomicInteger();
+        public final AtomicInteger matched = new AtomicInteger();
+        public final AtomicInteger sent = new AtomicInteger();
+        public final AtomicInteger skipped = new AtomicInteger();
+        public final AtomicInteger failed = new AtomicInteger();
+        public final AtomicInteger alreadySent = new AtomicInteger();
+    }
+}

+ 246 - 0
src/main/java/com/adx/tencent/honor/HonorHotStore.java

@@ -0,0 +1,246 @@
+package com.adx.tencent.honor;
+
+import com.adx.tencent.honor.model.HonorBidRecord;
+import com.adx.tencent.honor.model.HonorTrackingRecord;
+import com.adx.tencent.storage.model.QueuedEvent;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.data.redis.connection.stream.Consumer;
+import org.springframework.data.redis.connection.stream.MapRecord;
+import org.springframework.data.redis.connection.stream.PendingMessage;
+import org.springframework.data.redis.connection.stream.PendingMessagesSummary;
+import org.springframework.data.redis.connection.stream.ReadOffset;
+import org.springframework.data.redis.connection.stream.RecordId;
+import org.springframework.data.redis.connection.stream.StreamOffset;
+import org.springframework.data.redis.connection.stream.StreamReadOptions;
+import org.springframework.data.redis.core.StringRedisTemplate;
+
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class HonorHotStore {
+
+    private final StringRedisTemplate redis;
+    private final ObjectMapper objectMapper;
+    private final HonorColdStore coldStore;
+    private final String prefix;
+    private final String stream;
+    private final Duration bidTtl;
+
+    public HonorHotStore(StringRedisTemplate redis, ObjectMapper objectMapper,
+                         HonorColdStore coldStore, String prefix, String stream, Duration bidTtl) {
+        this.redis = redis;
+        this.objectMapper = objectMapper;
+        this.coldStore = coldStore;
+        this.prefix = prefix;
+        this.stream = stream;
+        this.bidTtl = bidTtl;
+    }
+
+    public void recordBid(HonorBidRecord record) {
+        if (record.getCreatedAt() == null) record.setCreatedAt(Instant.now());
+        try {
+            String hotPayload = objectMapper.writeValueAsString(record.toHotRecord());
+            String fullPayload = objectMapper.writeValueAsString(record);
+            redis.executePipelined((org.springframework.data.redis.connection.RedisConnection conn) -> {
+                byte[] hotBytes = hotPayload.getBytes(StandardCharsets.UTF_8);
+                long ttlMillis = bidTtl.toMillis();
+                conn.stringCommands().set(
+                        bidKey(record.getQk()).getBytes(StandardCharsets.UTF_8),
+                        hotBytes,
+                        org.springframework.data.redis.core.types.Expiration.milliseconds(ttlMillis),
+                        org.springframework.data.redis.connection.RedisStringCommands.SetOption.UPSERT
+                );
+                conn.stringCommands().set(
+                        mediaTraceKey(record.getMediaTraceId()).getBytes(StandardCharsets.UTF_8),
+                        hotBytes,
+                        org.springframework.data.redis.core.types.Expiration.milliseconds(ttlMillis),
+                        org.springframework.data.redis.connection.RedisStringCommands.SetOption.UPSERT
+                );
+                byte[] streamKey = stream.getBytes(StandardCharsets.UTF_8);
+                Map<byte[], byte[]> body = new LinkedHashMap<>();
+                body.put("type".getBytes(StandardCharsets.UTF_8), "bid".getBytes(StandardCharsets.UTF_8));
+                body.put("payload".getBytes(StandardCharsets.UTF_8), fullPayload.getBytes(StandardCharsets.UTF_8));
+                conn.streamCommands().xAdd(MapRecord.create(streamKey, body));
+                return null;
+            });
+        } catch (Exception e) {
+            throw new RuntimeException("record honor bid failed", e);
+        }
+    }
+
+    public void recordTracking(HonorTrackingRecord record) {
+        if (record.getCreatedAt() == null) record.setCreatedAt(Instant.now());
+        try {
+            String payload = objectMapper.writeValueAsString(record);
+            redis.opsForStream().add(MapRecord.create(stream, Map.of("type", "tracking", "payload", payload)).withStreamKey(stream));
+        } catch (Exception e) {
+            throw new RuntimeException("record honor tracking failed", e);
+        }
+    }
+
+    public void shrinkAfterClick(HonorBidRecord record) {
+        try {
+            String slimPayload = objectMapper.writeValueAsString(record.toPostClickRecord());
+            byte[] slimBytes = slimPayload.getBytes(StandardCharsets.UTF_8);
+            Long remainingTtl = redis.getExpire(bidKey(record.getQk()), java.util.concurrent.TimeUnit.MILLISECONDS);
+            if (remainingTtl == null || remainingTtl <= 0) remainingTtl = bidTtl.toMillis();
+            long ttlMs = remainingTtl;
+            redis.executePipelined((org.springframework.data.redis.connection.RedisConnection conn) -> {
+                conn.stringCommands().set(
+                        bidKey(record.getQk()).getBytes(StandardCharsets.UTF_8),
+                        slimBytes,
+                        org.springframework.data.redis.core.types.Expiration.milliseconds(ttlMs),
+                        org.springframework.data.redis.connection.RedisStringCommands.SetOption.UPSERT
+                );
+                conn.stringCommands().set(
+                        mediaTraceKey(record.getMediaTraceId()).getBytes(StandardCharsets.UTF_8),
+                        slimBytes,
+                        org.springframework.data.redis.core.types.Expiration.milliseconds(ttlMs),
+                        org.springframework.data.redis.connection.RedisStringCommands.SetOption.UPSERT
+                );
+                return null;
+            });
+        } catch (Exception e) {
+            throw new RuntimeException("shrink honor bid failed", e);
+        }
+    }
+
+    public HonorBidRecord findBidByMediaTrace(String traceId) {
+        String payload = redis.opsForValue().get(mediaTraceKey(traceId));
+        if (payload == null) return null;
+        return parseBidRecord(payload);
+    }
+
+    public Map<String, HonorBidRecord> findBidsByQks(List<String> qks) {
+        if (qks == null || qks.isEmpty()) return Collections.emptyMap();
+        Map<String, HonorBidRecord> result = new HashMap<>();
+        List<String> keys = new ArrayList<>();
+        Map<String, String> keyToQk = new HashMap<>();
+        for (String qk : qks) {
+            if (qk == null || qk.isBlank() || keyToQk.containsValue(qk)) continue;
+            String key = bidKey(qk);
+            keys.add(key);
+            keyToQk.put(key, qk);
+        }
+        List<String> payloads = redis.opsForValue().multiGet(keys);
+        Set<String> missing = new LinkedHashSet<>();
+        for (int i = 0; i < keys.size(); i++) {
+            String payload = payloads != null ? payloads.get(i) : null;
+            String qk = keyToQk.get(keys.get(i));
+            if (payload != null) {
+                result.put(qk, parseBidRecord(payload));
+            } else if (qk != null) {
+                missing.add(qk);
+            }
+        }
+        if (!missing.isEmpty() && coldStore != null) {
+            for (HonorBidRecord bid : coldStore.getBidsByQks(new ArrayList<>(missing))) {
+                if (bid != null && bid.getQk() != null) result.put(bid.getQk(), bid);
+            }
+        }
+        return result;
+    }
+
+    public void ensureGroup(String group) {
+        try {
+            redis.opsForStream().createGroup(stream, ReadOffset.from("0"), group);
+        } catch (Exception e) {
+            if (isBusyGroupError(e)) return;
+            try {
+                var id = redis.opsForStream().add(org.springframework.data.redis.connection.stream.StreamRecords
+                        .newRecord().ofMap(Collections.singletonMap("_init", "1")).withStreamKey(stream));
+                if (id != null) redis.opsForStream().delete(stream, id);
+                redis.opsForStream().createGroup(stream, ReadOffset.from("0"), group);
+            } catch (Exception retryEx) {
+                if (!isBusyGroupError(retryEx)) throw new RuntimeException("ensure honor group failed", retryEx);
+            }
+        }
+    }
+
+    public List<QueuedEvent> read(String group, String consumer, int count) {
+        try {
+            List<MapRecord<String, Object, Object>> records = redis.opsForStream().read(
+                    Consumer.from(group, consumer),
+                    StreamReadOptions.empty().count(count).block(Duration.ofSeconds(1)),
+                    StreamOffset.create(stream, ReadOffset.lastConsumed())
+            );
+            return toQueuedEvents(records);
+        } catch (Exception e) {
+            if (e.getMessage() != null && e.getMessage().contains("NOGROUP")) return Collections.emptyList();
+            throw new RuntimeException("read honor stream failed", e);
+        }
+    }
+
+    public List<QueuedEvent> claimStale(String group, String consumer, Duration minIdle, int count) {
+        try {
+            PendingMessagesSummary summary = redis.opsForStream().pending(stream, group);
+            if (summary == null || summary.getTotalPendingMessages() == 0) return Collections.emptyList();
+            var pendingMessages = redis.opsForStream().pending(
+                    stream, group, org.springframework.data.domain.Range.unbounded(), count);
+            if (pendingMessages == null || !pendingMessages.iterator().hasNext()) return Collections.emptyList();
+            List<String> staleIds = new ArrayList<>();
+            for (PendingMessage message : pendingMessages) {
+                if (message.getElapsedTimeSinceLastDelivery().compareTo(minIdle) >= 0) {
+                    staleIds.add(message.getIdAsString());
+                }
+            }
+            if (staleIds.isEmpty()) return Collections.emptyList();
+            List<MapRecord<String, Object, Object>> claimed = redis.opsForStream().claim(
+                    stream, group, consumer, minIdle, staleIds.stream().map(RecordId::of).toArray(RecordId[]::new)
+            );
+            return toQueuedEvents(claimed);
+        } catch (Exception e) {
+            return Collections.emptyList();
+        }
+    }
+
+    public void ack(String group, List<String> ids) {
+        if (ids == null || ids.isEmpty()) return;
+        redis.opsForStream().acknowledge(stream, group, ids.toArray(String[]::new));
+    }
+
+    public void trim(long maxLen) {
+        if (maxLen > 0) redis.opsForStream().trim(stream, maxLen, false);
+    }
+
+    private String bidKey(String qk) { return prefix + "bid:" + qk; }
+    private String mediaTraceKey(String traceId) { return prefix + "media:honor:" + traceId; }
+
+    private HonorBidRecord parseBidRecord(String payload) {
+        try {
+            return objectMapper.readValue(payload, HonorBidRecord.class);
+        } catch (Exception e) {
+            throw new RuntimeException("parse honor bid failed", e);
+        }
+    }
+
+    private List<QueuedEvent> toQueuedEvents(List<MapRecord<String, Object, Object>> records) {
+        if (records == null || records.isEmpty()) return Collections.emptyList();
+        List<QueuedEvent> result = new ArrayList<>(records.size());
+        for (MapRecord<String, Object, Object> record : records) {
+            QueuedEvent event = new QueuedEvent();
+            event.setId(record.getId().getValue());
+            event.setType(String.valueOf(record.getValue().get("type")));
+            event.setPayload(String.valueOf(record.getValue().get("payload")));
+            result.add(event);
+        }
+        return result;
+    }
+
+    private boolean isBusyGroupError(Throwable e) {
+        while (e != null) {
+            if (e.getMessage() != null && e.getMessage().contains("BUSYGROUP")) return true;
+            e = e.getCause();
+        }
+        return false;
+    }
+}

+ 113 - 0
src/main/java/com/adx/tencent/honor/HonorPlacement.java

@@ -0,0 +1,113 @@
+package com.adx.tencent.honor;
+
+import java.util.List;
+import java.util.Map;
+
+public class HonorPlacement {
+
+    private long baiduMediaId;
+    private String baiduAppId;
+    private String baiduTagId;
+    private Map<String, HonorAppPlacement> platforms;
+    private long bidFloor;
+    private List<Integer> actionTypes;
+
+    public long getBaiduMediaId() { return baiduMediaId; }
+    public void setBaiduMediaId(long v) { this.baiduMediaId = v; }
+    public String getBaiduAppId() { return baiduAppId; }
+    public void setBaiduAppId(String v) { this.baiduAppId = v; }
+    public String getBaiduTagId() { return baiduTagId; }
+    public void setBaiduTagId(String v) { this.baiduTagId = v; }
+    public Map<String, HonorAppPlacement> getPlatforms() { return platforms; }
+    public void setPlatforms(Map<String, HonorAppPlacement> v) { this.platforms = v; }
+    public long getBidFloor() { return bidFloor; }
+    public void setBidFloor(long v) { this.bidFloor = v; }
+    public List<Integer> getActionTypes() { return actionTypes; }
+    public void setActionTypes(List<Integer> v) { this.actionTypes = v; }
+
+    public Resolved resolve(Map<String, String> params) {
+        String tagId = firstParam(params, "tagId", "tag_id", "baidu_tag_id");
+        if (tagId != null && !tagId.isBlank()) {
+            Resolved r = resolveByTagId(tagId);
+            if (r != null) return r;
+            throw new IllegalArgumentException("unknown honor baidu tag id: " + tagId);
+        }
+
+        String platform = normalizePlatform(firstParam(params, "platform", "os", "system", "device_os"));
+        if (platform != null && !platform.isBlank()) {
+            Resolved r = resolveByPlatform(platform);
+            if (r != null) return r;
+            throw new IllegalArgumentException("unsupported honor baidu platform: " + platform);
+        }
+
+        if (baiduAppId != null && !baiduAppId.isBlank()
+                && baiduTagId != null && !baiduTagId.isBlank()) {
+            return new Resolved(null, baiduAppId, baiduTagId);
+        }
+
+        if (platforms != null && platforms.size() == 1) {
+            String p = platforms.keySet().iterator().next();
+            Resolved r = resolveByPlatform(p);
+            if (r != null) return r;
+        }
+
+        throw new IllegalArgumentException("missing honor tagId or platform");
+    }
+
+    private Resolved resolveByTagId(String tagId) {
+        if (platforms != null) {
+            for (Map.Entry<String, HonorAppPlacement> e : platforms.entrySet()) {
+                HonorAppPlacement p = e.getValue();
+                if (p.getAppId() != null && p.getTagIds() != null && p.getTagIds().contains(tagId)) {
+                    return new Resolved(e.getKey(), p.getAppId(), tagId);
+                }
+            }
+        }
+        if (baiduAppId != null && tagId.equals(baiduTagId)) {
+            return new Resolved(null, baiduAppId, baiduTagId);
+        }
+        return null;
+    }
+
+    private Resolved resolveByPlatform(String platform) {
+        if (platforms == null) return null;
+        HonorAppPlacement p = platforms.get(platform);
+        if (p == null || p.getAppId() == null || p.getTagIds() == null || p.getTagIds().isEmpty()) {
+            return null;
+        }
+        if (p.getTagIds().size() > 1) {
+            throw new IllegalArgumentException(
+                    "tagId is required for honor platform " + platform + " because multiple tag ids are configured");
+        }
+        return new Resolved(platform, p.getAppId(), p.getTagIds().get(0));
+    }
+
+    private static String firstParam(Map<String, String> p, String... keys) {
+        if (p == null) return null;
+        for (String k : keys) {
+            String v = p.get(k);
+            if (v != null && !v.isBlank()) return v.trim();
+        }
+        return null;
+    }
+
+    static String normalizePlatform(String value) {
+        if (value == null) return null;
+        return switch (value.toLowerCase().trim()) {
+            case "ios", "iphone", "ipad", "1" -> "ios";
+            case "android", "安卓", "0", "2" -> "android";
+            default -> null;
+        };
+    }
+
+    public static class HonorAppPlacement {
+        private String appId;
+        private List<String> tagIds;
+        public String getAppId() { return appId; }
+        public void setAppId(String v) { this.appId = v; }
+        public List<String> getTagIds() { return tagIds; }
+        public void setTagIds(List<String> v) { this.tagIds = v; }
+    }
+
+    public record Resolved(String platform, String appId, String tagId) {}
+}

+ 70 - 0
src/main/java/com/adx/tencent/honor/HonorRetryService.java

@@ -0,0 +1,70 @@
+package com.adx.tencent.honor;
+
+import com.adx.tencent.honor.model.HonorMediaCallbackRecord;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+
+public class HonorRetryService {
+
+    private final HonorColdStore coldStore;
+    private final HonorClient honorClient;
+    private final int defaultLimit;
+
+    public HonorRetryService(HonorColdStore coldStore, HonorClient honorClient, int defaultLimit) {
+        this.coldStore = coldStore;
+        this.honorClient = honorClient;
+        this.defaultLimit = defaultLimit > 0 ? defaultLimit : 100;
+    }
+
+    public RetryResult retryCallbacks(int limit) {
+        List<HonorMediaCallbackRecord> pending = coldStore.pendingMediaCallbacks(limit <= 0 ? defaultLimit : limit);
+        RetryResult result = new RetryResult();
+        result.fetched = pending.size();
+        List<HonorMediaCallbackRecord> saved = new ArrayList<>();
+        for (HonorMediaCallbackRecord record : pending) {
+            try {
+                HonorMediaCallbackRecord retry = honorClient.retryCallback(record);
+                saved.add(retry);
+                if (retry.isOk()) result.sent++;
+                else result.failed++;
+            } catch (Exception e) {
+                HonorMediaCallbackRecord failed = copy(record);
+                failed.setAttempt(record.getAttempt() + 1);
+                failed.setOk(false);
+                failed.setErrorMessage(e.getMessage());
+                failed.setCreatedAt(Instant.now());
+                saved.add(failed);
+                result.failed++;
+            }
+        }
+        coldStore.saveMediaCallbacks(saved);
+        return result;
+    }
+
+    private static HonorMediaCallbackRecord copy(HonorMediaCallbackRecord src) {
+        HonorMediaCallbackRecord copy = new HonorMediaCallbackRecord();
+        copy.setDedupeKey(src.getDedupeKey());
+        copy.setQk(src.getQk());
+        copy.setTagId(src.getTagId());
+        copy.setCallbackUrl(src.getCallbackUrl());
+        copy.setEventType(src.getEventType());
+        copy.setEventTimeMs(src.getEventTimeMs());
+        copy.setPurchase(src.getPurchase());
+        copy.setStatus(src.getStatus());
+        copy.setOk(src.isOk());
+        copy.setAttempt(src.getAttempt());
+        copy.setResponseBody(src.getResponseBody());
+        copy.setErrorMessage(src.getErrorMessage());
+        copy.setRequestBody(src.getRequestBody());
+        copy.setCreatedAt(src.getCreatedAt());
+        return copy;
+    }
+
+    public static class RetryResult {
+        public int fetched;
+        public int sent;
+        public int failed;
+    }
+}

+ 75 - 0
src/main/java/com/adx/tencent/honor/HonorTagEventResolver.java

@@ -0,0 +1,75 @@
+package com.adx.tencent.honor;
+
+import com.adx.tencent.honor.model.HonorTagEventRecord;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.redis.core.StringRedisTemplate;
+
+/**
+ * 荣耀广告位回传方式解析器。
+ * 根据广告位ID和百度行为解析荣耀 conversionId:
+ *   1. 优先从 Redis 读取(key = {prefix}{tagId}:{act});
+ *   2. Redis 未命中时回退查询 honor_tag_event 表;
+ *   3. 支持 act=0 作为默认兜底配置。
+ */
+public class HonorTagEventResolver {
+
+    private static final Logger log = LoggerFactory.getLogger(HonorTagEventResolver.class);
+
+    private final StringRedisTemplate redis;
+    private final HonorColdStore coldStore;
+    private final String keyPrefix;
+
+    public HonorTagEventResolver(StringRedisTemplate redis, HonorColdStore coldStore, String keyPrefix) {
+        this.redis = redis;
+        this.coldStore = coldStore;
+        this.keyPrefix = keyPrefix == null ? "" : keyPrefix;
+    }
+
+    public Integer resolveConversionId(String tagId, int baiduAct) {
+        if (tagId == null || tagId.isBlank()) {
+            return null;
+        }
+
+        try {
+            Integer cached = parseInt(redis.opsForValue().get(buildRedisKey(tagId, baiduAct)));
+            if (cached != null) {
+                return cached;
+            }
+            cached = parseInt(redis.opsForValue().get(buildRedisKey(tagId, 0)));
+            if (cached != null) {
+                return cached;
+            }
+        } catch (Exception e) {
+            log.warn("[HonorTagEventResolver] 读取 Redis 失败, 回退DB查询 | tagId={} | act={} | error={}",
+                    tagId, baiduAct, e.getMessage());
+        }
+
+        try {
+            HonorTagEventRecord record = coldStore.getTagEvent(tagId, baiduAct);
+            if (record == null) {
+                record = coldStore.getTagEvent(tagId, 0);
+            }
+            return record != null ? record.getConversionId() : null;
+        } catch (Exception e) {
+            log.warn("[HonorTagEventResolver] 查询 honor_tag_event 失败 | tagId={} | act={} | error={}",
+                    tagId, baiduAct, e.getMessage());
+            return null;
+        }
+    }
+
+    private String buildRedisKey(String tagId, int baiduAct) {
+        return keyPrefix + tagId + ":" + baiduAct;
+    }
+
+    private static Integer parseInt(String value) {
+        if (value == null || value.isBlank()) {
+            return null;
+        }
+        try {
+            return Integer.parseInt(value);
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+}

+ 71 - 0
src/main/java/com/adx/tencent/honor/HonorTagEventSyncService.java

@@ -0,0 +1,71 @@
+package com.adx.tencent.honor;
+
+import com.adx.tencent.honor.model.HonorTagEventRecord;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.connection.RedisStringCommands;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.core.types.Expiration;
+
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.util.List;
+
+/**
+ * 荣耀广告位回传方式同步服务。
+ * 定时从 honor_tag_event 表读取全部记录,同步到 Redis:
+ *   key   = {prefix}{tag_id}:{baidu_act}
+ *   value = conversion_id
+ */
+public class HonorTagEventSyncService {
+
+    private static final Logger log = LoggerFactory.getLogger(HonorTagEventSyncService.class);
+
+    private final HonorColdStore coldStore;
+    private final StringRedisTemplate redis;
+    private final String keyPrefix;
+    private final Duration ttl;
+
+    public HonorTagEventSyncService(HonorColdStore coldStore, StringRedisTemplate redis,
+                                    String keyPrefix, Duration ttl) {
+        this.coldStore = coldStore;
+        this.redis = redis;
+        this.keyPrefix = keyPrefix == null ? "" : keyPrefix;
+        this.ttl = ttl;
+    }
+
+    public int syncOnce() {
+        List<HonorTagEventRecord> rows = coldStore.listAllTagEvents();
+        if (rows == null || rows.isEmpty()) {
+            log.info("[HonorTagEventSync] 无数据可同步");
+            return 0;
+        }
+
+        long ttlMs = (ttl != null && !ttl.isZero()) ? ttl.toMillis() : 0;
+        redis.executePipelined((RedisConnection conn) -> {
+            for (HonorTagEventRecord row : rows) {
+                if (row.getTagId() == null || row.getTagId().isBlank()) {
+                    continue;
+                }
+                byte[] key = buildRedisKey(row.getTagId(), row.getBaiduAct());
+                byte[] val = String.valueOf(row.getConversionId()).getBytes(StandardCharsets.UTF_8);
+                if (ttlMs > 0) {
+                    conn.stringCommands().set(key, val,
+                            Expiration.milliseconds(ttlMs),
+                            RedisStringCommands.SetOption.UPSERT);
+                } else {
+                    conn.stringCommands().set(key, val);
+                }
+            }
+            return null;
+        });
+
+        log.info("[HonorTagEventSync] 同步完成,共 {} 条荣耀广告位回传方式写入 Redis", rows.size());
+        return rows.size();
+    }
+
+    private byte[] buildRedisKey(String tagId, int baiduAct) {
+        return (keyPrefix + tagId + ":" + baiduAct).getBytes(StandardCharsets.UTF_8);
+    }
+}

+ 288 - 0
src/main/java/com/adx/tencent/honor/HonorTrackingController.java

@@ -0,0 +1,288 @@
+package com.adx.tencent.honor;
+
+import com.adx.tencent.baidu.AdxClient;
+import com.adx.tencent.baidu.model.NormalizedBidResponse;
+import com.adx.tencent.baidu.model.TrackingResult;
+import com.adx.tencent.honor.model.HonorBidRecord;
+import com.adx.tencent.honor.model.HonorTrackingRecord;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.lang.Nullable;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.server.ResponseStatusException;
+
+import java.time.Instant;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Semaphore;
+
+@RestController
+public class HonorTrackingController {
+
+    private static final Logger log = LoggerFactory.getLogger(HonorTrackingController.class);
+    private static final int MAX_CONCURRENT = 50;
+    private final Semaphore impressionSemaphore = new Semaphore(MAX_CONCURRENT);
+    private final Semaphore clickSemaphore = new Semaphore(MAX_CONCURRENT);
+
+    private final AdxClient adxClient;
+    private final HonorHotStore hotStore;
+    private final HonorPlacement placement;
+
+    public HonorTrackingController(AdxClient adxClient,
+                                   HonorHotStore hotStore,
+                                   @Nullable HonorPlacement placement) {
+        this.adxClient = adxClient;
+        this.hotStore = hotStore;
+        this.placement = placement;
+    }
+
+    @GetMapping("/honor/impression")
+    @ResponseStatus(HttpStatus.NO_CONTENT)
+    public void impression(HttpServletRequest request) throws Exception {
+        impressionSemaphore.acquire();
+        try {
+            doImpression(request);
+        } finally {
+            impressionSemaphore.release();
+        }
+    }
+
+    @GetMapping("/honor/click")
+    public Object click(HttpServletRequest request, HttpServletResponse response) throws Exception {
+        clickSemaphore.acquire();
+        try {
+            return doClick(request, response);
+        } finally {
+            clickSemaphore.release();
+        }
+    }
+
+    private void doImpression(HttpServletRequest request) throws Exception {
+        if (placement == null) throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, "honor placement not configured");
+        Map<String, String> params = normalizeParams(request);
+        String tagId = requireTagId(params);
+        String traceId = honorTraceId(params);
+        if (traceId == null || traceId.isBlank()) {
+            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "missing honor trace id (trackId/requestId/oaid)");
+        }
+
+        NormalizedBidResponse response = adxClient.requestBid(buildBidRequest(request, traceId, params));
+        if (response == null) throw new ResponseStatusException(HttpStatus.BAD_GATEWAY, "empty baidu ADX response");
+
+        HonorBidRecord record = bidRecordFromResponse(response, traceId, "impression", params);
+        hotStore.recordBid(record);
+
+        if (response.getBid() != null && response.getBid().getShowUrls() != null && !response.getBid().getShowUrls().isEmpty()) {
+            List<TrackingResult> tracking = adxClient.reportImpression(response.getBid().getShowUrls(), response.getBid().getPrice());
+            recordTracking(record.getQk(), tagId, "impression", tracking);
+        }
+    }
+
+    private Object doClick(HttpServletRequest request, HttpServletResponse response) throws Exception {
+        if (placement == null) throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, "honor placement not configured");
+        Map<String, String> params = normalizeParams(request);
+        String tagId = requireTagId(params);
+        String traceId = honorTraceId(params);
+        if (traceId == null || traceId.isBlank()) {
+            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "missing honor trace id (trackId/requestId/oaid)");
+        }
+
+        HonorBidRecord record = hotStore.findBidByMediaTrace(traceId);
+        if (record == null) {
+            NormalizedBidResponse bidResponse = adxClient.requestBid(buildBidRequest(request, traceId, params));
+            if (bidResponse == null) throw new ResponseStatusException(HttpStatus.BAD_GATEWAY, "empty baidu ADX response");
+            record = bidRecordFromResponse(bidResponse, traceId, "click", params);
+            hotStore.recordBid(record);
+            if (bidResponse.getBid() != null && bidResponse.getBid().getShowUrls() != null && !bidResponse.getBid().getShowUrls().isEmpty()) {
+                List<TrackingResult> impressionTracking = adxClient.reportImpression(
+                        bidResponse.getBid().getShowUrls(), bidResponse.getBid().getPrice());
+                recordTracking(record.getQk(), tagId, "impression", impressionTracking);
+            }
+        }
+
+        List<TrackingResult> tracking = adxClient.reportClick(
+                record.getClickUrls() != null ? record.getClickUrls() : Collections.emptyList());
+        recordTracking(record.getQk(), tagId, "click", tracking);
+        hotStore.shrinkAfterClick(record);
+
+        String destination = record.getAppStoreLink() != null && !record.getAppStoreLink().isBlank()
+                ? record.getAppStoreLink() : record.getLandingPage();
+        if (destination != null && !destination.isBlank()) {
+            response.sendRedirect(destination);
+            return null;
+        }
+        return Map.of("qk", record.getQk(), "traceId", traceId, "tagId", tagId, "tracking", tracking);
+    }
+
+    private Map<String, Object> buildBidRequest(HttpServletRequest request, String traceId, Map<String, String> params) {
+        HonorPlacement.Resolved resolved = placement.resolve(params);
+        params.put("baidu_app_id", resolved.appId());
+        params.put("baidu_tag_id", resolved.tagId());
+        params.put("tagId", resolved.tagId());
+        if (resolved.platform() != null && !resolved.platform().isBlank()) params.put("baidu_platform", resolved.platform());
+
+        Map<String, Object> imp = new LinkedHashMap<>();
+        imp.put("id", "1");
+        imp.put("appId", resolved.appId());
+        imp.put("tagId", resolved.tagId());
+        imp.put("secure", 1);
+        imp.put("adType", 0);
+        imp.put("assets", List.of(
+                Map.of("templateId", 1, "ratio", 2),
+                Map.of("templateId", 1, "ratio", 7),
+                Map.of("templateId", 2, "ratio", 1),
+                Map.of("templateId", 2, "ratio", 4),
+                Map.of("templateId", 3, "ratio", 2),
+                Map.of("templateId", 4, "ratio", 4),
+                Map.of("templateId", 4, "ratio", 5)
+        ));
+        imp.put("actionType", placement.getActionTypes() == null || placement.getActionTypes().isEmpty()
+                ? List.of(0, 1, 2)
+                : placement.getActionTypes());
+        imp.put("maxCount", 1);
+        if (placement.getBidFloor() > 0) imp.put("bidFloor", placement.getBidFloor());
+
+        Map<String, Object> device = new LinkedHashMap<>();
+        device.put("ip", firstParam(params, "ip", "m6a") != null ? firstParam(params, "ip", "m6a") : clientIp(request));
+        device.put("ua", firstParam(params, "ua", "user_agent", "useragent") != null
+                ? firstParam(params, "ua", "user_agent", "useragent")
+                : defaultUa(resolved.platform()));
+        device.put("deviceType", 1);
+        if ("ios".equals(resolved.platform())) device.put("os", 1);
+        if ("android".equals(resolved.platform())) device.put("os", 2);
+        Map<String, Object> uid = new LinkedHashMap<>();
+        putUid(uid, "oaid", params.get("oaid"));
+        putUid(uid, "oaidMd5", firstParam(params, "oaid_md5", "oaidMd5", "hash_oaid"));
+        String muid = firstParam(params, "oaid_md5", "oaidMd5", "hash_oaid", "oaid");
+        putUid(uid, "muid", muid);
+        if (!uid.isEmpty()) device.put("uid", uid);
+
+        Map<String, Object> bidReq = new LinkedHashMap<>();
+        bidReq.put("mediaId", placement.getBaiduMediaId());
+        bidReq.put("reqId", traceId + "-" + Instant.now().toEpochMilli());
+        bidReq.put("imp", List.of(imp));
+        bidReq.put("device", device);
+        return bidReq;
+    }
+
+    private HonorBidRecord bidRecordFromResponse(NormalizedBidResponse response, String traceId, String eventType, Map<String, String> params) {
+        HonorBidRecord record = new HonorBidRecord();
+        record.setQk(response.getQk());
+        record.setReqId(response.getReqId());
+        record.setBidId(response.getBidId());
+        record.setMedia("honor");
+        record.setMediaTraceId(traceId);
+        record.setPlatform(params.get("baidu_platform"));
+        record.setAdId(params.get("creativeId"));
+        record.setAccountId(params.get("advertiserId"));
+        record.setTagId(params.get("tagId"));
+        record.setEventType(eventType);
+        record.setMediaParams(params);
+        record.setCreatedAt(Instant.now());
+        if (response.getBid() != null) {
+            record.setCreativeId(response.getBid().getCrid());
+            record.setImpId(response.getBid().getImpId());
+            record.setTagId(response.getBid().getTagId() != null ? response.getBid().getTagId() : record.getTagId());
+            record.setPrice(response.getBid().getPrice());
+            record.setShowUrls(response.getBid().getShowUrls());
+            record.setClickUrls(response.getBid().getClickUrls());
+            record.setLandingPage(response.getBid().getLandingPage());
+            record.setAppStoreLink(response.getBid().getAppStoreLink());
+            if (response.getBid().getAdm() != null) {
+                record.setPackageName(response.getBid().getAdm().getPackageName());
+            }
+        }
+        return record;
+    }
+
+    private void recordTracking(String qk, String tagId, String kind, List<TrackingResult> results) {
+        if (results == null) return;
+        for (TrackingResult result : results) {
+            HonorTrackingRecord record = new HonorTrackingRecord();
+            record.setQk(qk);
+            record.setTagId(tagId);
+            record.setKind(kind);
+            record.setUrl(result.getUrl());
+            record.setStatus(result.getStatus());
+            record.setOk(result.isOk());
+            record.setCreatedAt(Instant.now());
+            hotStore.recordTracking(record);
+        }
+    }
+
+    private Map<String, String> normalizeParams(HttpServletRequest request) {
+        Map<String, String> params = new HashMap<>();
+        request.getParameterMap().forEach((k, v) -> {
+            if (v != null && v.length > 0 && v[0] != null) params.put(k, v[0]);
+        });
+        copyAlias(params, "tagId", "tagId", "tag_id", "baidu_tag_id");
+        copyAlias(params, "trackId", "trackId", "track_id");
+        copyAlias(params, "advertiserId", "advertiserId", "advertiser_id");
+        copyAlias(params, "requestId", "requestId", "request_id", "REQUESTID");
+        copyAlias(params, "eventTime", "eventTime", "time");
+        copyAlias(params, "pkgName", "pkgName", "pkg_name", "packageName");
+        String os = firstParam(params, "platform", "os");
+        if (os != null) {
+            String platform = HonorPlacement.normalizePlatform(os);
+            if (platform != null) params.put("platform", platform);
+        }
+        return params;
+    }
+
+    private String requireTagId(Map<String, String> params) {
+        String tagId = params.get("tagId");
+        if (tagId == null || tagId.isBlank()) {
+            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "missing tagId");
+        }
+        return tagId;
+    }
+
+    private String honorTraceId(Map<String, String> params) {
+        return firstParam(params, "trackId", "requestId", "oaid");
+    }
+
+    private static void copyAlias(Map<String, String> params, String target, String... aliases) {
+        String value = firstParam(params, aliases);
+        if (value != null) params.put(target, value);
+    }
+
+    private static String firstParam(Map<String, String> p, String... keys) {
+        for (String key : keys) {
+            String value = p.get(key);
+            if (value != null && !value.isBlank()) return value.trim();
+        }
+        return null;
+    }
+
+    private static void putUid(Map<String, Object> uid, String key, String value) {
+        if (value != null && !value.isBlank()) uid.put(key, value);
+    }
+
+    private static String defaultUa(String platform) {
+        if ("ios".equals(platform)) {
+            return "Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1";
+        }
+        return "Mozilla/5.0 (Linux; Android 14; Pixel 8 Build/UQ1A.240205.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.71 Mobile Safari/537.36";
+    }
+
+    private static String clientIp(HttpServletRequest request) {
+        String xff = request.getHeader("X-Forwarded-For");
+        if (xff != null && !xff.isBlank()) {
+            for (String part : xff.split(",")) {
+                String ip = part.trim();
+                if (!ip.isEmpty()) return ip;
+            }
+        }
+        String xri = request.getHeader("X-Real-IP");
+        if (xri != null && !xri.isBlank()) return xri.trim();
+        return request.getRemoteAddr();
+    }
+}

+ 14 - 0
src/main/java/com/adx/tencent/honor/mapper/HonorBidEventMapper.java

@@ -0,0 +1,14 @@
+package com.adx.tencent.honor.mapper;
+
+import com.adx.tencent.honor.model.HonorBidRecord;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface HonorBidEventMapper {
+    HonorBidRecord selectByQk(@Param("qk") String qk);
+    List<HonorBidRecord> selectByQks(@Param("qks") List<String> qks);
+    void insertOrUpdate(@Param("record") HonorBidRecord record);
+}

+ 12 - 0
src/main/java/com/adx/tencent/honor/mapper/HonorConversionMapper.java

@@ -0,0 +1,12 @@
+package com.adx.tencent.honor.mapper;
+
+import com.adx.tencent.honor.model.HonorConversionRecord;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface HonorConversionMapper {
+    void batchInsertOrUpdate(@Param("records") List<HonorConversionRecord> records);
+}

+ 14 - 0
src/main/java/com/adx/tencent/honor/mapper/HonorMediaCallbackMapper.java

@@ -0,0 +1,14 @@
+package com.adx.tencent.honor.mapper;
+
+import com.adx.tencent.honor.model.HonorMediaCallbackRecord;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface HonorMediaCallbackMapper {
+    void batchInsert(@Param("records") List<HonorMediaCallbackRecord> records);
+    List<String> selectSuccessfulDedupeKeys(@Param("dedupeKeys") List<String> dedupeKeys);
+    List<HonorMediaCallbackRecord> selectPendingCallbacks(@Param("limit") int limit);
+}

+ 14 - 0
src/main/java/com/adx/tencent/honor/mapper/HonorTagEventMapper.java

@@ -0,0 +1,14 @@
+package com.adx.tencent.honor.mapper;
+
+import com.adx.tencent.honor.model.HonorTagEventRecord;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface HonorTagEventMapper {
+    HonorTagEventRecord selectByTagIdAndAct(@Param("tagId") String tagId, @Param("baiduAct") int baiduAct);
+
+    List<HonorTagEventRecord> selectAll();
+}

+ 9 - 0
src/main/java/com/adx/tencent/honor/mapper/HonorTrackingReportMapper.java

@@ -0,0 +1,9 @@
+package com.adx.tencent.honor.mapper;
+
+import com.adx.tencent.honor.model.HonorTrackingRecord;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface HonorTrackingReportMapper {
+    void insert(HonorTrackingRecord record);
+}

+ 117 - 0
src/main/java/com/adx/tencent/honor/model/HonorBidRecord.java

@@ -0,0 +1,117 @@
+package com.adx.tencent.honor.model;
+
+import java.time.Instant;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class HonorBidRecord {
+    private String qk;
+    private String media;
+    private String mediaTraceId;
+    private String platform;
+    private String adId;
+    private String accountId;
+    private String eventType;
+    private String reqId;
+    private String bidId;
+    private String creativeId;
+    private String impId;
+    private String tagId;
+    private Long price;
+    private List<String> showUrls;
+    private List<String> clickUrls;
+    private String landingPage;
+    private String appStoreLink;
+    private String packageName;
+    private Map<String, String> mediaParams;
+    private Instant createdAt;
+
+    public String getQk() { return qk; }
+    public void setQk(String v) { this.qk = v; }
+    public String getMedia() { return media; }
+    public void setMedia(String v) { this.media = v; }
+    public String getMediaTraceId() { return mediaTraceId; }
+    public void setMediaTraceId(String v) { this.mediaTraceId = v; }
+    public String getPlatform() { return platform; }
+    public void setPlatform(String v) { this.platform = v; }
+    public String getAdId() { return adId; }
+    public void setAdId(String v) { this.adId = v; }
+    public String getAccountId() { return accountId; }
+    public void setAccountId(String v) { this.accountId = v; }
+    public String getEventType() { return eventType; }
+    public void setEventType(String v) { this.eventType = v; }
+    public String getReqId() { return reqId; }
+    public void setReqId(String v) { this.reqId = v; }
+    public String getBidId() { return bidId; }
+    public void setBidId(String v) { this.bidId = v; }
+    public String getCreativeId() { return creativeId; }
+    public void setCreativeId(String v) { this.creativeId = v; }
+    public String getImpId() { return impId; }
+    public void setImpId(String v) { this.impId = v; }
+    public String getTagId() { return tagId; }
+    public void setTagId(String v) { this.tagId = v; }
+    public Long getPrice() { return price; }
+    public void setPrice(Long v) { this.price = v; }
+    public List<String> getShowUrls() { return showUrls; }
+    public void setShowUrls(List<String> v) { this.showUrls = v; }
+    public List<String> getClickUrls() { return clickUrls; }
+    public void setClickUrls(List<String> v) { this.clickUrls = v; }
+    public String getLandingPage() { return landingPage; }
+    public void setLandingPage(String v) { this.landingPage = v; }
+    public String getAppStoreLink() { return appStoreLink; }
+    public void setAppStoreLink(String v) { this.appStoreLink = v; }
+    public String getPackageName() { return packageName; }
+    public void setPackageName(String v) { this.packageName = v; }
+    public Map<String, String> getMediaParams() { return mediaParams; }
+    public void setMediaParams(Map<String, String> v) { this.mediaParams = v; }
+    public Instant getCreatedAt() { return createdAt; }
+    public void setCreatedAt(Instant v) { this.createdAt = v; }
+    public java.sql.Timestamp getCreatedAtTs() { return createdAt == null ? null : java.sql.Timestamp.from(createdAt); }
+
+    public HonorBidRecord toPostClickRecord() {
+        HonorBidRecord slim = new HonorBidRecord();
+        slim.qk = this.qk;
+        slim.media = this.media;
+        slim.mediaTraceId = this.mediaTraceId;
+        slim.platform = this.platform;
+        slim.tagId = this.tagId;
+        slim.mediaParams = this.mediaParams;
+        slim.packageName = this.packageName;
+        return slim;
+    }
+
+    public HonorBidRecord toHotRecord() {
+        HonorBidRecord hot = new HonorBidRecord();
+        hot.qk = this.qk;
+        hot.media = this.media;
+        hot.mediaTraceId = this.mediaTraceId;
+        hot.platform = this.platform;
+        hot.tagId = this.tagId;
+        hot.adId = this.adId;
+        hot.accountId = this.accountId;
+        hot.clickUrls = this.clickUrls;
+        hot.landingPage = this.landingPage;
+        hot.appStoreLink = this.appStoreLink;
+        hot.packageName = this.packageName;
+        hot.mediaParams = slimDeviceParams(this.mediaParams);
+        return hot;
+    }
+
+    private static Map<String, String> slimDeviceParams(Map<String, String> params) {
+        if (params == null || params.isEmpty()) return null;
+        List<String> keepKeys = List.of(
+                "oaid", "oaid_md5", "hash_oaid",
+                "trackId", "advertiserId", "requestId", "eventTime",
+                "pkgName", "channelId", "tagId", "ua", "ip"
+        );
+        Map<String, String> slim = new LinkedHashMap<>();
+        for (String key : keepKeys) {
+            String value = params.get(key);
+            if (value != null && !value.isBlank()) {
+                slim.put(key, value);
+            }
+        }
+        return slim.isEmpty() ? null : slim;
+    }
+}

+ 53 - 0
src/main/java/com/adx/tencent/honor/model/HonorConversionRecord.java

@@ -0,0 +1,53 @@
+package com.adx.tencent.honor.model;
+
+import java.time.Instant;
+
+public class HonorConversionRecord {
+    private String dedupeKey;
+    private String qk;
+    private String media;
+    private String tagId;
+    private String date;
+    private String appSid;
+    private String customerName;
+    private String deviceId;
+    private double conv;
+    private double payment;
+    private double gmv;
+    private int act;
+    private String tu;
+    private String clkTime;
+    private Instant createdAt;
+
+    public String getDedupeKey() { return dedupeKey; }
+    public void setDedupeKey(String v) { this.dedupeKey = v; }
+    public String getQk() { return qk; }
+    public void setQk(String v) { this.qk = v; }
+    public String getMedia() { return media; }
+    public void setMedia(String v) { this.media = v; }
+    public String getTagId() { return tagId; }
+    public void setTagId(String v) { this.tagId = v; }
+    public String getDate() { return date; }
+    public void setDate(String v) { this.date = v; }
+    public String getAppSid() { return appSid; }
+    public void setAppSid(String v) { this.appSid = v; }
+    public String getCustomerName() { return customerName; }
+    public void setCustomerName(String v) { this.customerName = v; }
+    public String getDeviceId() { return deviceId; }
+    public void setDeviceId(String v) { this.deviceId = v; }
+    public double getConv() { return conv; }
+    public void setConv(double v) { this.conv = v; }
+    public double getPayment() { return payment; }
+    public void setPayment(double v) { this.payment = v; }
+    public double getGmv() { return gmv; }
+    public void setGmv(double v) { this.gmv = v; }
+    public int getAct() { return act; }
+    public void setAct(int v) { this.act = v; }
+    public String getTu() { return tu; }
+    public void setTu(String v) { this.tu = v; }
+    public String getClkTime() { return clkTime; }
+    public void setClkTime(String v) { this.clkTime = v; }
+    public Instant getCreatedAt() { return createdAt; }
+    public void setCreatedAt(Instant v) { this.createdAt = v; }
+    public java.sql.Timestamp getCreatedAtTs() { return createdAt == null ? null : java.sql.Timestamp.from(createdAt); }
+}

+ 50 - 0
src/main/java/com/adx/tencent/honor/model/HonorMediaCallbackRecord.java

@@ -0,0 +1,50 @@
+package com.adx.tencent.honor.model;
+
+import java.time.Instant;
+
+public class HonorMediaCallbackRecord {
+    private String dedupeKey;
+    private String qk;
+    private String tagId;
+    private String callbackUrl;
+    private int eventType;
+    private long eventTimeMs;
+    private double purchase;
+    private int status;
+    private boolean ok;
+    private int attempt;
+    private String responseBody;
+    private String errorMessage;
+    private String requestBody;
+    private Instant createdAt;
+
+    public String getDedupeKey() { return dedupeKey; }
+    public void setDedupeKey(String v) { this.dedupeKey = v; }
+    public String getQk() { return qk; }
+    public void setQk(String v) { this.qk = v; }
+    public String getTagId() { return tagId; }
+    public void setTagId(String v) { this.tagId = v; }
+    public String getCallbackUrl() { return callbackUrl; }
+    public void setCallbackUrl(String v) { this.callbackUrl = v; }
+    public int getEventType() { return eventType; }
+    public void setEventType(int v) { this.eventType = v; }
+    public long getEventTimeMs() { return eventTimeMs; }
+    public void setEventTimeMs(long v) { this.eventTimeMs = v; }
+    public double getPurchase() { return purchase; }
+    public void setPurchase(double v) { this.purchase = v; }
+    public int getStatus() { return status; }
+    public void setStatus(int v) { this.status = v; }
+    public boolean isOk() { return ok; }
+    public void setOk(boolean v) { this.ok = v; }
+    public int getAttempt() { return attempt; }
+    public void setAttempt(int v) { this.attempt = v; }
+    public String getResponseBody() { return responseBody; }
+    public void setResponseBody(String v) { this.responseBody = v; }
+    public String getErrorMessage() { return errorMessage; }
+    public void setErrorMessage(String v) { this.errorMessage = v; }
+    public String getRequestBody() { return requestBody; }
+    public void setRequestBody(String v) { this.requestBody = v; }
+    public Instant getCreatedAt() { return createdAt; }
+    public void setCreatedAt(Instant v) { this.createdAt = v; }
+    public java.sql.Timestamp getCreatedAtTs() { return createdAt == null ? null : java.sql.Timestamp.from(createdAt); }
+}

+ 14 - 0
src/main/java/com/adx/tencent/honor/model/HonorTagEventRecord.java

@@ -0,0 +1,14 @@
+package com.adx.tencent.honor.model;
+
+public class HonorTagEventRecord {
+    private String tagId;
+    private int baiduAct;
+    private int conversionId;
+
+    public String getTagId() { return tagId; }
+    public void setTagId(String v) { this.tagId = v; }
+    public int getBaiduAct() { return baiduAct; }
+    public void setBaiduAct(int v) { this.baiduAct = v; }
+    public int getConversionId() { return conversionId; }
+    public void setConversionId(int v) { this.conversionId = v; }
+}

+ 29 - 0
src/main/java/com/adx/tencent/honor/model/HonorTrackingRecord.java

@@ -0,0 +1,29 @@
+package com.adx.tencent.honor.model;
+
+import java.time.Instant;
+
+public class HonorTrackingRecord {
+    private String qk;
+    private String tagId;
+    private String kind;
+    private String url;
+    private int status;
+    private boolean ok;
+    private Instant createdAt;
+
+    public String getQk() { return qk; }
+    public void setQk(String v) { this.qk = v; }
+    public String getTagId() { return tagId; }
+    public void setTagId(String v) { this.tagId = v; }
+    public String getKind() { return kind; }
+    public void setKind(String v) { this.kind = v; }
+    public String getUrl() { return url; }
+    public void setUrl(String v) { this.url = v; }
+    public int getStatus() { return status; }
+    public void setStatus(int v) { this.status = v; }
+    public boolean isOk() { return ok; }
+    public void setOk(boolean v) { this.ok = v; }
+    public Instant getCreatedAt() { return createdAt; }
+    public void setCreatedAt(Instant v) { this.createdAt = v; }
+    public java.sql.Timestamp getCreatedAtTs() { return createdAt == null ? null : java.sql.Timestamp.from(createdAt); }
+}

+ 18 - 3
src/main/java/com/adx/tencent/storage/TiDBColdStore.java

@@ -6,6 +6,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.ibatis.session.ExecutorType;
 import org.apache.ibatis.session.SqlSession;
 import org.apache.ibatis.session.SqlSessionFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.sql.Statement;
 import java.sql.Timestamp;
@@ -23,6 +25,9 @@ import java.util.List;
  */
 public class TiDBColdStore {
 
+    private static final Logger log = LoggerFactory.getLogger(TiDBColdStore.class);
+    private static final int CONVERSION_BATCH_SIZE = 1000;
+
     private final SqlSessionFactory sqlSessionFactory;
     private final ObjectMapper objectMapper;
 
@@ -251,12 +256,22 @@ public class TiDBColdStore {
 
     public void saveConversions(List<ConversionRecord> records) {
         if (records == null || records.isEmpty()) return;
+        long startedAt = System.currentTimeMillis();
+        Instant now = Instant.now();
         for (ConversionRecord record : records) {
-            if (record.getCreatedAt() == null) record.setCreatedAt(Instant.now());
+            if (record.getCreatedAt() == null) record.setCreatedAt(now);
         }
-        try (SqlSession session = sqlSessionFactory.openSession(true)) {
+        try (SqlSession session = sqlSessionFactory.openSession(false)) {
             ConversionMapper mapper = session.getMapper(ConversionMapper.class);
-            mapper.batchInsertOrUpdate(records);
+            for (int start = 0; start < records.size(); start += CONVERSION_BATCH_SIZE) {
+                int end = Math.min(start + CONVERSION_BATCH_SIZE, records.size());
+                mapper.batchInsertOrUpdate(records.subList(start, end));
+            }
+            session.commit();
+            long costMs = System.currentTimeMillis() - startedAt;
+            int batchCount = (records.size() + CONVERSION_BATCH_SIZE - 1) / CONVERSION_BATCH_SIZE;
+            log.info("[TiDBColdStore] saveConversions done: size={}, batches={}, batchSize={}, cost={}ms",
+                    records.size(), batchCount, CONVERSION_BATCH_SIZE, costMs);
         }
     }
 

+ 84 - 0
src/main/resources/mapper/HonorBidEventMapper.xml

@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.adx.tencent.honor.mapper.HonorBidEventMapper">
+    <resultMap id="honorBidRecordResultMap" type="com.adx.tencent.honor.model.HonorBidRecord">
+        <result property="qk" column="qk"/>
+        <result property="media" column="media"/>
+        <result property="mediaTraceId" column="media_trace_id"/>
+        <result property="platform" column="platform"/>
+        <result property="adId" column="ad_id"/>
+        <result property="accountId" column="account_id"/>
+        <result property="eventType" column="event_type"/>
+        <result property="reqId" column="req_id"/>
+        <result property="bidId" column="bid_id"/>
+        <result property="creativeId" column="creative_id"/>
+        <result property="impId" column="imp_id"/>
+        <result property="tagId" column="tag_id"/>
+        <result property="price" column="price"/>
+        <result property="showUrls" column="show_urls" typeHandler="com.adx.tencent.storage.typehandler.JsonStringListTypeHandler"/>
+        <result property="clickUrls" column="click_urls" typeHandler="com.adx.tencent.storage.typehandler.JsonStringListTypeHandler"/>
+        <result property="landingPage" column="landing_page"/>
+        <result property="appStoreLink" column="app_store_link"/>
+        <result property="packageName" column="package_name"/>
+        <result property="mediaParams" column="media_params" typeHandler="com.adx.tencent.storage.typehandler.JsonStringMapTypeHandler"/>
+        <result property="createdAt" column="created_at"/>
+    </resultMap>
+
+    <select id="selectByQk" resultMap="honorBidRecordResultMap">
+        SELECT qk, media, media_trace_id, platform, ad_id, account_id, event_type,
+               req_id, bid_id, creative_id, imp_id, tag_id, price, show_urls,
+               click_urls, landing_page, app_store_link, package_name, media_params, created_at
+        FROM honor_ad_bid_events
+        WHERE qk = #{qk}
+        LIMIT 1
+    </select>
+
+    <select id="selectByQks" resultMap="honorBidRecordResultMap">
+        SELECT qk, media, media_trace_id, platform, ad_id, account_id, event_type,
+               req_id, bid_id, creative_id, imp_id, tag_id, price, show_urls,
+               click_urls, landing_page, app_store_link, package_name, media_params, created_at
+        FROM honor_ad_bid_events
+        WHERE qk IN
+        <foreach collection="qks" item="qk" open="(" separator="," close=")">
+            #{qk}
+        </foreach>
+    </select>
+
+    <insert id="insertOrUpdate">
+        INSERT INTO honor_ad_bid_events (
+            qk, media, media_trace_id, platform, ad_id, account_id, event_type,
+            req_id, bid_id, creative_id, imp_id, tag_id, price,
+            show_urls, click_urls, landing_page, app_store_link, package_name,
+            media_params, created_at
+        ) VALUES (
+            #{record.qk}, #{record.media}, #{record.mediaTraceId}, #{record.platform},
+            #{record.adId}, #{record.accountId}, #{record.eventType}, #{record.reqId},
+            #{record.bidId}, #{record.creativeId}, #{record.impId}, #{record.tagId},
+            #{record.price}, #{record.showUrls, typeHandler=com.adx.tencent.storage.typehandler.JsonStringListTypeHandler},
+            #{record.clickUrls, typeHandler=com.adx.tencent.storage.typehandler.JsonStringListTypeHandler},
+            #{record.landingPage}, #{record.appStoreLink}, #{record.packageName},
+            #{record.mediaParams, typeHandler=com.adx.tencent.storage.typehandler.JsonStringMapTypeHandler},
+            #{record.createdAtTs}
+        )
+        ON DUPLICATE KEY UPDATE
+            media = VALUES(media),
+            media_trace_id = VALUES(media_trace_id),
+            platform = VALUES(platform),
+            ad_id = VALUES(ad_id),
+            account_id = VALUES(account_id),
+            event_type = VALUES(event_type),
+            req_id = VALUES(req_id),
+            bid_id = VALUES(bid_id),
+            creative_id = VALUES(creative_id),
+            imp_id = VALUES(imp_id),
+            tag_id = VALUES(tag_id),
+            price = VALUES(price),
+            show_urls = VALUES(show_urls),
+            click_urls = VALUES(click_urls),
+            landing_page = VALUES(landing_page),
+            app_store_link = VALUES(app_store_link),
+            package_name = VALUES(package_name),
+            media_params = VALUES(media_params)
+    </insert>
+</mapper>

+ 32 - 0
src/main/resources/mapper/HonorConversionMapper.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.adx.tencent.honor.mapper.HonorConversionMapper">
+    <insert id="batchInsertOrUpdate">
+        INSERT INTO tencent_baidu_conversions (
+            dedupe_key, qk, media, tag_id, date_value, appsid, customer_name, device_id, conv,
+            payment, gmv, act, tu, clk_time, created_at
+        ) VALUES
+        <foreach collection="records" item="r" separator=",">
+            (
+                #{r.dedupeKey}, #{r.qk}, #{r.media}, #{r.tagId}, #{r.date}, #{r.appSid}, #{r.customerName},
+                #{r.deviceId}, #{r.conv}, #{r.payment}, #{r.gmv}, #{r.act}, #{r.tu},
+                #{r.clkTime}, #{r.createdAtTs}
+            )
+        </foreach>
+        ON DUPLICATE KEY UPDATE
+            qk = VALUES(qk),
+            media = CASE
+                WHEN VALUES(media) IS NOT NULL AND VALUES(media) != '' THEN VALUES(media)
+                ELSE media
+            END,
+            tag_id = CASE
+                WHEN VALUES(tag_id) IS NOT NULL AND VALUES(tag_id) != '' THEN VALUES(tag_id)
+                ELSE tag_id
+            END,
+            conv = VALUES(conv),
+            payment = VALUES(payment),
+            gmv = VALUES(gmv),
+            clk_time = VALUES(clk_time)
+    </insert>
+</mapper>

+ 65 - 0
src/main/resources/mapper/HonorMediaCallbackMapper.xml

@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.adx.tencent.honor.mapper.HonorMediaCallbackMapper">
+    <resultMap id="honorMediaCallbackResultMap" type="com.adx.tencent.honor.model.HonorMediaCallbackRecord">
+        <result property="dedupeKey" column="dedupe_key"/>
+        <result property="qk" column="qk"/>
+        <result property="tagId" column="tag_id"/>
+        <result property="callbackUrl" column="callback_url"/>
+        <result property="eventType" column="event_type"/>
+        <result property="eventTimeMs" column="event_time_ms"/>
+        <result property="purchase" column="purchase"/>
+        <result property="status" column="status"/>
+        <result property="ok" column="ok_val"/>
+        <result property="attempt" column="attempt"/>
+        <result property="responseBody" column="response_body"/>
+        <result property="errorMessage" column="error_message"/>
+        <result property="requestBody" column="request_body"/>
+        <result property="createdAt" column="created_at"/>
+    </resultMap>
+
+    <insert id="batchInsert">
+        INSERT INTO honor_media_callbacks (
+            dedupe_key, qk, tag_id, callback_url, event_type, event_time_ms, purchase,
+            status, ok, attempt, response_body, error_message, request_body, created_at
+        ) VALUES
+        <foreach collection="records" item="r" separator=",">
+            (
+                #{r.dedupeKey}, #{r.qk}, #{r.tagId}, #{r.callbackUrl}, #{r.eventType},
+                #{r.eventTimeMs}, #{r.purchase}, #{r.status}, #{r.ok}, #{r.attempt},
+                #{r.responseBody}, #{r.errorMessage}, #{r.requestBody}, #{r.createdAtTs}
+            )
+        </foreach>
+    </insert>
+
+    <select id="selectSuccessfulDedupeKeys" resultType="string">
+        SELECT DISTINCT dedupe_key
+        FROM honor_media_callbacks
+        WHERE ok = 1
+          AND dedupe_key IN
+          <foreach collection="dedupeKeys" item="dedupeKey" open="(" separator="," close=")">
+              #{dedupeKey}
+          </foreach>
+    </select>
+
+    <select id="selectPendingCallbacks" resultMap="honorMediaCallbackResultMap">
+        SELECT m.dedupe_key, m.qk, m.tag_id, m.callback_url, m.event_type, m.event_time_ms,
+               m.purchase, m.status, m.ok AS ok_val, m.attempt, m.response_body, m.error_message,
+               m.request_body, m.created_at
+        FROM honor_media_callbacks m
+        JOIN (
+            SELECT dedupe_key, MAX(id) AS id
+            FROM honor_media_callbacks
+            WHERE dedupe_key IS NOT NULL AND dedupe_key != ''
+            GROUP BY dedupe_key
+        ) latest ON latest.id = m.id
+        WHERE m.ok = 0
+          AND NOT EXISTS (
+            SELECT 1 FROM honor_media_callbacks ok_cb
+            WHERE ok_cb.dedupe_key = m.dedupe_key AND ok_cb.ok = 1
+          )
+        ORDER BY m.created_at ASC
+        LIMIT #{limit}
+    </select>
+</mapper>

+ 21 - 0
src/main/resources/mapper/HonorTagEventMapper.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.adx.tencent.honor.mapper.HonorTagEventMapper">
+    <resultMap id="honorTagEventResultMap" type="com.adx.tencent.honor.model.HonorTagEventRecord">
+        <result property="tagId" column="tag_id"/>
+        <result property="baiduAct" column="baidu_act"/>
+        <result property="conversionId" column="conversion_id"/>
+    </resultMap>
+
+    <select id="selectByTagIdAndAct" resultMap="honorTagEventResultMap">
+        SELECT tag_id, baidu_act, conversion_id
+        FROM honor_tag_event
+        WHERE tag_id = #{tagId} AND baidu_act = #{baiduAct}
+    </select>
+
+    <select id="selectAll" resultMap="honorTagEventResultMap">
+        SELECT tag_id, baidu_act, conversion_id
+        FROM honor_tag_event
+    </select>
+</mapper>

+ 9 - 0
src/main/resources/mapper/HonorTrackingReportMapper.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.adx.tencent.honor.mapper.HonorTrackingReportMapper">
+    <insert id="insert">
+        INSERT INTO honor_tracking_reports (qk, tag_id, kind, tracking_url, status, ok, created_at)
+        VALUES (#{qk}, #{tagId}, #{kind}, #{url}, #{status}, #{ok}, #{createdAtTs})
+    </insert>
+</mapper>

+ 100 - 0
src/main/resources/schema-honor.sql

@@ -0,0 +1,100 @@
+-- ============================================================
+-- honor-baidu-tracking 数据库建表语句
+-- 数据库: adx_honor
+-- 日期: 2026-07-20
+-- ============================================================
+
+CREATE DATABASE IF NOT EXISTS adx_honor DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
+USE adx_honor;
+
+-- ------------------------------------------------------------
+-- 1. 竞价事件表(百度ADX竞价记录)
+-- ------------------------------------------------------------
+CREATE TABLE IF NOT EXISTS honor_ad_bid_events (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    qk VARCHAR(128) NOT NULL COMMENT '唯一业务键(query key)',
+    media VARCHAR(64) NOT NULL COMMENT '媒体来源(honor)',
+    media_trace_id VARCHAR(255) COMMENT '媒体追踪ID(trackId/requestId/oaid)',
+    platform VARCHAR(16) COMMENT '平台(android/ios)',
+    ad_id VARCHAR(128) COMMENT '荣耀创意ID',
+    account_id VARCHAR(128) COMMENT '荣耀广告主ID(advertiserId)',
+    event_type VARCHAR(32) COMMENT '事件类型(impression/click)',
+    req_id VARCHAR(512) COMMENT '百度ADX请求ID',
+    bid_id VARCHAR(128) COMMENT '竞价ID',
+    creative_id VARCHAR(128) COMMENT '创意ID',
+    imp_id VARCHAR(128) COMMENT '曝光ID',
+    tag_id VARCHAR(128) NOT NULL COMMENT '百度广告位ID,对外链接也要求显式传入',
+    price BIGINT UNSIGNED COMMENT '竞价价格(分)',
+    show_urls JSON COMMENT '曝光上报URL列表',
+    click_urls JSON COMMENT '点击上报URL列表',
+    landing_page TEXT COMMENT '落地页URL',
+    app_store_link TEXT COMMENT '应用商店链接',
+    package_name VARCHAR(255) COMMENT '应用包名',
+    media_params JSON COMMENT '荣耀监测参数(trackId/advertiserId/requestId/oaid/time/pkgName/tagId等)',
+    created_at TIMESTAMP(3) NOT NULL COMMENT '创建时间',
+    UNIQUE KEY uk_honor_ad_bid_events_qk (qk),
+    KEY idx_honor_ad_bid_events_media_trace (media, media_trace_id),
+    KEY idx_honor_ad_bid_events_platform_tag (platform, tag_id),
+    KEY idx_honor_ad_bid_events_created_at (created_at)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='荣耀竞价事件记录';
+
+-- ------------------------------------------------------------
+-- 2. 上报结果表(曝光/点击上报百度DSP的结果)
+-- ------------------------------------------------------------
+CREATE TABLE IF NOT EXISTS honor_tracking_reports (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    qk VARCHAR(128) COMMENT '关联的业务键',
+    tag_id VARCHAR(128) NOT NULL COMMENT '百度广告位ID',
+    kind VARCHAR(32) NOT NULL COMMENT '类型(impression/click)',
+    tracking_url TEXT NOT NULL COMMENT '上报的目标URL',
+    status INT NOT NULL COMMENT 'HTTP响应状态码',
+    ok TINYINT(1) NOT NULL COMMENT '是否成功(1=成功,0=失败)',
+    created_at TIMESTAMP(3) NOT NULL COMMENT '创建时间',
+    KEY idx_honor_tracking_reports_qk_kind_created_at (qk, kind, created_at),
+    KEY idx_honor_tracking_reports_tag_created_at (tag_id, created_at)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='荣耀曝光/点击上报结果';
+
+-- ------------------------------------------------------------
+-- 3. 媒体回传记录表(向荣耀回传转化的结果)
+-- ------------------------------------------------------------
+CREATE TABLE IF NOT EXISTS honor_media_callbacks (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    dedupe_key VARCHAR(255) NOT NULL COMMENT '去重键',
+    qk VARCHAR(128) COMMENT '关联的业务键',
+    tag_id VARCHAR(128) NOT NULL COMMENT '百度广告位ID',
+    callback_url TEXT NOT NULL COMMENT '回传的API URL',
+    event_type INT NOT NULL COMMENT '事件类型(百度act)',
+    event_time_ms BIGINT NOT NULL COMMENT '事件时间(毫秒时间戳)',
+    purchase DOUBLE COMMENT '付费金额',
+    status INT NOT NULL COMMENT 'HTTP响应状态码',
+    ok TINYINT(1) NOT NULL COMMENT '是否成功(1=成功,0=失败)',
+    attempt INT NOT NULL DEFAULT 1 COMMENT '尝试次数',
+    response_body TEXT COMMENT '响应体',
+    error_message TEXT COMMENT '错误信息',
+    request_body TEXT COMMENT '请求参数快照',
+    created_at TIMESTAMP(3) NOT NULL COMMENT '创建时间',
+    KEY idx_honor_media_callbacks_dedupe_ok_created_at (dedupe_key, ok, created_at),
+    KEY idx_honor_media_callbacks_qk_created_at (qk, created_at),
+    KEY idx_honor_media_callbacks_tag_created_at (tag_id, created_at)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='荣耀转化回传记录';
+
+-- ------------------------------------------------------------
+-- 4. 广告位回传事件映射表
+-- ------------------------------------------------------------
+CREATE TABLE IF NOT EXISTS honor_tag_event (
+    tag_id VARCHAR(128) NOT NULL COMMENT '百度广告位ID',
+    baidu_act INT NOT NULL COMMENT '百度转化行为',
+    conversion_id INT NOT NULL COMMENT '荣耀 conversionId',
+    PRIMARY KEY (tag_id, baidu_act)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='荣耀广告位转化事件映射';
+
+-- ------------------------------------------------------------
+-- 初始化说明
+-- ------------------------------------------------------------
+-- 1. 对外提供的荣耀监测链接必须显式带上 tagId。
+-- 2. 百度转化明细统一落 tencent_baidu_conversions,使用 media=honor 区分,tag_id 可为空。
+-- 3. honor_tag_event 需要按广告位配置 tag_id + baidu_act -> conversion_id 映射。
+-- 4. Redis 热存储命名空间为:
+--    adx:honor:bid:{qk}
+--    adx:honor:media:honor:{traceId}
+--    adx:honor:events