体检统计:"给未选发送消息"bug整改,改成分批发送用户

This commit is contained in:
2026-05-13 10:54:52 +08:00
parent 90c5335526
commit 99fc264143
5 changed files with 331 additions and 126 deletions
@@ -107,6 +107,20 @@ public interface SmsService {
*/
void massSendTextCard(List<String> loginNames, String title, String content, String pcUrl, String mobileUrl);
/**
* 群发短信文本卡片消息
* @param loginNames 接收人工号列表(必填,不能为空列表)
* @param content 卡片内容(必填)
* @param pcUrl PC端跳转链接(必填)
* @param mobileUrl 移动端跳转链接(必填)
* @param type 类型(如:体检管理)
* @param projectName 项目名称
* @param remark 备注信息
* @return
*/
void massSendTextCard(List<String> loginNames, String content, String pcUrl, String mobileUrl, String type, String projectName, String remark);
/**
* 群发图文卡片消息
* @param loginNames 接收人工号列表(必填,不能为空列表)
@@ -1,21 +1,29 @@
package com.budwk.app.base.sms.impl.ncu;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
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 com.budwk.app.sys.models.Sys_msg;
import com.budwk.app.sys.models.Sys_msg_user;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.web.commons.base.Globals;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
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.Times;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -27,145 +35,314 @@ import java.util.stream.Stream;
@IocBean
public class SmsNcuServiceImpl implements SmsService {
@Inject
private SmsTokenUtil smsTokenUtil;
@Inject
private SmsTokenUtil smsTokenUtil;
@Inject
private Dao dao;
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";
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 sendSingleMessage(String loginName, String content) {
this.sendSingleMessage(loginName, "智慧工会", content);
}
@Override
public void sendSingleMessage(String loginName, String title, String content) {
this.massSendMessage(List.of(loginName), title, content);
}
@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 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 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 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 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 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 massSendMessage(List<String> loginNames, String title, String content) {
this.doSend("text", loginNames, 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 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 massSendTextCard(List<String> loginNames, String title, String content, String pcUrl, String mobileUrl) {
this.doSend("textCard", loginNames, 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 massSendTextCard(List<String> loginNames, String content, String pcUrl, String mobileUrl, String type, String projectName, String remark) {
this.doSendAndMessageRecordByType("textCard", loginNames, "智慧工会", content, pcUrl, mobileUrl, null, type, projectName, remark);
}
@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);
}
@Override
public void massSendPicTextCard(List<String> loginNames, String content, String pcUrl, String mobileUrl, String picUrl) {
this.massSendPicTextCard(loginNames, "智慧工会", content, pcUrl, mobileUrl, picUrl);
}
private void doSend(String sendType,
String receivers,
String title,
String content,
String pcUrl,
String mobileUrl,
String picUrl) {
if(!Globals.sso) {
log.error("【log】本地开发模式不允许调用消息接口");
return;
}
if(!Globals.MyConfig.getBoolean("AppNotify", false)) {
log.error("【log】消息配置未开启");
return;
}
@Override
public void massSendPicTextCard(List<String> loginNames, String title, String content, String pcUrl, String mobileUrl, String picUrl) {
this.doSend("news", loginNames, title, content, pcUrl, mobileUrl, 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;
}
private void doSend(String sendType,
List<String> loginNames,
String title,
String content,
String pcUrl,
String mobileUrl,
String picUrl) {
if (!Globals.sso) {
log.error("【log】本地开发模式不允许调用消息接口");
return;
}
if (!Globals.MyConfig.getBoolean("AppNotify", false)) {
log.error("【log】消息配置未开启");
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);
String receivers = String.join(",", loginNames);
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> dataParams = new HashMap<>();
dataParams.put("title", title);
dataParams.put("paramValueJson", Map.of("content", content));
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);
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);
Map<String, Object> dataParams = new HashMap<>();
dataParams.put("title", title);
dataParams.put("paramValueJson", Map.of("content", content));
log.info("【log】发送消息通知,请求参数:{}", Json.toJson(params));
String body = HttpUtil.createPost(SEND_MSG_URL)
.header("Authorization", token)
.header("Content-Type", "application/json")
.body(Json.toJson(params)).execute().body();
JSONObject resultJsonBody = JSONUtil.parseObj(body);
log.info("【log】发送消息通知,接口响应数据:{}", body);
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);
if (resultJsonBody.getInt("code") == 0) {
log.info("【log】发送消息通知成功");
JSONObject data = resultJsonBody.getJSONObject("data");
String sendId = data.getStr("sendId");
System.out.println(sendId);
} else {
log.info("【log】发送消息通知失败{}", resultJsonBody.getStr("message"));
}
} catch (Exception e) {
log.error("【log】发送消息通知失败", e);
}
}
log.info("【log】发送消息通知,请求参数:{}", Json.toJson(params));
String body = HttpUtil.createPost(SEND_MSG_URL)
.header("Authorization", token)
.header("Content-Type", "application/json")
.body(Json.toJson(params)).execute().body();
JSONObject resultJsonBody = JSONUtil.parseObj(body);
log.info("【log】发送消息通知,接口响应数据{}", body);
if (resultJsonBody.getInt("code") == 0) {
log.info("【log】发送消息通知成功");
JSONObject data = resultJsonBody.getJSONObject("data");
String sendId = data.getStr("sendId");
System.out.println(sendId);
} else {
log.info("【log】发送消息通知失败:{}", resultJsonBody.getStr("message"));
}
} catch (Exception e) {
log.error("【log】发送消息通知失败", e);
}
}
/**
* 消息发送 成功后记录消息信息
*
* @param sendType
* @param loginNames
* @param title
* @param content
* @param pcUrl
* @param mobileUrl
* @param projectName
* @param type
* @param remark
*/
private void doSendAndMessageRecordByType(String sendType, List<String> loginNames, String title,
String content, String pcUrl, String mobileUrl, String picUrl, String type, String projectName, String remark) {
if (!Globals.sso) {
log.error("【log】本地开发模式不允许调用消息接口");
return;
}
if (!Globals.MyConfig.getBoolean("AppNotify", false)) {
log.error("【log】消息配置未开启");
return;
}
//消息推送记录
long time = Times.getTS();
String userId = SecurityUtil.getUserId();
Sys_msg sysMsg = new Sys_msg();
sysMsg.setType(type);
sysMsg.setTitle(projectName);
sysMsg.setNote(content);
sysMsg.setSendAt(time);
sysMsg.setCreatedBy(userId);
sysMsg.setCreatedAt(time);
sysMsg.setUpdatedBy(userId);
sysMsg.setUpdatedAt(time);
sysMsg.setDelFlag(false);
sysMsg.setSendType("show");
List<Sys_msg_user> sysMsgUsersInsert = new ArrayList<>();
List<List<String>> loginNameLists = chunkList(loginNames, 50);
for (List<String> loginNameList : loginNameLists) {
String receivers = String.join(",", loginNameList);
if (Lang.isEmpty(receivers)) {
log.error("【log】send fail by receivers is null");
return;
}
List<Sys_msg_user> sysMsgUsers = loginNameList.stream().map(loginName -> {
Sys_msg_user sysMsgUser = new Sys_msg_user();
sysMsgUser.setLoginname(loginName);
sysMsgUser.setReadAt(Times.getTS());
sysMsgUser.setCreatedAt(time);
sysMsgUser.setCreatedBy(userId);
sysMsgUser.setUpdatedAt(time);
sysMsgUser.setUpdatedBy(userId);
sysMsgUser.setDelFlag(false);
return sysMsgUser;
}).collect(Collectors.toList());
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")
.body(Json.toJson(params)).execute().body();
JSONObject resultJsonBody = JSONUtil.parseObj(body);
log.info("【log】发送消息通知,接口响应数据:{}", body);
Integer codeStatus = resultJsonBody.getInt("code");
if (codeStatus == 0) {
log.info("【log】发送消息通知成功");
for (Sys_msg_user sysMsgUser : sysMsgUsers) {
sysMsgUser.setBackStatus(codeStatus);
sysMsgUser.setBackText("发送成功");
}
sysMsgUsersInsert.addAll(sysMsgUsers);
} else {
String message = resultJsonBody.getStr("message");
for (Sys_msg_user sysMsgUser : sysMsgUsers) {
sysMsgUser.setBackStatus(codeStatus);
sysMsgUser.setBackText(message);
}
sysMsgUsersInsert.addAll(sysMsgUsers);
log.info("【log】发送消息通知失败:{}", message);
}
} catch (Exception e) {
String message = "发送失败,接口异常:"+e.getMessage();
for (Sys_msg_user sysMsgUser : sysMsgUsers) {
sysMsgUser.setBackStatus(500);
sysMsgUser.setBackText(message);
}
sysMsgUsersInsert.addAll(sysMsgUsers);
log.error("【log】发送消息通知失败", e);
}
}
//记录发送状态及消息
Set<String> loginNameSet = sysMsgUsersInsert.stream().map(Sys_msg_user::getLoginname).collect(Collectors.toSet());
List<View_user> viewUsers = dao.query(View_user.class, Cnd.where("loginname", "in", loginNameSet));
for (Sys_msg_user sysMsgUser : sysMsgUsersInsert) {
String loginname = sysMsgUser.getLoginname();
View_user viewUser = viewUsers.stream().filter(e -> e.getLoginname().equals(loginname)).findFirst().orElse(null);
if(ObjectUtil.isNotEmpty(viewUser)) {
sysMsgUser.setUserName(viewUser.getUsername());
sysMsgUser.setUnitId(viewUser.getUnitId());
sysMsgUser.setUnitName(viewUser.getUnitName());
sysMsgUser.setUnionId(viewUser.getUnionId());
sysMsgUser.setUnionName(viewUser.getUnionName());
}
}
Sys_msg sys_msg = dao.insert(sysMsg);
for (Sys_msg_user sysMsgUser : sysMsgUsersInsert) {
sysMsgUser.setMsgId(sys_msg.getId());
}
dao.insert(sysMsgUsersInsert);
}
/**
* 不安全的集合
*
* @param list 需要拆分的集合
* @param chunkSize 以多少条数据进行拆分; 每个新集合多少条数据
* @param <T>
* @return
*/
public static <T> List<List<T>> chunkList(List<T> list, int chunkSize) {
List<List<T>> chunks = new CopyOnWriteArrayList<>();
for (int i = 0; i < list.size(); i += chunkSize) {
chunks.add(list.subList(i, Math.min(i + chunkSize, list.size())));
}
return chunks;
}
}
@@ -9,6 +9,7 @@ import org.nutz.dao.interceptor.annotation.PrevInsert;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
@@ -72,4 +73,9 @@ public class Sys_msg extends BaseModel implements Serializable {
@ColDefine(type = ColType.VARCHAR, width = 10)
private String sendType;
@Column
@Comment("备注")
@ColDefine(type = ColType.TEXT)
private String remarks;
}
@@ -74,6 +74,11 @@ public class Sys_msg_user extends BaseModel implements Serializable {
@One(field = "msgId")
private Sys_msg msg;
@Column
@Comment("发送状态")
@ColDefine(type = ColType.INT)
private Integer backStatus;
@Column
@Comment("反馈内容")
@ColDefine(type = ColType.VARCHAR, width = 500)
@@ -447,7 +447,10 @@ public class HealthCheckupSingleController {
smsService.massSendTextCard(loginNameList,
smsMessage,
"#",
Globals.AppDomain);
Globals.AppDomain,
"healthCheckup",
project.getName(),
"未体检人员消息提醒");
return Result.success("提醒成功");
} else {
return Result.success("不存在未体检人员");