feat(项目管理): 新增消息通知异步自定义资源池设置以及修改部分功能用例批量通知操作的问题

--bug=1039332 --user=郭雨琦 https://www.tapd.cn/55049933/bugtrace/bugs/view/1155049933001039332
--bug=1039160 --user=郭雨琦 https://www.tapd.cn/55049933/bugtrace/bugs/view/1155049933001039160
This commit is contained in:
guoyuqi 2024-04-15 14:36:16 +08:00 committed by 刘瑞斌
parent 33351bfee8
commit ded906fc5e
16 changed files with 213 additions and 62 deletions

View File

@ -366,6 +366,7 @@ message.domain.delete_user=Delete user
message.domain.create_time=Create time message.domain.create_time=Create time
message.domain.update_time=Update time message.domain.update_time=Update time
message.domain.delete_time=Delete time message.domain.delete_time=Delete time
message.domain.triggerMode=Trigger mode
#接口定义和case #接口定义和case
message.domain.protocol=Interface Protocol message.domain.protocol=Interface Protocol
message.domain.method=Http protocol type message.domain.method=Http protocol type

View File

@ -365,6 +365,7 @@ message.domain.deleteUser=删除人
message.domain.createTime=创建时间 message.domain.createTime=创建时间
message.domain.updateTime=更新时间 message.domain.updateTime=更新时间
message.domain.deleteTime=删除时间 message.domain.deleteTime=删除时间
message.domain.triggerMode=触发方式
#接口定义和用例 #接口定义和用例
message.domain.protocol=接口协议 message.domain.protocol=接口协议
message.domain.method=http协议类型 message.domain.method=http协议类型

View File

@ -366,6 +366,7 @@ message.domain.deleteUser=刪除人
message.domain.createTime=創建時間 message.domain.createTime=創建時間
message.domain.updateTime=更新時間 message.domain.updateTime=更新時間
message.domain.deleteTime=刪除時間 message.domain.deleteTime=刪除時間
message.domain.triggerMode=觸發方式
#接口定義和用例 #接口定義和用例
message.domain.protocol=介面協定 message.domain.protocol=介面協定
message.domain.method=http協定類型 message.domain.method=http協定類型

View File

@ -68,8 +68,7 @@ public class FunctionalCaseController {
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ) @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ)
@CheckOwner(resourceId = "#projectId", resourceType = "project") @CheckOwner(resourceId = "#projectId", resourceType = "project")
public TemplateDTO getDefaultTemplateField(@PathVariable String projectId) { public TemplateDTO getDefaultTemplateField(@PathVariable String projectId) {
TemplateDTO defaultTemplateDTO = projectTemplateService.getDefaultTemplateDTO(projectId, TemplateScene.FUNCTIONAL.name()); return projectTemplateService.getDefaultTemplateDTO(projectId, TemplateScene.FUNCTIONAL.name());
return defaultTemplateDTO;
} }
@ -99,7 +98,7 @@ public class FunctionalCaseController {
@Operation(summary = "用例管理-功能用例-更新用例") @Operation(summary = "用例管理-功能用例-更新用例")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_UPDATE) @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_UPDATE)
@Log(type = OperationLogType.UPDATE, expression = "#msClass.updateFunctionalCaseLog(#request, #files)", msClass = FunctionalCaseLogService.class) @Log(type = OperationLogType.UPDATE, expression = "#msClass.updateFunctionalCaseLog(#request, #files)", msClass = FunctionalCaseLogService.class)
@SendNotice(taskType = NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK, event = NoticeConstants.Event.UPDATE, target = "#targetClass.getMainFunctionalCaseDTO(#request.name, #request.caseEditType, #request.projectId, #request.customFields)", targetClass = FunctionalCaseNoticeService.class) @SendNotice(taskType = NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK, event = NoticeConstants.Event.UPDATE, target = "#targetClass.getMainFunctionalCaseDTO(#request, #request.customFields)", targetClass = FunctionalCaseNoticeService.class)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") @CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public FunctionalCase updateFunctionalCase(@Validated @RequestPart("request") FunctionalCaseEditRequest request, @RequestPart(value = "files", required = false) List<MultipartFile> files) { public FunctionalCase updateFunctionalCase(@Validated @RequestPart("request") FunctionalCaseEditRequest request, @RequestPart(value = "files", required = false) List<MultipartFile> files) {
String userId = SessionUtils.getUserId(); String userId = SessionUtils.getUserId();
@ -159,7 +158,6 @@ public class FunctionalCaseController {
@Operation(summary = "用例管理-功能用例-批量删除用例") @Operation(summary = "用例管理-功能用例-批量删除用例")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_DELETE) @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_DELETE)
@Log(type = OperationLogType.DELETE, expression = "#msClass.batchDeleteFunctionalCaseLog(#request)", msClass = FunctionalCaseLogService.class) @Log(type = OperationLogType.DELETE, expression = "#msClass.batchDeleteFunctionalCaseLog(#request)", msClass = FunctionalCaseLogService.class)
@SendNotice(taskType = NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK, event = NoticeConstants.Event.DELETE, target = "#targetClass.getBatchDeleteFunctionalCaseDTO(#request)", targetClass = FunctionalCaseNoticeService.class)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") @CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public void batchDeleteFunctionalCaseToGc(@Validated @RequestBody FunctionalCaseBatchRequest request) { public void batchDeleteFunctionalCaseToGc(@Validated @RequestBody FunctionalCaseBatchRequest request) {
String userId = SessionUtils.getUserId(); String userId = SessionUtils.getUserId();
@ -199,7 +197,6 @@ public class FunctionalCaseController {
@Operation(summary = "用例管理-功能用例-批量编辑用例") @Operation(summary = "用例管理-功能用例-批量编辑用例")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_UPDATE) @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_UPDATE)
@Log(type = OperationLogType.UPDATE, expression = "#msClass.batchEditFunctionalCaseLog(#request)", msClass = FunctionalCaseLogService.class) @Log(type = OperationLogType.UPDATE, expression = "#msClass.batchEditFunctionalCaseLog(#request)", msClass = FunctionalCaseLogService.class)
@SendNotice(taskType = NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK, event = NoticeConstants.Event.UPDATE, target = "#targetClass.getBatchEditFunctionalCaseDTO(#request)", targetClass = FunctionalCaseNoticeService.class)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") @CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public void batchEditFunctionalCase(@Validated @RequestBody FunctionalCaseBatchEditRequest request) { public void batchEditFunctionalCase(@Validated @RequestBody FunctionalCaseBatchEditRequest request) {
String userId = SessionUtils.getUserId(); String userId = SessionUtils.getUserId();

