Build lightweight AI agent admin
This commit is contained in:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user