package cn.iocoder.yudao.module.crm.framework.permission.core.aop; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils; import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CRM_PERMISSION_DENIED; /** * Crm 数据权限校验 AOP 切面 * * @author HUIHUI */ @Component @Aspect @Slf4j public class CrmPermissionAspect { @Resource private CrmPermissionService crmPermissionService; @Resource private AdminUserApi adminUserApi; @Resource private CrmBusinessMapper businessMapper; @Before("@annotation(crmPermission)") public void doBefore(JoinPoint joinPoint, CrmPermission crmPermission) { // 1.1 获取相关属性值 Map expressionValues = parseExpressions(joinPoint, crmPermission); Integer bizType = StrUtil.isEmpty(crmPermission.bizTypeValue()) ? crmPermission.bizType()[0].getType() : (Integer) expressionValues.get(crmPermission.bizTypeValue()); // 模块类型 // 1.2 处理兼容多个 bizId 的情况 Object object = expressionValues.get(crmPermission.bizId()); // 模块数据编号 Set bizIds = new HashSet<>(); if (object instanceof Collection) { bizIds.addAll(convertSet((Collection) object, item -> Long.parseLong(item.toString()))); } else { bizIds.add(Long.parseLong(object.toString())); } Integer permissionLevel = crmPermission.level().getLevel(); // 需要的权限级别 // 2. 逐个校验权限 List permissionList = crmPermissionService.getPermissionListByBiz(bizType, bizIds); Map> multiMap = convertMultiMap(permissionList, CrmPermissionDO::getBizId); bizIds.forEach(bizId -> validatePermission(bizType, bizId, multiMap.get(bizId), permissionLevel)); } private void validatePermission(Integer bizType, Long bizId, List bizPermissions, Integer permissionLevel) { // 1. 如果是超级管理员则直接通过 if (CrmPermissionUtils.isCrmAdmin()) { return; } // 特殊:没有数据权限的情况,针对 READ 的特殊处理 if (CollUtil.isEmpty(bizPermissions)) { // 1.1 公海数据,如果没有团队成员,大家也应该有 READ 权限才对 if (CrmPermissionLevelEnum.isRead(permissionLevel)) { return; } // 没有数据权限的情况下超出了读权限直接报错,避免后面校验空指针 throw exception(CRM_PERMISSION_DENIED, CrmBizTypeEnum.getNameByType(bizType)); } else { // 1.2 有数据权限但是没有负责人的情况 if (!anyMatch(bizPermissions, item -> CrmPermissionLevelEnum.isOwner(item.getLevel())) && CrmPermissionLevelEnum.isRead(permissionLevel)) { return; } } // 2. 只考虑自身的权限 Long userId = getUserId(); CrmPermissionDO userPermission = CollUtil.findOne(bizPermissions, permission -> ObjUtil.equal(permission.getUserId(), userId)); if (userPermission != null) { if (isUserPermissionValid(userPermission, permissionLevel)) { return; } } // 3. 商机特殊处理:检查客户团队权限 if (CrmBizTypeEnum.CRM_BUSINESS.getType().equals(bizType)) { if (validateBusinessCustomerPermission(bizId, userId, permissionLevel)) { return; } } // 4. 考虑下级的权限 List subordinateUserIds = adminUserApi.getUserListBySubordinate(userId); for (Long subordinateUserId : convertSet(subordinateUserIds, AdminUserRespDTO::getId)) { CrmPermissionDO subordinatePermission = CollUtil.findOne(bizPermissions, permission -> ObjUtil.equal(permission.getUserId(), subordinateUserId)); if (subordinatePermission != null && isUserPermissionValid(subordinatePermission, permissionLevel)) { return; } // 商机特殊处理:检查客户团队权限(下属的) if (CrmBizTypeEnum.CRM_BUSINESS.getType().equals(bizType)) { if (validateBusinessCustomerPermission(bizId, subordinateUserId, permissionLevel)) { return; } } } // 5. 没有权限,抛出异常 log.info("[doBefore][userId({}) 要求权限({}) 实际权限({}) 数据校验错误]", // 打个 info 日志,方便后续排查问题、审计 userId, permissionLevel, toJsonString(userPermission)); throw exception(CRM_PERMISSION_DENIED, CrmBizTypeEnum.getNameByType(bizType)); } /** * 商机特殊权限校验:检查用户是否有该商机所属客户的团队权限 * * @param businessId 商机编号 * @param userId 用户编号 * @param permissionLevel 需要的权限级别 * @return 是否有权限 */ private boolean validateBusinessCustomerPermission(Long businessId, Long userId, Integer permissionLevel) { // 获取商机的客户ID CrmBusinessDO business = businessMapper.selectById(businessId); if (business == null || business.getCustomerId() == null) { return false; } // 检查客户团队权限 List customerPermissions = crmPermissionService.getPermissionListByBiz( CrmBizTypeEnum.CRM_CUSTOMER.getType(), Collections.singleton(business.getCustomerId())); if (CollUtil.isEmpty(customerPermissions)) { // 客户公海数据,READ 权限放行 return CrmPermissionLevelEnum.isRead(permissionLevel); } // 检查用户是否有客户团队的权限 CrmPermissionDO userCustomerPermission = CollUtil.findOne(customerPermissions, permission -> ObjUtil.equal(permission.getUserId(), userId)); if (userCustomerPermission != null && isUserPermissionValid(userCustomerPermission, permissionLevel)) { return true; } return false; } /** * 校验用户权限是否有效 * * @param userPermission 用户拥有的权限 * @param permissionLevel 需要的权限级别 * @return 是否有效 */ @SuppressWarnings("RedundantIfStatement") private boolean isUserPermissionValid(CrmPermissionDO userPermission, Integer permissionLevel) { // 2.1 情况一:如果自己是负责人,则默认有所有权限 if (CrmPermissionLevelEnum.isOwner(userPermission.getLevel())) { return true; } // 2.2 情况二:校验自己是否有读权限 if (CrmPermissionLevelEnum.isRead(permissionLevel)) { if (CrmPermissionLevelEnum.isRead(userPermission.getLevel()) // 校验当前用户是否有读权限 || CrmPermissionLevelEnum.isWrite(userPermission.getLevel())) { // 校验当前用户是否有写权限 return true; } } // 2.3 情况三:校验自己是否有写权限 if (CrmPermissionLevelEnum.isWrite(permissionLevel)) { if (CrmPermissionLevelEnum.isWrite(userPermission.getLevel())) { // 校验当前用户是否有写权限 return true; } } return false; } /** * 获得用户编号 * * @return 用户编号 */ private static Long getUserId() { return WebFrameworkUtils.getLoginUserId(); } private static Map parseExpressions(JoinPoint joinPoint, CrmPermission crmPermission) { // 1. 需要解析的表达式 List expressionStrings = new ArrayList<>(2); expressionStrings.add(crmPermission.bizId()); if (StrUtil.isNotEmpty(crmPermission.bizTypeValue())) { // 为空则表示 bizType 有值 expressionStrings.add(crmPermission.bizTypeValue()); } // 2. 执行解析 return SpringExpressionUtils.parseExpressions(joinPoint, expressionStrings); } }