feat:服务号通知

This commit is contained in:
苏竹红
2025-10-16 15:44:09 +08:00
parent 7be3958624
commit 1a416ce4b4
9 changed files with 153 additions and 64 deletions

View File

@@ -0,0 +1,46 @@
package com.cool.store.enums.wechat;
/**
* @Author suzhuhong
* @Date 2025/10/16 15:19
* @Version 1.0
*/
public enum WechatTemplateDetailEnum {
CHARACTER_STRING2("编号","character_string2"),
THING10("项目名称","thing10"),
TIME14("完成时间","time14"),
THING25("客户名称","thing25"),
THING60("位置","thing60"),
;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
private String name;
private String code;
WechatTemplateDetailEnum(String name, String code) {
this.name = name;
this.code = code;
}
}

View File

@@ -2,6 +2,12 @@ package com.cool.store.enums.wechat;
import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.annotation.JsonValue;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
import static com.cool.store.enums.wechat.WechatTemplateDetailEnum.*;
/** /**
* @Author suzhuhong * @Author suzhuhong
* @Date 2025/10/10 14:39 * @Date 2025/10/10 14:39
@@ -9,24 +15,22 @@ import com.fasterxml.jackson.annotation.JsonValue;
*/ */
public enum WechatTemplateEnum { public enum WechatTemplateEnum {
ORDER_PAY_SUCCESS("ORDER_PAY_SUCCESS", "TM00001", "订单支付成功通知", QUESTION_NOTICE("QUESTION_NOTICE", "T3sp5gBItHKD8oCeEiQMjn7JXpngFiz3dDcaArk84xY", "收到工单通知",
"您的订单已支付成功\n订单号{{orderNo.DATA}}\n支付金额{{amount.DATA}}元\n支付时间{{payTime.DATA}}\n感谢您的购买"), Arrays.asList(CHARACTER_STRING2,THING10,TIME14,THING25,THING60)),
TEST("TEST", "T3sp5gBItHKD8oCeEiQMjn7JXpngFiz3dDcaArk84xY", "收到工单通知",
"测试模板"),
; ;
private final String code; private final String code;
private final String templateId; private final String templateId;
private final String title; private final String title;
private final String content; private final List<WechatTemplateDetailEnum> contentList;
WechatTemplateEnum(String code, String templateId, String title, String content) { WechatTemplateEnum(String code, String templateId, String title, List<WechatTemplateDetailEnum> contentList) {
this.code = code; this.code = code;
this.templateId = templateId; this.templateId = templateId;
this.title = title; this.title = title;
this.content = content; this.contentList = contentList;
} }
@JsonValue @JsonValue
@@ -42,8 +46,8 @@ public enum WechatTemplateEnum {
return title; return title;
} }
public String getContent() { public List<WechatTemplateDetailEnum> getContentList() {
return content; return contentList;
} }
/** /**

View File

@@ -1,5 +1,6 @@
package com.cool.store.dao; package com.cool.store.dao;
import com.cool.store.dto.wechat.ServiceAccountOpenIdDTO;
import com.cool.store.entity.HyPartnerUserInfoDO; import com.cool.store.entity.HyPartnerUserInfoDO;
import com.cool.store.mapper.HyPartnerUserInfoMapper; import com.cool.store.mapper.HyPartnerUserInfoMapper;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@@ -112,4 +113,11 @@ public class HyPartnerUserInfoDAO {
return hyPartnerUserInfoMapper.selectPasswordIsNull(); return hyPartnerUserInfoMapper.selectPasswordIsNull();
} }
public List<ServiceAccountOpenIdDTO> selectLastBindRecord(List<String> mobileList){
if (CollectionUtils.isEmpty(mobileList)){
return Lists.newArrayList();
}
return hyPartnerUserInfoMapper.selectLastBindRecord(mobileList);
}
} }

View File

@@ -1,5 +1,6 @@
package com.cool.store.mapper; package com.cool.store.mapper;
import com.cool.store.dto.wechat.ServiceAccountOpenIdDTO;
import com.cool.store.entity.HyPartnerUserInfoDO; import com.cool.store.entity.HyPartnerUserInfoDO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@@ -55,4 +56,6 @@ public interface HyPartnerUserInfoMapper extends tk.mybatis.mapper.common.Mappe
List<HyPartnerUserInfoDO> selectPasswordIsNull(); List<HyPartnerUserInfoDO> selectPasswordIsNull();
List<ServiceAccountOpenIdDTO> selectLastBindRecord(@Param("mobileList") List<String> mobileList);
} }

View File

@@ -195,4 +195,23 @@
</foreach> </foreach>
</update> </update>
<select id="selectLastBindRecord" resultType="com.cool.store.dto.wechat.ServiceAccountOpenIdDTO">
select
b.partner_id,
b.union_id,
.service_account_open_id,
MAX(b.update_time)
from xfsg_partner_user_info a
left join xfsg_partner_user_wechat_bind b
on a.partner_id = b.partner_id
where b.partner_id is not null
and service_account_open_id is not null
<if test="mobileList !=null and mobileList.size>0">
<foreach collection="mobileList" open="and mobile in (" close=")" separator="," item="mobile">
#{mobile}
</foreach>
</if>
GROUP BY b.partner_id
</select>
</mapper> </mapper>

View File

@@ -0,0 +1,23 @@
package com.cool.store.dto.wechat;
import lombok.Data;
import java.util.Date;
/**
* @Author suzhuhong
* @Date 2025/10/16 14:13
* @Version 1.0
*/
@Data
public class ServiceAccountOpenIdDTO {
private String partnerId;
private String unionId;
private String serviceAccountOpenId;
private Date lastUpdateTime;
}

View File

@@ -10,9 +10,12 @@ import com.cool.store.dto.notice.MessageTemplateCountDTO;
import com.cool.store.dto.notice.NoticeDTO; import com.cool.store.dto.notice.NoticeDTO;
import com.cool.store.dto.store.AuthStoreUserDTO; import com.cool.store.dto.store.AuthStoreUserDTO;
import com.cool.store.dto.store.StoreAreaDTO; import com.cool.store.dto.store.StoreAreaDTO;
import com.cool.store.dto.wechat.ServiceAccountOpenIdDTO;
import com.cool.store.entity.*; import com.cool.store.entity.*;
import com.cool.store.enums.ErrorCodeEnum; import com.cool.store.enums.ErrorCodeEnum;
import com.cool.store.enums.notice.*; import com.cool.store.enums.notice.*;
import com.cool.store.enums.wechat.WechatTemplateDetailEnum;
import com.cool.store.enums.wechat.WechatTemplateEnum;
import com.cool.store.exception.ServiceException; import com.cool.store.exception.ServiceException;
import com.cool.store.mapper.StoreGroupMappingMapper; import com.cool.store.mapper.StoreGroupMappingMapper;
import com.cool.store.mapper.StoreMapper; import com.cool.store.mapper.StoreMapper;
@@ -20,12 +23,15 @@ import com.cool.store.request.notice.*;
import com.cool.store.response.bigdata.ApiResponse; import com.cool.store.response.bigdata.ApiResponse;
import com.cool.store.service.MessageTemplateService; import com.cool.store.service.MessageTemplateService;
import com.cool.store.service.StoreService; import com.cool.store.service.StoreService;
import com.cool.store.service.wechat.WechatTemplateService;
import com.cool.store.utils.CoolDateUtils; import com.cool.store.utils.CoolDateUtils;
import com.cool.store.utils.RedisUtilPool; import com.cool.store.utils.RedisUtilPool;
import com.cool.store.utils.poi.DateUtils;
import com.cool.store.vo.PartnerUserInfoVO; import com.cool.store.vo.PartnerUserInfoVO;
import com.cool.store.vo.notice.*; import com.cool.store.vo.notice.*;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
@@ -70,9 +76,13 @@ public class MessageTemplateServiceImpl implements MessageTemplateService {
@Resource @Resource
MatterConfigDAO matterConfigDAO; MatterConfigDAO matterConfigDAO;
@Resource @Resource
WechatTemplateService wechatTemplateService;
@Resource
RedisUtilPool redisUtilPool; RedisUtilPool redisUtilPool;
@Resource @Resource
TaskExecutor noticeThreadPool; TaskExecutor noticeThreadPool;
@Resource
HyPartnerUserInfoDAO hyPartnerUserInfoDAO;
@@ -207,6 +217,36 @@ public class MessageTemplateServiceImpl implements MessageTemplateService {
JSONObject.toJSONString(request.getStoreInfoList()), JSONObject.toJSONString(request.getStoreInfoList()),
JSONObject.toJSONString(request.getUserInfoList()), JSONObject.toJSONString(request.getUserInfoList()),
userId); userId);
//发送通知
Set<String> userIds = authUser.values().stream().flatMap(Collection::stream).collect(Collectors.toSet());
//分批 查询用户信息
List<String> openIdList = new ArrayList<>();
Lists.partition(new ArrayList<>(userIds), 100).forEach(x->{
List<EnterpriseUserDO> userInfoByUserIds = enterpriseUserDAO.getUserInfoByUserIds(x);
//取出用户的手机号,过滤掉空的手机号
List<String> mobileList = userInfoByUserIds.stream().filter(user -> StringUtils.isNotBlank(user.getMobile())).map(EnterpriseUserDO::getMobile).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(mobileList)){
List<ServiceAccountOpenIdDTO> serviceAccountOpenIdDTOS = hyPartnerUserInfoDAO.selectLastBindRecord(mobileList);
if (CollectionUtils.isNotEmpty(serviceAccountOpenIdDTOS)){
//服务号ID
openIdList.addAll(serviceAccountOpenIdDTOS.stream().map(ServiceAccountOpenIdDTO::getServiceAccountOpenId).collect(Collectors.toList()));
}
}
});
MessageTemplateDO messageTemplateDO = list.get(0);
Map<String, Object> data = new HashMap<>();
data.put(WechatTemplateDetailEnum.CHARACTER_STRING2.getCode(), "ceshi002");
data.put(WechatTemplateDetailEnum.THING10.getCode(), messageTemplateDO.getMessageTitle());
data.put(WechatTemplateDetailEnum.TIME14.getCode(), DateUtils.parseDateToStr(DateUtils.SPECIAL_DATE_START_1, messageTemplateDO.getDeadline()));
data.put(WechatTemplateDetailEnum.THING25.getCode(), "正新管理有限公司");
data.put(WechatTemplateDetailEnum.THING60.getCode(), "上海市-松江区");
openIdList.forEach(x->{
wechatTemplateService.sendMiniAppTemplate(x, WechatTemplateEnum.QUESTION_NOTICE,data,null);
});
} catch (Exception e) { } catch (Exception e) {
log.info("发布流程异常,已取消发布"); log.info("发布流程异常,已取消发布");
} finally { } finally {

View File

@@ -127,58 +127,4 @@ public class WechatTemplateService {
return false; return false;
} }
} }
public void sendTemplateMessageAsync(WechatTemplateMessageDTO messageDTO, WechatCallback callback) {
String accessToken = getAccessToken();
if (accessToken == null) {
log.error("获取access_token失败无法发送模板消息");
callback.onFailure(new IOException("获取access_token失败"));
return;
}
String url = String.format("%s?access_token=%s",
wechatMpProperties.getSendTemplateMessageUrl(), accessToken);
okHttpUtil.doPostAsync(url, messageDTO, new okhttp3.Callback() {
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {
try {
String result = response.body() != null ? response.body().string() : null;
log.info("异步发送模板消息响应: {}", result);
if (response.isSuccessful() && result != null) {
callback.onSuccess(Boolean.TRUE);
} else {
callback.onFailure(new IOException("请求失败,状态码: " + response.code()));
}
} catch (Exception e) {
callback.onFailure(e);
}
}
@Override
public void onFailure(okhttp3.Call call, IOException e) {
log.error("异步发送模板消息失败", e);
callback.onFailure(e);
}
});
}
public void sendNormalTemplateAsync(String openId, WechatTemplateEnum template,
Map<String, Object> data, WechatCallback callback) {
WechatTemplateMessageDTO messageDTO = templateMessageBuilder.buildNormalTemplate(openId, template, data);
sendTemplateMessageAsync(messageDTO, callback);
}
public void sendMiniAppTemplateAsync(String openId, WechatTemplateEnum template,
Map<String, Object> data, String miniAppPagePath, WechatCallback callback) {
WechatTemplateMessageDTO messageDTO = templateMessageBuilder.buildMiniappTemplate(
openId, template, data, miniAppPagePath);
sendTemplateMessageAsync(messageDTO, callback);
}
public interface WechatCallback {
void onSuccess(Boolean successFlag);
void onFailure(Exception e);
}
} }

View File

@@ -602,7 +602,7 @@ public class PCTestController {
data.put("time14", "2025-10-01 12:00:00"); data.put("time14", "2025-10-01 12:00:00");
data.put("thing25", "正新管理有限公司"); data.put("thing25", "正新管理有限公司");
data.put("thing60", "上海市-松江区"); data.put("thing60", "上海市-松江区");
wechatTemplateService.sendNormalTemplate("o9_unvpJy1SGdnkeun7igRBSLuB0", WechatTemplateEnum.TEST, data); wechatTemplateService.sendNormalTemplate("o9_unvpJy1SGdnkeun7igRBSLuB0", WechatTemplateEnum.QUESTION_NOTICE, data);
return ApiResponse.success(true); return ApiResponse.success(true);
} }