feat(sj_1.0.0): 通知人导入导出完成

This commit is contained in:
opensnail 2024-05-31 11:27:19 +08:00
parent 7ee0253163
commit 39cd48b3d8
10 changed files with 261 additions and 88 deletions

View File

@ -1,16 +1,25 @@
package com.aizuda.snailjob.server.web.controller; package com.aizuda.snailjob.server.web.controller;
import com.aizuda.snailjob.common.core.annotation.OriginalControllerReturnValue;
import com.aizuda.snailjob.server.web.annotation.LoginRequired; import com.aizuda.snailjob.server.web.annotation.LoginRequired;
import com.aizuda.snailjob.server.web.annotation.RoleEnum;
import com.aizuda.snailjob.server.web.model.base.PageResult; import com.aizuda.snailjob.server.web.model.base.PageResult;
import com.aizuda.snailjob.server.web.model.request.ExportNotifyRecipientVO;
import com.aizuda.snailjob.server.web.model.request.NotifyRecipientQueryVO; import com.aizuda.snailjob.server.web.model.request.NotifyRecipientQueryVO;
import com.aizuda.snailjob.server.web.model.request.NotifyRecipientRequestVO; import com.aizuda.snailjob.server.web.model.request.NotifyRecipientRequestVO;
import com.aizuda.snailjob.server.web.model.response.CommonLabelValueResponseVO; import com.aizuda.snailjob.server.web.model.response.CommonLabelValueResponseVO;
import com.aizuda.snailjob.server.web.model.response.NotifyRecipientResponseVO; import com.aizuda.snailjob.server.web.model.response.NotifyRecipientResponseVO;
import com.aizuda.snailjob.server.web.service.NotifyRecipientService; import com.aizuda.snailjob.server.web.service.NotifyRecipientService;
import com.aizuda.snailjob.server.web.util.ExportUtils;
import com.aizuda.snailjob.server.web.util.ImportUtils;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -57,4 +66,17 @@ public class NotifyRecipientController {
public Boolean batchDeleteByIds(@RequestBody Set<Long> ids) { public Boolean batchDeleteByIds(@RequestBody Set<Long> ids) {
return notifyRecipientService.batchDeleteByIds(ids); return notifyRecipientService.batchDeleteByIds(ids);
} }
@PostMapping(value = "/import", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@LoginRequired(role = RoleEnum.ADMIN)
public void importScene(@RequestPart("file") MultipartFile file) throws IOException {
notifyRecipientService.importNotifyRecipient(ImportUtils.parseList(file, NotifyRecipientRequestVO.class));
}
@PostMapping("/export")
@LoginRequired
@OriginalControllerReturnValue
public ResponseEntity<String> exportGroup(@RequestBody ExportNotifyRecipientVO exportNotifyRecipientVO) {
return ExportUtils.doExport(notifyRecipientService.exportNotifyRecipient(exportNotifyRecipientVO));
}
} }

View File

@ -0,0 +1,20 @@
package com.aizuda.snailjob.server.web.model.request;
import lombok.Data;
import java.util.Set;
/**
* @author: opensnail
* @date : 2024-05-31
* @since : sj_1.0.0
*/
@Data
public class ExportNotifyRecipientVO {
private Set<Long> notifyRecipientIds;
private Integer notifyType;
private String recipientName;
}

View File

@ -2,12 +2,14 @@ package com.aizuda.snailjob.server.web.model.request;
import com.aizuda.snailjob.server.web.model.base.BaseQueryVO; import com.aizuda.snailjob.server.web.model.base.BaseQueryVO;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
/** /**
* @author opensnail * @author opensnail
* @date 2024-04-17 21:26:22 * @date 2024-04-17 21:26:22
* @since sj_1.0.0 * @since sj_1.0.0
*/ */
@EqualsAndHashCode(callSuper = true)
@Data @Data
public class NotifyRecipientQueryVO extends BaseQueryVO { public class NotifyRecipientQueryVO extends BaseQueryVO {

View File

@ -1,10 +1,13 @@
package com.aizuda.snailjob.server.web.service; package com.aizuda.snailjob.server.web.service;
import com.aizuda.snailjob.server.web.model.base.PageResult; import com.aizuda.snailjob.server.web.model.base.PageResult;
import com.aizuda.snailjob.server.web.model.request.ExportNotifyRecipientVO;
import com.aizuda.snailjob.server.web.model.request.NotifyRecipientQueryVO; import com.aizuda.snailjob.server.web.model.request.NotifyRecipientQueryVO;
import com.aizuda.snailjob.server.web.model.request.NotifyRecipientRequestVO; import com.aizuda.snailjob.server.web.model.request.NotifyRecipientRequestVO;
import com.aizuda.snailjob.server.web.model.response.CommonLabelValueResponseVO; import com.aizuda.snailjob.server.web.model.response.CommonLabelValueResponseVO;
import com.aizuda.snailjob.server.web.model.response.NotifyRecipientResponseVO; import com.aizuda.snailjob.server.web.model.response.NotifyRecipientResponseVO;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -25,4 +28,8 @@ public interface NotifyRecipientService {
List<CommonLabelValueResponseVO> getNotifyRecipientList(); List<CommonLabelValueResponseVO> getNotifyRecipientList();
Boolean batchDeleteByIds(Set<Long> ids); Boolean batchDeleteByIds(Set<Long> ids);
void importNotifyRecipient(@Valid @NotEmpty(message = "导入数据不能为空") List<NotifyRecipientRequestVO> notifyRecipientRequestVOS);
String exportNotifyRecipient(ExportNotifyRecipientVO exportNotifyRecipientVO);
} }

View File

@ -32,4 +32,7 @@ public interface NotifyRecipientConverter {
@Mapping(target = "value", source = "id") @Mapping(target = "value", source = "id")
}) })
CommonLabelValueResponseVO convert(NotifyRecipient notifyRecipient); CommonLabelValueResponseVO convert(NotifyRecipient notifyRecipient);
List<NotifyRecipientRequestVO> toNotifyRecipientRequestVOs(List<NotifyRecipient> notifyRecipients);
} }

