CRM 四层数据权限方案

核心定位

一句話說清楚:在 Yudao 原生 @CrmPermission 註解基礎上,構建「使用者-角色-崗位-部門」四層資料許可權 + 客戶等級級聯過濾的完整許可權體系。

解決什麼問題

層級當前狀態增強後
使用者級✅ 支援✅ 保持
角色級❌ 不支援✅ 角色-許可權關聯
崗位級❌ 不支援✅ 崗位-許可權關聯
部門級❌ 不支援✅ 部門-許可權關聯
客戶等級❌ 不支援✅ 等級疊加 + 級聯過濾

適合什麼樣的使用者

  • 需要精細化資料許可權控制的 CRM 系統
  • 有客戶等級分層管理需求的企業
  • 需要按部門/崗位隔離客戶資料的組織

1. 四層資料許可權模型

graph TB subgraph "許可權決策引擎" A["使用者請求"] --> B["許可權解析"] B --> C["許可權合併
取並集"] C --> D["資料範圍過濾"] D --> E["客戶等級過濾
AND 疊加"] end subgraph "許可權來源" F["使用者級許可權
優先順序 1 (最高)"] G["角色級許可權
優先順序 2"] H["崗位級許可權
優先順序 3"] I["部門級許可權
優先順序 4 (最低)"] end F --> B G --> B H --> B I --> B E --> J["SQL 過濾"] J --> K["返回結果"]

2. 許可權校驗流程

2.1 操作許可權校驗

sequenceDiagram participant User as 使用者 participant Controller as Controller participant Aspect as @CrmPermission Aspect participant Engine as 許可權引擎 participant DB as 資料庫 User->>Controller: 請求操作客戶 Controller->>Aspect: 觸發許可權校驗 Aspect->>Engine: hasBizOperationPermission() Engine->>Engine: 檢查使用者級許可權 Engine->>Engine: 檢查角色級許可權 Engine->>Engine: 檢查崗位級許可權 Engine->>Engine: 檢查部門級許可權 Engine->>Engine: 合併結果 (取並集) alt 有許可權 Engine->>Aspect: 返回 true Aspect->>Controller: 放行 Controller->>DB: 執行業務操作 else 無許可權 Engine->>Aspect: 返回 false Aspect->>Controller: 丟擲許可權異常 end

2.2 資料許可權過濾流程

sequenceDiagram participant User as 使用者 participant Controller as Controller participant Interceptor as MyBatis 攔截器 participant Engine as 許可權引擎 participant DB as 資料庫 User->>Controller: 查詢客戶列表 Controller->>Interceptor: 觸發 SQL 攔截 Interceptor->>Engine: buildDataScopeSql() Engine->>Engine: 獲取資料範圍條件 Engine->>Engine: 獲取客戶等級條件 Engine->>Engine: 判斷是否級聯 (關聯表) Engine->>Interceptor: 返回 SQL 過濾條件 Interceptor->>Interceptor: 注入 WHERE 條件 Interceptor->>DB: 執行過濾後的 SQL DB->>User: 返回過濾結果

3. 資料庫表結構

3.1 業務物件操作許可權表

CREATE TABLE `crm_biz_object_permission` (
    `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主鍵',
    `biz_type` INT NOT NULL COMMENT '業務物件型別(1線索/2客戶/3聯絡人/4商機/5合同)',
    `biz_type_name` VARCHAR(50) NOT NULL COMMENT '業務物件名稱',
    `operation_type` VARCHAR(20) NOT NULL COMMENT '操作型別(CREATE/READ/UPDATE/DELETE/TRANSFER)',
    `operation_name` VARCHAR(50) NOT NULL COMMENT '操作名稱',
    `role_id` BIGINT DEFAULT NULL COMMENT '角色ID',
    `post_id` BIGINT DEFAULT NULL COMMENT '崗位ID',
    `dept_id` BIGINT DEFAULT NULL COMMENT '部門ID',
    `user_id` BIGINT DEFAULT NULL COMMENT '使用者ID',
    `status` TINYINT DEFAULT 1 COMMENT '狀態(0禁用/1啟用)',
    `creator` VARCHAR(64) DEFAULT '' COMMENT '建立者',
    `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間',
    `updater` VARCHAR(64) DEFAULT '' COMMENT '更新者',
    `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    `deleted` BIT DEFAULT 0 COMMENT '是否刪除',
    `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租戶編號',
    PRIMARY KEY (`id`),
    KEY `idx_biz_type` (`biz_type`),
    KEY `idx_role_id` (`role_id`),
    KEY `idx_post_id` (`post_id`),
    KEY `idx_dept_id` (`dept_id`),
    KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='業務物件操作許可權表';

3.2 資料許可權範圍表(含客戶等級)

CREATE TABLE `crm_data_scope_permission` (
    `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主鍵',
    `biz_type` INT NOT NULL COMMENT '業務物件型別',
    `scope_type` VARCHAR(20) NOT NULL COMMENT '資料範圍型別(ALL/SELF/DEPT/DEPT_AND_CHILD/CUSTOM)',
    `scope_value` TEXT COMMENT '自定義範圍值(JSON格式)',
    `max_customer_level` INT DEFAULT NULL COMMENT '最大客戶等級(NULL=不限制)',
    `role_id` BIGINT DEFAULT NULL COMMENT '角色ID',
    `post_id` BIGINT DEFAULT NULL COMMENT '崗位ID',
    `dept_id` BIGINT DEFAULT NULL COMMENT '部門ID',
    `user_id` BIGINT DEFAULT NULL COMMENT '使用者ID',
    `status` TINYINT DEFAULT 1 COMMENT '狀態(0禁用/1啟用)',
    `creator` VARCHAR(64) DEFAULT '' COMMENT '建立者',
    `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間',
    `updater` VARCHAR(64) DEFAULT '' COMMENT '更新者',
    `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    `deleted` BIT DEFAULT 0 COMMENT '是否刪除',
    `tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT '租戶編號',
    PRIMARY KEY (`id`),
    KEY `idx_biz_type` (`biz_type`),
    KEY `idx_role_id` (`role_id`),
    KEY `idx_post_id` (`post_id`),
    KEY `idx_dept_id` (`dept_id`),
    KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='資料許可權範圍表';

3.3 資料範圍型別說明

範圍型別說明SQL 過濾邏輯
ALL全部資料無過濾條件
SELF自己的資料owner_user_id = current_user
DEPT本部門資料dept_id IN (current_dept)
DEPT_AND_CHILD本部門及子部門dept_id IN (current_dept_and_children)
CUSTOM自定義範圍根據 scope_value 生成過濾條件

3.4 客戶等級過濾說明

等級配置說明SQL 過濾邏輯
NULL不限制客戶等級無過濾條件
0僅 Level0level = 0
1<= Level1level <= 1
2<= Level2level <= 2

4. 許可權引擎核心實現

@Service
public class CrmPermissionEngineServiceImpl implements CrmPermissionEngineService {

    @Override
    public boolean hasBizOperationPermission(Long userId, Integer bizType, String operationType) {
        // 按優先順序從高到低檢查:使用者 → 角色 → 崗位 → 部門
        if (hasUserPermission(userId, bizType, operationType)) return true;
        if (hasRolePermission(userId, bizType, operationType)) return true;
        if (hasPostPermission(userId, bizType, operationType)) return true;
        if (hasDeptPermission(userId, bizType, operationType)) return true;
        return false;
    }

    @Override
    public String buildDataScopeSql(Long userId, Integer bizType) {
        DataScopePermissionDO scope = getDataScope(userId, bizType);
        if (scope == null) return "1=0";

        StringBuilder sqlBuilder = new StringBuilder();

        // 資料範圍過濾
        switch (scope.getScopeType()) {
            case "ALL": break;
            case "SELF": sqlBuilder.append("owner_user_id = ").append(userId); break;
            case "DEPT": sqlBuilder.append("dept_id = ").append(getUserDeptId(userId)); break;
            case "DEPT_AND_CHILD": sqlBuilder.append("dept_id IN (").append(getUserDeptAndChildIds(userId)).append(")"); break;
            case "CUSTOM": sqlBuilder.append(scope.getScopeValue()); break;
            default: return "1=0";
        }

        // 客戶等級過濾(AND 疊加)
        Integer maxLevel = scope.getMaxCustomerLevel();
        if (maxLevel != null) {
            if (sqlBuilder.length() > 0) sqlBuilder.append(" AND ");
            sqlBuilder.append("level <= ").append(maxLevel);
        }

        return sqlBuilder.toString();
    }
}

5. MyBatis 資料許可權攔截器

@Component
@Intercepts({@Signature(type = Executor.class, method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class CrmDataPermissionInterceptor implements Interceptor {

    // 需要透過 customer_id 關聯客戶表進行等級級聯過濾的表
    private static final Set<String> CASCADE_CUSTOMER_LEVEL_TABLES = Set.of(
        "crm_business", "crm_contract", "crm_contact",
        "crm_receivable", "crm_receivable_plan"
    );

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 1. 判斷是否需要資料許可權過濾
        // 2. 獲取當前使用者ID
        // 3. 解析業務型別
        // 4. 構建資料許可權過濾條件
        // 5. 修改 SQL,新增 WHERE 過濾條件
        // 6. 執行查詢
    }
}

級聯關聯關係

graph TB subgraph "客戶等級級聯" CUSTOMER["客戶表
crm_customer
level 欄位"] -->|customer_id| CONTACT["聯絡人
crm_contact"] CUSTOMER -->|customer_id| CONTRACT["合同
crm_contract"] CUSTOMER -->|customer_id| RECEIVABLE["回款
crm_receivable"] CUSTOMER -->|customer_id| RECEIVABLE_PLAN["回款計劃
crm_receivable_plan"] CUSTOMER -->|customer_id| BUSINESS["商機
crm_business"] end CLUE["線索
crm_clue"] -.->|不級聯| CUSTOMER
業務物件關聯欄位是否級聯說明
客戶level❌ 源直接應用等級條件
聯絡人customer_id✅ 是透過客戶ID關聯繼承等級
合同customer_id✅ 是透過客戶ID關聯繼承等級
回款customer_id✅ 是透過客戶ID關聯繼承等級
商機customer_id✅ 是透過客戶ID關聯繼承等級
線索-❌ 否線索不關聯客戶

6. 前端許可權配置介面

graph TB subgraph "CRM 許可權管理" subgraph "許可權層級切換" A1["部門許可權"] A2["崗位許可權"] A3["角色許可權"] A4["使用者許可權"] end subgraph "業務物件選擇" B1["客戶"] B2["商機"] B3["合同"] B4["聯絡人"] B5["線索"] B6["回款"] end subgraph "操作許可權配置" C1["建立"] C2["檢視"] C3["編輯"] C4["刪除"] C5["轉移"] C6["匯出"] end subgraph "資料範圍配置" D1["全部資料"] D2["自己的資料"] D3["本部門資料"] D4["本部門及子部門"] D5["自定義範圍"] end subgraph "客戶等級過濾" E1["全部等級"] E2["Level0"] E3["<= Level1"] E4["<= Level2"] end end

7. 實施進度

階段任務狀態
Phase 1資料庫表結構設計與建立✅ 已完成
Phase 2後端實體類與 Mapper✅ 已完成
Phase 3許可權引擎服務實現✅ 已完成
Phase 4MyBatis 資料許可權攔截器✅ 已完成
Phase 5API 介面實現✅ 已完成
Phase 6前端頁面開發✅ 已完成
Phase 7客戶等級級聯過濾✅ 已完成
Phase 8測試與驗證✅ 已完成

總結

本方案實現了基於 部門-崗位-角色-使用者 的四層資料許可權過濾機制:

  1. 操作許可權:透過 crm_biz_object_permission 表管理,四層優先順序檢查
  2. 資料範圍:支援 ALL/SELF/DEPT/DEPT_AND_CHILD/CUSTOM 五種範圍
  3. 客戶等級過濾max_customer_level 欄位,支援等級範圍疊加過濾
  4. 級聯繼承:合同、聯絡人等透過 customer_id 自動繼承客戶等級限制
  5. SQL 攔截器:自動注入 WHERE 條件,無需修改現有業務程式碼
  6. 前端配置:統一的許可權配置介面,支援層級切換和等級配置
docs