微信公众号模板消息功能配置信息参数修改

This commit is contained in:
纪寒 2024-03-25 17:46:41 +08:00
parent e0f8e8072a
commit 35d9a6199c
16 changed files with 896 additions and 4 deletions

View File

@ -218,3 +218,5 @@ wechat-official-account-config:
official-account-token: xinyilu official-account-token: xinyilu
# 微信公众号事件回调消息加密密钥 # 微信公众号事件回调消息加密密钥
official-account-encoding-aes-key: Awcn7nvDU4bcfBwAZmiRbB3lFgXAm2RIg45utdb5Zt3 official-account-encoding-aes-key: Awcn7nvDU4bcfBwAZmiRbB3lFgXAm2RIg45utdb5Zt3
# 测试模板id
test-template-id: WUCYtSbH-QFRV_fMcfmn86QLsz1zo881QW7fQNTWOjc

View File

@ -111,6 +111,12 @@
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
</dependency> </dependency>
<!-- httpclient 依赖-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -223,4 +223,44 @@ public class Constants {
* 获取微信小程序和微信公众号accessToken的url地址 * 获取微信小程序和微信公众号accessToken的url地址
*/ */
public static final String WE_CHAT_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"; public static final String WE_CHAT_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";
/**
* 微信公众号模板消息推送发送接口地址
*/
public static final String OFFICIAL_ACCOUNT_TEMPLATE_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=";
/**
* 获取 access_token AppSecret 错误或者 access_token 无效请开发者认真比对 AppSecret 的正确性或查看是否正在为恰当的公众号调用接口
*/
public static final int INVALID_CREDENTIAL_ACCESS_TOKEN_ISINVALID_OR_NOT_LATEST = 40001;
/**
* 不合法的 OpenID 请开发者确认 OpenID 该用户是否已关注公众号或是否是其他公众号的 OpenID
*/
public static final int INVALID_OPENID = 40003;
/**
* 不合法的 access_token 请开发者认真比对 access_token 的有效性如是否过期或查看是否正在为恰当的公众号调用接口
*/
public static final int INVALID_ACCESS_TOKEN = 40014;
/**
* 不合法的 template_id
*/
public static final int INVALID_TEMPLATE_ID = 40037;
/**
* 用户拒接订阅
*/
public static final int DENY_SUBSCRIPTION = 43101;
/**
* 参数无效
*/
public static final int ARGUMENT_INVALID = 47003;
/**
* 成功
*/
public static final int SUCCESS_ERROR_CODE = 0;
} }

View File

@ -0,0 +1,34 @@
package com.xinelu.common.enums;
import lombok.Getter;
/**
* @Description 微信公众号模板事件消息发送状态枚举
* @Author 纪寒
* @Date 2024-03-25 14:38:27
* @Version 1.0
*/
@Getter
public enum TemplateEventSendStatusEnum {
/**
* 成功
*/
SUCCESS("success"),
/**
* 用户拒绝接收
*/
REJECT("failed:user block"),
/**
* 发送失败
*/
FAILED("failed:system failed"),
;
final private String info;
TemplateEventSendStatusEnum(String info) {
this.info = info;
}
}

View File

@ -2,6 +2,16 @@ package com.xinelu.common.utils.http;
import com.xinelu.common.constant.Constants; import com.xinelu.common.constant.Constants;
import com.xinelu.common.utils.StringUtils; import com.xinelu.common.utils.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -207,4 +217,40 @@ public class HttpUtils {
return true; return true;
} }
} }
/**
* 发送json格式的POST类型的http请求
*
* @param url 请求地址
* @param param json格式的请求参数
* @return String 返回值信息
*/
public static String sendPostJson(String url, String param) {
String result = null;
CloseableHttpClient httpClient = null;
CloseableHttpResponse response = null;
try {
httpClient = HttpClients.createDefault();
// 字符串编码
StringEntity entity = new StringEntity(param, Consts.UTF_8);
// 设置content-type
entity.setContentType("application/json");
HttpPost httpPost = new HttpPost(url);
httpPost.setConfig(RequestConfig.custom().setSocketTimeout(10000).setConnectTimeout(10000).build());
// 防止被当成攻击添加的
httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/54.0.2840.87 Safari/537.36");
// 接收参数设置
httpPost.setHeader("Accept", "application/json");
httpPost.setEntity(entity);
response = httpClient.execute(httpPost);
HttpEntity httpEntity = response.getEntity();
result = EntityUtils.toString(httpEntity);
} catch (Exception e) {
log.warn("post请求发送失败请求路径[{}],请求参数:[{}],异常信息:[{}]", url, param, e);
} finally {
HttpClientUtils.closeQuietly(response);
HttpClientUtils.closeQuietly(httpClient);
}
return result;
}
} }

