diff --git a/dinky-admin/pom.xml b/dinky-admin/pom.xml index fa65529d6f..2cd42444eb 100644 --- a/dinky-admin/pom.xml +++ b/dinky-admin/pom.xml @@ -337,6 +337,14 @@ org.flywaydb flyway-mysql + + org.dinky + dinky-alert-dingtalk + + + org.dinky + dinky-alert-sms + diff --git a/dinky-admin/src/main/java/org/dinky/job/handler/JobAlertHandler.java b/dinky-admin/src/main/java/org/dinky/job/handler/JobAlertHandler.java index fccfc36f06..0ca798db0b 100644 --- a/dinky-admin/src/main/java/org/dinky/job/handler/JobAlertHandler.java +++ b/dinky-admin/src/main/java/org/dinky/job/handler/JobAlertHandler.java @@ -22,6 +22,8 @@ import org.dinky.alert.Alert; import org.dinky.alert.AlertConfig; import org.dinky.alert.AlertResult; +import org.dinky.alert.dingtalk.DingTalkConstants; +import org.dinky.alert.sms.SmsConstants; import org.dinky.assertion.Asserts; import org.dinky.context.FreeMarkerHolder; import org.dinky.context.SpringContextUtils; @@ -30,6 +32,7 @@ import org.dinky.data.dto.TaskDTO; import org.dinky.data.enums.JobLifeCycle; import org.dinky.data.enums.Status; +import org.dinky.data.enums.TaskOwnerAlertStrategyEnum; import org.dinky.data.exception.DinkyException; import org.dinky.data.model.Configuration; import org.dinky.data.model.SystemConfiguration; @@ -38,16 +41,21 @@ import org.dinky.data.model.alert.AlertInstance; import org.dinky.data.model.ext.JobAlertData; import org.dinky.data.model.ext.JobInfoDetail; +import org.dinky.data.model.rbac.User; import org.dinky.data.options.JobAlertRuleOptions; import org.dinky.service.AlertHistoryService; import org.dinky.service.TaskService; +import org.dinky.service.UserService; import org.dinky.service.impl.AlertRuleServiceImpl; import org.dinky.utils.JsonUtils; +import org.apache.commons.compress.utils.Lists; + import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -65,6 +73,7 @@ import com.google.common.cache.LoadingCache; import cn.hutool.core.text.StrFormatter; +import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import lombok.Data; @@ -78,6 +87,7 @@ public class JobAlertHandler { private static final TaskService taskService; private static final AlertRuleServiceImpl alertRuleService; private static final SystemConfiguration systemConfiguration = SystemConfiguration.getInstances(); + private static final UserService userService; /** * Rules for evaluating alert conditions. @@ -103,12 +113,15 @@ public class JobAlertHandler { */ private static LoadingCache> alertCache; + private static LoadingCache userCache; + private static volatile JobAlertHandler defaultJobAlertHandler; static { taskService = SpringContextUtils.getBean("taskServiceImpl", TaskService.class); alertHistoryService = SpringContextUtils.getBean("alertHistoryServiceImpl", AlertHistoryService.class); alertRuleService = SpringContextUtils.getBean("alertRuleServiceImpl", AlertRuleServiceImpl.class); + userService = SpringContextUtils.getBean("userServiceImpl", UserService.class); Configuration jobReSendDiffSecond = systemConfiguration.getJobReSendDiffSecond(); jobReSendDiffSecond.addChangeEvent((c) -> { @@ -118,6 +131,15 @@ public class JobAlertHandler { .build(CacheLoader.from(() -> new HashMap<>())); }); jobReSendDiffSecond.runChangeEvent(); + + userCache = CacheBuilder.newBuilder() + .expireAfterWrite(10, TimeUnit.MINUTES) + .build(new CacheLoader() { + @Override + public User load(Integer id) { // no checked exception + return userService.getById(id); + } + }); } public static JobAlertHandler getInstance() { @@ -228,12 +250,87 @@ private void executeAlertAction(Facts facts, AlertRuleDTO alertRuleDTO) throws E String alertContent = freeMarkerHolder.buildWithData(alertRuleDTO.getTemplateName(), dataModel); if (!Asserts.isNull(task.getAlertGroup())) { + // 获取任务的责任人和维护人对应的用户信息|Get the responsible person and maintainer of the task + User ownerInfo = userCache.get(task.getFirstLevelOwner()); + List maintainerInfo = task.getSecondLevelOwners().stream() + .map(id -> { + try { + return userCache.get(id); + } catch (ExecutionException e) { + e.printStackTrace(); + } + return null; + }) + .collect(Collectors.toList()); AlertGroup alertGroup = task.getAlertGroup(); alertGroup.getInstances().stream() .filter(Objects::nonNull) .filter(AlertInstance::getEnabled) - .forEach(alertInstance -> sendAlert( - alertInstance, jobInstanceId, alertGroup.getId(), alertRuleDTO.getName(), alertContent)); + .forEach(alertInstance -> { + addOwnerAlert(alertInstance, ownerInfo, maintainerInfo); + sendAlert( + alertInstance, jobInstanceId, alertGroup.getId(), alertRuleDTO.getName(), alertContent); + }); + } + } + + /** + * Add the contact number of the task owner to the phone list of the alarm instance to alert the task owner + * @param alertInstance + * @param ownerInfo + * @param maintainerInfo + */ + private void addOwnerAlert(AlertInstance alertInstance, User ownerInfo, List maintainerInfo) { + List extraMobileList = Lists.newArrayList(); + TaskOwnerAlertStrategyEnum value = + SystemConfiguration.getInstances().getTaskOwnerAlertStrategy().getValue(); + switch (value) { + case OWNER: + if (ownerInfo != null && ownerInfo.getMobile() != null) { + extraMobileList.add(ownerInfo.getMobile()); + } + break; + case OWNER_AND_MAINTAINER: + if (ownerInfo != null && ownerInfo.getMobile() != null) { + extraMobileList.add(ownerInfo.getMobile()); + } + extraMobileList.addAll(maintainerInfo.stream() + .filter(user -> Objects.nonNull(user) && StrUtil.isNotBlank(user.getMobile())) + .map(User::getMobile) + .collect(Collectors.toList())); + break; + case NONE: + default: + log.error("Alert Strategy Type: {} is not supported", value); + return; + } + // 获取告警实例的配置参数|Get the configuration parameters of the alert instance + Map alertInstanceParams = alertInstance.getParams(); + switch (alertInstance.getType()) { + case DingTalkConstants.TYPE: + Boolean atAll = + (Boolean) alertInstanceParams.getOrDefault(DingTalkConstants.ALERT_TEMPLATE_AT_ALL, false); + if (!atAll) { + // 重新构告警实例的告警人员|Rebuild the alert personnel of the alert instance + List atMobiles = + (List) alertInstanceParams.get(DingTalkConstants.ALERT_TEMPLATE_AT_MOBILES); + atMobiles.addAll(extraMobileList.stream() + .filter(mobile -> !atMobiles.contains(mobile)) + .collect(Collectors.toList())); + alertInstanceParams.put(DingTalkConstants.ALERT_TEMPLATE_AT_MOBILES, atMobiles); + alertInstance.setParams(alertInstanceParams); + } + break; + case SmsConstants.TYPE: + // 重新构告警实例的告警人员|Rebuild the alert personnel of the alert instance + List phoneNumbers = (List) alertInstanceParams.get(SmsConstants.PHONE_NUMBERS); + phoneNumbers.addAll(extraMobileList.stream() + .filter(mobile -> !phoneNumbers.contains(mobile)) + .collect(Collectors.toList())); + alertInstanceParams.put(DingTalkConstants.ALERT_TEMPLATE_AT_MOBILES, phoneNumbers); + alertInstance.setParams(alertInstanceParams); + break; + default: } } diff --git a/dinky-common/src/main/java/org/dinky/data/enums/Status.java b/dinky-common/src/main/java/org/dinky/data/enums/Status.java index a420709a55..ef58f83f59 100644 --- a/dinky-common/src/main/java/org/dinky/data/enums/Status.java +++ b/dinky-common/src/main/java/org/dinky/data/enums/Status.java @@ -360,6 +360,8 @@ public enum Status { SYS_ENV_SETTINGS_EXPRESSION_VARIABLE_NOTE(1176, "sys.env.settings.expressionVariable.note"), SYS_ENV_SETTINGS_TASK_OWNER_LOCK_STRATEGY(1177, "sys.env.settings.taskOwnerLockStrategy"), SYS_ENV_SETTINGS_TASK_OWNER_LOCK_STRATEGY_NOTE(1178, "sys.env.settings.taskOwnerLockStrategy.note"), + SYS_ENV_SETTINGS_TASK_OWNER_ALERT_STRATEGY(1179, "sys.env.settings.taskOwnerAlertStrategy"), + SYS_ENV_SETTINGS_TASK_OWNER_ALERT_STRATEGY_NOTE(1180, "sys.env.settings.taskOwnerAlertStrategy.note"), SYS_DOLPHINSCHEDULER_SETTINGS_ENABLE(118, "sys.dolphinscheduler.settings.enable"), SYS_DOLPHINSCHEDULER_SETTINGS_ENABLE_NOTE(119, "sys.dolphinscheduler.settings.enable.note"), diff --git a/dinky-common/src/main/java/org/dinky/data/enums/TaskOwnerAlertStrategyEnum.java b/dinky-common/src/main/java/org/dinky/data/enums/TaskOwnerAlertStrategyEnum.java new file mode 100644 index 0000000000..364f439cd9 --- /dev/null +++ b/dinky-common/src/main/java/org/dinky/data/enums/TaskOwnerAlertStrategyEnum.java @@ -0,0 +1,32 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.dinky.data.enums; + +/** Business Operation Types */ +public enum TaskOwnerAlertStrategyEnum { + /** Alert includes responsible person */ + OWNER, + + /** Alert includes responsible person and maintainer */ + OWNER_AND_MAINTAINER, + + /** Alert does not include responsible person and maintainer */ + NONE, +} diff --git a/dinky-common/src/main/java/org/dinky/data/model/SystemConfiguration.java b/dinky-common/src/main/java/org/dinky/data/model/SystemConfiguration.java index 899b1fbea8..62a1af8f17 100644 --- a/dinky-common/src/main/java/org/dinky/data/model/SystemConfiguration.java +++ b/dinky-common/src/main/java/org/dinky/data/model/SystemConfiguration.java @@ -22,6 +22,7 @@ import org.dinky.context.EngineContextHolder; import org.dinky.data.constant.CommonConstant; import org.dinky.data.enums.Status; +import org.dinky.data.enums.TaskOwnerAlertStrategyEnum; import org.dinky.data.enums.TaskOwnerLockStrategyEnum; import org.dinky.data.properties.OssProperties; @@ -139,6 +140,12 @@ public static Configuration.OptionBuilder key(Status status) { .defaultValue(TaskOwnerLockStrategyEnum.ALL) .note(Status.SYS_ENV_SETTINGS_TASK_OWNER_LOCK_STRATEGY_NOTE); + private final Configuration taskOwnerAlertStrategy = + key(Status.SYS_ENV_SETTINGS_TASK_OWNER_ALERT_STRATEGY) + .enumType(TaskOwnerAlertStrategyEnum.class) + .defaultValue(TaskOwnerAlertStrategyEnum.NONE) + .note(Status.SYS_ENV_SETTINGS_TASK_OWNER_ALERT_STRATEGY_NOTE); + private final Configuration dolphinschedulerEnable = key(Status.SYS_DOLPHINSCHEDULER_SETTINGS_ENABLE) .booleanType() .defaultValue(false) diff --git a/dinky-common/src/main/resources/i18n/messages_en_US.properties b/dinky-common/src/main/resources/i18n/messages_en_US.properties index 27f8970f85..9a6e9cf8a8 100644 --- a/dinky-common/src/main/resources/i18n/messages_en_US.properties +++ b/dinky-common/src/main/resources/i18n/messages_en_US.properties @@ -212,6 +212,8 @@ sys.env.settings.expressionVariable=Expression variable list sys.env.settings.expressionVariable.note= Used to use expression variables in task configuration, use , to separate multiple variables, need to use the fully qualified name of the class, for example: com.dinky.common.utils.DateUtils, please ensure that the class is in the classpath of Dinky sys.env.settings.taskOwnerLockStrategy=Job Responsibility Person Locking Mechanism sys.env.settings.taskOwnerLockStrategy.note=When [OWNER] is selected, only the assigned person in charge of the task can operate and modify it, while other users cannot. When [OWNER_AND_MAINTAINER] is chosen, both the assigned person in charge and the maintainer can operate and modify the task. When [ALL] is selected, anyone can operate and modify the task. The default setting is [ALL] +sys.env.settings.taskOwnerAlertStrategy=Job Responsibility Person Alert Mechanism +sys.env.settings.taskOwnerAlertStrategy.note=When selecting [OWNER], triggering alerts for jobs will additionally alert the responsible person. When selecting [OWNER_AND_MAINTAINER], triggering alerts for jobs will additionally alert the responsible person and the maintainer. When selecting [NONE], triggering alerts for jobs will not additionally alert the responsible person and the maintainer. The default is [NONE] sys.dolphinscheduler.settings.enable=Whether to enable DolphinScheduler sys.dolphinscheduler.settings.enable.note=Whether to enable DolphinScheduler. Only after enabling it can you use the related functions of DolphinScheduler. Please fill in the following configuration items first, and then enable this configuration after completion. Also: Please ensure that the related configurations of DolphinScheduler are correct. sys.dolphinscheduler.settings.url=DolphinScheduler address diff --git a/dinky-common/src/main/resources/i18n/messages_zh_CN.properties b/dinky-common/src/main/resources/i18n/messages_zh_CN.properties index 1004a03c1e..668b63ba73 100644 --- a/dinky-common/src/main/resources/i18n/messages_zh_CN.properties +++ b/dinky-common/src/main/resources/i18n/messages_zh_CN.properties @@ -212,6 +212,8 @@ sys.env.settings.expressionVariable=表达式变量列表 sys.env.settings.expressionVariable.note=用于在任务配置中使用表达式变量,逗号分割,需要使用类的全限定名,例如: com.dinky.common.utils.DateUtils,请确保类在Dinky的classpath中 sys.env.settings.taskOwnerLockStrategy=作业责任人锁机制 sys.env.settings.taskOwnerLockStrategy.note=当选择[OWNER]时,只有作业责任人才能操作作业,其他用户无法操作/修改作业; 当选择[OWNER_AND_MAINTAINER]时,作业责任人和维护人都可以操作/修改作业; 当选择[ALL]时,所有人都可以操作/修改作业; 默认为[ALL] +sys.env.settings.taskOwnerAlertStrategy=作业责任人告警策略 +sys.env.settings.taskOwnerAlertStrategy.note=当选择[OWNER]时,作业触发告警时会额外告警责任人; 当选择[OWNER_AND_MAINTAINER]时,作业触发告警时会额外告警责任人和维护人; 当选择[NONE]时,作业触发告警时不会额外告警责任人和维护人; 默认为[NONE] sys.dolphinscheduler.settings.enable=是否启用 DolphinScheduler sys.dolphinscheduler.settings.enable.note=是否启用 DolphinScheduler ,启用后才能使用 DolphinScheduler 的相关功能,请先填写下列配置项,完成后再开启此项配置, 另:请确保 DolphinScheduler 的相关配置正确 sys.dolphinscheduler.settings.url=DolphinScheduler 地址