feat: 2.5.0 定时任务添加告警通知

This commit is contained in:
zuojunlin 2023-12-03 01:02:03 +08:00 committed by byteblogs168
parent 4d2c1ebfe5
commit 0f8e928bb6
40 changed files with 1807 additions and 27 deletions

View File

@ -0,0 +1,59 @@
package com.aizuda.easy.retry.common.core.enums;
import lombok.Getter;
/**
* 通知场景枚举
*
* @author: zuoJunLin
* @date : 2023-12-02 18:18
*/
@Getter
public enum JobNotifySceneEnum {
JOB_TASK_ERROR(1, "JOB任务执行失败", NodeTypeEnum.SERVER);
/**
* 通知场景
*/
private final int notifyScene;
/**
* 触发通知节点类型
*/
private final NodeTypeEnum nodeType;
/**
* 描述
*/
private final String desc;
JobNotifySceneEnum(int notifyScene, String desc, NodeTypeEnum nodeType) {
this.notifyScene = notifyScene;
this.desc = desc;
this.nodeType = nodeType;
}
/**
* 获取通知场景
*
* @param notifyScene 场景
* @param nodeType 触发通知节点类型
* @return this
*/
public static JobNotifySceneEnum getJobNotifyScene(int notifyScene, NodeTypeEnum nodeType) {
for (JobNotifySceneEnum sceneEnum : JobNotifySceneEnum.values()) {
if (sceneEnum.getNotifyScene() == notifyScene && sceneEnum.nodeType.getType().equals(nodeType.getType())) {
return sceneEnum;
}
}
return null;
}
}

View File

