Commit 9cd45bc3 by 陈健智

企业微信机器人自主版数据用量预警

parent b3219858
...@@ -82,4 +82,9 @@ public interface TaskService{ ...@@ -82,4 +82,9 @@ public interface TaskService{
* 生成ai搜索参考提问缓存 * 生成ai搜索参考提问缓存
*/ */
void cacheAIQuestion(); void cacheAIQuestion();
/**
* 非人工项目企业微信机器人数据用量预警
*/
void wechatRobotWarnPush();
} }
...@@ -19,6 +19,7 @@ import com.zhiwei.brandkbs2.service.MarkDataService; ...@@ -19,6 +19,7 @@ import com.zhiwei.brandkbs2.service.MarkDataService;
import com.zhiwei.brandkbs2.service.ProjectService; import com.zhiwei.brandkbs2.service.ProjectService;
import com.zhiwei.brandkbs2.service.UserService; import com.zhiwei.brandkbs2.service.UserService;
import com.zhiwei.brandkbs2.util.MongoUtil; import com.zhiwei.brandkbs2.util.MongoUtil;
import com.zhiwei.brandkbs2.util.RobotPushUtils;
import com.zhiwei.brandkbs2.util.Tools; import com.zhiwei.brandkbs2.util.Tools;
import com.zhiwei.middleware.auth.util.JwtUtil; import com.zhiwei.middleware.auth.util.JwtUtil;
import com.zhiwei.middleware.event.pojo.dto.EventTagRelatedDTO; import com.zhiwei.middleware.event.pojo.dto.EventTagRelatedDTO;
...@@ -40,6 +41,7 @@ import org.springframework.web.context.request.ServletRequestAttributes; ...@@ -40,6 +41,7 @@ import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.text.MessageFormat;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
...@@ -649,6 +651,11 @@ public class ProjectServiceImpl implements ProjectService { ...@@ -649,6 +651,11 @@ public class ProjectServiceImpl implements ProjectService {
projectDao.updateOneByIdWithField(projectId, update); projectDao.updateOneByIdWithField(projectId, update);
NonManualProjectDataUpdateRecord record = new NonManualProjectDataUpdateRecord(projectId, project.getDataBalance(), currentCount, UserThreadLocal.getNickname()); NonManualProjectDataUpdateRecord record = new NonManualProjectDataUpdateRecord(projectId, project.getDataBalance(), currentCount, UserThreadLocal.getNickname());
nonManualProjectDataUpdateRecordDao.insertOne(record); nonManualProjectDataUpdateRecordDao.insertOne(record);
String message = "预警项目:{0}-数据额度调整" + "\n" +
"调整前额度:{1}" + "\n" +
"调整后额度:{2}" + "\n" +
"调整人员:{3}";
RobotPushUtils.pushCommonMessage(MessageFormat.format(message, project.getProjectName(), project.getDataBalance(), currentCount, UserThreadLocal.getNickname()));
} }
@Override @Override
......
...@@ -8,6 +8,7 @@ import com.zhiwei.brandkbs2.common.RedisKeyPrefix; ...@@ -8,6 +8,7 @@ import com.zhiwei.brandkbs2.common.RedisKeyPrefix;
import com.zhiwei.brandkbs2.config.Constant; import com.zhiwei.brandkbs2.config.Constant;
import com.zhiwei.brandkbs2.dao.*; import com.zhiwei.brandkbs2.dao.*;
import com.zhiwei.brandkbs2.enmus.ReportTypeEnum; import com.zhiwei.brandkbs2.enmus.ReportTypeEnum;
import com.zhiwei.brandkbs2.enmus.RoleEnum;
import com.zhiwei.brandkbs2.es.ChannelEsDao; import com.zhiwei.brandkbs2.es.ChannelEsDao;
import com.zhiwei.brandkbs2.es.EsClientDao; import com.zhiwei.brandkbs2.es.EsClientDao;
import com.zhiwei.brandkbs2.es.EsQueryTools; import com.zhiwei.brandkbs2.es.EsQueryTools;
...@@ -17,9 +18,11 @@ import com.zhiwei.brandkbs2.model.CommonCodeEnum; ...@@ -17,9 +18,11 @@ import com.zhiwei.brandkbs2.model.CommonCodeEnum;
import com.zhiwei.brandkbs2.pojo.*; import com.zhiwei.brandkbs2.pojo.*;
import com.zhiwei.brandkbs2.service.*; import com.zhiwei.brandkbs2.service.*;
import com.zhiwei.brandkbs2.util.RedisUtil; import com.zhiwei.brandkbs2.util.RedisUtil;
import com.zhiwei.brandkbs2.util.RobotPushUtils;
import com.zhiwei.brandkbs2.util.Tools; import com.zhiwei.brandkbs2.util.Tools;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils; import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
...@@ -35,6 +38,7 @@ import org.springframework.stereotype.Service; ...@@ -35,6 +38,7 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.IOException; import java.io.IOException;
import java.text.MessageFormat;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
...@@ -83,6 +87,9 @@ public class TaskServiceImpl implements TaskService { ...@@ -83,6 +87,9 @@ public class TaskServiceImpl implements TaskService {
@Resource(name = "xiaohongshuRecordDao") @Resource(name = "xiaohongshuRecordDao")
private XiaohongshuRecordDao xiaohongshuRecordDao; private XiaohongshuRecordDao xiaohongshuRecordDao;
@Resource(name = "userDao")
private UserDao userDao;
@Resource(name = "customInteractionUpdateRecordDao") @Resource(name = "customInteractionUpdateRecordDao")
private CustomInteractionUpdateRecordDao customInteractionUpdateRecordDao; private CustomInteractionUpdateRecordDao customInteractionUpdateRecordDao;
...@@ -513,6 +520,95 @@ public class TaskServiceImpl implements TaskService { ...@@ -513,6 +520,95 @@ public class TaskServiceImpl implements TaskService {
}, cacheServiceExecutor)).toArray(CompletableFuture[]::new)).join(); }, cacheServiceExecutor)).toArray(CompletableFuture[]::new)).join();
} }
@Override
public void wechatRobotWarnPush() {
CompletableFuture.allOf(GlobalPojo.PROJECT_MAP.values().stream().filter(project -> project.isStart() && !project.isManual()).map(project -> CompletableFuture.supplyAsync(() -> {
try {
long yestConsume = getYesterdayProjectPlanConsume(project.getId(), null);
double balanceCanUsedDay = 0 >= project.getDataBalance() ? 0 :
0 == yestConsume ? project.getDataBalance() : (double) project.getDataBalance() / yestConsume;
// 项目管理员
Criteria criteria = Criteria.where("roles.projectId").is(project.getId()).and("superAdmin").is(false);
criteria.andOperator(Criteria.where("roles.key").is(Tools.concat(project.getId(), RoleEnum.ADMIN.getState())));
List<String> adminList = userDao.findList(new Query(criteria)).stream().map(User::getNickname).collect(Collectors.toList());
// 大数据量方案预警
if (yestConsume > 10000){
largeDataWarn(project);
}
// 数据额度不足方案停用预警
if (0 == balanceCanUsedDay){
pushWarn("数据额度不足方案停用", project, yestConsume, adminList);
return null;
}
// 数据额度不足7天预警
if (balanceCanUsedDay < 7){
pushWarn("数据额度不足7天预警", project, yestConsume, adminList);
return null;
}
// 数据额度不足15天预警
if (balanceCanUsedDay < 15){
pushWarn("数据额度不足15天预警", project, yestConsume, adminList);
return null;
}
// 数据额度不足30天预警
if (balanceCanUsedDay < 30){
pushWarn("数据额度不足30天预警", project, yestConsume, adminList);
return null;
}
}catch (Exception e){
log.error("非人工项目:{}-{}-企业微信机器人预警出错-", project.getProjectName(), project.getId(), e);
}
return null;
}, cacheServiceExecutor)).toArray(CompletableFuture[]::new)).join();
}
private void largeDataWarn(Project project) throws IOException {
List<NonManualProjectPlan> plans = nonManualProjectPlanDao.findList(new Query(Criteria.where("projectId").is(project.getId())));
if (CollectionUtils.isEmpty(plans)){
return;
}
StringBuilder sb = new StringBuilder();
sb.append(MessageFormat.format("项目:{0}-{1}", project.getProjectName(), "大数据量方案预警")).append("\n");
for (int i = 0; i < plans.size(); i++) {
NonManualProjectPlan plan = plans.get(i);
long count = getYesterdayProjectPlanConsume(project.getId(), plan.getId());
String message = MessageFormat.format("方案{0}:{1}({2}),数据量:{3}", i + 1, plan.getName(), project.getProjectName(), count);
sb.append(message);
if (i + 1 != plans.size()){
sb.append("\n");
}
}
RobotPushUtils.pushCommonMessage(sb.toString());
}
private void pushWarn(String warnType, Project project, long yestConsume, List<String> admins){
String template = "项目:{0}-{1}" + "\n" +
"剩余额度:{2}" + "\n" +
"昨日消耗:{3}" + "\n" +
"项目管理员:{4}";
String message = MessageFormat.format(template, project.getProjectName(), warnType, project.getDataBalance(), yestConsume, StringUtils.join(admins, ","));
RobotPushUtils.pushCommonMessage(message);
}
private long getYesterdayProjectPlanConsume(String projectId, String planId) throws IOException {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_YEAR, -1);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.MILLISECOND, 0);
long yesterdayStartTime = calendar.getTimeInMillis();
calendar.add(Calendar.DAY_OF_YEAR, 1);
long yesterdayEndTime = calendar.getTimeInMillis();
BoolQueryBuilder query = EsQueryTools.assembleCacheMapsQuery(projectId, Constant.PRIMARY_CONTEND_ID);
if (Objects.nonNull(planId)){
query = EsQueryTools.assembleCacheMapsPlanQuery(projectId, planId);
}
// stime
query.must(QueryBuilders.rangeQuery("stime").gte(yesterdayStartTime).lt(yesterdayEndTime));
return esClientDao.count(query);
}
private void updateRefreshTask(String id, String status){ private void updateRefreshTask(String id, String status){
Update update = new Update(); Update update = new Update();
update.set("status", status); update.set("status", status);
......
...@@ -146,4 +146,17 @@ public class ControlCenter { ...@@ -146,4 +146,17 @@ public class ControlCenter {
log.info("每十分钟拉取并进行渠道库更新任务-结束"); log.info("每十分钟拉取并进行渠道库更新任务-结束");
} }
} }
@Async("scheduledExecutor")
@Scheduled(cron = "0 0 6 * * ?")
public void wechatRobotWarnPush() {
log.info("每天六点非人工项目企业微信机器人数据用量预警-启动");
try {
taskService.wechatRobotWarnPush();
} catch (Exception e) {
log.error("每天六点非人工项目企业微信机器人数据用量预警-出错", e);
} finally {
log.info("每天六点非人工项目企业微信机器人数据用量预警-结束");
}
}
} }
package com.zhiwei.brandkbs2.util;
import com.alibaba.fastjson.JSONObject;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.zhiwei.pushlog.config.LogConfig;
import com.zhiwei.pushlog.pojo.CountExpireValue;
import com.zhiwei.pushlog.tools.HttpClientUtils;
import com.zhiwei.pushlog.tools.Tools;
import org.springframework.beans.factory.annotation.Value;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* Description:
*
* @author LiuMingHuan
* @classname RobotPushUtils
* @date 2020/3/13
*/
public class RobotPushUtils {
private static final String[] MENTION_MOBILE_LIST = new String[]{};
private static final int STAGE = 50;
private static final String WECHAT_ROBOT_ADDRESS = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=664bf154-5da9-4615-8043-a396c43301ac";
private static Cache<String, CountExpireValue> COUNT_RECORDS_CACHE = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.DAYS)
.maximumSize(1000)
.build();
/**
* 错误消息推送
*
* @param message
* @param throwable
* @return
*/
public static String pushErrorMessage(String message, Throwable throwable) {
// if (com.zhiwei.pushlog.tools.Tools.isEmpty(LogConfig.getAddress()) && LogConfig.loadProperties()) {
// throw new IllegalArgumentException("push.robot.address is null");
// }
if (!LogConfig.getEnable()){
return null;
}
if (Objects.nonNull(throwable) && !timeFilter(throwable.getClass().getName())) {
return null;
}
JSONObject data = new JSONObject();
JSONObject markdown = new JSONObject();
markdown.put("content", getMarkDownMessage(message, throwable));
markdown.put("mentioned_mobile_list", MENTION_MOBILE_LIST);
data.put("msgtype", "markdown");
data.put("markdown", markdown);
// 缓存超时信息清除
if(Objects.nonNull(throwable)) {
invalidate(throwable.getClass().getName());
}
return HttpClientUtils.httpPostJson(WECHAT_ROBOT_ADDRESS, data.toJSONString());
}
/**
* pushCommonMessage
*
* @param message
* @return
*/
public static String pushCommonMessage(String message) {
// if (com.zhiwei.pushlog.tools.Tools.isEmpty(LogConfig.getAddress()) && LogConfig.loadProperties()) {
// throw new IllegalArgumentException("push.robot.address is null");
// }
if (!LogConfig.getEnable()){
return null;
}
if (Objects.isNull(message) || !timeFilter(message)) {
return null;
}
JSONObject data = new JSONObject();
JSONObject markdown = new JSONObject();
markdown.put("content", getMarkDownMessageCommon(message));
markdown.put("mentioned_mobile_list", MENTION_MOBILE_LIST);
data.put("msgtype", "markdown");
data.put("markdown", markdown);
// 缓存超时信息清除
invalidate(message);
return HttpClientUtils.httpPostJson(WECHAT_ROBOT_ADDRESS, data.toJSONString());
}
/**
* timeFilter
*
* @param exceptionName
* @return
*/
private static boolean timeFilter(String exceptionName) {
boolean b = Objects.isNull(COUNT_RECORDS_CACHE.getIfPresent(exceptionName));
if (b) {
// time 数值
CountExpireValue value = new CountExpireValue(System.currentTimeMillis(), LogConfig.getInterval(), 1);
COUNT_RECORDS_CACHE.put(exceptionName, value);
} else {
CountExpireValue value = COUNT_RECORDS_CACHE.getIfPresent(exceptionName);
// 判断过期
if (Objects.nonNull(value)) {
if (value.getTime() + value.getExpire() < System.currentTimeMillis()) {
// 过期
return true;
} else {
// 累计
value = new CountExpireValue(value.getTime(), value.getExpire(), value.getValue() + 1);
COUNT_RECORDS_CACHE.put(exceptionName, value);
}
}
}
return b;
}
/**
* timeoutCache invalidate
*
* @param exceptionName
*/
private static void invalidate(String exceptionName) {
CountExpireValue value = COUNT_RECORDS_CACHE.getIfPresent(exceptionName);
if (Objects.nonNull(value) && (value.getTime() + value.getExpire() < System.currentTimeMillis())) {
COUNT_RECORDS_CACHE.invalidate(exceptionName);
}
}
/**
* getMarkDownMessage
*
* @param message
* @param throwable
* @return
*/
public static String getMarkDownMessage(String message, Throwable throwable) {
StringBuilder stringBuilder = getPushHeader("error");
String stackTrace = !com.zhiwei.pushlog.tools.Tools.isEmpty(throwable) ? getStackTrace(throwable.getStackTrace()) : "无";
String exceptionType = !com.zhiwei.pushlog.tools.Tools.isEmpty(throwable) ? throwable.getClass().getName() : "无";
String localizedMessage = !com.zhiwei.pushlog.tools.Tools.isEmpty(throwable) ? throwable.getLocalizedMessage() : "无";
stringBuilder.append(">").append("异常类型:").append("**").append(exceptionType).append("**\n");
stringBuilder.append(">").append("错误信息:").append("**").append(message).append("**\n");
stringBuilder.append(">").append("异常信息:").append(localizedMessage).append("\n");
CountExpireValue countExpireValue = !com.zhiwei.pushlog.tools.Tools.isEmpty(throwable) ? COUNT_RECORDS_CACHE.getIfPresent(throwable.getClass().getName()) : null;
if (!Tools.isEmpty(countExpireValue) && countExpireValue.getValue() > 1) {
stringBuilder.append(">").append("相同异常统计过期前,累计出现次数:").append(countExpireValue.getValue()).append("\n");
}
stringBuilder.append(">").append("堆栈信息:\n").append("<font color=\"comment\">").append(stackTrace).append("</font>\n");
return stringBuilder.toString();
}
/**
* getMarkDownMessageCommon
*
* @param message
* @return
*/
private static String getMarkDownMessageCommon(String message) {
StringBuilder stringBuilder = getPushHeader("common");
stringBuilder.append(">").append("预警信息:").append("**").append(message).append("**\n");
return stringBuilder.toString();
}
/**
* getPushHeader
*
* @param type
* @return
*/
public static StringBuilder getPushHeader(String type) {
StringBuilder stringBuilder = new StringBuilder("本机信息:");
try {
String header = "error".equals(type) ? "异常推送预警\n" : "日常预警推送\n";
InetAddress localHost = Inet4Address.getLocalHost();
stringBuilder.append(localHost.getHostAddress()).append("\n").append(LogConfig.getAppName()).append(" ").append(header);
} catch (Exception e) {
System.err.println("推送出错");
e.printStackTrace();
}
return stringBuilder;
}
/**
* getStackTrace
*
* @param stackTraceElements
* @return
*/
private static String getStackTrace(StackTraceElement[] stackTraceElements) {
StringBuilder stringBuilder = new StringBuilder();
if (stackTraceElements.length > 0) {
for (int i = 0; i < 5; i++) {
stringBuilder.append(stackTraceElements[i].toString());
if (i != 4) {
stringBuilder.append("\n");
}
}
}
return stringBuilder.toString();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment