業務場景: 需要在定時器中查詢某個人的狀態(相當於實時查詢),判斷是否需要推送消息,推送消息的機制是20分鐘之後,並且需要無限推送,使用delayQueue來實現
實現細節: 定時器掃描任務,維護一個需要推送消息的map,key爲mobile,value爲延時推送消息對象,同時維護一個queue,這個queue存放需要推送的對象
每次先衝map中取出推送對象(根據mobile獲取):
1.獲取不到(說明上一次這個人不需要推送消息),在判斷本次是否需要推送,如果需要推送,則存入map,同時讓多線程取執行,否則不進行處理
2.獲取到了(說明上一次需要推送,並且延時隊列中未消費),判斷本次是否需要推送,如果需要推送,存入map,推送消息
否則移除map和deayQueue中的對象,目的是保證map和delayQueue中一個人(手機號的唯一性)
這樣就保證了能循環間隔推送,並且當本次查詢的不需要推送的話,直接取消上一次沒有推送的任務(因爲我們需要實時監測是否需要推送)
定時器代碼:
private DelayQueue<SalesMessage> delayedQueue;
/**
* 維護需要推送的對象
*/
private Map<String,SalesMessage> salesMap;
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
@Async
@Scheduled(fixedDelay = 60000)
public void searchUnSaveCustomer(){
log.info("進入查詢銷售顧問未錄入的顧客定時任務.......");
//拉取所有銷售顧問列表
QueryWrapper<SysAccount> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("role_id",RoleEnum.SELLER.getValue());
List<SysAccount> sysList = Optional.ofNullable(sysAccountDAO.list(queryWrapper)).orElse(new ArrayList<>());
if(!CollectionUtils.isEmpty(sysList)){
for (SysAccount account : sysList) {
Store store = Optional.ofNullable(storeDAO.getById(account.getStoreId())).orElse(new Store());
if(ObjectUtils.isEmpty(store.getId()) || ObjectUtils.isEmpty(store.getDealerId()) || StringUtils.isEmpty(account.getMobile())){
log.warn("查無店鋪/經銷商信息.....");
continue;
}
List<WorkStatusIteam> list = new ArrayList<>();
if(1 == Integer.parseInt(isChongQing)){
list = this.queryWorkStatus(store.getDealerId(),null);
}
if(CollectionUtils.isEmpty(list)){
return;
}
long n = list.stream().filter(x -> x.getChangeStatus().equals(WorkStatusEnum.IN_THE_RECEPTION.getValue())).count();
//long n = customerRecordDAO.count();
//查詢當天該銷售顧問錄入的顧客數量
QueryWrapper<Customer> wrapper = new QueryWrapper<>();
wrapper.eq("sales_id",account.getId());
wrapper.eq("store_id",account.getStoreId());
wrapper.apply("date(create_time) = CURDATE()");
int count = customerDAO.count(wrapper);
String mobile = account.getMobile();
//判斷是否存在對象
SalesMessage salesMessage = salesMap.get(mobile);
if(ObjectUtils.isEmpty(salesMessage)){
//不存在,判斷是否需要推送
if(count != n){
this.pushMessage(n,count,mobile);
}
}else{
//存在,判斷當前是否需要推送,需要推送就維持之前的推送隊列,否則,就移除隊列中對應的數據
if(count == n){
//說明當前不需要推送,那麼久移除隊列中對應的數據
if(!delayedQueue.isEmpty()){
boolean remove = delayedQueue.remove(salesMessage);
log.info("當前狀態下,銷售顧問已經錄入了顧客,不需要推送消息,取消隊列任務:[{}]",remove);
}
}
}
}
}
}
延時消息對象代碼:
package com.hfepay.ai.store.changan.scheduled;
import lombok.Data;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/***
* @ClassName: SalesMessage
* @Description: 延時消息對象
* @Author: wm_yu
* @Create_time: 17:14 2020-1-9
*/
@Data
public class SalesMessage implements Delayed {
/**
* 顧問手機
*/
private String mobile;
/**
* 消息內容
*/
private String body;
/**
* 延遲時長,這個是必須的屬性因爲要按照這個判斷延時時長。
*/
private long excuteTime;
/**
* 到期時間
*/
private long expire;
public SalesMessage(String mobile, String body, long excuteTime) {
this.mobile = mobile;
this.body = body;
this.excuteTime = excuteTime;
this.expire = System.currentTimeMillis() + excuteTime*1000;
}
/**
* 需要實現的接口,獲得延遲時間 用過期時間-當前時間
* @param unit
* @return
*/
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(this.expire - System.currentTimeMillis() , TimeUnit.MILLISECONDS);
}
/**
* 用於延遲隊列內部比較排序 當前時間的延遲時間 - 比較對象的延遲時間
* @param o
* @return
*/
@Override
public int compareTo(Delayed o) {
return (int) (o.getDelay(TimeUnit.MILLISECONDS) - this.getDelay(TimeUnit.MILLISECONDS));
}
}
消費者代碼,一直去讀取queue中的對象,只有在延時時間到期,才能獲取到數據
package com.hfepay.ai.store.changan.scheduled;
import com.alibaba.fastjson.JSONObject;
import com.hfepay.ai.store.changan.service.GuanChuangProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;
import java.util.Map;
import java.util.concurrent.DelayQueue;
/***
* @ClassName: SalesMessageConsumer
* @Description:
* @Author: wm_yu
* @Create_time: 17:21 2020-1-9
*/
@AllArgsConstructor
@Data
@Slf4j
public class SalesMessageConsumer implements Runnable {
/**
* 延時隊列 ,消費者從其中獲取消息進行消費
*/
private DelayQueue<SalesMessage> queue;
private GuanChuangProperties guanChuangProperties;
private String isChongQing;
private Map<String,SalesMessage> map;
@Override
public void run() {
while (queue.size() > 0){
try {
SalesMessage salesMessage = queue.poll();
if(!ObjectUtils.isEmpty(salesMessage)){
log.info("延時隊列獲取的數據:[{}],是否需要發送消息提醒:[{}]", JSONObject.toJSONString(salesMessage),!ObjectUtils.isEmpty(salesMessage));
String mobile = salesMessage.getMobile();
String body = salesMessage.getBody();
guanChuangProperties.sendMessage(Integer.parseInt(isChongQing),mobile,body);
//移除map中的對應數據
map.remove(mobile);
log.info("推送消息手機號:[{}],推送內容:[{}]",mobile,body);
}
} catch (Exception e) {
log.info("獲取延時隊列數據異常,",e.getMessage(),e);
}
}
}
}