1.要有一个关于超时推送的定时器,可随时关闭及维护
2.推送类型: 体检报告、资料审核、培训审核等等
3.推送的内容做成模板(创建一张表)要求可以维护而且模板可分为多个用户类型模板
4.推送可以维护提醒间隔时间(创建一张表),例如:下次体检为2020-10-09,距离下次体检30天提醒一次,15天提醒一次
5.可以根据提醒间隔来设置是否生成工单
6.而且要有发送记录(创建一张表)
注意:可能写的不好,还望多指教,框架用的ssm,因为在自己开发中遇到的需求,做一下笔记,完整代码有点多,有很多实体类没有写,就是那些表的截图,大致实现方式就是这样的,也可以再添加其它的推送技术,如:短信、公众号,都可以,只要推送类继承Observer 观察者实现update方法即可
使用的工具类为hutool工具类连接:https://www.hutool.cn/
设计模式之观察者模式理论:https://blog.csdn.net/weixin_43861630/article/details/108978667
hutool文档地址:https://www.hutool.cn/docs/#/extra/%E9%82%AE%E4%BB%B6%E5%B7%A5%E5%85%B7-MailUtil?id=%e5%8f%91%e9%80%81%e9%82%ae%e4%bb%b6
# 邮件服务器的SMTP地址,可选,默认为smtp.<发件人邮箱后缀>
host = smtp.qq.com
# 邮件服务器的SMTP端口,可选,默认25
port = 25
# 发件人(必须正确,否则发送失败)
from = 发件人邮箱 例如: 昵称<qq号@qq.com>
# 用户名,默认为发件人邮箱前缀
user = qq号
# 密码(注意,某些邮箱需要为SMTP服务单独设置密码---在这里是SMTP授权码)
pass = 授权码
要引入第三方jar
cn.hutool
hutool-all
5.4.0
javax.mail
mail
1.4.7
package com.hx.slj.modules.zyb.ObserverModel;
/**
* @author qb
* @version 1.0
* @Description
* 抽象观察者
* @date 2020/8/24 13:51
*/
public interface Observer {
public void update(PushEntity pushEntity);
}
package com.hx.slj.modules.zyb.ObserverModel;
/**
* @author qb
* @version 1.0
* @Description
* 抽象被观察者
* 声明添加、删除、通知观察者方法
* @date 2020/8/24 13:49
*/
public interface Observerable {
/**
* 添加
* @param o
*/
public void registerObserver(Observer o);
/**
* 删除
* @param o
*/
public void removeObserver(Observer o);
/**
* 通知观察者
*/
public void notifyObserver(PushEntity pushEntity);
}
package com.hx.slj.modules.zyb.ObserverModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author qb
* @version 1.0
* @Description 观察者通知参数
* @date 2020/8/24 17:52
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PushEntity {
/**
* 企业id
*/
private String id;
/**
* 触发的提醒间隔id
*/
private String txjgId;
/**
* 各种报告id
*/
private String bgId;
/**
* 是否生成工单
*/
private String hasWo;
/**
* 推送类型
*/
private String pushType;
}
package com.hx.slj.modules.zyb.ObserverModelImpl;
import com.hx.slj.modules.zyb.ObserverModel.Observer;
import com.hx.slj.modules.zyb.ObserverModel.Observerable;
import com.hx.slj.modules.zyb.ObserverModel.PushEntity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
/**
* @author qb
* @version 1.0
* @Description 观察主题
* @date 2020/8/24 14:10
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OvertimePushServer implements Observerable {
private List<Observer> list = new ArrayList<>();
@Override
public void registerObserver(Observer o) {
System.err.println(o);
list.add(o);
}
@Override
public void removeObserver(Observer o) {
if(!list.isEmpty()){
list.remove(o);
}
}
@Override
public void notifyObserver(PushEntity pushEntity) {
for(int i=0;i<list.size();i++){
Observer observer = list.get(i);
observer.update(pushEntity);
}
}
}
package com.hx.slj.modules.zyb.ObserverModel;
import com.hx.slj.modules.zyb.entity.ZybWoTxjg;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @author qb
* @version 1.0
* @Description 超时推送公用实体
* @date 2020/9/1 10:28
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class zybPushEntity {
private String id;
/**
* 企业id
*/
private String enterpriseid;
/**
* 下次时间
*/
private Date nextTime;
/**
* 实体类型
*/
private String entityType;
/**
* 提醒间隔 实体类
*/
private ZybWoTxjg zybWoTxjg;
}
package com.hx.slj.modules.zyb.TemplateParsing;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author qb
* @version 1.0
* @Description 模板解析 {
{name}} ==》 name(赋值后)
* @date 2020/8/24 15:51
*/
public class TemplateParsing {
/**
* 模板解析
* @param content
* @param map
* @return
*/
public static String renderString(String content, Map<String,String> map){
Set<Map.Entry<String,String>> sets = map.entrySet();
try{
for(Map.Entry<String,String> entry : sets){
//定义正则
String regex = "\\{\\{" + entry.getKey() + "\\}\\}";
//创建匹配模式
Pattern pattern = Pattern.compile(regex);
//将要匹配的内容加入进行匹配的
Matcher matcher = pattern.matcher(content);
//将匹配的结果替换为map的value值
content = matcher.replaceAll(entry.getValue() == null ? "null" : entry.getValue());
}
}catch (ClassCastException e){
new ClassCastException("格式错误,模板解析时map都应为string类型");
return "格式错误,模板解析时map中的value都应为string类型";
}
return content;
}
}
package com.hx.slj.modules.zyb.utils;
import cn.hutool.extra.mail.MailUtil;
import java.io.File;
import java.util.ArrayList;
/**
* @author qb
* @version 1.0
* @Description 推送工具类
* @date 2020/8/19 15:24
*/
public class PushUtil {
/**
* 邮箱工具类,mail配置文件中,指定邮箱类型
* @param to 收件人
* @param subject 邮件主题
* @param content 邮寄内容(可以是文本,也可以是html)
* @param isHtml 是否是html
* @param files 附件
*/
public static void SendMail(String to, String subject, String content, Boolean isHtml, File... files){
if(files == null || files.length <=0){
MailUtil.send(to, subject, content, isHtml);
}else{
MailUtil.send(to, subject, content, isHtml,files);
}
}
}
package com.hx.slj.modules.zyb.ObserverModelImpl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.hx.slj.common.utils.CacheUtils;
import com.hx.slj.modules.zyb.ObserverModel.Observer;
import com.hx.slj.modules.zyb.ObserverModel.PushEntity;
import com.hx.slj.modules.zyb.TemplateParsing.TemplateParsing;
import com.hx.slj.modules.zyb.config.WoProjectTypeEnum;
import com.hx.slj.modules.zyb.entity.*;
import com.hx.slj.modules.zyb.service.*;
import com.hx.slj.modules.zyb.utils.BeanUtil;
import com.hx.slj.modules.zyb.utils.PushUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
/**
* @author qb
* @version 1.0
* @Description
* @date 2020/8/24 13:54
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class QQPush implements Observer {
@Autowired
private zybEnterpriseVoService zybEnterpriseVoService;
@Autowired
private zybWoService zybWoService;
@Autowired
private ZybWoFsjlService zybWoFsjlService;
/**
* 是否是html标识
*/
public Boolean isHtml = false;
@Override
public void update(PushEntity pushEntity) {
//创建线程 所有推送不共用一个线程,
new Thread(() -> {
//相关业务实现,给成自己的业务
if(zybEnterpriseVoService == null){
zybEnterpriseVoService = BeanUtil.getBean(zybEnterpriseVoService.class);
}
zybEnterpriseVo zybEnterprise01 = new zybEnterpriseVo();
//设置企业id
zybEnterprise01.setId(pushEntity.getId());
//设置报告id
zybEnterprise01.setTbgId(pushEntity.getBgId());
//根据id获取企业信息及执法人员邮箱
zybEnterpriseVo zybEnterprise;
switch (pushEntity.getPushType()){
case "ZYB_QYTJXX" :
zybEnterprise = zybEnterpriseVoService.selectQyItemAndZfryEamilById(zybEnterprise01);
break;
case "ZYB_PJXX" :
zybEnterprise = zybEnterpriseVoService.selectQyItemAndZfryEamilByIdPjxx(zybEnterprise01);
break;
default:
zybEnterprise = null;
break;
}
if(zybEnterprise == null){
System.err.println("查询的企业信息为null");
}else{
//获取动态泛型
String PushType = pushEntity.getPushType();
//设置项目名称
zybEnterprise.setXmmc(WoProjectTypeEnum.matchOpCode(PushType).getName());
//设置下次上传报告时间
if(zybEnterprise.getNextTime() != null){
//date时间转str时间
String dateStr = DateUtil.format(zybEnterprise.getNextTime(),"yyyy-MM-dd");
zybEnterprise.setTDate(dateStr);
//解决模板解析时,因为value不是string型而产生报错
zybEnterprise.setNextTime(null);
}
//将数据序列化成键值对 注:序列化时value只能使用string型,如果是别的类型 模板解析时会报错
HashMap hashMap = JSON.parseObject(JSON.toJSONString(zybEnterprise,SerializerFeature.WriteMapNullValue),HashMap.class);
//System.err.println("数据序列化后的map:"+hashMap);
List<ZybWoMb> zybWoMbList = (List<ZybWoMb>)CacheUtils.get("ZYB_WO_MB","ZYB_WO_MB_LIST");
//System.err.println("从缓存中获取qq发送时的模板 zybWoMbList: "+zybWoMbList);
zybWoMbList.forEach((item) ->{
if(item.getTxjgId().equals(pushEntity.getTxjgId())){
//将序列化好的数据进行解析
String result = TemplateParsing.renderString(item.getMbNr(),hashMap);
//根据mb_status状态执行模板
switch (item.getMbStatus()){
case "0" :
if(!StrUtil.hasEmpty(zybEnterprise.getEamil())){
//发送邮箱
System.err.println("企业人员邮箱: "+zybEnterprise.getEamil()+
" 邮箱主题: "+item.getMbZt()+" 邮箱模板内容: "+result+" 是否是html:"+isHtml);
PushUtil.SendMail(zybEnterprise.getEamil(),item.getMbZt(),result,isHtml);
System.err.println("企业发送完毕!");
}else{
System.err.println("企业邮箱为空或者null!");
}
break;
case "1" :
if(!StrUtil.hasEmpty(zybEnterprise.getZfryEmail())){
//发送邮箱
System.err.println("执法人员邮箱: "+zybEnterprise.getZfryEmail()+
" 邮箱主题: "+item.getMbZt()+" 邮箱模板内容: "+result+" 是否是html:"+isHtml);
PushUtil.SendMail(zybEnterprise.getZfryEmail(),item.getMbZt(),result,isHtml);
System.err.println("执法人员发送完毕!");
}else{
System.err.println("执法人员邮箱为空或者null!");
};
//判断是否生成功工单
if("1".equals(pushEntity.getHasWo())){
//String zybWoId = IdUtil.simpleUUID();
zybEnterprise.setTyZd(item.getMbZt());
int row =createWo(zybEnterprise,pushEntity);
System.err.println("创建工单返回值 row: "+row);
}
default:
break;
}
}
});
//插入发送记录
int row = createFsjl(zybEnterprise,pushEntity);
System.err.println("插入发送记录返回结果 row: "+row);
}
}).start();
}
/**
* 创建工单
* @param zybEnterprise 企业实体
* @param pushEntity 观察者参数
* @return
*/
public int createWo(zybEnterpriseVo zybEnterprise,PushEntity pushEntity){
if(zybWoService == null){
zybWoService = BeanUtil.getBean(zybWoService.class);
}
//创建工单
zybWo zybWo = new zybWo();
String StrType;
switch (pushEntity.getPushType()){
case "ZYB_QYTJXX":
StrType = "体检";
break;
case "ZYB_PJXX" :
StrType = "评价";
break;
case "ZYB_JCXX" :
StrType = "检测";
break;
case "ZYB_PXXX" :
StrType = "培训";
break;
case "ZYB_STS" :
StrType ="三同时";
break;
default:
StrType = "其他";
break;
}
zybWo.setWoName(zybEnterprise.getCompanyName()+"-"+StrType+"("+zybEnterprise.getTyZd()+")");
zybWo.setWoType("0");
zybWo.setEnterpriseId(zybEnterprise.getId());
zybWo.setReceiveUserId(zybEnterprise.getZfryId());
zybWo.setCreateTime(new Date());
zybWo.setProjectId(pushEntity.getBgId());
zybWo.setProjectType(WoProjectTypeEnum.matchOpCode(pushEntity.getPushType()).toString());
//插入工单记录
ZybWoItem zybWoItem = new ZybWoItem();
String zybWoItemId = IdUtil.simpleUUID();
zybWoItem.setId(zybWoItemId);
zybWoItem.setCreateTime(new Date());
zybWoItem.setWoType("0");
zybWoItem.setCreateBy("1");
return zybWoService.insertSelective(zybWo,zybWoItem);
}
/**
* 插入发送记录
* @param zybEnterpriseVo 企业实体类
* @param pushEntity 观察者参数
* @return
*/
public int createFsjl(zybEnterpriseVo zybEnterpriseVo,PushEntity pushEntity){
if(zybWoFsjlService == null){
zybWoFsjlService = BeanUtil.getBean(ZybWoFsjlService.class);
}
ZybWoFsjl zybWoFsjl = new ZybWoFsjl();
String zybWofsjlId = IdUtil.simpleUUID();
zybWoFsjl.setId(zybWofsjlId);
zybWoFsjl.setEnterpriseId(zybEnterpriseVo.getId());
zybWoFsjl.setTxjgId(pushEntity.getTxjgId());
zybWoFsjl.setFssj(new Date());
zybWoFsjl.setProjectType(WoProjectTypeEnum.matchOpCode(pushEntity.getPushType()).toString());
zybWoFsjl.setCflx("0");
zybWoFsjl.setProjectId(pushEntity.getBgId());
return zybWoFsjlService.insertSelective(zybWoFsjl);
}
}
package com.hx.slj.modules.zyb.job;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.hx.slj.common.utils.CacheUtils;
import com.hx.slj.modules.zyb.ObserverModel.PushEntity;
import com.hx.slj.modules.zyb.ObserverModel.zybPushEntity;
import com.hx.slj.modules.zyb.ObserverModelImpl.OvertimePushServer;
import com.hx.slj.modules.zyb.ObserverModelImpl.QQPush;
import com.hx.slj.modules.zyb.entity.ZybWoMb;
import com.hx.slj.modules.zyb.entity.zybEnterpriseVo;
import com.hx.slj.modules.zyb.entity.zybPjxx;
import com.hx.slj.modules.zyb.entity.zybQytjxx;
import com.hx.slj.modules.zyb.service.*;
import com.hx.slj.modules.zyb.utils.BeanUtil;
import com.hx.slj.modules.zyb.utils.PushUtil;
import net.sf.ehcache.Cache;
import org.apache.poi.ss.formula.functions.T;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import java.util.*;
/**
* @author qb
* @version 1.0
* @Description 超时推送
* @date 2020/8/19 17:35
*/
public class OvertimePush extends QuartzJobBean {
@Autowired
private ZybQyTtxxVoService zybQyTtxxVoService;
@Autowired
private zybWombService zybWombService;
@Autowired
private zybPjxxService zybPjxxService;
@Autowired
private zybPxxxService zybPxxxService;
@Autowired
private zybJcxxVoService zybJcxxVoService;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
//以下4个判断,是解决定时器注入的service为null,
if(zybQyTtxxVoService == null){
zybQyTtxxVoService = BeanUtil.getBean(ZybQyTtxxVoService.class);
}
if(zybPjxxService == null){
zybPjxxService = BeanUtil.getBean(zybPjxxService.class);
}
if(zybPxxxService == null){
zybPxxxService = BeanUtil.getBean(zybPxxxService.class);
}
if(zybJcxxVoService == null){
zybJcxxVoService = BeanUtil.getBean(zybJcxxVoService.class);
}
//获取缓存 这是推送模板的缓存
Cache cache = CacheUtils.getCacheN("ZYB_WO_MB");
//System.err.println("获取cache: "+ cache);
//判断缓存是否存在
if(ObjectUtil.isNull(cache)){
if(zybWombService == null){
zybWombService = BeanUtil.getBean(zybWombService.class);
}
//获取所有的模板
List<ZybWoMb> zybWoMbList = zybWombService.getwombListPush();
//System.err.println("获取的模板zybWoMbList: "+zybWoMbList);
//创建cache
CacheUtils.getCache("ZYB_WO_MB");
//添加模板缓存 list
CacheUtils.put("ZYB_WO_MB","ZYB_WO_MB_LIST",zybWoMbList);
//System.err.println("设置缓存后获取的内容ZybWoMb :"+CacheUtils.get("ZYB_WO_MB","ZYB_WO_MB_LIST"));
}
//后台获取企业即将超时体检数据
List<zybPushEntity> qyList = zybQyTtxxVoService.selectOverTime();
//获取企业即将超时评价数据
List<zybPushEntity> zybPjxxList = zybPjxxService.selectOverTime();
//获取企业即将超时数据 --培训
List<zybPushEntity> zybPxxxList = zybPxxxService.selectOverTime();
//获取企业即将超时数据 --检测
List<zybPushEntity> zybJcxxList = zybJcxxVoService.selectOverTime();
//企业是否有超时推送数据 ---体检
if(qyList.size() > 0){
foreachList(qyList,"ZYB_QYTJXX");
}
//企业是否有超时推送数据 ---评价
if(zybPjxxList.size()>0){
foreachList(zybPjxxList,"ZYB_PJXX");
}
//企业是否有超时推送数据 ---培训
if(zybPxxxList.size()>0){
foreachList(zybPxxxList,"ZYB_PXXX");
}
//企业是否有超时推送数据 ---检测
if(zybJcxxList.size()>0){
foreachList(zybJcxxList,"ZYB_JCXX");
}
System.err.println("执行推送定时!");
}
/**
* 循环赋值
* @param list 实体集合
* @param pushType 推送类型
*/
public void foreachList(List<zybPushEntity> list,String pushType){
//实例化观察者
QQPush qqPush = new QQPush();
//实例化观察主题
OvertimePushServer overtimePushServer = new OvertimePushServer();
//添加观察者
overtimePushServer.registerObserver(qqPush);
//循环赋值观察者参数
for(zybPushEntity item:list){
//实例化观察者参数
PushEntity pushEntity = new PushEntity();
//观察者需要通知的企业id
pushEntity.setId(item.getEnterpriseid());
//观察者需要用的 提醒间隔id
pushEntity.setTxjgId(item.getZybWoTxjg().getId());
//观察者用到的各种报告id
pushEntity.setBgId(item.getId());
//观察者用到的推送类型
pushEntity.setPushType(pushType);
//观察者检查是否生成工单
pushEntity.setHasWo(item.getZybWoTxjg().getHasWo().toString());
//观察者执行通知
overtimePushServer.notifyObserver(pushEntity);
}
}
}
在写sql时遇到的难点,以下下sql,可以将发送记录中存在的数据(15、30天等)去除,只取发送记录中不存在的数据
project_type='ZYB_QYTJXX’此条件为获取体检数据
<select id="selectOverTime" resultType="com.hx.slj.modules.zyb.ObserverModel.zybPushEntity">
select
a.id as "id",
a.enterpriseId as "enterpriseid",
a.nexttjTime as "nextTime",
b.id as "zybWoTxjg.id",
b.txts as "zybWoTxjg.txts",
b.has_wo as "zybWoTxjg.hasWo"
from zyb_qytjxx a cross join zyb_wo_txjg b where nexttjTime < date_add(SYSDATE(), INTERVAL b.txts day)
and not exists(select 1 from zyb_wo_fsjl c where c.txjg_id=b.id and c.project_id=a.id and project_type='ZYB_QYTJXX')
and a.nexttjTime > '2020-08-01'
select>