@ -14,6 +14,11 @@ public class JobBatchResponseDO {
private Long id;
/**
* 命名空间
*/
private String namespaceId;
/**
* 组名称
*/
@ -57,12 +62,15 @@ public class JobBatchResponseDO {
/**
* 执行器名称
*/
private String executorName;
private String executorInfo;
private Integer taskType;
private Integer blockStrategy;
private Integer triggerType;
private String argsStr;
}

View File

@ -0,0 +1,21 @@
package com.aizuda.easy.retry.template.datasource.persistence.dataobject;
import lombok.Data;
/**
* @author zuoJunLin
* @date 2023-12-02 23:03:01
* @since 2.4.0
*/
@Data
public class JobNotifyConfigQueryDO {
private String groupName;
private Long jobId;
/**
* 命名空间id
*/
private String namespaceId;
}

View File

@ -0,0 +1,44 @@
package com.aizuda.easy.retry.template.datasource.persistence.dataobject;
import lombok.Data;
import java.time.LocalDateTime;
/**
* @author zuoJunLin
* @date 2023-12-02 23:03:01
* @since 2.4.0
*/
@Data
public class JobNotifyConfigResponseDO {
private Long id;
private String namespaceId;
private String groupName;
private Long jobId;
private String jobName;
private Integer notifyStatus;
private Integer notifyType;
private String notifyAttribute;
private Integer notifyThreshold;
private Integer notifyScene;
private Integer rateLimiterStatus;
private Integer rateLimiterThreshold;
private String description;
private LocalDateTime createDt;
private LocalDateTime updateDt;
}

View File

@ -0,0 +1,19 @@
package com.aizuda.easy.retry.template.datasource.persistence.mapper;
import com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobNotifyConfigQueryDO;
import com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobNotifyConfigResponseDO;
import com.aizuda.easy.retry.template.datasource.persistence.po.JobNotifyConfig;
import com.aizuda.easy.retry.template.datasource.persistence.po.NotifyConfig;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author zuoJunLin
*/
public interface JobNotifyConfigMapper extends BaseMapper<JobNotifyConfig> {
List<JobNotifyConfigResponseDO> selectJobNotifyConfigList(IPage<JobNotifyConfig> iPage, @Param("queryDO") JobNotifyConfigQueryDO queryDO);
}

View File

@ -23,7 +23,9 @@ import java.util.List;
@Mapper
public interface JobTaskBatchMapper extends BaseMapper<JobTaskBatch> {
List<JobBatchResponseDO> selectJobBatchList(IPage<JobTaskBatch> iPage, @Param("queryDO") JobBatchQueryDO queryDO);
List<JobBatchResponseDO> selectJobBatchPageList(IPage<JobTaskBatch> iPage, @Param("queryDO") JobBatchQueryDO queryDO);
List<JobBatchResponseDO> selectJobBatchListByIds( @Param("ids") List<Long> ids);
List<JobBatchSummaryResponseDO> summaryJobBatchList(@Param("from") LocalDateTime todayFrom, @Param("to") LocalDateTime to);
}

View File

@ -0,0 +1,45 @@
package com.aizuda.easy.retry.template.datasource.persistence.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class JobNotifyConfig implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
private String namespaceId;
private String groupName;
private Long jobId;
private Integer notifyStatus;
private Integer notifyType;
private String notifyAttribute;
private Integer notifyThreshold;
private Integer notifyScene;
private Integer rateLimiterStatus;
private Integer rateLimiterThreshold;
private String description;
private LocalDateTime createDt;
private LocalDateTime updateDt;
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aizuda.easy.retry.template.datasource.persistence.mapper.JobNotifyConfigMapper">
<resultMap id="BaseResultMap" type="com.aizuda.easy.retry.template.datasource.persistence.po.JobNotifyConfig">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="namespace_id" jdbcType="VARCHAR" property="namespaceId" />
<result column="group_name" jdbcType="VARCHAR" property="groupName" />
<result column="job_id" jdbcType="BIGINT" property="jobId" />
<result column="notify_status" jdbcType="TINYINT" property="notifyStatus" />
<result column="notify_type" jdbcType="TINYINT" property="notifyType" />
<result column="notify_attribute" jdbcType="VARCHAR" property="notifyAttribute" />
<result column="notify_threshold" jdbcType="TINYINT" property="notifyThreshold" />
<result column="notify_scene" jdbcType="TINYINT" property="notifyScene" />
<result column="rate_limiter_status" jdbcType="TINYINT" property="rateLimiterStatus" />
<result column="rate_limiter_threshold" jdbcType="TINYINT" property="rateLimiterThreshold" />
<result column="description" jdbcType="VARCHAR" property="description" />
<result column="create_dt" jdbcType="TIMESTAMP" property="createDt" />
<result column="update_dt" jdbcType="TIMESTAMP" property="updateDt" />
</resultMap>
<sql id="Base_Column_List">
id,namespace_id, group_name,job_id,notify_status,notify_type, notify_attribute, notify_threshold, notify_scene,rate_limiter_status,rate_limiter_threshold, description,
create_dt, update_dt
</sql>
<select id="selectJobNotifyConfigList"
resultType="com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobNotifyConfigResponseDO">
SELECT a.*, b.job_name
FROM job_notify_config a join job b on a.job_id = b.id
<where>
a.namespace_id = #{queryDO.namespaceId}
<if test="queryDO.jobId != null">
and a.job_id = #{queryDO.jobId}
</if>
<if test="queryDO.groupName != null">
and a.group_name = #{queryDO.groupName}
</if>
order by a.id desc
</where>
</select>
</mapper>

View File

@ -13,7 +13,7 @@
<result column="update_dt" property="updateDt" />
<result column="deleted" property="deleted" />
</resultMap>
<select id="selectJobBatchList"
<select id="selectJobBatchPageList"
parameterType="com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobBatchQueryDO"
resultType="com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobBatchResponseDO">
SELECT a.*, b.job_name, b.task_type, b.block_strategy, b.trigger_type
@ -37,4 +37,33 @@
</where>
</select>
<select id="summaryJobBatchList"
resultType="com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobBatchSummaryResponseDO">
SELECT namespace_id AS namespaceId,
job_id AS jobId,
group_name AS groupName,
task_batch_status AS taskBatchStatus,
operation_reason AS operationReason,
COUNT(operation_reason) AS operationReasonTotal,
SUM(CASE WHEN (task_batch_status = 3) THEN 1 ELSE 0 END) AS successNum,
SUM(CASE WHEN (operation_reason IN (2, 3)) THEN 1 ELSE 0 END) AS cancelNum,
SUM(CASE WHEN (operation_reason IN (1, 5, 8)) THEN 1 ELSE 0 END) AS stopNum,
SUM(CASE WHEN (operation_reason IN (4, 6, 7)) THEN 1 ELSE 0 END) AS failNum
FROM job_task_batch
WHERE create_dt BETWEEN #{from} AND #{to}
GROUP BY namespace_id, group_name, job_id, task_batch_status, operation_reason
</select>
<select id="selectJobBatchListByIds"
resultType="com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobBatchResponseDO">
SELECT a.*, b.job_name, b.task_type, b.block_strategy, b.trigger_type,b.executor_info,b.args_str
FROM job_task_batch a join job b on a.job_id = b.id
<where>
a.id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
and a.deleted = 0
</where>
</select>
</mapper>

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aizuda.easy.retry.template.datasource.persistence.mapper.JobNotifyConfigMapper">
<resultMap id="BaseResultMap" type="com.aizuda.easy.retry.template.datasource.persistence.po.JobNotifyConfig">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="namespace_id" jdbcType="VARCHAR" property="namespaceId" />
<result column="group_name" jdbcType="VARCHAR" property="groupName" />
<result column="job_id" jdbcType="BIGINT" property="jobId" />
<result column="notify_status" jdbcType="TINYINT" property="notifyStatus" />
<result column="notify_type" jdbcType="TINYINT" property="notifyType" />
<result column="notify_attribute" jdbcType="VARCHAR" property="notifyAttribute" />
<result column="notify_threshold" jdbcType="TINYINT" property="notifyThreshold" />
<result column="notify_scene" jdbcType="TINYINT" property="notifyScene" />
<result column="rate_limiter_status" jdbcType="TINYINT" property="rateLimiterStatus" />
<result column="rate_limiter_threshold" jdbcType="TINYINT" property="rateLimiterThreshold" />
<result column="description" jdbcType="VARCHAR" property="description" />
<result column="create_dt" jdbcType="TIMESTAMP" property="createDt" />
<result column="update_dt" jdbcType="TIMESTAMP" property="updateDt" />
</resultMap>
<sql id="Base_Column_List">
id,namespace_id, group_name,job_id,notify_status,notify_type, notify_attribute, notify_threshold, notify_scene,rate_limiter_status,rate_limiter_threshold, description,
create_dt, update_dt
</sql>
<select id="selectJobNotifyConfigList"
resultType="com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobNotifyConfigResponseDO">
SELECT a.*, b.job_name
FROM job_notify_config a join job b on a.job_id = b.id
<where>
a.namespace_id = #{queryDO.namespaceId}
<if test="queryDO.jobId != null">
and a.job_id = #{queryDO.jobId}
</if>
<if test="queryDO.groupName != null">
and a.group_name = #{queryDO.groupName}
</if>
order by a.id desc
</where>
</select>
</mapper>

View File

@ -13,7 +13,7 @@
<result column="update_dt" property="updateDt" />
<result column="deleted" property="deleted" />
</resultMap>
<select id="selectJobBatchList"
<select id="selectJobBatchPageList"
parameterType="com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobBatchQueryDO"
resultType="com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobBatchResponseDO">
SELECT a.*, b.job_name, b.task_type, b.block_strategy, b.trigger_type
@ -55,4 +55,17 @@
GROUP BY namespace_id, group_name, job_id, task_batch_status, operation_reason
</select>
<select id="selectJobBatchListByIds"
resultType="com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobBatchResponseDO">
SELECT a.*, b.job_name, b.task_type, b.block_strategy, b.trigger_type,b.executor_info,b.args_str
FROM job_task_batch a join job b on a.job_id = b.id
<where>
a.id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
and a.deleted = 0
</where>
</select>
</mapper>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.aizuda.easy.retry.template.datasource.persistence.mapper.JobNotifyConfigMapper">
<resultMap id="BaseResultMap" type="com.aizuda.easy.retry.template.datasource.persistence.po.JobNotifyConfig">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="namespace_id" jdbcType="VARCHAR" property="namespaceId" />
<result column="group_name" jdbcType="VARCHAR" property="groupName" />
<result column="job_id" jdbcType="BIGINT" property="jobId" />
<result column="notify_status" jdbcType="TINYINT" property="notifyStatus" />
<result column="notify_type" jdbcType="TINYINT" property="notifyType" />
<result column="notify_attribute" jdbcType="VARCHAR" property="notifyAttribute" />
<result column="notify_threshold" jdbcType="TINYINT" property="notifyThreshold" />
<result column="notify_scene" jdbcType="TINYINT" property="notifyScene" />
<result column="rate_limiter_status" jdbcType="TINYINT" property="rateLimiterStatus" />
<result column="rate_limiter_threshold" jdbcType="TINYINT" property="rateLimiterThreshold" />
<result column="description" jdbcType="VARCHAR" property="description" />
<result column="create_dt" jdbcType="TIMESTAMP" property="createDt" />
<result column="update_dt" jdbcType="TIMESTAMP" property="updateDt" />
</resultMap>
<sql id="Base_Column_List">
id,namespace_id, group_name,job_id,notify_status,notify_type, notify_attribute, notify_threshold, notify_scene,rate_limiter_status,rate_limiter_threshold, description,
create_dt, update_dt
</sql>
<select id="selectJobNotifyConfigList"
resultType="com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobNotifyConfigResponseDO">
SELECT a.*, b.job_name
FROM job_notify_config a join job b on a.job_id = b.id
<where>
a.namespace_id = #{queryDO.namespaceId}
<if test="queryDO.jobId != null">
and a.job_id = #{queryDO.jobId}
</if>
<if test="queryDO.groupName != null">
and a.group_name = #{queryDO.groupName}
</if>
order by a.id desc
</where>
</select>
</mapper>

View File

@ -13,7 +13,7 @@
<result column="update_dt" property="updateDt" />
<result column="deleted" property="deleted" />
</resultMap>
<select id="selectJobBatchList"
<select id="selectJobBatchPageList"
parameterType="com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobBatchQueryDO"
resultType="com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobBatchResponseDO">
SELECT a.*, b.job_name, b.task_type, b.block_strategy, b.trigger_type
@ -35,6 +35,34 @@
and a.deleted = 0
order by a.id desc
</where>
</select>
<select id="summaryJobBatchList"
resultType="com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobBatchSummaryResponseDO">
SELECT namespace_id AS namespaceId,
job_id AS jobId,
group_name AS groupName,
task_batch_status AS taskBatchStatus,
operation_reason AS operationReason,
COUNT(operation_reason) AS operationReasonTotal,
SUM(CASE WHEN (task_batch_status = 3) THEN 1 ELSE 0 END) AS successNum,
SUM(CASE WHEN (operation_reason IN (2, 3)) THEN 1 ELSE 0 END) AS cancelNum,
SUM(CASE WHEN (operation_reason IN (1, 5, 8)) THEN 1 ELSE 0 END) AS stopNum,
SUM(CASE WHEN (operation_reason IN (4, 6, 7)) THEN 1 ELSE 0 END) AS failNum
FROM job_task_batch
WHERE create_dt BETWEEN #{from} AND #{to}
GROUP BY namespace_id, group_name, job_id, task_batch_status, operation_reason
</select>
<select id="selectJobBatchListByIds"
resultType="com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobBatchResponseDO">
SELECT a.*, b.job_name, b.task_type, b.block_strategy, b.trigger_type,b.executor_info,b.args_str
FROM job_task_batch a join job b on a.job_id = b.id
<where>
a.id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
and a.deleted = 0
</where>
</select>
</mapper>

View File

@ -18,10 +18,12 @@ import com.aizuda.easy.retry.server.job.task.dto.TaskExecuteDTO;
import com.aizuda.easy.retry.server.job.task.support.JobExecutor;
import com.aizuda.easy.retry.server.job.task.support.JobTaskConverter;
import com.aizuda.easy.retry.server.job.task.support.cache.ResidentTaskCache;
import com.aizuda.easy.retry.server.job.task.support.event.JobTaskFailAlarmEvent;
import com.aizuda.easy.retry.server.job.task.support.executor.JobExecutorContext;
import com.aizuda.easy.retry.server.job.task.support.executor.JobExecutorFactory;
import com.aizuda.easy.retry.server.job.task.support.timer.JobTimerWheel;
import com.aizuda.easy.retry.server.job.task.support.timer.ResidentJobTimerTask;
import com.aizuda.easy.retry.server.retry.task.support.event.RetryTaskFailMoreThresholdAlarmEvent;
import com.aizuda.easy.retry.template.datasource.persistence.mapper.JobMapper;
import com.aizuda.easy.retry.template.datasource.persistence.mapper.JobTaskBatchMapper;
import com.aizuda.easy.retry.template.datasource.persistence.po.Job;
@ -30,6 +32,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
@ -58,6 +61,8 @@ public class JobExecutorActor extends AbstractActor {
private JobTaskBatchMapper jobTaskBatchMapper;
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private ApplicationContext context;
@Override
public Receive createReceive() {
@ -74,7 +79,7 @@ public class JobExecutorActor extends AbstractActor {
} catch (Exception e) {
LogUtils.error(log, "job executor exception. [{}]", taskExecute, e);
handlerTaskBatch(taskExecute, JobTaskBatchStatusEnum.FAIL.getStatus(), JobOperationReasonEnum.TASK_EXECUTE_ERROR.getReason());
// TODO 告警通知
context.publishEvent(new JobTaskFailAlarmEvent(taskExecute.getTaskBatchId()));
} finally {
getContext().stop(getSelf());
}

View File

@ -0,0 +1,22 @@
package com.aizuda.easy.retry.server.job.task.support.event;
import org.springframework.context.ApplicationEvent;
/**
* job任务失败事件
* @author: zuoJunLin
* @date : 2023-12-02 21:40
* @since 2.5.0
*/
public class JobTaskFailAlarmEvent extends ApplicationEvent {
private Long jobTaskBatchId;
public JobTaskFailAlarmEvent(Long jobTaskBatchId) {
super(jobTaskBatchId);
this.jobTaskBatchId=jobTaskBatchId;
}
public Long getJobTaskBatchId() {
return jobTaskBatchId;
}
}

View File

@ -0,0 +1,178 @@
package com.aizuda.easy.retry.server.job.task.support.listener;
import com.aizuda.easy.retry.common.core.alarm.Alarm;
import com.aizuda.easy.retry.common.core.alarm.AlarmContext;
import com.aizuda.easy.retry.common.core.alarm.EasyRetryAlarmFactory;
import com.aizuda.easy.retry.common.core.enums.JobNotifySceneEnum;
import com.aizuda.easy.retry.common.core.enums.StatusEnum;
import com.aizuda.easy.retry.common.core.log.LogUtils;
import com.aizuda.easy.retry.common.core.util.EnvironmentUtils;
import com.aizuda.easy.retry.common.core.util.HostUtils;
import com.aizuda.easy.retry.server.common.Lifecycle;
import com.aizuda.easy.retry.server.common.triple.ImmutableTriple;
import com.aizuda.easy.retry.server.common.triple.Triple;
import com.aizuda.easy.retry.server.common.util.DateUtils;
import com.aizuda.easy.retry.server.job.task.support.event.JobTaskFailAlarmEvent;
import com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobBatchResponseDO;
import com.aizuda.easy.retry.template.datasource.persistence.mapper.JobNotifyConfigMapper;
import com.aizuda.easy.retry.template.datasource.persistence.mapper.JobTaskBatchMapper;
import com.aizuda.easy.retry.template.datasource.persistence.po.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.stream.Collectors;
/**
* JOB任务执行失败告警
*
* @author: zuoJunLin
* @date : 2023-12-02 21:40
* @since 2.5.0
*/
@Component
@Slf4j
public class JobTaskFailAlarmListener implements ApplicationListener<JobTaskFailAlarmEvent>, Runnable, Lifecycle {
@Autowired
private JobNotifyConfigMapper jobNotifyConfigMapper;
@Autowired
private JobTaskBatchMapper jobTaskBatchMapper;
@Autowired
private EasyRetryAlarmFactory easyRetryAlarmFactory;
/**
* job任务失败数据
*/
private LinkedBlockingQueue<Long> queue = new LinkedBlockingQueue<>(1000);
private static String jobTaskFailTextMessagesFormatter =
"<font face=\"微软雅黑\" color=#ff0000 size=4>{}环境 Job任务执行失败</font> \n" +
"> 组名称:{} \n" +
"> 任务名称:{} \n" +
"> 执行器名称:{} \n" +
"> 方法参数:{} \n" +
"> 时间:{} \n";
private Thread thread;
@Override
public void start() {
thread = new Thread(this);
thread.start();
}
@Override
public void close() {
if (Objects.nonNull(thread)) {
thread.interrupt();
}
}
@Override
public void run() {
LogUtils.info(log, "JobTaskFailAlarmListener time[{}] ip:[{}]", LocalDateTime.now(), HostUtils.getIp());
while (!Thread.currentThread().isInterrupted()) {
try {
// 无数据时阻塞线程
Long jobTaskBatchId = queue.take();
// 拉取200条
List<Long> jobTaskBatchIds = Lists.newArrayList(jobTaskBatchId);
queue.drainTo(jobTaskBatchIds, 200);
List<JobBatchResponseDO> jobTaskBatchList = jobTaskBatchMapper.selectJobBatchListByIds(jobTaskBatchIds);
Set<String> namespaceIds = new HashSet<>();
Set<String> groupNames = new HashSet<>();
Set<Long> jobIds = new HashSet<>();
Map<Triple<String, String, Long>, List<JobBatchResponseDO>> jobTaskBatchMap = getJobTaskBatchMap(
jobTaskBatchList, namespaceIds, groupNames, jobIds);
Map<Triple<String, String, Long>, List<JobNotifyConfig>> jobNotifyConfigMap = getJobNotifyConfigMap(
namespaceIds, groupNames, jobIds);
if (jobNotifyConfigMap == null) {
continue;
}
// 循环发送消息
jobTaskBatchMap.forEach((key, list) -> {
List<JobNotifyConfig> jobNotifyConfigsList = jobNotifyConfigMap.get(key);
for (JobBatchResponseDO JobBatch : list) {
for (final JobNotifyConfig jobNotifyConfig : jobNotifyConfigsList) {
// 预警
AlarmContext context = AlarmContext.build()
.text(jobTaskFailTextMessagesFormatter,
EnvironmentUtils.getActiveProfile(),
JobBatch.getGroupName(),
JobBatch.getJobName(),
JobBatch.getExecutorInfo(),
JobBatch.getArgsStr(),
DateUtils.toNowFormat(DateUtils.NORM_DATETIME_PATTERN))
.title("{}环境 JOB任务失败", EnvironmentUtils.getActiveProfile())
.notifyAttribute(jobNotifyConfig.getNotifyAttribute());
Alarm<AlarmContext> alarmType = easyRetryAlarmFactory.getAlarmType(jobNotifyConfig.getNotifyType());
alarmType.asyncSendMessage(context);
}
}
});
} catch (InterruptedException e) {
LogUtils.info(log, "job task fail more alarm stop");
Thread.currentThread().interrupt();
} catch (Exception e) {
LogUtils.error(log, "JobTaskFailAlarmListener queue take Exception", e);
}
}
}
private Map<Triple<String, String, Long>, List<JobNotifyConfig>> getJobNotifyConfigMap(Set<String> namespaceIds, Set<String> groupNames, Set<Long> jobIds) {
// 批量获取所需的通知配置
List<JobNotifyConfig> jobNotifyConfigs = jobNotifyConfigMapper.selectList(
new LambdaQueryWrapper<JobNotifyConfig>()
.eq(JobNotifyConfig::getNotifyStatus, StatusEnum.YES)
.eq(JobNotifyConfig::getNotifyScene, JobNotifySceneEnum.JOB_TASK_ERROR.getNotifyScene())
.in(JobNotifyConfig::getNamespaceId, namespaceIds)
.in(JobNotifyConfig::getGroupName, groupNames)
.in(JobNotifyConfig::getJobId, jobIds)
);
if (CollectionUtils.isEmpty(jobNotifyConfigs)) {
return null;
}
return jobNotifyConfigs.stream()
.collect(Collectors.groupingBy(i -> {
String namespaceId = i.getNamespaceId();
String groupName = i.getGroupName();
Long jobId = i.getJobId();
return ImmutableTriple.of(namespaceId, groupName, jobId);
}));
}
private Map<Triple<String, String, Long>, List<JobBatchResponseDO>> getJobTaskBatchMap(List<JobBatchResponseDO> jobTaskBatchList, Set<String> namespaceIds, Set<String> groupNames, Set<Long> jobIds) {
return jobTaskBatchList.stream().collect(Collectors.groupingBy(i -> {
String namespaceId = i.getNamespaceId();
String groupName = i.getGroupName();
Long jobId = i.getJobId();
namespaceIds.add(namespaceId);
groupNames.add(groupName);
jobIds.add(jobId);
return ImmutableTriple.of(namespaceId, groupName, jobId);
}));
}
@Override
public void onApplicationEvent(JobTaskFailAlarmEvent event) {
if (queue.offer(event.getJobTaskBatchId())) {
LogUtils.warn(log, "JOB任务执行失败告警队列已满");
}
}
}

View File

@ -27,12 +27,18 @@ public class JobController {
@Autowired
private JobService jobService;
@GetMapping("/list")
@GetMapping("/page/list")
@LoginRequired
public PageResult<List<JobResponseVO>> getJobPage(JobQueryVO jobQueryVO) {
return jobService.getJobPage(jobQueryVO);
}
@GetMapping("/list")
@LoginRequired
public List<JobResponseVO> getJobList(@RequestParam("groupName") String groupName) {
return jobService.getJobList(groupName);
}
@GetMapping("{id}")
@LoginRequired
public JobResponseVO getJobDetail(@PathVariable("id") Long id) {

View File

@ -0,0 +1,53 @@
package com.aizuda.easy.retry.server.web.controller;
import com.aizuda.easy.retry.server.web.annotation.LoginRequired;
import com.aizuda.easy.retry.server.web.model.base.PageResult;
import com.aizuda.easy.retry.server.web.model.request.JobNotifyConfigQueryVO;
import com.aizuda.easy.retry.server.web.model.request.JobNotifyConfigRequestVO;
import com.aizuda.easy.retry.server.web.model.response.JobNotifyConfigResponseVO;
import com.aizuda.easy.retry.server.web.service.JobNotifyConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* Job通知配置接口
*
* @author: zuoJunLin
* @date : 2023-12-02 11:32
* @since 2.5.0
*/
@RestController
@RequestMapping("/job/notify/config")
public class JobNotifyConfigController {
@Autowired
private JobNotifyConfigService jobNotifyConfigService;
@LoginRequired
@GetMapping("/page/list")
public PageResult<List<JobNotifyConfigResponseVO>> getNotifyConfigList(JobNotifyConfigQueryVO queryVO) {
return jobNotifyConfigService.getJobNotifyConfigList(queryVO);
}
@LoginRequired
@GetMapping("{id}")
public JobNotifyConfigResponseVO getJobNotifyConfigDetail(@PathVariable("id") Long id) {
return jobNotifyConfigService.getJobNotifyConfigDetail(id);
}
@LoginRequired
@PostMapping
public Boolean saveNotify(@RequestBody @Validated JobNotifyConfigRequestVO requestVO) {
return jobNotifyConfigService.saveJobNotify(requestVO);
}
@LoginRequired
@PutMapping
public Boolean updateNotify(@RequestBody @Validated JobNotifyConfigRequestVO requestVO) {
return jobNotifyConfigService.updateJobNotify(requestVO);
}
}

View File

@ -0,0 +1,17 @@
package com.aizuda.easy.retry.server.web.model.request;
import com.aizuda.easy.retry.server.web.model.base.BaseQueryVO;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author zuoJunLin
* @date 2023-12-02 11:16:14
* @since 2.5.0
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class JobNotifyConfigQueryVO extends BaseQueryVO {
private String groupName;
private Long jobId;
}

View File

@ -0,0 +1,54 @@
package com.aizuda.easy.retry.server.web.model.request;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
/**
* @author zuoJunLin
* @date 2023-11-02 11:40:10
* @since 2.5.0
*/
@Data
public class JobNotifyConfigRequestVO {
private Long id;
@NotBlank(message = "组名称 不能为空")
@Pattern(regexp = "^[A-Za-z0-9_]{1,64}$", message = "仅支持长度为1~64字符且类型为数字、字母和下划线")
private String groupName;
@NotNull(message = "任务不能为空")
private Long jobId;
@NotNull(message = "通知状态不能为空")
private Integer notifyStatus;
@NotNull(message = "通知类型不能为空")
private Integer notifyType;
@NotBlank(message = "通知属性不能为空")
private String notifyAttribute;
private Integer notifyThreshold;
@NotNull(message = "通知场景不能为空")
private Integer notifyScene;
@NotNull(message = "限流状态不能为空")
private Integer rateLimiterStatus;
private Integer rateLimiterThreshold;
/**
* 描述
*/
private String description;
/**
* 是否删除
*/
private Integer isDeleted;
}

View File

@ -0,0 +1,51 @@
package com.aizuda.easy.retry.server.web.model.response;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* @author: zuoJunLin
* @date : 2023-12-02 11:22
* @since : 2.5.0
*/
@Data
public class JobNotifyConfigResponseVO implements Serializable {
private Long id;
private String namespaceId;
private String groupName;
private Long jobId;
private String jobName;
private Integer notifyStatus;
private String notifyName;
private Integer notifyType;
private String notifyAttribute;
private Integer notifyThreshold;
private Integer notifyScene;
private Integer rateLimiterStatus;
private Integer rateLimiterThreshold;
private String description;
private LocalDateTime createDt;
private LocalDateTime updateDt;
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,27 @@
package com.aizuda.easy.retry.server.web.service;
import com.aizuda.easy.retry.server.web.model.base.PageResult;
import com.aizuda.easy.retry.server.web.model.request.JobNotifyConfigQueryVO;
import com.aizuda.easy.retry.server.web.model.request.JobNotifyConfigRequestVO;
import com.aizuda.easy.retry.server.web.model.request.NotifyConfigQueryVO;
import com.aizuda.easy.retry.server.web.model.request.NotifyConfigRequestVO;
import com.aizuda.easy.retry.server.web.model.response.JobNotifyConfigResponseVO;
import com.aizuda.easy.retry.server.web.model.response.NotifyConfigResponseVO;
import java.util.List;
/**
* @author: zuoJunLin
* @date : 2023-12-02 12:54
* @since 2.5.0
*/
public interface JobNotifyConfigService {
PageResult<List<JobNotifyConfigResponseVO>> getJobNotifyConfigList(JobNotifyConfigQueryVO queryVO);
Boolean saveJobNotify(JobNotifyConfigRequestVO requestVO);
Boolean updateJobNotify(JobNotifyConfigRequestVO requestVO);
JobNotifyConfigResponseVO getJobNotifyConfigDetail(Long id);
}

View File

@ -35,4 +35,5 @@ public interface JobService {
boolean trigger(Long jobId);
List<JobResponseVO> getJobList(String groupName);
}

View File

@ -0,0 +1,18 @@
package com.aizuda.easy.retry.server.web.service.convert;
import com.aizuda.easy.retry.server.web.model.request.JobNotifyConfigRequestVO;
import com.aizuda.easy.retry.template.datasource.persistence.po.JobNotifyConfig;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* @author: zuoJunLin
* @date : 2023-12-02 13:43
*/
@Mapper
public interface JobNotifyConfigConverter {
JobNotifyConfigConverter INSTANCE = Mappers.getMapper(JobNotifyConfigConverter.class);
JobNotifyConfig toJobNotifyConfig(JobNotifyConfigRequestVO jobNotifyConfigVO);
}

View File

@ -0,0 +1,23 @@
package com.aizuda.easy.retry.server.web.service.convert;
import com.aizuda.easy.retry.server.web.model.response.JobNotifyConfigResponseVO;
import com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobNotifyConfigResponseDO;
import com.aizuda.easy.retry.template.datasource.persistence.po.JobNotifyConfig;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* @author: www.byteblogs.com
* @date : 2022-03-03 11:20
*/
@Mapper
public interface JobNotifyConfigResponseVOConverter {
JobNotifyConfigResponseVOConverter INSTANCE = Mappers.getMapper(JobNotifyConfigResponseVOConverter.class);
JobNotifyConfigResponseVO convert(JobNotifyConfig jobNotifyConfig);
List<JobNotifyConfigResponseVO> batchConvert(List<JobNotifyConfigResponseDO> jobNotifyConfigs);
}

View File

@ -60,7 +60,7 @@ public class JobBatchServiceImpl implements JobBatchService {
jobBatchQueryDO.setTaskBatchStatus(queryVO.getTaskBatchStatus());
jobBatchQueryDO.setGroupName(queryVO.getGroupName());
jobBatchQueryDO.setNamespaceId(UserSessionUtils.currentUserSession().getNamespaceId());
List<JobBatchResponseDO> batchResponseDOList = jobTaskBatchMapper.selectJobBatchList(pageDTO, jobBatchQueryDO);
List<JobBatchResponseDO> batchResponseDOList = jobTaskBatchMapper.selectJobBatchPageList(pageDTO, jobBatchQueryDO);
List<JobBatchResponseVO> batchResponseVOList = JobBatchResponseVOConverter.INSTANCE.toJobBatchResponseVOs(
batchResponseDOList);

View File

@ -0,0 +1,83 @@
package com.aizuda.easy.retry.server.web.service.impl;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.aizuda.easy.retry.common.core.util.JsonUtil;
import com.aizuda.easy.retry.server.common.exception.EasyRetryServerException;
import com.aizuda.easy.retry.server.web.model.base.PageResult;
import com.aizuda.easy.retry.server.web.model.request.JobNotifyConfigQueryVO;
import com.aizuda.easy.retry.server.web.model.request.JobNotifyConfigRequestVO;
import com.aizuda.easy.retry.server.web.model.response.JobNotifyConfigResponseVO;
import com.aizuda.easy.retry.server.web.service.JobNotifyConfigService;
import com.aizuda.easy.retry.server.web.service.convert.JobNotifyConfigConverter;
import com.aizuda.easy.retry.server.web.service.convert.JobNotifyConfigResponseVOConverter;
import com.aizuda.easy.retry.server.web.util.UserSessionUtils;
import com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobBatchQueryDO;
import com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobBatchResponseDO;
import com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobNotifyConfigQueryDO;
import com.aizuda.easy.retry.template.datasource.persistence.dataobject.JobNotifyConfigResponseDO;
import com.aizuda.easy.retry.template.datasource.persistence.mapper.JobNotifyConfigMapper;
import com.aizuda.easy.retry.template.datasource.persistence.po.JobNotifyConfig;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
/**
* @author: zuoJunLin
* @date : 2023-12-02 12:54
* @since 2.5.0
*/
@Service
public class JobNotifyConfigServiceImpl implements JobNotifyConfigService {
@Autowired
private JobNotifyConfigMapper jobNotifyConfigMapper;
@Override
public PageResult<List<JobNotifyConfigResponseVO>> getJobNotifyConfigList(JobNotifyConfigQueryVO queryVO) {
PageDTO<JobNotifyConfig> pageDTO = new PageDTO<>();
JobNotifyConfigQueryDO jobNotifyConfigQueryDO = new JobNotifyConfigQueryDO();
jobNotifyConfigQueryDO.setNamespaceId(UserSessionUtils.currentUserSession().getNamespaceId());
if (StrUtil.isNotBlank(queryVO.getGroupName())) {
jobNotifyConfigQueryDO.setGroupName(queryVO.getGroupName());
}
if (Objects.nonNull(queryVO.getJobId())) {
jobNotifyConfigQueryDO.setJobId(queryVO.getJobId());
}
List<JobNotifyConfigResponseDO> batchResponseDOList = jobNotifyConfigMapper.selectJobNotifyConfigList(pageDTO, jobNotifyConfigQueryDO);
return new PageResult<>(pageDTO, JobNotifyConfigResponseVOConverter.INSTANCE.batchConvert(batchResponseDOList));
}
@Override
public Boolean saveJobNotify(JobNotifyConfigRequestVO requestVO) {
JobNotifyConfig jobNotifyConfig = JobNotifyConfigConverter.INSTANCE.toJobNotifyConfig(requestVO);
jobNotifyConfig.setCreateDt(LocalDateTime.now());
jobNotifyConfig.setNamespaceId(UserSessionUtils.currentUserSession().getNamespaceId());
Assert.isTrue(1 == jobNotifyConfigMapper.insert(jobNotifyConfig),
() -> new EasyRetryServerException("failed to insert jobNotifyConfig. sceneConfig:[{}]", JsonUtil.toJsonString(jobNotifyConfig)));
return Boolean.TRUE;
}
@Override
public Boolean updateJobNotify(JobNotifyConfigRequestVO requestVO) {
Assert.notNull(requestVO.getId(), () -> new EasyRetryServerException("参数异常"));
JobNotifyConfig jobNotifyConfig = JobNotifyConfigConverter.INSTANCE.toJobNotifyConfig(requestVO);
// 防止被覆盖
jobNotifyConfig.setNamespaceId(null);
Assert.isTrue(1 == jobNotifyConfigMapper.updateById(jobNotifyConfig),
() -> new EasyRetryServerException("failed to update jobNotifyConfig. sceneConfig:[{}]", JsonUtil.toJsonString(jobNotifyConfig)));
return Boolean.TRUE;
}
@Override
public JobNotifyConfigResponseVO getJobNotifyConfigDetail(Long id) {
JobNotifyConfig jobNotifyConfig = jobNotifyConfigMapper.selectOne(new LambdaQueryWrapper<JobNotifyConfig>()
.eq(JobNotifyConfig::getId, id));
return JobNotifyConfigResponseVOConverter.INSTANCE.convert(jobNotifyConfig);
}
}

View File

@ -22,9 +22,11 @@ import com.aizuda.easy.retry.server.web.model.response.JobResponseVO;
import com.aizuda.easy.retry.server.web.service.JobService;
import com.aizuda.easy.retry.server.web.service.convert.JobConverter;
import com.aizuda.easy.retry.server.web.service.convert.JobResponseVOConverter;
import com.aizuda.easy.retry.server.web.service.convert.SceneConfigResponseVOConverter;
import com.aizuda.easy.retry.server.web.util.UserSessionUtils;
import com.aizuda.easy.retry.template.datasource.persistence.mapper.JobMapper;
import com.aizuda.easy.retry.template.datasource.persistence.po.Job;
import com.aizuda.easy.retry.template.datasource.persistence.po.SceneConfig;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import lombok.extern.slf4j.Slf4j;
@ -216,4 +218,16 @@ public class JobServiceImpl implements JobService {
return Boolean.TRUE;
}
}
@Override
public List<JobResponseVO> getJobList(String groupName) {
String namespaceId = UserSessionUtils.currentUserSession().getNamespaceId();
List<Job> jobs = jobMapper.selectList(new LambdaQueryWrapper<Job>()
.select(Job::getId, Job::getJobName)
.eq(Job::getNamespaceId, namespaceId)
.eq(Job::getGroupName, groupName)
.orderByDesc(Job::getCreateDt));
List<JobResponseVO> jobResponseList = JobResponseVOConverter.INSTANCE.toJobResponseVOs(jobs);
return jobResponseList;
}
}

View File

@ -1,6 +1,8 @@
import request from '@/utils/request'
import retryApi from "@/api/retryApi";
const jobApi = {
// 任务信息
jobPageList: '/job/page/list',
jobList: '/job/list',
jobDetail: '/job/',
saveJob: '/job/',
@ -16,6 +18,13 @@ const jobApi = {
jobBatchDetail: '/job/batch/',
stop: '/job/batch/stop/',
//通知
jobNotifyConfigPageList: '/job/notify/config/page/list',
jobNotifyConfigDetail: '/job/notify/config/',
saveJobNotify: '/job/notify/config/',
updateJobNotify: '/job/notify/config/',
// 任务
jobTaskList: '/job/task/list',
@ -101,6 +110,14 @@ export function jobBatchDetail (id) {
})
}
export function getJobPageList (parameter) {
return request({
url: jobApi.jobPageList,
method: 'get',
params: parameter
})
}
export function getJobList (parameter) {
return request({
url: jobApi.jobList,
@ -131,3 +148,33 @@ export function updateJob (data) {
data
})
}
export function jobNotifyConfigPageList (parameter) {
return request({
url: jobApi.jobNotifyConfigPageList,
method: 'get',
params: parameter
})
}
export function getJobNotifyConfigDetail (id) {
return request({
url: jobApi.jobNotifyConfigDetail + id,
method: 'get'
})
}
export function saveJobNotify (data) {
return request({
url: jobApi.saveJobNotify,
method: 'post',
data
})
}
export function updateJobNotify (data) {
return request({
url: jobApi.updateJobNotify,
method: 'put',
data
})
}

View File

@ -130,9 +130,9 @@ export const asyncRouterMap = [
},
{
path: '/retry/scene/config',
name: 'SceneFrom',
name: 'SceneForm',
hidden: true,
component: () => import('@/views/task/form/SceneFrom'),
component: () => import('@/views/task/form/SceneForm.vue'),
meta: { title: '场景配置', icon: 'profile', keepAlive: true, permission: ['retryTask'] }
},
{
@ -143,9 +143,9 @@ export const asyncRouterMap = [
},
{
path: '/retry/notify/config',
name: 'NotifyFrom',
name: 'NotifyForm',
hidden: true,
component: () => import('@/views/task/form/NotifyFrom'),
component: () => import('@/views/task/form/NotifyForm.vue'),
meta: { title: '通知配置', icon: 'profile', keepAlive: true, permission: ['retryTask'] }
}
]
@ -172,9 +172,9 @@ export const asyncRouterMap = [
},
{
path: '/job/config',
name: 'JobFrom',
name: 'JobForm',
hidden: true,
component: () => import('@/views/job/from/JobFrom'),
component: () => import('@/views/job/form/JobForm.vue'),
meta: { title: '任务配置', icon: 'profile', permission: ['job'] }
},
{
@ -196,6 +196,19 @@ export const asyncRouterMap = [
hidden: true,
component: () => import('@/views/job/JobTaskList'),
meta: { title: '任务项', icon: 'profile', permission: ['jobBatch'] }
},
{
path: '/job/notify/list',
name: 'JobNotifyList',
component: () => import('@/views/job/JobNotifyList'),
meta: { title: '通知列表', icon: 'profile', keepAlive: true, permission: ['jobNotify'] }
},
{
path: '/job/notify/config',
name: 'JobNotifyForm',
hidden: true,
component: () => import('@/views/job/form/JobNotifyForm.vue'),
meta: { title: '通知配置', icon: 'profile', keepAlive: true, permission: ['jobNotify'] }
}
]
},

View File

@ -151,7 +151,45 @@ const enums = {
'name': '停止',
'color': '#ac2df5'
}
}
},
notifyScene: {
'1': {
'name': '任务执行失败',
'color': '#d06892'
}
},
notifyType: {
'1': {
'name': '钉钉通知',
'color': '#64a6ea'
},
'2': {
'name': '邮箱通知',
'color': '#1b7ee5'
},
'4': {
'name': '飞书',
'color': '#087da1'
}
}, notifyStatus: {
'0': {
'name': '停用',
'color': '#9c1f1f'
},
'1': {
'name': '启用',
'color': '#f5a22d'
}
}, rateLimiterStatus: {
'0': {
'name': '未启用',
'color': '#9c1f1f'
},
'1': {
'name': '启用',
'color': '#f5a22d'
}
},
}
module.exports = enums

View File

@ -111,6 +111,10 @@ const jobAdmin = [
roleId: 1,
permissionId: 'jobBatch',
permissionName: '任务批次'
},{
roleId: 1,
permissionId: 'jobNotify',
permissionName: '任务通知'
}
]
@ -171,6 +175,11 @@ const jobUser = [
roleId: 1,
permissionId: 'jobBatch',
permissionName: '任务批次'
},
{
roleId: 1,
permissionId: 'jobNotify',
permissionName: '任务通知'
}
]

View File

@ -55,11 +55,11 @@ const enums = {
'color': '#a127f3'
},
'5': {
'name': '任务重试数量超过阈值',
'name': '任务重试失败数量超过阈值',
'color': '#f5a22d'
},
'6': {
'name': '任务失败进入死信队列',
'name': '任务重试失败进入死信队列',
'color': '#f5a22d'
}
},

View File

@ -178,7 +178,7 @@
import ATextarea from 'ant-design-vue/es/input/TextArea'
import AInput from 'ant-design-vue/es/input/Input'
import { STable, Drawer } from '@/components'
import { delJob, getJobList, triggerJob, updateJobStatus } from '@/api/jobApi'
import { delJob, getJobPageList, triggerJob, updateJobStatus } from '@/api/jobApi'
import { getAllGroupNameList } from '@/api/manage'
import enums from '@/utils/jobEnum'
import JobInfo from '@/views/job/JobInfo'
@ -278,7 +278,7 @@ export default {
],
// Promise
loadData: (parameter) => {
return getJobList(Object.assign(parameter, this.queryParam)).then((res) => {
return getJobPageList(Object.assign(parameter, this.queryParam)).then((res) => {
return res
})
},

View File

@ -0,0 +1,247 @@
<template>
<div>
<a-card :bordered="false">
<div class="table-page-search-wrapper">
<a-form layout="inline">
<a-row :gutter="48">
<a-col :md="8" :sm="24">
<a-form-item label="组名称">
<a-select
v-model="queryParam.groupName"
placeholder="请输入组名称"
@change="(value) => handleChange(value)"
>
<a-select-option v-for="item in groupNameList" :value="item" :key="item">{{ item }}</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :md="8" :sm="24">
<a-form-item label="任务名称">
<a-select
show-search
v-model="queryParam.jobId"
placeholder="请输入任务名称"
:default-active-first-option="false"
:show-arrow="true"
:filter-option="false"
:not-found-content="null"
@search="handleSearch"
>
<a-select-option v-for="(item, index) in jobNameList" :value="item.id" :key="index">
{{item.jobName}}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<template>
</template>
<a-col :md="(!advanced && 8) || 24" :sm="24">
<span
class="table-page-search-submitButtons"
:style="(advanced && { float: 'right', overflow: 'hidden' }) || {}"
>
<a-button type="primary" @click="$refs.table.refresh(true)">查询</a-button>
<a-button style="margin-left: 8px" @click="() => (queryParam = {})">重置</a-button>
<a @click="toggleAdvanced" style="margin-left: 8px">
{{ advanced ? '收起' : '展开' }}
<a-icon :type="advanced ? 'up' : 'down'" />
</a>
</span>
</a-col>
</a-row>
</a-form>
</div>
<div class="table-operator">
<a-button type="primary" icon="plus" @click="handleNew()">新增</a-button>
</div>
<s-table
ref="table"
size="default"
:rowKey="(record) => record.id"
:columns="notifyColumns"
:data="loadData"
:alert="options.alert"
:rowSelection="options.rowSelection"
>
<span slot="notifyType" slot-scope="text">
<a-tag :color="notifyTypeList[text].color">
{{ notifyTypeList[text].name }}
</a-tag>
</span>
<span slot="notifyStatus" slot-scope="text">
<a-tag :color="notifyStatusList[text].color">
{{ notifyStatusList[text].name }}
</a-tag>
</span>
<span slot="notifyScene" slot-scope="text">
<a-tag :color="notifySceneList[text].color">
{{ notifySceneList[text].name }}
</a-tag>
</span>
<span slot="notifyAttribute" slot-scope="text, record">
{{ parseJson(JSON.parse(text),record) }}
</span>
<span slot="notifyThreshold" slot-scope="text">
{{ text === 0 ? '无' : text }}
</span>
<span slot="action" slot-scope="record">
<template>
<a @click="handleEdit(record)">编辑</a>
</template>
</span>
</s-table>
</a-card>
</div>
</template>
<script>
import {getAllGroupNameList, getSceneList} from '@/api/manage'
import { STable } from '@/components'
import {getJobList, jobNameList, jobNotifyConfigPageList} from "@/api/jobApi";
const enums = require('@/utils/retryEnum')
export default {
name: 'JobNotifyList',
components: { STable },
data () {
return {
notifyColumns: [
{
title: '组名',
dataIndex: 'groupName',
key: 'groupName',
width: '10%',
scopedSlots: { customRender: 'groupName' }
},
{
title: '任务',
dataIndex: 'jobName',
key: 'jobName',
width: '10%',
scopedSlots: { customRender: 'jobName' }
},
{
title: '通知状态',
dataIndex: 'notifyStatus',
width: '10%',
scopedSlots: { customRender: 'notifyStatus' }
},
{
title: '通知类型',
dataIndex: 'notifyType',
key: 'notifyType',
width: '10%',
scopedSlots: { customRender: 'notifyType' }
},
{
title: '通知场景',
dataIndex: 'notifyScene',
key: 'notifyScene',
width: '10%',
scopedSlots: { customRender: 'notifyScene' }
},
{
title: '通知阈值',
dataIndex: 'notifyThreshold',
key: 'notifyThreshold',
width: '10%',
scopedSlots: { customRender: 'notifyThreshold' }
},
{
title: '描述',
dataIndex: 'description',
key: 'description',
width: '20%',
scopedSlots: { customRender: 'description' }
},
{
title: '操作',
key: 'action',
fixed: 'right',
width: '180px',
scopedSlots: { customRender: 'action' }
}
],
data: [],
loading: false,
form: this.$form.createForm(this),
// /
advanced: false,
queryParam: {},
loadData: (parameter) => {
return jobNotifyConfigPageList(Object.assign(parameter, this.queryParam)).then((res) => {
return res
})
},
selectedRowKeys: [],
selectedRows: [],
// custom table alert & rowSelection
options: {
alert: {
show: true,
clear: () => {
this.selectedRowKeys = []
}
},
rowSelection: {
selectedRowKeys: this.selectedRowKeys,
onChange: this.onSelectChange
}
},
optionAlertShow: false,
memberLoading: false,
notifySceneList: enums.notifyScene,
notifyTypeList: enums.notifyType,
notifyStatusList: enums.notifyStatus,
visible: false,
key: '',
notifyTypeValue: '1',
groupNameList: [],
jobNameList: [],
}
},
created () {
getAllGroupNameList().then((res) => {
this.groupNameList = res.data
})
const jobId = this.$route.query.jobId
jobNameList({ jobId: jobId }).then(res => {
this.jobNameList = res.data
console.log(jobId)
if (jobId) {
this.queryParam['jobId'] = this.jobNameList[0].id
this.$refs.table.refresh(true)
}
})
},
methods: {
handleSearch (value) {
jobNameList({ keywords: value }).then(res => {
this.jobNameList = res.data
})
},
handleChange (value) {
getJobList({ groupName: value }).then((res) => {
this.jobNameList = res.data
})
},
toggleAdvanced () {
this.advanced = !this.advanced
},
handleNew () {
this.$router.push({ path: '/job/notify/config' })
},
handleEdit (record) {
this.$router.push({ path: '/job/notify/config', query: { id: record.id } })
},
}
}
</script>
<style scoped>
</style>

View File

@ -313,7 +313,7 @@
import { getAllGroupNameList } from '@/api/manage'
import { getJobDetail, saveJob, updateJob } from '@/api/jobApi'
import pick from 'lodash.pick'
import CronModal from '@/views/job/from/CronModal'
import CronModal from '@/views/job/form/CronModal'
import AFormModel from 'ant-design-vue/es/form-model/Form'
import AFormModelItem from 'ant-design-vue/es/form-model/FormItem'

View File

@ -0,0 +1,488 @@
<template>
<div>
<page-header-wrapper content="场景配置" @back="() => $router.go(-1)" style="margin: -24px -1px 0">
<div></div>
</page-header-wrapper>
<a-card :body-style="{padding: '24px 32px'}" :bordered="false" :loading="loading">
<a-form @submit="handleSubmit" :form="form" class="form-row" layout="vertical" style="width: 40%;margin: auto;">
<a-row class="form-row" :gutter="16">
<a-col :lg="18" :md="12" :sm="24">
<a-form-item>
<a-input
hidden
v-decorator="['id']" />
</a-form-item>
<a-form-item label="通知场景">
<a-select
placeholder="通知场景"
style="width: 100%;"
@change="changeNotifyScene"
v-decorator="[
'notifyScene',
{
initialValue: '1',
rules: [{ required: true, message: '请选通知场景'}]
}
]"
>
<a-select-option :value="index" v-for="(item, index) in notifySceneList" :key="index">{{ item.name }}</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item>
<a-input
hidden />
</a-form-item>
<a-form-item label="通知阈值">
<a-input
v-if="notifyThresholdDisabled.includes(this.notifySceneValue)"
disabled />
<a-input-number
v-else
id="inputNumber"
:min="1"
style="width: -webkit-fill-available"
v-decorator="[
'notifyThreshold',
{
initialValue: '16',
rules: [{ required: !notifyThresholdDisabled.includes(this.notifySceneValue), message: '请输入通知阈值'}]
}
]" />
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="16">
<a-col :lg="18" :md="12" :sm="24">
<a-form-item label="组">
<a-select placeholder="请选择组" v-decorator="['groupName', { rules: [{ required: true, message: '请选择组' }] }]" @change="value => changeGroup(value)">
<a-select-option v-for="item in groupNameList" :value="item" :key="item">{{ item }}</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24">
<a-form-item label="任务">
<a-select :disabled="sceneNameDisabled.includes(this.notifySceneValue)" placeholder="请选择任务" v-decorator="['jobId', { rules: [{ required: true, message: '请选择任务' }] }]" >
<a-select-option v-for="item in jobList" :value="item.id" :key="item.id">{{item.jobName}}</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="16">
<a-col :lg="8" :md="12" :sm="12">
<a-form-item label="通知类型">
<a-select
@change="handleChange"
placeholder="通知类型"
style="width: 100%;"
v-decorator="[
'notifyType',
{
initialValue: '1',
rules: [{ required: true, message: '请选择通知类型'}]
}
]"
>
<a-select-option :value="index" v-for="(item, index) in notifyTypeList" :key="index">{{ item.name }}</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="16" :md="12" :sm="12">
<a-form-item label="配置属性">
<a-input
placeholder="请输入配置属性"
@click="handleBlur"
v-decorator="[
'notifyAttribute',
{rules: [{ required: true, message: '请输入配置属性', whitespace: true}]}
]" />
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="16">
<a-col :lg="8" :md="12" :sm="24">
<a-form-item label="限流状态">
<a-select :disabled="rateLimiterStatusDisabled.includes(this.notifySceneValue)" placeholder="请选择限流状态" @change="changeRateLimiterStatus" v-decorator="['rateLimiterStatus',{initialValue: '0', rules: [{ required: true, message: '请选择限流状态'}]}]" >
<a-select-option :value="index" v-for="(item, index) in rateLimiterStatusList" :key="index">{{ item.name }}</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="8" :md="12" :sm="24">
<a-form-item label="每秒限流阈值">
<a-input-number :disabled="rateLimiterThresholdDisabled.includes(this.rateLimiterStatusValue)" id="inputNumber" :min="1" style="width: -webkit-fill-available" v-decorator= "['rateLimiterThreshold',{initialValue: '100',rules: [{ required: !rateLimiterThresholdDisabled.includes(this.rateLimiterStatusValue), message: '请输入通知阈值' }]}]" />
</a-form-item>
</a-col>
<a-col :lg="8" :md="12" :sm="24">
<a-form-item label="状态">
<a-select
placeholder="请选择状态"
v-decorator="[
'notifyStatus',
{
initialValue: '1',
rules: [{ required: true, message: '请选择状态'}]
}
]" >
<a-select-option :value="index" v-for="(item, index) in notifyStatusList" :key="index">{{ item.name }}</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="16">
<a-col :lg="24" :md="24" :sm="24">
<a-form-item label="描述">
<a-input
placeholder="请输入描述"
type="textarea"
v-decorator="[
'description',
{rules: [{required: false, max: 256, message: '最多支持256个字符'}]}
]" />
</a-form-item>
</a-col>
</a-row>
<a-form-item
:wrapperCol="{ span: 24 }"
style="text-align: center"
>
<a-button htmlType="submit" type="primary">提交</a-button>
</a-form-item>
</a-form>
</a-card>
<a-modal :visible="visible" title="添加配置" @ok="handleOk" @cancel="handlerCancel" width="1000px">
<a-form :form="notifyAttributeForm" @submit="handleSubmit" :body-style="{padding: '0px 0px'}" v-bind="formItemLayout" >
<a-form-item
v-if="this.notifyTypeValue === '1'"
label="钉钉URL">
<a-input
placeholder="请输入钉钉URL"
v-decorator="[
'webhookUrl',
{rules: [{ required: true, message: '请输入钉钉URL', whitespace: true}]}
]" />
</a-form-item>
<a-form-item v-if="this.notifyTypeValue === '1'">
<span slot="label">@人手机号&nbsp;<a :href="officialWebsite + '/pages/32e4a0/#被@人手机号是何物' +''" target="_blank"> <a-icon type="question-circle-o" /></a></span>
<a-input
placeholder="请输入被@负责人手机号"
type="textarea"
v-if="this.notifyTypeValue === '1'"
v-decorator="[
'ats',
{rules: [{ required: true, message: '请输入被@人手机号', whitespace: true}]}
]" />
</a-form-item>
<a-form-item
v-if="this.notifyTypeValue === '4'"
label="飞书URL">
<a-input
placeholder="请输入飞书URL"
v-decorator="[
'webhookUrl',
{rules: [{ required: true, message: '请输入飞书URL', whitespace: true}]}
]" />
</a-form-item>
<a-form-item
v-if="this.notifyTypeValue === '4'">
<span slot="label">@负责人用户id&nbsp;<a :href="officialWebsite + '/pages/32e4a0/#被@人open_id是何物' +''" target="_blank"> <a-icon type="question-circle-o" /></a></span>
<a-input
placeholder="请输入被@人open_id"
type="textarea"
v-if="this.notifyTypeValue === '4'"
v-decorator="[
'ats',
{rules: [{ required: true, message: '请输入被@人open_id', whitespace: true}]}
]" />
</a-form-item>
<a-form-item
v-if="this.notifyTypeValue === '2'"
label="用户名">
<a-input
placeholder="请输入用户名"
v-if="this.notifyTypeValue === '2'"
v-decorator="[
'user',
{rules: [{ required: true, message: '请输入用户名', whitespace: true}]}
]" />
</a-form-item>
<a-form-item
v-if="this.notifyTypeValue === '2'"
label="密码">
<a-input
placeholder="请输入密码"
v-if="this.notifyTypeValue === '2'"
v-decorator="[
'pass',
{rules: [{ required: true, message: '请输入密码', whitespace: true}]}
]"/>
</a-form-item>
<a-form-item
v-if="this.notifyTypeValue === '2'"
label="SMTP地址">
<a-input
placeholder="请输入邮件服务器的SMTP地址"
v-if="this.notifyTypeValue === '2'"
v-decorator="[
'host',
{rules: [{ required: true, message: '请输入邮件服务器的SMTP地址', whitespace: true}]}
]"/>
</a-form-item>
<a-form-item
v-if="this.notifyTypeValue === '2'"
label="SMTP端口">
<a-input
v-if="this.notifyTypeValue === '2'"
placeholder="请输入邮件服务器的SMTP端口"
v-decorator="[
'port',
{rules: [{ required: true, message: '请输入邮件服务器的SMTP端口', whitespace: true}]}
]"/>
</a-form-item>
<a-form-item
v-if="this.notifyTypeValue === '2'"
label="发件人">
<a-input
v-if="this.notifyTypeValue === '2'"
placeholder="请输入发件人"
v-decorator="[
'from',
{rules: [{ required: true, message: '请输入发件人', whitespace: true}]}
]"/>
</a-form-item>
<a-form-item
v-if="this.notifyTypeValue === '2'"
label="收件人">
<a-input
v-if="this.notifyTypeValue === '2'"
placeholder="请输入收件人"
v-decorator="[
'tos',
{rules: [{ required: true, message: '请输入收件人', whitespace: true}]}
]"/>
</a-form-item>
<a-form-item
:wrapper-col="{
xs: { span: 24, offset: 0 },
sm: { span: 16, offset: 8 },
lg: { span: 7 }
}">
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
<script>
import { getAllGroupNameList, getSceneList } from '@/api/manage'
import { getNotifyConfigDetail, saveNotify, updateNotify } from '@/api/retryApi'
import pick from 'lodash.pick'
import CronModal from '@/views/job/form/CronModal'
import { officialWebsite } from '@/utils/util'
import {getJobList, getJobNotifyConfigDetail, saveJobNotify, updateJobNotify} from "@/api/jobApi";
const enums = require('@/utils/jobEnum')
export default {
name: 'NotifyFrom',
props: {},
comments: {
CronModal
},
data () {
return {
form: this.$form.createForm(this),
formItemLayout: {
labelCol: { lg: { span: 7 }, sm: { span: 7 } },
wrapperCol: { lg: { span: 10 }, sm: { span: 17 } }
},
officialWebsite: officialWebsite(),
formItemLayoutWithOutLabel: {
wrapperCol: {
xs: { span: 24, offset: 0 },
sm: { span: 20, offset: 4 }
}
},
formType: 'create',
groupNameList: [],
jobList: [],
notifySceneList: enums.notifyScene,
notifyTypeList: enums.notifyType,
notifyStatusList: enums.notifyStatus,
rateLimiterStatusList: enums.rateLimiterStatus,
loading: false,
visible: false,
count: 0,
notifyTypeValue: '1',
notifyAttribute: '',
notifyThresholdDisabled: ['1'],
sceneNameDisabled: ['3', '4'],
rateLimiterStatusDisabled: ['1'],
rateLimiterThresholdDisabled: ['0'],
notifySceneValue: '1',
rateLimiterStatusValue: '0',
defaultRateLimiterStatusValue: '0',
defaultRateLimiterThreshold: '100'
}
},
beforeCreate () {
this.notifyAttributeForm = this.$form.createForm(this, { name: 'notify_attribute_form_item' })
},
mounted () {
getAllGroupNameList().then((res) => {
this.groupNameList = res.data
})
this.$nextTick(() => {
const id = this.$route.query.id
console.log(id)
if (id) {
this.loading = true
getJobNotifyConfigDetail(id).then(res => {
this.loadEditInfo(res.data)
this.loading = false
})
}
})
},
methods: {
resetFiled () {
this.form.resetFields()
},
buildNotifyAttribute (formData) {
formData.ats = formData.ats && formData.ats.replace(/\s+/g, '').split(',')
return JSON.stringify(formData)
},
handleChange (notifyType) {
this.notifyTypeValue = notifyType
},
changeGroup (value) {
getJobList({ groupName: value }).then((res) => {
this.jobList = res.data
})
},
changeRateLimiterStatus (rateLimiterStatus) {
this.rateLimiterStatusValue = rateLimiterStatus
},
changeNotifyScene (notifyScene) {
this.notifySceneValue = notifyScene
const { form } = this
if (this.sceneNameDisabled.includes(notifyScene)) {
form.setFieldsValue({
sceneName: ''
})
}
if (this.rateLimiterStatusDisabled.includes(notifyScene)) {
form.setFieldsValue({
rateLimiterStatus: this.defaultRateLimiterStatusValue,
rateLimiterThreshold: this.defaultRateLimiterThreshold
})
this.changeRateLimiterStatus(this.defaultRateLimiterStatusValue)
}
},
handleBlur () {
new Promise((resolve) => {
setTimeout(resolve, 100)
}).then(() => {
if (this.formType === 'edit') {
const formData = pick(JSON.parse(this.notifyAttribute), ['webhookUrl', 'ats', 'user', 'pass', 'host', 'port', 'from', 'tos'])
this.notifyAttributeForm.getFieldDecorator(`webhookUrl`, { initialValue: formData.webhookUrl, preserve: true })
this.notifyAttributeForm.getFieldDecorator(`ats`, { initialValue: formData.ats.join(','), preserve: true })
this.notifyAttributeForm.getFieldDecorator(`user`, { initialValue: formData.user, preserve: true })
this.notifyAttributeForm.getFieldDecorator(`pass`, { initialValue: formData.pass, preserve: true })
this.notifyAttributeForm.getFieldDecorator(`host`, { initialValue: formData.host, preserve: true })
this.notifyAttributeForm.getFieldDecorator(`port`, { initialValue: formData.port, preserve: true })
this.notifyAttributeForm.getFieldDecorator(`from`, { initialValue: formData.from, preserve: true })
this.notifyAttributeForm.getFieldDecorator(`tos`, { initialValue: formData.tos, preserve: true })
}
this.visible = !this.visible
})
},
handlerCancel () {
this.visible = false
},
handleOk () {
this.notifyAttributeForm.validateFields((err, values) => {
if (!err) {
const { form } = this
const formData = pick(values, ['webhookUrl', 'ats', 'user', 'pass', 'host', 'port', 'from', 'tos'])
this.notifyAttribute = this.buildNotifyAttribute(formData)
form.setFieldsValue({
notifyAttribute: this.parseJson(formData)
})
this.visible = false
}
})
},
handleSubmit (e) {
e.preventDefault()
this.form.validateFields((err, values) => {
if (!err) {
values['notifyAttribute'] = this.notifyAttribute
if (this.formType === 'create') {
saveJobNotify(values).then(res => {
this.$message.success('任务新增完成')
this.form.resetFields()
this.$router.go(-1)
})
} else {
updateJobNotify(values).then(res => {
this.$message.success('任务更新完成')
this.form.resetFields()
this.$router.go(-1)
})
}
}
})
},
loadEditInfo (data) {
this.formType = 'edit'
const { form } = this
// ajax
new Promise((resolve) => {
setTimeout(async () => {
await this.changeGroup(data.groupName)
resolve()
}, 100)
}).then(() => {
const formData = pick(data, [
'id', 'notifyAttribute', 'groupName', 'jobId', 'notifyStatus', 'notifyScene', 'notifyThreshold', 'notifyType', 'description', 'rateLimiterStatus', 'rateLimiterThreshold'])
formData.notifyStatus = formData.notifyStatus.toString()
formData.notifyScene = formData.notifyScene.toString()
formData.notifyType = formData.notifyType.toString()
formData.notifyThreshold = formData.notifyThreshold.toString()
formData.rateLimiterStatus = formData.rateLimiterStatus.toString()
formData.rateLimiterThreshold = formData.rateLimiterThreshold.toString()
this.notifyTypeValue = formData.notifyType
this.notifyAttribute = formData.notifyAttribute
this.notifySceneValue = formData.notifyScene
this.rateLimiterStatusValue = formData.rateLimiterStatus
formData.notifyAttribute = this.parseJson(JSON.parse(formData.notifyAttribute))
form.setFieldsValue(formData)
})
},
parseJson (json) {
if (!json) {
return null
}
let s =
'用户名:' + json['user'] + ';' +
'密码:' + json['pass'] + ';' +
'SMTP地址:' + json['host'] + ';' +
'SMTP端口:' + json['port'] + ';' +
'发件人:' + json['from'] + ';' +
'收件人:' + json['tos'] + ';'
if (this.notifyTypeValue === '1') {
s =
'钉钉Url:' + json['webhookUrl'] + ';' +
'被@人手机号:' + json['ats'] + ';'
} else if (this.notifyTypeValue === '4') {
s =
'飞书Url:' + json['webhookUrl'] + ';' +
'被@人用户id:' + json['ats'] + ';'
}
return s
}
}
}
</script>

