This commit is contained in:
van
2026-05-09 23:46:33 +08:00
parent 217ea3afdc
commit 83dc41f3a5
7 changed files with 100 additions and 25 deletions

View File

@@ -11,9 +11,10 @@ public interface IPhoneForwardActivePush {
/**
* 在被动回复已发出后,按序推送到企微成员(首条前短暂延迟,避免次序错乱)。
*
* @param toUser 成员 UserIDFromUserName
* @param toUser 企微 message/send 的 touser可与 XML FromUserName 相同,或由超级管理员 touser 解析为 {@code |} 分隔多成员
* @param wecomCallbackAgentId 回调明文 XML 中的 AgentId字符串空则 wxSend 使用默认 {@code qywx.app.agentId}
* @param chunks 每段不超过 UTF-8 2048 字节的正文
* @return {@code true} 表示配置齐全且每一段非空正文均收到 wxSend 2xx 响应
*/
boolean schedulePushChunks(String toUser, List<String> chunks);
boolean schedulePushChunks(String toUser, String wecomCallbackAgentId, List<String> chunks);
}

View File

@@ -61,4 +61,10 @@ public interface SuperAdminService
SuperAdmin selectSuperAdminByUnionId(Long unionId);
SuperAdmin selectSuperAdminByWecomUserId(String wxid);
/**
* 企微 {@code message/send} 的 {@code touser}:优先超级管理员表 {@code touser}(支持英文/中文逗号,转换为 {@code |}
* 否则为发消息成员 UserID。逻辑与 {@link com.ruoyi.jarvis.service.impl.WeComInboundServiceImpl} 中 {@code resolveTouser} 一致。
*/
String resolveTouserPipeForActivePush(String fromWecomUserId);
}

View File

