This commit is contained in:
tanglong 2026-07-21 16:30:18 +08:00
parent 577d2b51d7
commit f684b4db18
2 changed files with 76 additions and 38 deletions

View File

@ -25,6 +25,7 @@ public class WecomStudentContactStatsService {
private static final Logger log = LoggerFactory.getLogger(WecomStudentContactStatsService.class); private static final Logger log = LoggerFactory.getLogger(WecomStudentContactStatsService.class);
private static final ZoneId CHINA_ZONE = ZoneId.of("Asia/Shanghai"); private static final ZoneId CHINA_ZONE = ZoneId.of("Asia/Shanghai");
private static final int BATCH_SIZE = 100; private static final int BATCH_SIZE = 100;
private static final int API_WINDOW_DAYS = 30;
private final WecomClient wecomClient; private final WecomClient wecomClient;
@ -59,8 +60,8 @@ public class WecomStudentContactStatsService {
int rangeNew = (int) earliestCreateTimeByStudent.values().stream() int rangeNew = (int) earliestCreateTimeByStudent.values().stream()
.filter(createTime -> createTime != null && createTime >= rangeStart && createTime < rangeEndExclusive) .filter(createTime -> createTime != null && createTime >= rangeStart && createTime < rangeEndExclusive)
.count(); .count();
BehaviorSummary behaviorSummary = fetchBehaviorSummary(followUsers, rangeStart, rangeEndExclusive - 1); BehaviorSummary behaviorSummary = fetchBehaviorSummary(followUsers, startDate, endDate);
GroupMessageSummary groupMessageSummary = fetchGroupMessageSummary(rangeStart, rangeEndExclusive - 1); GroupMessageSummary groupMessageSummary = fetchGroupMessageSummary(startDate, endDate);
return new WecomStudentContactStats( return new WecomStudentContactStats(
allStudents.size(), allStudents.size(),
@ -107,7 +108,7 @@ public class WecomStudentContactStatsService {
} }
} }
private BehaviorSummary fetchBehaviorSummary(List<String> followUsers, long startTime, long endTime) { private BehaviorSummary fetchBehaviorSummary(List<String> followUsers, LocalDate startDate, LocalDate endDate) {
int newApplyCount = 0; int newApplyCount = 0;
int newContactCount = 0; int newContactCount = 0;
int chatCount = 0; int chatCount = 0;
@ -118,33 +119,41 @@ public class WecomStudentContactStatsService {
long avgReplyTimeWeightedSum = 0L; long avgReplyTimeWeightedSum = 0L;
long avgReplyTimeWeight = 0L; long avgReplyTimeWeight = 0L;
for (int start = 0; start < followUsers.size(); start += BATCH_SIZE) { LocalDate windowStart = startDate;
List<String> batchUsers = followUsers.subList(start, Math.min(start + BATCH_SIZE, followUsers.size())); while (!windowStart.isAfter(endDate)) {
WecomUserBehaviorDataResponse response = wecomClient.getStudentContactBehaviorData(batchUsers, startTime, endTime); LocalDate windowEnd = windowStart.plusDays(API_WINDOW_DAYS - 1L);
List<WecomUserBehaviorDataResponse.BehaviorData> behaviorData = response.behaviorData(); if (windowEnd.isAfter(endDate)) windowEnd = endDate;
if (behaviorData == null) { long startTime = windowStart.atStartOfDay(CHINA_ZONE).toEpochSecond();
continue; long endTime = windowEnd.plusDays(1).atStartOfDay(CHINA_ZONE).toEpochSecond() - 1;
}
for (WecomUserBehaviorDataResponse.BehaviorData item : behaviorData) { for (int start = 0; start < followUsers.size(); start += BATCH_SIZE) {
if (item == null) { List<String> batchUsers = followUsers.subList(start, Math.min(start + BATCH_SIZE, followUsers.size()));
WecomUserBehaviorDataResponse response =
wecomClient.getStudentContactBehaviorData(batchUsers, startTime, endTime);
List<WecomUserBehaviorDataResponse.BehaviorData> behaviorData = response.behaviorData();
if (behaviorData == null) {
continue; continue;
} }
int itemChatCount = value(item.chatCnt()); for (WecomUserBehaviorDataResponse.BehaviorData item : behaviorData) {
int itemMessageCount = value(item.messageCnt()); if (item == null) continue;
newApplyCount += value(item.newApplyCnt()); int itemChatCount = value(item.chatCnt());
newContactCount += value(item.newContactCnt()); int itemMessageCount = value(item.messageCnt());
chatCount += itemChatCount; newApplyCount += value(item.newApplyCnt());
messageCount += itemMessageCount; newContactCount += value(item.newContactCnt());
negativeFeedbackCount += value(item.negativeFeedbackCnt()); chatCount += itemChatCount;
if (item.replyPercentage() != null && itemChatCount > 0) { messageCount += itemMessageCount;
replyPercentageWeightedSum += item.replyPercentage() * itemChatCount; negativeFeedbackCount += value(item.negativeFeedbackCnt());
replyPercentageWeight += itemChatCount; if (item.replyPercentage() != null && itemChatCount > 0) {
} replyPercentageWeightedSum += item.replyPercentage() * itemChatCount;
if (item.avgReplyTime() != null && itemMessageCount > 0) { replyPercentageWeight += itemChatCount;
avgReplyTimeWeightedSum += item.avgReplyTime() * itemMessageCount; }
avgReplyTimeWeight += itemMessageCount; if (item.avgReplyTime() != null && itemMessageCount > 0) {
avgReplyTimeWeightedSum += item.avgReplyTime() * itemMessageCount;
avgReplyTimeWeight += itemMessageCount;
}
} }
} }
windowStart = windowEnd.plusDays(1);
} }
Integer replyPercentage = replyPercentageWeight == 0 ? null Integer replyPercentage = replyPercentageWeight == 0 ? null
@ -155,21 +164,29 @@ public class WecomStudentContactStatsService {
negativeFeedbackCount, replyPercentage, avgReplySeconds); negativeFeedbackCount, replyPercentage, avgReplySeconds);
} }
private GroupMessageSummary fetchGroupMessageSummary(long startTime, long endTime) { private GroupMessageSummary fetchGroupMessageSummary(LocalDate startDate, LocalDate endDate) {
try { try {
Set<String> messageIds = new HashSet<>(); Set<String> messageIds = new HashSet<>();
String cursor = ""; LocalDate windowStart = startDate;
while (true) { while (!windowStart.isAfter(endDate)) {
WecomGroupMessageListResponse response = LocalDate windowEnd = windowStart.plusDays(API_WINDOW_DAYS - 1L);
wecomClient.getCustomerGroupMessages(startTime, endTime, cursor, BATCH_SIZE); if (windowEnd.isAfter(endDate)) windowEnd = endDate;
if (response.groupMessageList() != null) { long startTime = windowStart.atStartOfDay(CHINA_ZONE).toEpochSecond();
response.groupMessageList().stream() long endTime = windowEnd.plusDays(1).atStartOfDay(CHINA_ZONE).toEpochSecond() - 1;
.filter(item -> item != null && item.msgid() != null && !item.msgid().isBlank()) String cursor = "";
.map(WecomGroupMessageListResponse.GroupMessage::msgid) while (true) {
.forEach(messageIds::add); 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(); windowStart = windowEnd.plusDays(1);
if (cursor == null || cursor.isBlank()) break;
} }
int targets = 0; int targets = 0;

View File

@ -21,6 +21,8 @@ import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
class WecomStudentContactStatsServiceTest { class WecomStudentContactStatsServiceTest {
@ -67,4 +69,23 @@ class WecomStudentContactStatsServiceTest {
assertEquals(0.3333D, stats.groupReachRate()); assertEquals(0.3333D, stats.groupReachRate());
assertTrue(stats.groupStatsAvailable()); 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());
}
} }