fix(组织管理): 修复组织成员批量操作全选失败问题以及消息通知显示问题

--bug=1039487 --user=郭雨琦 https://www.tapd.cn/55049933/bugtrace/bugs/view/1155049933001039487
--bug=1039244 --user=郭雨琦 https://www.tapd.cn/55049933/bugtrace/bugs/view/1155049933001039244
This commit is contained in:
guoyuqi 2024-04-16 19:38:34 +08:00 committed by Craftsman
parent b3cd1fb950
commit a260e98116
8 changed files with 141 additions and 34 deletions

View File

@ -5,7 +5,7 @@ role.not.global.system=角色不是全局系统角色
role.not.contains.member=角色不包含系统成员角色
schedule.cron.error=Cron表达式错误
user.not.login=未获取到登录用户
user.not.empty=用户不为空
user.not.empty=用户不为空
user.not.exist=用户不存在
personal.no.permission=无权操作非本人账户
personal.change.password=修改了密码

View File

@ -75,7 +75,7 @@ public class FunctionalCaseController {
@PostMapping("/add")
@Operation(summary = "用例管理-功能用例-新增用例")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_ADD)
@SendNotice(taskType = NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK, event = NoticeConstants.Event.CREATE, target = "#targetClass.getMainFunctionalCaseDTO(#request.name, #request.caseEditType, #request.projectId, #request.customFields)", targetClass = FunctionalCaseNoticeService.class)
@SendNotice(taskType = NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK, event = NoticeConstants.Event.CREATE, target = "#targetClass.getAddMainFunctionalCaseDTO(#request, #request.customFields)", targetClass = FunctionalCaseNoticeService.class)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public FunctionalCase addFunctionalCase(@Validated @RequestPart("request") FunctionalCaseAddRequest request, @RequestPart(value = "files", required = false) List<MultipartFile> files) {
String userId = SessionUtils.getUserId();

View File

@ -7,6 +7,7 @@ import io.metersphere.functional.mapper.CaseReviewFunctionalCaseMapper;
import io.metersphere.functional.mapper.CaseReviewMapper;
import io.metersphere.functional.mapper.FunctionalCaseCustomFieldMapper;
import io.metersphere.functional.mapper.FunctionalCaseMapper;
import io.metersphere.functional.request.FunctionalCaseAddRequest;
import io.metersphere.functional.request.FunctionalCaseCommentRequest;
import io.metersphere.functional.request.FunctionalCaseEditRequest;
import io.metersphere.sdk.util.BeanUtils;
@ -128,6 +129,12 @@ public class FunctionalCaseNoticeService {
return optionDTOList;
}
public FunctionalCaseDTO getAddMainFunctionalCaseDTO(FunctionalCaseAddRequest request, List<CaseCustomFieldDTO> customFields){
FunctionalCaseEditRequest editRequest = new FunctionalCaseEditRequest();
BeanUtils.copyBean(editRequest, request);
return getMainFunctionalCaseDTO(editRequest,customFields);
}
public FunctionalCaseDTO getMainFunctionalCaseDTO(FunctionalCaseEditRequest request, List<CaseCustomFieldDTO> customFields) {
FunctionalCaseDTO functionalCaseDTO = new FunctionalCaseDTO();
if (StringUtils.isNotBlank(request.getId())) {

View File

@ -104,7 +104,7 @@ public class OrganizationController {
@GetMapping("/not-exist/user/list/{organizationId}")
@Operation(summary = "系统设置-组织-成员-获取不在当前组织的所有用户")
@RequiresPermissions(PermissionConstants.SYSTEM_USER_ROLE_READ)
@RequiresPermissions(PermissionConstants.ORGANIZATION_MEMBER_ADD)
public List<OptionDisabledDTO> getUserList(@PathVariable(value = "organizationId") String organizationId, @Schema(description = "查询关键字,根据用户名查询", requiredMode = Schema.RequiredMode.REQUIRED)
@RequestParam(value = "keyword", required = false) String keyword) {
return organizationService.getUserList(organizationId, keyword);

View File

@ -1,12 +1,11 @@
package io.metersphere.system.dto.request;
import io.metersphere.system.dto.sdk.BaseCondition;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.List;
/**
@ -14,7 +13,7 @@ import java.util.List;
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class OrganizationMemberRequestByOrg implements Serializable {
public class OrganizationMemberRequestByOrg extends BaseCondition {
/**
* 组织ID
@ -22,11 +21,15 @@ public class OrganizationMemberRequestByOrg implements Serializable {
@Schema(description = "组织ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{organization.id.not_blank}")
private String organizationId;
/**
* 成员ID集合
*/
@Schema(description = "成员ID集合", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "{user.id.not_blank}")
private List<String> memberIds;
@Schema(description = "是否选择所有数据")
private boolean selectAll;
@Schema(description = "不处理的ID")
private List<String> excludeIds;
}

View File

@ -450,17 +450,37 @@ public class OrganizationService {
public void addMemberByOrg(OrganizationMemberExtendRequest organizationMemberExtendRequest, String createUserId) {
String organizationId = organizationMemberExtendRequest.getOrganizationId();
checkOrgExistById(organizationId);
Map<String, User> userMap = checkUserExist(organizationMemberExtendRequest.getMemberIds());
Map<String, User> userMap;
userMap = getUserMap(organizationMemberExtendRequest);
Map<String, UserRole> userRoleMap = checkUseRoleExist(organizationMemberExtendRequest.getUserRoleIds(), organizationId);
setRelationByMemberAndGroupIds(organizationMemberExtendRequest, createUserId, userMap, userRoleMap, true);
}
private Map<String, User> getUserMap(OrganizationMemberExtendRequest organizationMemberExtendRequest) {
Map<String, User> userMap;
if(organizationMemberExtendRequest.isSelectAll()) {
OrganizationRequest organizationRequest = new OrganizationRequest();
BeanUtils.copyBean(organizationRequest, organizationMemberExtendRequest);
List<OrgUserExtend> orgUserExtends = extOrganizationMapper.listMemberByOrg(organizationRequest);
List<String> excludeIds = organizationMemberExtendRequest.getExcludeIds();
if (CollectionUtils.isNotEmpty(excludeIds)) {
userMap = orgUserExtends.stream().filter(user->!excludeIds.contains(user.getId())).collect(Collectors.toMap(User::getId, user -> user));
} else {
userMap = orgUserExtends.stream().collect(Collectors.toMap(User::getId, user -> user));
}
} else {
userMap = checkUserExist(organizationMemberExtendRequest.getMemberIds());
}
return userMap;
}
private void setRelationByMemberAndGroupIds(OrganizationMemberExtendRequest organizationMemberExtendRequest, String createUserId, Map<String, User> userMap, Map<String, UserRole> userRoleMap, boolean add) {
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
UserRoleRelationMapper userRoleRelationMapper = sqlSession.getMapper(UserRoleRelationMapper.class);
List<LogDTO> logDTOList = new ArrayList<>();
String organizationId = organizationMemberExtendRequest.getOrganizationId();
organizationMemberExtendRequest.getMemberIds().forEach(memberId -> {
userMap.keySet().forEach(memberId -> {
if (userMap.get(memberId) == null) {
throw new MSException("id:" + memberId + Translator.get("user.not.exist"));
}
@ -512,7 +532,8 @@ public class OrganizationService {
public void addMemberRole(OrganizationMemberExtendRequest organizationMemberExtendRequest, String userId) {
String organizationId = organizationMemberExtendRequest.getOrganizationId();
checkOrgExistById(organizationId);
Map<String, User> userMap = checkUserExist(organizationMemberExtendRequest.getMemberIds());
Map<String, User> userMap;
userMap = getUserMap(organizationMemberExtendRequest);
Map<String, UserRole> userRoleMap = checkUseRoleExist(organizationMemberExtendRequest.getUserRoleIds(), organizationId);
//在新增组织成员与用户组和组织的关系
setRelationByMemberAndGroupIds(organizationMemberExtendRequest, userId, userMap, userRoleMap, false);
@ -526,7 +547,20 @@ public class OrganizationService {
List<LogDTO> logDTOList = new ArrayList<>();
List<String> projectIds = orgMemberExtendProjectRequest.getProjectIds();
//用户不在当前组织内过掉
Map<String, User> userMap = checkUserExist(orgMemberExtendProjectRequest.getMemberIds());
Map<String, User> userMap;
if(orgMemberExtendProjectRequest.isSelectAll()) {
OrganizationRequest organizationRequest = new OrganizationRequest();
BeanUtils.copyBean(organizationRequest, orgMemberExtendProjectRequest);
List<OrgUserExtend> orgUserExtends = extOrganizationMapper.listMemberByOrg(organizationRequest);
List<String> excludeIds = orgMemberExtendProjectRequest.getExcludeIds();
if (CollectionUtils.isNotEmpty(excludeIds)) {
userMap = orgUserExtends.stream().filter(user->!excludeIds.contains(user.getId())).collect(Collectors.toMap(User::getId, user -> user));
} else {
userMap = orgUserExtends.stream().collect(Collectors.toMap(User::getId, user -> user));
}
} else {
userMap = checkUserExist(orgMemberExtendProjectRequest.getMemberIds());
}
List<String> userIds = userMap.values().stream().map(User::getId).toList();
userIds.forEach(memberId -> {
projectIds.forEach(projectId -> {
@ -875,6 +909,9 @@ public class OrganizationService {
*/
private Map<String, User> checkUserExist(List<String> userIds) {
UserExample userExample = new UserExample();
if (CollectionUtils.isEmpty(userIds)) {
throw new MSException(Translator.get("user.not.empty"));
}
userExample.createCriteria().andIdIn(userIds);
List<User> users = userMapper.selectByExample(userExample);
if (CollectionUtils.isEmpty(users)) {

View File

@ -69,11 +69,9 @@ public class OrganizationControllerTests extends BaseTest {
public void addMemberByOrgError() throws Exception {
//组织ID正确
// 成员选择为空
addOrganizationMemberError(ORGANIZATION_LIST_ADD_MEMBER, "sys_default_organization_3", Collections.emptyList(), Arrays.asList("sys_default_org_role_id_2", "sys_default_org_role_id_3"), status().isBadRequest());
addOrganizationMemberError(ORGANIZATION_LIST_ADD_MEMBER, "sys_default_organization_3", Collections.emptyList(), Arrays.asList("sys_default_org_role_id_2", "sys_default_org_role_id_3"), status().is5xxServerError());
// 成员都不存在
addOrganizationMemberError(ORGANIZATION_LIST_ADD_MEMBER, "sys_default_organization_3", Arrays.asList("sys_default_userX", "sys_default_userY"), Arrays.asList("sys_default_org_role_id_2", "sys_default_org_role_id_3"), status().is5xxServerError());
// 成员有一个不存在
addOrganizationMemberError(ORGANIZATION_LIST_ADD_MEMBER, "sys_default_organization_3", Arrays.asList("sys_default_user3", "sys_default_userY"), Arrays.asList("sys_default_org_role_id_2", "sys_default_org_role_id_3"), status().is5xxServerError());
// 用户组为空
addOrganizationMemberError(ORGANIZATION_LIST_ADD_MEMBER, "sys_default_organization_3", Arrays.asList("sys_default_user", "sys_default_user2"), Collections.emptyList(), status().isBadRequest());
// 用户组都不存在
@ -84,7 +82,7 @@ public class OrganizationControllerTests extends BaseTest {
addOrganizationMemberError(ORGANIZATION_LIST_ADD_MEMBER, "sys_default_organization_3", Collections.emptyList(), Collections.emptyList(), status().isBadRequest());
// 组织不存在
// 成员选择为空
addOrganizationMemberError(ORGANIZATION_LIST_ADD_MEMBER, "default-organization-x", Collections.emptyList(), Arrays.asList("sys_default_org_role_id_2", "sys_default_org_role_id_3"), status().isBadRequest());
addOrganizationMemberError(ORGANIZATION_LIST_ADD_MEMBER, "default-organization-x", Collections.emptyList(), Arrays.asList("sys_default_org_role_id_2", "sys_default_org_role_id_3"), status().is5xxServerError());
// 用户组不存在
addOrganizationMemberError(ORGANIZATION_LIST_ADD_MEMBER, "default-organization-x", Arrays.asList("sys_default_user", "sys_default_user2"), Collections.emptyList(), status().isBadRequest());
//成员和用户组都为空
@ -183,6 +181,9 @@ public class OrganizationControllerTests extends BaseTest {
organizationMemberRequest.setMemberIds(Arrays.asList("sys_default_user", "sys_default_user2"));
organizationMemberRequest.setUserRoleIds(List.of("sys_default_org_role_id_4"));
this.requestPost(ORGANIZATION_UPDATE_MEMBER_TO_ROLE, organizationMemberRequest, status().isOk());
organizationMemberRequest.setSelectAll(true);
organizationMemberRequest.setExcludeIds(List.of("sys_default_user2"));
this.requestPost(ORGANIZATION_UPDATE_MEMBER_TO_ROLE, organizationMemberRequest, status().isOk());
// 批量添加成员成功后, 验证是否添加成功
listByKeyWord("testUserTwo", "sys_default_organization_3", true, "sys_default_org_role_id_4", null, false, null, null);
}
@ -192,18 +193,16 @@ public class OrganizationControllerTests extends BaseTest {
public void updateOrgMemberToRoleError() throws Exception {
//组织ID正确
// 成员选择为空
addOrganizationMemberError(ORGANIZATION_UPDATE_MEMBER_TO_ROLE, "sys_default_organization_3", Collections.emptyList(), Arrays.asList("sys_default_org_role_id_2", "sys_default_org_role_id_3"), status().isBadRequest());
addOrganizationMemberError(ORGANIZATION_UPDATE_MEMBER_TO_ROLE, "sys_default_organization_3", Collections.emptyList(), Arrays.asList("sys_default_org_role_id_2", "sys_default_org_role_id_3"), status().is5xxServerError());
// 成员都不存在
addOrganizationMemberError(ORGANIZATION_UPDATE_MEMBER_TO_ROLE, "sys_default_organization_3", Arrays.asList("sys_default_userX", "sys_default_userY"), Arrays.asList("sys_default_org_role_id_2", "sys_default_org_role_id_3"), status().is5xxServerError());
// 成员有一个不存在
addOrganizationMemberError(ORGANIZATION_UPDATE_MEMBER_TO_ROLE, "sys_default_organization_3", Arrays.asList("sys_default_user3", "sys_default_userY"), Arrays.asList("sys_default_org_role_id_2", "sys_default_org_role_id_3"), status().is5xxServerError());
// 用户组不存在
addOrganizationMemberError(ORGANIZATION_UPDATE_MEMBER_TO_ROLE, "sys_default_organization_3", Arrays.asList("sys_default_user", "sys_default_user2"), Collections.emptyList(), status().isBadRequest());
//成员和用户组都为空
addOrganizationMemberError(ORGANIZATION_UPDATE_MEMBER_TO_ROLE, "sys_default_organization_3", Collections.emptyList(), Collections.emptyList(), status().isBadRequest());
// 组织不存在
// 成员选择为空
addOrganizationMemberError(ORGANIZATION_UPDATE_MEMBER_TO_ROLE, "default-organization-x", Collections.emptyList(), Arrays.asList("sys_default_org_role_id_2", "sys_default_org_role_id_3"), status().isBadRequest());
addOrganizationMemberError(ORGANIZATION_UPDATE_MEMBER_TO_ROLE, "default-organization-x", Collections.emptyList(), Arrays.asList("sys_default_org_role_id_2", "sys_default_org_role_id_3"), status().is5xxServerError());
// 用户组不存在
addOrganizationMemberError(ORGANIZATION_UPDATE_MEMBER_TO_ROLE, "default-organization-x", Arrays.asList("sys_default_user", "sys_default_user2"), Collections.emptyList(), status().isBadRequest());
//成员和用户组都为空
@ -229,6 +228,9 @@ public class OrganizationControllerTests extends BaseTest {
organizationMemberRequest.setMemberIds(Arrays.asList("sys_default_user"));
organizationMemberRequest.setProjectIds(Arrays.asList("sys_org_projectId2"));
this.requestPost(ORGANIZATION_PROJECT_ADD_MEMBER, organizationMemberRequest, status().isOk());
organizationMemberRequest.setSelectAll(true);
organizationMemberRequest.setExcludeIds(List.of("sys_org_projectId2"));
this.requestPost(ORGANIZATION_PROJECT_ADD_MEMBER, organizationMemberRequest, status().isOk());
// 批量添加成员成功后, 验证是否添加成功
listByKeyWord("testUserOne", "sys_default_organization_3", false, InternalUserRole.PROJECT_MEMBER.getValue(), "sys_org_projectId2", false, null, null);
}
@ -237,13 +239,13 @@ public class OrganizationControllerTests extends BaseTest {
@Order(10)
public void addOrgMemberToProjectError() throws Exception {
// 成员选择为空
addOrUpdateOrganizationProjectMemberError(ORGANIZATION_PROJECT_ADD_MEMBER, "sys_default_organization_3", Collections.emptyList(), Arrays.asList("projectId1", "projectId2"), status().isBadRequest());
addOrUpdateOrganizationProjectMemberError(ORGANIZATION_PROJECT_ADD_MEMBER, "sys_default_organization_3", Collections.emptyList(), Arrays.asList("projectId1", "projectId2"), status().is5xxServerError());
// 项目集合不存在
addOrUpdateOrganizationProjectMemberError(ORGANIZATION_PROJECT_ADD_MEMBER, "sys_default_organization_3", Arrays.asList("sys_default_user", "sys_default_user2"), Collections.emptyList(), status().isBadRequest());
//成员和项目集合都为空
addOrUpdateOrganizationProjectMemberError(ORGANIZATION_PROJECT_ADD_MEMBER, "sys_default_organization_3", Collections.emptyList(), Collections.emptyList(), status().isBadRequest());
// 成员选择为空
addOrUpdateOrganizationProjectMemberError(ORGANIZATION_PROJECT_ADD_MEMBER, "sys_default_organization_X", Collections.emptyList(), Arrays.asList("projectId1", "projectId2"), status().isBadRequest());
addOrUpdateOrganizationProjectMemberError(ORGANIZATION_PROJECT_ADD_MEMBER, "sys_default_organization_X", Collections.emptyList(), Arrays.asList("projectId1", "projectId2"), status().is5xxServerError());
}
@Test
@ -252,11 +254,15 @@ public class OrganizationControllerTests extends BaseTest {
OrganizationMemberExtendRequest organizationMemberRequest = new OrganizationMemberExtendRequest();
organizationMemberRequest.setOrganizationId("sys_default_organization_3");
organizationMemberRequest.setMemberIds(Arrays.asList("sys_default_user3"));
organizationMemberRequest.setSelectAll(true);
organizationMemberRequest.setExcludeIds(List.of("sys_default_userM"));
organizationMemberRequest.setUserRoleIds(Arrays.asList("sys_default_org_role_id_3"));
this.requestPost(ORGANIZATION_LIST_ADD_MEMBER, organizationMemberRequest, status().isOk());
listByKeyWord("testUserThree", "sys_default_organization_3", false, null, null, false, null, null);
listByKeyWord(null, "sys_default_organization_3", false, null, null, false, null, null);
organizationMemberRequest.setMemberIds(Arrays.asList("sys_default_user3"));
organizationMemberRequest.setSelectAll(true);
organizationMemberRequest.setUserRoleIds(Arrays.asList("sys_default_org_role_id_3"));
this.requestPost(ORGANIZATION_LIST_ADD_MEMBER, organizationMemberRequest, status().isOk());
}
@Test

View File

@ -74,18 +74,38 @@
<div class="flex items-center">
<MSAvatar :avatar="item.avatar" />
<div class="ml-[8px] flex">
<div class="font-medium text-[var(--color-text-1)]">{{ item.userName }}</div>
<a-tooltip v-if="translateTextToPX(item.subject) > 300">
<template #content>
<span>
{{ item.subject }}
</span>
</template>
<div class="one-line-text font-medium text-[var(--color-text-2)]" style="max-width: 300px">{{
item.subject
}}</div>
</a-tooltip>
<div v-else class="font-medium text-[var(--color-text-2)]">{{ item.subject }}</div>
<div class="font-medium text-[rgb(var(--primary-5))]"
>&nbsp;&nbsp;{{ t('ms.message.me', { var: '@' }) }}</div
>
</div>
</div>
<div class="ml-[50px] flex items-center">
<div class="font-medium text-[var(--color-text-2)]">{{ item.userName }}&nbsp;&nbsp;</div>
<a-tooltip v-if="translateTextToPX(item.userName) > 300">
<template #content>
<span>
{{ item.userName }}
</span>
</template>
<div class="one-line-text font-medium text-[var(--color-text-2)]" style="max-width: 300px">{{
item.userName
}}</div>
</a-tooltip>
<div v-else class="font-medium text-[var(--color-text-2)]">{{ item.userName }}</div>
<div class="font-medium text-[var(--color-text-2)]">{{ item.subject }}</div>
<MsButton @click="handleNameClick(item)">
<a-tooltip :content="item.resourceName" :mouse-enter-delay="300">
<div class="one-line-text max-w-[400px]">
<div class="one-line-text max-w-[300px]">
{{ item.resourceName }}
</div>
</a-tooltip>
@ -98,20 +118,53 @@
<div v-else>
<div class="flex items-center">
<a-badge v-if="item.status === 'UNREAD'" :count="9" dot :offset="[6, -2]">
<div class="one-line-text max-w-[400px] font-medium text-[var(--color-text-1)]">{{
t('ms.message.notice.title')
<a-tooltip v-if="translateTextToPX(item.subject) > 300">
<template #content>
<span>
{{ item.subject }}
</span>
</template>
<div class="one-line-text max-w-[300px] font-medium text-[var(--color-text-1)]">{{
item.subject
}}</div>
</a-tooltip>
<div v-else class="one-line-text max-w-[300px] font-medium text-[var(--color-text-1)]">{{
item.subject
}}</div>
</a-badge>
<div v-else class="one-line-text max-w-[400px] font-medium text-[var(--color-text-1)]">{{
t('ms.message.notice.title')
}}</div>
<a-tooltip v-if="item.status === 'READ' && translateTextToPX(item.subject) > 300">
<template #content>
<span>
{{ item.subject }}
</span>
</template>
<div class="one-line-text max-w-[300px] font-medium text-[var(--color-text-1)]">{{
item.subject
}}</div>
</a-tooltip>
<div
v-if="item.status === 'READ' && translateTextToPX(item.subject) <= 300"
class="one-line-text max-w-[300px] font-medium text-[var(--color-text-1)]"
>
{{ item.subject }}</div
>
</div>
<div class="flex items-center">
<div class="font-medium text-[var(--color-text-2)]">{{ item.userName }}&nbsp;&nbsp;</div>
<a-tooltip v-if="translateTextToPX(item.userName) > 300">
<template #content>
<span>
{{ item.userName }}
</span>
</template>
<div class="one-line-text font-medium text-[var(--color-text-2)]" style="max-width: 300px">{{
item.userName
}}</div>
</a-tooltip>
<div v-else class="font-medium text-[var(--color-text-2)]">{{ item.userName }}</div>
<div class="font-medium text-[var(--color-text-2)]">{{ item.subject }}</div>
<MsButton @click="handleNameClick(item)">
<a-tooltip :content="item.resourceName" :mouse-enter-delay="300">
<div class="one-line-text max-w-[400px]">
<div class="one-line-text max-w-[300px]">
{{ item.resourceName }}
</div>
</a-tooltip>
@ -152,6 +205,7 @@
import { getMessageList } from '@/api/modules/project-management/messageManagement';
import { useI18n } from '@/hooks/useI18n';
import usePathMap from '@/hooks/usePathMap';
import { translateTextToPX } from '@/utils/css';
import { MessageItem } from '@/models/projectManagement/message';
import { ProjectManagementRouteEnum } from '@/enums/routeEnum';