View File

@ -0,0 +1,119 @@
package com.xinelu.manage.domain.officialaccounttemplateevent;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xinelu.common.annotation.Excel;
import com.xinelu.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.time.LocalDateTime;
/**
* 微信公众号模板信息事件推送对象 official_account_template_event
*
* @author xinelu
* @date 2024-03-25
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "微信公众号模板信息事件推送对象", description = "official_account_template_event")
public class OfficialAccountTemplateEvent extends BaseEntity {
private static final long serialVersionUID = -8437491233815143821L;
/**
* 主键id
*/
private Long id;
/**
* 患者信息表id
*/
@ApiModelProperty(value = "患者信息表id")
@Excel(name = "患者信息表id")
private Long patientId;
/**
* 患者姓名
*/
@ApiModelProperty(value = "患者姓名")
@Excel(name = "患者姓名")
private String patientName;
/**
* 微信用户openid
*/
@ApiModelProperty(value = "微信用户openid")
@Excel(name = "微信用户openid")
private String openid;
/**
* 微信公众号id
*/
@ApiModelProperty(value = "微信公众号id")
@Excel(name = "微信公众号id")
private String officialAccountAppId;
/**
* 发送时间事件创建时间yyyy-MM-dd HH:mm:ss
*/
@ApiModelProperty(value = "发送时间事件创建时间yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "发送时间事件创建时间yyyy-MM-dd HH:mm:ss", width = 30, dateFormat = "yyyy-MM-dd")
private LocalDateTime sendTime;
/**
* 消息类型是事件固定取值为event
*/
@ApiModelProperty(value = "消息类型是事件固定取值为event")
@Excel(name = "消息类型是事件固定取值为event")
private String msgType;
/**
* 事件为模板消息发送结束固定取值为TEMPLATESENDJOBFINISH
*/
@ApiModelProperty(value = "事件为模板消息发送结束固定取值为TEMPLATESENDJOBFINISH")
@Excel(name = "事件为模板消息发送结束固定取值为TEMPLATESENDJOBFINISH")
private String eventType;
/**
* 消息id
*/
@ApiModelProperty(value = "消息id")
@Excel(name = "消息id")
private String msgId;
/**
* 发送状态,成功success用户拒绝接收failed:user block发送失败非用户拒绝failed:system failed
*/
@ApiModelProperty(value = "发送状态,成功success用户拒绝接收failed:user block发送失败")
@Excel(name = "发送状态,成功success用户拒绝接收failed:user block发送失败", readConverterExp = "非=用户拒绝")
private String status;
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("patientId", getPatientId())
.append("patientName", getPatientName())
.append("openid", getOpenid())
.append("officialAccountAppId", getOfficialAccountAppId())
.append("sendTime", getSendTime())
.append("msgType", getMsgType())
.append("eventType", getEventType())
.append("msgId", getMsgId())
.append("status", getStatus())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.toString();
}
}

View File

@ -0,0 +1,61 @@
package com.xinelu.manage.mapper.officialaccounttemplateevent;
import com.xinelu.manage.domain.officialaccounttemplateevent.OfficialAccountTemplateEvent;
import java.util.List;
/**
* 微信公众号模板信息事件推送Mapper接口
*
* @author xinelu
* @date 2024-03-25
*/
public interface OfficialAccountTemplateEventMapper {
/**
* 查询微信公众号模板信息事件推送
*
* @param id 微信公众号模板信息事件推送主键
* @return 微信公众号模板信息事件推送
*/
OfficialAccountTemplateEvent selectOfficialAccountTemplateEventById(Long id);
/**
* 查询微信公众号模板信息事件推送列表
*
* @param officialAccountTemplateEvent 微信公众号模板信息事件推送
* @return 微信公众号模板信息事件推送集合
*/
List<OfficialAccountTemplateEvent> selectOfficialAccountTemplateEventList(OfficialAccountTemplateEvent officialAccountTemplateEvent);
/**
* 新增微信公众号模板信息事件推送
*
* @param officialAccountTemplateEvent 微信公众号模板信息事件推送
* @return 结果
*/
int insertOfficialAccountTemplateEvent(OfficialAccountTemplateEvent officialAccountTemplateEvent);
/**
* 修改微信公众号模板信息事件推送
*
* @param officialAccountTemplateEvent 微信公众号模板信息事件推送
* @return 结果
*/
int updateOfficialAccountTemplateEvent(OfficialAccountTemplateEvent officialAccountTemplateEvent);
/**
* 删除微信公众号模板信息事件推送
*
* @param id 微信公众号模板信息事件推送主键
* @return 结果
*/
int deleteOfficialAccountTemplateEventById(Long id);
/**
* 批量删除微信公众号模板信息事件推送
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
int deleteOfficialAccountTemplateEventByIds(Long[] ids);
}

View File

@ -0,0 +1,61 @@
package com.xinelu.manage.service.officialaccounttemplateevent;
import com.xinelu.manage.domain.officialaccounttemplateevent.OfficialAccountTemplateEvent;
import java.util.List;
/**
* 微信公众号模板信息事件推送Service接口
*
* @author xinelu
* @date 2024-03-25
*/
public interface IOfficialAccountTemplateEventService {
/**
* 查询微信公众号模板信息事件推送
*
* @param id 微信公众号模板信息事件推送主键
* @return 微信公众号模板信息事件推送
*/
OfficialAccountTemplateEvent selectOfficialAccountTemplateEventById(Long id);
/**
* 查询微信公众号模板信息事件推送列表
*
* @param officialAccountTemplateEvent 微信公众号模板信息事件推送
* @return 微信公众号模板信息事件推送集合
*/
List<OfficialAccountTemplateEvent> selectOfficialAccountTemplateEventList(OfficialAccountTemplateEvent officialAccountTemplateEvent);
/**
* 新增微信公众号模板信息事件推送
*
* @param officialAccountTemplateEvent 微信公众号模板信息事件推送
* @return 结果
*/
int insertOfficialAccountTemplateEvent(OfficialAccountTemplateEvent officialAccountTemplateEvent);
/**
* 修改微信公众号模板信息事件推送
*
* @param officialAccountTemplateEvent 微信公众号模板信息事件推送
* @return 结果
*/
int updateOfficialAccountTemplateEvent(OfficialAccountTemplateEvent officialAccountTemplateEvent);
/**
* 批量删除微信公众号模板信息事件推送
*
* @param ids 需要删除的微信公众号模板信息事件推送主键集合
* @return 结果
*/
int deleteOfficialAccountTemplateEventByIds(Long[] ids);
/**
* 删除微信公众号模板信息事件推送信息
*
* @param id 微信公众号模板信息事件推送主键
* @return 结果
*/
int deleteOfficialAccountTemplateEventById(Long id);
}

View File

@ -0,0 +1,90 @@
package com.xinelu.manage.service.officialaccounttemplateevent.impl;
import com.xinelu.manage.domain.officialaccounttemplateevent.OfficialAccountTemplateEvent;
import com.xinelu.manage.mapper.officialaccounttemplateevent.OfficialAccountTemplateEventMapper;
import com.xinelu.manage.service.officialaccounttemplateevent.IOfficialAccountTemplateEventService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
/**
* 微信公众号模板信息事件推送Service业务层处理
*
* @author xinelu
* @date 2024-03-25
*/
@Service
public class OfficialAccountTemplateEventServiceImpl implements IOfficialAccountTemplateEventService {
@Resource
private OfficialAccountTemplateEventMapper officialAccountTemplateEventMapper;
/**
* 查询微信公众号模板信息事件推送
*
* @param id 微信公众号模板信息事件推送主键
* @return 微信公众号模板信息事件推送
*/
@Override
public OfficialAccountTemplateEvent selectOfficialAccountTemplateEventById(Long id) {
return officialAccountTemplateEventMapper.selectOfficialAccountTemplateEventById(id);
}
/**
* 查询微信公众号模板信息事件推送列表
*
* @param officialAccountTemplateEvent 微信公众号模板信息事件推送
* @return 微信公众号模板信息事件推送
*/
@Override
public List<OfficialAccountTemplateEvent> selectOfficialAccountTemplateEventList(OfficialAccountTemplateEvent officialAccountTemplateEvent) {
return officialAccountTemplateEventMapper.selectOfficialAccountTemplateEventList(officialAccountTemplateEvent);
}
/**
* 新增微信公众号模板信息事件推送
*
* @param officialAccountTemplateEvent 微信公众号模板信息事件推送
* @return 结果
*/
@Override
public int insertOfficialAccountTemplateEvent(OfficialAccountTemplateEvent officialAccountTemplateEvent) {
officialAccountTemplateEvent.setCreateTime(LocalDateTime.now());
return officialAccountTemplateEventMapper.insertOfficialAccountTemplateEvent(officialAccountTemplateEvent);
}
/**
* 修改微信公众号模板信息事件推送
*
* @param officialAccountTemplateEvent 微信公众号模板信息事件推送
* @return 结果
*/
@Override
public int updateOfficialAccountTemplateEvent(OfficialAccountTemplateEvent officialAccountTemplateEvent) {
officialAccountTemplateEvent.setUpdateTime(LocalDateTime.now());
return officialAccountTemplateEventMapper.updateOfficialAccountTemplateEvent(officialAccountTemplateEvent);
}
/**
* 批量删除微信公众号模板信息事件推送
*
* @param ids 需要删除的微信公众号模板信息事件推送主键
* @return 结果
*/
@Override
public int deleteOfficialAccountTemplateEventByIds(Long[] ids) {
return officialAccountTemplateEventMapper.deleteOfficialAccountTemplateEventByIds(ids);
}
/**
* 删除微信公众号模板信息事件推送信息
*
* @param id 微信公众号模板信息事件推送主键
* @return 结果
*/
@Override
public int deleteOfficialAccountTemplateEventById(Long id) {
return officialAccountTemplateEventMapper.deleteOfficialAccountTemplateEventById(id);
}
}

View File

@ -0,0 +1,185 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xinelu.manage.mapper.officialaccounttemplateevent.OfficialAccountTemplateEventMapper">
<resultMap type="OfficialAccountTemplateEvent" id="OfficialAccountTemplateEventResult">
<result property="id" column="id"/>
<result property="patientId" column="patient_id"/>
<result property="patientName" column="patient_name"/>
<result property="openid" column="openid"/>
<result property="officialAccountAppId" column="official_account_app_id"/>
<result property="sendTime" column="send_time"/>
<result property="msgType" column="msg_type"/>
<result property="eventType" column="event_type"/>
<result property="msgId" column="msg_id"/>
<result property="status" column="status"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<sql id="selectOfficialAccountTemplateEventVo">
select id, patient_id, patient_name, openid, official_account_app_id, send_time, msg_type, event_type, msg_id, status, create_by, create_time, update_by, update_time from official_account_template_event
</sql>
<select id="selectOfficialAccountTemplateEventList" parameterType="OfficialAccountTemplateEvent"
resultMap="OfficialAccountTemplateEventResult">
<include refid="selectOfficialAccountTemplateEventVo"/>
<where>
<if test="patientId != null ">
and patient_id = #{patientId}
</if>
<if test="patientName != null and patientName != ''">
and patient_name like concat('%', #{patientName}, '%')
</if>
<if test="openid != null and openid != ''">
and openid = #{openid}
</if>
<if test="officialAccountAppId != null and officialAccountAppId != ''">
and official_account_app_id = #{officialAccountAppId}
</if>
<if test="sendTime != null ">
and send_time = #{sendTime}
</if>
<if test="msgType != null and msgType != ''">
and msg_type = #{msgType}
</if>
<if test="eventType != null and eventType != ''">
and event_type = #{eventType}
</if>
<if test="msgId != null and msgId != ''">
and msg_id = #{msgId}
</if>
<if test="status != null and status != ''">
and status = #{status}
</if>
</where>
</select>
<select id="selectOfficialAccountTemplateEventById" parameterType="Long"
resultMap="OfficialAccountTemplateEventResult">
<include refid="selectOfficialAccountTemplateEventVo"/>
where id = #{id}
</select>
<insert id="insertOfficialAccountTemplateEvent" parameterType="OfficialAccountTemplateEvent" useGeneratedKeys="true"
keyProperty="id">
insert into official_account_template_event
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="patientId != null">patient_id,
</if>
<if test="patientName != null">patient_name,
</if>
<if test="openid != null">openid,
</if>
<if test="officialAccountAppId != null">official_account_app_id,
</if>
<if test="sendTime != null">send_time,
</if>
<if test="msgType != null">msg_type,
</if>
<if test="eventType != null">event_type,
</if>
<if test="msgId != null">msg_id,
</if>
<if test="status != null">status,
</if>
<if test="createBy != null">create_by,
</if>
<if test="createTime != null">create_time,
</if>
<if test="updateBy != null">update_by,
</if>
<if test="updateTime != null">update_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="patientId != null">#{patientId},
</if>
<if test="patientName != null">#{patientName},
</if>
<if test="openid != null">#{openid},
</if>
<if test="officialAccountAppId != null">#{officialAccountAppId},
</if>
<if test="sendTime != null">#{sendTime},
</if>
<if test="msgType != null">#{msgType},
</if>
<if test="eventType != null">#{eventType},
</if>
<if test="msgId != null">#{msgId},
</if>
<if test="status != null">#{status},
</if>
<if test="createBy != null">#{createBy},
</if>
<if test="createTime != null">#{createTime},
</if>
<if test="updateBy != null">#{updateBy},
</if>
<if test="updateTime != null">#{updateTime},
</if>
</trim>
</insert>
<update id="updateOfficialAccountTemplateEvent" parameterType="OfficialAccountTemplateEvent">
update official_account_template_event
<trim prefix="SET" suffixOverrides=",">
<if test="patientId != null">patient_id =
#{patientId},
</if>
<if test="patientName != null">patient_name =
#{patientName},
</if>
<if test="openid != null">openid =
#{openid},
</if>
<if test="officialAccountAppId != null">official_account_app_id =
#{officialAccountAppId},
</if>
<if test="sendTime != null">send_time =
#{sendTime},
</if>
<if test="msgType != null">msg_type =
#{msgType},
</if>
<if test="eventType != null">event_type =
#{eventType},
</if>
<if test="msgId != null">msg_id =
#{msgId},
</if>
<if test="status != null">status =
#{status},
</if>
<if test="createBy != null">create_by =
#{createBy},
</if>
<if test="createTime != null">create_time =
#{createTime},
</if>
<if test="updateBy != null">update_by =
#{updateBy},
</if>
<if test="updateTime != null">update_time =
#{updateTime},
</if>
</trim>
where id = #{id}
</update>
<delete id="deleteOfficialAccountTemplateEventById" parameterType="Long">
delete from official_account_template_event where id = #{id}
</delete>
<delete id="deleteOfficialAccountTemplateEventByIds" parameterType="String">
delete from official_account_template_event where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>

View File

@ -37,4 +37,12 @@ public class MobileTestController {
public String getOfficialAccountAccessToken() { public String getOfficialAccountAccessToken() {
return weChatOfficialAccountUtils.getWeChatOfficialAccountAccessToken(); return weChatOfficialAccountUtils.getWeChatOfficialAccountAccessToken();
} }
/**
* 测试微信公众号模板消息发送
*/
@GetMapping("/sendOfficialAccountTemplate")
public void sendOfficialAccountTemplateMessage() {
weChatOfficialAccountUtils.sendOfficialAccountTemplateMessage();
}
} }

View File

@ -4,12 +4,15 @@ import com.xinelu.common.config.WeChatOfficialAccountConfig;
import com.xinelu.common.utils.aes.AesException; import com.xinelu.common.utils.aes.AesException;
import com.xinelu.common.utils.aes.WXBizMsgCrypt; import com.xinelu.common.utils.aes.WXBizMsgCrypt;
import com.xinelu.mobile.dto.wechatappletcallback.MessageSignDTO; import com.xinelu.mobile.dto.wechatappletcallback.MessageSignDTO;
import com.xinelu.mobile.service.wechatofficialaccountcallback.WeChatOfficialAccountCallbackService;
import com.xinelu.mobile.utils.XmlUtil;
import com.xinelu.mobile.vo.wechatofficialaccountcallback.WeChatOfficialAccountEventPushVO;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping; import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Objects;
/** /**
* @Description 院后微信公众号事件回调控制器 * @Description 院后微信公众号事件回调控制器
@ -23,6 +26,8 @@ import javax.annotation.Resource;
public class WeChatOfficialAccountCallbackController { public class WeChatOfficialAccountCallbackController {
@Resource @Resource
private WeChatOfficialAccountConfig weChatOfficialAccountConfig; private WeChatOfficialAccountConfig weChatOfficialAccountConfig;
@Resource
private WeChatOfficialAccountCallbackService weChatOfficialAccountCallbackService;
/** /**
* 院后微信公众号回调验证方法 * 院后微信公众号回调验证方法
@ -33,10 +38,40 @@ public class WeChatOfficialAccountCallbackController {
*/ */
@GetMapping @GetMapping
public String getWeChatOfficialAccountCallBack(MessageSignDTO messageSignDTO) throws AesException { public String getWeChatOfficialAccountCallBack(MessageSignDTO messageSignDTO) throws AesException {
//解密微信报文信息并验证微信公众号与服务器之间是否正常通信
WXBizMsgCrypt wxCpt = new WXBizMsgCrypt(weChatOfficialAccountConfig.getOfficialAccountToken(), weChatOfficialAccountConfig.getOfficialAccountEncodingAesKey(), weChatOfficialAccountConfig.getOfficialAccountAppId()); WXBizMsgCrypt wxCpt = new WXBizMsgCrypt(weChatOfficialAccountConfig.getOfficialAccountToken(), weChatOfficialAccountConfig.getOfficialAccountEncodingAesKey(), weChatOfficialAccountConfig.getOfficialAccountAppId());
String verifyMessage = wxCpt.verifyUrl(messageSignDTO.getSignature(), messageSignDTO.getTimestamp(), messageSignDTO.getNonce(), messageSignDTO.getEchostr()); String verifyMessage = wxCpt.verifyUrl(messageSignDTO.getSignature(), messageSignDTO.getTimestamp(), messageSignDTO.getNonce(), messageSignDTO.getEchostr());
log.info("院后微信公众号回调设置验证URL成功验证信息verifyMessage = [{}]", verifyMessage); log.info("院后微信公众号回调设置验证URL成功验证信息verifyMessage = [{}]", verifyMessage);
return verifyMessage; return verifyMessage;
} }
/**
* 院后微信公众号模板消息事件回调处理
*
* @param signature 签名
* @param timestamp 时间戳
* @param nonce 随机字符串
* @param postData 事件报文内容
* @throws AesException 异常信息
*/
@PostMapping
public void handleWeChatOfficialAccountCallBack(@RequestParam("signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestBody String postData) throws AesException {
//解密微信报文信息并验证微信公众号与服务器之间是否正常通信
WXBizMsgCrypt wxCpt = new WXBizMsgCrypt(weChatOfficialAccountConfig.getOfficialAccountToken(), weChatOfficialAccountConfig.getOfficialAccountEncodingAesKey(), weChatOfficialAccountConfig.getOfficialAccountAppId());
String verifyMessage = wxCpt.verifyUrl(signature, timestamp, nonce, postData);
WeChatOfficialAccountEventPushVO eventPush = (WeChatOfficialAccountEventPushVO) XmlUtil.fromXml(verifyMessage, WeChatOfficialAccountEventPushVO.class);
if (Objects.isNull(eventPush)) {
log.info("院后微信公众号模板消息事件xml数据转换失败请求信息为 [{}]", verifyMessage);
return;
}
if (StringUtils.isBlank(eventPush.getToUserName()) || StringUtils.isBlank(eventPush.getEvent()) || StringUtils.isBlank(eventPush.getMsgType())) {
log.info("院后微信公众号模板消息事件解析事件数据为空!");
return;
}
weChatOfficialAccountCallbackService.handleOfficialAccountTemplateEvent(eventPush);
}
} }

View File

@ -0,0 +1,18 @@
package com.xinelu.mobile.service.wechatofficialaccountcallback;
import com.xinelu.mobile.vo.wechatofficialaccountcallback.WeChatOfficialAccountEventPushVO;
/**
* @Description 院后微信公众号事件回调service
* @Author 纪寒
* @Date 2024-03-25 14:52:03
* @Version 1.0
*/
public interface WeChatOfficialAccountCallbackService {
/**
* 院后微信公众号模板消息事件回调处理
*
* @param eventPushVO 模板消息事件回调数据
*/
void handleOfficialAccountTemplateEvent(WeChatOfficialAccountEventPushVO eventPushVO);
}

View File

@ -0,0 +1,65 @@
package com.xinelu.mobile.service.wechatofficialaccountcallback.impl;
import com.xinelu.common.exception.ServiceException;
import com.xinelu.common.utils.DateUtils;
import com.xinelu.manage.domain.officialaccounttemplateevent.OfficialAccountTemplateEvent;
import com.xinelu.manage.domain.patientinfo.PatientInfo;
import com.xinelu.manage.mapper.officialaccounttemplateevent.OfficialAccountTemplateEventMapper;
import com.xinelu.manage.mapper.patientinfo.PatientInfoMapper;
import com.xinelu.mobile.service.wechatofficialaccountcallback.WeChatOfficialAccountCallbackService;
import com.xinelu.mobile.vo.wechatofficialaccountcallback.WeChatOfficialAccountEventPushVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Objects;
/**
* @Description 院后微信公众号事件回调service实现
* @Author 纪寒
* @Date 2024-03-25 14:55:53
* @Version 1.0
*/
@Service
@Slf4j
public class WeChatOfficialAccountCallbackServiceImpl implements WeChatOfficialAccountCallbackService {
@Resource
private PatientInfoMapper patientInfoMapper;
@Resource
private OfficialAccountTemplateEventMapper officialAccountTemplateEventMapper;
/**
* 院后微信公众号模板消息事件回调处理
*
* @param eventPushVO 模板消息事件回调数据
*/
@Transactional(rollbackFor = Exception.class)
@Override
public void handleOfficialAccountTemplateEvent(WeChatOfficialAccountEventPushVO eventPushVO) {
//查询患者信息
PatientInfo patientInfo = patientInfoMapper.getPatientInfoByOpenId(eventPushVO.getFromUserName());
if (Objects.isNull(patientInfo)) {
log.info("当前患者信息不存在微信用户openid为{}", eventPushVO.getFromUserName());
return;
}
OfficialAccountTemplateEvent templateEvent = new OfficialAccountTemplateEvent();
templateEvent.setPatientId(Objects.isNull(patientInfo.getId()) ? null : patientInfo.getId());
templateEvent.setPatientName(StringUtils.isBlank(patientInfo.getPatientName()) ? "" : patientInfo.getPatientName());
templateEvent.setOpenid(eventPushVO.getFromUserName());
templateEvent.setOfficialAccountAppId(StringUtils.isBlank(eventPushVO.getToUserName()) ? "" : eventPushVO.getToUserName());
templateEvent.setSendTime(StringUtils.isBlank(eventPushVO.getCreateTime()) ? null : DateUtils.timestampToLocalDateTime(Long.parseLong(eventPushVO.getCreateTime()) * 1000L));
templateEvent.setMsgType(StringUtils.isBlank(eventPushVO.getMsgType()) ? "" : eventPushVO.getMsgType());
templateEvent.setEventType(StringUtils.isBlank(eventPushVO.getEvent()) ? "" : eventPushVO.getEvent());
templateEvent.setMsgId(StringUtils.isBlank(eventPushVO.getMsgId()) ? "" : eventPushVO.getMsgId());
templateEvent.setStatus(StringUtils.isBlank(eventPushVO.getStatus()) ? "" : eventPushVO.getStatus());
int insertTemplateEventCount = officialAccountTemplateEventMapper.insertOfficialAccountTemplateEvent(templateEvent);
if (insertTemplateEventCount < 0) {
log.error("院后微信公众号模板事件推送,新增模板事件消息信息失败,信息为:[{}]", templateEvent);
throw new ServiceException("院后微信公众号模板事件推送记录信息失败!");
}
log.info("院后微信公众号模板事件推送处理成功!");
}
}