View File

@ -165,7 +165,7 @@
<a-form-item v-if="this.notifyTypeValue === '1'">
<span slot="label">@人手机号&nbsp;<a :href="officialWebsite + '/pages/32e4a0/#被@人手机号是何物' +''" target="_blank"> <a-icon type="question-circle-o" /></a></span>
<a-input
placeholder="请输入被@负责人手机号"
placeholder="请输入被@人手机号"
type="textarea"
v-if="this.notifyTypeValue === '1'"
v-decorator="[
@ -185,7 +185,7 @@
</a-form-item>
<a-form-item
v-if="this.notifyTypeValue === '4'">
<span slot="label">@负责人用户id&nbsp;<a :href="officialWebsite + '/pages/32e4a0/#被@人open_id是何物' +''" target="_blank"> <a-icon type="question-circle-o" /></a></span>
<span slot="label">@人用户id&nbsp;<a :href="officialWebsite + '/pages/32e4a0/#被@人open_id是何物' +''" target="_blank"> <a-icon type="question-circle-o" /></a></span>
<a-input
placeholder="请输入被@人open_id"
type="textarea"
@ -277,7 +277,7 @@
import { getAllGroupNameList, getSceneList } from '@/api/manage'
import { getNotifyConfigDetail, saveNotify, updateNotify } from '@/api/retryApi'
import pick from 'lodash.pick'
import CronModal from '@/views/job/from/CronModal'
import CronModal from '@/views/job/form/CronModal'
import { officialWebsite } from '@/utils/util'
const enums = require('@/utils/retryEnum')
export default {
@ -474,11 +474,11 @@ export default {
if (this.notifyTypeValue === '1') {
s =
'钉钉Url:' + json['webhookUrl'] + ';' +
'被@负责人手机号:' + json['ats'] + ';'
'被@人手机号:' + json['ats'] + ';'
} else if (this.notifyTypeValue === '4') {
s =
'飞书Url:' + json['webhookUrl'] + ';' +
'被@负责人用户id:' + json['ats'] + ';'
'被@人用户id:' + json['ats'] + ';'
}
return s
}

View File

@ -202,7 +202,7 @@
import { getAllGroupNameList } from '@/api/manage'
import { saveScene, updateScene, getSceneDetail } from '@/api/retryApi'
import pick from 'lodash.pick'
import CronModal from '@/views/job/from/CronModal'
import CronModal from '@/views/job/form/CronModal'
const enums = require('@/utils/retryEnum')
export default {