View File

@ -0,0 +1,50 @@
package com.aizuda.snailjob.server.web.service.handler;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import com.aizuda.snailjob.common.core.util.StreamUtils;
import com.aizuda.snailjob.server.common.exception.SnailJobServerException;
import com.aizuda.snailjob.template.datasource.access.AccessTemplate;
import com.aizuda.snailjob.template.datasource.persistence.po.GroupConfig;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Sets;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Set;
/**
* @author: opensnail
* @date : 2024-05-31
* @since : sj_1.0.0
*/
@Component
@RequiredArgsConstructor
public class GroupHandler {
private final AccessTemplate accessTemplate;
/**
* 校验组是否存在
*
* @param groupNameSet 待校验的组
* @param namespaceId 空间
*/
public void validateGroupExistence(Set<String> groupNameSet, String namespaceId) {
Assert.notEmpty(groupNameSet, () -> new SnailJobServerException("组不能为空"));
List<GroupConfig> groupConfigs = accessTemplate.getGroupConfigAccess()
.list(new LambdaQueryWrapper<GroupConfig>()
.select(GroupConfig::getGroupName)
.eq(GroupConfig::getNamespaceId, namespaceId)
.in(GroupConfig::getGroupName, groupNameSet)
);
Set<String> notExistedGroupNameSet = Sets.difference(groupNameSet,
StreamUtils.toSet(groupConfigs, GroupConfig::getGroupName));
Assert.isTrue(CollUtil.isEmpty(notExistedGroupNameSet),
() -> new SnailJobServerException("组:{}不存在", notExistedGroupNameSet));
}
}

View File

