CRM Four-Layer Data Permission Solution
Core Positioning
In a nutshell: On top of Yudao’s native
@CrmPermissionannotation, build a complete permission system with “User-Role-Post-Department” four-layer data permissions + customer level cascade filtering.
What Problems It Solves
| Level | Current Status | After Enhancement |
|---|---|---|
| User-Level | ✅ Supported | ✅ Maintained |
| Role-Level | ❌ Not supported | ✅ Role-permission association |
| Post-Level | ❌ Not supported | ✅ Post-permission association |
| Dept-Level | ❌ Not supported | ✅ Dept-permission association |
| Customer Level | ❌ Not supported | ✅ Level stacking + cascade filtering |
Suitable Users
- CRM systems requiring fine-grained data permission control
- Enterprises with customer level tiered management needs
- Organizations needing to isolate customer data by department/post
1. Four-Layer Data Permission Model
graph TB
subgraph "Permission Decision Engine"
A["User Request"] --> B["Permission Resolution"]
B --> C["Permission Merge
Union"] C --> D["Data Scope Filtering"] D --> E["Customer Level Filtering
AND Stacking"] end subgraph "Permission Sources" F["User-Level Permission
Priority 1 (Highest)"] G["Role-Level Permission
Priority 2"] H["Post-Level Permission
Priority 3"] I["Dept-Level Permission
Priority 4 (Lowest)"] end F --> B G --> B H --> B I --> B E --> J["SQL Filtering"] J --> K["Return Results"]
Union"] C --> D["Data Scope Filtering"] D --> E["Customer Level Filtering
AND Stacking"] end subgraph "Permission Sources" F["User-Level Permission
Priority 1 (Highest)"] G["Role-Level Permission
Priority 2"] H["Post-Level Permission
Priority 3"] I["Dept-Level Permission
Priority 4 (Lowest)"] end F --> B G --> B H --> B I --> B E --> J["SQL Filtering"] J --> K["Return Results"]
2. Permission Verification Flow
2.1 Operation Permission Verification
sequenceDiagram
participant User as User
participant Controller as Controller
participant Aspect as @CrmPermission Aspect
participant Engine as Permission Engine
participant DB as Database
User->>Controller: Request customer operation
Controller->>Aspect: Trigger permission check
Aspect->>Engine: hasBizOperationPermission()
Engine->>Engine: Check user-level permission
Engine->>Engine: Check role-level permission
Engine->>Engine: Check post-level permission
Engine->>Engine: Check dept-level permission
Engine->>Engine: Merge results (union)
alt Has Permission
Engine->>Aspect: Return true
Aspect->>Controller: Allow
Controller->>DB: Execute business operation
else No Permission
Engine->>Aspect: Return false
Aspect->>Controller: Throw permission exception
end
2.2 Data Permission Filtering Flow
sequenceDiagram
participant User as User
participant Controller as Controller
participant Interceptor as MyBatis Interceptor
participant Engine as Permission Engine
participant DB as Database
User->>Controller: Query customer list
Controller->>Interceptor: Trigger SQL interception
Interceptor->>Engine: buildDataScopeSql()
Engine->>Engine: Get data scope conditions
Engine->>Engine: Get customer level conditions
Engine->>Engine: Determine if cascade (related tables)
Engine->>Interceptor: Return SQL filter conditions
Interceptor->>Interceptor: Inject WHERE conditions
Interceptor->>DB: Execute filtered SQL
DB->>User: Return filtered results
3. Database Table Structure
3.1 Business Object Operation Permission Table
CREATE TABLE `crm_biz_object_permission` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
`biz_type` INT NOT NULL COMMENT 'Business Object Type (1=Lead/2=Customer/3=Contact/4=Opportunity/5=Contract)',
`biz_type_name` VARCHAR(50) NOT NULL COMMENT 'Business Object Name',
`operation_type` VARCHAR(20) NOT NULL COMMENT 'Operation Type (CREATE/READ/UPDATE/DELETE/TRANSFER)',
`operation_name` VARCHAR(50) NOT NULL COMMENT 'Operation Name',
`role_id` BIGINT DEFAULT NULL COMMENT 'Role ID',
`post_id` BIGINT DEFAULT NULL COMMENT 'Post ID',
`dept_id` BIGINT DEFAULT NULL COMMENT 'Department ID',
`user_id` BIGINT DEFAULT NULL COMMENT 'User ID',
`status` TINYINT DEFAULT 1 COMMENT 'Status (0=Disabled/1=Enabled)',
`creator` VARCHAR(64) DEFAULT '' COMMENT 'Creator',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Create Time',
`updater` VARCHAR(64) DEFAULT '' COMMENT 'Updater',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted` BIT DEFAULT 0 COMMENT 'Is Deleted',
`tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT 'Tenant ID',
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='Business Object Operation Permission Table';
3.2 Data Scope Permission Table (Including Customer Level)
CREATE TABLE `crm_data_scope_permission` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
`biz_type` INT NOT NULL COMMENT 'Business Object Type',
`scope_type` VARCHAR(20) NOT NULL COMMENT 'Data Scope Type (ALL/SELF/DEPT/DEPT_AND_CHILD/CUSTOM)',
`scope_value` TEXT COMMENT 'Custom Scope Value (JSON format)',
`max_customer_level` INT DEFAULT NULL COMMENT 'Max Customer Level (NULL=No Limit)',
`role_id` BIGINT DEFAULT NULL COMMENT 'Role ID',
`post_id` BIGINT DEFAULT NULL COMMENT 'Post ID',
`dept_id` BIGINT DEFAULT NULL COMMENT 'Department ID',
`user_id` BIGINT DEFAULT NULL COMMENT 'User ID',
`status` TINYINT DEFAULT 1 COMMENT 'Status (0=Disabled/1=Enabled)',
`creator` VARCHAR(64) DEFAULT '' COMMENT 'Creator',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Create Time',
`updater` VARCHAR(64) DEFAULT '' COMMENT 'Updater',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted` BIT DEFAULT 0 COMMENT 'Is Deleted',
`tenant_id` BIGINT NOT NULL DEFAULT 0 COMMENT 'Tenant ID',
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='Data Scope Permission Table';
3.3 Data Scope Type Description
| Scope Type | Description | SQL Filter Logic |
|---|---|---|
| ALL | All data | No filter condition |
| SELF | Own data | owner_user_id = current_user |
| DEPT | Dept data | dept_id IN (current_dept) |
| DEPT_AND_CHILD | Dept and sub-dept data | dept_id IN (current_dept_and_children) |
| CUSTOM | Custom scope | Generate filter condition based on scope_value |
3.4 Customer Level Filtering Description
| Level Config | Description | SQL Filter Logic |
|---|---|---|
| NULL | No customer level restriction | No filter condition |
| 0 | Level 0 only | level = 0 |
| 1 | <= Level 1 | level <= 1 |
| 2 | <= Level 2 | level <= 2 |
4. Permission Engine Core Implementation
@Service
public class CrmPermissionEngineServiceImpl implements CrmPermissionEngineService {
@Override
public boolean hasBizOperationPermission(Long userId, Integer bizType, String operationType) {
// Check by priority high to low: User → Role → Post → Dept
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();
// Data scope filtering
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";
}
// Customer level filtering (AND stacking)
Integer maxLevel = scope.getMaxCustomerLevel();
if (maxLevel != null) {
if (sqlBuilder.length() > 0) sqlBuilder.append(" AND ");
sqlBuilder.append("level <= ").append(maxLevel);
}
return sqlBuilder.toString();
}
}
5. MyBatis Data Permission Interceptor
@Component
@Intercepts({@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class CrmDataPermissionInterceptor implements Interceptor {
// Tables that need cascade customer level filtering via 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. Determine if data permission filtering is needed
// 2. Get current user ID
// 3. Parse business type
// 4. Build data permission filter conditions
// 5. Modify SQL, add WHERE filter conditions
// 6. Execute query
}
}
Cascade Association Relationships
graph TB
subgraph "Customer Level Cascade"
CUSTOMER["Customer Table
crm_customer
level field"] -->|customer_id| CONTACT["Contact
crm_contact"] CUSTOMER -->|customer_id| CONTRACT["Contract
crm_contract"] CUSTOMER -->|customer_id| RECEIVABLE["Receivable
crm_receivable"] CUSTOMER -->|customer_id| RECEIVABLE_PLAN["Receivable Plan
crm_receivable_plan"] CUSTOMER -->|customer_id| BUSINESS["Opportunity
crm_business"] end CLUE["Lead
crm_clue"] -.->|No Cascade| CUSTOMER
crm_customer
level field"] -->|customer_id| CONTACT["Contact
crm_contact"] CUSTOMER -->|customer_id| CONTRACT["Contract
crm_contract"] CUSTOMER -->|customer_id| RECEIVABLE["Receivable
crm_receivable"] CUSTOMER -->|customer_id| RECEIVABLE_PLAN["Receivable Plan
crm_receivable_plan"] CUSTOMER -->|customer_id| BUSINESS["Opportunity
crm_business"] end CLUE["Lead
crm_clue"] -.->|No Cascade| CUSTOMER
| Business Object | Association Field | Cascade? | Description |
|---|---|---|---|
| Customer | level | ❌ Source | Directly apply level condition |
| Contact | customer_id | ✅ Yes | Inherit level via customer ID association |
| Contract | customer_id | ✅ Yes | Inherit level via customer ID association |
| Receivable | customer_id | ✅ Yes | Inherit level via customer ID association |
| Opportunity | customer_id | ✅ Yes | Inherit level via customer ID association |
| Lead | - | ❌ No | Leads are not associated with customers |
6. Frontend Permission Configuration Interface
graph TB
subgraph "CRM Permission Management"
subgraph "Permission Level Switch"
A1["Dept Permission"]
A2["Post Permission"]
A3["Role Permission"]
A4["User Permission"]
end
subgraph "Business Object Selection"
B1["Customer"]
B2["Opportunity"]
B3["Contract"]
B4["Contact"]
B5["Lead"]
B6["Receivable"]
end
subgraph "Operation Permission Config"
C1["Create"]
C2["View"]
C3["Edit"]
C4["Delete"]
C5["Transfer"]
C6["Export"]
end
subgraph "Data Scope Config"
D1["All Data"]
D2["Own Data"]
D3["Dept Data"]
D4["Dept & Sub-Dept"]
D5["Custom Scope"]
end
subgraph "Customer Level Filter"
E1["All Levels"]
E2["Level 0"]
E3["<= Level 1"]
E4["<= Level 2"]
end
end
7. Implementation Progress
| Phase | Task | Status |
|---|---|---|
| Phase 1 | Database table structure design & creation | ✅ Completed |
| Phase 2 | Backend entity classes & Mapper | ✅ Completed |
| Phase 3 | Permission engine service implementation | ✅ Completed |
| Phase 4 | MyBatis data permission interceptor | ✅ Completed |
| Phase 5 | API interface implementation | ✅ Completed |
| Phase 6 | Frontend page development | ✅ Completed |
| Phase 7 | Customer level cascade filtering | ✅ Completed |
| Phase 8 | Testing & verification | ✅ Completed |
Summary
This solution implements a four-layer data permission filtering mechanism based on Department-Post-Role-User:
- Operation Permission: Managed via
crm_biz_object_permissiontable, four-level priority checking - Data Scope: Supports five scope types: ALL/SELF/DEPT/DEPT_AND_CHILD/CUSTOM
- Customer Level Filtering:
max_customer_levelfield, supports level range stacking filter - Cascade Inheritance: Contracts, contacts, etc. automatically inherit customer level restrictions via
customer_id - SQL Interceptor: Auto-injects WHERE conditions without modifying existing business code
- Frontend Configuration: Unified permission configuration interface with level switching and level configuration