View File

@ -1,17 +1,15 @@
package io.metersphere.functional.service; package io.metersphere.functional.service;
import io.metersphere.functional.domain.FunctionalCase; import io.metersphere.functional.domain.*;
import io.metersphere.functional.domain.FunctionalCaseCustomField;
import io.metersphere.functional.domain.FunctionalCaseCustomFieldExample;
import io.metersphere.functional.dto.CaseCustomFieldDTO; import io.metersphere.functional.dto.CaseCustomFieldDTO;
import io.metersphere.functional.dto.FunctionalCaseDTO; import io.metersphere.functional.dto.FunctionalCaseDTO;
import io.metersphere.functional.mapper.CaseReviewFunctionalCaseMapper;
import io.metersphere.functional.mapper.CaseReviewMapper;
import io.metersphere.functional.mapper.FunctionalCaseCustomFieldMapper; import io.metersphere.functional.mapper.FunctionalCaseCustomFieldMapper;
import io.metersphere.functional.mapper.FunctionalCaseMapper; import io.metersphere.functional.mapper.FunctionalCaseMapper;
import io.metersphere.functional.request.FunctionalCaseBatchEditRequest;
import io.metersphere.functional.request.FunctionalCaseBatchRequest;
import io.metersphere.functional.request.FunctionalCaseCommentRequest; import io.metersphere.functional.request.FunctionalCaseCommentRequest;
import io.metersphere.functional.request.FunctionalCaseEditRequest;
import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.domain.CustomField; import io.metersphere.system.domain.CustomField;
import io.metersphere.system.domain.CustomFieldExample; import io.metersphere.system.domain.CustomFieldExample;
import io.metersphere.system.dto.sdk.OptionDTO; import io.metersphere.system.dto.sdk.OptionDTO;
@ -41,25 +39,26 @@ public class FunctionalCaseNoticeService {
@Resource @Resource
private CustomFieldMapper customFieldMapper; private CustomFieldMapper customFieldMapper;
@Resource
private FunctionalCaseService functionalCaseService;
@Resource @Resource
private FunctionalCaseCustomFieldService functionalCaseCustomFieldService; private FunctionalCaseCustomFieldService functionalCaseCustomFieldService;
@Resource @Resource
private ExtOrganizationCustomFieldMapper extOrganizationCustomFieldMapper; private ExtOrganizationCustomFieldMapper extOrganizationCustomFieldMapper;
@Resource
private CaseReviewFunctionalCaseMapper caseReviewFunctionalCaseMapper;
@Resource
private CaseReviewMapper caseReviewMapper;
public FunctionalCaseDTO getFunctionalCaseDTO(FunctionalCaseCommentRequest functionalCaseCommentRequest) { public FunctionalCaseDTO getFunctionalCaseDTO(FunctionalCaseCommentRequest functionalCaseCommentRequest) {
FunctionalCase functionalCase = functionalCaseMapper.selectByPrimaryKey(functionalCaseCommentRequest.getCaseId()); FunctionalCase functionalCase = functionalCaseMapper.selectByPrimaryKey(functionalCaseCommentRequest.getCaseId());
FunctionalCaseDTO functionalCaseDTO = new FunctionalCaseDTO(); FunctionalCaseDTO functionalCaseDTO = new FunctionalCaseDTO();
functionalCaseDTO.setTriggerMode(Translator.get("log.test_plan.functional_case")); functionalCaseDTO.setTriggerMode(NoticeConstants.TriggerMode.MANUAL_EXECUTION);
BeanUtils.copyBean(functionalCaseDTO, functionalCase); BeanUtils.copyBean(functionalCaseDTO, functionalCase);
setNotifier(functionalCaseCommentRequest, functionalCaseDTO); setNotifier(functionalCaseCommentRequest, functionalCaseDTO);
List<OptionDTO> customFields = getCustomFields(functionalCaseCommentRequest.getCaseId()); List<OptionDTO> customFields = getCustomFields(functionalCaseCommentRequest.getCaseId());
functionalCaseDTO.setFields(customFields); functionalCaseDTO.setFields(customFields);
//TODO:设置测试计划名称 //TODO:设置测试计划名称
//TODO设置用例评审名称 setReviewName(functionalCaseCommentRequest.getCaseId(), functionalCaseDTO);
return functionalCaseDTO; return functionalCaseDTO;
} }
@ -129,12 +128,17 @@ public class FunctionalCaseNoticeService {
return optionDTOList; return optionDTOList;
} }
public FunctionalCaseDTO getMainFunctionalCaseDTO(String name, String caseEditType, String projectId, List<CaseCustomFieldDTO> customFields) { public FunctionalCaseDTO getMainFunctionalCaseDTO(FunctionalCaseEditRequest request, List<CaseCustomFieldDTO> customFields) {
FunctionalCaseDTO functionalCaseDTO = new FunctionalCaseDTO(); FunctionalCaseDTO functionalCaseDTO = new FunctionalCaseDTO();
functionalCaseDTO.setName(name); if (StringUtils.isNotBlank(request.getId())) {
functionalCaseDTO.setProjectId(projectId); FunctionalCase functionalCase = functionalCaseMapper.selectByPrimaryKey(request.getId());
functionalCaseDTO.setCaseEditType(caseEditType); BeanUtils.copyBean(functionalCaseDTO, functionalCase);
functionalCaseDTO.setCreateUser(null); setReviewName(request.getId(), functionalCaseDTO);
} else {
BeanUtils.copyBean(functionalCaseDTO, request);
functionalCaseDTO.setCreateUser(null);
}
functionalCaseDTO.setTriggerMode(NoticeConstants.TriggerMode.MANUAL_EXECUTION);
List<OptionDTO> fields = new ArrayList<>(); List<OptionDTO> fields = new ArrayList<>();
if (CollectionUtils.isNotEmpty(customFields)) { if (CollectionUtils.isNotEmpty(customFields)) {
for (CaseCustomFieldDTO customFieldDTO : customFields) { for (CaseCustomFieldDTO customFieldDTO : customFields) {
@ -150,10 +154,24 @@ public class FunctionalCaseNoticeService {
} }
functionalCaseDTO.setFields(fields); functionalCaseDTO.setFields(fields);
//TODO:设置测试计划名称 //TODO:设置测试计划名称
//TODO设置用例评审名称
return functionalCaseDTO; return functionalCaseDTO;
} }
private void setReviewName(String caseId, FunctionalCaseDTO functionalCaseDTO) {
CaseReviewFunctionalCaseExample caseReviewFunctionalCaseExample = new CaseReviewFunctionalCaseExample();
caseReviewFunctionalCaseExample.createCriteria().andCaseIdEqualTo(caseId);
List<CaseReviewFunctionalCase> caseReviewFunctionalCases = caseReviewFunctionalCaseMapper.selectByExample(caseReviewFunctionalCaseExample);
List<String> reviewIds = caseReviewFunctionalCases.stream().map(CaseReviewFunctionalCase::getReviewId).distinct().toList();
if (CollectionUtils.isNotEmpty(reviewIds)) {
CaseReviewExample caseReviewExample = new CaseReviewExample();
caseReviewExample.createCriteria().andIdIn(reviewIds);
List<CaseReview> caseReviews = caseReviewMapper.selectByExample(caseReviewExample);
List<String> reviewName = caseReviews.stream().map(CaseReview::getName).toList();
String string = reviewName.toString();
functionalCaseDTO.setReviewName(string);
}
}
public FunctionalCaseDTO getDeleteFunctionalCaseDTO(String id) { public FunctionalCaseDTO getDeleteFunctionalCaseDTO(String id) {
FunctionalCase functionalCase = functionalCaseMapper.selectByPrimaryKey(id); FunctionalCase functionalCase = functionalCaseMapper.selectByPrimaryKey(id);
@ -165,46 +183,65 @@ public class FunctionalCaseNoticeService {
}); });
//TODO:设置测试计划名称 //TODO:设置测试计划名称
//TODO设置用例评审名称 setReviewName(id, functionalCaseDTO);
return functionalCaseDTO; return functionalCaseDTO;
} }
public Map<String, FunctionalCase> copyBaseCaseInfo(String projectId, List<String> ids) {
FunctionalCaseExample example = new FunctionalCaseExample();
public List<FunctionalCaseDTO> getBatchDeleteFunctionalCaseDTO(FunctionalCaseBatchRequest request) { example.createCriteria().andProjectIdEqualTo(projectId).andDeletedEqualTo(false).andIdIn(ids);
List<String> ids = functionalCaseService.doSelectIds(request, request.getProjectId()); List<FunctionalCase> functionalCaseLists = functionalCaseMapper.selectByExample(example);
return handleBatchNotice(request.getProjectId(), ids); return functionalCaseLists.stream().collect(Collectors.toMap(FunctionalCase::getId, functionalCase -> functionalCase));
} }
private List<FunctionalCaseDTO> handleBatchNotice(String projectId, List<String> ids) { public List<FunctionalCaseDTO> handleBatchNotice(String projectId, List<String> ids) {
List<FunctionalCaseDTO> dtoList = new ArrayList<>(); List<FunctionalCaseDTO> dtoList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(ids)) { if (CollectionUtils.isNotEmpty(ids)) {
Map<String, FunctionalCase> functionalCaseMap = functionalCaseService.copyBaseInfo(projectId, ids); Map<String, FunctionalCase> functionalCaseMap = copyBaseCaseInfo(projectId, ids);
Map<String, List<FunctionalCaseCustomField>> customFieldMap = functionalCaseCustomFieldService.getCustomFieldMapByCaseIds(ids); Map<String, List<FunctionalCaseCustomField>> customFieldMap = functionalCaseCustomFieldService.getCustomFieldMapByCaseIds(ids);
AtomicReference<List<OptionDTO>> optionDTOS = new AtomicReference<>(new ArrayList<>()); AtomicReference<List<OptionDTO>> optionDTOS = new AtomicReference<>(new ArrayList<>());
CaseReviewFunctionalCaseExample caseReviewFunctionalCaseExample = new CaseReviewFunctionalCaseExample();
caseReviewFunctionalCaseExample.createCriteria().andCaseIdIn(ids);
List<CaseReviewFunctionalCase> caseReviewFunctionalCases = caseReviewFunctionalCaseMapper.selectByExample(caseReviewFunctionalCaseExample);
List<String> reviewIds = caseReviewFunctionalCases.stream().map(CaseReviewFunctionalCase::getReviewId).distinct().toList();
Map<String, String> reviewMap = new HashMap<>();
if (CollectionUtils.isNotEmpty(reviewIds)) {
CaseReviewExample caseReviewExample = new CaseReviewExample();
caseReviewExample.createCriteria().andIdIn(reviewIds);
List<CaseReview> caseReviews = caseReviewMapper.selectByExample(caseReviewExample);
reviewMap = caseReviews.stream().collect(Collectors.toMap(CaseReview::getId, CaseReview::getName));
}
Map<String, List<CaseReviewFunctionalCase>> caseReviewMap = caseReviewFunctionalCases.stream().collect(Collectors.groupingBy(CaseReviewFunctionalCase::getCaseId));
Map<String, String> finalReviewMap = reviewMap;
ids.forEach(id -> { ids.forEach(id -> {
FunctionalCase functionalCase = functionalCaseMap.get(id); FunctionalCase functionalCase = functionalCaseMap.get(id);
List<FunctionalCaseCustomField> customFields = customFieldMap.get(id); if (functionalCase != null) {
if (CollectionUtils.isNotEmpty(customFields)) { List<FunctionalCaseCustomField> customFields = customFieldMap.get(id);
List<String> fields = customFields.stream().map(functionalCaseCustomField -> functionalCaseCustomField.getFieldId()).toList(); if (CollectionUtils.isNotEmpty(customFields)) {
optionDTOS.set(extOrganizationCustomFieldMapper.getCustomFieldOptions(fields)); List<String> fields = customFields.stream().map(FunctionalCaseCustomField::getFieldId).toList();
optionDTOS.set(extOrganizationCustomFieldMapper.getCustomFieldOptions(fields));
}
FunctionalCaseDTO functionalCaseDTO = new FunctionalCaseDTO();
functionalCaseDTO.setName(functionalCase.getName());
functionalCaseDTO.setProjectId(functionalCase.getProjectId());
functionalCaseDTO.setCaseEditType(functionalCase.getCaseEditType());
functionalCaseDTO.setCreateUser(null);
functionalCaseDTO.setFields(optionDTOS.get());
List<CaseReviewFunctionalCase> caseReviewFunctionalCases1 = caseReviewMap.get(id);
List<String>reviewName = new ArrayList<>();
if (CollectionUtils.isNotEmpty(caseReviewFunctionalCases1)) {
for (CaseReviewFunctionalCase caseReviewFunctionalCase : caseReviewFunctionalCases1) {
String s = finalReviewMap.get(caseReviewFunctionalCase.getReviewId());
reviewName.add(s);
}
}
List<String> list = reviewName.stream().distinct().toList();
functionalCaseDTO.setReviewName(list.toString());
dtoList.add(functionalCaseDTO);
} }
FunctionalCaseDTO functionalCaseDTO = new FunctionalCaseDTO();
functionalCaseDTO.setName(functionalCase.getName());
functionalCaseDTO.setProjectId(functionalCase.getProjectId());
functionalCaseDTO.setCaseEditType(functionalCase.getCaseEditType());
functionalCaseDTO.setCreateUser(null);
functionalCaseDTO.setFields(optionDTOS.get());
dtoList.add(functionalCaseDTO);
}); });
//TODO:设置测试计划名称 //TODO:设置测试计划名称
//TODO设置用例评审名称
} }
return dtoList; return dtoList;
} }
public List<FunctionalCaseDTO> getBatchEditFunctionalCaseDTO(FunctionalCaseBatchEditRequest request) {
List<String> ids = functionalCaseService.doSelectIds(request, request.getProjectId());
return handleBatchNotice(request.getProjectId(), ids);
}
} }

