客户日报

This commit is contained in:
tanglong 2026-07-21 16:13:17 +08:00
parent 52f46aabdc
commit 0d813d73f0
4 changed files with 74 additions and 12 deletions

View File

@ -101,6 +101,7 @@ public class FoundationProperties {
private boolean enabled; private boolean enabled;
private String corpId; private String corpId;
private String defaultApp = "general"; private String defaultApp = "general";
private String studentContactApp;
private String studentContactSecret; private String studentContactSecret;
private String token; private String token;
private String encodingAesKey; private String encodingAesKey;
@ -112,6 +113,8 @@ public class FoundationProperties {
public void setCorpId(String corpId) { this.corpId = corpId; } public void setCorpId(String corpId) { this.corpId = corpId; }
public String getDefaultApp() { return defaultApp; } public String getDefaultApp() { return defaultApp; }
public void setDefaultApp(String defaultApp) { this.defaultApp = defaultApp; } public void setDefaultApp(String defaultApp) { this.defaultApp = defaultApp; }
public String getStudentContactApp() { return studentContactApp; }
public void setStudentContactApp(String studentContactApp) { this.studentContactApp = studentContactApp; }
public String getStudentContactSecret() { return studentContactSecret; } public String getStudentContactSecret() { return studentContactSecret; }
public void setStudentContactSecret(String studentContactSecret) { this.studentContactSecret = studentContactSecret; } public void setStudentContactSecret(String studentContactSecret) { this.studentContactSecret = studentContactSecret; }
public String getToken() { return token; } public String getToken() { return token; }

View File

@ -105,8 +105,9 @@ public class WecomClient {
.block(); .block();
if (response == null || !response.success()) { if (response == null || !response.success()) {
log.error("wecom student contact get follow users failed err={}", response == null ? "null" : response.errmsg()); String error = apiError(response == null ? null : response.errcode(), response == null ? null : response.errmsg());
throw new IllegalStateException("WeCom student contact get follow users failed: " + (response == null ? "null" : response.errmsg())); log.error("wecom student contact get follow users failed {}", error);
throw new IllegalStateException("WeCom student contact get follow users failed: " + error);
} }
return response; return response;
} }
@ -125,8 +126,9 @@ public class WecomClient {
.block(); .block();
if (response == null || !response.success()) { if (response == null || !response.success()) {
log.error("wecom student contact batch get failed err={}", response == null ? "null" : response.errmsg()); String error = apiError(response == null ? null : response.errcode(), response == null ? null : response.errmsg());
throw new IllegalStateException("WeCom student contact batch get failed: " + (response == null ? "null" : response.errmsg())); log.error("wecom student contact batch get failed {}", error);
throw new IllegalStateException("WeCom student contact batch get failed: " + error);
} }
return response; return response;
} }
@ -145,8 +147,9 @@ public class WecomClient {
.block(); .block();
if (response == null || !response.success()) { if (response == null || !response.success()) {
log.error("wecom student contact behavior data failed err={}", response == null ? "null" : response.errmsg()); String error = apiError(response == null ? null : response.errcode(), response == null ? null : response.errmsg());
throw new IllegalStateException("WeCom student contact behavior data failed: " + (response == null ? "null" : response.errmsg())); log.error("wecom student contact behavior data failed {}", error);
throw new IllegalStateException("WeCom student contact behavior data failed: " + error);
} }
return response; return response;
} }
@ -194,19 +197,24 @@ public class WecomClient {
boolean success = response != null; boolean success = response != null;
String error = "null"; String error = "null";
Integer errorCode = null;
if (response instanceof WecomGroupMessageListResponse value) { if (response instanceof WecomGroupMessageListResponse value) {
success = value.success(); success = value.success();
error = value.errmsg(); error = value.errmsg();
errorCode = value.errcode();
} else if (response instanceof WecomGroupMessageTaskResponse value) { } else if (response instanceof WecomGroupMessageTaskResponse value) {
success = value.success(); success = value.success();
error = value.errmsg(); error = value.errmsg();
errorCode = value.errcode();
} else if (response instanceof WecomGroupMessageSendResultResponse value) { } else if (response instanceof WecomGroupMessageSendResultResponse value) {
success = value.success(); success = value.success();
error = value.errmsg(); error = value.errmsg();
errorCode = value.errcode();
} }
if (!success) { if (!success) {
log.error("wecom student contact {} failed err={}", operation, error); String apiError = apiError(errorCode, error);
throw new IllegalStateException("WeCom " + operation + " failed: " + error); log.error("wecom student contact {} failed {}", operation, apiError);
throw new IllegalStateException("WeCom " + operation + " failed: " + apiError);
} }
return response; return response;
} }
@ -229,8 +237,9 @@ public class WecomClient {
.block(); .block();
if (response == null || !response.success() || response.accessToken() == null) { if (response == null || !response.success() || response.accessToken() == null) {
log.error("wecom gettoken failed err={}", response == null ? "null" : response.errmsg()); String error = apiError(response == null ? null : response.errcode(), response == null ? null : response.errmsg());
throw new IllegalStateException("WeCom gettoken failed: " + (response == null ? "null" : response.errmsg())); log.error("wecom gettoken failed {}", error);
throw new IllegalStateException("WeCom gettoken failed: " + error);
} }
cache.token = response.accessToken(); cache.token = response.accessToken();
@ -242,7 +251,17 @@ public class WecomClient {
private String studentContactSecret() { private String studentContactSecret() {
String secret = properties.getWecom().getStudentContactSecret(); String secret = properties.getWecom().getStudentContactSecret();
return secret == null || secret.isBlank() ? app(properties.getWecom().getDefaultApp()).getSecret() : secret; if (secret != null && !secret.isBlank()) {
return secret;
}
String appKey = requireText(properties.getWecom().getStudentContactApp(),
"WeCom customer contact credential is missing: configure student-contact-secret or student-contact-app");
return app(appKey).getSecret();
}
private String apiError(Integer errcode, String errmsg) {
return "errcode=" + (errcode == null ? "unknown" : errcode)
+ ", errmsg=" + (errmsg == null || errmsg.isBlank() ? "unknown" : errmsg);
} }
private FoundationProperties.Wecom.App app(String appKey) { private FoundationProperties.Wecom.App app(String appKey) {

View File

@ -62,10 +62,48 @@ public class WecomCustomerOperationsReportIntentHandler implements SemanticInten
} catch (Exception e) { } catch (Exception e) {
log.error("wecom customer operations report failed wecomUserId={} range={}~{} err={}", log.error("wecom customer operations report failed wecomUserId={} range={}~{} err={}",
context.wecomUserId(), range.startDate(), range.endDate(), e.getMessage(), e); context.wecomUserId(), range.startDate(), range.endDate(), e.getMessage(), e);
return "企业微信客户运营日报查询失败,请稍后重试。"; return failureMessage(e);
} }
} }
private String failureMessage(Exception exception) {
String message = rootMessage(exception);
String lower = message.toLowerCase();
if (lower.contains("customer contact credential is missing")) {
return "企业微信客户联系凭证未配置:请设置 WECOM_STUDENT_CONTACT_SECRET,或配置可调用应用。";
}
if (lower.contains("errcode=60020") || lower.contains("not allow to access from your ip")) {
return "企业微信客户接口拒绝访问(60020):请将运行8880服务的服务器出口公网IP加入企业可信IP。";
}
if (lower.contains("errcode=48002") || lower.contains("api forbidden")) {
return "企业微信客户接口未授权(48002):请为当前应用开通客户联系接口权限,并检查客户联系Secret。";
}
if (lower.contains("errcode=40014") || lower.contains("invalid access_token")) {
return "企业微信客户接口凭证无效(40014):请检查客户联系Secret配置。";
}
if (lower.contains("errcode=60011") || lower.contains("no privilege to access")) {
return "企业微信客户接口无数据权限(60011):请检查应用可见范围和客户联系权限。";
}
if (lower.contains("wecom")) {
return "企业微信客户运营日报查询失败:" + shorten(message, 180);
}
return "企业微信客户运营日报查询失败,请查看服务器日志。";
}
private String rootMessage(Throwable throwable) {
Throwable current = throwable;
while (current.getCause() != null && current.getCause() != current) {
current = current.getCause();
}
String message = current.getMessage();
return message == null || message.isBlank() ? throwable.getClass().getSimpleName() : message;
}
private String shorten(String value, int maxLength) {
String normalized = value.replaceAll("[\\r\\n]+", " ").trim();
return normalized.length() <= maxLength ? normalized : normalized.substring(0, maxLength) + "…";
}
private String format(JsonNode report, SemanticDateRangeResolver.DateRange range) { private String format(JsonNode report, SemanticDateRangeResolver.DateRange range) {
StringBuilder reply = new StringBuilder("### 🌱 企业微信客户运营日报\n> 日期:") StringBuilder reply = new StringBuilder("### 🌱 企业微信客户运营日报\n> 日期:")
.append(range.startDate()); .append(range.startDate());

View File

@ -51,6 +51,8 @@ foundation:
enabled: true enabled: true
corp-id: wwaec174f56094f2b5 corp-id: wwaec174f56094f2b5
default-app: general default-app: general
student-contact-app: sports-venue
student-contact-secret: ${WECOM_STUDENT_CONTACT_SECRET:}
token: crmAgentToken2026 token: crmAgentToken2026
encoding-aes-key: k3F8mN2vQ7xLp9sTd4YwH6cRb1JuEz5AaZq7BnVcK2D encoding-aes-key: k3F8mN2vQ7xLp9sTd4YwH6cRb1JuEz5AaZq7BnVcK2D
apps: apps: