统计数据
This commit is contained in:
parent
d14002251f
commit
c5eaaf4bb1
@ -41,9 +41,10 @@ public class CustomerDashboardDailyCapabilityHandler implements AgentCapabilityH
|
|||||||
学员总数:暂未获取
|
学员总数:暂未获取
|
||||||
今日新增:暂未获取
|
今日新增:暂未获取
|
||||||
跟进成员数:暂未获取
|
跟进成员数:暂未获取
|
||||||
|
统计管理:暂未获取
|
||||||
今日收款:暂未接入
|
今日收款:暂未接入
|
||||||
""".trim(),
|
""".trim(),
|
||||||
List.of("企业微信学员联系 API"),
|
List.of("企业微信学员联系 API", "企业微信统计管理 API"),
|
||||||
Map.of("error", e.getMessage() == null ? "api forbidden" : e.getMessage()),
|
Map.of("error", e.getMessage() == null ? "api forbidden" : e.getMessage()),
|
||||||
Instant.now()
|
Instant.now()
|
||||||
);
|
);
|
||||||
@ -60,15 +61,43 @@ public class CustomerDashboardDailyCapabilityHandler implements AgentCapabilityH
|
|||||||
学员总数:%d
|
学员总数:%d
|
||||||
今日新增:%d
|
今日新增:%d
|
||||||
跟进成员数:%d
|
跟进成员数:%d
|
||||||
|
今日咨询数:%d
|
||||||
|
今日聊天数:%d
|
||||||
|
今日发送消息数:%d
|
||||||
|
今日回复率:%s
|
||||||
|
平均首次回复时长:%s
|
||||||
|
今日负反馈数:%d
|
||||||
今日收款:暂未接入
|
今日收款:暂未接入
|
||||||
""".formatted(stats.totalStudents(), stats.todayNewStudents(), stats.followUserCount()).trim(),
|
""".formatted(
|
||||||
List.of("企业微信学员联系 API"),
|
stats.totalStudents(),
|
||||||
|
stats.todayNewStudents(),
|
||||||
|
stats.followUserCount(),
|
||||||
|
stats.todayNewApplyCount(),
|
||||||
|
stats.todayChatCount(),
|
||||||
|
stats.todayMessageCount(),
|
||||||
|
percent(stats.todayReplyPercentage()),
|
||||||
|
seconds(stats.todayAvgReplySeconds()),
|
||||||
|
stats.todayNegativeFeedbackCount()
|
||||||
|
).trim(),
|
||||||
|
List.of("企业微信学员联系 API", "企业微信统计管理 API"),
|
||||||
Map.of(
|
Map.of(
|
||||||
"totalStudents", stats.totalStudents(),
|
"totalStudents", stats.totalStudents(),
|
||||||
"todayNewStudents", stats.todayNewStudents(),
|
"todayNewStudents", stats.todayNewStudents(),
|
||||||
"followUserCount", stats.followUserCount()
|
"followUserCount", stats.followUserCount(),
|
||||||
|
"todayNewApplyCount", stats.todayNewApplyCount(),
|
||||||
|
"todayChatCount", stats.todayChatCount(),
|
||||||
|
"todayMessageCount", stats.todayMessageCount(),
|
||||||
|
"todayNegativeFeedbackCount", stats.todayNegativeFeedbackCount()
|
||||||
),
|
),
|
||||||
Instant.now()
|
Instant.now()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String percent(Integer value) {
|
||||||
|
return value == null ? "暂无数据" : value + "%";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String seconds(Integer value) {
|
||||||
|
return value == null ? "暂无数据" : value + "秒";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -89,6 +89,26 @@ public class WecomClient {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WecomUserBehaviorDataResponse getStudentContactBehaviorData(List<String> userIds, long startTime, long endTime) {
|
||||||
|
String token = getAccessToken(studentContactSecret());
|
||||||
|
WecomUserBehaviorDataRequest request = new WecomUserBehaviorDataRequest(userIds, startTime, endTime);
|
||||||
|
WecomUserBehaviorDataResponse response = webClient.post()
|
||||||
|
.uri(uriBuilder -> uriBuilder.path("/cgi-bin/externalcontact/get_user_behavior_data")
|
||||||
|
.queryParam("access_token", token)
|
||||||
|
.build())
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.bodyValue(request)
|
||||||
|
.retrieve()
|
||||||
|
.bodyToMono(WecomUserBehaviorDataResponse.class)
|
||||||
|
.block();
|
||||||
|
|
||||||
|
if (response == null || !response.success()) {
|
||||||
|
log.error("wecom student contact behavior data failed err={}", response == null ? "null" : response.errmsg());
|
||||||
|
throw new IllegalStateException("WeCom student contact behavior data failed: " + (response == null ? "null" : response.errmsg()));
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
private String getAccessToken() {
|
private String getAccessToken() {
|
||||||
if (cachedToken != null && Instant.now().isBefore(tokenExpireAt)) {
|
if (cachedToken != null && Instant.now().isBefore(tokenExpireAt)) {
|
||||||
log.info("wecom gettoken use cached token expireAt={}", tokenExpireAt);
|
log.info("wecom gettoken use cached token expireAt={}", tokenExpireAt);
|
||||||
|
|||||||
@ -3,5 +3,11 @@ package com.company.agentfoundation.integration.wecom;
|
|||||||
public record WecomStudentContactStats(
|
public record WecomStudentContactStats(
|
||||||
int totalStudents,
|
int totalStudents,
|
||||||
int todayNewStudents,
|
int todayNewStudents,
|
||||||
int followUserCount
|
int followUserCount,
|
||||||
|
int todayNewApplyCount,
|
||||||
|
int todayChatCount,
|
||||||
|
int todayMessageCount,
|
||||||
|
int todayNegativeFeedbackCount,
|
||||||
|
Integer todayReplyPercentage,
|
||||||
|
Integer todayAvgReplySeconds
|
||||||
) {}
|
) {}
|
||||||
|
|||||||
@ -24,9 +24,11 @@ public class WecomStudentContactStatsService {
|
|||||||
public WecomStudentContactStats fetchTodayStats() {
|
public WecomStudentContactStats fetchTodayStats() {
|
||||||
List<String> followUsers = wecomClient.getStudentContactFollowUsers().followUser();
|
List<String> followUsers = wecomClient.getStudentContactFollowUsers().followUser();
|
||||||
if (followUsers == null || followUsers.isEmpty()) {
|
if (followUsers == null || followUsers.isEmpty()) {
|
||||||
return new WecomStudentContactStats(0, 0, 0);
|
return new WecomStudentContactStats(0, 0, 0, 0, 0, 0, 0, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long todayStart = LocalDate.now(CHINA_ZONE).atStartOfDay(CHINA_ZONE).toEpochSecond();
|
||||||
|
long tomorrowStart = LocalDate.now(CHINA_ZONE).plusDays(1).atStartOfDay(CHINA_ZONE).toEpochSecond();
|
||||||
Set<String> allStudents = new HashSet<>();
|
Set<String> allStudents = new HashSet<>();
|
||||||
Map<String, Long> earliestCreateTimeByStudent = new HashMap<>();
|
Map<String, Long> earliestCreateTimeByStudent = new HashMap<>();
|
||||||
for (int start = 0; start < followUsers.size(); start += BATCH_SIZE) {
|
for (int start = 0; start < followUsers.size(); start += BATCH_SIZE) {
|
||||||
@ -34,13 +36,22 @@ public class WecomStudentContactStatsService {
|
|||||||
fetchBatch(batchUsers, allStudents, earliestCreateTimeByStudent);
|
fetchBatch(batchUsers, allStudents, earliestCreateTimeByStudent);
|
||||||
}
|
}
|
||||||
|
|
||||||
long todayStart = LocalDate.now(CHINA_ZONE).atStartOfDay(CHINA_ZONE).toEpochSecond();
|
|
||||||
long tomorrowStart = LocalDate.now(CHINA_ZONE).plusDays(1).atStartOfDay(CHINA_ZONE).toEpochSecond();
|
|
||||||
int todayNew = (int) earliestCreateTimeByStudent.values().stream()
|
int todayNew = (int) earliestCreateTimeByStudent.values().stream()
|
||||||
.filter(createTime -> createTime != null && createTime >= todayStart && createTime < tomorrowStart)
|
.filter(createTime -> createTime != null && createTime >= todayStart && createTime < tomorrowStart)
|
||||||
.count();
|
.count();
|
||||||
|
BehaviorSummary behaviorSummary = fetchBehaviorSummary(followUsers, todayStart, tomorrowStart - 1);
|
||||||
|
|
||||||
return new WecomStudentContactStats(allStudents.size(), todayNew, followUsers.size());
|
return new WecomStudentContactStats(
|
||||||
|
allStudents.size(),
|
||||||
|
todayNew,
|
||||||
|
followUsers.size(),
|
||||||
|
behaviorSummary.newApplyCount(),
|
||||||
|
behaviorSummary.chatCount(),
|
||||||
|
behaviorSummary.messageCount(),
|
||||||
|
behaviorSummary.negativeFeedbackCount(),
|
||||||
|
behaviorSummary.replyPercentage(),
|
||||||
|
behaviorSummary.avgReplySeconds()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchBatch(List<String> userIds, Set<String> allStudents, Map<String, Long> earliestCreateTimeByStudent) {
|
private void fetchBatch(List<String> userIds, Set<String> allStudents, Map<String, Long> earliestCreateTimeByStudent) {
|
||||||
@ -68,4 +79,60 @@ public class WecomStudentContactStatsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BehaviorSummary fetchBehaviorSummary(List<String> followUsers, long startTime, long endTime) {
|
||||||
|
int newApplyCount = 0;
|
||||||
|
int chatCount = 0;
|
||||||
|
int messageCount = 0;
|
||||||
|
int negativeFeedbackCount = 0;
|
||||||
|
int replyPercentageWeightedSum = 0;
|
||||||
|
int replyPercentageWeight = 0;
|
||||||
|
int avgReplyTimeWeightedSum = 0;
|
||||||
|
int avgReplyTimeWeight = 0;
|
||||||
|
|
||||||
|
for (int start = 0; start < followUsers.size(); start += BATCH_SIZE) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
for (WecomUserBehaviorDataResponse.BehaviorData item : behaviorData) {
|
||||||
|
if (item == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int itemChatCount = value(item.chatCnt());
|
||||||
|
int itemMessageCount = value(item.messageCnt());
|
||||||
|
newApplyCount += value(item.newApplyCnt());
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer replyPercentage = replyPercentageWeight == 0 ? null : replyPercentageWeightedSum / replyPercentageWeight;
|
||||||
|
Integer avgReplySeconds = avgReplyTimeWeight == 0 ? null : avgReplyTimeWeightedSum / avgReplyTimeWeight;
|
||||||
|
return new BehaviorSummary(newApplyCount, chatCount, messageCount, negativeFeedbackCount, replyPercentage, avgReplySeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int value(Integer value) {
|
||||||
|
return value == null ? 0 : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private record BehaviorSummary(
|
||||||
|
int newApplyCount,
|
||||||
|
int chatCount,
|
||||||
|
int messageCount,
|
||||||
|
int negativeFeedbackCount,
|
||||||
|
Integer replyPercentage,
|
||||||
|
Integer avgReplySeconds
|
||||||
|
) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.company.agentfoundation.integration.wecom;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record WecomUserBehaviorDataRequest(
|
||||||
|
@JsonProperty("userid") List<String> userIds,
|
||||||
|
@JsonProperty("start_time") Long startTime,
|
||||||
|
@JsonProperty("end_time") Long endTime
|
||||||
|
) {}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package com.company.agentfoundation.integration.wecom;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record WecomUserBehaviorDataResponse(
|
||||||
|
Integer errcode,
|
||||||
|
String errmsg,
|
||||||
|
@JsonProperty("behavior_data") List<BehaviorData> behaviorData
|
||||||
|
) {
|
||||||
|
public boolean success() {
|
||||||
|
return errcode == null || errcode == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public record BehaviorData(
|
||||||
|
String statTime,
|
||||||
|
@JsonProperty("chat_cnt") Integer chatCnt,
|
||||||
|
@JsonProperty("message_cnt") Integer messageCnt,
|
||||||
|
@JsonProperty("reply_percentage") Integer replyPercentage,
|
||||||
|
@JsonProperty("avg_reply_time") Integer avgReplyTime,
|
||||||
|
@JsonProperty("negative_feedback_cnt") Integer negativeFeedbackCnt,
|
||||||
|
@JsonProperty("new_apply_cnt") Integer newApplyCnt,
|
||||||
|
@JsonProperty("new_contact_cnt") Integer newContactCnt
|
||||||
|
) {}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user