@@ -14,6 +14,7 @@ import com.ruoyi.jarvis.domain.TgScalperPhone;
import com.ruoyi.jarvis.domain.dto.WeComInboundRequest;
import com.ruoyi.jarvis.service.IPhoneForwardActivePush;
import com.ruoyi.jarvis.service.ITgScalperPhoneService;
import com.ruoyi.jarvis.service.SuperAdminService;
import com.ruoyi.jarvis.util.WeComUtf8ChunkUtil;
import java.io.InputStream;
@@ -138,6 +139,9 @@ public class OpenPhoneForwardService {
@Resource
private ITgScalperPhoneService tgScalperPhoneService;
@Resource
private SuperAdminService superAdminService;
@Autowired(required = false)
private IPhoneForwardActivePush phoneForwardActivePush;
@@ -218,8 +222,9 @@ public class OpenPhoneForwardService {
if (asyncResultPushEnabled && phoneForwardActivePush != null && StringUtils.hasText(req.getFromUserName())) {
markForwardInflight(cKey);
final String toUser = req.getFromUserName().trim();
CompletableFuture.runAsync(() -> runForwardAndPush(toUser, phone, bot, cKey));
final String pushTouserPipe = superAdminService.resolveTouserPipeForActivePush(req.getFromUserName());
final String callbackAgentId = StringUtils.hasText(req.getAgentId()) ? req.getAgentId().trim() : null;
CompletableFuture.runAsync(() -> runForwardAndPush(pushTouserPipe, callbackAgentId, phone, bot, cKey));
return String.format("收到电话:%s。\n后续结果将通过应用消息推送。", phone);
}
@@ -256,31 +261,33 @@ public class OpenPhoneForwardService {
forwardInflightUntilMs.remove(cKey);
}
private void runForwardAndPush(String toUser, String phone, String bot, String cKey) {
private void runForwardAndPush(String pushTouserPipe, String wecomCallbackAgentId, String phone, String bot, String cKey) {
try {
log.info("phone-forward 异步 TG 查询开始 phone={} bot={} toUser={}", phone, bot, toUser);
log.info(
"phone-forward 异步 TG 查询开始 phone={} bot={} touser={} callbackAgentId={}",
phone, bot, pushTouserPipe, wecomCallbackAgentId);
String reply = doForward(phone, bot, dedupEnabled ? cKey : null);
List<String> chunks = WeComUtf8ChunkUtil.splitUtf8Chunks(reply, WeComUtf8ChunkUtil.WE_COM_TEXT_MAX_UTF8_BYTES);
chunks.removeIf(s -> !StringUtils.hasText(s));
if (chunks.isEmpty()) {
chunks = Collections.singletonList("(无返回内容)");
}
boolean pushed = phoneForwardActivePush.schedulePushChunks(toUser, chunks);
boolean pushed = phoneForwardActivePush.schedulePushChunks(pushTouserPipe, wecomCallbackAgentId, chunks);
if (!pushed) {
log.error(
"phone-forward 结果未能推送到企微 user={} phone={}(可能为 TG/转发错误或服务端返回的正文,请检查 wxSend 与 jarvis.wecom",
toUser, phone);
"phone-forward 结果未能推送到企微 touser={} phone={}(可能为 TG/转发错误或服务端返回的正文,请检查 wxSend 与 jarvis.wecom",
pushTouserPipe, phone);
}
} catch (Exception e) {
log.error("phone-forward 异步处理异常 phone={} user={}", phone, toUser, e);
pushForwardFailureNotice(toUser, userVisibleThrowableMessage(e));
log.error("phone-forward 异步处理异常 phone={} user={}", phone, pushTouserPipe, e);
pushForwardFailureNotice(pushTouserPipe, wecomCallbackAgentId, userVisibleThrowableMessage(e));
} finally {
clearForwardInflight(cKey);
}
}
private void pushForwardFailureNotice(String toUser, String line) {
if (phoneForwardActivePush == null || !StringUtils.hasText(toUser)) {
private void pushForwardFailureNotice(String pushTouserPipe, String wecomCallbackAgentId, String line) {
if (phoneForwardActivePush == null || !StringUtils.hasText(pushTouserPipe)) {
return;
}
try {
@@ -288,9 +295,9 @@ public class OpenPhoneForwardService {
if (parts.isEmpty()) {
parts = Collections.singletonList("「转发服务」异常,请稍后重试。");
}
boolean ok = phoneForwardActivePush.schedulePushChunks(toUser.trim(), parts);
boolean ok = phoneForwardActivePush.schedulePushChunks(pushTouserPipe.trim(), wecomCallbackAgentId, parts);
if (!ok) {
log.error("phone-forward 异常说明未能推送到企微 user={}", toUser);
log.error("phone-forward 异常说明未能推送到企微 user={}", pushTouserPipe);
}
} catch (Exception e2) {
log.warn("phone-forward 异常说明推送过程失败 err={}", e2.toString());

View File

@@ -3,6 +3,7 @@ package com.ruoyi.jarvis.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.ruoyi.jarvis.mapper.SuperAdminMapper;
import com.ruoyi.jarvis.domain.SuperAdmin;
import com.ruoyi.jarvis.service.SuperAdminService;
@@ -15,6 +16,9 @@ import com.ruoyi.jarvis.service.SuperAdminService;
@Service
public class SuperAdminServiceImpl implements SuperAdminService
{
/** 与 {@link com.ruoyi.jarvis.service.impl.WeComInboundServiceImpl#WE_COM_SUPER_USER_ID} 一致 */
private static final String WE_COM_SUPER_USER_ID = "LinPingFan";
@Autowired
private SuperAdminMapper superAdminMapper;
@@ -99,4 +103,47 @@ public class SuperAdminServiceImpl implements SuperAdminService
public SuperAdmin selectSuperAdminByWecomUserId(String wxid) {
return superAdminMapper.selectSuperAdminByWecomUserId(wxid);
}
@Override
public String resolveTouserPipeForActivePush(String fromWecomUserId) {
if (!StringUtils.hasText(fromWecomUserId)) {
return "";
}
String from = fromWecomUserId.trim();
boolean isSuper = WE_COM_SUPER_USER_ID.equals(from);
SuperAdmin row = selectSuperAdminByWecomUserId(from);
String raw = null;
if (row != null && StringUtils.hasText(row.getTouser())) {
raw = row.getTouser().trim();
} else if (isSuper) {
SuperAdmin ping = selectSuperAdminByWecomUserId(WE_COM_SUPER_USER_ID);
if (ping != null && StringUtils.hasText(ping.getTouser())) {
raw = ping.getTouser().trim();
}
}
if (raw != null) {
return commaSeparatedToWeComTouser(raw);
}
return from;
}
/** 企微 API多个成员 UserID 用 {@code |} 分隔 */
private static String commaSeparatedToWeComTouser(String raw) {
if (!StringUtils.hasText(raw)) {
return "";
}
String[] parts = raw.split("[,]");
StringBuilder sb = new StringBuilder();
for (String p : parts) {
String t = p != null ? p.trim() : "";
if (t.isEmpty()) {
continue;
}
if (sb.length() > 0) {
sb.append('|');
}
sb.append(t);
}
return sb.length() > 0 ? sb.toString() : raw.trim();
}
}