View File

@ -6,11 +6,14 @@ import com.xinelu.common.constant.Constants;
import com.xinelu.common.entity.AccessToken; import com.xinelu.common.entity.AccessToken;
import com.xinelu.common.exception.ServiceException; import com.xinelu.common.exception.ServiceException;
import com.xinelu.common.utils.http.HttpUtils; import com.xinelu.common.utils.http.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -20,6 +23,7 @@ import java.util.concurrent.TimeUnit;
* @Date 2024-03-21 13:42:31 * @Date 2024-03-21 13:42:31
* @Version 1.0 * @Version 1.0
*/ */
@Slf4j
@Component @Component
public class WeChatOfficialAccountUtils { public class WeChatOfficialAccountUtils {
@ -68,4 +72,62 @@ public class WeChatOfficialAccountUtils {
} }
return accessToken; return accessToken;
} }
/**
* 微信公众号模板消息发送
*/
public void sendOfficialAccountTemplateMessage() {
//获取微信公众号的accessToken
String accessToken = this.getWeChatOfficialAccountAccessToken();
//定义模板内容公众模板内容
Map<String, Object> paramsMap = new LinkedHashMap<>();
paramsMap.put("touser", "oHBuH5T90TghJdYKfBlxE4fhayBc");
paramsMap.put("template_id", "WUCYtSbH-QFRV_fMcfmn86QLsz1zo881QW7fQNTWOjc");
//微信小程序跳转内容
Map<String, Object> miniprogramMap = new LinkedHashMap<>();
miniprogramMap.put("appid", "wxdc32268eca6b78f9");
miniprogramMap.put("pagepath", "pages/startup/startup");
//微信小程序模板data内容
Map<String, Object> dataMap = new LinkedHashMap<>();
dataMap.put("phrase7", "泉医陪护服务");
dataMap.put("character_string3", "000026315412331612100");
dataMap.put("time6", "2024-03-25 16:21:32");
dataMap.put("time10", "2024-03-27 10:00:00");
dataMap.put("thing11", "济南市槐荫区首诺城市之光东座22楼E10");
paramsMap.put("miniprogram", miniprogramMap);
paramsMap.put("data", dataMap);
//拼接请求地址并发送
String messageUrl = Constants.OFFICIAL_ACCOUNT_TEMPLATE_SEND_URL + accessToken;
String param = JSON.toJSONString(paramsMap);
String result = HttpUtils.sendPostJson(messageUrl, param);
//返回参数映射
AccessToken errCode = JSON.parseObject(result, AccessToken.class);
if (Objects.nonNull(errCode) && Objects.nonNull(errCode.getErrcode())) {
switch (errCode.getErrcode()) {
case Constants.SUCCESS_ERROR_CODE:
log.info("发送消息成功!");
break;
case Constants.INVALID_CREDENTIAL_ACCESS_TOKEN_ISINVALID_OR_NOT_LATEST:
log.error("取 access_token 时 AppSecret 错误,或者 access_token 无效!");
break;
case Constants.INVALID_OPENID:
log.error("不合法的 OpenId");
break;
case Constants.INVALID_ACCESS_TOKEN:
log.error("合法的 access_token");
break;
case Constants.INVALID_TEMPLATE_ID:
log.error("不合法的 template_id");
break;
case Constants.ARGUMENT_INVALID:
log.error("参数无效!");
break;
case Constants.DENY_SUBSCRIPTION:
log.error("用户拒接订阅!");
break;
default:
break;
}
}
}
} }

