feat:2.5.0

1. 完成重试任务、死信任务、定时任务、任务批次、用户权限详情页抽屉展示
This commit is contained in:
byteblogs168 2023-11-25 23:49:13 +08:00
parent 722be7623d
commit 3a76423849
43 changed files with 671 additions and 426 deletions

View File

@ -12,7 +12,7 @@ import java.util.List;
@Mapper
public interface ServerNodeMapper extends BaseMapper<ServerNode> {
int insertOrUpdate(List<ServerNode> records);
int insertOrUpdate(@Param("records") List<ServerNode> records);
int deleteByExpireAt(@Param("endTime") LocalDateTime endTime);

View File

@ -19,9 +19,9 @@
SELECT a.*, b.job_name, b.task_type, b.block_strategy, b.trigger_type
FROM job_task_batch a join job b on a.job_id = b.id
<where>
namespace_id = #{queryDO.namespaceId}
a.namespace_id = #{queryDO.namespaceId}
<if test="queryDO.jobId != null">
and job_id = #{queryDO.jobId}
and a.job_id = #{queryDO.jobId}
</if>
<if test="queryDO.groupName != null">
and a.group_name = #{queryDO.groupName}

View File

@ -3,6 +3,7 @@
<mapper namespace="com.aizuda.easy.retry.template.datasource.persistence.mapper.RetryDeadLetterMapper">
<resultMap id="BaseResultMap" type="com.aizuda.easy.retry.template.datasource.persistence.po.RetryDeadLetter">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="namespace_id" jdbcType="VARCHAR" property="namespaceId"/>
<result column="unique_id" jdbcType="VARCHAR" property="uniqueId"/>
<result column="group_name" jdbcType="VARCHAR" property="groupName" />
<result column="scene_name" jdbcType="VARCHAR" property="sceneName" />
@ -15,16 +16,16 @@
<result column="create_dt" jdbcType="TIMESTAMP" property="createDt" />
</resultMap>
<sql id="Base_Column_List">
id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, create_dt, task_type
id, namespace_id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, create_dt, task_type
</sql>
<insert id="insertBatch">
insert into retry_dead_letter (id, unique_id, group_name, scene_name,
insert into retry_dead_letter (namespace_id, unique_id, group_name, scene_name,
idempotent_id, biz_no, executor_name, args_str,
ext_attrs, create_dt
)
values
<foreach collection="retryDeadLetters" item="retryDeadLetter" separator=",">
(#{retryDeadLetter.id,jdbcType=BIGINT}, #{retryDeadLetter.uniqueId,jdbcType=VARCHAR}, #{retryDeadLetter.groupName,jdbcType=VARCHAR}, #{retryDeadLetter.sceneName,jdbcType=VARCHAR},
(#{retryDeadLetter.namespaceId,jdbcType=VARCHAR}, #{retryDeadLetter.uniqueId,jdbcType=VARCHAR}, #{retryDeadLetter.groupName,jdbcType=VARCHAR}, #{retryDeadLetter.sceneName,jdbcType=VARCHAR},
#{retryDeadLetter.idempotentId,jdbcType=VARCHAR}, #{retryDeadLetter.bizNo,jdbcType=VARCHAR}, #{retryDeadLetter.executorName,jdbcType=VARCHAR}, #{retryDeadLetter.argsStr,jdbcType=VARCHAR},
#{retryDeadLetter.extAttrs,jdbcType=VARCHAR}, #{retryDeadLetter.createDt,jdbcType=TIMESTAMP})
</foreach>

View File

@ -3,6 +3,7 @@
<mapper namespace="com.aizuda.easy.retry.template.datasource.persistence.mapper.RetryTaskMapper">
<resultMap id="BaseResultMap" type="com.aizuda.easy.retry.template.datasource.persistence.po.RetryTask">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="namespace_id" jdbcType="VARCHAR" property="namespaceId"/>
<result column="unique_id" jdbcType="VARCHAR" property="uniqueId"/>
<result column="group_name" jdbcType="VARCHAR" property="groupName" />
<result column="scene_name" jdbcType="VARCHAR" property="sceneName" />
@ -19,15 +20,15 @@
<result column="update_dt" jdbcType="TIMESTAMP" property="updateDt" />
</resultMap>
<sql id="Base_Column_List">
id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, retry_count, retry_status,
id, namespace_id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, retry_count, retry_status,
create_dt, update_dt, task_type
</sql>
<!-- 定义批量新增的 SQL 映射 -->
<insert id="batchInsert" parameterType="java.util.List">
INSERT INTO retry_task (unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, task_type, retry_status, create_dt)
INSERT INTO retry_task (namespace_id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, task_type, retry_status, create_dt)
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.uniqueId}, #{item.groupName}, #{item.sceneName}, #{item.idempotentId}, #{item.bizNo}, #{item.executorName}, #{item.argsStr}, #{item.extAttrs}, #{item.nextTriggerAt}, #{item.taskType}, #{item.retryStatus}, #{item.createDt})
(#{item.namespaceId}, #{item.uniqueId}, #{item.groupName}, #{item.sceneName}, #{item.idempotentId}, #{item.bizNo}, #{item.executorName}, #{item.argsStr}, #{item.extAttrs}, #{item.nextTriggerAt}, #{item.taskType}, #{item.retryStatus}, #{item.createDt})
</foreach>
</insert>

View File

@ -19,9 +19,9 @@
SELECT a.*, b.job_name, b.task_type, b.block_strategy, b.trigger_type
FROM job_task_batch a join job b on a.job_id = b.id
<where>
namespace_id = #{queryDO.namespaceId}
a.namespace_id = #{queryDO.namespaceId}
<if test="queryDO.jobId != null">
and job_id = #{queryDO.jobId}
and a.job_id = #{queryDO.jobId}
</if>
<if test="queryDO.groupName != null">
and a.group_name = #{queryDO.groupName}

View File

@ -3,6 +3,7 @@
<mapper namespace="com.aizuda.easy.retry.template.datasource.persistence.mapper.RetryDeadLetterMapper">
<resultMap id="BaseResultMap" type="com.aizuda.easy.retry.template.datasource.persistence.po.RetryDeadLetter">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="namespace_id" jdbcType="VARCHAR" property="namespaceId"/>
<result column="unique_id" jdbcType="VARCHAR" property="uniqueId"/>
<result column="group_name" jdbcType="VARCHAR" property="groupName" />
<result column="scene_name" jdbcType="VARCHAR" property="sceneName" />
@ -18,13 +19,13 @@
id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, create_dt, task_type
</sql>
<insert id="insertBatch">
insert into retry_dead_letter (id, unique_id, group_name, scene_name,
insert into retry_dead_letter (namespace_id, unique_id, group_name, scene_name,
idempotent_id, biz_no, executor_name, args_str,
ext_attrs, create_dt
)
values
<foreach collection="retryDeadLetters" item="retryDeadLetter" separator=",">
(#{retryDeadLetter.id,jdbcType=BIGINT}, #{retryDeadLetter.uniqueId,jdbcType=VARCHAR}, #{retryDeadLetter.groupName,jdbcType=VARCHAR}, #{retryDeadLetter.sceneName,jdbcType=VARCHAR},
(#{retryDeadLetter.namespaceId,jdbcType=VARCHAR}, #{retryDeadLetter.uniqueId,jdbcType=VARCHAR}, #{retryDeadLetter.groupName,jdbcType=VARCHAR}, #{retryDeadLetter.sceneName,jdbcType=VARCHAR},
#{retryDeadLetter.idempotentId,jdbcType=VARCHAR}, #{retryDeadLetter.bizNo,jdbcType=VARCHAR}, #{retryDeadLetter.executorName,jdbcType=VARCHAR}, #{retryDeadLetter.argsStr,jdbcType=VARCHAR},
#{retryDeadLetter.extAttrs,jdbcType=VARCHAR}, #{retryDeadLetter.createDt,jdbcType=TIMESTAMP})
</foreach>

View File

@ -3,6 +3,7 @@
<mapper namespace="com.aizuda.easy.retry.template.datasource.persistence.mapper.RetryTaskMapper">
<resultMap id="BaseResultMap" type="com.aizuda.easy.retry.template.datasource.persistence.po.RetryTask">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="namespace_id" jdbcType="VARCHAR" property="namespaceId"/>
<result column="unique_id" jdbcType="VARCHAR" property="uniqueId"/>
<result column="group_name" jdbcType="VARCHAR" property="groupName" />
<result column="scene_name" jdbcType="VARCHAR" property="sceneName" />
@ -19,15 +20,15 @@
<result column="update_dt" jdbcType="TIMESTAMP" property="updateDt" />
</resultMap>
<sql id="Base_Column_List">
id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, retry_count, retry_status,
id, namespace_id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, retry_count, retry_status,
create_dt, update_dt, task_type
</sql>
<!-- 定义批量新增的 SQL 映射 -->
<insert id="batchInsert" parameterType="java.util.List">
INSERT INTO retry_task (unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, task_type, retry_status, create_dt)
INSERT INTO retry_task (namespace_id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, task_type, retry_status, create_dt)
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.uniqueId}, #{item.groupName}, #{item.sceneName}, #{item.idempotentId}, #{item.bizNo}, #{item.executorName}, #{item.argsStr}, #{item.extAttrs}, #{item.nextTriggerAt}, #{item.taskType}, #{item.retryStatus}, #{item.createDt})
(#{item.namespaceId}, #{item.uniqueId}, #{item.groupName}, #{item.sceneName}, #{item.idempotentId}, #{item.bizNo}, #{item.executorName}, #{item.argsStr}, #{item.extAttrs}, #{item.nextTriggerAt}, #{item.taskType}, #{item.retryStatus}, #{item.createDt})
</foreach>
</insert>
<update id="updateBatchNextTriggerAtById" parameterType="java.util.List">

View File

@ -7,6 +7,7 @@
<id column="id" property="id" />
<result column="group_name" property="groupName" />
<result column="system_user_id" property="systemUserId" />
<result column="namespace_id" property="namespaceId" />
<result column="create_dt" property="createDt" />
</resultMap>

View File

@ -19,9 +19,9 @@
SELECT a.*, b.job_name, b.task_type, b.block_strategy, b.trigger_type
FROM job_task_batch a join job b on a.job_id = b.id
<where>
namespace_id = #{queryDO.namespaceId}
a.namespace_id = #{queryDO.namespaceId}
<if test="queryDO.jobId != null">
and job_id = #{queryDO.jobId}
and a.job_id = #{queryDO.jobId}
</if>
<if test="queryDO.groupName != null">
and a.group_name = #{queryDO.groupName}

View File

@ -3,6 +3,7 @@
<mapper namespace="com.aizuda.easy.retry.template.datasource.persistence.mapper.RetryDeadLetterMapper">
<resultMap id="BaseResultMap" type="com.aizuda.easy.retry.template.datasource.persistence.po.RetryDeadLetter">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="namespace_id" jdbcType="VARCHAR" property="namespaceId"/>
<result column="unique_id" jdbcType="VARCHAR" property="uniqueId"/>
<result column="group_name" jdbcType="VARCHAR" property="groupName" />
<result column="scene_name" jdbcType="VARCHAR" property="sceneName" />
@ -15,16 +16,16 @@
<result column="create_dt" jdbcType="TIMESTAMP" property="createDt" />
</resultMap>
<sql id="Base_Column_List">
id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, create_dt, task_type
id, namespace_id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, create_dt, task_type
</sql>
<insert id="insertBatch">
insert into retry_dead_letter (id, unique_id, group_name, scene_name,
insert into retry_dead_letter (namespace_id, unique_id, group_name, scene_name,
idempotent_id, biz_no, executor_name, args_str,
ext_attrs, create_dt
)
values
<foreach collection="retryDeadLetters" item="retryDeadLetter" separator=",">
(#{retryDeadLetter.id,jdbcType=BIGINT}, #{retryDeadLetter.uniqueId,jdbcType=VARCHAR}, #{retryDeadLetter.groupName,jdbcType=VARCHAR}, #{retryDeadLetter.sceneName,jdbcType=VARCHAR},
(#{retryDeadLetter.namespaceId,jdbcType=VARCHAR}, #{retryDeadLetter.uniqueId,jdbcType=VARCHAR}, #{retryDeadLetter.groupName,jdbcType=VARCHAR}, #{retryDeadLetter.sceneName,jdbcType=VARCHAR},
#{retryDeadLetter.idempotentId,jdbcType=VARCHAR}, #{retryDeadLetter.bizNo,jdbcType=VARCHAR}, #{retryDeadLetter.executorName,jdbcType=VARCHAR}, #{retryDeadLetter.argsStr,jdbcType=VARCHAR},
#{retryDeadLetter.extAttrs,jdbcType=VARCHAR}, #{retryDeadLetter.createDt,jdbcType=TIMESTAMP})
</foreach>

View File

@ -3,6 +3,7 @@
<mapper namespace="com.aizuda.easy.retry.template.datasource.persistence.mapper.RetryTaskMapper">
<resultMap id="BaseResultMap" type="com.aizuda.easy.retry.template.datasource.persistence.po.RetryTask">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="namespace_id" jdbcType="VARCHAR" property="namespaceId"/>
<result column="unique_id" jdbcType="VARCHAR" property="uniqueId"/>
<result column="group_name" jdbcType="VARCHAR" property="groupName" />
<result column="scene_name" jdbcType="VARCHAR" property="sceneName" />
@ -19,15 +20,15 @@
<result column="update_dt" jdbcType="TIMESTAMP" property="updateDt" />
</resultMap>
<sql id="Base_Column_List">
id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, retry_count, retry_status,
id, namespace_id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, retry_count, retry_status,
create_dt, update_dt, task_type
</sql>
<!-- 定义批量新增的 SQL 映射 -->
<insert id="batchInsert" parameterType="java.util.List">
INSERT INTO retry_task (unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, task_type, retry_status, create_dt)
INSERT INTO retry_task (namespace_id, unique_id, group_name, scene_name, idempotent_id, biz_no, executor_name, args_str, ext_attrs, next_trigger_at, task_type, retry_status, create_dt)
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.uniqueId}, #{item.groupName}, #{item.sceneName}, #{item.idempotentId}, #{item.bizNo}, #{item.executorName}, #{item.argsStr}, #{item.extAttrs}, #{item.nextTriggerAt}, #{item.taskType}, #{item.retryStatus}, #{item.createDt})
(#{item.namespaceId}, #{item.uniqueId}, #{item.groupName}, #{item.sceneName}, #{item.idempotentId}, #{item.bizNo}, #{item.executorName}, #{item.argsStr}, #{item.extAttrs}, #{item.nextTriggerAt}, #{item.taskType}, #{item.retryStatus}, #{item.createDt})
</foreach>
</insert>
<update id="updateBatchNextTriggerAtById" parameterType="java.util.List">

View File

@ -159,7 +159,7 @@ public class ScanJobTaskActor extends AbstractActor {
new LambdaQueryWrapper<Job>()
.select(Job::getGroupName, Job::getNextTriggerAt, Job::getBlockStrategy, Job::getTriggerType,
Job::getTriggerInterval, Job::getExecutorTimeout, Job::getTaskType, Job::getResident,
Job::getId)
Job::getId, Job::getNamespaceId)
.eq(Job::getJobStatus, StatusEnum.YES.getStatus())
.eq(Job::getDeleted, StatusEnum.NO.getStatus())
.in(Job::getBucketIndex, scanTask.getBuckets())

View File

@ -129,7 +129,7 @@ public abstract class AbstractGenerator implements TaskGenerator {
}
RetryTask retryTask = RetryTaskConverter.INSTANCE.toRetryTask(taskInfo);
retryTask.setNamespaceId(sceneConfig.getNamespaceId());
retryTask.setNamespaceId(taskContext.getNamespaceId());
retryTask.setUniqueId(getIdGenerator(taskContext.getGroupName(), taskContext.getNamespaceId()));
retryTask.setTaskType(TaskTypeEnum.RETRY.getType());
retryTask.setGroupName(taskContext.getGroupName());

View File

@ -186,7 +186,8 @@ public abstract class AbstractScanGroup extends AbstractActor {
.listPage(groupName, new PageDTO<>(0, systemProperties.getRetryPullPageSize()),
new LambdaQueryWrapper<RetryTask>()
.select(RetryTask::getId, RetryTask::getNextTriggerAt, RetryTask::getUniqueId,
RetryTask::getGroupName, RetryTask::getRetryCount, RetryTask::getSceneName)
RetryTask::getGroupName, RetryTask::getRetryCount, RetryTask::getSceneName,
RetryTask::getNamespaceId)
.eq(RetryTask::getRetryStatus, RetryStatusEnum.RUNNING.getStatus())
.eq(RetryTask::getGroupName, groupName)
.eq(RetryTask::getNamespaceId, namespaceId)

View File

@ -49,7 +49,8 @@ public class RetryTaskSchedule extends AbstractSchedule implements Lifecycle {
try {
Set<String> groupNameList = accessTemplate.getGroupConfigAccess()
.list(new LambdaQueryWrapper<GroupConfig>()
.select(GroupConfig::getGroupName).eq(GroupConfig::getGroupStatus, StatusEnum.YES))
.select(GroupConfig::getGroupName)
.eq(GroupConfig::getGroupStatus, StatusEnum.YES.getStatus()))
.stream().map(GroupConfig::getGroupName).collect(Collectors.toSet());
for (String groupName : groupNameList) {

View File

@ -1,6 +1,7 @@
package com.aizuda.easy.retry.server.web.controller;
import com.aizuda.easy.retry.server.web.model.request.UserSessionVO;
import com.aizuda.easy.retry.server.web.model.response.PermissionsResponseVO;
import com.aizuda.easy.retry.server.web.service.SystemUserService;
import com.aizuda.easy.retry.server.web.model.base.PageResult;
import com.aizuda.easy.retry.server.web.model.request.SystemUserQueryVO;
@ -62,6 +63,12 @@ public class SystemUserController {
return systemUserService.getSystemUserByUserName(username);
}
@LoginRequired(role = RoleEnum.ADMIN)
@GetMapping("/user-permissions/{id}")
public List<PermissionsResponseVO> getSystemUserPermissionByUserName(@PathVariable("id") Long id) {
return systemUserService.getSystemUserPermissionByUserName(id);
}
@LoginRequired
@DeleteMapping("/user/{id}")
public boolean delUser(@PathVariable("id") Long id) {

View File

@ -29,12 +29,6 @@ public class JobRequestVO {
@NotBlank(message = "jobName 不能为空")
private String jobName;
/**
* 命名空间id
*/
@NotNull(message = "命名空间id 不能为空")
private String namespaceId;
/**
* 重试状态 0关闭1开启
*/

View File

@ -20,12 +20,6 @@ public class NotifyConfigRequestVO {
@Pattern(regexp = "^[A-Za-z0-9_]{1,64}$", message = "仅支持长度为1~64字符且类型为数字、字母和下划线")
private String groupName;
/**
* 命名空间id
*/
@NotNull(message = "命名空间id 不能为空")
private String namespaceId;
private String sceneName;
@NotNull(message = "通知状态不能为空")

View File

@ -2,6 +2,9 @@ package com.aizuda.easy.retry.server.web.model.response;
import lombok.Data;
import java.util.List;
import java.util.Set;
/**
* @author: xiaowoniu
* @date : 2023-11-23 14:01
@ -12,4 +15,6 @@ public class PermissionsResponseVO {
private String groupName;
private String namespaceId;
private String namespaceName;
private Set<String> groupNames;
}

View File

@ -4,6 +4,7 @@ import com.aizuda.easy.retry.server.web.model.request.UserSessionVO;
import com.aizuda.easy.retry.server.web.model.base.PageResult;
import com.aizuda.easy.retry.server.web.model.request.SystemUserQueryVO;
import com.aizuda.easy.retry.server.web.model.request.SystemUserRequestVO;
import com.aizuda.easy.retry.server.web.model.response.PermissionsResponseVO;
import com.aizuda.easy.retry.server.web.model.response.SystemUserResponseVO;
import java.util.List;
@ -31,4 +32,6 @@ public interface SystemUserService {
SystemUserResponseVO getSystemUserByUserName(String username);
boolean delUser(Long id);
List<PermissionsResponseVO> getSystemUserPermissionByUserName(Long id);
}

View File

@ -122,6 +122,7 @@ public class JobServiceImpl implements JobService {
job.setBucketIndex(HashUtil.bkdrHash(jobRequestVO.getGroupName() + jobRequestVO.getJobName())
% systemProperties.getBucketTotal());
job.setNextTriggerAt(calculateNextTriggerAt(jobRequestVO, DateUtils.toNowMilli()));
job.setNamespaceId(UserSessionUtils.currentUserSession().getNamespaceId());
return 1 == jobMapper.insert(job);
}

View File

@ -53,7 +53,7 @@ public class NotifyConfigServiceImpl implements NotifyConfigService {
public Boolean saveNotify(NotifyConfigRequestVO requestVO) {
NotifyConfig notifyConfig = NotifyConfigConverter.INSTANCE.toNotifyConfig(requestVO);
notifyConfig.setCreateDt(LocalDateTime.now());
notifyConfig.setNamespaceId(UserSessionUtils.currentUserSession().getNamespaceId());
ConfigAccess<NotifyConfig> notifyConfigAccess = accessTemplate.getNotifyConfigAccess();
Assert.isTrue(1 == notifyConfigAccess.insert(notifyConfig),

View File

@ -122,8 +122,8 @@ public class SceneConfigServiceImpl implements SceneConfigService {
Assert.isTrue(1 == accessTemplate.getSceneConfigAccess().update(sceneConfig,
new LambdaUpdateWrapper<SceneConfig>()
.eq(SceneConfig::getNamespaceId, namespaceId)
.eq(SceneConfig::getGroupName, sceneConfig.getGroupName())
.eq(SceneConfig::getSceneName, sceneConfig.getSceneName())),
.eq(SceneConfig::getGroupName, requestVO.getGroupName())
.eq(SceneConfig::getSceneName, requestVO.getSceneName())),
() -> new EasyRetryServerException("failed to update scene. sceneConfig:[{}]",
JsonUtil.toJsonString(sceneConfig)));
return Boolean.TRUE;

View File

@ -1,12 +1,15 @@
package com.aizuda.easy.retry.server.web.service.impl;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.aizuda.easy.retry.server.common.config.SystemProperties;
import com.aizuda.easy.retry.server.common.exception.EasyRetryServerException;
import com.aizuda.easy.retry.server.common.triple.Triple;
import com.aizuda.easy.retry.server.web.model.request.UserPermissionRequestVO;
import com.aizuda.easy.retry.server.web.model.request.UserSessionVO;
import com.aizuda.easy.retry.server.web.model.response.PermissionsResponseVO;
import com.aizuda.easy.retry.server.web.service.convert.NamespaceResponseVOConverter;
import com.aizuda.easy.retry.server.web.service.convert.PermissionsResponseVOConverter;
import com.aizuda.easy.retry.template.datasource.persistence.mapper.NamespaceMapper;
@ -27,14 +30,13 @@ import com.aizuda.easy.retry.server.web.model.base.PageResult;
import com.aizuda.easy.retry.server.web.model.request.SystemUserQueryVO;
import com.aizuda.easy.retry.server.web.model.request.SystemUserRequestVO;
import com.aizuda.easy.retry.server.web.model.response.SystemUserResponseVO;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.*;
import java.util.stream.Collectors;
/**
@ -230,30 +232,57 @@ public class SystemUserServiceImpl implements SystemUserService {
}
SystemUserResponseVO responseVO = SystemUserResponseVOConverter.INSTANCE.convert(systemUser);
if (RoleEnum.ADMIN.getRoleId().equals(systemUser.getRole())) {
return responseVO;
}
getPermission(systemUser.getRole(), systemUser.getId(), responseVO);
List<SystemUserPermission> systemUserPermissions = systemUserPermissionMapper.selectList(
new LambdaQueryWrapper<SystemUserPermission>()
.select(SystemUserPermission::getNamespaceId, SystemUserPermission::getGroupName)
.eq(SystemUserPermission::getSystemUserId, responseVO.getId()));
LambdaQueryWrapper<Namespace> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.select(Namespace::getId, Namespace::getUniqueId, Namespace::getName);
queryWrapper.in(Namespace::getUniqueId, systemUserPermissions.stream()
.map(SystemUserPermission::getNamespaceId).distinct().collect(Collectors.toList()));
List<Namespace> namespaces = namespaceMapper.selectList(queryWrapper);
responseVO.setNamespaceIds(
NamespaceResponseVOConverter.INSTANCE.toNamespaceResponseVOs(namespaces));
new LambdaQueryWrapper<SystemUserPermission>()
.select(SystemUserPermission::getNamespaceId, SystemUserPermission::getGroupName)
.eq(SystemUserPermission::getSystemUserId, responseVO.getId()));
responseVO.setPermissions(PermissionsResponseVOConverter.INSTANCE.toPermissionsResponseVOs(systemUserPermissions));
return responseVO;
}
@Override
public List<PermissionsResponseVO> getSystemUserPermissionByUserName(Long id) {
List<SystemUserPermission> systemUserPermissions = systemUserPermissionMapper.selectList(
new LambdaQueryWrapper<SystemUserPermission>()
.select(SystemUserPermission::getNamespaceId, SystemUserPermission::getGroupName)
.eq(SystemUserPermission::getSystemUserId, id));
if (CollectionUtils.isEmpty(systemUserPermissions)) {
return Lists.newArrayList();
}
Map<String, List<SystemUserPermission>> permissionsMap = systemUserPermissions.stream().collect(Collectors.groupingBy(SystemUserPermission::getNamespaceId));
LambdaQueryWrapper<Namespace> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.select(Namespace::getId, Namespace::getUniqueId, Namespace::getName);
queryWrapper.in(Namespace::getUniqueId, permissionsMap.keySet());
List<Namespace> namespaces = namespaceMapper.selectList(queryWrapper);
Map<String, String> map = namespaces.stream().collect(Collectors.toMap(Namespace::getUniqueId, Namespace::getName));
List<PermissionsResponseVO> response = new ArrayList<>();
permissionsMap.forEach((namespaceId, values) -> {
PermissionsResponseVO responseVO = new PermissionsResponseVO();
responseVO.setNamespaceName(map.get(namespaceId));
responseVO.setNamespaceId(namespaceId);
responseVO.setGroupNames(values.stream().map(SystemUserPermission::getGroupName).collect(Collectors.toSet()));
response.add(responseVO);
});
return response;
}
@Override
@Transactional
public boolean delUser(final Long id) {
systemUserPermissionMapper.delete(
new LambdaQueryWrapper<SystemUserPermission>()
.eq(SystemUserPermission::getSystemUserId, id)
);
return 1 == systemUserMapper.deleteById(id);
}

View File

@ -36,6 +36,7 @@ const api = {
delUser: '/user/',
saveUser: '/user',
systemUserByUserName: '/user/username/user-info',
systemUserPermissionByUserId: '/user-permissions/',
countTask: '/dashboard/task/count',
countDispatch: '/dashboard/dispatch/count',
countActivePod: '/dashboard/active-pod/count',
@ -359,6 +360,13 @@ export function getSystemUserByUserName (parameter) {
})
}
export function getSystemUserPermissionByUserId (id) {
return request({
url: api.systemUserPermissionByUserId + id,
method: 'get'
})
}
export function getNotifyConfigList (parameter) {
return request({
url: api.notifyConfigList,

View File

@ -0,0 +1,81 @@
<template>
<a-drawer
placement="right"
:width="width"
@afterVisibleChange="afterVisibleChange"
:visible="visible"
@close="onClose"
>
<span slot="title">
{{ title }}
<button class="ant-drawer-close" style="margin-right: 40px" v-if="visibleAmplify">
<a-icon type="arrows-alt" style="font-size : 16px;" @click="this.onAmplify"/>
</button>
</span>
<slot></slot>
</a-drawer>
</template>
<script>
export default {
props: {
visible: {
type: Boolean,
default: false
},
visibleAmplify: {
type: Boolean,
default: false
},
title: {
type: String,
default: ''
},
width: {
type: Number,
default: 300
}
},
methods: {
onClose () {
this.$emit('closeDrawer')
},
onAmplify () {
this.$emit('handlerAmplify')
},
afterVisibleChange () {
this.$emit('afterVisibleChange')
}
}
}
</script>
<style scoped lang='less'>
.amplify {
position: absolute;
top: 0;
right: 0;
z-index: 10;
display: block;
width: 56px;
height: 56px;
color: rgba(0, 0, 0, 0.45);
font-weight: 700;
font-size: 16px;
font-style: normal;
line-height: 56px;
text-align: center;
text-transform: none;
text-decoration: none;
background: transparent;
border: 0;
outline: 0;
cursor: pointer;
-webkit-transition: color 0.3s;
transition: color 0.3s;
text-rendering: auto;
margin-right: 45px;
}
</style>

View File

@ -0,0 +1,3 @@
import Drawer from './Drawer.vue'
export default Drawer

View File

@ -25,6 +25,7 @@ import IconSelector from '@/components/IconSelector'
import TagSelect from '@/components/TagSelect'
import StandardFormRow from '@/components/StandardFormRow'
import ArticleListContent from '@/components/ArticleListContent'
import Drawer from '@/components/Drawer'
import Dialog from '@/components/Dialog'
@ -53,6 +54,6 @@ export {
StandardFormRow,
ArticleListContent,
G2Line,
Drawer,
Dialog
}

View File

@ -76,20 +76,20 @@ export const asyncRouterMap = [
name: 'RetryTaskInfo',
hidden: true,
component: () => import('@/views/task/RetryTaskInfo'),
meta: { title: '任务管理详情', icon: 'profile', keepAlive: true, permission: ['retryTask'] }
meta: { title: '重试任务详情', icon: 'profile', keepAlive: true, permission: ['retryTask'] }
},
{
path: '/retry/dead-letter/list',
name: 'RetryDeadLetterList',
component: () => import('@/views/task/RetryDeadLetterList'),
meta: { title: '死信队列', icon: 'profile', permission: ['retryDeadLetter'] }
meta: { title: '死信任务', icon: 'profile', permission: ['retryDeadLetter'] }
},
{
path: '/retry/dead-letter/info',
name: 'RetryDeadLetterInfo',
hidden: true,
component: () => import('@/views/task/RetryDeadLetterInfo'),
meta: { title: '死信队列管理详情', icon: 'profile', permission: ['retryDeadLetter'] }
meta: { title: '死信任务详情', icon: 'profile', permission: ['retryDeadLetter'] }
},
{
path: '/retry/log/list',
@ -143,14 +143,14 @@ export const asyncRouterMap = [
path: '/job/list',
name: 'JobList',
component: () => import('@/views/job/JobList'),
meta: { title: '任务信息', icon: 'profile', permission: ['job'] }
meta: { title: '任务信息', keepAlive: true, icon: 'profile', permission: ['job'] }
},
{
path: '/job/info',
name: 'JobInfo',
hidden: true,
component: () => import('@/views/job/JobInfo'),
meta: { title: '定时任务详情', icon: 'profile', permission: ['job'] }
meta: { title: '定时任务详情', keepAlive: true, icon: 'profile', permission: ['job'] }
},
{
path: '/job/config',

View File

@ -1,10 +1,10 @@
<template>
<div>
<page-header-wrapper @back="() => $router.go(-1)" style="margin: -24px -1px 0">
<page-header-wrapper @back="() => $router.replace('/job/batch/list')" style="margin: -24px -1px 0"v-if="showHeader">
<div></div>
</page-header-wrapper>
<a-card :bordered="false" v-if="jobBatchInfo !==null ">
<a-descriptions title="" :column="3" bordered>
<a-descriptions title="" :column="column" bordered>
<a-descriptions-item label="组名称">
{{ jobBatchInfo.groupName }}
</a-descriptions-item>
@ -38,14 +38,15 @@
</a-descriptions>
</a-card>
<div style="margin: 20px 0; border-left: #f5222d 5px solid; font-size: medium; font-weight: bold">
&nbsp;&nbsp; 任务项列表
<span style="padding-left: 18px">任务项列表</span>
<span style="padding-left: 18px"><a-icon type="sync" @click="()=> this.$refs.JobTaskListRef.refreshTable(this.queryParam)"/></span>
</div>
<JobTaskList ref="JobTaskListRef" />
</div>
</template>
<script>
import { jobBatchDetail, jobTaskList } from '@/api/jobApi'
import { jobBatchDetail } from '@/api/jobApi'
import moment from 'moment'
import enums from '@/utils/jobEnum'
import JobTaskList from './JobTaskList'
@ -56,6 +57,16 @@ export default {
JobTaskList
},
props: {
showHeader: {
type: Boolean,
default: true
},
column: {
type: Number,
default: 3
}
},
data () {
return {
jobBatchInfo: null,
@ -64,13 +75,26 @@ export default {
taskType: enums.taskType,
triggerType: enums.triggerType,
blockStrategy: enums.blockStrategy,
executorType: enums.executorType
executorType: enums.executorType,
queryParam: {}
}
},
created () {
const id = this.$route.query.id
const groupName = this.$route.query.groupName
if (id && groupName) {
this.jobBatchDetail(id)
} else {
if (this.showHeader) {
this.$router.push({ path: '/404' })
}
}
},
methods: {
parseDate (date) {
return moment(date).format('YYYY-MM-DD HH:mm:ss')
},
jobBatchDetail (id) {
jobBatchDetail(id).then(res => {
this.jobBatchInfo = res.data
this.queryParam = {
@ -79,14 +103,6 @@ export default {
}
this.$refs.JobTaskListRef.refreshTable(this.queryParam)
})
} else {
this.$router.push({ path: '/404' })
}
},
methods: {
jobTaskList,
parseDate (date) {
return moment(date).format('YYYY-MM-DD HH:mm:ss')
}
}
}

View File

@ -77,13 +77,6 @@
</a-form>
</div>
<div class="table-operator">
<a-dropdown v-action:edit v-if="selectedRowKeys.length > 0">
<a-menu slot="overlay" @click="onClick">
<a-menu-item key="1"><a-icon type="delete" />删除</a-menu-item>
<a-menu-item key="2"><a-icon type="edit" />更新</a-menu-item>
</a-menu>
<a-button style="margin-left: 8px"> 批量操作 <a-icon type="down" /> </a-button>
</a-dropdown>
</div>
<s-table
@ -97,7 +90,7 @@
:scroll="{ x: 1800 }"
>
<span slot="serial" slot-scope="record">
{{ record.id }}
<a href="#" @click="handlerOpenDrawer(record)">{{ record.id }}</a>
</span>
<span slot="taskBatchStatus" slot-scope="text">
<a-tag :color="taskBatchStatus[text].color">
@ -119,60 +112,39 @@
>
<a href="javascript:;" v-if="record.taskBatchStatus === 1 || record.taskBatchStatus === 2">停止</a>
</a-popconfirm>
<!-- <a-popconfirm-->
<!-- title="是否暂停?"-->
<!-- ok-text="恢复"-->
<!-- cancel-text="取消"-->
<!-- @confirm="handleSuspend(record)"-->
<!-- >-->
<!-- <a href="javascript:;" v-if="record.retryStatus === 0">暂停</a>-->
<!-- </a-popconfirm>-->
<!-- <a-divider type="vertical" v-if="record.retryStatus === 0" />-->
<!-- <a-popconfirm-->
<!-- title="是否恢复?"-->
<!-- ok-text="恢复"-->
<!-- cancel-text="取消"-->
<!-- @confirm="handleRecovery(record)"-->
<!-- >-->
<!-- <a href="javascript:;" v-if="record.retryStatus === 3">恢复</a>-->
<!-- </a-popconfirm>-->
<!-- <a-divider type="vertical" v-if="record.retryStatus === 3" />-->
<!-- <a-popconfirm-->
<!-- title="是否完成?"-->
<!-- ok-text="完成"-->
<!-- cancel-text="取消"-->
<!-- @confirm="handleFinish(record)"-->
<!-- >-->
<!-- <a href="javascript:;" v-if="record.retryStatus !== 1 && record.retryStatus !== 2">完成</a>-->
<!-- </a-popconfirm>-->
<!-- <a-divider type="vertical" v-if="record.retryStatus !== 1 && record.retryStatus !== 2" />-->
<!-- <a-popconfirm-->
<!-- title="是否执行任务?"-->
<!-- ok-text="执行"-->
<!-- cancel-text="取消"-->
<!-- @confirm="handleTrigger(record)"-->
<!-- >-->
<!-- <a href="javascript:;" v-if="record.retryStatus !== 1 && record.retryStatus !== 2">执行</a>-->
<!-- </a-popconfirm>-->
</template>
</span>
</s-table>
<Drawer
title="任务详情"
placement="right"
:width="800"
:visibleAmplify="true"
:visible="openDrawer"
@closeDrawer="onClose"
@handlerAmplify="handleInfo"
>
<job-batch-info ref="jobBatchInfoRef" :showHeader="false" :column="2"/>
</Drawer>
</a-card>
</template>
<script>
import ATextarea from 'ant-design-vue/es/input/TextArea'
import AInput from 'ant-design-vue/es/input/Input'
import { STable } from '@/components'
import { Drawer, STable } from '@/components'
import { jobBatchList, jobNameList, stop } from '@/api/jobApi'
import { getAllGroupNameList } from '@/api/manage'
import JobBatchInfo from '@/views/job/JobBatchInfo'
const enums = require('@/utils/jobEnum')
export default {
name: 'JobBatchList',
components: {
JobBatchInfo,
Drawer,
AInput,
ATextarea,
STable
@ -266,7 +238,9 @@ export default {
},
optionAlertShow: false,
groupNameList: [],
jobNameList: []
jobNameList: [],
openDrawer: false,
currentShowRecord: null
}
},
created () {
@ -285,27 +259,19 @@ export default {
})
},
methods: {
handleNew () {
this.$refs.saveRetryTask.isShow(true, null)
},
handleBatchNew () {
this.$refs.batchSaveRetryTask.isShow(true, null)
},
handleSearch (value) {
console.log(`selected ${value}`)
jobNameList({ keywords: value }).then(res => {
this.jobNameList = res.data
})
},
handleChange (value) {
console.log(`handleChange ${value}`)
// this.queryParam['jobId'] = value
// this.$refs.table.refresh(true)
},
toggleAdvanced () {
this.advanced = !this.advanced
},
handleInfo (record) {
record = record || this.currentShowRecord
this.$router.push({ path: '/job/batch/info', query: { id: record.id, groupName: record.groupName } })
},
handleOk (record) {},
@ -320,51 +286,6 @@ export default {
}
})
},
handleRecovery (record) {
// updateRetryTaskStatus({ id: record.id, groupName: record.groupName, retryStatus: 0 }).then((res) => {
// const { status } = res
// if (status === 0) {
// this.$message.error('')
// } else {
// this.$refs.table.refresh(true)
// this.$message.success('')
// }
// })
},
handleFinish (record) {
// updateRetryTaskStatus({ id: record.id, groupName: record.groupName, retryStatus: 1 }).then((res) => {
// const { status } = res
// if (status === 0) {
// this.$message.error('')
// } else {
// this.$refs.table.refresh(true)
// this.$message.success('')
// }
// })
},
handleTrigger (record) {
// if (record.taskType === 1) {
// manualTriggerRetryTask({ groupName: record.groupName, uniqueIds: [ record.uniqueId ] }).then(res => {
// const { status } = res
// if (status === 0) {
// this.$message.error('')
// } else {
// this.$refs.table.refresh(true)
// this.$message.success('')
// }
// })
// } else {
// manualTriggerCallbackTask({ groupName: record.groupName, uniqueIds: [ record.uniqueId ] }).then(res => {
// const { status } = res
// if (status === 0) {
// this.$message.error('')
// } else {
// this.$refs.table.refresh(true)
// this.$message.success('')
// }
// })
// }
},
refreshTable (v) {
this.$refs.table.refresh(true)
},
@ -372,32 +293,16 @@ export default {
this.selectedRowKeys = selectedRowKeys
this.selectedRows = selectedRows
},
handlerDel () {
// var that = this
// this.$confirm({
// title: '?',
// content: h => <div style="color:red;">!</div>,
// onOk () {
// batchDelete({ groupName: that.selectedRows[0].groupName, ids: that.selectedRowKeys }).then(res => {
// that.$refs.table.refresh(true)
// that.$message.success(`${res.data}`)
// that.selectedRowKeys = []
// })
// },
// onCancel () {
// },
// class: 'test'
// })
handlerOpenDrawer (record) {
this.currentShowRecord = record
this.openDrawer = true
setTimeout(() => {
this.$refs.jobBatchInfoRef.jobBatchDetail(record.id)
}, 200)
},
onClick ({ key }) {
if (key === '2') {
this.$refs.batchUpdateRetryTaskInfo.isShow(true, this.selectedRows, this.selectedRowKeys)
return
}
if (key === '1') {
this.handlerDel()
}
onClose () {
this.openDrawer = false
this.currentShowRecord = null
}
}
}

View File

@ -1,10 +1,10 @@
<template>
<div>
<page-header-wrapper @back="() => $router.go(-1)" style="margin: -24px -1px 0">
<page-header-wrapper @back="() => $router.replace('/job/list')" style="margin: -24px -1px 0" v-if="showHeader">
<div></div>
</page-header-wrapper>
<a-card :bordered="false" v-if="jobInfo !==null ">
<a-descriptions title="" :column="4" bordered>
<a-descriptions title="" :column="column" bordered>
<a-descriptions-item label="组名称">
{{ jobInfo.groupName }}
</a-descriptions-item>
@ -84,7 +84,16 @@ import enums from '@/utils/jobEnum'
export default {
name: 'JobInfo',
components: {
},
props: {
showHeader: {
type: Boolean,
default: true
},
column: {
type: Number,
default: 4
}
},
data () {
return {
@ -101,16 +110,21 @@ export default {
const id = this.$route.query.id
const groupName = this.$route.query.groupName
if (id && groupName) {
getJobDetail(id).then(res => {
this.jobInfo = res.data
})
this.jobDetail(id)
} else {
this.$router.push({ path: '/404' })
if (this.showHeader) {
this.$router.push({ path: '/404' })
}
}
},
methods: {
parseDate (date) {
return moment(date).format('YYYY-MM-DD HH:mm:ss')
},
jobDetail (id) {
getJobDetail(id).then(res => {
this.jobInfo = res.data
})
}
}
}

View File

@ -65,10 +65,8 @@
<a-button type="primary" icon="plus" @click="handleNew()">新增</a-button>
<!-- <a-button type="primary" icon="plus" @click="handleBatchNew()">批量</a-button>-->
<a-dropdown v-action:edit v-if="selectedRowKeys.length > 0">
<a-menu slot="overlay" @click="onClick">
<!-- <a-menu-item key="1"><a-icon type="delete" />删除</a-menu-item>-->
</a-menu>
<!-- <a-button style="margin-left: 8px"> 批量操作 <a-icon type="down" /> </a-button>-->
<!-- <a-menu-item key="1"><a-icon type="delete" />删除</a-menu-item>-->
<!-- <a-button style="margin-left: 8px"> 批量操作 <a-icon type="down" /> </a-button>-->
</a-dropdown>
</div>
@ -83,6 +81,9 @@
<span slot="serial" slot-scope="text, record">
{{ record.id }}
</span>
<span slot="jobName" slot-scope="text, record">
<a href="#" @click="handlerOpenDrawer(record)">{{ text }}</a>
</span>
<span slot="taskType" slot-scope="text">
<a-tag :color="taskType[text].color">
{{ taskType[text].name }}
@ -157,23 +158,38 @@
</span>
</s-table>
<Drawer
title="任务详情"
placement="right"
:width="800"
:visibleAmplify="true"
:visible="openDrawer"
@closeDrawer="onClose"
@handlerAmplify="handleInfo"
>
<job-info ref="jobInfoRef" :showHeader="false" :column="2"/>
</Drawer>
</a-card>
</template>
<script>
import ATextarea from 'ant-design-vue/es/input/TextArea'
import AInput from 'ant-design-vue/es/input/Input'
import { STable } from '@/components'
import { STable, Drawer } from '@/components'
import { delJob, getJobList, triggerJob, updateJobStatus } from '@/api/jobApi'
import { getAllGroupNameList } from '@/api/manage'
import enums from '@/utils/jobEnum'
import JobInfo from '@/views/job/JobInfo'
export default {
name: 'JobList',
components: {
AInput,
ATextarea,
STable
STable,
JobInfo,
Drawer
},
data () {
return {
@ -205,6 +221,7 @@ export default {
{
title: '任务名称',
dataIndex: 'jobName',
scopedSlots: { customRender: 'jobName' },
ellipsis: true,
width: '10%'
},
@ -282,7 +299,9 @@ export default {
},
optionAlertShow: false,
groupNameList: [],
sceneList: []
sceneList: [],
openDrawer: false,
currentShowRecord: null
}
},
created () {
@ -306,6 +325,7 @@ export default {
this.advanced = !this.advanced
},
handleInfo (record) {
record = record || this.currentShowRecord
this.$router.push({ path: '/job/info', query: { id: record.id, groupName: record.groupName } })
},
handleOk (record) {},
@ -360,15 +380,16 @@ export default {
this.selectedRowKeys = selectedRowKeys
this.selectedRows = selectedRows
},
onClick ({ key }) {
if (key === '2') {
this.$refs.batchUpdateRetryTaskInfo.isShow(true, this.selectedRows, this.selectedRowKeys)
return
}
if (key === '1') {
this.handlerDel()
}
handlerOpenDrawer (record) {
this.currentShowRecord = record
this.openDrawer = true
setTimeout(() => {
this.$refs.jobInfoRef.jobDetail(record.id)
}, 200)
},
onClose () {
this.openDrawer = false
this.currentShowRecord = null
}
}
}

View File

@ -283,7 +283,7 @@
{
required: true,
whitespace: true,
message: '分片参数必填',
message: '分片参数必填'
},
],
},

View File

@ -1,11 +1,11 @@
<template>
<div>
<page-header-wrapper @back="() => $router.go(-1)" style="margin: -24px -1px 0">
<page-header-wrapper @back="() => $router.replace('/retry/dead-letter/list')" style="margin: -24px -1px 0" v-if="showHeader">
<div></div>
</page-header-wrapper>
<a-card :bordered="false">
<a-descriptions title="" bordered v-if='retryDealLetterInfo !== null'>
<a-descriptions title="" :column="column" bordered v-if="retryDealLetterInfo !== null">
<a-descriptions-item label="组名称">
{{ retryDealLetterInfo.groupName }}
</a-descriptions-item>
@ -43,10 +43,19 @@
<script>
import { getRetryDeadLetterById } from '@/api/manage'
import moment from 'moment'
export default {
name: 'RetryDeadLetterInfo',
props: {
showHeader: {
type: Boolean,
default: true
},
column: {
type: Number,
default: 3
}
},
data () {
return {
retryDealLetterInfo: null,
@ -66,15 +75,16 @@ export default {
const id = this.$route.query.id
const groupName = this.$route.query.groupName
if (id && groupName) {
this.retryDeadLetterById(id, groupName)
}
},
methods: {
retryDeadLetterById (id, groupName) {
getRetryDeadLetterById(id, { 'groupName': groupName }).then(res => {
this.retryDealLetterInfo = res.data
})
}
},
methods: {
parseDate (date) {
return moment(date).format('YYYY-MM-DD HH:mm:ss')
}
}
}
</script>

View File

@ -66,10 +66,10 @@
:data="loadData"
:alert="options.alert"
:rowSelection="options.rowSelection"
:scroll="{ x: 2000 }"
:scroll="{ x: 1800 }"
>
<span slot="serial" slot-scope="text, record">
{{ record.id }}
<span slot="uniqueId" slot-scope="text, record">
<a href="#" @click="handlerOpenDrawer(record)">{{ text }}</a>
</span>
<span slot="taskType" slot-scope="text">
<a-tag :color="taskType[text].color">
@ -110,6 +110,18 @@
</a-dropdown>
</span>
</s-table>
<Drawer
title="任务详情"
placement="right"
:width="800"
:visibleAmplify="true"
:visible="openDrawer"
@closeDrawer="onClose"
@handlerAmplify="handleInfo"
>
<retry-dead-letter-info ref="retryDeadLetterInfoRef" :showHeader="false" :column="1"/>
</Drawer>
</a-card>
</template>
@ -125,12 +137,15 @@ import {
deleteRetryDeadLetter
} from '@/api/manage'
import { STable } from '@/components'
import { Drawer, STable } from '@/components'
import moment from 'moment'
import RetryDeadLetterInfo from '@/views/task/RetryDeadLetterInfo'
export default {
name: 'RetryDeadLetterList',
components: {
RetryDeadLetterInfo,
Drawer,
AInput,
ATextarea,
STable
@ -157,9 +172,10 @@ export default {
//
columns: [
{
title: '#',
scopedSlots: { customRender: 'serial' },
width: '5%'
title: 'UniqueId',
dataIndex: 'uniqueId',
fixed: 'left',
scopedSlots: { customRender: 'uniqueId' }
},
{
title: '组名称',
@ -171,11 +187,6 @@ export default {
dataIndex: 'sceneName',
ellipsis: true
},
{
title: 'UniqueId',
dataIndex: 'uniqueId',
width: '10%'
},
{
title: '幂等id',
dataIndex: 'idempotentId',
@ -189,15 +200,13 @@ export default {
{
title: '任务类型',
dataIndex: 'taskType',
scopedSlots: { customRender: 'taskType' },
width: '5%'
scopedSlots: { customRender: 'taskType' }
},
{
title: '创建时间',
dataIndex: 'createDt',
sorter: true,
customRender: (text) => moment(text).format('YYYY-MM-DD HH:mm:ss'),
ellipsis: true
customRender: (text) => moment(text).format('YYYY-MM-DD HH:mm:ss')
},
{
title: '操作',
@ -227,7 +236,9 @@ export default {
},
optionAlertShow: false,
groupNameList: [],
sceneList: []
sceneList: [],
currentShowRecord: null,
openDrawer: false
}
},
created () {
@ -267,6 +278,7 @@ export default {
this.advanced = !this.advanced
},
handleInfo (record) {
record = record || this.currentShowRecord
this.$router.push({ path: '/retry/dead-letter/info', query: { id: record.id, groupName: record.groupName } })
},
onClick ({ key }) {
@ -314,6 +326,17 @@ export default {
onSelectChange (selectedRowKeys, selectedRows) {
this.selectedRowKeys = selectedRowKeys
this.selectedRows = selectedRows
},
handlerOpenDrawer (record) {
this.currentShowRecord = record
this.openDrawer = true
setTimeout(() => {
this.$refs.retryDeadLetterInfoRef.retryDeadLetterById(record.id, record.groupName)
}, 200)
},
onClose () {
this.openDrawer = false
this.currentShowRecord = null
}
}
}

View File

@ -1,10 +1,10 @@
<template>
<div>
<page-header-wrapper @back="() => $router.go(-1)" style="margin: -24px -1px 0">
<page-header-wrapper @back="() => $router.replace('/retry/log/list')" style="margin: -24px -1px 0" v-if="showHeader">
<div></div>
</page-header-wrapper>
<a-card :bordered="false">
<a-descriptions title="" bordered v-if="retryInfo !== null">
<a-descriptions title="" :column="column" bordered v-if="retryInfo !== null">
<a-descriptions-item label="组名称">
{{ retryInfo.groupName }}
</a-descriptions-item>
@ -30,7 +30,7 @@
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="创建时间">
{{ retryInfo.createDt }}
{{ retryInfo.createDt }}
</a-descriptions-item>
<a-descriptions-item label="执行器名称" :span="3">
{{ retryInfo.executorName }}
@ -49,7 +49,6 @@
<script>
import { getRetryTaskLogById } from '@/api/manage'
import moment from 'moment'
import { STable } from '@/components'
import RetryTaskLogMessageList from '@/views/task/RetryTaskLogMessageList'
@ -59,6 +58,16 @@ export default {
RetryTaskLogMessageList,
STable
},
props: {
showHeader: {
type: Boolean,
default: true
},
column: {
type: Number,
default: 3
}
},
data () {
return {
retryInfo: null,
@ -82,6 +91,11 @@ export default {
created () {
const id = this.$route.query.id
if (id) {
this.getRetryTaskLogById(id)
}
},
methods: {
getRetryTaskLogById (id) {
getRetryTaskLogById(id).then(res => {
this.retryInfo = res.data
this.queryParam = {
@ -91,11 +105,7 @@ export default {
this.$refs.retryTaskLogMessageListRef.refreshTable(this.queryParam)
})
}
},
methods: {
parseDate (date) {
return moment(date).format('YYYY-MM-DD HH:mm:ss')
}
}
}
</script>

View File

@ -58,8 +58,8 @@
:rowSelection="options.rowSelection"
:scroll="{ x: 2000 }"
>
<span slot="serial" slot-scope="text, record, index">
{{ index + 1 }}
<span slot="uniqueId" slot-scope="text, record">
<a href="#" @click="handlerOpenDrawer(record)"> {{ text }}</a>
</span>
<span slot="taskType" slot-scope="text">
<a-tag :color="taskType[text].color">
@ -77,6 +77,19 @@
</template>
</span>
</s-table>
<Drawer
title="重试日志详情"
placement="right"
:width="800"
:visibleAmplify="true"
:visible="openDrawer"
@closeDrawer="onClose"
@handlerAmplify="handleInfo"
>
<retry-log-info ref="retryLogInfoRef" :showHeader="false" :column="1"/>
</Drawer>
</a-card>
</template>
@ -86,12 +99,15 @@ import ATextarea from 'ant-design-vue/es/input/TextArea'
import AInput from 'ant-design-vue/es/input/Input'
import { getAllGroupNameList, getRetryTaskLogPage, getSceneList } from '@/api/manage'
import { STable } from '@/components'
import { Drawer, STable } from '@/components'
import moment from 'moment'
import RetryLogInfo from '@/views/task/RetryLogInfo'
export default {
name: 'RetryTaskLog',
components: {
RetryLogInfo,
Drawer,
AInput,
ATextarea,
STable
@ -140,15 +156,11 @@ export default {
},
//
columns: [
{
title: '#',
scopedSlots: { customRender: 'serial' },
width: '5%'
},
{
title: 'UniqueId',
dataIndex: 'uniqueId',
width: '10%'
width: '10%',
scopedSlots: { customRender: 'uniqueId' }
},
{
title: '组名称',
@ -227,7 +239,9 @@ export default {
sceneList: [],
groupName: '',
uniqueId: '',
sceneName: ''
sceneName: '',
currentShowRecord: null,
openDrawer: false
}
},
created () {
@ -257,8 +271,19 @@ export default {
this.advanced = !this.advanced
},
handleInfo (record) {
console.log(record)
record = record || this.currentShowRecord
this.$router.push({ path: '/retry/log/info', query: { id: record.id } })
},
handlerOpenDrawer (record) {
this.currentShowRecord = record
this.openDrawer = true
setTimeout(() => {
this.$refs.retryLogInfoRef.getRetryTaskLogById(record.id)
}, 1000)
},
onClose () {
this.openDrawer = false
this.currentShowRecord = null
}
}
}

View File

@ -1,10 +1,10 @@
<template>
<div>
<page-header-wrapper @back="() => $router.go(-1)" style="margin: -24px -1px 0">
<page-header-wrapper @back="() => $router.replace('/retry/list')" style="margin: -24px -1px 0" v-if="showHeader">
<div></div>
</page-header-wrapper>
<a-card :bordered="false" v-if="retryTaskInfo !==null ">
<a-descriptions title="" bordered>
<a-card :bordered="false" v-if="retryTaskInfo !== null">
<a-descriptions title="" :column="column" bordered>
<a-descriptions-item label="组名称">
{{ retryTaskInfo.groupName }}
</a-descriptions-item>
@ -55,7 +55,6 @@
<script>
import { getRetryTaskById } from '@/api/manage'
import moment from 'moment'
import RetryTaskLogMessageList from './RetryTaskLogMessageList'
export default {
@ -63,6 +62,16 @@ export default {
components: {
RetryTaskLogMessageList
},
props: {
showHeader: {
type: Boolean,
default: true
},
column: {
type: Number,
default: 3
}
},
data () {
return {
retryTaskInfo: null,
@ -88,6 +97,15 @@ export default {
const id = this.$route.query.id
const groupName = this.$route.query.groupName
if (id && groupName) {
this.getRetryTaskById(id, groupName)
} else {
if (this.showHeader) {
this.$router.push({ path: '/404' })
}
}
},
methods: {
getRetryTaskById (id, groupName) {
getRetryTaskById(id, { 'groupName': groupName }).then(res => {
this.retryTaskInfo = res.data
this.queryParam = {
@ -96,13 +114,6 @@ export default {
}
this.$refs.retryTaskLogMessageListRef.refreshTable(this.queryParam)
})
} else {
// this.$router.push({ path: '/404' })
}
},
methods: {
parseDate (date) {
return moment(date).format('YYYY-MM-DD HH:mm:ss')
}
}
}

View File

@ -87,8 +87,8 @@
:rowSelection="options.rowSelection"
:scroll="{ x: 2000 }"
>
<span slot="serial" slot-scope="text, record">
{{ record.id }}
<span slot="uniqueId" slot-scope="text, record">
<a href="#" @click="handlerOpenDrawer(record)"> {{ text }}</a>
</span>
<span slot="taskType" slot-scope="text">
<a-tag :color="taskType[text].color">
@ -144,6 +144,18 @@
</span>
</s-table>
<Drawer
title="任务详情"
placement="right"
:width="800"
:visibleAmplify="true"
:visible="openDrawer"
@closeDrawer="onClose"
@handlerAmplify="handleInfo"
>
<retry-task-info ref="retryTaskInfoRef" :showHeader="false" :column="1"/>
</Drawer>
<SaveRetryTask ref="saveRetryTask" @refreshTable="refreshTable"/>
<BatchUpdateRetryTaskInfo ref="batchUpdateRetryTaskInfo" @refreshTable="refreshTable"/>
<BatchSaveRetryTask ref="batchSaveRetryTask" @refreshTable="refreshTable"/>
@ -162,14 +174,17 @@ import {
batchDelete,
manualTriggerCallbackTask, manualTriggerRetryTask
} from '@/api/manage'
import { STable } from '@/components'
import { Drawer, STable } from '@/components'
import SaveRetryTask from './form/SaveRetryTask'
import BatchUpdateRetryTaskInfo from './form/BatchUpdateRetryTaskInfo'
import BatchSaveRetryTask from '@/views/task/form/BatchSaveRetryTask.vue'
import RetryTaskInfo from '@/views/task/RetryTaskInfo'
export default {
name: 'RetryTask',
components: {
Drawer,
RetryTaskInfo,
AInput,
ATextarea,
STable,
@ -217,15 +232,11 @@ export default {
},
//
columns: [
{
title: 'ID',
scopedSlots: { customRender: 'serial' },
fixed: 'left'
},
{
title: 'UniqueId',
dataIndex: 'uniqueId',
width: '9%'
fixed: 'left',
scopedSlots: { customRender: 'uniqueId' }
},
{
title: '组名称',
@ -310,7 +321,9 @@ export default {
},
optionAlertShow: false,
groupNameList: [],
sceneList: []
sceneList: [],
openDrawer: false,
currentShowRecord: null
}
},
created () {
@ -343,6 +356,7 @@ export default {
this.advanced = !this.advanced
},
handleInfo (record) {
record = record || this.currentShowRecord
this.$router.push({ path: '/retry/info', query: { id: record.id, groupName: record.groupName } })
},
handleOk (record) {},
@ -435,6 +449,17 @@ export default {
if (key === '1') {
this.handlerDel()
}
},
handlerOpenDrawer (record) {
this.currentShowRecord = record
this.openDrawer = true
setTimeout(() => {
this.$refs.retryTaskInfoRef.getRetryTaskById(record.id, record.groupName)
}, 200)
},
onClose () {
this.openDrawer = false
this.currentShowRecord = null
}
}
}

View File

@ -1,7 +1,8 @@
<template>
<div>
<div style="margin: 20px 0; border-left: #f5222d 5px solid; font-size: medium; font-weight: bold">
&nbsp;&nbsp; 调用日志详情 (总调度次数: {{ total }})
<span style="padding-left: 18px">调用日志详情 (总调度次数: {{ total }})</span>
<span style="padding-left: 18px"><a-icon type="sync" @click="()=> this.$refs.table.refresh(true)"/></span>
</div>
<a-card>
<s-table
@ -64,7 +65,6 @@ export default {
queryParam: {},
// Promise
loadData: parameter => {
console.log(this.queryParam)
if (!this.queryParam['groupName']) {
return
}

View File

@ -1,85 +1,103 @@
<template>
<a-card class="card" title="组配置" :bordered="false">
<a-form @submit="handleSubmit" :form="form" :body-style="{padding: '24px 32px'}" v-bind="formItemLayout" >
<a-form-item>
<a-input
hidden
v-decorator="['id']" />
</a-form-item>
<a-form-item
label="用户名">
<a-input
placeholder="请输入用户名"
v-decorator="[
'username',
{rules: [{ required: true, message: '请输入用户名', whitespace: true}]}
]" />
</a-form-item>
<a-form-item
label="密码">
<a-input
placeholder="请输入密码"
v-decorator="[
'password',
{rules: [{ required: formType === 'create', message: '请输入密码', whitespace: true}]}
]"/>
</a-form-item>
<a-form-item
label="角色">
<a-select
placeholder="请选择角色"
v-decorator="[
'role',
{rules: [{ required: true, message: '请选择角色'}]}
]"
@change="value => handleChange(value)">
<a-select-option value="1">普通用户</a-select-option>
<a-select-option value="2">管理员</a-select-option>
</a-select>
</a-form-item>
<a-form-item
label="命名空间"
v-if="role !== '2'">
<a-select
mode="tags"
style="width: 100%"
:token-separators="[',']"
@change="value => handleNamespacesIdChange(value)"
v-decorator="[
'namespaceIds',
{rules: [{ required: true, message: '请分配命名空间'}]}
]">
<a-select-option v-for="(item, index) in namespaceList" :key="index" :value="item.uniqueId">
{{ item.name }} ({{ item.uniqueId }})
</a-select-option>
</a-select>
</a-form-item>
<a-form-item
label="组"
v-if="role !== '2'">
<a-select
mode="tags"
style="width: 100%"
:token-separators="[',']"
v-decorator="[
'permissions',
{rules: [{ required: true, message: '请分配组'}]}
]">
<a-select-option v-for="(item, index) in groupNameList" :key="index" :value="item.groupName + '@@'+ item.namespaceId">
{{ item.groupName }} ({{ item.namespaceId }})
</a-select-option>
</a-select>
</a-form-item>
<a-form-item
:wrapper-col="{
xs: { span: 24, offset: 0 },
sm: { span: 16, offset: 8 },
lg: { span: 7 }
}">
<a-button htmlType="submit" type="primary">提交</a-button>
</a-form-item>
</a-form>
</a-card>
<div>
<page-header-wrapper :content="formType === 'create' ? '新增用户':'更新用户信息'" @back="() => $router.go(-1)" style="margin: -24px -1px 0">
<div></div>
</page-header-wrapper>
<a-card class="card" title="" :bordered="false">
<a-form @submit="handleSubmit" :form="form" :body-style="{padding: '24px 32px'}" v-bind="formItemLayout" :rules="rules">
<a-form-item>
<a-input
hidden
v-decorator="['id']" />
</a-form-item>
<a-form-item
label="用户名">
<a-input
placeholder="请输入用户名"
v-decorator="[
'username',
{rules: [{ required: true, message: '请输入用户名', whitespace: true}]}
]" />
</a-form-item>
<a-form-item
label="密码">
<a-input
placeholder="请输入密码"
type="password"
autocomplete="off"
v-decorator="[
'password',
{rules: [{ required: formType === 'create', message: '请输入密码', whitespace: true}, {validator: validatePass, validateTrigger: ['change', 'blur']}]}
]"/>
</a-form-item>
<a-form-item
label="确认密码">
<a-input
placeholder="请输入确认密码"
type="password"
autocomplete="off"
v-decorator="[
'checkPassword',
{rules: [{ required: formType === 'create', message: '请输入密码', whitespace: true}, {validator: validateCheckPass, validateTrigger: ['change', 'blur']}]}
]"/>
</a-form-item>
<a-form-item
label="角色">
<a-select
placeholder="请选择角色"
v-decorator="[
'role',
{rules: [{ required: true, message: '请选择角色'}]}
]"
@change="value => handleChange(value)">
<a-select-option value="1">普通用户</a-select-option>
<a-select-option value="2">管理员</a-select-option>
</a-select>
</a-form-item>
<a-form-item
label="命名空间"
v-if="role !== '2'">
<a-select
mode="tags"
style="width: 100%"
:token-separators="[',']"
@change="value => handleNamespacesIdChange(value)"
v-decorator="[
'namespaceIds',
{rules: [{ required: true, message: '请分配命名空间'}]}
]">
<a-select-option v-for="(item, index) in namespaceList" :key="index" :value="item.uniqueId">
{{ item.name }} ({{ item.uniqueId }})
</a-select-option>
</a-select>
</a-form-item>
<a-form-item
label="组"
v-if="role !== '2'">
<a-select
mode="tags"
style="width: 100%"
:token-separators="[',']"
v-decorator="[
'permissions',
{rules: [{ required: true, message: '请分配组'}]}
]">
<a-select-option v-for="(item, index) in groupNameList" :key="index" :value="item.groupName + '@@'+ item.namespaceId">
{{ item.groupName }} ({{ item.namespaceId }})
</a-select-option>
</a-select>
</a-form-item>
<a-form-item
:wrapper-col="{
xs: { span: 24, offset: 0 },
sm: { span: 16, offset: 8 },
lg: { span: 7 }
}">
<a-button htmlType="submit" type="primary">提交</a-button>
</a-form-item>
</a-form>
</a-card>
</div>
</template>
@ -161,19 +179,31 @@ export default {
}
})
},
validate (rule, value, callback) {
const regex = /^user-(.*)$/
if (!regex.test(value)) {
callback(new Error('需要以 user- 开头'))
validatePass (rule, value, callback) {
if (value === '') {
callback(new Error('请输入密码'))
} else {
if (this.form.getFieldValue('checkPassword') !== '') {
this.$refs.form.validateField('checkPassword')
}
callback()
}
},
validateCheckPass (rule, value, callback) {
if (value === '') {
callback(new Error('请再次输入密码'))
} else if (value !== this.form.getFieldValue('password')) {
callback(new Error('两次密码不匹配!'))
} else {
callback()
}
callback()
},
loadEditInfo (data) {
this.formType = 'edit'
const { form } = this
// ajax
new Promise((resolve) => {
setTimeout(resolve, 1500)
setTimeout(resolve, 100)
}).then(() => {
const formData = pick(data, ['id', 'username', 'role', 'permissions', 'namespaceIds'])
formData.role = formData.role.toString()

View File

@ -43,7 +43,11 @@
:rowSelection="options.rowSelection"
>
<span slot="serial" slot-scope="text, record">
<a href="#" @click="handlerDetail">{{ record.id }}</a>
{{ record.id }}
</span>
<span slot="username" slot-scope="text, record">
<a href="#" @click="handlerDetail(record)" v-if="record.role === 1">{{ text }}</a>
<span v-else>{{ text }}</span>
</span>
<span slot="groupNameList" slot-scope="text, record">
{{ record.role === 2 ? '所有组' : text.toString() }}
@ -66,33 +70,23 @@
</template>
</span>
</s-table>
<a-drawer
title="Basic Drawer"
<Drawer
title="用户权限"
placement="right"
:width="800"
:visibleAmplify="false"
:visible="openDrawer"
@close="onClose"
@closeDrawer="onClose"
>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
<div
:style="{
position: 'absolute',
top: 0,
right: 0,
marginRight: '5px',
width: '100%',
borderTop: '1px solid #e9e9e9',
padding: '10px 16px',
background: '#fff',
// textAlign: 'right',
zIndex: 1,
}"
>
<a-button style='background: none'><a-icon type="arrows-alt" style='size: 16px;'/></a-button>
</div></a-drawer>
<a-descriptions :column="8" bordered>
<a-descriptions-item :label="item.namespaceName + ' (' + item.namespaceId + ')'" :key="item.namespaceId" :span="8" v-for="item in systemPermissions">
<a-tag v-for="item in item.groupNames" :key="item">
{{ item }}
</a-tag>
</a-descriptions-item>
</a-descriptions>
</Drawer>
</a-card>
</template>
@ -102,12 +96,15 @@ import ATextarea from 'ant-design-vue/es/input/TextArea'
import AInput from 'ant-design-vue/es/input/Input'
import moment from 'moment'
//
import { getUserPage, delUser } from '@/api/manage'
import { STable } from '@/components'
import { getUserPage, delUser, getSystemUserPermissionByUserId } from '@/api/manage'
import { Drawer, STable } from '@/components'
import RetryDeadLetterInfo from '@/views/task/RetryDeadLetterInfo.vue'
export default {
name: 'TableListWrapper',
components: {
Drawer,
RetryDeadLetterInfo,
AInput,
ATextarea,
STable
@ -125,29 +122,20 @@ export default {
columns: [
{
title: '#',
width: '5%',
scopedSlots: { customRender: 'serial' }
},
{
title: '用户名',
width: '12%',
dataIndex: 'username'
dataIndex: 'username',
scopedSlots: { customRender: 'username' }
},
{
title: '角色',
dataIndex: 'role',
width: '10%',
scopedSlots: { customRender: 'role' }
},
{
title: '权限',
dataIndex: 'groupNameList',
width: '45%',
scopedSlots: { customRender: 'groupNameList' }
},
{
title: '更新时间',
width: '18%',
dataIndex: 'updateDt',
customRender: (text) => moment(text).format('YYYY-MM-DD HH:mm:ss')
},
@ -178,7 +166,8 @@ export default {
}
},
optionAlertShow: false,
openDrawer: false
openDrawer: false,
systemPermissions: []
}
},
filters: {
@ -204,11 +193,14 @@ export default {
this.record = ''
this.currentComponet = 'List'
},
handlerDetail () {
this.openDrawer = true
handlerDetail (record) {
getSystemUserPermissionByUserId(record.id).then(res => {
this.systemPermissions = res.data
this.openDrawer = true
})
},
onClose () {
this.openDrawer = false
this.openDrawer = !this.openDrawer
}
},
watch: {
@ -219,3 +211,31 @@ export default {
}
}
</script>
<style>
.open {
position: absolute;
top: 0;
right: 0;
z-index: 10;
display: block;
width: 56px;
height: 56px;
padding: 0;
color: rgba(0, 0, 0, 0.45);
font-weight: 700;
font-size: 16px;
font-style: normal;
line-height: 56px;
text-align: center;
text-transform: none;
text-decoration: none;
background: transparent;
border: 0;
outline: 0;
cursor: pointer;
-webkit-transition: color 0.3s;
transition: color 0.3s;
text-rendering: auto;
padding-right: 70px;
}
</style>