1
This commit is contained in:
@@ -8,6 +8,7 @@ import cn.van333.wxsend.business.service.LogService;
|
|||||||
import cn.van333.wxsend.enums.WXMessageType;
|
import cn.van333.wxsend.enums.WXMessageType;
|
||||||
import cn.van333.wxsend.util.SourceForQLUtil;
|
import cn.van333.wxsend.util.SourceForQLUtil;
|
||||||
import cn.van333.wxsend.util.TokenUtil;
|
import cn.van333.wxsend.util.TokenUtil;
|
||||||
|
import cn.van333.wxsend.business.service.WeComApplicationTextPushService;
|
||||||
import cn.van333.wxsend.util.WxSendUtil;
|
import cn.van333.wxsend.util.WxSendUtil;
|
||||||
import cn.van333.wxsend.util.request.MessageRequest;
|
import cn.van333.wxsend.util.request.MessageRequest;
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
@@ -18,7 +19,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Leo
|
* @author Leo
|
||||||
@@ -31,6 +34,9 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
public class WXController {
|
public class WXController {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(LogService.class);
|
private static final Logger logger = LoggerFactory.getLogger(LogService.class);
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private WeComApplicationTextPushService weComApplicationTextPushService;
|
||||||
|
|
||||||
@RequestMapping(value = "/send")
|
@RequestMapping(value = "/send")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@RateLimiter(time = 1, count = 60)
|
@RateLimiter(time = 1, count = 60)
|
||||||
@@ -211,6 +217,62 @@ public class WXController {
|
|||||||
return R.ok(result);
|
return R.ok(result);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 闲鱼通知:与 {@link #sendToPDD} 相同鉴权(Header vanToken)与请求体 {@link MessageRequest}(title、text、touser),
|
||||||
|
* 下发到企微自建应用 {@code qywx.app.goofishAgentId}。
|
||||||
|
*/
|
||||||
|
@RequestMapping(value = "/send/goofish")
|
||||||
|
@ResponseBody
|
||||||
|
@RateLimiter(time = 2, count = 1)
|
||||||
|
public R sendToGoofish(HttpServletRequest request, @RequestBody MessageRequest message) {
|
||||||
|
logger.info("goofish message 打印---{}", JSON.toJSONString(message));
|
||||||
|
String vanToken = request.getHeader("vanToken");
|
||||||
|
if (StrUtil.isEmpty(vanToken)) {
|
||||||
|
return R.error("vanToken为空");
|
||||||
|
}
|
||||||
|
if (!TokenUtil.checkToken(vanToken)) {
|
||||||
|
return R.error("vanToken无效");
|
||||||
|
}
|
||||||
|
if (StrUtil.isEmpty(message.getTouser())) {
|
||||||
|
return R.error("touser 必填");
|
||||||
|
}
|
||||||
|
String title = message.getTitle();
|
||||||
|
String text = message.getText();
|
||||||
|
if (StrUtil.isEmpty(text) && StrUtil.isEmpty(title)) {
|
||||||
|
return R.error("缺少标题和内容");
|
||||||
|
}
|
||||||
|
String content = StrUtil.isEmpty(title)
|
||||||
|
? (text != null ? text : "")
|
||||||
|
: (title + "\n" + (text != null ? text : ""));
|
||||||
|
String touser = normalizeTouserToPipe(message.getTouser());
|
||||||
|
if (StrUtil.isEmpty(touser)) {
|
||||||
|
return R.error("touser 无效");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
weComApplicationTextPushService.sendGoofishTextToUser(touser, content);
|
||||||
|
return R.ok("sent");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("企微闲鱼应用推送失败 toUser={} err={}", touser, e.toString());
|
||||||
|
return R.error("推送失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 逗号或 | 分隔的成员 UserID → 企微 message/send 的 user1|user2 */
|
||||||
|
private static String normalizeTouserToPipe(String raw) {
|
||||||
|
if (StrUtil.isEmpty(raw)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
String[] parts = raw.split("[,|]");
|
||||||
|
ArrayList<String> ids = new ArrayList<>();
|
||||||
|
for (String p : parts) {
|
||||||
|
if (StrUtil.isNotBlank(p)) {
|
||||||
|
ids.add(p.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return String.join("|", ids);
|
||||||
|
}
|
||||||
|
|
||||||
//@RequestMapping("/ok")
|
//@RequestMapping("/ok")
|
||||||
//@RateLimiter(time = 5, count = 60)
|
//@RateLimiter(time = 5, count = 60)
|
||||||
//public R ok(HttpServletRequest request,String content) throws Exception {
|
//public R ok(HttpServletRequest request,String content) throws Exception {
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ import javax.annotation.Resource;
|
|||||||
* <p>
|
* <p>
|
||||||
* Header: X-WxSend-WeCom-Push-Secret 与 {@code jarvis.wecom.push-secret} 一致
|
* Header: X-WxSend-WeCom-Push-Secret 与 {@code jarvis.wecom.push-secret} 一致
|
||||||
* <p>
|
* <p>
|
||||||
* 闲管家订单状态/自动发货等:{@code POST /wecom/goofish-active-push},使用自建应用
|
* 闲鱼通知请使用 {@code WXController} 的 {@code POST /wx/send/goofish}(与 {@code /send/pdd} 相同 Header vanToken + 请求体 {@code MessageRequest})。
|
||||||
* {@code qywx.app.goofishAgentId},Header {@link #HEADER_GOOFISH_PUSH_SECRET}
|
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/wecom")
|
@RequestMapping("/wecom")
|
||||||
@@ -28,15 +27,9 @@ public class WeComActivePushController {
|
|||||||
|
|
||||||
public static final String HEADER_PUSH_SECRET = "X-WxSend-WeCom-Push-Secret";
|
public static final String HEADER_PUSH_SECRET = "X-WxSend-WeCom-Push-Secret";
|
||||||
|
|
||||||
/** 本地 / ruoyi 调用「闲鱼通知应用」推送时携带,与 {@code jarvis.wecom.goofish-push-secret} 一致 */
|
|
||||||
public static final String HEADER_GOOFISH_PUSH_SECRET = "X-WxSend-Goofish-Push-Secret";
|
|
||||||
|
|
||||||
@Value("${jarvis.wecom.push-secret:}")
|
@Value("${jarvis.wecom.push-secret:}")
|
||||||
private String pushSecret;
|
private String pushSecret;
|
||||||
|
|
||||||
@Value("${jarvis.wecom.goofish-push-secret:}")
|
|
||||||
private String goofishPushSecret;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private WeComApplicationTextPushService weComApplicationTextPushService;
|
private WeComApplicationTextPushService weComApplicationTextPushService;
|
||||||
|
|
||||||
@@ -61,36 +54,4 @@ public class WeComActivePushController {
|
|||||||
return R.error("推送失败: " + e.getMessage());
|
return R.error("推送失败: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 闲鱼订单状态变更、自动发货等:使用企微应用 {@code qywx.app.goofishAgentId}(如 1000013)发文本给成员。
|
|
||||||
* <p>
|
|
||||||
* 示例:<pre>
|
|
||||||
* curl -X POST http://127.0.0.1:36699/wecom/goofish-active-push \
|
|
||||||
* -H "Content-Type: application/json" \
|
|
||||||
* -H "X-WxSend-Goofish-Push-Secret: <与 jarvis.wecom.goofish-push-secret 一致>" \
|
|
||||||
* -d '{"toUser":"UserID","content":"订单 xxx 状态已更新"}'
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
@PostMapping(value = "/goofish-active-push", consumes = MediaType.APPLICATION_JSON_VALUE)
|
|
||||||
public R goofishActivePush(
|
|
||||||
@RequestHeader(value = HEADER_GOOFISH_PUSH_SECRET, required = false) String secret,
|
|
||||||
@RequestBody WeComActivePushRequest body) {
|
|
||||||
if (!StringUtils.hasText(goofishPushSecret) || !goofishPushSecret.equals(secret)) {
|
|
||||||
return R.error(403, "拒绝访问");
|
|
||||||
}
|
|
||||||
if (body == null || !StringUtils.hasText(body.getToUser())) {
|
|
||||||
return R.error(400, "toUser 必填");
|
|
||||||
}
|
|
||||||
if (!StringUtils.hasText(body.getContent())) {
|
|
||||||
return R.error(400, "content 不能为空");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
weComApplicationTextPushService.sendGoofishTextToUser(body.getToUser().trim(), body.getContent());
|
|
||||||
return R.ok("sent");
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warn("企微闲鱼应用推送失败 toUser={} err={}", body.getToUser(), e.toString());
|
|
||||||
return R.error("推送失败: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,8 +72,6 @@ jarvis:
|
|||||||
shared-secret: jarvis_wecom_bridge_change_me
|
shared-secret: jarvis_wecom_bridge_change_me
|
||||||
# Jarvis 调 POST /wecom/active-push 时的 Header,须与 ruoyi jarvis.wecom.push-secret 一致
|
# Jarvis 调 POST /wecom/active-push 时的 Header,须与 ruoyi jarvis.wecom.push-secret 一致
|
||||||
push-secret: jarvis_wecom_push_change_me
|
push-secret: jarvis_wecom_push_change_me
|
||||||
# 本地/ruoyi 调 POST /wecom/goofish-active-push 时 Header: X-WxSend-Goofish-Push-Secret
|
|
||||||
goofish-push-secret: jarvis_wecom_goofish_push_change_me
|
|
||||||
|
|
||||||
qywx:
|
qywx:
|
||||||
webhook:
|
webhook:
|
||||||
@@ -86,7 +84,7 @@ qywx:
|
|||||||
agentId: "1000012"
|
agentId: "1000012"
|
||||||
# 与 agentId 对应的应用 Secret(企微后台应用管理),用于 access_token + message/send
|
# 与 agentId 对应的应用 Secret(企微后台应用管理),用于 access_token + message/send
|
||||||
secret: ""
|
secret: ""
|
||||||
# 闲鱼订单通知自建应用:POST /wecom/goofish-active-push(corpid 仍用上面 corpId)
|
# 闲鱼订单通知自建应用:POST /wx/send/goofish(corpid 仍用上面 corpId)
|
||||||
# 生产建议仅通过环境变量注入,勿将真实 Secret 提交仓库
|
# 生产建议仅通过环境变量注入,勿将真实 Secret 提交仓库
|
||||||
goofishAgentId: "1000013"
|
goofishAgentId: "1000013"
|
||||||
goofishSecret: "${QYWX_GOOFISH_SECRET:hINY8boNFjY3lypOKbcukE8wYnNS9wnXeonB4iTO1rA}"
|
goofishSecret: "${QYWX_GOOFISH_SECRET:hINY8boNFjY3lypOKbcukE8wYnNS9wnXeonB4iTO1rA}"
|
||||||
|
|||||||
Reference in New Issue
Block a user