View File

@ -0,0 +1,60 @@
package com.xinelu.mobile.vo.wechatofficialaccountcallback;
import lombok.Data;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;
import java.io.Serializable;
/**
* @Description 微信公众号模板消息事件推送实体类
* @Author 纪寒
* @Date 2024-03-25 13:36:17
* @Version 1.0
*/
@Root(name = "xml", strict = false)
@Data
public class WeChatOfficialAccountEventPushVO implements Serializable {
private static final long serialVersionUID = 4329284826265109369L;
/**
* 公众号微信号
*/
@Element(name = "ToUserName", required = false)
private String toUserName;
/**
* 接收模板消息的用户的openid
*/
@Element(name = "FromUserName", required = false)
private String fromUserName;
/**
* 创建时间
*/
@Element(name = "CreateTime", required = false)
private String createTime;
/**
* 消息类型是事件
*/
@Element(name = "MsgType", required = false)
private String msgType;
/**
* 事件为模板消息发送结束
*/
@Element(name = "Event", required = false)
private String event;
/**
* 消息id
*/
@Element(name = "MsgID", required = false)
private String msgId;
/**
* 发送状态为成功success用户拒绝接收failed:user block发送失败非用户拒绝failed:system failed
*/
@Element(name = "Status", required = false)
private String status;
}