@ -27,6 +27,7 @@ import com.aizuda.snailjob.server.web.model.response.JobResponseVO;
import com.aizuda.snailjob.server.web.service.JobService; import com.aizuda.snailjob.server.web.service.JobService;
import com.aizuda.snailjob.server.web.service.convert.JobConverter; import com.aizuda.snailjob.server.web.service.convert.JobConverter;
import com.aizuda.snailjob.server.web.service.convert.JobResponseVOConverter; import com.aizuda.snailjob.server.web.service.convert.JobResponseVOConverter;
import com.aizuda.snailjob.server.web.service.handler.GroupHandler;
import com.aizuda.snailjob.server.web.util.UserSessionUtils; import com.aizuda.snailjob.server.web.util.UserSessionUtils;
import com.aizuda.snailjob.template.datasource.access.AccessTemplate; import com.aizuda.snailjob.template.datasource.access.AccessTemplate;
import com.aizuda.snailjob.template.datasource.persistence.mapper.JobMapper; import com.aizuda.snailjob.template.datasource.persistence.mapper.JobMapper;
@ -48,6 +49,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
/** /**
* @author opensnail * @author opensnail
@ -62,9 +64,9 @@ public class JobServiceImpl implements JobService {
private final SystemProperties systemProperties; private final SystemProperties systemProperties;
private final JobMapper jobMapper; private final JobMapper jobMapper;
@Lazy
private final JobPrepareHandler terminalJobPrepareHandler; private final JobPrepareHandler terminalJobPrepareHandler;
private final AccessTemplate accessTemplate; private final AccessTemplate accessTemplate;
private final GroupHandler groupHandler;
private static Long calculateNextTriggerAt(final JobRequestVO jobRequestVO, Long time) { private static Long calculateNextTriggerAt(final JobRequestVO jobRequestVO, Long time) {
if (Objects.equals(jobRequestVO.getTriggerType(), SystemConstants.WORKFLOW_TRIGGER_TYPE)) { if (Objects.equals(jobRequestVO.getTriggerType(), SystemConstants.WORKFLOW_TRIGGER_TYPE)) {
@ -84,14 +86,15 @@ public class JobServiceImpl implements JobService {
PageDTO<Job> pageDTO = new PageDTO<>(queryVO.getPage(), queryVO.getSize()); PageDTO<Job> pageDTO = new PageDTO<>(queryVO.getPage(), queryVO.getSize());
UserSessionVO userSessionVO = UserSessionUtils.currentUserSession(); UserSessionVO userSessionVO = UserSessionUtils.currentUserSession();
PageDTO<Job> selectPage = jobMapper.selectPage(pageDTO, PageDTO<Job> selectPage = jobMapper.selectPage(pageDTO,
new LambdaQueryWrapper<Job>() new LambdaQueryWrapper<Job>()
.eq(Job::getNamespaceId, userSessionVO.getNamespaceId()) .eq(Job::getNamespaceId, userSessionVO.getNamespaceId())
.in(userSessionVO.isUser(), Job::getGroupName, userSessionVO.getGroupNames()) .in(userSessionVO.isUser(), Job::getGroupName, userSessionVO.getGroupNames())
.eq(StrUtil.isNotBlank(queryVO.getGroupName()), Job::getGroupName, queryVO.getGroupName()) .eq(StrUtil.isNotBlank(queryVO.getGroupName()), Job::getGroupName, queryVO.getGroupName())
.likeRight(StrUtil.isNotBlank(queryVO.getJobName()), Job::getJobName, StrUtil.trim(queryVO.getJobName())) .likeRight(StrUtil.isNotBlank(queryVO.getJobName()), Job::getJobName,
.eq(Objects.nonNull(queryVO.getJobStatus()), Job::getJobStatus, queryVO.getJobStatus()) StrUtil.trim(queryVO.getJobName()))
.eq(Job::getDeleted, StatusEnum.NO.getStatus()) .eq(Objects.nonNull(queryVO.getJobStatus()), Job::getJobStatus, queryVO.getJobStatus())
.orderByDesc(Job::getId)); .eq(Job::getDeleted, StatusEnum.NO.getStatus())
.orderByDesc(Job::getId));
List<JobResponseVO> jobResponseList = JobResponseVOConverter.INSTANCE.convertList(selectPage.getRecords()); List<JobResponseVO> jobResponseList = JobResponseVOConverter.INSTANCE.convertList(selectPage.getRecords());
@ -114,16 +117,16 @@ public class JobServiceImpl implements JobService {
UserSessionVO userSessionVO = UserSessionUtils.currentUserSession(); UserSessionVO userSessionVO = UserSessionUtils.currentUserSession();
PageDTO<Job> selectPage = jobMapper.selectPage( PageDTO<Job> selectPage = jobMapper.selectPage(
new PageDTO<>(1, 20), new PageDTO<>(1, 20),
new LambdaQueryWrapper<Job>() new LambdaQueryWrapper<Job>()
.select(Job::getId, Job::getJobName) .select(Job::getId, Job::getJobName)
.eq(Job::getNamespaceId, userSessionVO.getNamespaceId()) .eq(Job::getNamespaceId, userSessionVO.getNamespaceId())
.likeRight(StrUtil.isNotBlank(keywords), Job::getJobName, StrUtil.trim(keywords)) .likeRight(StrUtil.isNotBlank(keywords), Job::getJobName, StrUtil.trim(keywords))
.eq(StrUtil.isNotBlank(groupName), Job::getGroupName, groupName) .eq(StrUtil.isNotBlank(groupName), Job::getGroupName, groupName)
.eq(Objects.nonNull(jobId), Job::getId, jobId) .eq(Objects.nonNull(jobId), Job::getId, jobId)
.eq(Job::getDeleted, StatusEnum.NO.getStatus()) .eq(Job::getDeleted, StatusEnum.NO.getStatus())
// SQLServer 分页必须 ORDER BY // SQLServer 分页必须 ORDER BY
.orderByAsc(Job::getId)); .orderByAsc(Job::getId));
return JobResponseVOConverter.INSTANCE.convertList(selectPage.getRecords()); return JobResponseVOConverter.INSTANCE.convertList(selectPage.getRecords());
} }
@ -133,7 +136,7 @@ public class JobServiceImpl implements JobService {
Job job = JobConverter.INSTANCE.convert(jobRequestVO); Job job = JobConverter.INSTANCE.convert(jobRequestVO);
job.setResident(isResident(jobRequestVO)); job.setResident(isResident(jobRequestVO));
job.setBucketIndex(HashUtil.bkdrHash(jobRequestVO.getGroupName() + jobRequestVO.getJobName()) job.setBucketIndex(HashUtil.bkdrHash(jobRequestVO.getGroupName() + jobRequestVO.getJobName())
% systemProperties.getBucketTotal()); % systemProperties.getBucketTotal());
job.setNextTriggerAt(calculateNextTriggerAt(jobRequestVO, DateUtils.toNowMilli())); job.setNextTriggerAt(calculateNextTriggerAt(jobRequestVO, DateUtils.toNowMilli()));
job.setNamespaceId(UserSessionUtils.currentUserSession().getNamespaceId()); job.setNamespaceId(UserSessionUtils.currentUserSession().getNamespaceId());
job.setId(null); job.setId(null);
@ -157,18 +160,18 @@ public class JobServiceImpl implements JobService {
job.setNextTriggerAt(0L); job.setNextTriggerAt(0L);
// 非常驻任务 > 非常驻任务 // 非常驻任务 > 非常驻任务
} else if (Objects.equals(job.getResident(), StatusEnum.NO.getStatus()) && Objects.equals( } else if (Objects.equals(job.getResident(), StatusEnum.NO.getStatus()) && Objects.equals(
updateJob.getResident(), updateJob.getResident(),
StatusEnum.NO.getStatus())) { StatusEnum.NO.getStatus())) {
updateJob.setNextTriggerAt(calculateNextTriggerAt(jobRequestVO, DateUtils.toNowMilli())); updateJob.setNextTriggerAt(calculateNextTriggerAt(jobRequestVO, DateUtils.toNowMilli()));
} else if (Objects.equals(job.getResident(), StatusEnum.YES.getStatus()) && Objects.equals( } else if (Objects.equals(job.getResident(), StatusEnum.YES.getStatus()) && Objects.equals(
updateJob.getResident(), StatusEnum.NO.getStatus())) { updateJob.getResident(), StatusEnum.NO.getStatus())) {
// 常驻任务的触发时间 // 常驻任务的触发时间
long time = Optional.ofNullable(ResidentTaskCache.get(jobRequestVO.getId())) long time = Optional.ofNullable(ResidentTaskCache.get(jobRequestVO.getId()))
.orElse(DateUtils.toNowMilli()); .orElse(DateUtils.toNowMilli());
updateJob.setNextTriggerAt(calculateNextTriggerAt(jobRequestVO, time)); updateJob.setNextTriggerAt(calculateNextTriggerAt(jobRequestVO, time));
// 老的是不是常驻任务 新的是常驻任务 需要使用当前时间计算下次触发时间 // 老的是不是常驻任务 新的是常驻任务 需要使用当前时间计算下次触发时间
} else if (Objects.equals(job.getResident(), StatusEnum.NO.getStatus()) && Objects.equals( } else if (Objects.equals(job.getResident(), StatusEnum.NO.getStatus()) && Objects.equals(
updateJob.getResident(), StatusEnum.YES.getStatus())) { updateJob.getResident(), StatusEnum.YES.getStatus())) {
updateJob.setNextTriggerAt(DateUtils.toNowMilli()); updateJob.setNextTriggerAt(DateUtils.toNowMilli());
} }
@ -221,12 +224,13 @@ public class JobServiceImpl implements JobService {
Assert.notNull(job, () -> new SnailJobServerException("job can not be null.")); Assert.notNull(job, () -> new SnailJobServerException("job can not be null."));
long count = accessTemplate.getGroupConfigAccess().count(new LambdaQueryWrapper<GroupConfig>() long count = accessTemplate.getGroupConfigAccess().count(new LambdaQueryWrapper<GroupConfig>()
.eq(GroupConfig::getGroupName, job.getGroupName()) .eq(GroupConfig::getGroupName, job.getGroupName())
.eq(GroupConfig::getNamespaceId, job.getNamespaceId()) .eq(GroupConfig::getNamespaceId, job.getNamespaceId())
.eq(GroupConfig::getGroupStatus, StatusEnum.YES.getStatus()) .eq(GroupConfig::getGroupStatus, StatusEnum.YES.getStatus())
); );
Assert.isTrue(count > 0, () -> new SnailJobServerException("组:[{}]已经关闭,不支持手动执行.", job.getGroupName())); Assert.isTrue(count > 0,
() -> new SnailJobServerException("组:[{}]已经关闭,不支持手动执行.", job.getGroupName()));
JobTaskPrepareDTO jobTaskPrepare = JobTaskConverter.INSTANCE.toJobTaskPrepare(job); JobTaskPrepareDTO jobTaskPrepare = JobTaskConverter.INSTANCE.toJobTaskPrepare(job);
// 设置now表示立即执行 // 设置now表示立即执行
jobTaskPrepare.setNextTriggerAt(DateUtils.toNowMilli()); jobTaskPrepare.setNextTriggerAt(DateUtils.toNowMilli());
@ -241,12 +245,12 @@ public class JobServiceImpl implements JobService {
public List<JobResponseVO> getJobList(String groupName) { public List<JobResponseVO> getJobList(String groupName) {
String namespaceId = UserSessionUtils.currentUserSession().getNamespaceId(); String namespaceId = UserSessionUtils.currentUserSession().getNamespaceId();
List<Job> jobs = jobMapper.selectList( List<Job> jobs = jobMapper.selectList(
new LambdaQueryWrapper<Job>() new LambdaQueryWrapper<Job>()
.select(Job::getId, Job::getJobName) .select(Job::getId, Job::getJobName)
.eq(Job::getNamespaceId, namespaceId) .eq(Job::getNamespaceId, namespaceId)
.eq(Job::getGroupName, groupName) .eq(Job::getGroupName, groupName)
.eq(Job::getDeleted, StatusEnum.NO.getStatus()) .eq(Job::getDeleted, StatusEnum.NO.getStatus())
.orderByDesc(Job::getCreateDt)); .orderByDesc(Job::getCreateDt));
List<JobResponseVO> jobResponseList = JobResponseVOConverter.INSTANCE.convertList(jobs); List<JobResponseVO> jobResponseList = JobResponseVOConverter.INSTANCE.convertList(jobs);
return jobResponseList; return jobResponseList;
} }
@ -254,6 +258,10 @@ public class JobServiceImpl implements JobService {
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@Override @Override
public void importJobs(List<JobRequestVO> requestList) { public void importJobs(List<JobRequestVO> requestList) {
String namespaceId = UserSessionUtils.currentUserSession().getNamespaceId();
groupHandler.validateGroupExistence(
StreamUtils.toSet(requestList, JobRequestVO::getJobName), namespaceId
);
requestList.forEach(this::saveJob); requestList.forEach(this::saveJob);
} }
@ -263,23 +271,25 @@ public class JobServiceImpl implements JobService {
List<JobRequestVO> requestList = new ArrayList<>(); List<JobRequestVO> requestList = new ArrayList<>();
PartitionTaskUtils.process(startId -> { PartitionTaskUtils.process(startId -> {
List<Job> jobList = jobMapper.selectPage(new PageDTO<>(0, 100), List<Job> jobList = jobMapper.selectPage(new PageDTO<>(0, 100),
new LambdaQueryWrapper<Job>() new LambdaQueryWrapper<Job>()
.eq(Job::getNamespaceId, namespaceId) .eq(Job::getNamespaceId, namespaceId)
.eq(StrUtil.isNotBlank(exportJobVO.getGroupName()), Job::getGroupName, exportJobVO.getGroupName()) .eq(StrUtil.isNotBlank(exportJobVO.getGroupName()), Job::getGroupName, exportJobVO.getGroupName())
.likeRight(StrUtil.isNotBlank(exportJobVO.getJobName()), Job::getJobName, StrUtil.trim(exportJobVO.getJobName())) .likeRight(StrUtil.isNotBlank(exportJobVO.getJobName()), Job::getJobName,
.eq(Objects.nonNull(exportJobVO.getJobStatus()), Job::getJobStatus, exportJobVO.getJobStatus()) StrUtil.trim(exportJobVO.getJobName()))
.in(CollUtil.isNotEmpty(exportJobVO.getJobIds()), Job::getId, exportJobVO.getJobIds()) .eq(Objects.nonNull(exportJobVO.getJobStatus()), Job::getJobStatus, exportJobVO.getJobStatus())
.eq(Job::getDeleted, StatusEnum.NO.getStatus()) .in(CollUtil.isNotEmpty(exportJobVO.getJobIds()), Job::getId, exportJobVO.getJobIds())
.gt(Job::getId, startId) .eq(Job::getDeleted, StatusEnum.NO.getStatus())
.orderByAsc(Job::getId) .gt(Job::getId, startId)
).getRecords(); .orderByAsc(Job::getId)
return StreamUtils.toList(jobList, JobPartitionTask::new); ).getRecords();
}, return StreamUtils.toList(jobList, JobPartitionTask::new);
partitionTasks -> { },
List<JobPartitionTask> jobPartitionTasks = (List<JobPartitionTask>) partitionTasks; partitionTasks -> {
requestList.addAll(JobConverter.INSTANCE.convertList(StreamUtils.toList(jobPartitionTasks, JobPartitionTask::getJob))); List<JobPartitionTask> jobPartitionTasks = (List<JobPartitionTask>) partitionTasks;
}, 0); requestList.addAll(
JobConverter.INSTANCE.convertList(StreamUtils.toList(jobPartitionTasks, JobPartitionTask::getJob)));
}, 0);
return JsonUtil.toJsonString(requestList); return JsonUtil.toJsonString(requestList);
} }
@ -287,6 +297,7 @@ public class JobServiceImpl implements JobService {
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Getter @Getter
private static class JobPartitionTask extends PartitionTask { private static class JobPartitionTask extends PartitionTask {
// 这里就直接放GroupConfig为了后面若加字段不需要再这里在调整了 // 这里就直接放GroupConfig为了后面若加字段不需要再这里在调整了
private final Job job; private final Job job;

View File

@ -1,21 +1,34 @@
package com.aizuda.snailjob.server.web.service.impl; package com.aizuda.snailjob.server.web.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.aizuda.snailjob.common.core.util.JsonUtil;
import com.aizuda.snailjob.common.core.util.StreamUtils;
import com.aizuda.snailjob.server.common.dto.PartitionTask;
import com.aizuda.snailjob.server.common.util.PartitionTaskUtils;
import com.aizuda.snailjob.server.web.model.base.PageResult; import com.aizuda.snailjob.server.web.model.base.PageResult;
import com.aizuda.snailjob.server.web.model.request.ExportNotifyRecipientVO;
import com.aizuda.snailjob.server.web.model.request.NotifyRecipientQueryVO; import com.aizuda.snailjob.server.web.model.request.NotifyRecipientQueryVO;
import com.aizuda.snailjob.server.web.model.request.NotifyRecipientRequestVO; import com.aizuda.snailjob.server.web.model.request.NotifyRecipientRequestVO;
import com.aizuda.snailjob.server.web.model.response.CommonLabelValueResponseVO; import com.aizuda.snailjob.server.web.model.response.CommonLabelValueResponseVO;
import com.aizuda.snailjob.server.web.model.response.NotifyRecipientResponseVO; import com.aizuda.snailjob.server.web.model.response.NotifyRecipientResponseVO;
import com.aizuda.snailjob.server.web.service.NotifyRecipientService; import com.aizuda.snailjob.server.web.service.NotifyRecipientService;
import com.aizuda.snailjob.server.web.service.convert.NotifyRecipientConverter; import com.aizuda.snailjob.server.web.service.convert.NotifyRecipientConverter;
import com.aizuda.snailjob.server.web.service.handler.GroupHandler;
import com.aizuda.snailjob.server.web.util.UserSessionUtils; import com.aizuda.snailjob.server.web.util.UserSessionUtils;
import com.aizuda.snailjob.template.datasource.persistence.mapper.NotifyRecipientMapper; import com.aizuda.snailjob.template.datasource.persistence.mapper.NotifyRecipientMapper;
import com.aizuda.snailjob.template.datasource.persistence.po.NotifyRecipient; import com.aizuda.snailjob.template.datasource.persistence.po.NotifyRecipient;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO; import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
@ -27,21 +40,26 @@ import java.util.Set;
*/ */
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
@Validated
public class NotifyRecipientServiceImpl implements NotifyRecipientService { public class NotifyRecipientServiceImpl implements NotifyRecipientService {
private final NotifyRecipientMapper notifyRecipientMapper; private final NotifyRecipientMapper notifyRecipientMapper;
private final GroupHandler groupHandler;
@Override @Override
public PageResult<List<NotifyRecipientResponseVO>> getNotifyRecipientPageList(NotifyRecipientQueryVO queryVO) { public PageResult<List<NotifyRecipientResponseVO>> getNotifyRecipientPageList(NotifyRecipientQueryVO queryVO) {
String namespaceId = UserSessionUtils.currentUserSession().getNamespaceId();
PageDTO<NotifyRecipient> pageDTO = new PageDTO<>(queryVO.getPage(), queryVO.getSize()); PageDTO<NotifyRecipient> pageDTO = new PageDTO<>(queryVO.getPage(), queryVO.getSize());
PageDTO<NotifyRecipient> notifyRecipientPageDTO = notifyRecipientMapper.selectPage(pageDTO, PageDTO<NotifyRecipient> notifyRecipientPageDTO = notifyRecipientMapper.selectPage(pageDTO,
new LambdaQueryWrapper<NotifyRecipient>() new LambdaQueryWrapper<NotifyRecipient>()
.likeRight(StrUtil.isNotBlank(queryVO.getRecipientName()), NotifyRecipient::getRecipientName, queryVO.getRecipientName()) .eq(NotifyRecipient::getNamespaceId, namespaceId)
.likeRight(Objects.nonNull(queryVO.getNotifyType()), NotifyRecipient::getNotifyType, queryVO.getNotifyType()) .eq(Objects.nonNull(queryVO.getNotifyType()), NotifyRecipient::getNotifyType, queryVO.getNotifyType())
.orderByDesc(NotifyRecipient::getCreateDt)); .likeRight(StrUtil.isNotBlank(queryVO.getRecipientName()), NotifyRecipient::getRecipientName,
queryVO.getRecipientName())
.orderByDesc(NotifyRecipient::getCreateDt));
return new PageResult<>(pageDTO, return new PageResult<>(pageDTO,
NotifyRecipientConverter.INSTANCE.convertList(notifyRecipientPageDTO.getRecords())); NotifyRecipientConverter.INSTANCE.convertList(notifyRecipientPageDTO.getRecords()));
} }
@Override @Override
@ -49,6 +67,7 @@ public class NotifyRecipientServiceImpl implements NotifyRecipientService {
String namespaceId = UserSessionUtils.currentUserSession().getNamespaceId(); String namespaceId = UserSessionUtils.currentUserSession().getNamespaceId();
NotifyRecipient notifyRecipient = NotifyRecipientConverter.INSTANCE.convert(requestVO); NotifyRecipient notifyRecipient = NotifyRecipientConverter.INSTANCE.convert(requestVO);
notifyRecipient.setNamespaceId(namespaceId); notifyRecipient.setNamespaceId(namespaceId);
notifyRecipient.setId(null);
return 1 == notifyRecipientMapper.insert(notifyRecipient); return 1 == notifyRecipientMapper.insert(notifyRecipient);
} }
@ -62,9 +81,12 @@ public class NotifyRecipientServiceImpl implements NotifyRecipientService {
@Override @Override
public List<CommonLabelValueResponseVO> getNotifyRecipientList() { public List<CommonLabelValueResponseVO> getNotifyRecipientList() {
String namespaceId = UserSessionUtils.currentUserSession().getNamespaceId();
List<NotifyRecipient> notifyRecipients = notifyRecipientMapper.selectList( List<NotifyRecipient> notifyRecipients = notifyRecipientMapper.selectList(
new LambdaQueryWrapper<NotifyRecipient>() new LambdaQueryWrapper<NotifyRecipient>()
.select(NotifyRecipient::getRecipientName, NotifyRecipient::getId)); .select(NotifyRecipient::getRecipientName, NotifyRecipient::getId)
.eq(NotifyRecipient::getNamespaceId, namespaceId)
);
return NotifyRecipientConverter.INSTANCE.convertListToCommonLabelValueList(notifyRecipients); return NotifyRecipientConverter.INSTANCE.convertListToCommonLabelValueList(notifyRecipients);
} }
@ -72,4 +94,53 @@ public class NotifyRecipientServiceImpl implements NotifyRecipientService {
public Boolean batchDeleteByIds(final Set<Long> ids) { public Boolean batchDeleteByIds(final Set<Long> ids) {
return ids.size() == notifyRecipientMapper.deleteBatchIds(ids); return ids.size() == notifyRecipientMapper.deleteBatchIds(ids);
} }
@Override
@Transactional
public void importNotifyRecipient(final List<NotifyRecipientRequestVO> notifyRecipientRequestVOS) {
for (final NotifyRecipientRequestVO notifyRecipientRequestVO : notifyRecipientRequestVOS) {
this.saveNotifyRecipient(notifyRecipientRequestVO);
}
}
@Override
public String exportNotifyRecipient(final ExportNotifyRecipientVO exportVO) {
List<NotifyRecipientRequestVO> requestList = new ArrayList<>();
String namespaceId = UserSessionUtils.currentUserSession().getNamespaceId();
PartitionTaskUtils.process(startId -> {
List<NotifyRecipient> recipients = notifyRecipientMapper.selectPage(new PageDTO<>(0, 100),
new LambdaQueryWrapper<NotifyRecipient>()
.eq(NotifyRecipient::getNamespaceId, namespaceId)
.eq(Objects.nonNull(exportVO.getNotifyType()), NotifyRecipient::getNotifyType,
exportVO.getNotifyType())
.likeRight(StrUtil.isNotBlank(exportVO.getRecipientName()), NotifyRecipient::getRecipientName,
exportVO.getRecipientName())
.ge(NotifyRecipient::getId, startId)
.in(CollUtil.isNotEmpty(exportVO.getNotifyRecipientIds()), NotifyRecipient::getId,
exportVO.getNotifyRecipientIds())
.orderByAsc(NotifyRecipient::getId)).getRecords();
return StreamUtils.toList(recipients, NotifyRecipientPartitionTask::new);
}, partitionTasks -> {
List<NotifyRecipientPartitionTask> partitionTaskList = (List<NotifyRecipientPartitionTask>) partitionTasks;
List<NotifyRecipientRequestVO> notifyRecipientRequestVOs = NotifyRecipientConverter.INSTANCE.toNotifyRecipientRequestVOs(
StreamUtils.toList(partitionTaskList, NotifyRecipientPartitionTask::getRecipient));
requestList.addAll(notifyRecipientRequestVOs);
}, 0);
return JsonUtil.toJsonString(requestList);
}
@EqualsAndHashCode(callSuper = true)
@Getter
private static class NotifyRecipientPartitionTask extends PartitionTask {
// 这里就直接放NotifyRecipient为了后面若加字段不需要再这里在调整了
private final NotifyRecipient recipient;
public NotifyRecipientPartitionTask(@NotNull NotifyRecipient recipient) {
this.recipient = recipient;
setId(recipient.getId());
}
}
} }

View File

@ -19,6 +19,7 @@ import com.aizuda.snailjob.server.web.model.response.SceneConfigResponseVO;
import com.aizuda.snailjob.server.web.service.SceneConfigService; import com.aizuda.snailjob.server.web.service.SceneConfigService;
import com.aizuda.snailjob.server.web.service.convert.SceneConfigConverter; import com.aizuda.snailjob.server.web.service.convert.SceneConfigConverter;
import com.aizuda.snailjob.server.web.service.convert.SceneConfigResponseVOConverter; import com.aizuda.snailjob.server.web.service.convert.SceneConfigResponseVOConverter;
import com.aizuda.snailjob.server.web.service.handler.GroupHandler;
import com.aizuda.snailjob.server.web.service.handler.SyncConfigHandler; import com.aizuda.snailjob.server.web.service.handler.SyncConfigHandler;
import com.aizuda.snailjob.server.web.util.UserSessionUtils; import com.aizuda.snailjob.server.web.util.UserSessionUtils;
import com.aizuda.snailjob.template.datasource.access.AccessTemplate; import com.aizuda.snailjob.template.datasource.access.AccessTemplate;
@ -53,7 +54,7 @@ import java.util.*;
public class SceneConfigServiceImpl implements SceneConfigService { public class SceneConfigServiceImpl implements SceneConfigService {
private final AccessTemplate accessTemplate; private final AccessTemplate accessTemplate;
private final NamespaceMapper namespaceMapper; private final GroupHandler groupHandler;
private static void checkExecuteInterval(SceneConfigRequestVO requestVO) { private static void checkExecuteInterval(SceneConfigRequestVO requestVO) {
if (Lists.newArrayList(WaitStrategies.WaitStrategyEnum.FIXED.getType(), if (Lists.newArrayList(WaitStrategies.WaitStrategyEnum.FIXED.getType(),
@ -231,18 +232,7 @@ public class SceneConfigServiceImpl implements SceneConfigService {
sceneNameSet.add(request.getSceneName()); sceneNameSet.add(request.getSceneName());
} }
List<GroupConfig> groupConfigs = accessTemplate.getGroupConfigAccess() groupHandler.validateGroupExistence(groupNameSet, namespaceId);
.list(new LambdaQueryWrapper<GroupConfig>()
.select(GroupConfig::getGroupName)
.eq(GroupConfig::getNamespaceId, namespaceId)
.in(GroupConfig::getGroupName, groupNameSet)
);
SetView<String> notExistedGroupNameSet = Sets.difference(groupNameSet,
StreamUtils.toSet(groupConfigs, GroupConfig::getGroupName));
Assert.isTrue(CollUtil.isEmpty(notExistedGroupNameSet),
() -> new SnailJobServerException("导入失败. 原因: 组{}不存在", notExistedGroupNameSet));
ConfigAccess<RetrySceneConfig> sceneConfigAccess = accessTemplate.getSceneConfigAccess(); ConfigAccess<RetrySceneConfig> sceneConfigAccess = accessTemplate.getSceneConfigAccess();
List<RetrySceneConfig> sceneConfigs = sceneConfigAccess.list( List<RetrySceneConfig> sceneConfigs = sceneConfigAccess.list(

View File

@ -39,6 +39,7 @@ import com.aizuda.snailjob.server.web.model.response.WorkflowDetailResponseVO;
import com.aizuda.snailjob.server.web.model.response.WorkflowResponseVO; import com.aizuda.snailjob.server.web.model.response.WorkflowResponseVO;
import com.aizuda.snailjob.server.web.service.WorkflowService; import com.aizuda.snailjob.server.web.service.WorkflowService;
import com.aizuda.snailjob.server.web.service.convert.WorkflowConverter; import com.aizuda.snailjob.server.web.service.convert.WorkflowConverter;
import com.aizuda.snailjob.server.web.service.handler.GroupHandler;
import com.aizuda.snailjob.server.web.service.handler.WorkflowHandler; import com.aizuda.snailjob.server.web.service.handler.WorkflowHandler;
import com.aizuda.snailjob.server.web.util.UserSessionUtils; import com.aizuda.snailjob.server.web.util.UserSessionUtils;
import com.aizuda.snailjob.template.datasource.access.AccessTemplate; import com.aizuda.snailjob.template.datasource.access.AccessTemplate;
@ -61,6 +62,7 @@ import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.util.*; import java.util.*;
import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.LinkedBlockingDeque;
@ -74,16 +76,17 @@ import java.util.stream.Collectors;
@Service @Service
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Validated
public class WorkflowServiceImpl implements WorkflowService { public class WorkflowServiceImpl implements WorkflowService {
private final WorkflowMapper workflowMapper; private final WorkflowMapper workflowMapper;
private final WorkflowNodeMapper workflowNodeMapper; private final WorkflowNodeMapper workflowNodeMapper;
private final SystemProperties systemProperties; private final SystemProperties systemProperties;
private final WorkflowHandler workflowHandler; private final WorkflowHandler workflowHandler;
@Lazy
private final WorkflowPrePareHandler terminalWorkflowPrepareHandler; private final WorkflowPrePareHandler terminalWorkflowPrepareHandler;
private final JobMapper jobMapper; private final JobMapper jobMapper;
private final AccessTemplate accessTemplate; private final AccessTemplate accessTemplate;
private final GroupHandler groupHandler;
private static Long calculateNextTriggerAt(final WorkflowRequestVO workflowRequestVO, Long time) { private static Long calculateNextTriggerAt(final WorkflowRequestVO workflowRequestVO, Long time) {
checkExecuteInterval(workflowRequestVO); checkExecuteInterval(workflowRequestVO);
@ -315,6 +318,7 @@ public class WorkflowServiceImpl implements WorkflowService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void importWorkflowTask(List<WorkflowRequestVO> requests) { public void importWorkflowTask(List<WorkflowRequestVO> requests) {
batchSaveWorkflowTask(requests, UserSessionUtils.currentUserSession().getNamespaceId()); batchSaveWorkflowTask(requests, UserSessionUtils.currentUserSession().getNamespaceId());
} }
@ -328,8 +332,10 @@ public class WorkflowServiceImpl implements WorkflowService {
.eq(Workflow::getNamespaceId, UserSessionUtils.currentUserSession().getNamespaceId()) .eq(Workflow::getNamespaceId, UserSessionUtils.currentUserSession().getNamespaceId())
.eq(Workflow::getDeleted, StatusEnum.NO.getStatus()) .eq(Workflow::getDeleted, StatusEnum.NO.getStatus())
.eq(StrUtil.isNotBlank(exportVO.getGroupName()), Workflow::getGroupName, exportVO.getGroupName()) .eq(StrUtil.isNotBlank(exportVO.getGroupName()), Workflow::getGroupName, exportVO.getGroupName())
.eq(Objects.nonNull(exportVO.getWorkflowStatus()), Workflow::getWorkflowStatus, exportVO.getWorkflowStatus()) .eq(Objects.nonNull(exportVO.getWorkflowStatus()), Workflow::getWorkflowStatus,
.likeRight(StrUtil.isNotBlank(exportVO.getWorkflowName()), Workflow::getWorkflowName, exportVO.getWorkflowName()) exportVO.getWorkflowStatus())
.likeRight(StrUtil.isNotBlank(exportVO.getWorkflowName()), Workflow::getWorkflowName,
exportVO.getWorkflowName())
.in(CollUtil.isNotEmpty(exportVO.getWorkflowIds()), Workflow::getId, exportVO.getWorkflowIds()) .in(CollUtil.isNotEmpty(exportVO.getWorkflowIds()), Workflow::getId, exportVO.getWorkflowIds())
.ge(Workflow::getId, startId) .ge(Workflow::getId, startId)
.orderByAsc(Workflow::getId) .orderByAsc(Workflow::getId)
@ -348,19 +354,8 @@ public class WorkflowServiceImpl implements WorkflowService {
private void batchSaveWorkflowTask(final List<WorkflowRequestVO> workflowRequestVOList, final String namespaceId) { private void batchSaveWorkflowTask(final List<WorkflowRequestVO> workflowRequestVOList, final String namespaceId) {
Set<String> groupNameSet =StreamUtils.toSet(workflowRequestVOList, WorkflowRequestVO::getGroupName); Set<String> groupNameSet = StreamUtils.toSet(workflowRequestVOList, WorkflowRequestVO::getGroupName);
List<GroupConfig> groupConfigs = accessTemplate.getGroupConfigAccess() groupHandler.validateGroupExistence(groupNameSet, namespaceId);
.list(new LambdaQueryWrapper<GroupConfig>()
.select(GroupConfig::getGroupName)
.eq(GroupConfig::getNamespaceId, namespaceId)
.in(GroupConfig::getGroupName, groupNameSet)
);
Sets.SetView<String> notExistedGroupNameSet = Sets.difference(groupNameSet,
StreamUtils.toSet(groupConfigs, GroupConfig::getGroupName));
Assert.isTrue(CollUtil.isEmpty(notExistedGroupNameSet),
() -> new SnailJobServerException("导入失败. 原因: 组{}不存在", notExistedGroupNameSet));
for (final WorkflowRequestVO workflowRequestVO : workflowRequestVOList) { for (final WorkflowRequestVO workflowRequestVO : workflowRequestVOList) {
checkExecuteInterval(workflowRequestVO); checkExecuteInterval(workflowRequestVO);
@ -412,7 +407,9 @@ public class WorkflowServiceImpl implements WorkflowService {
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Getter @Getter
private static class WorkflowPartitionTask extends PartitionTask { private static class WorkflowPartitionTask extends PartitionTask {
private final WorkflowDetailResponseVO responseVO; private final WorkflowDetailResponseVO responseVO;
public WorkflowPartitionTask(@NotNull WorkflowDetailResponseVO responseVO) { public WorkflowPartitionTask(@NotNull WorkflowDetailResponseVO responseVO) {
this.responseVO = responseVO; this.responseVO = responseVO;
setId(responseVO.getId()); setId(responseVO.getId());