diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocController.java index 5ef560e..ccacaa6 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocController.java @@ -947,7 +947,7 @@ public class TencentDocController extends BaseController { sb.append("… 共 ").append(errorLogs.size()).append(" 条错误\n"); } } - wxSendGoofishNotifyClient.pushGoofishAgentText(null, sb.toString()); + wxSendGoofishNotifyClient.pushGoofishAgentText(null, "", sb.toString()); } /** @@ -2015,7 +2015,7 @@ public class TencentDocController extends BaseController { log.error("异常后更新批量推送记录失败 batchId={}", batchId, ex); } try { - wxSendGoofishNotifyClient.pushGoofishAgentText(null, + wxSendGoofishNotifyClient.pushGoofishAgentText(null, "", "【腾讯文档推送】批量同步异常\n批次: " + batchId + "\n" + errMsg); } catch (Exception ex) { log.warn("腾讯文档推送异常企微通知失败: {}", ex.toString()); diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java index 2309ab3..dd924af 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java @@ -148,7 +148,7 @@ public class ServerController } /** - * 手动测试企微闲鱼通知(经 wxSend goofish-active-push) + * 手动测试企微闲鱼通知(经 wxSend POST /wx/send/goofish,与 /send/pdd 相同 vanToken + title/text/touser) */ @PreAuthorize("@ss.hasPermi('monitor:server:list')") @PostMapping("/health/goofish-notify-test") diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml index 40a060a..efb3d43 100644 --- a/ruoyi-admin/src/main/resources/application-dev.yml +++ b/ruoyi-admin/src/main/resources/application-dev.yml @@ -220,8 +220,8 @@ jarvis: wxsend-base-url: http://127.0.0.1:36699 # 须与 wxSend jarvis.wecom.push-secret 一致(Header X-WxSend-WeCom-Push-Secret) push-secret: jarvis_wecom_push_change_me - # 闲鱼订单事件 → wxSend POST /wecom/goofish-active-push(须与 wxSend goofish-push-secret 一致) - goofish-push-secret: jarvis_wecom_goofish_push_change_me + # 与 /wx/send/pdd、/wx/send/goofish 请求头 vanToken 一致(wxSend TokenUtil) + wxsend-van-token: super_token_b62190c26 # 接收企微通知的成员 UserID,多个逗号或 |;留空则不推送 goofish-notify-touser: "LinPinFan" # 多轮会话:与 JDUtil interaction_state 类似,TTL 与空闲超时(分钟) diff --git a/ruoyi-admin/src/main/resources/application-prod.yml b/ruoyi-admin/src/main/resources/application-prod.yml index 1b25f07..262a21a 100644 --- a/ruoyi-admin/src/main/resources/application-prod.yml +++ b/ruoyi-admin/src/main/resources/application-prod.yml @@ -214,7 +214,7 @@ jarvis: inbound-secret: jarvis_wecom_bridge_change_me wxsend-base-url: http://127.0.0.1:36699 push-secret: jarvis_wecom_push_change_me - goofish-push-secret: jarvis_wecom_goofish_push_change_me + wxsend-van-token: super_token_b62190c26 goofish-notify-touser: "LinPinFan" session-ttl-minutes: 30 session-idle-timeout-minutes: 30 diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/LogisticsServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/LogisticsServiceImpl.java index 157afde..e00e938 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/LogisticsServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/LogisticsServiceImpl.java @@ -855,11 +855,10 @@ public class LogisticsServiceImpl implements ILogisticsService { || (distributionMark != null && distributionMark.contains("闲鱼")); if (useGoofishWecom) { String touserGoofish = getTouserByDistributionMark(distributionMark); - String fullText = "JD物流信息推送\n" + pushContent; logger.info("闲鱼关联或分销含「闲鱼」:尝试企微闲鱼自建应用 - 订单ID: {}, 分销标识: {}, 接收人: {}", order.getId(), distributionMark, StringUtils.hasText(touserGoofish) ? touserGoofish : "(jarvis.wecom.goofish-notify-touser)"); - if (wxSendGoofishNotifyClient.pushGoofishAgentText(touserGoofish, fullText)) { + if (wxSendGoofishNotifyClient.pushGoofishAgentText(touserGoofish, "JD物流信息推送", pushContent.toString())) { logger.info("企微闲鱼应用推送成功 - 订单ID: {}, 订单号: {}, waybill_no: {}", order.getId(), order.getOrderId(), waybillNo); return true; diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/TencentDocBatchPushServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/TencentDocBatchPushServiceImpl.java index ce931a4..4dbec53 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/TencentDocBatchPushServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/TencentDocBatchPushServiceImpl.java @@ -213,7 +213,7 @@ public class TencentDocBatchPushServiceImpl implements ITencentDocBatchPushServi continue; } String pushText = "【腾讯文档推送】批次长时间未结束,已标记为「已中断」\n" + resultMsg; - boolean ok = wxSendGoofishNotifyClient.pushGoofishAgentText(null, pushText); + boolean ok = wxSendGoofishNotifyClient.pushGoofishAgentText(null, "", pushText); if (ok) { redisCache.setCacheObject(dedupeKey, "1", 7, TimeUnit.DAYS); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/TencentDocDelayedPushServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/TencentDocDelayedPushServiceImpl.java index 72d64d9..15c1879 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/TencentDocDelayedPushServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/TencentDocDelayedPushServiceImpl.java @@ -353,7 +353,7 @@ public class TencentDocDelayedPushServiceImpl implements ITencentDocDelayedPushS Object msgObj = ar.get(AjaxResult.MSG_TAG); String msg = msgObj != null ? String.valueOf(msgObj) : "同步接口返回失败"; batchPushService.updateBatchPushRecord(batchId, "FAILED", 0, 0, 0, null, msg); - wxSendGoofishNotifyClient.pushGoofishAgentText(null, + wxSendGoofishNotifyClient.pushGoofishAgentText(null, "", "【腾讯文档推送】定时批量同步失败\n批次: " + batchId + "\n" + msg); } } @@ -364,7 +364,7 @@ public class TencentDocDelayedPushServiceImpl implements ITencentDocDelayedPushS if (batchId != null) { String msg = "批量同步调用失败: " + (ex.getMessage() != null ? ex.getMessage() : ex.getClass().getSimpleName()); batchPushService.updateBatchPushRecord(batchId, "FAILED", 0, 0, 0, null, msg); - wxSendGoofishNotifyClient.pushGoofishAgentText(null, + wxSendGoofishNotifyClient.pushGoofishAgentText(null, "", "【腾讯文档推送】定时批量同步异常\n批次: " + batchId + "\n" + msg); } } @@ -376,7 +376,7 @@ public class TencentDocDelayedPushServiceImpl implements ITencentDocDelayedPushS try { String msg = "执行批量同步失败: " + (e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName()); batchPushService.updateBatchPushRecord(batchId, "FAILED", 0, 0, 0, null, msg); - wxSendGoofishNotifyClient.pushGoofishAgentText(null, + wxSendGoofishNotifyClient.pushGoofishAgentText(null, "", "【腾讯文档推送】定时批量同步异常\n批次: " + batchId + "\n" + msg); } catch (Exception ex) { log.error("更新批量推送记录失败", ex); diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/wecom/WxSendGoofishNotifyClient.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/wecom/WxSendGoofishNotifyClient.java index 5528de6..38dd212 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/wecom/WxSendGoofishNotifyClient.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/wecom/WxSendGoofishNotifyClient.java @@ -16,23 +16,22 @@ import java.util.ArrayList; import java.util.List; /** - * 调用 wxSend 闲鱼自建应用文本推送(POST /wecom/goofish-active-push,agent 见 wxSend qywx.app.goofishAgentId)。 + * 调用 wxSend 闲鱼自建应用文本推送,与 /wx/send/pdd 一致:Header vanToken + JSON(title、text、touser)。 */ @Component public class WxSendGoofishNotifyClient { private static final Logger log = LoggerFactory.getLogger(WxSendGoofishNotifyClient.class); - public static final String HEADER_GOOFISH_PUSH_SECRET = "X-WxSend-Goofish-Push-Secret"; - /** 与企微 message/send 文本 content 上限对齐,预留余量 */ private static final int CONTENT_MAX = 2000; @Value("${jarvis.wecom.wxsend-base-url:}") private String wxsendBaseUrl; - @Value("${jarvis.wecom.goofish-push-secret:}") - private String goofishPushSecret; + /** 与 wxSend TokenUtil 校验一致,请求头 vanToken */ + @Value("${jarvis.wecom.wxsend-van-token:}") + private String wxsendVanToken; /** * 接收通知的成员 UserID(企微管理后台可见),多个用英文逗号或 | 分隔; @@ -50,7 +49,7 @@ public class WxSendGoofishNotifyClient { * @param message 已截断的说明文案 */ public void notifyGoofishEvent(String orderNo, String eventType, String source, String message) { - if (!StringUtils.hasText(wxsendBaseUrl) || !StringUtils.hasText(goofishPushSecret)) { + if (!StringUtils.hasText(wxsendBaseUrl) || !StringUtils.hasText(wxsendVanToken)) { return; } String toUser = buildTouserParam(goofishNotifyTouser); @@ -63,22 +62,24 @@ public class WxSendGoofishNotifyClient { } try { String base = normalizeBase(wxsendBaseUrl); - String url = base + "/wecom/goofish-active-push"; - postJson(url, toUser, content); + String url = base + "/wx/send/goofish"; + postMessageReturnsOk(url, wxsendVanToken, "", content, toUser); } catch (Exception e) { - log.warn("wxSend goofish-active-push 失败 orderNo={} err={}", orderNo, e.toString()); + log.warn("wxSend /wx/send/goofish 失败 orderNo={} err={}", orderNo, e.toString()); } } /** - * 京东物流扫描等:全文发往企微「闲鱼」自建应用(wxSend qywx.app.goofishAgentId)。 + * 京东物流扫描等:发往企微「闲鱼」自建应用,请求体与 PDD 推送一致(title + text + touser)。 * * @param optionalToUser 非空时用与 logistics.push.touser.* 相同的成员 ID(逗号/| 亦可);空则用 goofish-notify-touser - * @return HTTP 2xx 为 true;未配置密钥或 toUser 为空返回 false + * @param title 与 {@code LogisticsServiceImpl} 推 PDD 时的 title 一致,可为空 + * @param textBody 正文(对应 PDD 的 text) + * @return wxSend 业务 code=200 为 true;未配置 token 或 touser 为空返回 false */ - public boolean pushGoofishAgentText(String optionalToUser, String content) { - if (!StringUtils.hasText(wxsendBaseUrl) || !StringUtils.hasText(goofishPushSecret)) { - log.debug("wxSend 闲鱼应用物流推送跳过:未配置 wxsend-base-url 或 goofish-push-secret"); + public boolean pushGoofishAgentText(String optionalToUser, String title, String textBody) { + if (!StringUtils.hasText(wxsendBaseUrl) || !StringUtils.hasText(wxsendVanToken)) { + log.debug("wxSend 闲鱼应用物流推送跳过:未配置 wxsend-base-url 或 wxsend-van-token"); return false; } String rawTouser = StringUtils.hasText(optionalToUser) ? optionalToUser : goofishNotifyTouser; @@ -87,16 +88,24 @@ public class WxSendGoofishNotifyClient { log.warn("wxSend 闲鱼应用物流推送跳过:无接收人(请配置 jarvis.wecom.goofish-notify-touser 或 logistics.push.touser)"); return false; } - String text = content != null ? content : ""; - if (text.length() > CONTENT_MAX) { - text = text.substring(0, CONTENT_MAX - 1) + "…"; + String t = title != null ? title : ""; + String body = textBody != null ? textBody : ""; + if (t.length() > CONTENT_MAX) { + t = t.substring(0, CONTENT_MAX - 1) + "…"; + body = ""; + } else { + int overhead = StringUtils.hasText(t) ? t.length() + 1 : 0; + int maxBody = Math.max(0, CONTENT_MAX - overhead); + if (body.length() > maxBody) { + body = body.substring(0, Math.max(0, maxBody - 1)) + "…"; + } } try { String base = normalizeBase(wxsendBaseUrl); - String url = base + "/wecom/goofish-active-push"; - return postJsonReturnsOk(url, toUser, text); + String url = base + "/wx/send/goofish"; + return postMessageReturnsOk(url, wxsendVanToken, t, body, toUser); } catch (Exception e) { - log.warn("wxSend goofish-active-push 物流全文失败 err={}", e.toString()); + log.warn("wxSend /wx/send/goofish 物流全文失败 err={}", e.toString()); return false; } } @@ -157,40 +166,41 @@ public class WxSendGoofishNotifyClient { if (!StringUtils.hasText(wxsendBaseUrl)) { return ""; } - return normalizeBase(wxsendBaseUrl) + "/wecom/goofish-active-push"; + return normalizeBase(wxsendBaseUrl) + "/wx/send/goofish"; } /** * 服务监控手动测试:经 wxSend 向企微「闲鱼」应用发一条文本。 * - * @return null 表示 HTTP 2xx 且调用链未抛错;非空为可直接展示的失败原因 + * @return null 表示 HTTP 2xx 且 wxSend 返回 code=200;非空为可直接展示的失败原因 */ public String testGoofishNotify() { if (!StringUtils.hasText(wxsendBaseUrl)) { return "未配置 jarvis.wecom.wxsend-base-url"; } - if (!StringUtils.hasText(goofishPushSecret)) { - return "未配置 jarvis.wecom.goofish-push-secret"; + if (!StringUtils.hasText(wxsendVanToken)) { + return "未配置 jarvis.wecom.wxsend-van-token(须与 wxSend TokenUtil 一致)"; } if (!StringUtils.hasText(goofishNotifyTouser)) { return "未配置 jarvis.wecom.goofish-notify-touser(接收人为空)"; } String content = "【服务监控·闲鱼通知测试】RuoYi 手动触发 " + new java.util.Date(); - boolean ok = pushGoofishAgentText(null, content); + boolean ok = pushGoofishAgentText(null, "", content); if (ok) { return null; } - return "推送未成功:请核对 wxSend 与企微应用、密钥及接收人,或查看服务端日志"; + return "推送未成功:请核对 wxSend 服务、vanToken、企微闲鱼应用及接收人,或查看服务端日志"; } - private void postJson(String url, String toUser, String content) throws Exception { - postJsonReturnsOk(url, toUser, content); - } - - private boolean postJsonReturnsOk(String url, String toUser, String content) throws Exception { + /** + * POST {@code /wx/send/goofish}:body 与 {@code /wx/send/pdd} 相同字段名(title、text、touser)。 + */ + private boolean postMessageReturnsOk(String url, String vanToken, String title, String text, String touserPipeJoined) + throws Exception { JSONObject body = new JSONObject(); - body.put("toUser", toUser); - body.put("content", content); + body.put("title", title != null ? title : ""); + body.put("text", text != null ? text : ""); + body.put("touser", touserPipeJoined); byte[] bytes = body.toJSONString().getBytes(StandardCharsets.UTF_8); HttpURLConnection conn = null; try { @@ -200,19 +210,32 @@ public class WxSendGoofishNotifyClient { conn.setReadTimeout(60000); conn.setDoOutput(true); conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); - conn.setRequestProperty(HEADER_GOOFISH_PUSH_SECRET, goofishPushSecret); + conn.setRequestProperty("vanToken", vanToken); try (OutputStream os = conn.getOutputStream()) { os.write(bytes); } - int code = conn.getResponseCode(); - InputStream is = code >= 200 && code < 300 ? conn.getInputStream() : conn.getErrorStream(); + int httpCode = conn.getResponseCode(); + InputStream is = httpCode >= 200 && httpCode < 300 ? conn.getInputStream() : conn.getErrorStream(); String resp = readAll(is); - if (code < 200 || code >= 300) { - log.warn("wxSend goofish-active-push HTTP {} body={}", code, resp); + if (httpCode < 200 || httpCode >= 300) { + log.warn("wxSend /wx/send/goofish HTTP {} body={}", httpCode, resp); return false; } - log.debug("wxSend goofish-active-push OK http={} resp={}", code, resp); - return true; + Integer bizCode = null; + try { + JSONObject jo = JSONObject.parseObject(resp); + if (jo != null) { + bizCode = jo.getInteger("code"); + } + } catch (Exception parseEx) { + log.debug("解析 wxSend 响应: {}", parseEx.toString()); + } + if (bizCode != null && bizCode == 200) { + log.debug("wxSend /wx/send/goofish OK resp={}", resp); + return true; + } + log.warn("wxSend /wx/send/goofish 业务未成功 resp={}", resp); + return false; } finally { if (conn != null) { conn.disconnect();