This commit is contained in:
@jyuhsin
2026-03-18 11:48:02 +08:00
parent 0f861019b2
commit 8b2cea8a98
4 changed files with 270 additions and 21 deletions
@@ -32,6 +32,8 @@ public class RedisConstant {
// 短信token
public final static String REDIS_SMS_TOKEN_KEY = PLATFORM_REDIS_PREFIX + "platfrom:sms:token:";
public final static String REDIS_TODO_TOKEN_KEY = PLATFORM_REDIS_PREFIX + "platfrom:todo:token:";
public final static String REDIS_KEY_API_SIGN_DEPLOY_NONCE = PLATFORM_REDIS_PREFIX + "api:sign:deploy:nonce:";
public final static String REDIS_KEY_API_SIGN_OPEN_NONCE = PLATFORM_REDIS_PREFIX + "api:sign:open:nonce:";
@@ -2,12 +2,131 @@ 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 消息标题(可选,可为null)
* @param content 消息内容(必填)
* @return
*/
void sendSingleMessage(String loginName, String title, String content);
/**
* 单发文本卡片消息
* @param loginName 接收人工号(必填)
* @param content 卡片内容(必填)
* @param pcUrl PC端跳转链接(必填)
* @param mobileUrl 移动端跳转链接(必填)
* @return
*/
void sendSingleTextCard(String loginName, String content, String pcUrl, 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 content 消息内容(必填)
* @return void
*/
void massSendMessage(List<String> loginNames, String content);
/**
* 群发普通消息(多人接收相同内容)
* @param loginNames 接收人工号列表(必填,不能为空列表)
* @param title 消息标题(可选,可为null)
* @param content 消息内容(必填)
* @return void
*/
void massSendMessage(List<String> loginNames, String title, String content);
/**
* 群发短信文本卡片消息
* @param loginNames 接收人工号列表(必填,不能为空列表)
* @param content 卡片内容(必填)
* @param pcUrl PC端跳转链接(必填)
* @param mobileUrl 移动端跳转链接(必填)
* @return
*/
void massSendTextCard(List<String> loginNames, String content, String pcUrl, String mobileUrl);
/**
* 群发短信文本卡片消息
* @param loginNames 接收人工号列表(必填,不能为空列表)
* @param title 卡片标题(可选,可为null)
* @param content 卡片内容(必填)
* @param pcUrl PC端跳转链接(必填)
* @param mobileUrl 移动端跳转链接(必填)
* @return
*/
void massSendTextCard(List<String> loginNames, String title, String content, String pcUrl, String mobileUrl);
/**
* 群发图文卡片消息
* @param loginNames 接收人工号列表(必填,不能为空列表)
* @param content 卡片内容(必填)
* @param pcUrl PC端跳转链接(必填)
* @param mobileUrl 移动端跳转链接(必填)
* @param picUrl 图片链接(必填)
* @return
*/
void massSendPicTextCard(List<String> loginNames, String content, String pcUrl, String mobileUrl, String picUrl);
/**
* 群发图文卡片消息
* @param loginNames 接收人工号列表(必填,不能为空列表)
* @param title 卡片标题(可选,可为null)
* @param content 卡片内容(必填)
* @param pcUrl PC端跳转链接(必填)
* @param mobileUrl 移动端跳转链接(必填)
* @param picUrl 图片链接(必填)
* @return
*/
void massSendPicTextCard(List<String> loginNames, String title, String content, String pcUrl, String mobileUrl, String picUrl);
}
@@ -1,27 +1,155 @@
package com.budwk.app.base.sms.impl.ncu;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.sms.SmsService;
import com.budwk.app.base.sms.impl.ncu.util.SmsTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.Lang;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 南昌大学短信实现
*/
@IocBean
@Slf4j
@IocBean
public class SmsNcuServiceImpl implements SmsService {
private static final String SEND_MSG_URL = "https://api.ucpass.cn/sms/send";
private static final String APP_ID = "";
private static final String SEND_MSG_URL = "https://poa.ncu.edu.cn/apis/messagecenter/v1/poaMessage/messageSend";
private static final String APP_ID = "72e34a101d3011f1a74cbae5193504f4";
private static final String TEMPLATE_ID = "MT1773223005000";
@Override
public void sendSingleMessage(String loginName, String content) {
this.sendSingleMessage(loginName, "智慧工会", content);
}
@Override
public void send(String loginName, String content) {
String token = SmsTokenUtil.getToken();
}
@Override
public void sendSingleMessage(String loginName, String title, String content) {
this.massSendMessage(List.of(loginName), title, content);
}
@Override
public void sendSingleTextCard(String loginName, String content, String pcUrl, String mobileUrl) {
this.sendSingleTextCard(loginName, "智慧工会", content, pcUrl, mobileUrl);
}
@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<String> loginNames, String content) {
this.massSendMessage(loginNames, "智慧工会", content);
}
@Override
public void massSendMessage(List<String> loginNames, String title, String content) {
String list = String.join(",", loginNames);
this.doSend("text", list, title, content, null, null, null);
}
@Override
public void massSendTextCard(List<String> loginNames, String content, String pcUrl, String mobileUrl) {
this.massSendTextCard(loginNames, "智慧工会", content, pcUrl, mobileUrl);
}
@Override
public void massSendTextCard(List<String> loginNames, String title, String content, String pcUrl, String mobileUrl) {
String list = String.join(",", loginNames);
this.doSend("textCard", list, title, content, pcUrl, mobileUrl, null);
}
@Override
public void massSendPicTextCard(List<String> loginNames, String content, String pcUrl, String mobileUrl, String picUrl) {
this.massSendPicTextCard(loginNames, "智慧工会", content, pcUrl, mobileUrl, picUrl);
}
@Override
public void massSendPicTextCard(List<String> loginNames, String title, String content, String pcUrl, String mobileUrl, String picUrl) {
String list = String.join(",", loginNames);
this.doSend("news", list, title, content, pcUrl, mobileUrl, picUrl);
}
private void doSend(String sendType,
String receivers,
String title,
String content,
String pcUrl,
String mobileUrl,
String picUrl) {
if (Lang.isEmpty(receivers)) {
log.error("【log】send fail by receivers is null");
return;
}
try {
String token = SmsTokenUtil.getToken();
if (Lang.isEmpty(token)) {
log.error("【log】send fail by token is null");
return;
}
Map<String, Object> params = new HashMap<>();
params.put("appId", APP_ID);
params.put("sendId", "");
params.put("sendType", Stream.of("WECHAT").toArray());
params.put("messageTypeCode", TEMPLATE_ID);
params.put("promise", false);
params.put("importantIdentity", false);
params.put("toPersons", receivers);
Map<String, Object> dataParams = new HashMap<>();
dataParams.put("title", title);
dataParams.put("paramValueJson", Map.of("content", content));
if ("textCard".equals(sendType)) {
dataParams.put("url", pcUrl);
dataParams.put("mobileUrl", mobileUrl);
}
if ("news".equals(sendType)) {
dataParams.put("url", pcUrl);
dataParams.put("mobileUrl", mobileUrl);
dataParams.put("coverImageUrl", picUrl);
}
params.put("data", dataParams);
log.info("【log】发送消息通知,请求参数:{}", Json.toJson(params));
String body = HttpUtil.createPost(SEND_MSG_URL)
.header("Authorization", token)
.header("Content-Type", "application/json")
.form(params).execute().body();
JSONObject resultJsonBody = JSONUtil.parseObj(body);
log.info("【log】发送消息通知,接口响应数据:{}", body);
if (resultJsonBody.getInt("code") == 0) {
log.info("【log】发送消息通知成功");
} else {
log.info("【log】发送消息通知失败:{}", resultJsonBody.getStr("message"));
}
} catch (Exception e) {
log.error("【log】发送消息通知失败", e);
}
}
}
@@ -5,8 +5,10 @@ import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.constant.RedisConstant;
import lombok.extern.slf4j.Slf4j;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.json.Json;
import java.util.HashMap;
import java.util.Map;
@@ -18,19 +20,16 @@ import java.util.Map;
* @Date 2025/12/3 9:53
* @注释 :短信token工具类
*/
@Slf4j
public class SmsTokenUtil {
@Inject
private static RedisService redisService;
private static final String TOKEN_URL = "https://api.ucpass.cn/token";
private static final String CLINT_ID = "";
private static final String CLINT_SECRET = "";
private static final String SCOPE = "";
private static final String TOKEN_URL = "https://poa.ncu.edu.cn/oauth2/token";
private static final String CLINT_ID = "dP7SXC1MoYAJHPh6k8Cgsda8tGY=";
private static final String CLINT_SECRET = "PFAwVcXOsVFzP3Jwb4RyO5V97o8j8zNTQ8QSTre8oIc=";
private static final String SCOPE = "messagecenter:v1:sendMessage";
public static String getToken() {
String key = RedisConstant.REDIS_SMS_TOKEN_KEY;
@@ -48,6 +47,7 @@ public class SmsTokenUtil {
.formStr(param).execute().body();
JSONObject resultJsonBody = JSONUtil.parseObj(body);
log.info("【log】获取消息token接口回调数据:{}", Json.toJson(resultJsonBody));
if (resultJsonBody.getInt("code") == 200) {
String accessToken = resultJsonBody.getStr("access_token");
Integer expiresIn = resultJsonBody.getInt("expires_in");