diff --git a/src/main/java/com/budwk/app/base/sms/SmsService.java b/src/main/java/com/budwk/app/base/sms/SmsService.java index 506ba4e..d49621f 100644 --- a/src/main/java/com/budwk/app/base/sms/SmsService.java +++ b/src/main/java/com/budwk/app/base/sms/SmsService.java @@ -2,38 +2,102 @@ package com.budwk.app.base.sms; import java.util.List; +/** + * 短信/消息发送服务接口 + * 保持参数直观可见,调用者无需查看实体类即可知晓参数含义 + */ public interface SmsService { - /** - * 单发 - * @param loginName 工号 - * @param content 内容 - */ - void send(String loginName, String content); + /** + * 单发普通消息(无标题) + * @param loginName 接收人工号(必填) + * @param content 消息内容(必填) + * @return + */ + void sendSingleMessage(String loginName, String content); - /** - * 单发 - * @param loginName 工号 - * @param title 标题 - * @param content 内容 - */ - void send(String loginName, String title, String content); + /** + * 单发普通消息(带标题) + * @param loginName 接收人工号(必填) + * @param title 消息标题(可选,可为null) + * @param content 消息内容(必填) + * @return + */ + void sendSingleMessage(String loginName, String title, String content); - /** - * 单发 - * @param loginName 工号 - * @param title 标题 - * @param content 内容 - * @param link 链接 - */ - void send(String loginName, String title, String content, String url, String mobileUrl); + /** + * 单发文本卡片消息 + * @param loginName 接收人工号(必填) + * @param content 卡片内容(必填) + * @param pcUrl PC端跳转链接(必填) + * @param mobileUrl 移动端跳转链接(必填) + * @return + */ + void sendSingleTextCard(String loginName, String content, String pcUrl, String mobileUrl); - /** - * 群发 多人接收内容相同时使用该方法 - * @param loginNames 工号 - * @param title 标题 - * @param content 内容 - * @param link 链接 - */ - void massSend(List loginNames, String title, String content, String url, String mobileUrl); + /** + * 单发文本卡片消息(带标题) + * @param loginName 接收人工号(必填) + * @param title 卡片标题(可选,可为null) + * @param content 卡片内容(必填) + * @param pcUrl PC端跳转链接(必填) + * @param mobileUrl 移动端跳转链接(必填) + * @return + */ + void sendSingleTextCard(String loginName, String title, String content, String pcUrl, String mobileUrl); + + /** + * 单发图文卡片消息 + * @param loginName 接收人工号(必填) + * @param content 卡片内容(必填) + * @param pcUrl PC端跳转链接(必填) + * @param mobileUrl 移动端跳转链接(必填) + * @param picUrl 图片链接(必填) + * @return + */ + void sendSinglePicTextCard(String loginName, String content, String pcUrl, String mobileUrl, String picUrl); + + /** + * 单发图文卡片消息(带标题) + * @param loginName 接收人工号(必填) + * @param title 卡片标题(可选,可为null) + * @param content 卡片内容(必填) + * @param pcUrl PC端跳转链接(必填) + * @param mobileUrl 移动端跳转链接(必填) + * @param picUrl 图片链接(必填) + * @return + */ + void sendSinglePicTextCard(String loginName, String title, String content, String pcUrl, String mobileUrl, String picUrl); + + /** + * 群发普通消息(多人接收相同内容) + * @param loginNames 接收人工号列表(必填,不能为空列表) + * @param title 消息标题(可选,可为null) + * @param content 消息内容(必填) + * @return void + */ + void massSendMessage(List loginNames, String title, String content); + + /** + * 群发短信文本卡片消息 + * @param loginNames 接收人工号列表(必填,不能为空列表) + * @param title 卡片标题(可选,可为null) + * @param content 卡片内容(必填) + * @param pcUrl PC端跳转链接(必填) + * @param mobileUrl 移动端跳转链接(必填) + * @return + */ + void massSendTextCard(List loginNames, String title, String content, String pcUrl, String mobileUrl); + + /** + * 群发图文卡片消息 + * @param loginNames 接收人工号列表(必填,不能为空列表) + * @param title 卡片标题(可选,可为null) + * @param content 卡片内容(必填) + * @param pcUrl PC端跳转链接(必填) + * @param mobileUrl 移动端跳转链接(必填) + * @param picUrl 图片链接(必填) + * @return + */ + void massSendPicTextCard(List loginNames, String title, String content, String pcUrl, String mobileUrl, String picUrl); } diff --git a/src/main/java/com/budwk/app/base/sms/impl/jshvc/SmsJshvcServiceImpl.java b/src/main/java/com/budwk/app/base/sms/impl/jshvc/SmsJshvcServiceImpl.java index dab8230..31db1cc 100644 --- a/src/main/java/com/budwk/app/base/sms/impl/jshvc/SmsJshvcServiceImpl.java +++ b/src/main/java/com/budwk/app/base/sms/impl/jshvc/SmsJshvcServiceImpl.java @@ -1,21 +1,21 @@ package com.budwk.app.base.sms.impl.jshvc; -import cn.hutool.core.util.StrUtil; -import cn.hutool.crypto.digest.DigestUtil; -import cn.hutool.http.HttpRequest; -import cn.hutool.http.HttpResponse; import com.budwk.app.base.sms.SmsService; -import com.budwk.app.web.commons.auth.utils.SecurityUtil; +import com.budwk.app.todo.platform.TodoPlatformConfig; +import com.wisedu.casp.sdk.api.mc.ReceiverDto; +import com.wisedu.casp.sdk.api.mc.SendMsgRequest; +import com.wisedu.casp.sdk.api.mc.SendMsgResponse; +import com.wisedu.casp.sdk.api.mc.constants.MsgType_Constants; +import com.wisedu.casp.sdk.api.mc.constants.SendType_Constants; +import com.wisedu.casp.sdk.api.mc.constants.WxSendType_Constants; +import com.wisedu.casp.sdk.core.Client; +import com.wisedu.casp.sdk.core.DefaultClient; import lombok.extern.slf4j.Slf4j; -import org.nutz.integration.jedis.RedisService; -import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.json.Json; import org.nutz.lang.Lang; -import org.nutz.lang.util.NutMap; import java.util.List; -import java.util.Map; /** * @ClassName SmsJshvcServiceImpl @@ -28,126 +28,112 @@ import java.util.Map; @IocBean public class SmsJshvcServiceImpl implements SmsService { - private static final String APPID = "1430576717768122368"; - private static final String APP_SECRET = "19A0ACB03FBQHL4R4XQD"; - private static final String TOKEN_URL = "https://gateway.jshvc.edu.cn/token/gateway/accessToken"; - private static final String MSG_URL = "https://gateway.jshvc.edu.cn/mp/restful/v2/message/send"; - private static final String REDIS_KEY_MSG_ACCESS_TOKEN = "msg:token:"; - - @Inject - private RedisService redisService; - - @Override - public void send(String loginName, String content) { - send(loginName, "智慧工会", content); - } - - @Override - public void send(String loginName, String title, String content) { - send(loginName, "智慧工会", content, null, null); - } - - @Override - public void send(String loginName, String title, String content, String url, String mobileUrl) { - Map paramMap = Map.of("userId", loginName); - doSend(title, content, List.of(paramMap), url, mobileUrl); - } - - @Override - public void massSend(List loginNames, String title, String content, String url, String mobileUrl) { - List> receivers = loginNames.stream() - .map(name -> Map.of("userId", name)) - .toList(); - doSend(title, content, receivers, url, mobileUrl); - } - - /** - * sign: 请求签名:(accessToken + 第一个receivers 的userID )的32位小写的MD5加密值,其中如果相应部分没有则忽略 - * msgType: 0: 普通消息(默认) 1: 必读消息 2: 验证码(为验证码时消息一定不入收件箱) - * expiredTime: 当msgType 为1必读消息 ,该字段为必填字段 格式:yyyy-MM-dd HH:mm:ss - * sendType: 1.只发送PC门户 2.只发送移动校园 3.邮件 4.短信 5.微信企业号 6.钉钉企业内部应用工作通知 7.微信服务号 8.welink - * wxSendType: 当发送类型为5时此字段才会生效:text(文本消息)、textcard(文本卡片)、nes(图文卡片,如果图文,则qyWeChatImgUrl必填)、button(按钮卡片详见示例),不传默认为text - * receiverType: 1:用户 2:用户组 3:部门,默认为1 - * - * @param title - * @param content - * @param receivers - */ - private void doSend(String title, String content, List> receivers, String pcUrl, String mobileUrl) { + private void doSend(String sendType, + List receivers, + String title, + String content, + String pcUrl, + String mobileUrl, + String picUrl) { if (Lang.isEmpty(receivers)) { log.error("send fail by receivers is null"); return; } - - // 获取token - String accessToken = buildAccessToken(); - // 获取第一个接收人的userID - String firstUserId = receivers.get(0).get("userId"); - // md5小写加密生成签名 - String sign = DigestUtil.md5Hex(accessToken + firstUserId); - - NutMap paramsMap = new NutMap(); - paramsMap.put("sign", sign); - paramsMap.put("msgType", "0"); - paramsMap.put("subject", "智慧工会"); - paramsMap.put("content", content); - paramsMap.put("sendType", "5"); - paramsMap.put("receivers", receivers); - if (StrUtil.isBlank(pcUrl) && StrUtil.isBlank(mobileUrl)) { - paramsMap.put("wxSendType", "text"); - } else { - paramsMap.put("wxSendType", "textcard"); - paramsMap.put("pcUrl", pcUrl); - paramsMap.put("mobileUrl", mobileUrl); - // 这是图文卡片,后面用到再对接吧 - // paramsMap.put("qyWeChatImgUrl", ""); - paramsMap.put("urlDesc", "查看详情"); - } - - log.debug("send params: {}", Json.toJson(paramsMap)); - - HttpRequest request = HttpRequest.post(MSG_URL) - .header("appId", APPID) - .header("accessToken", accessToken) - .body(Json.toJson(paramsMap)); - try { - HttpResponse response = request.execute(); - log.info("send response body: {}", response.body()); + // 创建客户端 + Client client = new DefaultClient(TodoPlatformConfig.TODO_PLATFORM_BASE_URL, TodoPlatformConfig.TODO_APP_ID, TodoPlatformConfig.TODO_APP_SECRET); + SendMsgRequest request = new SendMsgRequest(); + request.setSubject(title); + request.setContent(content); + request.setMsgType(MsgType_Constants.COMMON); + request.setSendType(SendType_Constants.WECHAT_ENTERPRISE); + request.setWxSendType(sendType); + request.setReceivers(receivers); - NutMap bodyMap = Json.fromJson(NutMap.class, response.body()); - if (bodyMap.getInt("status") == 200) { - log.info("send success code: {}, msg: {}", bodyMap.getInt("status"), bodyMap.getString("msg")); + if (WxSendType_Constants.TEXT_CARD.equals(sendType)) { + request.setPcUrl(pcUrl); + request.setMobileUrl(mobileUrl); + request.setUrlDesc("查看详情"); + } + if (WxSendType_Constants.NEWS.equals(sendType)) { + request.setPcUrl(pcUrl); + request.setMobileUrl(mobileUrl); + request.setUrlDesc("查看详情"); + request.setQyWeChatImgUrl(picUrl); + } + + log.info("发送企业微信通知,请求参数:{}", Json.toJson(request)); + SendMsgResponse response = client.execute(request); + log.info("发送企业微信通知,接口响应数据:{}", Json.toJson(response)); + + if (response.isSuccess() && response.getStatus() == 200) { + log.info("发送企业微信通知成功: {}, msg: {}", response.getStatus(), response.getMsg()); } else { - log.error("send error code: {}, msg: {}", bodyMap.getInt("status"), bodyMap.getString("msg")); + log.info("发送企业微信通知失败:{}", response.getMsg()); } } catch (Exception e) { - log.error("Failed to send: {}", e.getMessage(), e); + log.error("发送企业微信通知失败", e); } } - private String buildAccessToken() { - String token = redisService.get(REDIS_KEY_MSG_ACCESS_TOKEN); - if (StrUtil.isNotBlank(token)) { - return token; - } - HttpRequest request = HttpRequest.get(TOKEN_URL); - request.header("appId", APPID); - request.header("appSecret", APP_SECRET); + @Override + public void sendSingleMessage(String loginName, String content) { + this.sendSingleMessage(loginName, "智慧工会", content); + } - HttpResponse response = request.execute(); + @Override + public void sendSingleMessage(String loginName, String title, String content) { + this.massSendMessage(List.of(loginName), title, content); + } - log.info("accessToken response: {}", Json.toJson(response.body())); + @Override + public void sendSingleTextCard(String loginName, String content, String pcUrl, String mobileUrl) { + this.sendSingleTextCard(loginName, "智慧工会", content, pcUrl, mobileUrl); + } - NutMap bodyMap = Json.fromJson(NutMap.class, response.body()); - if (bodyMap.getInt("errcode") == 0) { - log.info("accessToken status: {}", bodyMap.getInt("errorcode")); - redisService.setex(REDIS_KEY_MSG_ACCESS_TOKEN, 60 * 120, bodyMap.getString("data")); - return bodyMap.getString("data"); - } else { - log.error("accessToken status: {}", bodyMap.getInt("errorcode")); - } - return ""; + @Override + public void sendSingleTextCard(String loginName, String title, String content, String pcUrl, String mobileUrl) { + this.massSendTextCard(List.of(loginName), title, content, pcUrl, mobileUrl); + } + + @Override + public void sendSinglePicTextCard(String loginName, String content, String pcUrl, String mobileUrl, String picUrl) { + this.sendSinglePicTextCard(loginName, "智慧工会", content, pcUrl, mobileUrl, picUrl); + } + + @Override + public void sendSinglePicTextCard(String loginName, String title, String content, String pcUrl, String mobileUrl, String picUrl) { + this.massSendPicTextCard(List.of(loginName), title, content, pcUrl, mobileUrl, picUrl); + } + + @Override + public void massSendMessage(List loginNames, String title, String content) { + List list = loginNames.stream().map(loginName -> { + ReceiverDto receiverDto = new ReceiverDto(); + receiverDto.setUserId(loginName); + return receiverDto; + }).toList(); + this.doSend(WxSendType_Constants.TEXT, list, title, content, null, null, null); + } + + @Override + public void massSendTextCard(List loginNames, String title, String content, String pcUrl, String mobileUrl) { + List list = loginNames.stream().map(loginName -> { + ReceiverDto receiverDto = new ReceiverDto(); + receiverDto.setUserId(loginName); + return receiverDto; + }).toList(); + this.doSend(WxSendType_Constants.TEXT_CARD, list, title, content, pcUrl, mobileUrl, null); + } + + @Override + public void massSendPicTextCard(List loginNames, String title, String content, String pcUrl, String mobileUrl, String picUrl) { + List list = loginNames.stream().map(loginName -> { + ReceiverDto receiverDto = new ReceiverDto(); + receiverDto.setUserId(loginName); + return receiverDto; + }).toList(); + this.doSend(WxSendType_Constants.NEWS, list, title, content, pcUrl, mobileUrl, picUrl); } }