feat(缺陷管理): 缺陷消息通知增加处理人选项

--task=1015666 --user=宋昌昌 【缺陷管理】缺陷创建,更新处理人,对处理人发送通知-后端 https://www.tapd.cn/55049933/s/1559449
This commit is contained in:
song-cc-rock 2024-08-07 15:31:26 +08:00 committed by Craftsman
parent a7a50d1516
commit d3036e9ae7
14 changed files with 138 additions and 14 deletions

View File

@ -191,10 +191,12 @@ message.execute_fail=执行不通过
message.execute_at=执行被@
message.open=开启
message.close=关闭
message.assign=分配
message.sync_completed=同步完成
message.create_user=创建人
message.follow_people=关注人
message.operator=操作人
message.handle_user=处理人 (第三方平台的处理人, 不会接收到通知)
message.trigger_mode=触发方式
message.jenkins_name=名称
message.custom_field=自定义字段
@ -212,6 +214,7 @@ message.bug_task_comment=${OPERATOR}评论了你的缺陷:${title}
message.bug_task_at_comment=${OPERATOR}评论了缺陷:${title} 并@了你
message.bug_task_reply_comment=${OPERATOR}在缺陷 ${title} 回复了你的评论
message.bug_sync_task_execute_completed=${OPERATOR}同步了${total}条缺陷
message.bug_task_assign=${OPERATOR}给你分配了一个缺陷: ${title}
message.functional_case_task_create=${OPERATOR}创建了功能用例:${name}
message.functional_case_task_update=${OPERATOR}更新了功能用例:${name}
message.functional_case_task_delete=${OPERATOR}删除了功能用例:${name}
@ -268,6 +271,7 @@ message.title.bug_task_update=缺陷更新通知
message.title.bug_task_delete=缺陷删除通知
message.title.bug_task_comment=缺陷评论通知
message.title.bug_sync_task_execute_completed=同步缺陷执行完成通知
message.title.bug_task_assign=缺陷分配通知
message.title.functional_case_task_create=功能用例创建通知
message.title.functional_case_task_update=功能用例更新通知
message.title.functional_case_task_delete=功能用例删除通知

View File

@ -229,10 +229,12 @@ message.execute_fail=Execution failed
message.execute_at=Execution is @
message.open=turn on
message.close=closure
message.assign=assign
message.sync_completed=Sync completed
message.create_user=Create user
message.follow_people=Follow people
message.operator=Operator
message.handle_user=Handler (The handler of the third party platform will not receive the notification)
message.trigger_mode=Trigger mode
message.jenkins_name=Name
message.custom_field=Custom fields
@ -250,6 +252,7 @@ message.bug_task_comment=${OPERATOR} commented on your bug: ${title}
message.bug_task_at_comment=${OPERATOR} commented on the bug: ${title} and @you
message.bug_task_reply_comment=${OPERATOR} in defect ${title} replied to your comment
message.bug_sync_task_execute_completed=${OPERATOR} synchronized ${total} bugs
message.bug_task_assign=${OPERATOR} assign you a bug: ${title}
message.functional_case_task_create=${OPERATOR} created the functional case: ${name}
message.functional_case_task_update=${OPERATOR} updated the functional case: ${name}
message.functional_case_task_delete=${OPERATOR} deleted the functional case: ${name}
@ -305,6 +308,7 @@ message.title.bug_task_update=Bug update notification
message.title.bug_task_delete=Bug deletion notification
message.title.bug_task_comment=Bug comment notification
message.title.bug_sync_task_execute_completed=Synchronization bug execution completion notification
message.title.bug_task_assign=Bug assignment notification
message.title.functional_case_task_create=Functional case creation notification
message.title.functional_case_task_update=Functional case update notification
message.title.functional_case_task_delete=Functional case deletion notification

View File

