Commit 8d94c7c1054e972bbfc9f3d12f871dbadbcc62df
1 parent
85f2928a
fix(crm): 校验转移后旧负责人保留权限级别
Showing
3 changed files
with
85 additions
and
3 deletions
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
| @@ -76,6 +76,7 @@ public interface ErrorCodeConstants { | @@ -76,6 +76,7 @@ public interface ErrorCodeConstants { | ||
| 76 | ErrorCode CRM_PERMISSION_DELETE_SELF_PERMISSION_FAIL_EXIST_OWNER = new ErrorCode(1_020_007_007, "删除数据权限失败,原因:不能删除负责人"); | 76 | ErrorCode CRM_PERMISSION_DELETE_SELF_PERMISSION_FAIL_EXIST_OWNER = new ErrorCode(1_020_007_007, "删除数据权限失败,原因:不能删除负责人"); |
| 77 | ErrorCode CRM_PERMISSION_CREATE_FAIL = new ErrorCode(1_020_007_008, "创建数据权限失败,原因:所加用户已有权限"); | 77 | ErrorCode CRM_PERMISSION_CREATE_FAIL = new ErrorCode(1_020_007_008, "创建数据权限失败,原因:所加用户已有权限"); |
| 78 | ErrorCode CRM_PERMISSION_CREATE_FAIL_EXISTS = new ErrorCode(1_020_007_009, "同时添加数据权限失败,原因:用户【{}】已有模块【{}】数据【{}】的【{}】权限"); | 78 | ErrorCode CRM_PERMISSION_CREATE_FAIL_EXISTS = new ErrorCode(1_020_007_009, "同时添加数据权限失败,原因:用户【{}】已有模块【{}】数据【{}】的【{}】权限"); |
| 79 | + ErrorCode CRM_PERMISSION_MODEL_TRANSFER_FAIL_OLD_OWNER_PERMISSION_LEVEL_ERROR = new ErrorCode(1_020_007_010, "{}操作失败,原因:老负责人的权限级别只能是只读或读写"); | ||
| 79 | 80 | ||
| 80 | // ========== 产品 1_020_008_000 ========== | 81 | // ========== 产品 1_020_008_000 ========== |
| 81 | ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_020_008_000, "产品不存在"); | 82 | ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_020_008_000, "产品不存在"); |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java
| @@ -34,6 +34,8 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU | @@ -34,6 +34,8 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU | ||
| 34 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; | 34 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; |
| 35 | import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; | 35 | import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; |
| 36 | import static cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum.isOwner; | 36 | import static cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum.isOwner; |
| 37 | +import static cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum.isRead; | ||
| 38 | +import static cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum.isWrite; | ||
| 37 | 39 | ||
| 38 | /** | 40 | /** |
| 39 | * CRM 数据权限 Service 接口实现类 | 41 | * CRM 数据权限 Service 接口实现类 |
| @@ -216,17 +218,22 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { | @@ -216,17 +218,22 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { | ||
| 216 | if (!owner && !CrmPermissionUtils.isCrmAdmin()) { | 218 | if (!owner && !CrmPermissionUtils.isCrmAdmin()) { |
| 217 | throw exception(CRM_PERMISSION_DENIED, bizTypeName); | 219 | throw exception(CRM_PERMISSION_DENIED, bizTypeName); |
| 218 | } | 220 | } |
| 219 | - // 1.2 获得老负责人的权限:操作人是负责人时,就是操作人的权限;否则(CRM 管理员),是数据实际的负责人,而不是管理员自己的团队权限 | 221 | + // 1.2 校验老负责人的权限级别:为空时表示移除,否则只能是只读或读写,避免出现多个负责人 |
| 222 | + Integer oldOwnerPermissionLevel = transferReqBO.getOldOwnerPermissionLevel(); | ||
| 223 | + if (oldOwnerPermissionLevel != null && !isRead(oldOwnerPermissionLevel) && !isWrite(oldOwnerPermissionLevel)) { | ||
| 224 | + throw exception(CRM_PERMISSION_MODEL_TRANSFER_FAIL_OLD_OWNER_PERMISSION_LEVEL_ERROR, bizTypeName); | ||
| 225 | + } | ||
| 226 | + // 1.3 获得老负责人的权限:操作人是负责人时,就是操作人的权限;否则(CRM 管理员),是数据实际的负责人,而不是管理员自己的团队权限 | ||
| 220 | CrmPermissionDO oldOwnerPermission = owner ? userPermission | 227 | CrmPermissionDO oldOwnerPermission = owner ? userPermission |
| 221 | : CollUtil.findOne(permissions, item -> isOwner(item.getLevel())); | 228 | : CollUtil.findOne(permissions, item -> isOwner(item.getLevel())); |
| 222 | if (oldOwnerPermission == null) { // 数据缺少负责人时,在修改任何权限前失败,避免部分写入 | 229 | if (oldOwnerPermission == null) { // 数据缺少负责人时,在修改任何权限前失败,避免部分写入 |
| 223 | throw exception(CRM_PERMISSION_NOT_EXISTS); | 230 | throw exception(CRM_PERMISSION_NOT_EXISTS); |
| 224 | } | 231 | } |
| 225 | - // 1.3 校验转移对象是否已经是该负责人 | 232 | + // 1.4 校验转移对象是否已经是该负责人 |
| 226 | if (ObjUtil.equal(transferReqBO.getNewOwnerUserId(), oldOwnerPermission.getUserId())) { | 233 | if (ObjUtil.equal(transferReqBO.getNewOwnerUserId(), oldOwnerPermission.getUserId())) { |
| 227 | throw exception(CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_EXISTS, bizTypeName); | 234 | throw exception(CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_EXISTS, bizTypeName); |
| 228 | } | 235 | } |
| 229 | - // 1.4 校验新负责人是否存在 | 236 | + // 1.5 校验新负责人是否存在 |
| 230 | adminUserApi.validateUserList(Collections.singletonList(transferReqBO.getNewOwnerUserId())); | 237 | adminUserApi.validateUserList(Collections.singletonList(transferReqBO.getNewOwnerUserId())); |
| 231 | 238 | ||
| 232 | // 2. 修改新负责人的权限:已在团队中时,提升为负责人,不重复新增 | 239 | // 2. 修改新负责人的权限:已在团队中时,提升为负责人,不重复新增 |
yudao-module-crm/src/test/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImplTest.java
| @@ -15,6 +15,8 @@ import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum; | @@ -15,6 +15,8 @@ import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum; | ||
| 15 | import org.junit.jupiter.api.AfterEach; | 15 | import org.junit.jupiter.api.AfterEach; |
| 16 | import org.junit.jupiter.api.BeforeEach; | 16 | import org.junit.jupiter.api.BeforeEach; |
| 17 | import org.junit.jupiter.api.Test; | 17 | import org.junit.jupiter.api.Test; |
| 18 | +import org.junit.jupiter.params.ParameterizedTest; | ||
| 19 | +import org.junit.jupiter.params.provider.ValueSource; | ||
| 18 | import org.mockito.InjectMocks; | 20 | import org.mockito.InjectMocks; |
| 19 | import org.mockito.Mock; | 21 | import org.mockito.Mock; |
| 20 | import org.mockito.MockedStatic; | 22 | import org.mockito.MockedStatic; |
| @@ -246,6 +248,78 @@ public class CrmPermissionServiceImplTest extends BaseMockitoUnitTest { | @@ -246,6 +248,78 @@ public class CrmPermissionServiceImplTest extends BaseMockitoUnitTest { | ||
| 246 | assertNoPermissionWrite(); | 248 | assertNoPermissionWrite(); |
| 247 | } | 249 | } |
| 248 | 250 | ||
| 251 | + // ========== 老负责人的权限级别 ========== | ||
| 252 | + | ||
| 253 | + @Test | ||
| 254 | + public void testTransferPermission_ownerKeepOldOwnerRead() { | ||
| 255 | + // mock 数据:当前用户是负责人,新负责人不在团队中 | ||
| 256 | + mockPermissions(buildPermission(LOGIN_USER_PERMISSION_ID, LOGIN_USER_ID, CrmPermissionLevelEnum.OWNER)); | ||
| 257 | + | ||
| 258 | + // 调用:老负责人保留为 READ | ||
| 259 | + permissionService.transferPermission(buildTransferReqBO(NEW_OWNER_USER_ID, CrmPermissionLevelEnum.READ.getLevel())); | ||
| 260 | + // 断言:新增新负责人;老负责人(当前用户)改为 READ | ||
| 261 | + verify(permissionMapper).selectByBizTypeAndBizId(BIZ_TYPE, BIZ_ID); | ||
| 262 | + verifyNewOwnerInserted(); | ||
| 263 | + verifyLevelUpdated(LOGIN_USER_PERMISSION_ID, CrmPermissionLevelEnum.READ); | ||
| 264 | + verifyNoMoreInteractions(permissionMapper); | ||
| 265 | + } | ||
| 266 | + | ||
| 267 | + @ParameterizedTest | ||
| 268 | + @ValueSource(ints = {1, 0, -1, 4, Integer.MAX_VALUE, Integer.MIN_VALUE}) // 1 是 OWNER,其它是不存在的级别 | ||
| 269 | + public void testTransferPermission_ownerInvalidOldOwnerPermissionLevel(int oldOwnerPermissionLevel) { | ||
| 270 | + // mock 数据:当前用户是负责人 | ||
| 271 | + mockPermissions(buildPermission(LOGIN_USER_PERMISSION_ID, LOGIN_USER_ID, CrmPermissionLevelEnum.OWNER)); | ||
| 272 | + | ||
| 273 | + // 调用,并断言异常:在修改任何权限前失败 | ||
| 274 | + assertServiceException(() -> permissionService.transferPermission(buildTransferReqBO(NEW_OWNER_USER_ID, oldOwnerPermissionLevel)), | ||
| 275 | + CRM_PERMISSION_MODEL_TRANSFER_FAIL_OLD_OWNER_PERMISSION_LEVEL_ERROR, BIZ_TYPE_NAME); | ||
| 276 | + assertNoPermissionWrite(); | ||
| 277 | + verifyNoInteractions(adminUserApi); | ||
| 278 | + } | ||
| 279 | + | ||
| 280 | + @ParameterizedTest | ||
| 281 | + @ValueSource(ints = {1, 0, -1, 4, Integer.MAX_VALUE, Integer.MIN_VALUE}) // 1 是 OWNER,其它是不存在的级别 | ||
| 282 | + public void testTransferPermission_crmAdminInvalidOldOwnerPermissionLevel(int oldOwnerPermissionLevel) { | ||
| 283 | + // mock 数据:CRM 管理员没有权限,数据的负责人是其他人(修复前:老负责人被更新回 OWNER,留下两个负责人) | ||
| 284 | + mockPermissions(buildPermission(OWNER_PERMISSION_ID, OWNER_USER_ID, CrmPermissionLevelEnum.OWNER)); | ||
| 285 | + mockCrmAdmin(true); | ||
| 286 | + | ||
| 287 | + // 调用,并断言异常:在修改任何权限前失败 | ||
| 288 | + assertServiceException(() -> permissionService.transferPermission(buildTransferReqBO(NEW_OWNER_USER_ID, oldOwnerPermissionLevel)), | ||
| 289 | + CRM_PERMISSION_MODEL_TRANSFER_FAIL_OLD_OWNER_PERMISSION_LEVEL_ERROR, BIZ_TYPE_NAME); | ||
| 290 | + assertNoPermissionWrite(); | ||
| 291 | + verifyNoInteractions(adminUserApi); | ||
| 292 | + } | ||
| 293 | + | ||
| 294 | + @Test | ||
| 295 | + public void testTransferPermission_teamMemberDeniedBeforeOldOwnerPermissionLevel() { | ||
| 296 | + // mock 数据:当前用户只有 WRITE 团队权限,不是 CRM 管理员 | ||
| 297 | + mockPermissions(buildPermission(OWNER_PERMISSION_ID, OWNER_USER_ID, CrmPermissionLevelEnum.OWNER), | ||
| 298 | + buildPermission(LOGIN_USER_PERMISSION_ID, LOGIN_USER_ID, CrmPermissionLevelEnum.WRITE)); | ||
| 299 | + mockCrmAdmin(false); | ||
| 300 | + | ||
| 301 | + // 调用,并断言异常:先校验数据权限,再校验老负责人的权限级别 | ||
| 302 | + assertServiceException(() -> permissionService.transferPermission(buildTransferReqBO(NEW_OWNER_USER_ID, | ||
| 303 | + CrmPermissionLevelEnum.OWNER.getLevel())), | ||
| 304 | + CRM_PERMISSION_DENIED, BIZ_TYPE_NAME); | ||
| 305 | + assertNoPermissionWrite(); | ||
| 306 | + verifyNoInteractions(adminUserApi); | ||
| 307 | + } | ||
| 308 | + | ||
| 309 | + @Test | ||
| 310 | + public void testTransferPermission_crmAdminOldOwnerPermissionLevelBeforeOwnerNotExists() { | ||
| 311 | + // mock 数据:CRM 管理员有 WRITE 团队权限,但数据缺少负责人 | ||
| 312 | + mockPermissions(buildPermission(LOGIN_USER_PERMISSION_ID, LOGIN_USER_ID, CrmPermissionLevelEnum.WRITE)); | ||
| 313 | + mockCrmAdmin(true); | ||
| 314 | + | ||
| 315 | + // 调用,并断言异常:先校验老负责人的权限级别(请求参数),再校验数据的负责人 | ||
| 316 | + assertServiceException(() -> permissionService.transferPermission(buildTransferReqBO(NEW_OWNER_USER_ID, | ||
| 317 | + CrmPermissionLevelEnum.OWNER.getLevel())), | ||
| 318 | + CRM_PERMISSION_MODEL_TRANSFER_FAIL_OLD_OWNER_PERMISSION_LEVEL_ERROR, BIZ_TYPE_NAME); | ||
| 319 | + assertNoPermissionWrite(); | ||
| 320 | + verifyNoInteractions(adminUserApi); | ||
| 321 | + } | ||
| 322 | + | ||
| 249 | private void mockCrmAdmin(boolean crmAdmin) { | 323 | private void mockCrmAdmin(boolean crmAdmin) { |
| 250 | springUtilMockedStatic.when(() -> SpringUtil.getBean(PermissionCommonApi.class)).thenReturn(permissionCommonApi); | 324 | springUtilMockedStatic.when(() -> SpringUtil.getBean(PermissionCommonApi.class)).thenReturn(permissionCommonApi); |
| 251 | when(permissionCommonApi.hasAnyRoles(LOGIN_USER_ID, RoleCodeEnum.CRM_ADMIN.getCode())).thenReturn(crmAdmin); | 325 | when(permissionCommonApi.hasAnyRoles(LOGIN_USER_ID, RoleCodeEnum.CRM_ADMIN.getCode())).thenReturn(crmAdmin); |