微信公众号模板消息功能对接开发

This commit is contained in:
纪寒 2024-03-21 14:36:06 +08:00
parent 6341f71a25
commit ddf01f2f49
11 changed files with 337 additions and 25 deletions

View File

@ -185,8 +185,8 @@ xss:
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*
# 微信小程序参数配置信息
applet-chat-config:
# 院后微信小程序参数配置信息
wechat-applet-chat-config:
# 微信小程序idwxdec3416aa3d60cab
applet-id: wxdc32268eca6b78f9
# 微信小程序密钥f58e19be0380c2ebc6e9e9684c0dacce
@ -207,3 +207,14 @@ applet-chat-config:
appoint-order-template-id: nUB9HRbqQXOVuTpkKBIHMgzWlNq6touzxf5QYBiMkbU
# 签到成功通知模板id
sign-template-id: S_c9bR4znSWpXg-6ACIMn7AkaR11dzo113XM8w4CKz0
# 院后微信公众号参数配置
wechat-official-account-config:
# 微信公众号id
official-account-app-id: wx9d87c7c73ef1ebde
# 微信公众号secret
official-account-app-secret: 20ab2c266b1da75d71e9932e7d28326e
# 微信公众号事件回调令牌
official-account-token: Token
# 微信公众号事件回调消息加密密钥
official-account-encoding-aes-key: xinyilu

View File

