fix(缺陷管理): 工作流被删除编辑时展示有误

--bug=1036430 --user=宋昌昌 【缺陷管理】已使用工作流-删除-编辑缺陷下拉应该显示其他状态 https://www.tapd.cn/55049933/s/1481941
This commit is contained in:
song-cc-rock 2024-03-27 13:47:26 +08:00 committed by 刘瑞斌
parent 17750d5636
commit 129e67b3f5
12 changed files with 128 additions and 25 deletions

View File

@ -5,6 +5,7 @@ import io.metersphere.bug.dto.request.BugFileSourceRequest;
import io.metersphere.bug.dto.request.BugFileTransferRequest;
import io.metersphere.bug.dto.request.BugUploadFileRequest;
import io.metersphere.bug.dto.response.BugFileDTO;
import io.metersphere.bug.service.BugAttachmentLogService;
import io.metersphere.bug.service.BugAttachmentService;
import io.metersphere.project.dto.filemanagement.request.FileMetadataTableRequest;
import io.metersphere.project.dto.filemanagement.response.FileInformationResponse;
@ -13,6 +14,8 @@ import io.metersphere.project.service.FileMetadataService;
import io.metersphere.project.service.FileModuleService;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.system.dto.sdk.BaseTreeNode;
import io.metersphere.system.log.annotation.Log;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.security.CheckOwner;
import io.metersphere.system.utils.Pager;
import io.metersphere.system.utils.SessionUtils;
@ -62,6 +65,7 @@ public class BugAttachmentController {
@Operation(summary = "缺陷管理-附件-上传/关联文件")
@RequiresPermissions(PermissionConstants.PROJECT_BUG_UPDATE)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
@Log(type = OperationLogType.UPDATE, expression = "#msClass.uploadLog(#request, #file)", msClass = BugAttachmentLogService.class)
public void uploadFile(@Validated @RequestPart("request") BugUploadFileRequest request, @RequestPart(value = "file", required = false) MultipartFile file) {
bugAttachmentService.uploadFile(request, file, SessionUtils.getUserId());
}
@ -70,6 +74,7 @@ public class BugAttachmentController {
@Operation(summary = "缺陷管理-附件-删除/取消关联文件")
@RequiresPermissions(PermissionConstants.PROJECT_BUG_UPDATE)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
@Log(type = OperationLogType.UPDATE, expression = "#msClass.deleteLog(#request)", msClass = BugAttachmentLogService.class)
public void deleteFile(@RequestBody BugDeleteFileRequest request) {
bugAttachmentService.deleteFile(request);
}

View File

@ -42,6 +42,7 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Objects;
/**
* @author song-cc-rock
@ -134,8 +135,8 @@ public class BugController {
@GetMapping("/delete/{id}")
@Operation(summary = "缺陷管理-列表-删除缺陷")
@RequiresPermissions(PermissionConstants.PROJECT_BUG_DELETE)
@SendNotice(taskType = NoticeConstants.TaskType.BUG_TASK, event = NoticeConstants.Event.DELETE, target = "#targetClass.getNoticeById(#id)", targetClass = BugNoticeService.class)
@Log(type = OperationLogType.DELETE, expression = "#msClass.deleteLog(#id)", msClass = BugLogService.class)
@SendNotice(taskType = NoticeConstants.TaskType.BUG_TASK, event = NoticeConstants.Event.DELETE, target = "#targetClass.getNoticeById(#id)", targetClass = BugNoticeService.class)
public void delete(@PathVariable String id) {
bugService.delete(id, SessionUtils.getUserId());
}
@ -145,7 +146,7 @@ public class BugController {
@RequiresPermissions(PermissionConstants.PROJECT_BUG_UPDATE)
@CheckOwner(resourceId = "#projectId", resourceType = "project")
public void sync(@PathVariable String projectId) {
bugSyncService.syncBugs(projectId, SessionUtils.getUserId());
bugSyncService.syncBugs(projectId, SessionUtils.getUserId(), Objects.requireNonNull(SessionUtils.getUser()).getLanguage());
}
@PostMapping("/sync/all")
@ -153,7 +154,7 @@ public class BugController {
@RequiresPermissions(PermissionConstants.PROJECT_BUG_UPDATE)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public void syncAll(@RequestBody BugSyncRequest request) {
bugSyncService.syncAllBugs(request, SessionUtils.getUserId());
bugSyncService.syncAllBugs(request, SessionUtils.getUserId(), Objects.requireNonNull(SessionUtils.getUser()).getLanguage());
}
@GetMapping("/sync/check/{projectId}")

View File

@ -0,0 +1,86 @@
package io.metersphere.bug.service;
import io.metersphere.bug.domain.Bug;
import io.metersphere.bug.dto.request.BugDeleteFileRequest;
import io.metersphere.bug.dto.request.BugUploadFileRequest;
import io.metersphere.bug.dto.response.BugFileDTO;
import io.metersphere.bug.mapper.BugMapper;
import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.log.dto.LogDTO;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.stream.Collectors;
@Service
@Transactional(rollbackFor = Exception.class)
public class BugAttachmentLogService {
@Resource
private BugMapper bugMapper;
@Resource
private BugAttachmentService bugAttachmentService;
/**
* 更新缺陷-上传附件日志
*
* @param request 请求参数
* @param file 上传的文件
* @return 日志
*/
public LogDTO uploadLog(BugUploadFileRequest request, MultipartFile file) {
Bug bug = bugMapper.selectByPrimaryKey(request.getBugId());
List<BugFileDTO> allBugFiles = bugAttachmentService.getAllBugFiles(request.getBugId());
List<String> originalFileNames = allBugFiles.stream().map(BugFileDTO::getFileName).collect(Collectors.toList());
LogDTO dto = buildUpdateLog(bug);
dto.setModifiedValue(JSON.toJSONBytes(originalFileNames));
originalFileNames.add(file.getOriginalFilename());
dto.setOriginalValue(JSON.toJSONBytes(originalFileNames));
return dto;
}
/**
* 更新缺陷-删除附件日志
*
* @param request 请求参数
* @return 日志
*/
public LogDTO deleteLog(BugDeleteFileRequest request) {
Bug bug = bugMapper.selectByPrimaryKey(request.getBugId());
List<BugFileDTO> allBugFiles = bugAttachmentService.getAllBugFiles(request.getBugId());
List<String> originalFileNames = allBugFiles.stream().map(BugFileDTO::getFileName).collect(Collectors.toList());
BugFileDTO deleteFile;
if (request.getAssociated()) {
// 关联
deleteFile = allBugFiles.stream().filter(file -> !file.getLocal() && StringUtils.equals(request.getRefId(), file.getRefId())).toList().get(0);
} else {
// 本地
deleteFile = allBugFiles.stream().filter(file -> file.getLocal() && StringUtils.equals(request.getRefId(), file.getRefId())).toList().get(0);
}
LogDTO dto = buildUpdateLog(bug);
dto.setModifiedValue(JSON.toJSONBytes(originalFileNames));
originalFileNames.remove(deleteFile.getFileName());
dto.setOriginalValue(JSON.toJSONBytes(originalFileNames));
return dto;
}
/**
* 构建更新的缺陷日志对象
* @param bug 缺陷内容
* @return 日志对象
*/
private LogDTO buildUpdateLog(Bug bug) {
LogDTO dto = new LogDTO(bug.getProjectId(), null, bug.getId(), null, OperationLogType.UPDATE.name(), OperationLogModule.BUG_MANAGEMENT_INDEX, bug.getTitle());
dto.setHistory(true);
dto.setPath("/bug/update");
dto.setMethod(HttpMethodConstants.POST.name());
return dto;
}
}

View File

@ -520,11 +520,11 @@ public class BugService {
* @param currentUser 当前用户
*/
@Async
public void syncPlatformAllBugs(BugSyncRequest request, Project project, String currentUser) {
public void syncPlatformAllBugs(BugSyncRequest request, Project project, String currentUser, String language) {
try {
XpackBugService bugService = CommonBeanFactory.getBean(XpackBugService.class);
if (bugService != null) {
bugService.syncPlatformBugs(project, request, currentUser);
bugService.syncPlatformBugs(project, request, currentUser, language);
}
} catch (Exception e) {
LogUtils.error(e);
@ -542,7 +542,7 @@ public class BugService {
* @param project 项目
*/
@Async
public void syncPlatformBugs(List<Bug> remainBugs, Project project, String currentUser) {
public void syncPlatformBugs(List<Bug> remainBugs, Project project, String currentUser, String language) {
try {
// 分页同步
SubListUtils.dealForSubList(remainBugs, 100, (subBugs) -> doSyncPlatformBugs(subBugs, project));
@ -554,7 +554,7 @@ public class BugService {
// 异常或正常结束都得删除当前项目执行同步的Key
bugSyncExtraService.deleteSyncKey(project.getId());
// 发送同步通知
bugSyncNoticeService.sendNotice(remainBugs.size(), currentUser, project.getId());
bugSyncNoticeService.sendNotice(remainBugs.size(), currentUser, language, project.getId());
}
}

View File

@ -23,7 +23,7 @@ public class BugSyncNoticeService {
@Resource
private NoticeSendService noticeSendService;
public void sendNotice(int total, String currentUser, String projectId) {
public void sendNotice(int total, String currentUser, String language, String projectId) {
User user = userMapper.selectByPrimaryKey(currentUser);
Map<String, String> defaultTemplateMap = MessageTemplateUtils.getDefaultTemplateMap();
String template = defaultTemplateMap.get(NoticeConstants.TemplateText.BUG_SYNC_TASK_EXECUTE_COMPLETED);
@ -34,6 +34,7 @@ public class BugSyncNoticeService {
paramMap.put(NoticeConstants.RelatedUser.OPERATOR, user.getName());
paramMap.put("total", total);
paramMap.put("projectId", projectId);
paramMap.put("Language", language);
NoticeModel noticeModel = NoticeModel.builder().operator(currentUser)
.context(template).subject(subject).paramMap(paramMap).event(NoticeConstants.Event.EXECUTE_COMPLETED).build();
noticeSendService.send(NoticeConstants.TaskType.BUG_SYNC_TASK, noticeModel);

View File

@ -22,6 +22,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
@ -52,7 +53,7 @@ public class BugSyncService {
* @param request 同步全量参数
* @param currentUser 当前用户
*/
public void syncAllBugs(BugSyncRequest request, String currentUser) {
public void syncAllBugs(BugSyncRequest request, String currentUser, String language) {
try {
// 获取当前项目同步缺陷唯一Key
String syncValue = bugSyncExtraService.getSyncKey(request.getProjectId());
@ -60,7 +61,7 @@ public class BugSyncService {
// 不存在, 设置保证唯一性, 并开始同步
bugSyncExtraService.setSyncKey(request.getProjectId());
Project project = getProjectById(request.getProjectId());
bugService.syncPlatformAllBugs(request, project, currentUser);
bugService.syncPlatformAllBugs(request, project, currentUser, language);
}
} catch (Exception e) {
bugSyncExtraService.deleteSyncKey(request.getProjectId());
@ -72,7 +73,7 @@ public class BugSyncService {
* 开源用户 (同步存量缺陷)
* @param projectId 项目ID
*/
public void syncBugs(String projectId, String currentUser) {
public void syncBugs(String projectId, String currentUser, String language) {
try {
String syncValue = bugSyncExtraService.getSyncKey(projectId);
if (StringUtils.isEmpty(syncValue)) {
@ -98,7 +99,7 @@ public class BugSyncService {
Map<String, Template> templateMap = templates.stream().collect(Collectors.toMap(Template::getId, t -> t));
// 非插件默认模板且模板不存在, 无需同步
bugs.removeIf(bug -> !templateMap.containsKey(bug.getTemplateId()) && !StringUtils.equals(bug.getTemplateId(), pluginTemplate.getId()));
bugService.syncPlatformBugs(bugs, project, currentUser);
bugService.syncPlatformBugs(bugs, project, currentUser, language);
}
}
} catch (Exception e) {
@ -130,10 +131,10 @@ public class BugSyncService {
}
/**
* 定时任务同步缺陷(存量)
* 定时任务同步缺陷(存量-默认中文环境通知)
*/
public void syncPlatformBugBySchedule(String projectId, String scheduleUser) {
syncBugs(projectId, scheduleUser);
syncBugs(projectId, scheduleUser, Locale.SIMPLIFIED_CHINESE.getLanguage());
}
/**

View File

@ -20,7 +20,8 @@ public interface XpackBugService {
* @param project 项目
* @param request 同步请求参数
* @param currentUser 当前用户
* @param language 语言环境
*/
void syncPlatformBugs(Project project, BugSyncRequest request, String currentUser);
void syncPlatformBugs(Project project, BugSyncRequest request, String currentUser, String language);
}

View File

@ -634,13 +634,13 @@ public class BugControllerTests extends BaseTest {
List<Bug> remainBugs = bugMapper.selectByExample(example);
Project defaultProject = projectMapper.selectByPrimaryKey("default-project-for-bug");
// 同步第一次
bugService.syncPlatformBugs(remainBugs, defaultProject, "admin");
bugService.syncPlatformBugs(remainBugs, defaultProject, "admin", Locale.SIMPLIFIED_CHINESE.getLanguage());
// 同步第二次
renameLocalFile(updateRequest2.getId()); // 重命名后, 同步时会删除本地文件
bugService.syncPlatformBugs(remainBugs, defaultProject, "admin");
bugService.syncPlatformBugs(remainBugs, defaultProject, "admin", Locale.SIMPLIFIED_CHINESE.getLanguage());
// 同步第三次
deleteLocalFile(updateRequest2.getId()); // 手动删除关联的文件, 重新同步时会下载平台附件
bugService.syncPlatformBugs(remainBugs, defaultProject, "admin");
bugService.syncPlatformBugs(remainBugs, defaultProject, "admin", Locale.SIMPLIFIED_CHINESE.getLanguage());
// 全选删除所有Jira缺陷
BugBatchRequest request = new BugBatchRequest();
@ -719,9 +719,9 @@ public class BugControllerTests extends BaseTest {
Project project = projectMapper.selectByPrimaryKey("default-project-for-bug");
this.requestPostWithOk(BUG_SYNC_ALL, request);
BugService mockBugService = Mockito.mock(BugService.class);
Mockito.doThrow(new MSException("sync error!")).when(mockBugService).syncPlatformAllBugs(syncRequest, project, "admin");
Mockito.doThrow(new MSException("sync error!")).when(mockBugService).syncPlatformAllBugs(syncRequest, project, "admin", Locale.SIMPLIFIED_CHINESE.getLanguage());
ReflectionTestUtils.setField(bugSyncService, "bugService", mockBugService);
MSException msException = assertThrows(MSException.class, () -> bugSyncService.syncAllBugs(syncRequest, "admin"));
MSException msException = assertThrows(MSException.class, () -> bugSyncService.syncAllBugs(syncRequest, "admin", Locale.SIMPLIFIED_CHINESE.getLanguage()));
assertEquals(msException.getMessage(), "sync error!");
}

View File

@ -14,7 +14,7 @@ public class XpackBugMockServiceImpl implements XpackBugService {
}
@Override
public void syncPlatformBugs(Project project, BugSyncRequest request, String currentUser) {
public void syncPlatformBugs(Project project, BugSyncRequest request, String currentUser, String language) {
}
}

View File

@ -17,6 +17,7 @@ import io.metersphere.system.dto.sdk.request.StatusDefinitionUpdateRequest;
import io.metersphere.system.dto.sdk.request.StatusFlowUpdateRequest;
import io.metersphere.system.dto.sdk.request.StatusItemAddRequest;
import io.metersphere.system.mapper.StatusDefinitionMapper;
import io.metersphere.system.mapper.StatusItemMapper;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
@ -41,6 +42,8 @@ import java.util.stream.Collectors;
@Transactional(rollbackFor = Exception.class)
public class BaseStatusFlowSettingService {
@Resource
protected StatusItemMapper statusItemMapper;
@Resource
protected StatusDefinitionMapper statusDefinitionMapper;
@Resource
@ -280,8 +283,9 @@ public class BaseStatusFlowSettingService {
* @return 状态选项集合
*/
public List<SelectOption> getStatusTransitions(String scopeId, String scene, String targetStatusId) {
if (StringUtils.isBlank(targetStatusId)) {
// 创建时, 获取开始状态的选项值即可
StatusItem targetStatus = statusItemMapper.selectByPrimaryKey(targetStatusId);
if (StringUtils.isBlank(targetStatusId) || targetStatus == null) {
// 创建或该目标状态被删除时, 获取开始状态的选项值即可
List<StatusItem> statusItems = baseStatusItemService.getByScopeIdAndScene(scopeId, scene);
statusItems = baseStatusItemService.translateInternalStatusItem(statusItems);
List<String> statusIds = statusItems.stream().map(StatusItem::getId).toList();

View File

@ -92,7 +92,9 @@
<div v-for="element in couldSortColumn" :key="element.dataIndex" class="column-drag-item">
<div class="flex w-[60%] items-center">
<MsIcon type="icon-icon_drag" class="sort-handle cursor-move text-[16px] text-[var(--color-text-4)]" />
<span class="ml-[8px]">{{ t((element.title || element.columnTitle) as string) }}</span>
<span class="one-line-text ml-[8px] max-w-[85%]">{{
t((element.title || element.columnTitle) as string)
}}</span>
</div>
<a-switch v-model="element.showInTable" size="small" type="line" @change="handleSwitchChange" />
</div>

View File

@ -34,7 +34,9 @@
<div v-for="element in couldSortColumn" :key="element.dataIndex" class="column-drag-item">
<div class="flex w-[90%] items-center">
<MsIcon type="icon-icon_drag" class="sort-handle cursor-move text-[16px] text-[var(--color-text-4)]" />
<span class="ml-[8px]">{{ t((element.title || element.columnTitle) as string) }}</span>
<span class="one-line-text ml-[8px] max-w-[85%]">{{
t((element.title || element.columnTitle) as string)
}}</span>
</div>
<a-switch
v-model="element.showInTable"