feat: add tenant-scoped rule and permission management

This commit is contained in:
wren
2026-05-21 22:03:08 +08:00
parent a2c2bf1969
commit 1f1bccf3b3
193 changed files with 64463 additions and 1771 deletions
@@ -40,7 +40,8 @@ class ContractTemplateController(BaseController):
keyword: str | None = Query(None, description="关键词"),
category_id: int | None = Query(None, description="分类ID"),
category_name: str | None = Query(None, description="分类名称"),
region: str | None = Query(None, description="地区"),
region: str | None = Query(None, description="兼容保留字段:租户展示值/旧地区"),
tenant_code: str | None = Query(None, description="租户编码"),
file_format: str | None = Query(None, description="文件格式"),
is_featured: bool | None = Query(None, description="是否推荐"),
page: int = Query(1, ge=1, description="页码"),
@@ -56,6 +57,7 @@ class ContractTemplateController(BaseController):
category_id=category_id,
category_name=category_name,
region=region,
tenant_code=tenant_code,
file_format=file_format,
is_featured=is_featured,
page=page,
@@ -72,6 +74,7 @@ class ContractTemplateController(BaseController):
template_code: str = Form(...),
category_id: int = Form(...),
region: str | None = Form(default=None),
tenant_code: str | None = Form(default=None),
description: str | None = Form(default=None),
is_featured: bool = Form(default=False),
file: UploadFile = File(...),
@@ -79,12 +82,13 @@ class ContractTemplateController(BaseController):
payload: dict = Depends(verify_access_token),
):
if not await self._check_permission(int(payload["user_id"]), ["contract_template:create:write"]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前仅允许地区管理员上传合同模板", "data": None})
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前仅允许租户管理员上传合同模板", "data": None})
body = ContractTemplateCreateDTO(
title=title,
template_code=template_code,
category_id=category_id,
region=region,
tenant_code=tenant_code,
description=description,
is_featured=is_featured,
)
@@ -96,7 +100,8 @@ class ContractTemplateController(BaseController):
q: str = Query(..., min_length=1, description="搜索关键词"),
category_id: int | None = Query(None, description="分类ID"),
category_name: str | None = Query(None, description="分类名称"),
region: str | None = Query(None, description="地区"),
region: str | None = Query(None, description="兼容保留字段:租户展示值/旧地区"),
tenant_code: str | None = Query(None, description="租户编码"),
page: int = Query(1, ge=1, description="页码"),
page_size: int = Query(12, ge=1, le=200, description="分页大小"),
sort_by: str = Query("updated_at", description="排序字段"),
@@ -110,6 +115,7 @@ class ContractTemplateController(BaseController):
category_id=category_id,
category_name=category_name,
region=region,
tenant_code=tenant_code,
page=page,
page_size=page_size,
sort_by=sort_by,
@@ -51,6 +51,13 @@ class ReviewPointAuditDTO(BaseModel):
class DocumentController(BaseController):
"""文档控制器。"""
@staticmethod
def _tenant_context(payload: dict[str, Any]) -> dict[str, str | None]:
return {
"TenantCode": payload.get("tenant_code"),
"TenantName": payload.get("tenant_name"),
}
def __init__(self):
super().__init__(prefix="", tags=["文档"])
self.DocumentService: IDocumentService = DocumentServiceImpl()
@@ -62,7 +69,8 @@ class DocumentController(BaseController):
typeId: int | None = Form(None, description="文档类型ID"),
typeCode: str | None = Form(None, description="文档类型编码"),
groupId: int | None = Form(None, description="二级分组ID"),
region: str = Form("default", description="所属地区"),
region: str | None = Form(None, description="所属租户/地区"),
tenant_code: str | None = Form(None, description="租户编码"),
fileRole: str = Form("primary", description="文件角色"),
createdBy: int | None = Form(None, description="上传用户ID"),
autoRun: bool = Form(False, description="是否上传后自动触发评查"),
@@ -81,6 +89,7 @@ class DocumentController(BaseController):
attachment.content_type,
)
)
tenant_context = self._tenant_context(payload)
Data = await self.DocumentService.Upload(
FileName=file.filename or "upload.bin",
FileContent=Content,
@@ -91,6 +100,8 @@ class DocumentController(BaseController):
Region=region,
FileRole=fileRole,
CreatedBy=int(payload["user_id"]),
TenantCode=tenant_code or tenant_context.get("TenantCode"),
TenantName=tenant_context.get("TenantName"),
Attachments=attachmentPayloads,
AutoRun=autoRun,
Speed=speed,
@@ -133,7 +144,8 @@ class DocumentController(BaseController):
typeCode: str | None = None,
type_ids: str | None = Query(None, description="逗号分隔的文档类型ID列表"),
entry_module_id: int | None = Query(None, description="按入口模块ID过滤文档"),
region: str | None = None,
region: str | None = Query(None, description="兼容保留字段:租户展示值/旧地区"),
tenant_code: str | None = Query(None, description="租户编码"),
processingStatus: str | None = None,
resultStatus: str | None = None,
auditStatus: int | None = Query(None, description="按人工审核状态过滤"),
@@ -156,6 +168,7 @@ class DocumentController(BaseController):
TypeIds=typeIdList,
EntryModuleId=entry_module_id,
Region=region,
TenantCode=tenant_code,
ProcessingStatus=processingStatus,
ResultStatus=resultStatus,
AuditStatus=auditStatus,
@@ -23,7 +23,8 @@ class EntryModuleController(BaseController):
@self.router.get("")
async def GetEntryModules(
name: str | None = Query(None, description="模块名称模糊搜索"),
area: str | None = Query(None, description="地区筛选"),
area: str | None = Query(None, description="历史地区筛选(兼容参数,建议改用 tenant_code"),
tenant_code: str | None = Query(None, description="租户编码筛选"),
page: int = Query(1, ge=1, description="页码"),
page_size: int = Query(10, ge=1, le=200, description="每页数量"),
payload: dict = Depends(verify_access_token),
@@ -31,7 +32,13 @@ class EntryModuleController(BaseController):
"""查询入口模块列表。"""
if not await self.PermissionService.CheckPermission(int(payload["user_id"]), "entry_module:list:read"):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有入口模块列表权限", "data": None})
data = await self.EntryModuleService.ListModules(Name=name, Area=area, Page=page, PageSize=page_size)
data = await self.EntryModuleService.ListModules(
Name=name,
Area=area,
TenantCode=tenant_code,
Page=page,
PageSize=page_size,
)
return JSONResponse(status_code=200, content={"code": 0, "msg": "success", "data": data.model_dump()})
@self.router.get("/{ModuleId}")
@@ -26,6 +26,15 @@ class EvaluationPointController(BaseController):
"delete": "evaluation_point:delete:delete",
}
@staticmethod
def _tenant_context(payload: dict) -> dict[str, str | None]:
return {
"UserArea": payload.get("area"),
"UserRole": payload.get("user_role"),
"TenantCode": payload.get("tenant_code"),
"TenantName": payload.get("tenant_name"),
}
def __init__(self):
super().__init__(prefix="/v3/evaluation-points", tags=["评查点"])
self.PointService: IEvaluationPointService = EvaluationPointServiceImpl()
@@ -40,14 +49,22 @@ class EvaluationPointController(BaseController):
evaluation_point_groups_pid: int | None = Query(None, description="一级分组ID"),
evaluation_point_groups_id: int | None = Query(None, description="二级分组ID"),
document_attribute_type: str | None = Query(None, description="文档属性类型"),
area: str | None = Query(None, description="地区"),
area: str | None = Query(None, description="地区/兼容租户展示值"),
tenant_code: str | None = Query(None, description="租户编码"),
tenant_name: str | None = Query(None, description="租户名称(兼容筛选)"),
page: int = Query(1, ge=1, description="页码"),
page_size: int = Query(20, ge=1, le=500, description="分页大小"),
payload: dict = Depends(verify_access_token),
):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["list"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有评查点查看权限", "data": None})
tenant_context = self._tenant_context(payload)
data = await self.PointService.ListPoints(
int(payload["user_id"]),
tenant_context["UserArea"],
tenant_context["UserRole"],
tenant_context["TenantCode"],
tenant_context["TenantName"],
name,
code,
risk,
@@ -56,6 +73,8 @@ class EvaluationPointController(BaseController):
evaluation_point_groups_id,
document_attribute_type,
area,
tenant_code,
tenant_name,
page,
page_size,
)
@@ -72,28 +91,61 @@ class EvaluationPointController(BaseController):
async def GetEvaluationPoint(PointId: int, payload: dict = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["detail"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有评查点查看权限", "data": None})
data = await self.PointService.GetPoint(PointId)
tenant_context = self._tenant_context(payload)
data = await self.PointService.GetPoint(
int(payload["user_id"]),
tenant_context["UserArea"],
tenant_context["UserRole"],
tenant_context["TenantCode"],
tenant_context["TenantName"],
PointId,
)
return JSONResponse(status_code=200, content=data.model_dump())
@self.router.post("")
async def CreateEvaluationPoint(body: EvaluationPointCreateDTO, payload: dict = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["create"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有创建评查点权限", "data": None})
data = await self.PointService.CreatePoint(body)
tenant_context = self._tenant_context(payload)
data = await self.PointService.CreatePoint(
int(payload["user_id"]),
tenant_context["UserArea"],
tenant_context["UserRole"],
tenant_context["TenantCode"],
tenant_context["TenantName"],
body,
)
return JSONResponse(status_code=200, content=data.model_dump())
@self.router.put("/{PointId}")
async def UpdateEvaluationPoint(PointId: int, body: EvaluationPointUpdateDTO, payload: dict = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["update"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有更新评查点权限", "data": None})
data = await self.PointService.UpdatePoint(PointId, body)
tenant_context = self._tenant_context(payload)
data = await self.PointService.UpdatePoint(
int(payload["user_id"]),
tenant_context["UserArea"],
tenant_context["UserRole"],
tenant_context["TenantCode"],
tenant_context["TenantName"],
PointId,
body,
)
return JSONResponse(status_code=200, content=data.model_dump())
@self.router.delete("/{PointId}")
async def DeleteEvaluationPoint(PointId: int, payload: dict = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["delete"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有删除评查点权限", "data": None})
data = await self.PointService.DeletePoint(PointId)
tenant_context = self._tenant_context(payload)
data = await self.PointService.DeletePoint(
int(payload["user_id"]),
tenant_context["UserArea"],
tenant_context["UserRole"],
tenant_context["TenantCode"],
tenant_context["TenantName"],
PointId,
)
return JSONResponse(status_code=200, content=data.model_dump())
async def _check_permission(self, user_id: int, permission_keys: list[str]) -> bool:
@@ -41,7 +41,7 @@ class EvaluationPointGroupController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), ["evaluation_group:list:read", "rules:list:read"]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有评查点分组查看权限", "data": None})
data = await self.GroupService.ListGroups(name, code, is_enabled, pid, page, page_size)
data = await self.GroupService.ListGroups(name, code, is_enabled, pid, page, page_size, int(payload["user_id"]))
return JSONResponse(status_code=200, content=data.model_dump())
@self.router.get("/all")
@@ -52,7 +52,7 @@ class EvaluationPointGroupController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), ["evaluation_group:list:read", "rules:list:read"]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有评查点分组查看权限", "data": None})
data = await self.GroupService.ListAllGroups(include_disabled, with_rule_count)
data = await self.GroupService.ListAllGroups(include_disabled, with_rule_count, int(payload["user_id"]))
return JSONResponse(status_code=200, content=[item.model_dump() for item in data])
@self.router.get("/by-document-types")
@@ -65,14 +65,19 @@ class EvaluationPointGroupController(BaseController):
if not await self._check_permission(int(payload["user_id"]), ["evaluation_group:list:read", "rules:list:read"]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有评查点分组查看权限", "data": None})
document_type_id_list = [int(item.strip()) for item in document_type_ids.split(",") if item.strip().isdigit()]
data = await self.GroupService.ListGroupsByDocumentTypes(document_type_id_list, include_disabled, with_rule_count)
data = await self.GroupService.ListGroupsByDocumentTypes(
document_type_id_list,
include_disabled,
with_rule_count,
int(payload["user_id"]),
)
return JSONResponse(status_code=200, content=[item.model_dump() for item in data])
@self.router.post("")
async def CreateEvaluationPointGroup(body: EvaluationPointGroupCreateDTO, payload: dict = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), ["evaluation_group:create:write"]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有创建评查点分组权限", "data": None})
data = await self.GroupService.CreateGroup(body)
data = await self.GroupService.CreateGroup(body, int(payload["user_id"]))
return JSONResponse(status_code=200, content=data.model_dump())
@self.router.patch("/batch/status")
@@ -82,7 +87,7 @@ class EvaluationPointGroupController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), ["evaluation_group:batch:write", "evaluation_group:update:write"]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有批量更新评查点分组权限", "data": None})
data = await self.GroupService.BatchUpdateStatus(body)
data = await self.GroupService.BatchUpdateStatus(body, int(payload["user_id"]))
return JSONResponse(status_code=200, content=data.model_dump())
@self.router.delete("/batch")
@@ -92,7 +97,7 @@ class EvaluationPointGroupController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), ["evaluation_group:batch:write", "evaluation_group:delete:delete"]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有批量删除评查点分组权限", "data": None})
data = await self.GroupService.BatchDelete(body)
data = await self.GroupService.BatchDelete(body, int(payload["user_id"]))
return JSONResponse(status_code=200, content=data.model_dump())
@self.router.get("/{GroupId}")
@@ -103,21 +108,21 @@ class EvaluationPointGroupController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), ["evaluation_group:list:read", "rules:list:read"]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有评查点分组查看权限", "data": None})
data = await self.GroupService.GetGroup(GroupId, with_rule_count)
data = await self.GroupService.GetGroup(GroupId, with_rule_count, int(payload["user_id"]))
return JSONResponse(status_code=200, content=data.model_dump())
@self.router.put("/{GroupId}")
async def UpdateEvaluationPointGroup(GroupId: int, body: EvaluationPointGroupUpdateDTO, payload: dict = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), ["evaluation_group:batch:write", "evaluation_group:update:write"]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有更新评查点分组权限", "data": None})
data = await self.GroupService.UpdateGroup(GroupId, body)
data = await self.GroupService.UpdateGroup(GroupId, body, int(payload["user_id"]))
return JSONResponse(status_code=200, content=data.model_dump())
@self.router.delete("/{GroupId}")
async def DeleteEvaluationPointGroup(GroupId: int, payload: dict = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), ["evaluation_group:batch:write", "evaluation_group:delete:delete"]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有删除评查点分组权限", "data": None})
data = await self.GroupService.DeleteGroup(GroupId)
data = await self.GroupService.DeleteGroup(GroupId, int(payload["user_id"]))
return JSONResponse(status_code=200, content=data.model_dump())
@self.router.get("/{GroupId}/children")
@@ -130,42 +135,42 @@ class EvaluationPointGroupController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), ["evaluation_group:list:read", "rules:list:read"]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有评查点分组查看权限", "data": None})
data = await self.GroupService.GetChildren(GroupId, is_enabled, page, page_size)
data = await self.GroupService.GetChildren(GroupId, is_enabled, page, page_size, int(payload["user_id"]))
return JSONResponse(status_code=200, content=data.model_dump())
@self.router.put("/{GroupId}/rebind")
async def RebindEvaluationPointGroup(GroupId: int, body: EvaluationPointGroupRebindDTO, payload: dict = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), ["evaluation_group:update:write"]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有换绑评查点分组权限", "data": None})
data = await self.GroupService.RebindGroup(GroupId, body)
data = await self.GroupService.RebindGroup(GroupId, body, int(payload["user_id"]))
return JSONResponse(status_code=200, content=data.model_dump())
@self.router.post("/{GroupId}/bindings")
async def CreateEvaluationPointGroupBinding(GroupId: int, body: EvaluationPointGroupBindingCreateDTO, payload: dict = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), ["evaluation_group:update:write"]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有绑定规则集权限", "data": None})
data = await self.GroupService.CreateBinding(GroupId, body)
data = await self.GroupService.CreateBinding(GroupId, body, int(payload["user_id"]))
return JSONResponse(status_code=200, content=data.model_dump())
@self.router.put("/bindings/{BindingId}")
async def UpdateEvaluationPointGroupBinding(BindingId: int, body: EvaluationPointGroupBindingUpdateDTO, payload: dict = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), ["evaluation_group:update:write"]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有更新规则集绑定权限", "data": None})
data = await self.GroupService.UpdateBinding(BindingId, body)
data = await self.GroupService.UpdateBinding(BindingId, body, int(payload["user_id"]))
return JSONResponse(status_code=200, content=data.model_dump())
@self.router.delete("/bindings/{BindingId}")
async def DeleteEvaluationPointGroupBinding(BindingId: int, payload: dict = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), ["evaluation_group:update:write"]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有删除规则集绑定权限", "data": None})
await self.GroupService.DeleteBinding(BindingId)
await self.GroupService.DeleteBinding(BindingId, int(payload["user_id"]))
return JSONResponse(status_code=200, content={"success": True})
@self.router.get("/{GroupId}/rule-template")
async def GetEvaluationPointGroupRuleTemplate(GroupId: int, payload: dict = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), ["evaluation_group:list:read", "rules:list:read"]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看规则模板权限", "data": None})
data = await self.GroupService.GetRuleTemplate(GroupId)
data = await self.GroupService.GetRuleTemplate(GroupId, int(payload["user_id"]))
return JSONResponse(status_code=200, content=data.model_dump())
@self.router.post("/{GroupId}/rule-drafts")
@@ -177,7 +182,7 @@ class EvaluationPointGroupController(BaseController):
if not await self._check_permission(int(payload["user_id"]), ["evaluation_group:update:write", "rules:create:write"]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有保存规则草稿权限", "data": None})
effective_body = body.model_copy(update={"editor_user_id": body.editor_user_id or int(payload["user_id"])})
data = await self.GroupService.CreateRuleDraft(GroupId, effective_body)
data = await self.GroupService.CreateRuleDraft(GroupId, effective_body, int(payload["user_id"]))
return JSONResponse(status_code=200, content=data.model_dump())
async def _check_permission(self, user_id: int, permission_keys: list[str]) -> bool:
@@ -30,7 +30,8 @@ class GovdocController(BaseController):
async def UploadDocument(
file: UploadFile = File(...),
typeId: int | None = Form(default=None),
region: str = Form(default="default"),
region: str | None = Form(default=None, description="兼容保留字段:租户展示值/旧地区"),
tenant_code: str | None = Form(default=None, description="租户编码"),
autoRun: bool = Form(default=True),
speed: str = Form(default="normal"),
ruleVersionId: int | None = Form(default=None),
@@ -44,6 +45,7 @@ class GovdocController(BaseController):
file=file,
typeId=typeId,
region=region,
tenantCode=tenant_code,
autoRun=autoRun,
speed=speed,
ruleVersionId=ruleVersionId,
@@ -57,7 +59,8 @@ class GovdocController(BaseController):
pageSize: int = Query(default=20, ge=1, le=100),
keyword: str | None = Query(default=None),
fileExt: str | None = Query(default=None),
region: str | None = Query(default=None),
region: str | None = Query(default=None, description="兼容保留字段:租户展示值/旧地区"),
tenant_code: str | None = Query(default=None, description="租户编码"),
status: str | None = Query(default=None),
resultStatus: str | None = Query(default=None),
createdBy: int | None = Query(default=None),
@@ -75,6 +78,7 @@ class GovdocController(BaseController):
keyword=keyword,
fileExt=fileExt,
region=region,
tenantCode=tenant_code,
status=status,
resultStatus=resultStatus,
createdBy=createdBy,
@@ -151,7 +155,7 @@ class GovdocController(BaseController):
payload: dict[str, Any] = Depends(verify_access_token),
):
"""查询 run 状态、阶段、耗时、错误摘要。"""
result = await self.GovdocService.GetRunStatus(runId=runId)
result = await self.GovdocService.GetRunStatus(runId=runId, userId=int(payload["user_id"]))
return Result.success(data=result)
# ── 结果与报告 ────────────────────────────────────
@@ -162,7 +166,7 @@ class GovdocController(BaseController):
payload: dict[str, Any] = Depends(verify_access_token),
):
"""获取审查结果摘要:summary + checked rules + findings 统计 + entities 摘要。"""
result = await self.GovdocService.GetRunResult(runId=runId)
result = await self.GovdocService.GetRunResult(runId=runId, userId=int(payload["user_id"]))
return Result.success(data=result)
@self.router.get("/runs/{runId}/findings")
@@ -171,7 +175,7 @@ class GovdocController(BaseController):
payload: dict[str, Any] = Depends(verify_access_token),
):
"""获取段落级 findings 明细列表。"""
result = await self.GovdocService.GetRunFindings(runId=runId)
result = await self.GovdocService.GetRunFindings(runId=runId, userId=int(payload["user_id"]))
return Result.success(data=result)
@self.router.get("/runs/{runId}/entities")
@@ -180,7 +184,7 @@ class GovdocController(BaseController):
payload: dict[str, Any] = Depends(verify_access_token),
):
"""获取识别出的标题、文号、署名等实体。"""
result = await self.GovdocService.GetRunEntities(runId=runId)
result = await self.GovdocService.GetRunEntities(runId=runId, userId=int(payload["user_id"]))
return Result.success(data=result)
@self.router.get("/runs/{runId}/structure")
@@ -189,7 +193,7 @@ class GovdocController(BaseController):
payload: dict[str, Any] = Depends(verify_access_token),
):
"""获取文档结构统计(结构面板数据)。"""
result = await self.GovdocService.GetRunStructure(runId=runId)
result = await self.GovdocService.GetRunStructure(runId=runId, userId=int(payload["user_id"]))
return Result.success(data=result)
@self.router.get("/runs/{runId}/outline")
@@ -198,7 +202,7 @@ class GovdocController(BaseController):
payload: dict[str, Any] = Depends(verify_access_token),
):
"""获取文档大纲树(大纲面板数据)。"""
result = await self.GovdocService.GetRunOutline(runId=runId)
result = await self.GovdocService.GetRunOutline(runId=runId, userId=int(payload["user_id"]))
return Result.success(data=result)
@self.router.get("/runs/{runId}/paragraphs")
@@ -207,7 +211,7 @@ class GovdocController(BaseController):
payload: dict[str, Any] = Depends(verify_access_token),
):
"""获取前端文档联动视图所需的段落 HTML。"""
result = await self.GovdocService.GetRunParagraphs(runId=runId)
result = await self.GovdocService.GetRunParagraphs(runId=runId, userId=int(payload["user_id"]))
return Result.success(data=result)
@self.router.get("/runs/{runId}/report/html")
@@ -216,7 +220,7 @@ class GovdocController(BaseController):
payload: dict[str, Any] = Depends(verify_access_token),
):
"""获取 HTML 报告内容或下载地址。"""
result = await self.GovdocService.GetReportHtml(runId=runId)
result = await self.GovdocService.GetReportHtml(runId=runId, userId=int(payload["user_id"]))
return Result.success(data=result)
@self.router.get("/runs/{runId}/report/docx")
@@ -225,7 +229,7 @@ class GovdocController(BaseController):
payload: dict[str, Any] = Depends(verify_access_token),
):
"""获取批注 DOCX 下载地址。"""
result = await self.GovdocService.GetReportDocx(runId=runId)
result = await self.GovdocService.GetReportDocx(runId=runId, userId=int(payload["user_id"]))
return Result.success(data=result)
@self.router.get("/documents/{documentId}/original")
@@ -234,7 +238,7 @@ class GovdocController(BaseController):
payload: dict[str, Any] = Depends(verify_access_token),
):
"""获取原始上传文档下载地址。"""
result = await self.GovdocService.DownloadOriginal(documentId=documentId)
result = await self.GovdocService.DownloadOriginal(documentId=documentId, userId=int(payload["user_id"]))
return Result.success(data=result)
# ── 规则 ──────────────────────────────────────────
@@ -0,0 +1,62 @@
"""页级图片质量控制器。"""
from typing import Any
from fastapi import Depends, Form
from fastapi_common.fastapi_common_security.security import verify_access_token
from fastapi_common.fastapi_common_web.controller import BaseController
from fastapi_common.fastapi_common_web.domain.responses import Result
from fastapi_modules.fastapi_leaudit.domian.vo.pageQualityVo import (
PageQualityDetailVO,
PageQualityRecheckVO,
PageQualitySummaryVO,
)
from fastapi_modules.fastapi_leaudit.services.impl.pageQualityServiceImpl import PageQualityServiceImpl
from fastapi_modules.fastapi_leaudit.services.pageQualityService import IPageQualityService
class PageQualityController(BaseController):
"""页级图片质量控制器。"""
def __init__(self):
super().__init__(prefix="", tags=["页级图片质量"])
self.PageQualityService: IPageQualityService = PageQualityServiceImpl()
@self.router.get("/documents/{DocumentId}/page-quality/summary", response_model=Result[PageQualitySummaryVO])
async def GetDocumentPageQualitySummary(
DocumentId: int,
payload: dict[str, Any] = Depends(verify_access_token),
):
"""获取文档页级模糊检测摘要。"""
data = await self.PageQualityService.GetDocumentSummary(
CurrentUserId=int(payload["user_id"]),
DocumentId=DocumentId,
)
return Result.success(data=data)
@self.router.get("/documents/{DocumentId}/page-quality", response_model=Result[PageQualityDetailVO])
async def GetDocumentPageQualityDetail(
DocumentId: int,
payload: dict[str, Any] = Depends(verify_access_token),
):
"""获取文档页级模糊检测详情。"""
data = await self.PageQualityService.GetDocumentDetail(
CurrentUserId=int(payload["user_id"]),
DocumentId=DocumentId,
)
return Result.success(data=data)
@self.router.post("/documents/{DocumentId}/page-quality/recheck", response_model=Result[PageQualityRecheckVO])
async def RecheckDocumentPageQuality(
DocumentId: int,
speed: str = Form("normal", description="执行速度档位:urgent/normal"),
payload: dict[str, Any] = Depends(verify_access_token),
):
"""手工重跑页级模糊检测。"""
data = await self.PageQualityService.RecheckDocument(
CurrentUserId=int(payload["user_id"]),
DocumentId=DocumentId,
Speed=speed,
)
return Result.success(data=data, message="页级模糊检测任务已投递")
@@ -64,6 +64,15 @@ class RagChatController(BaseController):
"dataset_delete": "rag:dataset:delete",
}
@staticmethod
def _tenant_context(payload: dict[str, Any]) -> dict[str, str | None]:
return {
"UserArea": payload.get("area"),
"UserRole": payload.get("user_role"),
"TenantCode": payload.get("tenant_code"),
"TenantName": payload.get("tenant_name"),
}
def __init__(self):
super().__init__(prefix="/v3/rag", tags=["RAG 聊天"])
self.RagChatService: IRagChatService = RagChatServiceImpl()
@@ -74,10 +83,10 @@ class RagChatController(BaseController):
async def GetApps(payload: dict[str, Any] = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["app_read"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看聊天应用权限", "data": None})
tenant_context = self._tenant_context(payload)
data = await self.RagChatService.GetApps(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
)
return Result.success(data=data)
@@ -85,10 +94,10 @@ class RagChatController(BaseController):
async def GetDefaultApp(payload: dict[str, Any] = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["app_read"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看默认聊天应用权限", "data": None})
tenant_context = self._tenant_context(payload)
data = await self.RagChatService.GetDefaultApp(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
)
return Result.success(data=data)
@@ -96,16 +105,17 @@ class RagChatController(BaseController):
async def GetMyDatasets(payload: dict[str, Any] = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["dataset_read"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看知识库权限", "data": None})
tenant_context = self._tenant_context(payload)
data = await self.RagDatasetService.GetMyDatasets(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
)
return Result.success(data=data)
@self.router.get("/datasets/admin", response_model=Result[RagDatasetPageVO])
async def GetAdminDatasets(
area: str | None = Query(None),
tenant_code: str | None = Query(None, description="租户编码,支持逗号分隔"),
onlyEnabled: bool | None = Query(None),
page: int = Query(1, ge=1),
pageSize: int = Query(20, ge=1, le=200),
@@ -113,11 +123,12 @@ class RagChatController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["dataset_manage"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有管理知识库权限", "data": None})
tenant_context = self._tenant_context(payload)
data = await self.RagDatasetService.GetAdminDatasets(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
Area=area,
TenantFilterCode=tenant_code,
OnlyEnabled=onlyEnabled,
Page=page,
PageSize=pageSize,
@@ -128,10 +139,10 @@ class RagChatController(BaseController):
async def CreateAdminDataset(Body: dict[str, Any], payload: dict[str, Any] = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["dataset_create"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有创建知识库权限", "data": None})
tenant_context = self._tenant_context(payload)
data = await self.RagDatasetService.CreateAdminDataset(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
Body=Body,
)
return Result.success(data=data)
@@ -140,10 +151,10 @@ class RagChatController(BaseController):
async def UpdateAdminDataset(DatasetId: int, Body: dict[str, Any], payload: dict[str, Any] = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["dataset_update"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有更新知识库权限", "data": None})
tenant_context = self._tenant_context(payload)
data = await self.RagDatasetService.UpdateAdminDataset(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
DatasetId=DatasetId,
Body=Body,
)
@@ -153,10 +164,10 @@ class RagChatController(BaseController):
async def DeleteAdminDataset(DatasetId: int, payload: dict[str, Any] = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["dataset_delete"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有删除知识库权限", "data": None})
tenant_context = self._tenant_context(payload)
data = await self.RagDatasetService.DeleteAdminDataset(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
DatasetId=DatasetId,
)
return Result.success(data=data)
@@ -165,10 +176,10 @@ class RagChatController(BaseController):
async def GetDatasetDetail(DatasetId: int, payload: dict[str, Any] = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["dataset_read"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看知识库权限", "data": None})
tenant_context = self._tenant_context(payload)
data = await self.RagDatasetService.GetDatasetDetail(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
DatasetId=DatasetId,
)
return Result.success(data=data)
@@ -177,10 +188,10 @@ class RagChatController(BaseController):
async def UpdateDataset(DatasetId: int, Body: RagDatasetUpdateDTO, payload: dict[str, Any] = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["dataset_update"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有修改知识库权限", "data": None})
tenant_context = self._tenant_context(payload)
data = await self.RagDatasetService.UpdateDataset(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
DatasetId=DatasetId,
Body=Body,
)
@@ -196,10 +207,10 @@ class RagChatController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["dataset_read"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看知识库文档权限", "data": None})
tenant_context = self._tenant_context(payload)
data = await self.RagDatasetService.GetDatasetDocuments(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
DatasetId=DatasetId,
Page=page,
Limit=limit,
@@ -215,10 +226,10 @@ class RagChatController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["dataset_read"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看知识库文档权限", "data": None})
tenant_context = self._tenant_context(payload)
data = await self.RagDatasetService.GetDatasetDocumentDetail(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
DatasetId=DatasetId,
DocumentId=DocumentId,
)
@@ -235,10 +246,10 @@ class RagChatController(BaseController):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有上传知识库文档权限", "data": None})
process_config = json.loads(data) if data else None
file_bytes = await file.read()
tenant_context = self._tenant_context(payload)
result = await self.RagDatasetService.UploadDatasetDocument(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
DatasetId=DatasetId,
FileName=file.filename or "document",
ContentType=file.content_type,
@@ -259,10 +270,10 @@ class RagChatController(BaseController):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有重处理知识库文档权限", "data": None})
process_config = json.loads(data) if data else None
file_bytes = await file.read()
tenant_context = self._tenant_context(payload)
result = await self.RagDatasetService.UpdateDatasetDocumentByFile(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
DatasetId=DatasetId,
DocumentId=DocumentId,
FileName=file.filename or "document",
@@ -280,10 +291,10 @@ class RagChatController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["dataset_read"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看知识库处理进度权限", "data": None})
tenant_context = self._tenant_context(payload)
result = await self.RagDatasetService.GetDatasetDocumentIndexingStatus(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
DatasetId=DatasetId,
DocumentId=DocumentId,
)
@@ -302,10 +313,10 @@ class RagChatController(BaseController):
if Action not in {"enable", "disable"}:
return JSONResponse(status_code=400, content={"code": 400, "msg": "当前仅支持启用和禁用", "data": None})
document_ids = Body.get("document_ids") or []
tenant_context = self._tenant_context(payload)
result = await self.RagDatasetService.BatchUpdateDatasetDocumentStatus(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
DatasetId=DatasetId,
DocumentIds=[int(item) for item in document_ids],
Enabled=enabled,
@@ -323,10 +334,10 @@ class RagChatController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["dataset_read"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看知识库分段权限", "data": None})
tenant_context = self._tenant_context(payload)
result = await self.RagDatasetService.GetDatasetDocumentSegments(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
DatasetId=DatasetId,
DocumentId=DocumentId,
Page=page,
@@ -343,10 +354,10 @@ class RagChatController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["dataset_delete"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有删除知识库文档权限", "data": None})
tenant_context = self._tenant_context(payload)
result = await self.RagDatasetService.DeleteDatasetDocument(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
DatasetId=DatasetId,
DocumentId=DocumentId,
)
@@ -360,10 +371,10 @@ class RagChatController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["dataset_delete"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有删除知识库文档权限", "data": None})
tenant_context = self._tenant_context(payload)
result = await self.RagDatasetService.BatchDeleteDatasetDocuments(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
DatasetId=DatasetId,
DocumentIds=Body.document_ids,
)
@@ -377,10 +388,10 @@ class RagChatController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["dataset_read"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有检索知识库权限", "data": None})
tenant_context = self._tenant_context(payload)
result = await self.RagDatasetService.RetrieveDataset(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
DatasetId=DatasetId,
Query=str(Body.get("query") or ""),
RetrievalModel=Body.get("retrieval_model") if isinstance(Body.get("retrieval_model"), dict) else None,
@@ -396,10 +407,10 @@ class RagChatController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["dataset_read"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看知识库分段权限", "data": None})
tenant_context = self._tenant_context(payload)
result = await self.RagDatasetService.GetDatasetDocumentSegmentDetail(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
DatasetId=DatasetId,
DocumentId=DocumentId,
SegmentId=SegmentId,
@@ -416,10 +427,10 @@ class RagChatController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["dataset_update"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有修改知识库分段权限", "data": None})
tenant_context = self._tenant_context(payload)
result = await self.RagDatasetService.UpdateDatasetDocumentSegment(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
DatasetId=DatasetId,
DocumentId=DocumentId,
SegmentId=SegmentId,
@@ -436,10 +447,10 @@ class RagChatController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["dataset_delete"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有删除知识库分段权限", "data": None})
tenant_context = self._tenant_context(payload)
result = await self.RagDatasetService.DeleteDatasetDocumentSegment(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
DatasetId=DatasetId,
DocumentId=DocumentId,
SegmentId=SegmentId,
@@ -453,10 +464,10 @@ class RagChatController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["chat_use"], self._PERMISSIONS["app_read"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看聊天配置权限", "data": None})
tenant_context = self._tenant_context(payload)
data = await self.RagChatService.GetAppParameters(
CurrentUserId=int(payload["user_id"]),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
AppId=appId,
)
return Result.success(data=data)
@@ -465,11 +476,11 @@ class RagChatController(BaseController):
async def SendMessage(Body: RagChatSendMessageDTO, payload: dict[str, Any] = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["chat_use"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有使用 RAG 对话权限", "data": None})
tenant_context = self._tenant_context(payload)
stream = self.RagChatService.SendMessage(
CurrentUserId=int(payload["user_id"]),
UserName=payload.get("username") or str(payload.get("user_id")),
UserArea=payload.get("area"),
UserRole=payload.get("user_role"),
**tenant_context,
Query=Body.query,
ConversationId=Body.conversationId,
AppId=Body.appId,
@@ -488,7 +499,13 @@ class RagChatController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["chat_use"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有停止 RAG 对话权限", "data": None})
data = await self.RagChatService.StopMessage(int(payload["user_id"]), MessageId, Body)
tenant_context = self._tenant_context(payload)
data = await self.RagChatService.StopMessage(
CurrentUserId=int(payload["user_id"]),
**tenant_context,
MessageId=MessageId,
Body=Body,
)
return Result.success(data=data)
@self.router.get("/chat/conversations", response_model=Result[RagConversationPageVO])
@@ -500,7 +517,14 @@ class RagChatController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["conversation_read"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看聊天会话权限", "data": None})
data = await self.RagChatService.GetConversations(int(payload["user_id"]), appId, page, pageSize)
tenant_context = self._tenant_context(payload)
data = await self.RagChatService.GetConversations(
CurrentUserId=int(payload["user_id"]),
**tenant_context,
AppId=appId,
Page=page,
PageSize=pageSize,
)
return Result.success(data=data)
@self.router.get("/chat/conversations/{ConversationId}/messages", response_model=Result[RagMessagePageVO])
@@ -512,7 +536,14 @@ class RagChatController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["conversation_read"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看聊天消息权限", "data": None})
data = await self.RagChatService.GetConversationMessages(int(payload["user_id"]), ConversationId, page, pageSize)
tenant_context = self._tenant_context(payload)
data = await self.RagChatService.GetConversationMessages(
CurrentUserId=int(payload["user_id"]),
**tenant_context,
ConversationId=ConversationId,
Page=page,
PageSize=pageSize,
)
return Result.success(data=data)
@self.router.patch("/chat/conversations/{ConversationId}", response_model=Result[RagConversationRenameVO])
@@ -523,14 +554,25 @@ class RagChatController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["conversation_update"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有修改聊天会话权限", "data": None})
data = await self.RagChatService.RenameConversation(int(payload["user_id"]), ConversationId, Body)
tenant_context = self._tenant_context(payload)
data = await self.RagChatService.RenameConversation(
CurrentUserId=int(payload["user_id"]),
**tenant_context,
ConversationId=ConversationId,
Body=Body,
)
return Result.success(data=data)
@self.router.delete("/chat/conversations/{ConversationId}", response_model=Result[RagOperationResultVO])
async def DeleteConversation(ConversationId: str, payload: dict[str, Any] = Depends(verify_access_token)):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["conversation_delete"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有删除聊天会话权限", "data": None})
data = await self.RagChatService.DeleteConversation(int(payload["user_id"]), ConversationId)
tenant_context = self._tenant_context(payload)
data = await self.RagChatService.DeleteConversation(
CurrentUserId=int(payload["user_id"]),
**tenant_context,
ConversationId=ConversationId,
)
return Result.success(data=data)
@self.router.post("/chat/messages/{MessageId}/feedback", response_model=Result[RagOperationResultVO])
@@ -541,7 +583,13 @@ class RagChatController(BaseController):
):
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["message_feedback"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有反馈聊天消息权限", "data": None})
data = await self.RagChatService.UpdateFeedback(int(payload["user_id"]), MessageId, Body)
tenant_context = self._tenant_context(payload)
data = await self.RagChatService.UpdateFeedback(
CurrentUserId=int(payload["user_id"]),
**tenant_context,
MessageId=MessageId,
Body=Body,
)
return Result.success(data=data)
async def _check_permission(self, user_id: int, permission_keys: list[str]) -> bool:
@@ -8,7 +8,7 @@ from fastapi.responses import JSONResponse
from fastapi_common.fastapi_common_security.security import verify_access_token
from fastapi_common.fastapi_common_web.controller import BaseController
from fastapi_modules.fastapi_leaudit.domian.Dto.rbacAdminDto import RoleAccessSaveDTO, RoleCreateDTO, RolePermissionsBatchDTO, RoleRoutesUpdateDTO, RoleUpdateDTO, UserRolesAssignDTO
from fastapi_modules.fastapi_leaudit.domian.Dto.rbacAdminDto import RoleAccessSaveDTO, RoleCreateDTO, RolePermissionsBatchDTO, RoleRoutesUpdateDTO, RoleUpdateDTO, UserRolesAssignDTO, UserTenantUpdateDTO
from fastapi_modules.fastapi_leaudit.services.impl.rbacAdminServiceImpl import RbacAdminServiceImpl
from fastapi_modules.fastapi_leaudit.services.rbacAdminService import IRbacAdminService
@@ -56,11 +56,12 @@ class RbacAdminController(BaseController):
payload: dict[str, Any] = Depends(verify_access_token),
page: int = Query(1, ge=1),
page_size: int = Query(50, ge=1, le=200),
area: str | None = Query(None),
area: str | None = Query(None, description="兼容租户展示值/旧地区"),
tenant_code: str | None = Query(None, description="租户编码"),
nick_name: str | None = Query(None),
):
"""查询用户列表。"""
data = await self.RbacAdminService.ListUsers(int(payload["user_id"]), page, page_size, area, nick_name)
data = await self.RbacAdminService.ListUsers(int(payload["user_id"]), page, page_size, area, tenant_code, nick_name)
return JSONResponse(status_code=200, content={"code": 200, "message": "success", "data": data.model_dump()})
@self.router.get("/admin/users/organizations/tree")
@@ -79,11 +80,12 @@ class RbacAdminController(BaseController):
payload: dict[str, Any] = Depends(verify_access_token),
page: int = Query(1, ge=1),
page_size: int = Query(50, ge=1, le=200),
area: str | None = Query(None),
area: str | None = Query(None, description="兼容租户展示值/旧地区"),
tenant_code: str | None = Query(None, description="租户编码"),
username: str | None = Query(None),
):
"""查询指定角色下的用户列表。"""
data = await self.RbacAdminService.ListRoleUsers(int(payload["user_id"]), RoleId, page, page_size, area, username)
data = await self.RbacAdminService.ListRoleUsers(int(payload["user_id"]), RoleId, page, page_size, area, tenant_code, username)
return JSONResponse(status_code=200, content={"code": 200, "message": "success", "data": data.model_dump()})
@self.router.post("/v3/rbac/users/{UserId}/roles")
@@ -92,6 +94,12 @@ class RbacAdminController(BaseController):
data = await self.RbacAdminService.AssignUserRoles(int(payload["user_id"]), UserId, Body.role_ids)
return JSONResponse(status_code=200, content={"code": 200, "message": "角色分配成功", "data": data.model_dump()})
@self.router.put("/v3/rbac/users/{UserId}/tenant")
async def UpdateUserTenant(UserId: int, Body: UserTenantUpdateDTO, payload: dict[str, Any] = Depends(verify_access_token)):
"""更新用户租户。"""
data = await self.RbacAdminService.UpdateUserTenant(int(payload["user_id"]), UserId, Body)
return JSONResponse(status_code=200, content={"code": 200, "message": "用户租户更新成功", "data": data.model_dump()})
@self.router.delete("/v3/rbac/users/{UserId}/roles/{RoleId}")
async def RevokeUserRole(UserId: int, RoleId: int, payload: dict[str, Any] = Depends(verify_access_token)):
"""移除用户角色。"""
@@ -30,7 +30,12 @@ class RuleConfigController(BaseController):
"""列出规则配置页 pack。"""
if not await self._check_permission(int(payload["user_id"])):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有规则配置查看权限", "data": None})
data = await (self.RuleConfigService.ListPackSummaries() if summaryOnly else self.RuleConfigService.ListPacks())
current_user_id = int(payload["user_id"])
data = await (
self.RuleConfigService.ListPackSummaries(CurrentUserId=current_user_id)
if summaryOnly
else self.RuleConfigService.ListPacks(CurrentUserId=current_user_id)
)
return JSONResponse(status_code=200, content={"code": 200, "message": "success", "data": [item.model_dump() for item in data]})
@self.router.get("/{PackId}")
@@ -38,7 +43,7 @@ class RuleConfigController(BaseController):
"""获取单个规则配置 pack。"""
if not await self._check_permission(int(payload["user_id"])):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有规则配置查看权限", "data": None})
data = await self.RuleConfigService.GetPack(PackId)
data = await self.RuleConfigService.GetPack(PackId, CurrentUserId=int(payload["user_id"]))
return JSONResponse(status_code=200, content={"code": 200, "message": "success", "data": data.model_dump()})
async def _check_permission(self, user_id: int) -> bool:
@@ -1,5 +1,11 @@
"""规则管理控制器。"""
from typing import Any
from fastapi import Depends
from fastapi.responses import JSONResponse
from fastapi_common.fastapi_common_security.security import verify_access_token
from fastapi_common.fastapi_common_web.controller import BaseController
from fastapi_common.fastapi_common_web.domain.responses import Result
@@ -18,7 +24,9 @@ from fastapi_modules.fastapi_leaudit.domian.vo.ruleVo import (
RuleVersionVO,
)
from fastapi_modules.fastapi_leaudit.services import IRuleService
from fastapi_modules.fastapi_leaudit.services.impl.permissionServiceImpl import PermissionServiceImpl
from fastapi_modules.fastapi_leaudit.services.impl.ruleServiceImpl import GetRuleServiceSingleton
from fastapi_modules.fastapi_leaudit.services.permissionService import IPermissionService
class RuleController(BaseController):
@@ -27,28 +35,50 @@ class RuleController(BaseController):
def __init__(self):
super().__init__(prefix="/rule-sets", tags=["规则管理"])
self.RuleService: IRuleService = GetRuleServiceSingleton()
self.PermissionService: IPermissionService = PermissionServiceImpl()
self._PERMISSIONS = {
"list": "rules:list:read",
"version_list": "rules:version_list:read",
"content": "rules:content:read",
"validate": "rules:validate:execute",
"create": "rules:version_create:write",
"publish": "rules:publish:write",
"rollback": "rules:rollback:write",
"binding_read": "rules:binding_list:read",
"binding_create": "rules:binding_create:write",
"binding_update": "rules:binding_update:write",
"binding_delete": "rules:binding_delete:delete",
}
@self.router.get("", response_model=Result[list[RuleSetVO]])
async def ListRuleSets():
async def ListRuleSets(payload: dict[str, Any] = Depends(verify_access_token)):
"""列出规则集。"""
Data = await self.RuleService.ListSets()
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["list"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有规则集查看权限", "data": None})
Data = await self.RuleService.ListSets(CurrentUserId=int(payload["user_id"]))
return Result.success(data=Data)
@self.router.get("/{RuleType}/versions", response_model=Result[list[RuleVersionVO]])
async def GetVersions(RuleType: str):
async def GetVersions(RuleType: str, payload: dict[str, Any] = Depends(verify_access_token)):
"""列出规则集的所有版本。"""
Data = await self.RuleService.GetVersions(RuleType=RuleType)
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["version_list"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有规则版本查看权限", "data": None})
Data = await self.RuleService.GetVersions(RuleType=RuleType, CurrentUserId=int(payload["user_id"]))
return Result.success(data=Data)
@self.router.get("/versions/{VersionId}/content", response_model=Result[RuleContentVO])
async def GetVersionContent(VersionId: int):
async def GetVersionContent(VersionId: int, payload: dict[str, Any] = Depends(verify_access_token)):
"""获取规则版本正文。"""
Data = await self.RuleService.GetContent(VersionId=VersionId)
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["content"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有规则正文查看权限", "data": None})
Data = await self.RuleService.GetContent(VersionId=VersionId, CurrentUserId=int(payload["user_id"]))
return Result.success(data=Data)
@self.router.post("/{RuleType}/validate", response_model=Result[RuleValidationVO])
async def ValidateRuleYaml(RuleType: str, body: RuleValidateDTO):
async def ValidateRuleYaml(RuleType: str, body: RuleValidateDTO, payload: dict[str, Any] = Depends(verify_access_token)):
"""校验规则 YAML。"""
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["validate"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有规则校验权限", "data": None})
Data = await self.RuleService.Validate(
RuleType=RuleType,
YamlText=body.yamlText,
@@ -56,47 +86,68 @@ class RuleController(BaseController):
return Result.success(data=Data)
@self.router.post("/{RuleType}/versions", response_model=Result[RuleVersionVO])
async def CreateRuleVersion(RuleType: str, body: RuleVersionCreateDTO):
async def CreateRuleVersion(RuleType: str, body: RuleVersionCreateDTO, payload: dict[str, Any] = Depends(verify_access_token)):
"""创建规则版本。"""
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["create"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有创建规则版本权限", "data": None})
Data = await self.RuleService.CreateVersion(
RuleType=RuleType,
YamlText=body.yamlText,
ChangeNote=body.changeNote,
EditorUserId=body.editorUserId,
EditorUserId=int(payload["user_id"]),
CurrentUserId=int(payload["user_id"]),
)
return Result.success(data=Data)
@self.router.post("/{RuleType}/publish", response_model=Result[RuleVersionVO])
async def PublishRuleVersion(RuleType: str, body: RulePublishDTO):
async def PublishRuleVersion(RuleType: str, body: RulePublishDTO, payload: dict[str, Any] = Depends(verify_access_token)):
"""发布规则版本。"""
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["publish"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有发布规则权限", "data": None})
Data = await self.RuleService.Publish(
RuleType=RuleType,
VersionId=body.versionId,
OperatorUserId=body.operatorUserId,
OperatorUserId=int(payload["user_id"]),
CurrentUserId=int(payload["user_id"]),
)
return Result.success(data=Data)
@self.router.post("/{RuleType}/rollback", response_model=Result[RuleVersionVO])
async def RollbackRuleVersion(RuleType: str, body: RulePublishDTO):
async def RollbackRuleVersion(RuleType: str, body: RulePublishDTO, payload: dict[str, Any] = Depends(verify_access_token)):
"""回滚到指定规则版本。"""
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["rollback"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有回滚规则权限", "data": None})
Data = await self.RuleService.Rollback(
RuleType=RuleType,
VersionId=body.versionId,
OperatorUserId=body.operatorUserId,
OperatorUserId=int(payload["user_id"]),
CurrentUserId=int(payload["user_id"]),
)
return Result.success(data=Data)
# ── 规则类型绑定 ──────────────────────────────────────────
@self.router.get("/bindings", response_model=Result[list[RuleBindingVO]])
async def ListBindings(ruleType: str | None = None, region: str | None = None):
"""列出规则类型绑定。可按规则类型/地区过滤。"""
Data = await self.RuleService.ListBindings(RuleType=ruleType, Region=region)
async def ListBindings(
ruleType: str | None = None,
region: str | None = None,
payload: dict[str, Any] = Depends(verify_access_token),
):
"""列出规则类型绑定。当前主要按规则类型过滤,region 仅兼容保留。"""
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["binding_read"], self._PERMISSIONS["list"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有规则绑定查看权限", "data": None})
Data = await self.RuleService.ListBindings(
RuleType=ruleType,
Region=region,
CurrentUserId=int(payload["user_id"]),
)
return Result.success(data=Data)
@self.router.post("/{RuleType}/bindings", response_model=Result[RuleBindingVO])
async def CreateBinding(RuleType: str, body: RuleBindingCreateDTO):
async def CreateBinding(RuleType: str, body: RuleBindingCreateDTO, payload: dict[str, Any] = Depends(verify_access_token)):
"""创建规则类型绑定。"""
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["binding_create"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有创建规则绑定权限", "data": None})
Data = await self.RuleService.CreateBinding(
DocTypeId=body.docTypeId,
RuleSetId=body.ruleSetId,
@@ -105,23 +156,32 @@ class RuleController(BaseController):
Priority=body.priority,
DocTypeCode=body.docTypeCode,
Note=body.note,
CurrentUserId=int(payload["user_id"]),
)
return Result.success(data=Data)
@self.router.put("/bindings/{BindingId}", response_model=Result[RuleBindingVO])
async def UpdateBinding(BindingId: int, body: RuleBindingUpdateDTO):
async def UpdateBinding(BindingId: int, body: RuleBindingUpdateDTO, payload: dict[str, Any] = Depends(verify_access_token)):
"""更新规则类型绑定。"""
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["binding_update"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有更新规则绑定权限", "data": None})
Data = await self.RuleService.UpdateBinding(
BindingId=BindingId,
IsActive=body.isActive,
Priority=body.priority,
BindingMode=body.bindingMode,
Note=body.note,
CurrentUserId=int(payload["user_id"]),
)
return Result.success(data=Data)
@self.router.delete("/bindings/{BindingId}", response_model=Result[None])
async def DeleteBinding(BindingId: int):
async def DeleteBinding(BindingId: int, payload: dict[str, Any] = Depends(verify_access_token)):
"""删除规则类型绑定。"""
await self.RuleService.DeleteBinding(BindingId=BindingId)
if not await self._check_permission(int(payload["user_id"]), [self._PERMISSIONS["binding_delete"]]):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有删除规则绑定权限", "data": None})
await self.RuleService.DeleteBinding(BindingId=BindingId, CurrentUserId=int(payload["user_id"]))
return Result.success()
async def _check_permission(self, user_id: int, permission_keys: list[str]) -> bool:
return await self.PermissionService.HasAnyPermission(UserId=user_id, PermissionKeys=permission_keys)
@@ -0,0 +1,80 @@
"""租户主数据控制器。"""
from __future__ import annotations
from typing import Any
from fastapi import Depends, Query
from fastapi.responses import JSONResponse
from fastapi_common.fastapi_common_security.security import verify_access_token
from fastapi_common.fastapi_common_web.controller import BaseController
from fastapi_modules.fastapi_leaudit.domian.Dto.tenantDto import (
TenantCreateDTO,
TenantStatusUpdateDTO,
TenantUpdateDTO,
)
from fastapi_modules.fastapi_leaudit.services.impl.permissionServiceImpl import PermissionServiceImpl
from fastapi_modules.fastapi_leaudit.services.impl.tenantServiceImpl import TenantServiceImpl
from fastapi_modules.fastapi_leaudit.services.permissionService import IPermissionService
from fastapi_modules.fastapi_leaudit.services.tenantService import ITenantService
class TenantController(BaseController):
"""租户主数据控制器。"""
def __init__(self):
super().__init__(prefix="/v3/tenants", tags=["租户主数据"])
self.TenantService: ITenantService = TenantServiceImpl()
self.PermissionService: IPermissionService = PermissionServiceImpl()
@self.router.get("")
async def GetTenants(
include_disabled: bool = Query(False, description="是否包含禁用租户"),
payload: dict[str, Any] = Depends(verify_access_token),
):
if not await self.PermissionService.CheckPermission(int(payload["user_id"]), "rbac:tenants:read"):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看租户主数据权限", "data": None})
data = await self.TenantService.ListTenants(IncludeDisabled=include_disabled)
return JSONResponse(status_code=200, content={"code": 0, "msg": "success", "data": {"items": data, "total": len(data)}})
@self.router.get("/options")
async def GetTenantOptions(
feature_key: str | None = Query(None, description="按功能键过滤租户"),
payload: dict[str, Any] = Depends(verify_access_token),
):
data = await self.TenantService.ListTenantOptions(FeatureKey=feature_key)
return JSONResponse(status_code=200, content={"code": 0, "msg": "success", "data": {"items": data, "total": len(data)}})
@self.router.get("/{TenantCode}")
async def GetTenant(TenantCode: str, payload: dict[str, Any] = Depends(verify_access_token)):
if not await self.PermissionService.CheckPermission(int(payload["user_id"]), "rbac:tenants:read"):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有查看租户详情权限", "data": None})
data = await self.TenantService.GetTenant(TenantCode)
if not data:
return JSONResponse(status_code=404, content={"code": 404, "msg": "租户不存在", "data": None})
features = await self.TenantService.GetTenantFeatures(TenantCode)
aliases = await self.TenantService.GetTenantAliases(TenantCode)
return JSONResponse(status_code=200, content={"code": 0, "msg": "success", "data": {**data, "feature_keys": features, "alias_values": aliases}})
@self.router.post("")
async def CreateTenant(Body: TenantCreateDTO, payload: dict[str, Any] = Depends(verify_access_token)):
if not await self.PermissionService.CheckPermission(int(payload["user_id"]), "rbac:tenants:create"):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有创建租户权限", "data": None})
data = await self.TenantService.CreateTenant(int(payload["user_id"]), Body)
return JSONResponse(status_code=200, content={"code": 0, "msg": "success", "data": data})
@self.router.put("/{TenantCode}")
async def UpdateTenant(TenantCode: str, Body: TenantUpdateDTO, payload: dict[str, Any] = Depends(verify_access_token)):
if not await self.PermissionService.CheckPermission(int(payload["user_id"]), "rbac:tenants:update"):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有更新租户权限", "data": None})
data = await self.TenantService.UpdateTenant(int(payload["user_id"]), TenantCode, Body)
return JSONResponse(status_code=200, content={"code": 0, "msg": "success", "data": data})
@self.router.patch("/{TenantCode}/status")
async def UpdateTenantStatus(TenantCode: str, Body: TenantStatusUpdateDTO, payload: dict[str, Any] = Depends(verify_access_token)):
if not await self.PermissionService.CheckPermission(int(payload["user_id"]), "rbac:tenants:status"):
return JSONResponse(status_code=403, content={"code": 403, "msg": "当前用户没有更新租户状态权限", "data": None})
data = await self.TenantService.UpdateTenantStatus(int(payload["user_id"]), TenantCode, Body)
return JSONResponse(status_code=200, content={"code": 0, "msg": "success", "data": data})