@ -229,10 +229,12 @@ message.execute_fail=执行不通过
message.execute_at=执行被@
message.open=开启
message.close=关闭
message.assign=分配
message.sync_completed=同步完成
message.create_user=创建人
message.follow_people=关注人
message.operator=操作人
message.handle_user=处理人 (第三方平台的处理人, 不会接收到通知)
message.trigger_mode=触发方式
message.jenkins_name=名称
message.custom_field=自定义字段
@ -250,6 +252,7 @@ message.bug_task_comment=${OPERATOR}评论了你的缺陷:${title}
message.bug_task_at_comment=${OPERATOR}评论了缺陷:${title} 并@了你
message.bug_task_reply_comment=${OPERATOR}在缺陷 ${title} 回复了你的评论
message.bug_sync_task_execute_completed=${OPERATOR}同步了${total}条缺陷
message.bug_task_assign=${OPERATOR}给你分配了一个缺陷: ${title}
message.functional_case_task_create=${OPERATOR}创建了功能用例:${name}
message.functional_case_task_update=${OPERATOR}更新了功能用例:${name}
message.functional_case_task_delete=${OPERATOR}删除了功能用例:${name}
@ -306,6 +309,7 @@ message.title.bug_task_update=缺陷更新通知
message.title.bug_task_delete=缺陷删除通知
message.title.bug_task_comment=缺陷评论通知
message.title.bug_sync_task_execute_completed=同步缺陷执行完成通知
message.title.bug_task_assign=缺陷分配通知
message.title.functional_case_task_create=功能用例创建通知
message.title.functional_case_task_update=功能用例更新通知
message.title.functional_case_task_delete=功能用例删除通知

View File

@ -229,10 +229,12 @@ message.execute_fail=執行不通過
message.execute_at=執行被@
message.open=開啟
message.close=關閉
message.assign=分配
message.sync_completed=同步完成
message.create_user=創建人
message.follow_people=關注人
message.operator=操作人
message.handle_user=處理人 (第三方平臺的處理人, 不會接收到通知)
message.trigger_mode=觸發方式
message.jenkins_name=名稱
message.custom_field=自訂字段
@ -250,6 +252,7 @@ message.bug_task_comment=${OPERATOR}評論了你的缺陷:${title}
message.bug_task_at_comment=${OPERATOR}評論了缺陷:${title} 並@了你
message.bug_task_reply_comment=${OPERATOR}在缺陷 ${title} 回覆了你的評論
message.bug_sync_task_execute_completed=${OPERATOR}同步了${total}條缺陷
message.bug_task_assign=${OPERATOR}給你分配了一個缺陷: ${title}
message.functional_case_task_create=${OPERATOR}創建了功能用例:${name}
message.functional_case_task_update=${OPERATOR}更新了功能用例:${name}
message.functional_case_task_delete=${OPERATOR}刪除了功能用例:${name}
@ -306,6 +309,7 @@ message.title.bug_task_update=缺陷更新通知
message.title.bug_task_delete=缺陷刪除通知
message.title.bug_task_comment=缺陷評論通知
message.title.bug_sync_task_execute_completed=同步缺陷執行完成通知
message.title.bug_task_assign=缺陷分配通知
message.title.functional_case_task_create=功能用例創建通知
message.title.functional_case_task_update=功能用例更新通知
message.title.functional_case_task_delete=功能用例刪除通知

View File

@ -203,7 +203,7 @@ public class BugService {
* 缺陷创建或者修改逻辑:
* 1. 判断所属项目是否关联第三方平台;
* 2. 第三方平台缺陷需调用插件同步缺陷至其他平台(自定义字段需处理);
* 3. 保存MS缺陷(基础字段, 自定义字段)
* 3. 保存MS缺陷(基础字段, 自定义字段) && 发送处理人通知
* 4. 处理附件(第三方平台缺陷需异步调用接口同步附件至第三方)
* 5. 处理富文本临时文件
* 6. 处理用例关联关系
@ -232,7 +232,7 @@ public class BugService {
}
}
// 处理基础字段
Bug bug = handleAndSaveBug(request, currentUser, platformName, platformBug);
Bug bug = handleAndSaveBugAndNotice(request, currentUser, platformName, platformBug);
// 处理自定义字段
handleAndSaveCustomFields(request, isUpdate, platformBug);
// 处理附件
@ -975,13 +975,13 @@ public class BugService {
}
/**
* 处理保存缺陷基础信息
* 处理保存缺陷基础信息并发送处理人通知
*
* @param request 请求参数
* @param currentUser 当前用户ID
* @param platformName 第三方平台名称
*/
private Bug handleAndSaveBug(BugEditRequest request, String currentUser, String platformName, PlatformBugUpdateDTO platformBug) {
private Bug handleAndSaveBugAndNotice(BugEditRequest request, String currentUser, String platformName, PlatformBugUpdateDTO platformBug) {
Bug bug = new Bug();
BeanUtils.copyBean(bug, request);
bug.setPlatform(platformName);
@ -1037,7 +1037,8 @@ public class BugService {
}
}
//保存基础信息
boolean noticeHandler = false;
// 保存基础信息
if (StringUtils.isEmpty(bug.getId())) {
bug.setId(IDGenerator.nextStr());
bug.setNum(Long.valueOf(NumGenerator.nextNum(request.getProjectId(), ApplicationNumScope.BUG_MANAGEMENT)).intValue());
@ -1054,11 +1055,13 @@ public class BugService {
bugContent.setBugId(bug.getId());
bugContent.setDescription(StringUtils.isEmpty(request.getDescription()) ? StringUtils.EMPTY : request.getDescription());
bugContentMapper.insert(bugContent);
noticeHandler = true;
} else {
Bug originalBug = checkBugExist(request.getId());
// 追加处理人
if (!StringUtils.equals(originalBug.getHandleUser(), bug.getHandleUser())) {
bug.setHandleUsers(originalBug.getHandleUsers() + "," + bug.getHandleUser());
noticeHandler = true;
}
bug.setUpdateUser(currentUser);
bug.setUpdateTime(System.currentTimeMillis());
@ -1068,6 +1071,11 @@ public class BugService {
bugContent.setDescription(StringUtils.isEmpty(request.getDescription()) ? StringUtils.EMPTY : request.getDescription());
bugContentMapper.updateByPrimaryKeySelective(bugContent);
}
// 异步发送处理人通知 (第三方不通知 && 处理人没更改不通知)
if (StringUtils.equals(platformName, BugPlatform.LOCAL.getName()) && noticeHandler) {
bugSyncNoticeService.sendHandleUserNotice(bug, currentUser);
}
return bug;
}

View File

@ -1,19 +1,27 @@
package io.metersphere.bug.service;
import com.google.common.collect.Maps;
import io.metersphere.bug.domain.Bug;
import io.metersphere.project.service.ProjectApplicationService;
import io.metersphere.system.domain.User;
import io.metersphere.system.mapper.UserMapper;
import io.metersphere.system.notice.MessageDetail;
import io.metersphere.system.notice.NoticeModel;
import io.metersphere.system.notice.Receiver;
import io.metersphere.system.notice.constants.NoticeConstants;
import io.metersphere.system.notice.constants.NotificationConstants;
import io.metersphere.system.notice.sender.impl.InSiteNoticeSender;
import io.metersphere.system.notice.utils.MessageTemplateUtils;
import io.metersphere.system.service.NoticeSendService;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -23,13 +31,14 @@ public class BugSyncNoticeService {
@Resource
private UserMapper userMapper;
@Resource
private NoticeSendService noticeSendService;
@Resource
private InSiteNoticeSender inSiteNoticeSender;
@Resource
private ProjectApplicationService projectApplicationService;
@Async
public void sendNotice(int total, String currentUser, String language, String triggerMode, String projectId) {
String platformName = projectApplicationService.getPlatformName(projectId);
User user = userMapper.selectByPrimaryKey(currentUser);
@ -51,6 +60,33 @@ public class BugSyncNoticeService {
noticeSendService.send(NoticeConstants.TaskType.BUG_SYNC_TASK, noticeModel);
}
/**
* 处理人通知为站内通知
* @param bug 缺陷
* @param currentUser 当前用户
*/
@Async
public void sendHandleUserNotice(Bug bug, String currentUser) {
User user = userMapper.selectByPrimaryKey(currentUser);
setLanguage(user.getLanguage());
Map<String, String> defaultTemplateMap = MessageTemplateUtils.getDefaultTemplateMap();
String context = defaultTemplateMap.get(NoticeConstants.TemplateText.BUG_TASK_ASSIGN);
Map<String, String> defaultSubjectMap = MessageTemplateUtils.getDefaultTemplateSubjectMap();
String subject = defaultSubjectMap.get(NoticeConstants.TemplateText.BUG_TASK_ASSIGN);
// ${OPERATOR}给你分配了一个缺陷: ${title}
Map<String, Object> paramMap = Maps.newHashMapWithExpectedSize(8);
paramMap.put(NoticeConstants.RelatedUser.OPERATOR, user.getName());
paramMap.put("id", bug.getId());
paramMap.put("title", bug.getTitle());
paramMap.put("projectId", bug.getProjectId());
MessageDetail messageDetail = new MessageDetail();
messageDetail.setProjectId(bug.getProjectId());
messageDetail.setTaskType(NoticeConstants.TaskType.BUG_TASK);
NoticeModel noticeModel = NoticeModel.builder().operator(currentUser).excludeSelf(true).receivers(List.of(new Receiver(bug.getHandleUser(), NotificationConstants.Type.SYSTEM_NOTICE.name())))
.context(context).subject(subject).paramMap(paramMap).event(NoticeConstants.Event.ASSIGN).build();
inSiteNoticeSender.sendAnnouncement(messageDetail, noticeModel, MessageTemplateUtils.getContent(context, paramMap), subject);
}
private static void setLanguage(String language) {
Locale locale = Locale.SIMPLIFIED_CHINESE;
if (StringUtils.containsIgnoreCase("US",language)) {

View File

@ -4,7 +4,10 @@ package io.metersphere.project.service;
import io.metersphere.project.domain.*;
import io.metersphere.project.dto.*;
import io.metersphere.project.enums.result.ProjectResultCode;
import io.metersphere.project.mapper.*;
import io.metersphere.project.mapper.ExtProjectUserRoleMapper;
import io.metersphere.project.mapper.MessageTaskBlobMapper;
import io.metersphere.project.mapper.MessageTaskMapper;
import io.metersphere.project.mapper.ProjectRobotMapper;
import io.metersphere.sdk.constants.TemplateScene;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.BeanUtils;
@ -257,6 +260,9 @@ public class NoticeMessageTaskService {
if (receiverIds.contains(NoticeConstants.RelatedUser.OPERATOR)) {
userIds.add(NoticeConstants.RelatedUser.OPERATOR);
}
if (receiverIds.contains(NoticeConstants.RelatedUser.HANDLE_USER)) {
userIds.add(NoticeConstants.RelatedUser.HANDLE_USER);
}
Map<String, List<String>> map = new HashMap<>();
List<String> noUserNames = new ArrayList<>();
if (userIds.size() < receiverIds.size()) {

View File

@ -196,6 +196,10 @@
{
"id": "FOLLOW_PEOPLE",
"name": ""
},
{
"id": "HANDLE_USER",
"name": ""
}
],
"projectRobotConfigList":[
@ -218,6 +222,10 @@
{
"id": "CREATE_USER",
"name": ""
},
{
"id": "HANDLE_USER",
"name": ""
}
],
"projectRobotConfigList":[
@ -240,6 +248,10 @@
{
"id": "CREATE_USER",
"name": ""
},
{
"id": "HANDLE_USER",
"name": ""
}
],
"projectRobotConfigList":[

View File

@ -578,7 +578,7 @@ public class NoticeMessageTaskControllerTests extends BaseTest {
String contentAsString = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(contentAsString, ResultHolder.class);
List<OptionDTO> userDtoList = JSON.parseArray(JSON.toJSONString(resultHolder.getData()), OptionDTO.class);
Assertions.assertEquals(3, userDtoList.size());
Assertions.assertEquals(4, userDtoList.size());
}
@Test

View File

@ -53,7 +53,7 @@ public class NoticeModel implements Serializable {
*/
private List<Receiver> recipients;
/**
* 包括自己
* 排除自己
*/
private boolean excludeSelf;
}

View File

@ -189,6 +189,9 @@ public interface NoticeConstants {
@Schema(description = "message.close")
String CLOSE = "CLOSE";
@Schema(description = "message.assign")
String ASSIGN = "ASSIGN";
}
interface RelatedUser {
@ -199,6 +202,8 @@ public interface NoticeConstants {
String FOLLOW_PEOPLE = "FOLLOW_PEOPLE";//关注人
@Schema(description = "message.operator")
String OPERATOR = "OPERATOR"; //操作人
@Schema(description = "message.handle_user")
String HANDLE_USER = "HANDLE_USER"; //处理人
}
interface FieldSource {
@ -239,6 +244,8 @@ public interface NoticeConstants {
String BUG_TASK_REPLY = "BUG_TASK_REPLY";//${OPERATOR}在缺陷${title} 回复了你的评论
@Schema(description = "message.bug_sync_task_execute_completed")
String BUG_SYNC_TASK_EXECUTE_COMPLETED = "BUG_SYNC_TASK_EXECUTE_COMPLETED";//${OPERATOR}同步了${total}条缺陷
@Schema(description = "message.bug_task_assign")
String BUG_TASK_ASSIGN = "BUG_TASK_ASSIGN";//${OPERATOR}给你分配了一个缺陷: ${title}
@Schema(description = "message.functional_case_task_create")
String FUNCTIONAL_CASE_TASK_CREATE = "FUNCTIONAL_CASE_TASK_CREATE"; // ${OPERATOR}创建了功能用例:${name}
@ -357,6 +364,8 @@ public interface NoticeConstants {
String BUG_TASK_REPLY = "BUG_TASK_REPLY";
@Schema(description = "message.title.bug_sync_task_execute_completed")//同步缺陷执行完成通知
String BUG_SYNC_TASK_EXECUTE_COMPLETED = "BUG_SYNC_TASK_EXECUTE_COMPLETED";
@Schema(description = "message.title.bug_task_assign") // 缺陷分配通知
String BUG_TASK_ASSIGN = "BUG_TASK_ASSIGN";
@Schema(description = "message.title.functional_case_task_create")//功能用例创建通知

View File

@ -157,6 +157,16 @@ public abstract class AbstractNoticeSender implements NoticeSender {
toUsers.add(new Receiver(operator, NotificationConstants.Type.SYSTEM_NOTICE.name()));
}
}
// 处理人(缺陷)
case NoticeConstants.RelatedUser.HANDLE_USER -> {
String handleUser = (String) paramMap.get(NoticeConstants.RelatedUser.HANDLE_USER);
if (StringUtils.isNotBlank(handleUser)) {
toUsers.add(new Receiver(handleUser, NotificationConstants.Type.SYSTEM_NOTICE.name()));
} else {
Receiver receiver = handleHandler(messageDetail, noticeModel);
toUsers.add(receiver);
}
}
case NoticeConstants.RelatedUser.FOLLOW_PEOPLE -> {
try {
List<String> followUser = (List) paramMap.get("followUsers");
@ -267,6 +277,27 @@ public abstract class AbstractNoticeSender implements NoticeSender {
return receiver;
}
/**
* 处理人字段
*
* @param messageDetail
* @param noticeModel
* @return 通知接收人
*/
private Receiver handleHandler(MessageDetail messageDetail, NoticeModel noticeModel) {
String id = (String) noticeModel.getParamMap().get("id");
if (StringUtils.isBlank(id)) {
return null;
}
Receiver receiver = null;
Bug bug = bugMapper.selectByPrimaryKey(id);
if (bug != null && StringUtils.equals(bug.getPlatform(), "Local")) {
// 本地缺陷的处理人才需要通知
receiver = new Receiver(bug.getHandleUser(), NotificationConstants.Type.SYSTEM_NOTICE.name());
}
return receiver;
}
private List<Receiver> handleFollows(MessageDetail messageDetail, NoticeModel noticeModel) {
List<Receiver> receivers = new ArrayList<>();
String id = (String) noticeModel.getParamMap().get("id");

View File

@ -399,7 +399,9 @@
initValue = JSON.parse(item.defaultValue);
}
} else if (multipleType.includes(item.type)) {
if (item.defaultValue && item.defaultValue.length > 0) {
if (item.defaultValue && Array.isArray(item.defaultValue) && item.defaultValue.length > 0) {
initValue = item.defaultValue;
} else if (item.defaultValue && typeof item.defaultValue === 'string') {
initValue = item.defaultValue ? JSON.parse(item.defaultValue) : [];
}
} else if (numberType.includes(item.type)) {

View File

@ -54,7 +54,7 @@
class="w-full"
mode="remote"
:options="defaultReceivers"
:remote-filter-func="(opts) => getReceiverOptions(opts, record.event)"
:remote-filter-func="(opts) => getReceiverOptions(opts, record.event, record.taskType)"
:search-keys="['label']"
allow-search
:at-least-one="true"
@ -330,15 +330,19 @@
}));
}
function getReceiverOptions(options: SelectOptionData[], event: string) {
function getReceiverOptions(options: SelectOptionData[], event: string, taskType: string) {
if (event === 'CREATE' || event === 'CASE_CREATE' || event === 'MOCK_CREATE') {
//
options = options.filter((e) => !['OPERATOR', 'CREATE_USER', 'FOLLOW_PEOPLE'].includes(e.id));
options = options.filter((e) => !['OPERATOR', 'CREATE_USER', 'FOLLOW_PEOPLE', 'HANDLE_USER'].includes(e.id));
}
if (event.indexOf('EXECUTE') === -1) {
//
options = options.filter((e) => !['OPERATOR'].includes(e.id));
}
if (taskType === 'BUG_SYNC_TASK') {
// ,
options = options.filter((e) => !['CREATE_USER', 'FOLLOW_PEOPLE', 'HANDLE_USER'].includes(e.id));
}
return options;
}