liu
4 天以前 8ad99e5f72085d14fd56e8faa7aa36701823c8b5
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/util/CrmPermissionUtils.java
@@ -27,13 +27,16 @@
public class CrmPermissionUtils {
    /**
     * 校验用户是否是 CRM 管理员
     * 校验用户是否是 CRM 管理员(CRM 管理员或系统超级管理员)
     *
     * 超级管理员(super_admin)不受 CRM 数据权限限制,可以查看全部数据
     *
     * @return 是/否
     */
    public static boolean isCrmAdmin() {
        PermissionCommonApi permissionApi = SpringUtil.getBean(PermissionCommonApi.class);
        return permissionApi.hasAnyRoles(getLoginUserId(), RoleCodeEnum.CRM_ADMIN.getCode());
        return permissionApi.hasAnyRoles(getLoginUserId(),
                RoleCodeEnum.CRM_ADMIN.getCode(), RoleCodeEnum.SUPER_ADMIN.getCode());
    }
    /**
@@ -57,6 +60,10 @@
     */
    public static <T extends MPJLambdaWrapper<?>, S> void appendPermissionCondition(T query, Integer bizType, SFunction<S, ?> bizId,
                                                                                    Long userId, Integer sceneType) {
        // 特殊逻辑:CRM 管理员 / 超级管理员直接查询所有,不过滤数据权限
        if (isCrmAdmin()) {
            return;
        }
        MybatisPlusJoinProperties mybatisPlusJoinProperties = SpringUtil.getBean(MybatisPlusJoinProperties.class);
        final String ownerUserIdField = mybatisPlusJoinProperties.getTableAlias() + ".owner_user_id";
        // 场景一:我负责的数据
@@ -65,12 +72,10 @@
        }
        // 场景二:我参与的数据(我有读或写权限,并且不是负责人)
        if (CrmSceneTypeEnum.isInvolved(sceneType)) {
            if (CrmPermissionUtils.isCrmAdmin()) { // 特殊逻辑:如果是超管,直接查询所有,不过滤数据权限
                return;
            }
            // 商机特殊处理:同时关联商机团队和客户团队的权限
            if (CrmBizTypeEnum.CRM_BUSINESS.getType().equals(bizType)) {
                appendBusinessPermissionCondition(query, bizId, userId, ownerUserIdField);
            // 需要继承客户团队的模块:数据自身团队 ∪ 客户负责人/客户团队
            String tableName = getInheritCustomerTeamTable(bizType);
            if (tableName != null) {
                appendCustomerInheritedPermissionCondition(query, bizType, tableName, userId, ownerUserIdField);
            } else {
                query.innerJoin(CrmPermissionDO.class, on -> on.eq(CrmPermissionDO::getBizType, bizType)
                        .eq(CrmPermissionDO::getBizId, bizId)
@@ -100,23 +105,60 @@
    }
    /**
     * 商机特殊权限条件:同时关联商机团队和客户团队的权限
     * 商机继承客户团队的权限,即客户团队成员也能看到该客户下的商机
     * 获得「继承客户团队」的业务表名;不需要继承的返回 null
     *
     * 目前统一为:联系人、商机、合同、销售报价、回款、回款计划、开票
     */
    private static <T extends MPJLambdaWrapper<?>, S> void appendBusinessPermissionCondition(T query, SFunction<S, ?> bizId,
                                                                                              Long userId, String ownerUserIdField) {
        // 使用子查询方式:商机团队成员 OR 客户团队成员
        // 子查询1:商机团队成员权限
    private static String getInheritCustomerTeamTable(Integer bizType) {
        if (CrmBizTypeEnum.CRM_CONTACT.getType().equals(bizType)) {
            return "crm_contact";
        }
        if (CrmBizTypeEnum.CRM_BUSINESS.getType().equals(bizType)) {
            return "crm_business";
        }
        if (CrmBizTypeEnum.CRM_CONTRACT.getType().equals(bizType)) {
            return "crm_contract";
        }
        if (CrmBizTypeEnum.CRM_SALE_QUOTATION.getType().equals(bizType)) {
            return "crm_sale_quotation";
        }
        if (CrmBizTypeEnum.CRM_RECEIVABLE.getType().equals(bizType)) {
            return "crm_receivable";
        }
        if (CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType().equals(bizType)) {
            return "crm_receivable_plan";
        }
        if (CrmBizTypeEnum.CRM_INVOICE.getType().equals(bizType)) {
            return "crm_invoice";
        }
        return null;
    }
    /**
     * 继承客户团队的数据权限:数据自身团队 ∪ 客户负责人/客户团队
     *
     * @param query            连表查询对象
     * @param bizType          数据类型
     * @param tableName        业务表名
     * @param userId           用户编号
     * @param ownerUserIdField 负责人字段
     */
    private static <T extends MPJLambdaWrapper<?>> void appendCustomerInheritedPermissionCondition(
            T query, Integer bizType, String tableName, Long userId, String ownerUserIdField) {
        // 子查询1:数据自身的团队成员权限
        String subQuery1 = "(SELECT p1.biz_id FROM crm_permission p1 " +
                "WHERE p1.biz_type = " + CrmBizTypeEnum.CRM_BUSINESS.getType() + " " +
                "AND p1.level IN (" + CrmPermissionLevelEnum.READ.getLevel() + "," + CrmPermissionLevelEnum.WRITE.getLevel() + ") " +
                "AND p1.user_id = " + userId + ")";
        // 子查询2:客户团队成员权限(通过商机的 customer_id 关联)
        String subQuery2 = "(SELECT b.id FROM crm_business b " +
                "INNER JOIN crm_permission p2 ON p2.biz_type = " + CrmBizTypeEnum.CRM_CUSTOMER.getType() + " " +
                "AND p2.biz_id = b.customer_id " +
                "AND p2.level IN (" + CrmPermissionLevelEnum.READ.getLevel() + "," + CrmPermissionLevelEnum.WRITE.getLevel() + ") " +
                "AND p2.user_id = " + userId + ")";
                "WHERE p1.biz_type = " + bizType + " " +
                "AND p1.level IN (" + CrmPermissionLevelEnum.OWNER.getLevel() + "," +
                CrmPermissionLevelEnum.READ.getLevel() + "," + CrmPermissionLevelEnum.WRITE.getLevel() + ") " +
                "AND p1.user_id = " + userId + " AND p1.deleted = 0)";
        // 子查询2:所属客户的负责人 + 团队成员
        // 注意:以 crm_customer.owner_user_id 为准,兼容权限表中负责人记录缺失/不一致的历史数据
        String subQuery2 = "(SELECT x.id FROM " + tableName + " x " +
                "INNER JOIN crm_customer cu ON cu.id = x.customer_id AND cu.deleted = 0 " +
                "WHERE x.deleted = 0 AND (cu.owner_user_id = " + userId +
                " OR EXISTS (SELECT 1 FROM crm_permission p2 " +
                "WHERE p2.biz_type = " + CrmBizTypeEnum.CRM_CUSTOMER.getType() + " " +
                "AND p2.biz_id = x.customer_id AND p2.user_id = " + userId + " AND p2.deleted = 0)))";
        // 使用 OR 连接两个子查询,并排除自己是负责人的情况
        query.apply("t.id IN (" + subQuery1 + " UNION " + subQuery2 + ")")
             .ne(ownerUserIdField, userId);