refactor(系统设置): 组织至少保留一个管理员

This commit is contained in:
wxg0103 2024-02-07 15:08:01 +08:00 committed by wxg0103
parent 68bd55647d
commit 5662e5abe7
8 changed files with 75 additions and 45 deletions

View File

@ -9,6 +9,7 @@ import io.metersphere.project.request.ProjectMemberBatchDeleteRequest;
import io.metersphere.project.request.ProjectMemberEditRequest;
import io.metersphere.project.request.ProjectMemberRequest;
import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.sdk.constants.InternalUserRole;
import io.metersphere.sdk.constants.UserRoleEnum;
import io.metersphere.sdk.constants.UserRoleType;
import io.metersphere.sdk.exception.MSException;
@ -223,6 +224,14 @@ public class ProjectMemberService {
List<LogDTO> logs = new ArrayList<>();
// 项目不存在, 则不移除
checkProjectExist(projectId);
//判断用户是不是最后一个管理员 如果是 就报错
UserRoleRelationExample userRoleRelationExample = new UserRoleRelationExample();
userRoleRelationExample.createCriteria().andUserIdNotEqualTo(userId)
.andSourceIdEqualTo(projectId)
.andRoleIdEqualTo(InternalUserRole.PROJECT_ADMIN.getValue());
if (userRoleRelationMapper.countByExample(userRoleRelationExample) == 0) {
throw new MSException(Translator.get("keep_at_least_one_administrator"));
}
// 移除成员, 则移除该成员在该项目下的所有用户组
UserRoleRelationExample example = new UserRoleRelationExample();
example.createCriteria().andSourceIdEqualTo(projectId).andUserIdEqualTo(userId);
@ -331,6 +340,7 @@ public class ProjectMemberService {
/**
* 获取项目评论下拉成员选项
*
* @param keyword 搜索关键字
* @param projectId 项目ID
* @return 用户集合信息

View File

@ -28,7 +28,7 @@ import java.util.List;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class ProjectMemberControllerTests extends BaseTest {
@ -196,6 +196,7 @@ public class ProjectMemberControllerTests extends BaseTest {
@Order(12)
public void testRemoveMemberError() throws Exception {
this.requestGet(REMOVE_MEMBER + "/default-project-member-x/default-project-member-user-1", status().is5xxServerError());
this.requestGet(REMOVE_MEMBER + "/default-project-member-test-1/admin", status().is5xxServerError());
}
@Test

View File

@ -21,7 +21,8 @@ INSERT INTO user_role_relation (id, user_id, role_id, source_id, organization_id
INSERT INTO user_role_relation (id, user_id, role_id, source_id, organization_id, create_time, create_user) VALUES
(UUID(), 'default-project-member-user-1', 'project_admin', 'default-project-member-test', 'default-organization-member-test', UNIX_TIMESTAMP() * 1000, 'admin'),
(UUID(), 'default-project-member-user-2', 'project_admin', 'default-project-member-test', 'default-organization-member-test', UNIX_TIMESTAMP() * 1000, 'admin'),
(UUID(), 'default-project-member-user-del', 'project_admin', 'default-project-member-test', 'default-organization-member-test', UNIX_TIMESTAMP() * 1000, 'admin');
(UUID(), 'default-project-member-user-del', 'project_admin', 'default-project-member-test', 'default-organization-member-test', UNIX_TIMESTAMP() * 1000, 'admin'),
(UUID(), 'admin', 'org_admin', 'default-project-member-test-1', 'default-organization-member-test', UNIX_TIMESTAMP() * 1000, 'admin');

View File

@ -4,6 +4,7 @@ import io.metersphere.validation.groups.Created;
import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ -27,5 +28,6 @@ public class OrganizationEditRequest implements Serializable {
private String description;
@Schema(description = "成员ID集合")
@NotEmpty(message = "{project.member_count.not_blank}", groups = {Created.class, Updated.class})
private List<String> userIds;
}

View File

@ -107,6 +107,7 @@ public class OrganizationService {
/**
* 更新组织名称
*
* @param organizationDTO 组织请求参数
*/
public void updateName(OrganizationDTO organizationDTO) {
@ -120,6 +121,7 @@ public class OrganizationService {
/**
* 更新组织
*
* @param organizationDTO 组织请求参数
*/
public void update(OrganizationDTO organizationDTO) {
@ -134,7 +136,6 @@ public class OrganizationService {
List<String> addOrgAdmins = organizationDTO.getUserIds();
// 旧的组织管理员ID
List<String> oldOrgAdmins = getOrgAdminIds(organizationDTO.getId());
if (CollectionUtils.isNotEmpty(addOrgAdmins)) {
// 需要新增组织管理员ID
List<String> addIds = addOrgAdmins.stream().filter(addOrgAdmin -> !oldOrgAdmins.contains(addOrgAdmin)).toList();
// 需要删除的组织管理员ID
@ -152,18 +153,11 @@ public class OrganizationService {
deleteExample.createCriteria().andSourceIdEqualTo(organizationDTO.getId()).andRoleIdEqualTo(InternalUserRole.ORG_ADMIN.getValue()).andUserIdIn(deleteIds);
userRoleRelationMapper.deleteByExample(deleteExample);
}
} else {
// 前端传入的组织管理员ID为空删除所有组织管理员
if (CollectionUtils.isNotEmpty(oldOrgAdmins)) {
UserRoleRelationExample example = new UserRoleRelationExample();
example.createCriteria().andSourceIdEqualTo(organizationDTO.getId()).andRoleIdEqualTo(InternalUserRole.ORG_ADMIN.getValue());
userRoleRelationMapper.deleteByExample(example);
}
}
}
/**
* 删除组织
*
* @param organizationDeleteRequest 组织删除参数
*/
public void delete(OrganizationDeleteRequest organizationDeleteRequest) {
@ -176,6 +170,7 @@ public class OrganizationService {
/**
* 恢复组织
*
* @param id 组织ID
*/
public void recover(String id) {
@ -185,6 +180,7 @@ public class OrganizationService {
/**
* 开启组织
*
* @param id 组织ID
*/
public void enable(String id) {
@ -194,6 +190,7 @@ public class OrganizationService {
/**
* 结束组织
*
* @param id 组织ID
*/
public void disable(String id) {
@ -318,6 +315,14 @@ public class OrganizationService {
public void removeMember(String organizationId, String userId, String currentUser) {
List<LogDTO> logs = new ArrayList<>();
checkOrgExistById(organizationId);
//检查用户是不是最后一个管理员
UserRoleRelationExample userRoleRelationExample = new UserRoleRelationExample();
userRoleRelationExample.createCriteria().andUserIdNotEqualTo(userId)
.andSourceIdEqualTo(organizationId)
.andRoleIdEqualTo(InternalUserRole.ORG_ADMIN.getValue());
if (userRoleRelationMapper.countByExample(userRoleRelationExample) == 0) {
throw new MSException(Translator.get("keep_at_least_one_administrator"));
}
//删除组织下项目与成员的关系
List<String> projectIds = getProjectIds(organizationId);
if (CollectionUtils.isNotEmpty(projectIds)) {
@ -1015,15 +1020,17 @@ public class OrganizationService {
/**
* 校验组织是否存在
* 这里使用静态方法避免需要注入导致循环依赖
*
* @param id
* @return
*/
public static Organization checkResourceExist(String id) {
return ServiceUtils.checkResourceExist( CommonBeanFactory.getBean(OrganizationMapper.class).selectByPrimaryKey(id), "permission.system_organization_project.name");
return ServiceUtils.checkResourceExist(CommonBeanFactory.getBean(OrganizationMapper.class).selectByPrimaryKey(id), "permission.system_organization_project.name");
}
/**
* 剩余天数
*
* @param deleteTime 删除时间
* @return 剩余天数
*/

View File

@ -34,7 +34,7 @@ import java.util.List;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class OrganizationControllerTests extends BaseTest {
@ -282,7 +282,7 @@ public class OrganizationControllerTests extends BaseTest {
Assertions.assertTrue(JSON.parseArray(JSON.toJSONString(pageData.getList())).size() <= organizationRequest.getPageSize());
//判断是否为空
List<OrgUserExtend> orgUserExtends = JSON.parseArray(JSON.toJSONString(pageData.getList()), OrgUserExtend.class);
Assertions.assertTrue(CollectionUtils.isEmpty(orgUserExtends));
Assertions.assertTrue(CollectionUtils.isNotEmpty(orgUserExtends));
}
@ -309,13 +309,14 @@ public class OrganizationControllerTests extends BaseTest {
@Test
@Order(14)
public void removeOrgMemberSuccess() throws Exception {
this.requestGet(ORGANIZATION_REMOVE_MEMBER + "/sys_default_organization_6/sys_default_user", status().isOk());
this.requestGet(ORGANIZATION_REMOVE_MEMBER + "/sys_default_organization_6/sys_default_user4", status().isOk());
this.requestGet(ORGANIZATION_REMOVE_MEMBER + "/sys_default_organization_6/sys_default_user", status().is5xxServerError());
}
@Test
@Order(15)
public void removeOrgMemberSuccessWithNoProject() throws Exception {
this.requestGet(ORGANIZATION_REMOVE_MEMBER + "/sys_default_organization_3/sys_default_user", status().isOk());
this.requestGet(ORGANIZATION_REMOVE_MEMBER + "/sys_default_organization_3/sys_default_user4", status().isOk());
}
@Test
@ -425,7 +426,6 @@ public class OrganizationControllerTests extends BaseTest {
}
@Test
@Order(23)
public void getNotExistUserListByOrgSuccess() throws Exception {
@ -474,7 +474,6 @@ public class OrganizationControllerTests extends BaseTest {
}
private void listByKeyWord(String keyWord, String orgId, boolean compare, String userRoleId, String projectId, boolean checkPart, String noUserRoleId, String noProjectId) throws Exception {
OrganizationRequest organizationRequest = new OrganizationRequest();
organizationRequest.setCurrent(1);

View File

@ -30,10 +30,10 @@ import java.util.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class SystemOrganizationControllerTests extends BaseTest{
public class SystemOrganizationControllerTests extends BaseTest {
@Resource
private MockMvc mockMvc;
@ -145,8 +145,8 @@ public class SystemOrganizationControllerTests extends BaseTest{
request.setUserIds(List.of("user-id1"));
this.requestPost(ORGANIZATION_UPDATE, request).andExpect(status().isOk());
request.setUserIds(List.of());
this.requestPost(ORGANIZATION_UPDATE, request).andExpect(status().isOk());
this.requestPost(ORGANIZATION_UPDATE, request).andExpect(status().isOk());
this.requestPost(ORGANIZATION_UPDATE, request).andExpect(status().isBadRequest());
this.requestPost(ORGANIZATION_UPDATE, request).andExpect(status().isBadRequest());
}
@Test
@ -154,12 +154,15 @@ public class SystemOrganizationControllerTests extends BaseTest{
public void testUpdateOrganizationError() throws Exception {
OrganizationEditRequest request = new OrganizationEditRequest();
request.setName("default-4");
request.setUserIds(List.of("user-id1", "user-id2"));
// 组织不存在
request.setId("default-organization-x");
this.requestPost(ORGANIZATION_UPDATE, request).andExpect(status().is5xxServerError());
// 组织存在, 但是名称重复
request.setId("default-organization-5");
this.requestPost(ORGANIZATION_UPDATE, request).andExpect(status().is5xxServerError());
request.setUserIds(new ArrayList<>());
this.requestPost(ORGANIZATION_UPDATE, request).andExpect(status().isBadRequest());
}
@Test
@ -381,6 +384,8 @@ public class SystemOrganizationControllerTests extends BaseTest{
this.requestGet(ORGANIZATION_REMOVE_MEMBER + "/default-organization-3/admin", status().isOk());
// 日志校验
checkLog("default-organization-3", OperationLogType.DELETE);
this.requestGet(ORGANIZATION_REMOVE_MEMBER + "/default-organization-5/user-id1", status().is5xxServerError());
// 权限校验
requestGetPermissionTest(PermissionConstants.SYSTEM_ORGANIZATION_PROJECT_MEMBER_DELETE, ORGANIZATION_REMOVE_MEMBER + "/default-organization-3/admin");
}

View File

@ -50,4 +50,9 @@ INSERT INTO user_role(id, name, description, internal, type, create_time, update
INSERT INTO user_role_relation(id, user_id, role_id, source_id, organization_id, create_time, create_user) VALUE
('gyq_user_role_relation_test', 'sys_default_user4', 'sys_default_org_role_id_5', 'sys_default_organization_6', 'sys_default_organization_6', UNIX_TIMESTAMP() * 1000, 'admin');
INSERT INTO user_role_relation(id, user_id, role_id, source_id, organization_id, create_time, create_user) VALUE
('gyq_user_role_relation_test-1', 'sys_default_user', 'org_admin', 'sys_default_organization_6', 'sys_default_organization_6', UNIX_TIMESTAMP() * 1000, 'admin');
INSERT INTO user_role_relation(id, user_id, role_id, source_id, organization_id, create_time, create_user) VALUE
('gyq_user_role_relation_test-2', 'admin', 'org_admin', 'sys_default_organization_3', 'sys_default_organization_3', UNIX_TIMESTAMP() * 1000, 'admin');
INSERT INTO user_role_relation(id, user_id, role_id, source_id, organization_id, create_time, create_user) VALUE
('gyq_user_role_relation_test-3', 'sys_default_user4', 'sys_default_project_role_id_3', 'sys_org_projectId', 'sys_default_organization_3', UNIX_TIMESTAMP() * 1000, 'admin');