@ -5,15 +5,15 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @Description 微信小程序参数配置类
* @Description 新医路院后微信小程序参数配置类
* @Author 纪寒
* @Date 2024-03-19 15:52:38
* @Version 1.0
*/
@Data
@Component
@ConfigurationProperties(prefix = "applet-chat-config")
public class AppletChatConfig {
@ConfigurationProperties(prefix = "wechat-applet-chat-config")
public class WeChatAppletChatConfig {
/**
* 小程序id

View File

@ -0,0 +1,36 @@
package com.xinelu.common.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @Description 新医路院后微信公众号参数配置类
* @Author 纪寒
* @Date 2024-03-21 11:12:04
* @Version 1.0
*/
@Data
@Component
@ConfigurationProperties(prefix = "wechat-official-account-config")
public class WeChatOfficialAccountConfig {
/**
* 微信公众号id
*/
private String officialAccountAppId;
/**
* 微信公众号secret
*/
private String officialAccountAppSecret;
/**
* 微信公众号事件回调令牌
*/
private String officialAccountToken;
/**
* 微信公众号事件回调消息加密密钥
*/
private String officialAccountEncodingAesKey;
}

View File

@ -189,23 +189,38 @@ public class Constants {
*/
public static final String TASK_PARTITION_CODE = "TPC";
/**
* 门诊患者
*/
public static final String OUTPATIENT = "outpatient";
/**
* 门诊患者
*/
public static final String OUTPATIENT = "outpatient";
/**
* 预住院
*/
public static final String PRE_HOSPITALIZED = "prehospitalized";
/**
* 预住院
*/
public static final String PRE_HOSPITALIZED = "prehospitalized";
/**
* 在院
*/
public static final String IN_HOSPITAL = "inhospital";
/**
* 在院
*/
public static final String IN_HOSPITAL = "inhospital";
/**
* 出院
*/
public static final String DISCHARGED = "discharged";
/**
* 出院
*/
public static final String DISCHARGED = "discharged";
/**
* 院后微信小程序accessToken的redis的键前缀
*/
public static final String WE_CHAT_APPLET_ACCESS_TOKEN = "WE_CHAT_APPLET_ACCESS_TOKEN_";
/**
* 院后微信公众号accessToken的redis的键前缀
*/
public static final String WE_CHAT_OFFICIAL_ACCOUNT_ACCESS_TOKEN = "WE_CHAT_OFFICIAL_ACCOUNT_ACCESS_TOKEN_";
/**
* 获取微信小程序和微信公众号accessToken的url地址
*/
public static final String WE_CHAT_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";
}

View File

@ -0,0 +1,44 @@
package com.xinelu.common.entity;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.io.Serializable;
/**
* @Description 微信小程序和微信公众号accessToken信息实体类
* @Author 纪寒
* @Date 2024-03-21 14:00:10
* @Version 1.0
*/
@Data
public class AccessToken implements Serializable {
private static final long serialVersionUID = -7751995012730354177L;
/**
* accessToken值
*/
@JSONField(name = "access_token")
private String accessToken;
/**
* access_token有效时间有效时间为7200秒
*/
@JSONField(name = "expires_in")
private String expiresIn;
/**
* 错误状态码
* 40001AppSecret 错误或者 AppSecret 不属于这个小程序请开发者确认 AppSecret 的正确性
* 40002请确保 grant_type 字段值为 client_credential
* 40013不合法的 AppID请开发者检查 AppID 的正确性避免异常字符注意大小写
*/
private Integer errcode;
/**
* 错误状态值
* 40001AppSecret 错误或者 AppSecret 不属于这个小程序请开发者确认 AppSecret 的正确性
* 40002请确保 grant_type 字段值为 client_credential
* 40013不合法的 AppID请开发者检查 AppID 的正确性避免异常字符注意大小写
*/
private String errmsg;
}

View File

@ -1,8 +1,12 @@
package com.xinelu.mobile.controller;
import com.xinelu.mobile.utils.WeChatAppletUtils;
import com.xinelu.mobile.utils.WeChatOfficialAccountUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @Description 测试controller
* @Author 纪寒
@ -12,5 +16,23 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/testMobile")
public class MobileTestController {
@Resource
private WeChatAppletUtils weChatAppletUtils;
@Resource
private WeChatOfficialAccountUtils weChatOfficialAccountUtils;
/**
* 测试获取微信小程序accessToken
*/
@RequestMapping("/getAppletAcccessToken")
public void getAppletAccessToken() {
weChatAppletUtils.getWeChatAppletAccessToken();
}
/**
* 测试获取微信公众号accessToken
*/
@RequestMapping("/getOfficialAccountAccessToken")
public void getOfficialAccountAccessToken() {
weChatOfficialAccountUtils.getWeChatOfficialAccountAccessToken();
}
}

View File

@ -1,6 +1,6 @@
package com.xinelu.mobile.controller.wechatappletcallback;
import com.xinelu.common.config.AppletChatConfig;
import com.xinelu.common.config.WeChatAppletChatConfig;
import com.xinelu.common.utils.aes.AesException;
import com.xinelu.common.utils.aes.WXBizMsgCrypt;
import com.xinelu.mobile.dto.wechatappletcallback.MessageSignDTO;
@ -26,7 +26,7 @@ import java.util.Objects;
public class WeChatAppletCallBackController {
@Resource
private AppletChatConfig appletChatConfig;
private WeChatAppletChatConfig appletChatConfig;
@Resource
private WeChatAppletCallBackService weChatAppletCallBackService;

View File

@ -0,0 +1,42 @@
package com.xinelu.mobile.controller.wechatofficialaccountcallback;
import com.xinelu.common.config.WeChatOfficialAccountConfig;
import com.xinelu.common.utils.aes.AesException;
import com.xinelu.common.utils.aes.WXBizMsgCrypt;
import com.xinelu.mobile.dto.wechatappletcallback.MessageSignDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @Description 院后微信公众号事件回调控制器
* @Author 纪寒
* @Date 2024-03-21 14:26:56
* @Version 1.0
*/
@Slf4j
@RestController
@RequestMapping("/postDischarge/officialAccountCallback")
public class WeChatOfficialAccountCallbackController {
@Resource
private WeChatOfficialAccountConfig weChatOfficialAccountConfig;
/**
* 院后微信公众号回调验证方法
*
* @param messageSignDTO 微信输入参数
* @return 解密后的字符串
* @throws AesException 异常信息
*/
@GetMapping
public String getWeChatOfficialAccountCallBack(MessageSignDTO messageSignDTO) throws AesException {
WXBizMsgCrypt wxCpt = new WXBizMsgCrypt(weChatOfficialAccountConfig.getOfficialAccountToken(), weChatOfficialAccountConfig.getOfficialAccountEncodingAesKey(), weChatOfficialAccountConfig.getOfficialAccountAppId());
String verifyMessage = wxCpt.verifyUrl(messageSignDTO.getSignature(), messageSignDTO.getTimestamp(), messageSignDTO.getNonce(), messageSignDTO.getEchostr());
log.info("院后微信公众号回调设置验证URL成功验证信息verifyMessage = [{}]", verifyMessage);
return verifyMessage;
}
}

View File

@ -1,6 +1,6 @@
package com.xinelu.mobile.service.wechatappletcallback.impl;
import com.xinelu.common.config.AppletChatConfig;
import com.xinelu.common.config.WeChatAppletChatConfig;
import com.xinelu.common.enums.SubscribeMessageTypeEnum;
import com.xinelu.common.exception.ServiceException;
import com.xinelu.common.utils.DateUtils;
@ -39,7 +39,7 @@ public class WeChatAppletCallBackServiceImpl implements WeChatAppletCallBackServ
@Resource
private SubscribeMessageRecordMapper subscribeMessageRecordMapper;
@Resource
private AppletChatConfig appletChatConfig;
private WeChatAppletChatConfig appletChatConfig;
@Resource
private PatientInfoMapper patientInfoMapper;
@Resource

View File

@ -0,0 +1,71 @@
package com.xinelu.mobile.utils;
import com.alibaba.fastjson2.JSON;
import com.xinelu.common.config.WeChatAppletChatConfig;
import com.xinelu.common.constant.Constants;
import com.xinelu.common.entity.AccessToken;
import com.xinelu.common.exception.ServiceException;
import com.xinelu.common.utils.http.HttpUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* @Description 院后微信小程序公共方法工具类
* @Author 纪寒
* @Date 2024-03-21 13:41:08
* @Version 1.0
*/
@Component
public class WeChatAppletUtils {
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Resource
private WeChatAppletChatConfig weChatAppletChatConfig;
/**
* 返回成功状态码
*/
private static final int SUCCESS_CODE = 0;
/**
* 获取微信小程序的accessToken值
*
* @return 小程序accessToken值
*/
public String getWeChatAppletAccessToken() {
String accessToken;
String accessTokenRedisKey = Constants.WE_CHAT_APPLET_ACCESS_TOKEN + "accessToken";
//从Redis中取出accessToken
Object object = redisTemplate.opsForValue().get(accessTokenRedisKey);
if (Objects.isNull(object)) {
//没有获取accessToken
String accessTokenUrl = Constants.WE_CHAT_ACCESS_TOKEN_URL + "&appid=" + weChatAppletChatConfig.getAppletId() + "&secret=" + weChatAppletChatConfig.getSecret();
//发送请求
String result = HttpUtils.sendGet(accessTokenUrl);
if (StringUtils.isBlank(result)) {
throw new ServiceException("获取微信小程序accessToken信息失败", 201);
}
AccessToken weAppletAccessToken = JSON.parseObject(result, AccessToken.class);
if (Objects.isNull(weAppletAccessToken)) {
throw new ServiceException("获取微信小程序accessToken信息失败");
}
if (Objects.nonNull(weAppletAccessToken.getErrcode()) && weAppletAccessToken.getErrcode() != SUCCESS_CODE) {
throw new ServiceException("获取微信小程序accessToken信息失败失败信息为" + weAppletAccessToken.getErrmsg(), 201);
}
if (StringUtils.isBlank(weAppletAccessToken.getAccessToken())) {
throw new ServiceException("微信小程序accessToken信息为空");
}
accessToken = weAppletAccessToken.getAccessToken();
//存入Redis中
redisTemplate.opsForValue().set(accessTokenRedisKey, accessToken, 3600, TimeUnit.SECONDS);
} else {
accessToken = (String) object;
}
return accessToken;
}
}

View File

@ -0,0 +1,71 @@
package com.xinelu.mobile.utils;
import com.alibaba.fastjson2.JSON;
import com.xinelu.common.config.WeChatOfficialAccountConfig;
import com.xinelu.common.constant.Constants;
import com.xinelu.common.entity.AccessToken;
import com.xinelu.common.exception.ServiceException;
import com.xinelu.common.utils.http.HttpUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* @Description 院后公众号公众方法工具类
* @Author 纪寒
* @Date 2024-03-21 13:42:31
* @Version 1.0
*/
@Component
public class WeChatOfficialAccountUtils {
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Resource
private WeChatOfficialAccountConfig weChatOfficialAccountConfig;
/**
* 返回成功状态码
*/
private static final int SUCCESS_CODE = 0;
/**
* 获取院后微信公众号accessToken
*
* @return 微信公众号的accessToken
*/
public String getWeChatOfficialAccountAccessToken() {
String accessToken;
String accessTokenRedisKey = Constants.WE_CHAT_OFFICIAL_ACCOUNT_ACCESS_TOKEN + "accessToken";
//从Redis中取出accessToken
Object object = redisTemplate.opsForValue().get(accessTokenRedisKey);
if (Objects.isNull(object)) {
//没有获取accessToken
String accessTokenUrl = Constants.WE_CHAT_ACCESS_TOKEN_URL + "&appid=" + weChatOfficialAccountConfig.getOfficialAccountAppId() + "&secret=" + weChatOfficialAccountConfig.getOfficialAccountAppSecret();
//发送请求
String result = HttpUtils.sendGet(accessTokenUrl);
if (StringUtils.isBlank(result)) {
throw new ServiceException("获取微信公众号accessToken信息失败", 201);
}
AccessToken officialAccountAccessToken = JSON.parseObject(result, AccessToken.class);
if (Objects.isNull(officialAccountAccessToken)) {
throw new ServiceException("获取微信公众号accessToken信息失败");
}
if (Objects.nonNull(officialAccountAccessToken.getErrcode()) && officialAccountAccessToken.getErrcode() != SUCCESS_CODE) {
throw new ServiceException("获取微信公众号accessToken信息失败失败信息为" + officialAccountAccessToken.getErrmsg(), 201);
}
if (StringUtils.isBlank(officialAccountAccessToken.getAccessToken())) {
throw new ServiceException("微信公众号accessToken信息为空");
}
accessToken = officialAccountAccessToken.getAccessToken();
//存入Redis中
redisTemplate.opsForValue().set(accessTokenRedisKey, accessToken, 3600, TimeUnit.SECONDS);
} else {
accessToken = (String) object;
}
return accessToken;
}
}