5 月
This commit is contained in:
parent
577d2b51d7
commit
f684b4db18
@ -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;
|
||||||
|
|||||||
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user