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["返回結果"]
取並集"] 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 | 僅 Level0 | level = 0 |
| 1 | <= Level1 | level <= 1 |
| 2 | <= Level2 | level <= 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
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 4 | MyBatis 資料許可權攔截器 | ✅ 已完成 |
| Phase 5 | API 介面實現 | ✅ 已完成 |
| Phase 6 | 前端頁面開發 | ✅ 已完成 |
| Phase 7 | 客戶等級級聯過濾 | ✅ 已完成 |
| Phase 8 | 測試與驗證 | ✅ 已完成 |
總結
本方案實現了基於 部門-崗位-角色-使用者 的四層資料許可權過濾機制:
- 操作許可權:透過
crm_biz_object_permission表管理,四層優先順序檢查 - 資料範圍:支援 ALL/SELF/DEPT/DEPT_AND_CHILD/CUSTOM 五種範圍
- 客戶等級過濾:
max_customer_level欄位,支援等級範圍疊加過濾 - 級聯繼承:合同、聯絡人等透過
customer_id自動繼承客戶等級限制 - SQL 攔截器:自動注入 WHERE 條件,無需修改現有業務程式碼
- 前端配置:統一的許可權配置介面,支援層級切換和等級配置