View File

@ -168,6 +168,9 @@ public class FunctionalCaseService {
private FunctionalCaseCommentMapper functionalCaseCommentMapper; private FunctionalCaseCommentMapper functionalCaseCommentMapper;
@Resource @Resource
private ProjectService projectService; private ProjectService projectService;
@Resource
private FunctionalCaseNoticeService functionalCaseNoticeService;
private static final int MAX_TAG_SIZE = 10; private static final int MAX_TAG_SIZE = 10;
@ -608,10 +611,10 @@ public class FunctionalCaseService {
* @param userId userId * @param userId userId
*/ */
public void deleteFunctionalCase(FunctionalCaseDeleteRequest request, String userId) { public void deleteFunctionalCase(FunctionalCaseDeleteRequest request, String userId) {
handDeleteFunctionalCase(Collections.singletonList(request.getId()), request.getDeleteAll(), userId); handDeleteFunctionalCase(Collections.singletonList(request.getId()), request.getDeleteAll(), userId, request.getProjectId());
} }
private void handDeleteFunctionalCase(List<String> ids, Boolean deleteAll, String userId) { private void handDeleteFunctionalCase(List<String> ids, Boolean deleteAll, String userId, String projectId) {
Map<String, Object> param = new HashMap<>(); Map<String, Object> param = new HashMap<>();
if (deleteAll) { if (deleteAll) {
//全部删除 进入回收站 //全部删除 进入回收站
@ -626,6 +629,8 @@ public class FunctionalCaseService {
param.put(CaseEvent.Param.CASE_IDS, CollectionUtils.isNotEmpty(ids) ? ids : new ArrayList<>()); param.put(CaseEvent.Param.CASE_IDS, CollectionUtils.isNotEmpty(ids) ? ids : new ArrayList<>());
doDelete(ids, userId); doDelete(ids, userId);
} }
User user = userMapper.selectByPrimaryKey(userId);
batchSendNotice(projectId, ids, user, NoticeConstants.Event.DELETE);
param.put(CaseEvent.Param.USER_ID, userId); param.put(CaseEvent.Param.USER_ID, userId);
param.put(CaseEvent.Param.EVENT_NAME, CaseEvent.Event.DELETE_FUNCTIONAL_CASE); param.put(CaseEvent.Param.EVENT_NAME, CaseEvent.Event.DELETE_FUNCTIONAL_CASE);
provider.updateCaseReview(param); provider.updateCaseReview(param);
@ -718,7 +723,7 @@ public class FunctionalCaseService {
public void batchDeleteFunctionalCaseToGc(FunctionalCaseBatchRequest request, String userId) { public void batchDeleteFunctionalCaseToGc(FunctionalCaseBatchRequest request, String userId) {
List<String> ids = doSelectIds(request, request.getProjectId()); List<String> ids = doSelectIds(request, request.getProjectId());
if (CollectionUtils.isNotEmpty(ids)) { if (CollectionUtils.isNotEmpty(ids)) {
handDeleteFunctionalCase(ids, request.getDeleteAll(), userId); handDeleteFunctionalCase(ids, request.getDeleteAll(), userId, request.getProjectId());
} }
} }
@ -744,10 +749,31 @@ public class FunctionalCaseService {
* @param userId userId * @param userId userId
*/ */
public void batchMoveFunctionalCase(FunctionalCaseBatchMoveRequest request, String userId) { public void batchMoveFunctionalCase(FunctionalCaseBatchMoveRequest request, String userId) {
User user = userMapper.selectByPrimaryKey(userId);
List<String> ids = doSelectIds(request, request.getProjectId()); List<String> ids = doSelectIds(request, request.getProjectId());
if (CollectionUtils.isNotEmpty(ids)) { if (CollectionUtils.isNotEmpty(ids)) {
List<String> refId = extFunctionalCaseMapper.getRefIds(ids, false); List<String> refId = extFunctionalCaseMapper.getRefIds(ids, false);
extFunctionalCaseMapper.batchMoveModule(request, refId, userId); extFunctionalCaseMapper.batchMoveModule(request, refId, userId);
batchSendNotice(request.getProjectId(), ids, user, NoticeConstants.Event.UPDATE);
}
}
private void batchSendNotice(String projectId, List<String> ids, User user, String event) {
int amount = 100;//每次读取的条数
long roundTimes = Double.valueOf(Math.ceil((double) ids.size() / amount)).longValue();//循环的次数
for (int i = 0; i < (int)roundTimes; i++) {
int fromIndex = (i*amount);
int toIndex = ((i+1)*amount);
if (i == roundTimes-1){//最后一次遍历
toIndex = ids.size();
} else if (toIndex > ids.size()){
toIndex = ids.size();
}
List<String> subList = ids.subList(fromIndex, toIndex);
List<FunctionalCaseDTO> functionalCaseDTOS = functionalCaseNoticeService.handleBatchNotice(projectId, subList);
List<Map> resources = new ArrayList<>();
resources.addAll(JSON.parseArray(JSON.toJSONString(functionalCaseDTOS), Map.class));
commonNoticeSendService.sendNotice(NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK, event, resources, user, projectId);
} }
} }
@ -833,6 +859,10 @@ public class FunctionalCaseService {
historyLogDTO.setFileAssociationList(fileAssociationList); historyLogDTO.setFileAssociationList(fileAssociationList);
saveAddDataLog(functionalCase, new FunctionalCaseHistoryLogDTO(), historyLogDTO, userId, organizationId, OperationLogType.ADD.name(), OperationLogModule.FUNCTIONAL_CASE); saveAddDataLog(functionalCase, new FunctionalCaseHistoryLogDTO(), historyLogDTO, userId, organizationId, OperationLogType.ADD.name(), OperationLogModule.FUNCTIONAL_CASE);
} }
User user = userMapper.selectByPrimaryKey(userId);
batchSendNotice(request.getProjectId(), ids, user, NoticeConstants.Event.CREATE);
} }
} }
@ -875,6 +905,7 @@ public class FunctionalCaseService {
public void batchEditFunctionalCase(FunctionalCaseBatchEditRequest request, String userId) { public void batchEditFunctionalCase(FunctionalCaseBatchEditRequest request, String userId) {
List<String> ids = doSelectIds(request, request.getProjectId()); List<String> ids = doSelectIds(request, request.getProjectId());
if (CollectionUtils.isNotEmpty(ids)) { if (CollectionUtils.isNotEmpty(ids)) {
User user = userMapper.selectByPrimaryKey(userId);
//标签处理 //标签处理
handleTags(request, userId, ids); handleTags(request, userId, ids);
//自定义字段处理 //自定义字段处理
@ -885,6 +916,7 @@ public class FunctionalCaseService {
functionalCase.setUpdateTime(System.currentTimeMillis()); functionalCase.setUpdateTime(System.currentTimeMillis());
functionalCase.setUpdateUser(userId); functionalCase.setUpdateUser(userId);
extFunctionalCaseMapper.batchUpdate(functionalCase, ids); extFunctionalCaseMapper.batchUpdate(functionalCase, ids);
batchSendNotice(request.getProjectId(), ids, user, NoticeConstants.Event.UPDATE);
} }
} }
@ -1057,7 +1089,7 @@ public class FunctionalCaseService {
/** /**
* 组装通知数据 * 组装通知数据
* *
* @param noticeList * @param noticeList xiao
* @param functionalCaseExcelData * @param functionalCaseExcelData
* @param request * @param request
* @param userId * @param userId

View File

@ -1,10 +1,40 @@
package io.metersphere.system.config; package io.metersphere.system.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
@EnableAsync(proxyTargetClass = true) @EnableAsync(proxyTargetClass = true)
@Configuration @Configuration
public class AsyncConfig { public class AsyncConfig {
/**
* 核心线程数
*/
private static final int CORE_POOL_SIZE = 10;
/**
* 最大线程数
*/
private static final int MAX_POOL_SIZE = 10;
/**
* ThreadPoolTaskExecutor不会自动创建ThreadPoolExecutor需要手动调initialize才会创建
* 如果@Bean就不需手动会自动InitializingBean的afterPropertiesSet来调initialize
*
*/
@Bean(name = "threadPoolTaskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置线程池最大线程数
executor.setMaxPoolSize(MAX_POOL_SIZE);
// 线程池活跃的线程数
executor.setCorePoolSize(CORE_POOL_SIZE);
executor.setThreadNamePrefix("notice-task-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
} }

View File

@ -12,6 +12,7 @@ import io.metersphere.system.dto.request.OrganizationRequest;
import io.metersphere.system.dto.sdk.OptionDTO; import io.metersphere.system.dto.sdk.OptionDTO;
import io.metersphere.system.log.annotation.Log; import io.metersphere.system.log.annotation.Log;
import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.security.CheckOwner;
import io.metersphere.system.service.OrganizationService; import io.metersphere.system.service.OrganizationService;
import io.metersphere.system.utils.PageUtils; import io.metersphere.system.utils.PageUtils;
import io.metersphere.system.utils.Pager; import io.metersphere.system.utils.Pager;
@ -43,6 +44,7 @@ public class OrganizationController {
@PostMapping("/member/list") @PostMapping("/member/list")
@Operation(summary = "系统设置-组织-成员-获取组织成员列表") @Operation(summary = "系统设置-组织-成员-获取组织成员列表")
@RequiresPermissions(PermissionConstants.ORGANIZATION_MEMBER_READ) @RequiresPermissions(PermissionConstants.ORGANIZATION_MEMBER_READ)
@CheckOwner(resourceId = "#organizationId", resourceType = "organization")
public Pager<List<OrgUserExtend>> getMemberList(@Validated @RequestBody OrganizationRequest organizationRequest) { public Pager<List<OrgUserExtend>> getMemberList(@Validated @RequestBody OrganizationRequest organizationRequest) {
Page<Object> page = PageHelper.startPage(organizationRequest.getCurrent(), organizationRequest.getPageSize()); Page<Object> page = PageHelper.startPage(organizationRequest.getCurrent(), organizationRequest.getPageSize());
return PageUtils.setPageInfo(page, organizationService.getMemberListByOrg(organizationRequest)); return PageUtils.setPageInfo(page, organizationService.getMemberListByOrg(organizationRequest));

View File

@ -17,4 +17,7 @@ public class BugSyncNoticeDTO {
@Schema(description ="message.domain.bug_sync_total_count") @Schema(description ="message.domain.bug_sync_total_count")
private Integer total; private Integer total;
@Schema(description ="message.domain.triggerMode")
private String triggerMode;
} }

View File

@ -11,9 +11,9 @@ public class FunctionalCaseMessageDTO {
@Schema(description ="message.domain.name") @Schema(description ="message.domain.name")
private String name; private String name;
@Schema(description = "message.domain.testPlanName") /* @Schema(description = "message.domain.testPlanName")
private String testPlanName; private String testPlanName;
*/
@Schema(description = "message.domain.reviewName") @Schema(description = "message.domain.reviewName")
private String reviewName; private String reviewName;
@ -23,8 +23,8 @@ public class FunctionalCaseMessageDTO {
@Schema(description = "message.domain.caseModel") @Schema(description = "message.domain.caseModel")
private String caseEditType; private String caseEditType;
@Schema(description = "message.domain.lastExecuteResult") /* @Schema(description = "message.domain.lastExecuteResult")
private String lastExecuteResult; private String lastExecuteResult;*/
@Schema(description = "message.domain.createUser") @Schema(description = "message.domain.createUser")
private String createUser; private String createUser;
@ -44,4 +44,7 @@ public class FunctionalCaseMessageDTO {
@Schema(description = "message.domain.deleteTime") @Schema(description = "message.domain.deleteTime")
private Long deleteTime; private Long deleteTime;
@Schema(description = "message.domain.deleteTime")
private String triggerMode;
} }

View File

@ -213,7 +213,7 @@
<select id="getProjectMemberByUserId" resultType="io.metersphere.system.domain.User"> <select id="getProjectMemberByUserId" resultType="io.metersphere.system.domain.User">
select distinct u.* from user_role_relation urr join `user` u on urr.user_id = u.id select distinct u.* from user_role_relation urr join `user` u on urr.user_id = u.id
where where
u.deleted = 0 and u.enable = 1 u.deleted = 0
<if test="projectId != null and projectId != ''"> <if test="projectId != null and projectId != ''">
and urr.source_id = #{projectId} and urr.source_id = #{projectId}
</if> </if>

View File

@ -231,7 +231,7 @@ public class MessageTemplateUtils {
if (CollectionUtils.isNotEmpty(customFields)) { if (CollectionUtils.isNotEmpty(customFields)) {
Map <String,String>customFielddNameMap = new HashMap<>(); Map <String,String>customFielddNameMap = new HashMap<>();
for (CustomField customField : customFields) { for (CustomField customField : customFields) {
customFielddNameMap.put(customField.getName(), StringUtils.isBlank(customField.getName()) ? "-" : customField.getName()); customFielddNameMap.put(customField.getName(), StringUtils.isBlank(customField.getName()) ? "-" : "<"+customField.getName()+">");
} }
map.putAll(customFielddNameMap); map.putAll(customFielddNameMap);
} }

View File

@ -62,7 +62,7 @@ public class NoticeSendService {
/** /**
* 在线操作发送通知 * 在线操作发送通知
*/ */
@Async @Async("threadPoolTaskExecutor")
public void send(String taskType, NoticeModel noticeModel) { public void send(String taskType, NoticeModel noticeModel) {
setLanguage(noticeModel); setLanguage(noticeModel);
try { try {
@ -96,7 +96,7 @@ public class NoticeSendService {
/** /**
* jenkins 和定时任务触发的发送 * jenkins 和定时任务触发的发送
*/ */
@Async @Async("threadPoolTaskExecutor")
public void sendJenkins(String triggerMode, NoticeModel noticeModel) { public void sendJenkins(String triggerMode, NoticeModel noticeModel) {
// api和定时任务调用不排除自己 // api和定时任务调用不排除自己
noticeModel.setExcludeSelf(false); noticeModel.setExcludeSelf(false);
@ -127,7 +127,7 @@ public class NoticeSendService {
/** /**
* 后台触发的发送没有session * 后台触发的发送没有session
*/ */
@Async @Async("threadPoolTaskExecutor")
public void send(Project project, String taskType, NoticeModel noticeModel) { public void send(Project project, String taskType, NoticeModel noticeModel) {
setLanguage(noticeModel); setLanguage(noticeModel);
try { try {

View File

@ -47,7 +47,7 @@ public class NotificationService {
record.setStatus(NotificationConstants.Status.READ.name()); record.setStatus(NotificationConstants.Status.READ.name());
NotificationExample example = new NotificationExample(); NotificationExample example = new NotificationExample();
if (StringUtils.isNotBlank(resourceType)) { if (StringUtils.isNotBlank(resourceType)) {
example.createCriteria().andResourceTypeEqualTo(resourceType); example.createCriteria().andResourceTypeEqualTo("%" + resourceType + "%");
} }
example.createCriteria().andReceiverEqualTo(userId); example.createCriteria().andReceiverEqualTo(userId);
return notificationMapper.updateByExampleSelective(record, example); return notificationMapper.updateByExampleSelective(record, example);

View File

@ -246,7 +246,7 @@ public class TestResourcePoolService {
pool.getCreateUser(), pool.getCreateUser(),
OperationLogType.UPDATE.name(), OperationLogType.UPDATE.name(),
OperationLogModule.SETTING_SYSTEM_RESOURCE_POOL, OperationLogModule.SETTING_SYSTEM_RESOURCE_POOL,
"更新资源池配置"); pool.getName());
dto.setPath("/update"); dto.setPath("/update");
dto.setMethod(HttpMethodConstants.POST.name()); dto.setMethod(HttpMethodConstants.POST.name());

View File

@ -2,23 +2,33 @@ package io.metersphere.system.controller;
import io.metersphere.project.domain.Notification; import io.metersphere.project.domain.Notification;
import io.metersphere.project.domain.NotificationExample; import io.metersphere.project.domain.NotificationExample;
import io.metersphere.project.domain.Project;
import io.metersphere.project.mapper.NotificationMapper; import io.metersphere.project.mapper.NotificationMapper;
import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.JSON;
import io.metersphere.system.base.BaseTest; import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.handler.ResultHolder; import io.metersphere.system.controller.handler.ResultHolder;
import io.metersphere.system.dto.sdk.OptionDTO; import io.metersphere.system.dto.sdk.OptionDTO;
import io.metersphere.system.dto.sdk.request.NotificationRequest; import io.metersphere.system.dto.sdk.request.NotificationRequest;
import io.metersphere.system.log.dto.NotificationDTO; import io.metersphere.system.log.dto.NotificationDTO;
import io.metersphere.system.notice.NoticeModel;
import io.metersphere.system.notice.constants.NoticeConstants;
import io.metersphere.system.notice.constants.NotificationConstants; import io.metersphere.system.notice.constants.NotificationConstants;
import io.metersphere.system.service.NoticeSendService;
import io.metersphere.system.utils.Pager; import io.metersphere.system.utils.Pager;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.beanutils.BeanMap;
import org.junit.jupiter.api.*; import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.MvcResult;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc @AutoConfigureMockMvc
@ -32,6 +42,10 @@ public class NotificationControllerTests extends BaseTest {
@Resource @Resource
protected NotificationMapper notificationMapper; protected NotificationMapper notificationMapper;
@Resource
protected NoticeSendService noticeSendService;
@Resource
protected ProjectMapper projectMapper;
void saveNotice() { void saveNotice() {
Notification notification = new Notification(); Notification notification = new Notification();
@ -113,4 +127,34 @@ public class NotificationControllerTests extends BaseTest {
} }
@Autowired
ThreadPoolTaskExecutor threadPoolTaskExecutor; //会去匹配 @Bean("poolExecutor") 这个线程池
@Test
void contextLoads() throws InterruptedException {
for (int i = 0; i < 6; i++) {
Project project = projectMapper.selectByPrimaryKey("10000100001");
BeanMap beanMap = new BeanMap(project);
Map paramMap = new HashMap<>(beanMap);
paramMap.put(NoticeConstants.RelatedUser.OPERATOR, "admin");
paramMap.put("projectId", "10000100001");
NoticeModel noticeModel = NoticeModel.builder()
.operator("admin")
.context("nihao")
.paramMap(paramMap)
.subject("hahah")
.event("UPDATE")
.excludeSelf(true)
.build();
//一定要休眠 不然主线程关闭了子线程还没有启动
noticeSendService.send("FUNCTIONAL_CASE_TASK",noticeModel);
}
Thread.sleep(10000);
}
} }