Build lightweight AI agent admin

This commit is contained in:
Codex
2026-06-08 18:14:59 +08:00
commit e164840f43
2530 changed files with 435693 additions and 0 deletions
@@ -0,0 +1,148 @@
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.base_schema import ResponseModel
from app.resource_registry import ResourceRegistry
from core.resource_scope.field_permission.schema import (
FieldPermissionResponse,
FieldPermissionBatchUpdate,
ResourceFieldsMetadataResponse,
ResourceFieldMetadata
)
from core.resource_scope.field_permission.service import ResourceFieldPermissionService
router = APIRouter(prefix="/field-permissions", tags=["字段权限配置"])
@router.get("/resource-fields", response_model=List[ResourceFieldsMetadataResponse], summary="获取所有资源的字段元数据")
async def get_all_resource_fields(
application_id: Optional[str] = Query(None, alias="applicationId", description="应用ID,子应用访问时只显示该应用的资源")
):
"""获取所有已注册资源的字段元数据"""
# 获取资源列表,支持按应用过滤
all_resources = ResourceRegistry.get_all_resources(application_id=application_id)
result = []
for resource_item in all_resources:
resource_type = resource_item['resource_type']
resource_info = ResourceRegistry.get_resource(resource_type)
if not resource_info:
continue
# 优先从 service_class 获取字段元数据,否则从注册表获取
field_metadata = None
service_class = resource_info.get('service')
if service_class and hasattr(service_class, 'FIELD_METADATA'):
field_metadata = service_class.FIELD_METADATA
elif resource_info.get('field_metadata'):
field_metadata = resource_info.get('field_metadata')
if not field_metadata:
continue
fields = [
ResourceFieldMetadata(
field_name=field_name,
label=meta.get('label', field_name),
field_type=meta.get('field_type', 'string'),
required=meta.get('required', False),
sensitive=meta.get('sensitive', False),
maskable=meta.get('maskable', False),
default_permission=meta.get('default_permission', 'write')
)
for field_name, meta in field_metadata.items()
]
result.append(ResourceFieldsMetadataResponse(
resource_type=resource_type,
display_name=resource_info.get('display_name', resource_type),
fields=fields
))
return result
@router.get("/resource-fields/{resource_type}", response_model=ResourceFieldsMetadataResponse, summary="获取指定资源的字段元数据")
async def get_resource_fields(resource_type: str):
"""获取指定资源的字段元数据"""
resource_info = ResourceRegistry.get_resource(resource_type)
if not resource_info:
raise HTTPException(status_code=404, detail=f"资源类型不存在: {resource_type}")
# 优先从 service_class 获取字段元数据,否则从注册表获取
field_metadata = None
service_class = resource_info.get('service')
if service_class and hasattr(service_class, 'FIELD_METADATA'):
field_metadata = service_class.FIELD_METADATA
elif resource_info.get('field_metadata'):
field_metadata = resource_info.get('field_metadata')
if not field_metadata:
raise HTTPException(status_code=404, detail=f"资源未定义字段元数据: {resource_type}")
fields = [
ResourceFieldMetadata(
field_name=field_name,
label=meta.get('label', field_name),
field_type=meta.get('field_type', 'string'),
required=meta.get('required', False),
sensitive=meta.get('sensitive', False),
maskable=meta.get('maskable', False),
default_permission=meta.get('default_permission', 'write')
)
for field_name, meta in field_metadata.items()
]
return ResourceFieldsMetadataResponse(
resource_type=resource_type,
display_name=resource_info.get('display_name', resource_type),
fields=fields
)
@router.get("/{role_id}", response_model=List[FieldPermissionResponse], summary="获取角色的字段权限配置")
async def get_role_field_permissions(
role_id: str,
resource_type: str = Query(..., description="资源类型"),
db: AsyncSession = Depends(get_db)
):
"""获取角色在指定资源类型下的字段权限配置"""
configs = await ResourceFieldPermissionService.get_by_role_and_resource(
db, role_id, resource_type
)
return configs
@router.post("/batch", response_model=ResponseModel, summary="批量更新字段权限配置")
async def batch_update_field_permissions(
data: FieldPermissionBatchUpdate,
db: AsyncSession = Depends(get_db)
):
"""批量更新角色的字段权限配置"""
# 验证资源类型是否存在
if not ResourceRegistry.validate_resource_type(data.resource_type):
raise HTTPException(status_code=400, detail=f"资源类型不存在: {data.resource_type}")
await ResourceFieldPermissionService.batch_update(db, data)
return ResponseModel(message="字段权限配置成功")
@router.delete("/{role_id}", response_model=ResponseModel, summary="删除角色的字段权限配置")
async def delete_role_field_permissions(
role_id: str,
resource_type: str = Query(..., description="资源类型"),
db: AsyncSession = Depends(get_db)
):
"""删除角色在指定资源类型下的所有字段权限配置"""
from sqlalchemy import delete
from core.resource_scope.field_permission.model import ResourceFieldPermissionConfig
stmt = delete(ResourceFieldPermissionConfig).where(
ResourceFieldPermissionConfig.role_id == role_id,
ResourceFieldPermissionConfig.resource_type == resource_type
)
await db.execute(stmt)
await db.commit()
return ResponseModel(message="删除成功")
@@ -0,0 +1,24 @@
from sqlalchemy import Column, String, Index
from app.base_model import BaseModel
class ResourceFieldPermissionConfig(BaseModel):
"""角色资源字段权限配置(列权限)"""
__tablename__ = "sys_role_resource_field_permission_config"
role_id = Column(String(50), nullable=False, comment="角色ID")
resource_type = Column(String(100), nullable=False, comment="资源类型")
field_name = Column(String(100), nullable=False, comment="字段名称")
permission_type = Column(
String(20),
nullable=False,
default="read",
comment="权限类型: read/write/hidden/masked"
)
mask_rule = Column(String(50), nullable=True, comment="脱敏规则: phone/email/id_card/name")
__table_args__ = (
Index('idx_role_resource_field', 'role_id', 'resource_type', 'field_name', unique=True),
Index('idx_role_resource', 'role_id', 'resource_type'),
)
@@ -0,0 +1,81 @@
from datetime import datetime
from typing import Optional, List
from pydantic import BaseModel, ConfigDict, field_validator
from app.base_schema import CSTDatetime
class FieldPermissionBase(BaseModel):
"""字段权限基础 Schema"""
field_name: str
permission_type: str = "read" # read/write/hidden/masked
mask_rule: Optional[str] = None
@field_validator('permission_type')
@classmethod
def validate_permission_type(cls, v):
allowed = ['read', 'write', 'hidden', 'masked']
if v not in allowed:
raise ValueError(f'permission_type must be one of {allowed}')
return v
@field_validator('mask_rule')
@classmethod
def validate_mask_rule(cls, v, info):
if info.data.get('permission_type') == 'masked' and not v:
raise ValueError('mask_rule is required when permission_type is masked')
if v:
allowed = ['phone', 'email', 'id_card', 'name', 'default']
if v not in allowed:
raise ValueError(f'mask_rule must be one of {allowed}')
return v
class FieldPermissionCreate(FieldPermissionBase):
"""创建字段权限配置"""
role_id: str
resource_type: str
class FieldPermissionUpdate(BaseModel):
"""更新字段权限配置"""
permission_type: Optional[str] = None
mask_rule: Optional[str] = None
class FieldPermissionResponse(FieldPermissionBase):
"""字段权限配置响应"""
id: str
role_id: str
resource_type: str
sort: int = 0
is_deleted: bool = False
sys_create_datetime: Optional[CSTDatetime] = None
sys_update_datetime: Optional[CSTDatetime] = None
model_config = ConfigDict(from_attributes=True)
class FieldPermissionBatchUpdate(BaseModel):
"""批量更新字段权限配置"""
role_id: str
resource_type: str
configs: List[FieldPermissionBase]
class ResourceFieldMetadata(BaseModel):
"""资源字段元数据"""
field_name: str
label: str
field_type: str # string/integer/boolean/datetime
required: bool = False # 是否必填字段(必填字段不可隐藏)
sensitive: bool = False # 是否敏感字段
maskable: bool = False # 是否可脱敏
default_permission: str = "write"
class ResourceFieldsMetadataResponse(BaseModel):
"""资源字段元数据响应"""
resource_type: str
display_name: str
fields: List[ResourceFieldMetadata]
@@ -0,0 +1,152 @@
from typing import List, Dict
from sqlalchemy import select, delete
from sqlalchemy.ext.asyncio import AsyncSession
from app.base_service import BaseService
from core.resource_scope.field_permission.model import ResourceFieldPermissionConfig
from core.resource_scope.field_permission.schema import (
FieldPermissionCreate,
FieldPermissionUpdate,
FieldPermissionBatchUpdate
)
class ResourceFieldPermissionService(BaseService[
ResourceFieldPermissionConfig,
FieldPermissionCreate,
FieldPermissionUpdate
]):
"""资源字段权限配置服务"""
model = ResourceFieldPermissionConfig
@classmethod
async def get_by_role_and_resource(
cls,
db: AsyncSession,
role_id: str,
resource_type: str
) -> List[ResourceFieldPermissionConfig]:
"""获取角色在指定资源类型下的字段权限配置"""
stmt = select(cls.model).where(
cls.model.role_id == role_id,
cls.model.resource_type == resource_type,
cls.model.is_deleted == False
)
result = await db.execute(stmt)
return list(result.scalars().all())
@classmethod
async def get_by_roles_and_resource(
cls,
db: AsyncSession,
role_ids: List[str],
resource_type: str
) -> List[ResourceFieldPermissionConfig]:
"""获取多个角色在指定资源类型下的字段权限配置"""
stmt = select(cls.model).where(
cls.model.role_id.in_(role_ids),
cls.model.resource_type == resource_type,
cls.model.is_deleted == False
)
result = await db.execute(stmt)
return list(result.scalars().all())
@classmethod
async def batch_update(
cls,
db: AsyncSession,
data: FieldPermissionBatchUpdate
) -> None:
"""批量更新字段权限配置"""
from app.field_permission_cache import FieldPermissionCache
# 1. 删除该角色在该资源类型下的所有字段权限配置
stmt = delete(cls.model).where(
cls.model.role_id == data.role_id,
cls.model.resource_type == data.resource_type
)
await db.execute(stmt)
# 2. 批量插入新的配置
for config in data.configs:
new_config = cls.model(
role_id=data.role_id,
resource_type=data.resource_type,
field_name=config.field_name,
permission_type=config.permission_type,
mask_rule=config.mask_rule
)
db.add(new_config)
await db.commit()
# 3. 清除缓存
await FieldPermissionCache.delete(data.role_id, data.resource_type)
# 清除包含该角色的所有多角色缓存
await FieldPermissionCache.delete_by_role(data.role_id)
@classmethod
async def merge_field_permissions(
cls,
configs: List[ResourceFieldPermissionConfig],
strategy: str = "most_permissive"
) -> Dict[str, Dict]:
"""
合并多个角色的字段权限配置
Args:
configs: 字段权限配置列表
strategy: 合并策略
- most_permissive: 最宽松(默认)
- most_restrictive: 最严格
Returns:
合并后的字段权限字典
"""
field_perms = {}
# 权限等级定义(数字越大权限越高)
perm_levels = {
'hidden': 0,
'masked': 1,
'read': 2,
'write': 3
}
for config in configs:
field_name = config.field_name
current_perm = config.permission_type
current_level = perm_levels.get(current_perm, 0)
if field_name not in field_perms:
field_perms[field_name] = {
'permission': current_perm,
'mask_rule': config.mask_rule,
'level': current_level
}
else:
existing_level = field_perms[field_name]['level']
if strategy == "most_permissive":
# 取权限最高的
if current_level > existing_level:
field_perms[field_name] = {
'permission': current_perm,
'mask_rule': config.mask_rule,
'level': current_level
}
elif strategy == "most_restrictive":
# 取权限最低的
if current_level < existing_level:
field_perms[field_name] = {
'permission': current_perm,
'mask_rule': config.mask_rule,
'level': current_level
}
# 移除临时的 level 字段
for field_name in field_perms:
del field_perms[field_name]['level']
return field_perms
@@ -0,0 +1,128 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
ResourceDataScopeConfig API - 资源数据权限配置API
"""
from typing import List, Dict, Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.base_schema import ResponseModel
from core.resource_scope.scope_permission.schema import (
ResourceDataScopeConfigCreate,
ResourceDataScopeConfigUpdate,
ResourceDataScopeConfigResponse,
RoleResourceScopeBatchUpdate
)
from core.resource_scope.scope_permission.service import ResourceDataScopeConfigService
from app.resource_registry import ResourceRegistry
router = APIRouter(prefix="/resource-scope", tags=["资源数据权限配置"])
@router.get("/types", response_model=List[Dict], summary="获取所有资源类型")
async def get_resource_types(
application_id: Optional[str] = Query(None, alias="applicationId", description="应用ID,子应用访问时只显示该应用的资源")
):
"""
获取所有已注册的资源类型
返回格式:
[
{
"resource_type": "customer",
"display_name": "客户",
"model_name": "Customer",
"table_name": "core_customer"
},
...
]
"""
return ResourceRegistry.get_all_resources(application_id=application_id)
@router.get("/types/list", response_model=List[str], summary="获取资源类型列表")
async def get_resource_type_list():
"""获取所有资源类型的简单列表(只返回 resource_type 字符串)"""
return ResourceRegistry.get_all_resource_types()
@router.get("/registry/info", response_model=ResponseModel, summary="获取注册表信息")
async def get_registry_info():
"""获取资源类型注册表的统计信息"""
info = ResourceRegistry.get_registry_info()
return ResponseModel(message="获取成功", data=info)
@router.post("/", response_model=ResourceDataScopeConfigResponse, summary="创建资源权限配置")
async def create_config(
data: ResourceDataScopeConfigCreate,
db: AsyncSession = Depends(get_db)
):
"""创建资源数据权限配置"""
# 检查是否已存在
existing = await ResourceDataScopeConfigService.get_by_role_and_resource(
db, data.role_id, data.resource_type
)
if existing:
raise HTTPException(status_code=400, detail="该角色的资源权限配置已存在")
return await ResourceDataScopeConfigService.create(db=db, data=data)
@router.get("/role/{role_id}", response_model=List[ResourceDataScopeConfigResponse], summary="获取角色的资源权限配置")
async def get_role_configs(
role_id: str,
db: AsyncSession = Depends(get_db)
):
"""获取角色的所有资源权限配置"""
return await ResourceDataScopeConfigService.get_role_configs(db, role_id)
@router.put("/role/batch", response_model=List[ResourceDataScopeConfigResponse], summary="批量更新角色的资源权限配置")
async def batch_update_role_configs(
data: RoleResourceScopeBatchUpdate,
db: AsyncSession = Depends(get_db)
):
"""批量更新角色的资源权限配置(会删除旧配置,创建新配置)"""
return await ResourceDataScopeConfigService.batch_update_role_configs(db, data)
@router.get("/{config_id}", response_model=ResourceDataScopeConfigResponse, summary="获取配置详情")
async def get_config(
config_id: str,
db: AsyncSession = Depends(get_db)
):
"""获取资源权限配置详情"""
config = await ResourceDataScopeConfigService.get_by_id(db, config_id)
if not config:
raise HTTPException(status_code=404, detail="配置不存在")
return config
@router.put("/{config_id}", response_model=ResourceDataScopeConfigResponse, summary="更新配置")
async def update_config(
config_id: str,
data: ResourceDataScopeConfigUpdate,
db: AsyncSession = Depends(get_db)
):
"""更新资源权限配置"""
result = await ResourceDataScopeConfigService.update(db, config_id, data)
if not result:
raise HTTPException(status_code=404, detail="配置不存在")
return result
@router.delete("/{config_id}", response_model=ResponseModel, summary="删除配置")
async def delete_config(
config_id: str,
hard: bool = Query(default=False, description="是否物理删除"),
db: AsyncSession = Depends(get_db)
):
"""删除资源权限配置"""
success = await ResourceDataScopeConfigService.delete(db, config_id, hard=hard)
if not success:
raise HTTPException(status_code=404, detail="配置不存在")
return ResponseModel(message="删除成功")
@@ -0,0 +1,61 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
ResourceDataScopeConfig Model - 资源数据权限配置模型
用于管理角色对不同资源类型的数据访问权限
"""
from sqlalchemy import Column, String, Integer, JSON, UniqueConstraint
from app.base_model import BaseModel
class ResourceDataScopeConfig(BaseModel):
"""
资源数据权限配置模型
用于配置角色对特定资源类型的数据访问范围
字段说明:
- role_id: 角色ID(逻辑外键关联core_role
- resource_type: 资源类型(如 'customer', 'order', 'post' 等)
- data_scope: 数据权限范围(0-全部, 1-仅本人, 2-本部门, 3-本部门及下级, 4-自定义)
- dept_ids: 自定义权限时的部门ID列表(JSON格式)
示例:
- role_id='role001', resource_type='customer', data_scope=4, dept_ids=['dept1', 'dept2']
表示:角色role001对客户资源使用自定义权限,可以访问dept1和dept2的客户数据
"""
__tablename__ = "core_resource_data_scope_config"
# 数据权限范围选择
DATA_SCOPE_CHOICES = {
0: '全部数据',
1: '仅本人数据',
2: '本部门数据',
3: '本部门及下级部门数据',
4: '自定义数据',
}
# 角色ID(逻辑外键)
role_id = Column(String(21), nullable=False, index=True, comment="角色ID(逻辑外键关联core_role")
# 资源类型
resource_type = Column(String(50), nullable=False, index=True, comment="资源类型(如customer/order/post等)")
# 数据权限范围
data_scope = Column(Integer, default=0, comment="数据权限范围(0-全部, 1-仅本人, 2-本部门, 3-本部门及下级, 4-自定义)")
# 自定义权限的部门ID列表
dept_ids = Column(JSON, nullable=True, comment="自定义权限时的部门ID列表(JSON数组)")
# 联合唯一约束:同一个角色对同一个资源类型只能有一条配置
__table_args__ = (
UniqueConstraint('role_id', 'resource_type', name='uq_role_resource'),
)
def __str__(self):
return f"Role({self.role_id}) - Resource({self.resource_type}) - Scope({self.data_scope})"
def get_data_scope_display(self) -> str:
"""获取数据权限范围的显示名称"""
return self.DATA_SCOPE_CHOICES.get(self.data_scope, '未知')
@@ -0,0 +1,54 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
ResourceDataScopeConfig Schema - 资源数据权限配置Schema
"""
from typing import Optional, List
from datetime import datetime
from pydantic import BaseModel, ConfigDict, Field
from app.base_schema import CSTDatetime
class ResourceDataScopeConfigBase(BaseModel):
"""资源数据权限配置基础Schema"""
role_id: str = Field(..., description="角色ID")
resource_type: str = Field(..., description="资源类型")
data_scope: int = Field(default=0, ge=0, le=4, description="数据权限范围(0-4")
dept_ids: Optional[List[str]] = Field(default=None, description="自定义权限的部门ID列表")
class ResourceDataScopeConfigCreate(ResourceDataScopeConfigBase):
"""创建资源数据权限配置Schema"""
pass
class ResourceDataScopeConfigUpdate(BaseModel):
"""更新资源数据权限配置Schema"""
data_scope: Optional[int] = Field(default=None, ge=0, le=4, description="数据权限范围")
dept_ids: Optional[List[str]] = Field(default=None, description="部门ID列表")
class ResourceDataScopeConfigResponse(ResourceDataScopeConfigBase):
"""资源数据权限配置响应Schema"""
id: str
sort: int = 0
is_deleted: bool = False
sys_create_datetime: Optional[CSTDatetime] = None
sys_update_datetime: Optional[CSTDatetime] = None
model_config = ConfigDict(from_attributes=True)
class RoleResourceScopeConfig(BaseModel):
"""角色的资源权限配置(用于批量配置)"""
resource_type: str = Field(..., description="资源类型")
data_scope: int = Field(default=0, ge=0, le=4, description="数据权限范围")
dept_ids: Optional[List[str]] = Field(default=None, description="部门ID列表")
class RoleResourceScopeBatchUpdate(BaseModel):
"""批量更新角色的资源权限配置"""
role_id: str = Field(..., description="角色ID")
configs: List[RoleResourceScopeConfig] = Field(..., description="资源权限配置列表")
@@ -0,0 +1,268 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
ResourceDataScopeConfig Service - 资源数据权限配置服务
"""
from typing import List, Optional, Dict, Any
from sqlalchemy import select, delete
from sqlalchemy.ext.asyncio import AsyncSession
from app.base_service import BaseService
from core.resource_scope.scope_permission.model import ResourceDataScopeConfig
from core.resource_scope.scope_permission.schema import (
ResourceDataScopeConfigCreate,
ResourceDataScopeConfigUpdate,
RoleResourceScopeBatchUpdate
)
class ResourceDataScopeConfigService(BaseService[
ResourceDataScopeConfig,
ResourceDataScopeConfigCreate,
ResourceDataScopeConfigUpdate
]):
"""资源数据权限配置服务"""
model = ResourceDataScopeConfig
@classmethod
async def get_by_role_and_resource(
cls,
db: AsyncSession,
role_id: str,
resource_type: str
) -> Optional[ResourceDataScopeConfig]:
"""
根据角色ID和资源类型获取配置
:param db: 数据库会话
:param role_id: 角色ID
:param resource_type: 资源类型
:return: 配置记录或None
"""
result = await db.execute(
select(cls.model).where(
cls.model.role_id == role_id,
cls.model.resource_type == resource_type,
cls.model.is_deleted == False # noqa: E712
)
)
return result.scalar_one_or_none()
@classmethod
async def get_role_configs(
cls,
db: AsyncSession,
role_id: str
) -> List[ResourceDataScopeConfig]:
"""
获取角色的所有资源权限配置
:param db: 数据库会话
:param role_id: 角色ID
:return: 配置列表
"""
result = await db.execute(
select(cls.model).where(
cls.model.role_id == role_id,
cls.model.is_deleted == False # noqa: E712
)
)
return list(result.scalars().all())
@classmethod
async def batch_update_role_configs(
cls,
db: AsyncSession,
data: RoleResourceScopeBatchUpdate
) -> List[ResourceDataScopeConfig]:
"""
批量更新角色的资源权限配置
:param db: 数据库会话
:param data: 批量更新数据
:return: 更新后的配置列表
"""
# 删除该角色的所有现有配置
await db.execute(
delete(cls.model).where(cls.model.role_id == data.role_id)
)
# 创建新配置
configs = []
for config_data in data.configs:
config = cls.model(
role_id=data.role_id,
resource_type=config_data.resource_type,
data_scope=config_data.data_scope,
dept_ids=config_data.dept_ids
)
db.add(config)
configs.append(config)
await db.commit()
# 刷新所有配置
for config in configs:
await db.refresh(config)
return configs
@classmethod
def _merge_data_scope_configs(cls, configs: List[ResourceDataScopeConfig]) -> ResourceDataScopeConfig:
"""
合并多个角色的数据权限配置(使用最宽松策略)
权限优先级:0(全部) > 4(自定义) > 3(本部门及下级) > 2(本部门) > 1(仅本人)
:param configs: 配置列表
:return: 合并后的配置
"""
if not configs:
# 返回默认配置
return ResourceDataScopeConfig(data_scope=0)
# 如果有任何一个角色是"全部数据",则返回全部数据
for config in configs:
if config.data_scope == 0:
return config
# 找出最宽松的权限
max_scope = max(config.data_scope for config in configs)
# 如果是自定义权限,合并所有自定义部门
if max_scope == 4:
all_dept_ids = set()
for config in configs:
if config.data_scope == 4 and config.dept_ids:
all_dept_ids.update(config.dept_ids)
# 创建合并后的配置
merged = ResourceDataScopeConfig(
data_scope=4,
dept_ids=list(all_dept_ids) if all_dept_ids else []
)
return merged
# 返回最宽松的配置
for config in configs:
if config.data_scope == max_scope:
return config
return configs[0]
@classmethod
async def get_resource_data_scope(
cls,
db: AsyncSession,
role_id: Optional[str] = None,
resource_type: str = None,
is_superuser: bool = False,
role_ids: Optional[List[str]] = None
) -> Dict[str, Any]:
"""
获取资源的数据权限配置(支持多角色)
:param db: 数据库会话
:param role_id: 角色ID(单个,向后兼容)
:param resource_type: 资源类型
:param is_superuser: 是否超级管理员
:param role_ids: 角色ID列表(多个角色)
:return: 数据权限配置字典
"""
# 超级管理员:全部数据
if is_superuser:
return {
'filter_type': 'all',
'scope': 0,
'user_id': None,
'dept_id': None,
'dept_ids': None
}
# 处理角色ID列表
if role_ids is None:
role_ids = [role_id] if role_id else []
# 没有角色:仅本人
if not role_ids:
return {
'filter_type': 'self',
'scope': 1,
'user_id': None, # 将在应用时填充
'dept_id': None,
'dept_ids': None
}
# 查询所有角色的配置
configs = []
for rid in role_ids:
config = await cls.get_by_role_and_resource(db, rid, resource_type)
if config:
configs.append(config)
# 如果没有配置,默认全部数据
if not configs:
return {
'filter_type': 'all',
'scope': 0,
'user_id': None,
'dept_id': None,
'dept_ids': None
}
# 合并多个角色的权限配置(使用最宽松策略)
# 权限优先级:0(全部) > 4(自定义) > 3(本部门及下级) > 2(本部门) > 1(仅本人)
merged_config = cls._merge_data_scope_configs(configs)
# 根据 data_scope 返回配置
if merged_config.data_scope == 0: # 全部数据
return {
'filter_type': 'all',
'scope': 0,
'user_id': None,
'dept_id': None,
'dept_ids': None
}
elif merged_config.data_scope == 1: # 仅本人
return {
'filter_type': 'self',
'scope': 1,
'user_id': None, # 将在应用时填充
'dept_id': None,
'dept_ids': None
}
elif merged_config.data_scope == 2: # 本部门
return {
'filter_type': 'dept',
'scope': 2,
'user_id': None,
'dept_id': None, # 将在应用时填充
'dept_ids': None
}
elif merged_config.data_scope == 3: # 本部门及下级
return {
'filter_type': 'dept_and_children',
'scope': 3,
'user_id': None,
'dept_id': None,
'dept_ids': None # 将在应用时填充
}
elif merged_config.data_scope == 4: # 自定义
return {
'filter_type': 'custom',
'scope': 4,
'user_id': None,
'dept_id': None,
'dept_ids': merged_config.dept_ids or []
}
# 默认全部数据
return {
'filter_type': 'all',
'scope': 0,
'user_id': None,
'dept_id': None,
'dept_ids': None
}