package com.ruoyi.system.service.impl;
|
|
import com.ruoyi.common.core.domain.entity.SysUser;
|
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.system.domain.QyWechatArticle;
|
import com.ruoyi.system.mapper.SysUserMapper;
|
import com.ruoyi.system.service.IQyWechatAccessTokenService;
|
import com.ruoyi.system.service.IQyWechatService;
|
import com.ruoyi.system.service.ISysConfigService;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
|
import java.util.ArrayList;
|
import java.util.Collections;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
|
/**
|
* 企业微信服务实现类
|
*
|
* @author ruoyi
|
* @date 2025-12-11
|
*/
|
@Service
|
public class QyWechatServiceImpl implements IQyWechatService {
|
|
private static final Logger log = LoggerFactory.getLogger(QyWechatServiceImpl.class);
|
|
@Autowired
|
private IQyWechatAccessTokenService qyWechatAccessTokenService;
|
|
@Autowired
|
private ISysConfigService configService;
|
|
@Autowired
|
private SysUserMapper userMapper;
|
|
/**
|
* 发送企业微信消息
|
*/
|
@Override
|
public boolean sendNotifyMessage(Long userId, String title, String content,String notifyUrl) {
|
try {
|
// 检查服务是否启用
|
if (!isEnabled()) {
|
log.info("企业微信服务未启用,跳过消息发送");
|
return false;
|
}
|
|
// 获取用户的企业微信ID
|
String qyUserId = getQyUserIdByUserId(userId);
|
if (StringUtils.isEmpty(qyUserId)) {
|
log.warn("用户{}未绑定企业微信ID,无法发送消息", userId);
|
return false;
|
}
|
|
// 发送文本消息
|
return sendTextMessage(qyUserId, title, content, notifyUrl);
|
} catch (Exception e) {
|
log.error("企业微信消息发送异常,userId={}", userId, e);
|
return false;
|
}
|
}
|
|
/**
|
* 发送企业微信文本消息
|
*/
|
@Override
|
public boolean sendTextMessage(String qyUserId, String title, String content, String notifyUrl) {
|
try {
|
// 检查服务是否启用
|
if (!isEnabled()) {
|
log.info("企业微信服务未启用,跳过消息发送");
|
return false;
|
}
|
|
// 获取企业微信配置
|
String corpId = configService.selectConfigByKey("qy_wechat.corp_id");
|
String corpSecret = configService.selectConfigByKey("qy_wechat.corp_secret");
|
|
if (StringUtils.isEmpty(corpId) || StringUtils.isEmpty(corpSecret)) {
|
log.error("企业微信配置不完整,缺少corpId或corpSecret");
|
return false;
|
}
|
|
// 获取AccessToken
|
String accessToken = qyWechatAccessTokenService.getAppAccessToken(corpId, corpSecret);
|
if (StringUtils.isEmpty(accessToken)) {
|
log.error("获取企业微信AccessToken失败");
|
return false;
|
}
|
|
// 构造请求URL
|
String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + accessToken;
|
|
// 构造文章对象
|
QyWechatArticle article = new QyWechatArticle();
|
article.setTitle(title);
|
article.setDescription(content);
|
article.setUrl(notifyUrl);
|
// 设置默认图片URL,您可以根据需要修改
|
|
|
// 构造请求参数
|
Map<String, Object> params = new HashMap<>();
|
params.put("touser", qyUserId);
|
params.put("msgtype", "news");
|
params.put("agentid", Integer.parseInt(configService.selectConfigByKey("qy_wechat.agent_id")));
|
|
// 构造文章列表
|
List<QyWechatArticle> articles = new ArrayList<>();
|
articles.add(article);
|
params.put("news", Collections.singletonMap("articles", articles));
|
|
// 发送HTTP POST请求
|
String response = sendHttpPostRequest(url, params);
|
|
if (StringUtils.isEmpty(response)) {
|
log.error("发送企业微信消息失败,响应为空");
|
return false;
|
}
|
|
// 解析响应结果
|
QyWechatResponse result = parseResponse(response);
|
|
if (result != null && result.getErrcode() == 0) {
|
log.info("企业微信消息发送成功,用户ID: {}", qyUserId);
|
return true;
|
} else {
|
log.error("企业微信消息发送失败,错误码: {}, 错误信息: {}",
|
result != null ? result.getErrcode() : "unknown",
|
result != null ? result.getErrmsg() : response);
|
return false;
|
}
|
} catch (Exception e) {
|
log.error("企业微信文本消息发送异常,qyUserId={}", qyUserId, e);
|
return false;
|
}
|
}
|
|
/**
|
* 获取用户的企业微信ID
|
*/
|
@Override
|
public String getQyUserIdByUserId(Long userId) {
|
try {
|
if (userId == null) {
|
log.warn("用户ID不能为空");
|
return null;
|
}
|
|
// 查询用户信息
|
SysUser user = userMapper.selectUserById(userId);
|
if (user == null) {
|
log.warn("未找到用户,userId={}", userId);
|
return null;
|
}
|
|
// 返回企业微信用户ID
|
return user.getQyWechatUserId();
|
} catch (Exception e) {
|
log.error("获取用户企业微信ID异常,userId={}", userId, e);
|
return null;
|
}
|
}
|
|
/**
|
* 检查企业微信服务是否启用
|
*/
|
@Override
|
public boolean isEnabled() {
|
try {
|
String enabled = configService.selectConfigByKey("qy_wechat.enable");
|
return "true".equals(enabled);
|
} catch (Exception e) {
|
log.warn("获取企业微信服务启用状态失败,使用默认值(false)", e);
|
return false;
|
}
|
}
|
|
/**
|
* 发送HTTP POST请求
|
*
|
* @param url 请求URL
|
* @param params 请求参数
|
* @return 响应内容
|
*/
|
private String sendHttpPostRequest(String url, Map<String, Object> params) {
|
try {
|
// 将参数转换为JSON字符串
|
String jsonParams = toJsonString(params);
|
|
java.net.HttpURLConnection conn = (java.net.HttpURLConnection) new java.net.URL(url).openConnection();
|
conn.setRequestMethod("POST");
|
conn.setConnectTimeout(5000);
|
conn.setReadTimeout(5000);
|
conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
|
conn.setDoOutput(true);
|
|
// 发送请求数据
|
java.io.OutputStream os = conn.getOutputStream();
|
os.write(jsonParams.getBytes("UTF-8"));
|
os.flush();
|
os.close();
|
|
int responseCode = conn.getResponseCode();
|
if (responseCode == 200) {
|
java.io.BufferedReader reader = new java.io.BufferedReader(
|
new java.io.InputStreamReader(conn.getInputStream(), "UTF-8"));
|
StringBuilder response = new StringBuilder();
|
String line;
|
while ((line = reader.readLine()) != null) {
|
response.append(line);
|
}
|
reader.close();
|
return response.toString();
|
} else {
|
log.error("HTTP请求失败,响应码: {}", responseCode);
|
return null;
|
}
|
} catch (Exception e) {
|
log.error("发送HTTP POST请求失败", e);
|
return null;
|
}
|
}
|
|
/**
|
* 简单的JSON序列化方法
|
*
|
* @param map 要序列化的Map
|
* @return JSON字符串
|
*/
|
private String toJsonString(Map<String, Object> map) {
|
StringBuilder json = new StringBuilder("{");
|
boolean first = true;
|
|
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
if (!first) {
|
json.append(",");
|
}
|
|
json.append("\"").append(entry.getKey()).append("\":");
|
|
Object value = entry.getValue();
|
if (value instanceof String) {
|
json.append("\"").append(value).append("\"");
|
} else if (value instanceof Map) {
|
// 处理嵌套Map
|
json.append(toJsonString((Map<String, Object>) value));
|
} else if (value instanceof List) {
|
// 处理列表
|
json.append(toJsonList((List<Object>) value));
|
} else {
|
json.append(value);
|
}
|
|
first = false;
|
}
|
|
json.append("}");
|
return json.toString();
|
}
|
|
/**
|
* 序列化列表为JSON
|
*
|
* @param list 列表
|
* @return JSON字符串
|
*/
|
private String toJsonList(List<Object> list) {
|
StringBuilder json = new StringBuilder("[");
|
boolean first = true;
|
|
for (Object item : list) {
|
if (!first) {
|
json.append(",");
|
}
|
|
if (item instanceof String) {
|
json.append("\"").append(item).append("\"");
|
} else if (item instanceof Map) {
|
json.append(toJsonString((Map<String, Object>) item));
|
} else if (item instanceof QyWechatArticle) {
|
json.append(toJsonArticle((QyWechatArticle) item));
|
} else {
|
json.append(item);
|
}
|
|
first = false;
|
}
|
|
json.append("]");
|
return json.toString();
|
}
|
|
/**
|
* 序列化文章对象为JSON
|
*
|
* @param article 文章对象
|
* @return JSON字符串
|
*/
|
private String toJsonArticle(QyWechatArticle article) {
|
StringBuilder json = new StringBuilder("{");
|
boolean first = true;
|
|
// 添加非空字段
|
if (article.getTitle() != null) {
|
if (!first) json.append(",");
|
json.append("\"title\":\"").append(escapeJsonString(article.getTitle())).append("\"");
|
first = false;
|
}
|
if (article.getDescription() != null) {
|
if (!first) json.append(",");
|
json.append("\"description\":\"").append(escapeJsonString(article.getDescription())).append("\"");
|
first = false;
|
}
|
if (article.getUrl() != null) {
|
if (!first) json.append(",");
|
json.append("\"url\":\"").append(escapeJsonString(article.getUrl())).append("\"");
|
first = false;
|
}
|
if (article.getPicurl() != null) {
|
if (!first) json.append(",");
|
json.append("\"picurl\":\"").append(escapeJsonString(article.getPicurl())).append("\"");
|
first = false;
|
}
|
if (article.getAppid() != null) {
|
if (!first) json.append(",");
|
json.append("\"appid\":\"").append(escapeJsonString(article.getAppid())).append("\"");
|
first = false;
|
}
|
if (article.getPagepath() != null) {
|
if (!first) json.append(",");
|
json.append("\"pagepath\":\"").append(escapeJsonString(article.getPagepath())).append("\"");
|
first = false;
|
}
|
|
json.append("}");
|
return json.toString();
|
}
|
|
/**
|
* 转义JSON字符串中的特殊字符
|
*
|
* @param str 原始字符串
|
* @return 转义后的字符串
|
*/
|
private String escapeJsonString(String str) {
|
if (str == null) return "";
|
return str.replace("\\", "\\\\")
|
.replace("\"", "\\\"")
|
.replace("\n", "\\n")
|
.replace("\r", "\\r")
|
.replace("\t", "\\t");
|
}
|
|
/**
|
* 解析企业微信响应
|
*
|
* @param response 响应JSON
|
* @return 响应对象
|
*/
|
private QyWechatResponse parseResponse(String response) {
|
try {
|
// 使用简单JSON解析
|
QyWechatResponse result = new QyWechatResponse();
|
|
// 移除首尾花括号
|
String content = response.trim();
|
if (content.startsWith("{")) {
|
content = content.substring(1);
|
}
|
if (content.endsWith("}")) {
|
content = content.substring(0, content.length() - 1);
|
}
|
|
// 按逗号分割键值对
|
String[] pairs = content.split(",");
|
|
for (String pair : pairs) {
|
String[] keyValue = pair.split(":", 2); // 只分割第一个冒号
|
if (keyValue.length == 2) {
|
String key = keyValue[0].trim().replaceAll("\"", "");
|
String value = keyValue[1].trim().replaceAll("\"", "");
|
|
switch (key) {
|
case "errcode":
|
result.setErrcode(Integer.parseInt(value));
|
break;
|
case "errmsg":
|
result.setErrmsg(value);
|
break;
|
}
|
}
|
}
|
|
return result;
|
} catch (Exception e) {
|
log.error("解析企业微信响应失败: {}", response, e);
|
return null;
|
}
|
}
|
|
/**
|
* 企业微信响应内部类
|
*/
|
private static class QyWechatResponse {
|
private int errcode;
|
private String errmsg;
|
|
// Getters and Setters
|
public int getErrcode() {
|
return errcode;
|
}
|
|
public void setErrcode(int errcode) {
|
this.errcode = errcode;
|
}
|
|
public String getErrmsg() {
|
return errmsg;
|
}
|
|
public void setErrmsg(String errmsg) {
|
this.errmsg = errmsg;
|
}
|
}
|
}
|