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 条件,无需修改现有业务代码
- 前端配置:统一的权限配置界面,支持层级切换和等级配置