diff --git a/src/main/java/com/company/agentfoundation/integration/wecom/service/WecomStudentContactStatsService.java b/src/main/java/com/company/agentfoundation/integration/wecom/service/WecomStudentContactStatsService.java index 9095f81..fa0b96e 100644 --- a/src/main/java/com/company/agentfoundation/integration/wecom/service/WecomStudentContactStatsService.java +++ b/src/main/java/com/company/agentfoundation/integration/wecom/service/WecomStudentContactStatsService.java @@ -25,6 +25,7 @@ public class WecomStudentContactStatsService { private static final Logger log = LoggerFactory.getLogger(WecomStudentContactStatsService.class); private static final ZoneId CHINA_ZONE = ZoneId.of("Asia/Shanghai"); private static final int BATCH_SIZE = 100; + private static final int API_WINDOW_DAYS = 30; private final WecomClient wecomClient; @@ -59,8 +60,8 @@ public class WecomStudentContactStatsService { int rangeNew = (int) earliestCreateTimeByStudent.values().stream() .filter(createTime -> createTime != null && createTime >= rangeStart && createTime < rangeEndExclusive) .count(); - BehaviorSummary behaviorSummary = fetchBehaviorSummary(followUsers, rangeStart, rangeEndExclusive - 1); - GroupMessageSummary groupMessageSummary = fetchGroupMessageSummary(rangeStart, rangeEndExclusive - 1); + BehaviorSummary behaviorSummary = fetchBehaviorSummary(followUsers, startDate, endDate); + GroupMessageSummary groupMessageSummary = fetchGroupMessageSummary(startDate, endDate); return new WecomStudentContactStats( allStudents.size(), @@ -107,7 +108,7 @@ public class WecomStudentContactStatsService { } } - private BehaviorSummary fetchBehaviorSummary(List followUsers, long startTime, long endTime) { + private BehaviorSummary fetchBehaviorSummary(List followUsers, LocalDate startDate, LocalDate endDate) { int newApplyCount = 0; int newContactCount = 0; int chatCount = 0; @@ -118,33 +119,41 @@ public class WecomStudentContactStatsService { long avgReplyTimeWeightedSum = 0L; long avgReplyTimeWeight = 0L; - for (int start = 0; start < followUsers.size(); start += BATCH_SIZE) { - List batchUsers = followUsers.subList(start, Math.min(start + BATCH_SIZE, followUsers.size())); - WecomUserBehaviorDataResponse response = wecomClient.getStudentContactBehaviorData(batchUsers, startTime, endTime); - List behaviorData = response.behaviorData(); - if (behaviorData == null) { - continue; - } - for (WecomUserBehaviorDataResponse.BehaviorData item : behaviorData) { - if (item == null) { + LocalDate windowStart = startDate; + while (!windowStart.isAfter(endDate)) { + LocalDate windowEnd = windowStart.plusDays(API_WINDOW_DAYS - 1L); + if (windowEnd.isAfter(endDate)) windowEnd = endDate; + long startTime = windowStart.atStartOfDay(CHINA_ZONE).toEpochSecond(); + long endTime = windowEnd.plusDays(1).atStartOfDay(CHINA_ZONE).toEpochSecond() - 1; + + for (int start = 0; start < followUsers.size(); start += BATCH_SIZE) { + List batchUsers = followUsers.subList(start, Math.min(start + BATCH_SIZE, followUsers.size())); + WecomUserBehaviorDataResponse response = + wecomClient.getStudentContactBehaviorData(batchUsers, startTime, endTime); + List behaviorData = response.behaviorData(); + if (behaviorData == null) { continue; } - int itemChatCount = value(item.chatCnt()); - int itemMessageCount = value(item.messageCnt()); - newApplyCount += value(item.newApplyCnt()); - newContactCount += value(item.newContactCnt()); - chatCount += itemChatCount; - messageCount += itemMessageCount; - negativeFeedbackCount += value(item.negativeFeedbackCnt()); - if (item.replyPercentage() != null && itemChatCount > 0) { - replyPercentageWeightedSum += item.replyPercentage() * itemChatCount; - replyPercentageWeight += itemChatCount; - } - if (item.avgReplyTime() != null && itemMessageCount > 0) { - avgReplyTimeWeightedSum += item.avgReplyTime() * itemMessageCount; - avgReplyTimeWeight += itemMessageCount; + for (WecomUserBehaviorDataResponse.BehaviorData item : behaviorData) { + if (item == null) continue; + int itemChatCount = value(item.chatCnt()); + int itemMessageCount = value(item.messageCnt()); + newApplyCount += value(item.newApplyCnt()); + newContactCount += value(item.newContactCnt()); + chatCount += itemChatCount; + messageCount += itemMessageCount; + negativeFeedbackCount += value(item.negativeFeedbackCnt()); + if (item.replyPercentage() != null && itemChatCount > 0) { + replyPercentageWeightedSum += item.replyPercentage() * itemChatCount; + replyPercentageWeight += itemChatCount; + } + if (item.avgReplyTime() != null && itemMessageCount > 0) { + avgReplyTimeWeightedSum += item.avgReplyTime() * itemMessageCount; + avgReplyTimeWeight += itemMessageCount; + } } } + windowStart = windowEnd.plusDays(1); } Integer replyPercentage = replyPercentageWeight == 0 ? null @@ -155,21 +164,29 @@ public class WecomStudentContactStatsService { negativeFeedbackCount, replyPercentage, avgReplySeconds); } - private GroupMessageSummary fetchGroupMessageSummary(long startTime, long endTime) { + private GroupMessageSummary fetchGroupMessageSummary(LocalDate startDate, LocalDate endDate) { try { Set messageIds = new HashSet<>(); - String cursor = ""; - while (true) { - WecomGroupMessageListResponse response = - wecomClient.getCustomerGroupMessages(startTime, endTime, cursor, BATCH_SIZE); - if (response.groupMessageList() != null) { - response.groupMessageList().stream() - .filter(item -> item != null && item.msgid() != null && !item.msgid().isBlank()) - .map(WecomGroupMessageListResponse.GroupMessage::msgid) - .forEach(messageIds::add); + LocalDate windowStart = startDate; + while (!windowStart.isAfter(endDate)) { + LocalDate windowEnd = windowStart.plusDays(API_WINDOW_DAYS - 1L); + if (windowEnd.isAfter(endDate)) windowEnd = endDate; + long startTime = windowStart.atStartOfDay(CHINA_ZONE).toEpochSecond(); + long endTime = windowEnd.plusDays(1).atStartOfDay(CHINA_ZONE).toEpochSecond() - 1; + String cursor = ""; + while (true) { + WecomGroupMessageListResponse response = + wecomClient.getCustomerGroupMessages(startTime, endTime, cursor, BATCH_SIZE); + if (response.groupMessageList() != null) { + response.groupMessageList().stream() + .filter(item -> item != null && item.msgid() != null && !item.msgid().isBlank()) + .map(WecomGroupMessageListResponse.GroupMessage::msgid) + .forEach(messageIds::add); + } + cursor = response.nextCursor(); + if (cursor == null || cursor.isBlank()) break; } - cursor = response.nextCursor(); - if (cursor == null || cursor.isBlank()) break; + windowStart = windowEnd.plusDays(1); } int targets = 0; diff --git a/src/test/java/com/company/agentfoundation/integration/wecom/service/WecomStudentContactStatsServiceTest.java b/src/test/java/com/company/agentfoundation/integration/wecom/service/WecomStudentContactStatsServiceTest.java index b318e9c..8dda32c 100644 --- a/src/test/java/com/company/agentfoundation/integration/wecom/service/WecomStudentContactStatsServiceTest.java +++ b/src/test/java/com/company/agentfoundation/integration/wecom/service/WecomStudentContactStatsServiceTest.java @@ -21,6 +21,8 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class WecomStudentContactStatsServiceTest { @@ -67,4 +69,23 @@ class WecomStudentContactStatsServiceTest { assertEquals(0.3333D, stats.groupReachRate()); assertTrue(stats.groupStatsAvailable()); } + + @Test + void splitsThirtyOneDayMonthIntoTwoApiWindows() { + WecomClient client = mock(WecomClient.class); + when(client.getStudentContactFollowUsers()) + .thenReturn(new WecomFollowUserListResponse(0, "ok", List.of("employee-1"))); + when(client.batchGetStudentContacts(eq(List.of("employee-1")), anyString(), anyInt())) + .thenReturn(new WecomExternalContactBatchResponse(0, "ok", List.of(), "")); + when(client.getStudentContactBehaviorData(eq(List.of("employee-1")), anyLong(), anyLong())) + .thenReturn(new WecomUserBehaviorDataResponse(0, "ok", List.of())); + when(client.getCustomerGroupMessages(anyLong(), anyLong(), anyString(), anyInt())) + .thenReturn(new WecomGroupMessageListResponse(0, "ok", List.of(), "")); + + new WecomStudentContactStatsService(client).fetchStats( + LocalDate.of(2026, 5, 1), LocalDate.of(2026, 5, 31)); + + verify(client, times(2)).getStudentContactBehaviorData(eq(List.of("employee-1")), anyLong(), anyLong()); + verify(client, times(2)).getCustomerGroupMessages(anyLong(), anyLong(), anyString(), anyInt()); + } }