diff --git a/src/main/java/com/company/agentfoundation/config/FoundationProperties.java b/src/main/java/com/company/agentfoundation/config/FoundationProperties.java index e54dc0b..6771bbf 100644 --- a/src/main/java/com/company/agentfoundation/config/FoundationProperties.java +++ b/src/main/java/com/company/agentfoundation/config/FoundationProperties.java @@ -101,6 +101,7 @@ public class FoundationProperties { private boolean enabled; private String corpId; private String defaultApp = "general"; + private String studentContactApp; private String studentContactSecret; private String token; private String encodingAesKey; @@ -112,6 +113,8 @@ public class FoundationProperties { public void setCorpId(String corpId) { this.corpId = corpId; } public String getDefaultApp() { return 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 void setStudentContactSecret(String studentContactSecret) { this.studentContactSecret = studentContactSecret; } public String getToken() { return token; } diff --git a/src/main/java/com/company/agentfoundation/integration/wecom/client/WecomClient.java b/src/main/java/com/company/agentfoundation/integration/wecom/client/WecomClient.java index b53812e..a8d4d43 100644 --- a/src/main/java/com/company/agentfoundation/integration/wecom/client/WecomClient.java +++ b/src/main/java/com/company/agentfoundation/integration/wecom/client/WecomClient.java @@ -105,8 +105,9 @@ public class WecomClient { .block(); if (response == null || !response.success()) { - log.error("wecom student contact get follow users failed err={}", response == null ? "null" : response.errmsg()); - throw new IllegalStateException("WeCom student contact get follow users failed: " + (response == null ? "null" : response.errmsg())); + String error = apiError(response == null ? null : response.errcode(), 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; } @@ -125,8 +126,9 @@ public class WecomClient { .block(); if (response == null || !response.success()) { - log.error("wecom student contact batch get failed err={}", response == null ? "null" : response.errmsg()); - throw new IllegalStateException("WeCom student contact batch get failed: " + (response == null ? "null" : response.errmsg())); + String error = apiError(response == null ? null : response.errcode(), 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; } @@ -145,8 +147,9 @@ public class WecomClient { .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())); + String error = apiError(response == null ? null : response.errcode(), 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; } @@ -194,19 +197,24 @@ public class WecomClient { boolean success = response != null; String error = "null"; + Integer errorCode = null; if (response instanceof WecomGroupMessageListResponse value) { success = value.success(); error = value.errmsg(); + errorCode = value.errcode(); } else if (response instanceof WecomGroupMessageTaskResponse value) { success = value.success(); error = value.errmsg(); + errorCode = value.errcode(); } else if (response instanceof WecomGroupMessageSendResultResponse value) { success = value.success(); error = value.errmsg(); + errorCode = value.errcode(); } if (!success) { - log.error("wecom student contact {} failed err={}", operation, error); - throw new IllegalStateException("WeCom " + operation + " failed: " + error); + String apiError = apiError(errorCode, error); + log.error("wecom student contact {} failed {}", operation, apiError); + throw new IllegalStateException("WeCom " + operation + " failed: " + apiError); } return response; } @@ -229,8 +237,9 @@ public class WecomClient { .block(); if (response == null || !response.success() || response.accessToken() == null) { - log.error("wecom gettoken failed err={}", response == null ? "null" : response.errmsg()); - throw new IllegalStateException("WeCom gettoken failed: " + (response == null ? "null" : response.errmsg())); + String error = apiError(response == null ? null : response.errcode(), response == null ? null : response.errmsg()); + log.error("wecom gettoken failed {}", error); + throw new IllegalStateException("WeCom gettoken failed: " + error); } cache.token = response.accessToken(); @@ -242,7 +251,17 @@ public class WecomClient { private String studentContactSecret() { 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) { diff --git a/src/main/java/com/company/agentfoundation/semantic/handler/WecomCustomerOperationsReportIntentHandler.java b/src/main/java/com/company/agentfoundation/semantic/handler/WecomCustomerOperationsReportIntentHandler.java index 6d2b42f..4e60288 100644 --- a/src/main/java/com/company/agentfoundation/semantic/handler/WecomCustomerOperationsReportIntentHandler.java +++ b/src/main/java/com/company/agentfoundation/semantic/handler/WecomCustomerOperationsReportIntentHandler.java @@ -62,10 +62,48 @@ public class WecomCustomerOperationsReportIntentHandler implements SemanticInten } catch (Exception e) { log.error("wecom customer operations report failed wecomUserId={} range={}~{} err={}", 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) { StringBuilder reply = new StringBuilder("### 🌱 企业微信客户运营日报\n> 日期:") .append(range.startDate()); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index eec3bd0..170c396 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -51,6 +51,8 @@ foundation: enabled: true corp-id: wwaec174f56094f2b5 default-app: general + student-contact-app: sports-venue + student-contact-secret: ${WECOM_STUDENT_CONTACT_SECRET:} token: crmAgentToken2026 encoding-aes-key: k3F8mN2vQ7xLp9sTd4YwH6cRb1JuEz5AaZq7BnVcK2D apps: