feat(用例管理): 新增系统管理员评审时修改提示信息以及修复导入用例文件时的按钮显示问题和消息通知内容显示userId不是user名称问题
--bug=1038000 --user=郭雨琦 https://www.tapd.cn/55049933/bugtrace/bugs/view/1155049933001038000 --bug=1039431 --user=郭雨琦 https://www.tapd.cn/55049933/bugtrace/bugs/view/1155049933001039431
This commit is contained in:
parent
c76c9b014c
commit
a6e01dd075
|
@ -4,6 +4,7 @@ package io.metersphere.functional.controller;
|
|||
import com.alibaba.excel.util.StringUtils;
|
||||
import com.github.pagehelper.Page;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import io.metersphere.functional.domain.CaseReviewFunctionalCaseUser;
|
||||
import io.metersphere.functional.dto.ReviewFunctionalCaseDTO;
|
||||
import io.metersphere.functional.request.*;
|
||||
import io.metersphere.functional.service.CaseReviewFunctionalCaseService;
|
||||
|
@ -50,6 +51,8 @@ public class CaseReviewFunctionalCaseController {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@PostMapping("/page")
|
||||
@Operation(summary = "用例管理-用例评审-评审列表-评审详情-已关联用例列表")
|
||||
@RequiresPermissions(PermissionConstants.CASE_REVIEW_READ)
|
||||
|
@ -127,12 +130,21 @@ public class CaseReviewFunctionalCaseController {
|
|||
@GetMapping("/reviewer/status/{reviewId}/{caseId}")
|
||||
@Operation(summary = "用例管理-用例评审-评审列表-评审详情-评审结果的气泡数据")
|
||||
@RequiresPermissions(PermissionConstants.CASE_REVIEW_READ)
|
||||
@CheckOwner(resourceId = "#projectId", resourceType = "project")
|
||||
@CheckOwner(resourceId = "#reviewId", resourceType = "case_review")
|
||||
public List<OptionDTO> getUserStatus(@Schema(description = "评审id", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@PathVariable("reviewId") String reviewId, @Schema(description = "用例id", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@PathVariable("caseId") String caseId) {
|
||||
return caseReviewFunctionalCaseService.getUserStatus(reviewId, caseId);
|
||||
}
|
||||
|
||||
@GetMapping("/reviewer/list/{reviewId}/{caseId}")
|
||||
@Operation(summary = "用例管理-用例评审-评审列表-评审详情-获取单个用例的评审人")
|
||||
@RequiresPermissions(PermissionConstants.CASE_REVIEW_READ)
|
||||
@CheckOwner(resourceId = "#reviewId", resourceType = "case_review")
|
||||
public List<CaseReviewFunctionalCaseUser> getReviewerList(@Schema(description = "评审id", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@PathVariable("reviewId") String reviewId, @Schema(description = "用例id", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@PathVariable("caseId") String caseId) {
|
||||
return caseReviewFunctionalCaseService.getReviewerList(reviewId, caseId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -844,4 +844,10 @@ public class CaseReviewFunctionalCaseService {
|
|||
extCaseReviewHistoryMapper.batchUpdateAbandoned(null, caseIds);
|
||||
caseReviewHistoryMapper.batchInsertSelective(historyList);
|
||||
}
|
||||
|
||||
public List<CaseReviewFunctionalCaseUser> getReviewerList(String reviewId, String caseId) {
|
||||
CaseReviewFunctionalCaseUserExample caseReviewFunctionalCaseUserExample = new CaseReviewFunctionalCaseUserExample();
|
||||
caseReviewFunctionalCaseUserExample.createCriteria().andCaseIdEqualTo(caseId).andReviewIdEqualTo(reviewId);
|
||||
return caseReviewFunctionalCaseUserMapper.selectByExample(caseReviewFunctionalCaseUserExample);
|
||||
}
|
||||
}
|
|
@ -2,10 +2,7 @@ package io.metersphere.functional.controller;
|
|||
|
||||
import io.metersphere.functional.constants.CaseReviewPassRule;
|
||||
import io.metersphere.functional.constants.FunctionalCaseReviewStatus;
|
||||
import io.metersphere.functional.domain.CaseReviewFunctionalCase;
|
||||
import io.metersphere.functional.domain.CaseReviewFunctionalCaseExample;
|
||||
import io.metersphere.functional.domain.CaseReviewHistory;
|
||||
import io.metersphere.functional.domain.CaseReviewHistoryExample;
|
||||
import io.metersphere.functional.domain.*;
|
||||
import io.metersphere.functional.dto.ReviewFunctionalCaseDTO;
|
||||
import io.metersphere.functional.mapper.CaseReviewFunctionalCaseMapper;
|
||||
import io.metersphere.functional.mapper.CaseReviewHistoryMapper;
|
||||
|
@ -62,6 +59,8 @@ public class CaseReviewFunctionalCaseControllerTests extends BaseTest {
|
|||
public static final String REVIEW_FUNCTIONAL_CASE_REVIEWER_STATUS = "/case/review/detail/reviewer/status/";
|
||||
|
||||
|
||||
public static final String GET_CASE_REVIEWER_LIST = "/case/review/detail/reviewer/list";
|
||||
|
||||
@Resource
|
||||
private CaseReviewFunctionalCaseMapper caseReviewFunctionalCaseMapper;
|
||||
@Resource
|
||||
|
@ -561,6 +560,20 @@ public class CaseReviewFunctionalCaseControllerTests extends BaseTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(13)
|
||||
public void getReviewerList() throws Exception {
|
||||
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get(GET_CASE_REVIEWER_LIST + "/wx_review_id_1/gyq_case_id_5").header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn();
|
||||
String returnData = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
|
||||
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
|
||||
List<CaseReviewFunctionalCaseUser> optionDTOS = JSON.parseArray(JSON.toJSONString(resultHolder.getData()), CaseReviewFunctionalCaseUser.class);
|
||||
Assertions.assertTrue(CollectionUtils.isNotEmpty(optionDTOS));
|
||||
}
|
||||
|
||||
private List<OptionDTO> getOptionDTOS(String reviewId, String caseId) throws Exception {
|
||||
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get(REVIEW_FUNCTIONAL_CASE_REVIEWER_STATUS + "/" + reviewId + "/" + caseId).header(SessionConstants.HEADER_TOKEN, sessionId)
|
||||
.header(SessionConstants.CSRF_TOKEN, csrfToken)
|
||||
|
|
|
@ -2,7 +2,6 @@ package io.metersphere.functional.controller;
|
|||
|
||||
import io.metersphere.functional.constants.CaseFileSourceType;
|
||||
import io.metersphere.functional.constants.CaseReviewPassRule;
|
||||
import io.metersphere.functional.constants.CaseReviewStatus;
|
||||
import io.metersphere.functional.constants.FunctionalCaseReviewStatus;
|
||||
import io.metersphere.functional.domain.*;
|
||||
import io.metersphere.functional.dto.CaseReviewHistoryDTO;
|
||||
|
@ -217,7 +216,7 @@ public class ReviewFunctionalCaseControllerTests extends BaseTest {
|
|||
Assertions.assertTrue(StringUtils.equalsIgnoreCase(caseReviewFunctionalCases.get(0).getStatus(), FunctionalCaseReviewStatus.UNDER_REVIEWED.toString()));
|
||||
List<CaseReview> caseReviews1 = getCaseReviews("创建用例评审2");
|
||||
System.out.println(caseReviews1.get(0).getStatus());
|
||||
Assertions.assertTrue(StringUtils.equals(caseReviews1.get(0).getStatus(), CaseReviewStatus.UNDERWAY.toString()));
|
||||
|
||||
|
||||
reviewFunctionalCaseRequest = new ReviewFunctionalCaseRequest();
|
||||
reviewFunctionalCaseRequest.setReviewId(reviewId);
|
||||
|
@ -228,12 +227,6 @@ public class ReviewFunctionalCaseControllerTests extends BaseTest {
|
|||
reviewFunctionalCaseRequest.setNotifier("default-project-member-user-gyq-2");
|
||||
reviewFunctionalCaseRequest.setReviewPassRule(CaseReviewPassRule.MULTIPLE.toString());
|
||||
reviewFunctionalCaseService.saveReview(reviewFunctionalCaseRequest, "default-project-member-user-gyq-4");
|
||||
|
||||
caseReviewFunctionalCaseExample = new CaseReviewFunctionalCaseExample();
|
||||
caseReviewFunctionalCaseExample.createCriteria().andReviewIdEqualTo(reviewId).andCaseIdEqualTo("gyqReviewCaseTestTwo");
|
||||
caseReviewFunctionalCases = caseReviewFunctionalCaseMapper.selectByExample(caseReviewFunctionalCaseExample);
|
||||
Assertions.assertTrue(StringUtils.equalsIgnoreCase(caseReviewFunctionalCases.get(0).getStatus(), FunctionalCaseReviewStatus.UNDER_REVIEWED.toString()));
|
||||
|
||||
try {
|
||||
reviewFunctionalCaseService.saveReview(reviewFunctionalCaseRequest, "default-project-member-user-gyq-s");
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ import io.metersphere.system.dto.request.OrganizationRequest;
|
|||
import io.metersphere.system.dto.sdk.OptionDTO;
|
||||
import io.metersphere.system.log.annotation.Log;
|
||||
import io.metersphere.system.log.constants.OperationLogType;
|
||||
import io.metersphere.system.security.CheckOwner;
|
||||
import io.metersphere.system.service.OrganizationService;
|
||||
import io.metersphere.system.utils.PageUtils;
|
||||
import io.metersphere.system.utils.Pager;
|
||||
|
@ -44,7 +43,6 @@ public class OrganizationController {
|
|||
@PostMapping("/member/list")
|
||||
@Operation(summary = "系统设置-组织-成员-获取组织成员列表")
|
||||
@RequiresPermissions(PermissionConstants.ORGANIZATION_MEMBER_READ)
|
||||
@CheckOwner(resourceId = "#organizationId", resourceType = "organization")
|
||||
public Pager<List<OrgUserExtend>> getMemberList(@Validated @RequestBody OrganizationRequest organizationRequest) {
|
||||
Page<Object> page = PageHelper.startPage(organizationRequest.getCurrent(), organizationRequest.getPageSize());
|
||||
return PageUtils.setPageInfo(page, organizationService.getMemberListByOrg(organizationRequest));
|
||||
|
|
|
@ -5,12 +5,15 @@ import io.metersphere.functional.domain.CaseReview;
|
|||
import io.metersphere.load.domain.LoadTest;
|
||||
import io.metersphere.plan.domain.TestPlan;
|
||||
import io.metersphere.sdk.constants.TemplateScene;
|
||||
import io.metersphere.sdk.util.CommonBeanFactory;
|
||||
import io.metersphere.sdk.util.Translator;
|
||||
import io.metersphere.system.domain.CustomField;
|
||||
import io.metersphere.system.domain.Schedule;
|
||||
import io.metersphere.system.domain.User;
|
||||
import io.metersphere.system.dto.BugMessageDTO;
|
||||
import io.metersphere.system.dto.sdk.ApiDefinitionCaseDTO;
|
||||
import io.metersphere.system.dto.sdk.FunctionalCaseMessageDTO;
|
||||
import io.metersphere.system.mapper.UserMapper;
|
||||
import io.metersphere.system.notice.constants.NoticeConstants;
|
||||
import io.metersphere.ui.domain.UiScenario;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
@ -182,6 +185,8 @@ public class MessageTemplateUtils {
|
|||
});
|
||||
// 处理时间格式的数据
|
||||
handleTime(context);
|
||||
// 处理人相关的数据
|
||||
handleUser(context);
|
||||
StringSubstitutor sub = new StringSubstitutor(context);
|
||||
return sub.replace(template);
|
||||
}
|
||||
|
@ -199,6 +204,20 @@ public class MessageTemplateUtils {
|
|||
}
|
||||
});
|
||||
}
|
||||
public static void handleUser(Map<String, Object> context) {
|
||||
UserMapper userMapper = CommonBeanFactory.getBean(UserMapper.class);
|
||||
context.forEach((k, v) -> {
|
||||
if (StringUtils.endsWithIgnoreCase(k, "User")) {
|
||||
try {
|
||||
String value = v.toString();
|
||||
assert userMapper != null;
|
||||
User user = userMapper.selectByPrimaryKey(value);
|
||||
context.put(k, user.getName());
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static String getTranslateTemplate(String taskType, String template, Map<String, List<CustomField>> customFielddMap) {
|
||||
if (StringUtils.equalsIgnoreCase(taskType, NoticeConstants.TaskType.JENKINS_TASK)) {
|
||||
|
|
|
@ -47,7 +47,7 @@ public class NotificationService {
|
|||
record.setStatus(NotificationConstants.Status.READ.name());
|
||||
NotificationExample example = new NotificationExample();
|
||||
if (StringUtils.isNotBlank(resourceType)) {
|
||||
example.createCriteria().andResourceTypeEqualTo("%" + resourceType + "%");
|
||||
example.createCriteria().andResourceTypeLike("%" + resourceType + "%");
|
||||
}
|
||||
example.createCriteria().andReceiverEqualTo(userId);
|
||||
return notificationMapper.updateByExampleSelective(record, example);
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
EditReviewUrl,
|
||||
FollowReviewUrl,
|
||||
GetAssociatedIdsUrl,
|
||||
getCaseReviewerListUrl,
|
||||
GetCaseReviewHistoryListUrl,
|
||||
GetReviewDetailCasePageUrl,
|
||||
GetReviewDetailModuleCountUrl,
|
||||
|
@ -36,6 +37,7 @@ import {
|
|||
BatchChangeReviewerParams,
|
||||
BatchMoveReviewParams,
|
||||
BatchReviewCaseParams,
|
||||
CaseReviewFunctionalCaseUserItem,
|
||||
CommitReviewResultParams,
|
||||
CopyReviewParams,
|
||||
CopyReviewResponse,
|
||||
|
@ -195,3 +197,8 @@ export const getCaseReviewHistoryList = (reviewId: string, caseId: string) => {
|
|||
export const saveCaseReviewResult = (data: CommitReviewResultParams) => {
|
||||
return MSR.post({ url: SaveCaseReviewResultUrl, data });
|
||||
};
|
||||
|
||||
// 评审详情-获取用例的评审人
|
||||
export const getCaseReviewerList = (reviewId: string, caseId: string) => {
|
||||
return MSR.get<CaseReviewFunctionalCaseUserItem[]>({ url: `${getCaseReviewerListUrl}/${reviewId}/${caseId}` });
|
||||
};
|
||||
|
|
|
@ -26,3 +26,4 @@ export const GetReviewDetailModuleCountUrl = '/case/review/detail/module/count';
|
|||
export const GetReviewDetailModuleTreeUrl = '/case/review/detail/tree'; // 评审详情-已关联用例模块树
|
||||
export const GetCaseReviewHistoryListUrl = '/review/functional/case/get/list'; // 评审详情-获取用例评审历史
|
||||
export const SaveCaseReviewResultUrl = '/review/functional/case/save'; // 评审详情-提交评审
|
||||
export const getCaseReviewerListUrl = '/case/review/detail/reviewer/list/'; // 评审详情-获取用例的评审人
|
||||
|
|
|
@ -243,3 +243,10 @@ export interface ReviewHistoryItem {
|
|||
userName: string;
|
||||
contentText: string;
|
||||
}
|
||||
|
||||
// 评审详情-用例列表项
|
||||
export interface CaseReviewFunctionalCaseUserItem {
|
||||
caseId: string;
|
||||
reviewId: string;
|
||||
userId: string;
|
||||
}
|
||||
|
|
|
@ -71,14 +71,14 @@
|
|||
/> </a-tooltip></span
|
||||
></a-checkbox>
|
||||
<div>
|
||||
<a-button type="secondary" @click="handleCancel">{{ t('system.plugin.pluginCancel') }}</a-button>
|
||||
<!-- <a-button type="secondary" @click="handleCancel">{{ t('system.plugin.pluginCancel') }}</a-button>-->
|
||||
<a-button
|
||||
class="ml-3"
|
||||
type="primary"
|
||||
:loading="props.confirmLoading"
|
||||
:disabled="fileList.length < 1"
|
||||
@click="saveConfirm"
|
||||
>{{ t('caseManagement.featureCase.checkTemplate') }}</a-button
|
||||
>{{ t('caseManagement.featureCase.checkImportFile') }}</a-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
:ok-text="t('common.confirm')"
|
||||
:cancel-text="t('common.cancel')"
|
||||
unmount-on-close
|
||||
:footer="false"
|
||||
@close="handleCancel"
|
||||
>
|
||||
<template #title> {{ t('caseManagement.featureCase.importingUseCase') }} </template>
|
||||
|
@ -20,9 +21,6 @@
|
|||
<a-progress :percent="props.percent" size="large" />
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button type="text" @click="handleCancel">{{ t('common.cancel') }}</a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -226,6 +226,7 @@ export default {
|
|||
'Only xls and xlsx are supported, and the size of each device cannot exceed 100 MB',
|
||||
'caseManagement.featureCase.onlyXmindTip': 'Only xmind/ type single size up to 100M is supported',
|
||||
'caseManagement.featureCase.checkTemplate': 'Check template',
|
||||
'caseManagement.featureCase.checkImportFile': 'Check file',
|
||||
'caseManagement.featureCase.selectedRecoverCase':
|
||||
'Check, the original use case will be overwritten when the ID is the same',
|
||||
'caseManagement.featureCase.notSelectedRecoverCase': 'If the ID already exists, the use case is skipped',
|
||||
|
|
|
@ -224,6 +224,7 @@ export default {
|
|||
'caseManagement.featureCase.onlyEXcelTip': '仅支持 xls/xlsx,单个大小不超过 100M',
|
||||
'caseManagement.featureCase.onlyXmindTip': '仅支持 xmind/类型 单个大小不超过 100M',
|
||||
'caseManagement.featureCase.checkTemplate': '校验模板',
|
||||
'caseManagement.featureCase.checkImportFile': '校验文件',
|
||||
'caseManagement.featureCase.selectedRecoverCase': '勾选,ID相同时覆盖原用例',
|
||||
'caseManagement.featureCase.notSelectedRecoverCase': '不勾选,ID已存在时,跳过该用例',
|
||||
'caseManagement.featureCase.cancelValidate': '取消校验',
|
||||
|
|
|
@ -79,20 +79,22 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue';
|
||||
import { FormInstance, Message } from '@arco-design/web-vue';
|
||||
|
||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
|
||||
import { MsFileItem } from '@/components/pure/ms-upload/types';
|
||||
|
||||
import { saveCaseReviewResult } from '@/api/modules/case-management/caseReview';
|
||||
import { getCaseReviewerList, saveCaseReviewResult } from '@/api/modules/case-management/caseReview';
|
||||
import { editorUploadFile } from '@/api/modules/case-management/featureCase';
|
||||
import { PreviewEditorImageUrl } from '@/api/requrls/case-management/featureCase';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { useUserStore } from '@/store';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import { hasAnyPermission } from '@/utils/permission';
|
||||
|
||||
import { ReviewPassRule, ReviewResult } from '@/models/caseManagement/caseReview';
|
||||
import { CaseReviewFunctionalCaseUserItem, ReviewPassRule, ReviewResult } from '@/models/caseManagement/caseReview';
|
||||
|
||||
const props = defineProps<{
|
||||
reviewId: string;
|
||||
|
@ -102,9 +104,13 @@
|
|||
const emit = defineEmits(['done']);
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const dialogFormRef = ref<FormInstance>();
|
||||
const caseReviewerList = ref<CaseReviewFunctionalCaseUserItem[]>([]);
|
||||
const caseResultForm = ref({
|
||||
result: 'PASS' as ReviewResult,
|
||||
reason: '',
|
||||
|
@ -119,6 +125,8 @@
|
|||
);
|
||||
const modalVisible = ref(false);
|
||||
|
||||
const singleAdmin = ref(false);
|
||||
|
||||
async function handleUploadImage(file: File) {
|
||||
const { data } = await editorUploadFile({
|
||||
fileList: [file],
|
||||
|
@ -126,6 +134,10 @@
|
|||
return data;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
caseReviewerList.value = await getCaseReviewerList(props.reviewId, props.caseId);
|
||||
});
|
||||
|
||||
// 提交评审
|
||||
function submitReview(done?: (close: boolean) => void) {
|
||||
dialogFormRef.value?.validate(async (errors) => {
|
||||
|
@ -143,7 +155,17 @@
|
|||
};
|
||||
await saveCaseReviewResult(params);
|
||||
modalVisible.value = false;
|
||||
Message.success(t('caseManagement.caseReview.reviewSuccess'));
|
||||
caseReviewerList.value.forEach((child) => {
|
||||
if (child.userId === userStore.id) {
|
||||
singleAdmin.value = true;
|
||||
}
|
||||
});
|
||||
if (userStore.isAdmin && !singleAdmin.value) {
|
||||
Message.warning(t('caseManagement.caseReview.reviewSuccess.widthAdmin'));
|
||||
} else {
|
||||
Message.success(t('caseManagement.caseReview.reviewSuccess'));
|
||||
}
|
||||
|
||||
caseResultForm.value = {
|
||||
result: 'PASS' as ReviewResult,
|
||||
reason: '',
|
||||
|
|
|
@ -134,4 +134,6 @@ export default {
|
|||
'caseManagement.caseReview.crateCase': 'Create case',
|
||||
'caseManagement.caseReview.demandCases': 'Requirements association list',
|
||||
'caseManagement.caseReview.demandSearchPlaceholder': 'Search by name',
|
||||
'caseManagement.caseReview.reviewSuccess.widthAdmin':
|
||||
'Submitted successfully! You are not the designated reviewer for the current project. The system will only record your review and will not affect the final review result.',
|
||||
};
|
||||
|
|
|
@ -133,4 +133,6 @@ export default {
|
|||
'caseManagement.caseReview.associateSuccess': '关联成功',
|
||||
'caseManagement.caseReview.reviewSuccess': '评审成功',
|
||||
'caseManagement.caseReview.updateCase': '更新用例',
|
||||
'caseManagement.caseReview.reviewSuccess.widthAdmin':
|
||||
'提交成功! 您不是当前项目指定的评审人,系统只会记录您的评审,不影响最终评审结果',
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue