prd 消息平台改用浙师大接口及 v3 鉴权参数。
    企业微信沿用 v3 参数,补齐授权入口和回调。
    prd 系统域名固定为 https://lxy.zjnu.edu.cn。
    登录跳转兼容本站完整 URL。
    清空其他学校的废弃对接参数。
    公众号填入模拟参数,保持关闭;dev 配置不变。
This commit is contained in:
2026-10-10 16:13:37 +08:00
parent c262c8e715
commit 48329f933b
7 changed files with 250 additions and 26 deletions
@@ -50,6 +50,8 @@ public class SmsServiceImpl implements SmsService {
@Inject
private PropertiesProxy conf;
@Inject
private ZjnuMsgService zjnuMsgService;
/**
* 发送短信消息。
@@ -59,6 +61,9 @@ public class SmsServiceImpl implements SmsService {
*/
@Override
public MsgPlatformResponse sendSms(MsgPlatformSmsRequest request) {
if ("prd".equalsIgnoreCase(conf.get("nutz.profiles.active"))) {
return zjnuMsgService.send(request, "SMS");
}
validateCommonRequest(request);
JSONObject payload = buildCommonPayload(request, true);
return doPost(SMS_API_PATH, payload);
@@ -107,6 +112,9 @@ public class SmsServiceImpl implements SmsService {
*/
@Override
public MsgPlatformResponse sendWechat(MsgPlatformWechatRequest request) {
if ("prd".equalsIgnoreCase(conf.get("nutz.profiles.active"))) {
return zjnuMsgService.send(request, "WeChat");
}
validateCommonRequest(request);
if (Strings.isBlank(request.getWechatType())) {
request.setWechatType("text");
@@ -342,6 +350,9 @@ public class SmsServiceImpl implements SmsService {
* @param request 平台请求参数
*/
private void fillDefaultSenderInfo(MsgPlatformBaseRequest request) {
if ("prd".equalsIgnoreCase(conf.get("nutz.profiles.active"))) {
return;
}
request.setSendUserId(getRequiredConfig("msg.platform.default-send-user-id", "请先配置 msg.platform.default-send-user-id"));
request.setSendUserName(getRequiredConfig("msg.platform.default-send-user-name", "请先配置 msg.platform.default-send-user-name"));
request.setSendUnitId(conf.get("msg.platform.default-send-unit-id", ""));
@@ -0,0 +1,146 @@
package com.budwk.app.base.sms.impl;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.base.sms.model.*;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.web.commons.base.Globals;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.impl.PropertiesProxy;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@IocBean
public class ZjnuMsgService {
private static final Log log = Logs.get();
@Inject
private PropertiesProxy conf;
@Inject
private RedisService redisService;
@Inject
private Dao dao;
public MsgPlatformResponse send(MsgPlatformBaseRequest request, String channel) {
MsgPlatformResponse result = new MsgPlatformResponse();
if (!conf.getBoolean("msg.platform.enabled", false)
|| !Globals.MyConfig.getBoolean("SendMsg", Globals.MyConfig.getBoolean("AppSms", false))) {
result.setCode(0);
result.setResult(true);
result.setMsg("通讯平台未启用,已跳过实际发送");
return result;
}
if (request == null || Strings.isBlank(request.getInfo())) {
throw new BaseException("浙师大消息内容不能为空");
}
List<MsgPlatformRecipient> recipients = request.getRecipients();
if (Strings.isNotBlank(request.getPersonInfo())) {
recipients = new ArrayList<>();
for (String line : request.getPersonInfo().split("\\^@\\^")) {
String[] fields = line.split("\\|", -1);
MsgPlatformRecipient recipient = new MsgPlatformRecipient();
recipient.setName(fields[0]);
recipient.setAccount(fields.length > 1 ? fields[1] : "");
recipient.setMobile(fields.length > 4 ? fields[4] : "");
recipients.add(recipient);
}
}
if (recipients == null || recipients.isEmpty()) {
throw new BaseException("接收人列表不能为空");
}
List<Map<String, Object>> receivers = new ArrayList<>();
for (MsgPlatformRecipient recipient : recipients) {
if (recipient == null) {
throw new BaseException("接收人信息不能为空");
}
String account = recipient.getAccount();
String mobile = recipient.getMobile();
if (Strings.isBlank(mobile) && Strings.isNotBlank(account) && account.matches("^\\d{11}$")) {
mobile = account;
account = null;
}
if (Strings.isBlank(account) && Strings.isNotBlank(mobile)) {
List<Sys_user> users = dao.query(Sys_user.class, Cnd.where("mobile", "=", mobile));
if (users.size() != 1) {
throw new BaseException("无法通过手机号唯一确定浙师大消息接收人工号");
}
account = users.get(0).getLoginname();
}
if (Strings.isBlank(account)) {
throw new BaseException("浙师大消息接收人工号不能为空");
}
receivers.add(Map.of("type", "User", "userId", account, "name", Strings.sNull(recipient.getName())));
}
try {
Map<String, Object> payload = new LinkedHashMap<>();
payload.put("token", getToken());
payload.put("msgtitle", Globals.AppName);
payload.put("msgContent", request.getInfo().replace("&lt;", "<").replace("&gt;", ">"));
payload.put("msgType", "0");
payload.put("sendStatus", "1");
payload.put("sendChannel", channel);
payload.put("sendMode", "normal");
payload.put("receivers", JSONUtil.toJsonStr(receivers));
payload.put("summary", "");
payload.put("sendTime", Strings.sNull(request.getSendTime()));
payload.put("isReply", "0");
payload.put("sendMsgType", "1");
try (HttpResponse response = HttpRequest.post(baseUrl() + "/message/sendMessageApi")
.form(payload).timeout(conf.getInt("msg.platform.read-timeout", 10000)).execute()) {
JSONObject body = JSONUtil.parseObj(response.body());
Integer code = body.getInt("rspCode");
result.setHttpStatus(response.getStatus());
result.setCode(code);
result.setResult(response.getStatus() == 200 && Integer.valueOf(0).equals(code));
result.setMsg(body.getStr("rspMsg"));
if (!Boolean.TRUE.equals(result.getResult())) {
log.warn("浙师大消息发送失败,状态码:" + response.getStatus() + ",业务码:" + code);
}
return result;
}
} catch (Exception e) {
log.warn("浙师大消息接口调用失败:" + e.getClass().getSimpleName());
result.setCode(-1);
result.setResult(false);
result.setMsg("浙师大消息接口调用失败");
return result;
}
}
private String getToken() {
String appId = conf.get("custom.msg.platform.app-id");
String cacheKey = "zjnu:msg:token:" + appId;
String token = redisService.get(cacheKey);
if (Strings.isNotBlank(token)) {
return token;
}
try (HttpResponse response = HttpRequest.get(baseUrl() + "/msg/getThirdAPIToken")
.form("appId", appId, "appPassword", conf.get("custom.msg.platform.app-password"))
.timeout(conf.getInt("msg.platform.read-timeout", 10000)).execute()) {
JSONObject body = JSONUtil.parseObj(response.body());
JSONObject data = body.getJSONObject("resultData");
token = data == null ? null : data.getStr("token");
if (response.getStatus() != 200 || Strings.isBlank(token)) {
throw new BaseException("获取浙师大消息平台token失败");
}
redisService.setex(cacheKey, 1200, token);
return token;
}
}
private String baseUrl() {
return conf.get("msg.platform.base-url").replaceAll("/+$", "");
}
}
@@ -190,23 +190,72 @@ public class SysLoginController {
return ">>:" + sysUserService.loginPlus(sysUser, LoginType.CAS, request);
}
@At(value = "/platform/wxwork/oauth2/callback", top = true)
@At(value = {"/platform/qywechat/oauth", "/platform/qywechat"}, top = true)
@Ok("re")
public String qywechatOauth(@Param("redirect") String redirect, HttpServletRequest request) {
if (!"prd".equalsIgnoreCase(conf.get("nutz.profiles.active")) || !conf.getBoolean("wxwork.enable", false)) {
return ">>:/platform/home";
}
String state = UUID.randomUUID().toString();
request.getSession().setAttribute("zjnuWxworkState", state);
String callback = Globals.AppDomain + conf.get("wxwork.redirectUri");
if (StrUtil.isNotBlank(redirect)) {
callback += "?redirect=" + URLEncoder.encode(redirect, StandardCharsets.UTF_8);
}
return ">>:https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + conf.get("wxwork.corpId")
+ "&redirect_uri=" + URLEncoder.encode(callback, StandardCharsets.UTF_8)
+ "&response_type=code&scope=snsapi_base&state=" + state + "#wechat_redirect";
}
private String getZjnuWxworkToken() {
String cacheKey = RedisConstant.REDIS_KEY_WX_WORK_TOKEN + conf.get("wxwork.corpId");
String token = redisService.get(cacheKey);
if (StrUtil.isNotBlank(token)) {
return token;
}
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=" + conf.get("wxwork.corpId")
+ "&corpsecret=" + conf.get("wxwork.corpSecret");
JSONObject result = JSONUtil.parseObj(HttpUtil.get(url));
token = result.getStr("access_token");
if (!Integer.valueOf(0).equals(result.getInt("errcode")) || StrUtil.isBlank(token)) {
throw new BaseException("????????????");
}
redisService.setex(cacheKey, Math.max(1, result.getInt("expires_in", 7200) - 200), token);
return token;
}
@At(value = {"/platform/wxwork/oauth2/callback", "/platform/qywechat/getCode"}, top = true)
@Ok("re")
@ApiOperation("企业微信登录回调")
public String wxworkOAuth2(@Param("code") String code, @Param("redirect") String redirect, HttpServletRequest request) {
if (StrUtil.isBlank(code)) {
return ">>:/platform/home";
}
String token = redisService.get(RedisConstant.REDIS_KEY_WX_WORK_TOKEN);
boolean zjnu = "prd".equalsIgnoreCase(conf.get("nutz.profiles.active"));
if (zjnu) {
Object expectedState = request.getSession().getAttribute("zjnuWxworkState");
request.getSession().removeAttribute("zjnuWxworkState");
if (!conf.getBoolean("wxwork.enable", false) || expectedState == null
|| !expectedState.equals(request.getParameter("state"))) {
return ">>:/platform/home/500";
}
}
String token = zjnu ? getZjnuWxworkToken() : redisService.get(RedisConstant.REDIS_KEY_WX_WORK_TOKEN);
if (StrUtil.isBlank(token)) {
return ">>:/platform/home";
}
String url = "https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token=ACCESS_TOKEN&code=CODE";
if (zjnu) {
url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=ACCESS_TOKEN&code=CODE";
}
url = url.replace("ACCESS_TOKEN", token).replace("CODE", code);
String response = HttpUtil.get(url);
JSONObject jsonObject = JSONUtil.parseObj(response);
if (jsonObject.getInt("errcode") == 0) {
String loginName = jsonObject.getStr("userid");
String loginName = jsonObject.getStr(zjnu ? "UserId" : "userid");
if (StrUtil.isBlank(loginName)) {
return ">>:/platform/home/500";
}
sysUserService.checkThirdPlatformLoginName(loginName);
Sys_user sysUser = sysUserService.loginByLoginName(loginName);
return ">>:" + sysUserService.loginPlus(sysUser, LoginType.QI_YE_WECHAT, request);
@@ -496,6 +496,11 @@ public class SysUserServiceImpl extends BaseServiceImpl<Sys_user> implements Sys
String redirect = request.getParameter("redirect");
if (StrUtil.isNotBlank(redirect)) {
if ("prd".equalsIgnoreCase(conf.get("nutz.profiles.active"))
&& (redirect.equals(Globals.AppDomain) || redirect.startsWith(Globals.AppDomain + "/")
|| redirect.startsWith(Globals.AppDomain + "?") || redirect.startsWith(Globals.AppDomain + "#"))) {
return redirect;
}
return Globals.AppDomain + redirect;
} else {
return Globals.AppDomain + "/platform/home";
@@ -59,8 +59,12 @@ public class Globals {
private SysTaskService sysTaskService;
@Inject
private PropertiesProxy conf;
private static String productionAppDomain;
public void init() {
if ("prd".equalsIgnoreCase(conf.get("nutz.profiles.active"))) {
productionAppDomain = conf.get("custom.app-domain");
}
initSysConfig(sysConfigService);
initRoute(sysRouteService);
initTask(sysTaskService);
@@ -95,6 +99,9 @@ public class Globals {
default -> Globals.MyConfig.put(sysConfig.getConfigKey(), sysConfig.getConfigValue());
}
}
if (Strings.isNotBlank(productionAppDomain)) {
Globals.AppDomain = productionAppDomain;
}
}
public static void initRoute(SysRouteService sysRouteService) {
@@ -188,7 +188,10 @@ public class WkFailProcessor extends ViewProcessor {
if (Globals.sso) {
//1.判断是否为企业微信浏览器并且对接了企业微信
if (browserPlatform == BrowserPlatform.QI_YE_WEI_XIN && propertiesProxy.getBoolean("wxwork.enable", false)) {
if (browserPlatform == BrowserPlatform.QI_YE_WEI_XIN && propertiesProxy.getBoolean("wxwork.enable", false)
&& "prd".equalsIgnoreCase(propertiesProxy.get("nutz.profiles.active"))) {
new ServerRedirectView("/platform/qywechat/oauth?redirect=" + redirect).render(request, response, null);
} else if (browserPlatform == BrowserPlatform.QI_YE_WEI_XIN && propertiesProxy.getBoolean("wxwork.enable", false)) {
//进行企业微信授权
// String token = getWxWorkToken();
// if (StrUtil.isNotBlank(token)) {
+25 -22
View File
@@ -23,21 +23,21 @@
sdk:
wxwork:
enable: false
corpId: wwaca81b3016b9a58c
corpSecret: Auc7919Su1Uds5ZErfZybBrt-8FkP5NA-dldZyQuOrM
agentId: 1000131
redirectUri: /platform/wxwork/oauth2/callback
enable: true
corpId: 'ww5386df99fd3b6b0f'
corpSecret: 'HpSptE9wKZRq_c_kemAPJc0AfXkNSevpfYCJd1TTgrI'
agentId: ''
redirectUri: /platform/qywechat/getCode
wx:
enable: false
appID: wx4f65ff5f1b72e99d
appSecret: a65cec4fb69980bd88258fc8433a3eef
appID: 'MOCK_ZJNU_WX_APP_ID'
appSecret: 'MOCK_ZJNU_WX_APP_SECRET'
redirectUri: /platform/wx/oauth2/callback
cas:
enable: true
server-url-prefix: https://authserver.zjnu.edu.cn/authserver
server-login-url: https://authserver.zjnu.edu.cn/authserver/login
qi-wx-server-login-url: httos:/cas.whcp.eou.cn/lyuapServer/wechatauz/wxAuthRedirect
qi-wx-server-login-url: https://authserver.zjnu.edu.cn/authserver/login
client-host-url: https://lxy.zjnu.edu.cn
client-call-back-url: /sso/login
validation-type: cas
@@ -48,6 +48,7 @@ sdk:
bucket: zjnu
custom:
app-domain: https://lxy.zjnu.edu.cn
spdb:
bab7:
url: http://localhost:8888/supplierOpenApi/pointOrder/queryOrderInfo
@@ -55,26 +56,28 @@ custom:
keyHex: 00112233445566778899aabbccddeeff
ivHex: 0102030405060708090a0b0c0d0e0f10
data-center:
grant_type: password
scope: read
username: 3848A3C738EFBB2C2E76E0D59B419DACA16A80F3AB0354CD
password: 3848A3C738EFBB2C2E76E0D59B419DACA16A80F3AB0354CD
token-url: https://bd.whcp.edu.cn/sjjkfwpt/api/bd-api/oauth/token
grant_type: ''
scope: ''
username: ''
password: ''
token-url: ''
urls:
teacher: http://mdm.zjnu.edu.cn/do/api/call/lxyxt_jzgjbxx
unit: http://mdm.zjnu.edu.cn/do/api/call/lxyxt_dwxx
msg:
platform:
enabled: false
base-url: https://msg.cug.edu.cn
tp-name: nwszl.cug.edu.cn
default-send-user-id: 790036
default-send-user-name: 周斐霏
default-send-unit-id: 421
default-send-unit-name: 工会
enabled: true
app-id: 'MSG45363193'
app-password: 'OGQxNzVmOTJkZjM5NzJhZDg1MDNiZGM0MDQ5ZTJlNDY'
base-url: http://xxt.zjnu.edu.cn
tp-name: ''
default-send-user-id: ''
default-send-user-name: ''
default-send-unit-id: ''
default-send-unit-name: ''
default-send-user-sign: ''
default-template-id: 1978744168869937153
raw-secret-key: Z21kbDlwYWJ4c2Vtbnk4NjlxeQ==
default-template-id: ''
raw-secret-key: ''
encrypted-secret-key: ''
connect-timeout: 5000
read-timeout: 10000