feat: 增强 agent_assets 功能,支持更多资产操作
This commit is contained in:
@@ -2,19 +2,32 @@ from __future__ import annotations
|
||||
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends, Header, HTTPException, Query, status
|
||||
from fastapi import APIRouter, Body, Depends, Header, HTTPException, Query, status
|
||||
from fastapi.responses import FileResponse
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_db
|
||||
from app.api.deps import (
|
||||
CurrentUserContext,
|
||||
get_current_user,
|
||||
get_db,
|
||||
require_admin_user,
|
||||
require_rule_editor_user,
|
||||
require_rule_reviewer_user,
|
||||
)
|
||||
from app.schemas.agent_asset import (
|
||||
AgentAssetCreate,
|
||||
AgentAssetListItem,
|
||||
AgentAssetOnlyOfficeCallbackRead,
|
||||
AgentAssetOnlyOfficeCallbackWrite,
|
||||
AgentAssetOnlyOfficeConfigRead,
|
||||
AgentAssetRead,
|
||||
AgentAssetReviewCreate,
|
||||
AgentAssetReviewRead,
|
||||
AgentAssetVersionCompareRead,
|
||||
AgentAssetUpdate,
|
||||
AgentAssetVersionCreate,
|
||||
AgentAssetVersionRead,
|
||||
AgentAssetVersionTimelineItemRead,
|
||||
)
|
||||
from app.schemas.common import ErrorResponse
|
||||
from app.services.agent_assets import AgentAssetService
|
||||
@@ -29,6 +42,10 @@ RequestIdHeader = Annotated[
|
||||
str | None,
|
||||
Header(description="外部请求 ID,用于串联审计日志和上游调用链。"),
|
||||
]
|
||||
CurrentUser = Annotated[CurrentUserContext, Depends(get_current_user)]
|
||||
AdminUser = Annotated[CurrentUserContext, Depends(require_admin_user)]
|
||||
RuleEditorUser = Annotated[CurrentUserContext, Depends(require_rule_editor_user)]
|
||||
RuleReviewerUser = Annotated[CurrentUserContext, Depends(require_rule_reviewer_user)]
|
||||
|
||||
|
||||
def _handle_asset_error(exc: Exception) -> None:
|
||||
@@ -93,6 +110,185 @@ def get_agent_asset(asset_id: str, db: DbSession) -> AgentAssetRead:
|
||||
return asset
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{asset_id}/spreadsheet/onlyoffice-config",
|
||||
response_model=AgentAssetOnlyOfficeConfigRead,
|
||||
summary="读取规则 Excel 的 ONLYOFFICE 配置",
|
||||
description="为规则详情页中的 Excel 规则表生成 ONLYOFFICE 配置。",
|
||||
)
|
||||
def get_agent_asset_spreadsheet_onlyoffice_config(
|
||||
asset_id: str,
|
||||
current_user: CurrentUser,
|
||||
db: DbSession,
|
||||
version: Annotated[
|
||||
str | None,
|
||||
Query(description="可选的规则版本号;不传时默认当前版本。"),
|
||||
] = None,
|
||||
) -> AgentAssetOnlyOfficeConfigRead:
|
||||
try:
|
||||
return AgentAssetService(db).build_rule_spreadsheet_onlyoffice_config(
|
||||
asset_id,
|
||||
current_user,
|
||||
version=version,
|
||||
)
|
||||
except Exception as exc:
|
||||
_handle_asset_error(exc)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{asset_id}/spreadsheet/content",
|
||||
response_class=FileResponse,
|
||||
summary="下载或预览规则 Excel 文件",
|
||||
description="按版本返回规则的 Excel 快照,用于浏览器预览或下载。",
|
||||
)
|
||||
def get_agent_asset_spreadsheet_content(
|
||||
asset_id: str,
|
||||
_: CurrentUser,
|
||||
db: DbSession,
|
||||
version: Annotated[
|
||||
str | None,
|
||||
Query(description="可选的规则版本号;不传时默认当前版本。"),
|
||||
] = None,
|
||||
) -> FileResponse:
|
||||
try:
|
||||
file_path, media_type, filename = AgentAssetService(db).get_rule_spreadsheet_content(
|
||||
asset_id,
|
||||
version=version,
|
||||
)
|
||||
except Exception as exc:
|
||||
_handle_asset_error(exc)
|
||||
|
||||
return FileResponse(file_path, media_type=media_type, filename=filename)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{asset_id}/spreadsheet/onlyoffice/content",
|
||||
response_class=FileResponse,
|
||||
summary="供 ONLYOFFICE 读取规则 Excel 源文件",
|
||||
description="使用短时令牌供 ONLYOFFICE 拉取规则表源文件。",
|
||||
)
|
||||
def get_agent_asset_spreadsheet_onlyoffice_content(
|
||||
asset_id: str,
|
||||
db: DbSession,
|
||||
version: Annotated[
|
||||
str,
|
||||
Query(min_length=1, description="规则版本号。"),
|
||||
],
|
||||
access_token: Annotated[
|
||||
str,
|
||||
Query(min_length=1, description="ONLYOFFICE 临时访问令牌。"),
|
||||
],
|
||||
) -> FileResponse:
|
||||
try:
|
||||
service = AgentAssetService(db)
|
||||
service.validate_rule_spreadsheet_access_token(asset_id, version, access_token)
|
||||
file_path, media_type, filename = service.get_rule_spreadsheet_content(
|
||||
asset_id,
|
||||
version=version,
|
||||
)
|
||||
except FileNotFoundError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(exc)) from exc
|
||||
except Exception as exc:
|
||||
_handle_asset_error(exc)
|
||||
|
||||
return FileResponse(file_path, media_type=media_type, filename=filename)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/{asset_id}/spreadsheet/upload",
|
||||
response_model=AgentAssetRead,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
summary="上传规则 Excel 文件",
|
||||
description="为指定规则上传新的 Excel 快照,并自动生成新规则版本。",
|
||||
)
|
||||
def upload_agent_asset_spreadsheet(
|
||||
asset_id: str,
|
||||
content: Annotated[
|
||||
bytes,
|
||||
Body(
|
||||
media_type="application/octet-stream",
|
||||
description="待上传的 Excel 文件二进制内容。",
|
||||
),
|
||||
],
|
||||
filename: Annotated[str, Query(min_length=1, description="原始文件名。")],
|
||||
current_user: RuleEditorUser,
|
||||
db: DbSession,
|
||||
x_request_id: RequestIdHeader = None,
|
||||
) -> AgentAssetRead:
|
||||
try:
|
||||
return AgentAssetService(db).upload_rule_spreadsheet(
|
||||
asset_id,
|
||||
filename=filename,
|
||||
content=content,
|
||||
actor=current_user.name,
|
||||
request_id=x_request_id,
|
||||
)
|
||||
except Exception as exc:
|
||||
_handle_asset_error(exc)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/{asset_id}/spreadsheet/import-content",
|
||||
response_model=AgentAssetRead,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
summary="导入规则 Excel 表格内容",
|
||||
description="读取上传 Excel 中的工作表内容,写回当前规则表;保留当前规则文件名与规则身份。",
|
||||
)
|
||||
def import_agent_asset_spreadsheet_content(
|
||||
asset_id: str,
|
||||
content: Annotated[
|
||||
bytes,
|
||||
Body(
|
||||
media_type="application/octet-stream",
|
||||
description="待导入的 Excel 文件二进制内容。",
|
||||
),
|
||||
],
|
||||
filename: Annotated[str, Query(min_length=1, description="上传文件原始文件名。")],
|
||||
current_user: RuleEditorUser,
|
||||
db: DbSession,
|
||||
x_request_id: RequestIdHeader = None,
|
||||
) -> AgentAssetRead:
|
||||
try:
|
||||
return AgentAssetService(db).import_rule_spreadsheet_content(
|
||||
asset_id,
|
||||
filename=filename,
|
||||
content=content,
|
||||
actor=current_user.name,
|
||||
request_id=x_request_id,
|
||||
)
|
||||
except Exception as exc:
|
||||
_handle_asset_error(exc)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/{asset_id}/spreadsheet/onlyoffice/callback",
|
||||
response_model=AgentAssetOnlyOfficeCallbackRead,
|
||||
summary="接收规则 Excel 的 ONLYOFFICE 回调",
|
||||
description="接收 ONLYOFFICE 回写内容,并自动生成新的规则版本。",
|
||||
)
|
||||
def handle_agent_asset_spreadsheet_onlyoffice_callback(
|
||||
asset_id: str,
|
||||
payload: AgentAssetOnlyOfficeCallbackWrite,
|
||||
db: DbSession,
|
||||
version: Annotated[
|
||||
str,
|
||||
Query(min_length=1, description="打开编辑器时对应的规则版本号。"),
|
||||
],
|
||||
) -> AgentAssetOnlyOfficeCallbackRead:
|
||||
try:
|
||||
AgentAssetService(db).handle_rule_spreadsheet_onlyoffice_callback(
|
||||
asset_id,
|
||||
version=version,
|
||||
payload=payload.model_dump(),
|
||||
)
|
||||
except Exception as exc:
|
||||
_handle_asset_error(exc)
|
||||
|
||||
return AgentAssetOnlyOfficeCallbackRead()
|
||||
|
||||
|
||||
@router.post(
|
||||
"",
|
||||
response_model=AgentAssetRead,
|
||||
@@ -237,11 +433,22 @@ def create_agent_asset_version(
|
||||
def create_agent_asset_review(
|
||||
asset_id: str,
|
||||
payload: AgentAssetReviewCreate,
|
||||
current_user: CurrentUser,
|
||||
db: DbSession,
|
||||
x_actor: ActorHeader = None,
|
||||
x_request_id: RequestIdHeader = None,
|
||||
) -> AgentAssetReviewRead:
|
||||
try:
|
||||
role_codes = {item.strip() for item in current_user.role_codes}
|
||||
if payload.review_status.value == "pending":
|
||||
if not (
|
||||
current_user.is_admin
|
||||
or "manager" in role_codes
|
||||
or "finance" in role_codes
|
||||
):
|
||||
raise PermissionError("只有财务人员或高级管理人员可以提交审核。")
|
||||
elif not (current_user.is_admin or "manager" in role_codes):
|
||||
raise PermissionError("只有高级管理人员可以审核规则。")
|
||||
return AgentAssetService(db).create_review(
|
||||
asset_id,
|
||||
payload,
|
||||
@@ -270,6 +477,7 @@ def create_agent_asset_review(
|
||||
)
|
||||
def activate_agent_asset(
|
||||
asset_id: str,
|
||||
_: RuleReviewerUser,
|
||||
db: DbSession,
|
||||
x_actor: ActorHeader = None,
|
||||
x_request_id: RequestIdHeader = None,
|
||||
@@ -282,3 +490,68 @@ def activate_agent_asset(
|
||||
)
|
||||
except Exception as exc:
|
||||
_handle_asset_error(exc)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/{asset_id}/versions/{version}/restore",
|
||||
response_model=AgentAssetRead,
|
||||
summary="基于历史版本恢复工作稿",
|
||||
description="复制指定历史版本内容生成新的工作版本,用于误上线后的快速恢复与重新审核。",
|
||||
)
|
||||
def restore_agent_asset_version(
|
||||
asset_id: str,
|
||||
version: str,
|
||||
current_user: RuleReviewerUser,
|
||||
db: DbSession,
|
||||
x_actor: ActorHeader = None,
|
||||
x_request_id: RequestIdHeader = None,
|
||||
) -> AgentAssetRead:
|
||||
try:
|
||||
return AgentAssetService(db).restore_version_as_working_copy(
|
||||
asset_id,
|
||||
version,
|
||||
actor=(x_actor or current_user.name or "system").strip() or "system",
|
||||
request_id=x_request_id,
|
||||
)
|
||||
except Exception as exc:
|
||||
_handle_asset_error(exc)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{asset_id}/version-timeline",
|
||||
response_model=list[AgentAssetVersionTimelineItemRead],
|
||||
summary="读取规则版本流转时间线",
|
||||
description="返回规则版本创建、提交审核、审核结果和正式上线等流转事件。",
|
||||
)
|
||||
def get_agent_asset_version_timeline(
|
||||
asset_id: str,
|
||||
_: CurrentUser,
|
||||
db: DbSession,
|
||||
) -> list[AgentAssetVersionTimelineItemRead]:
|
||||
try:
|
||||
return AgentAssetService(db).list_version_timeline(asset_id)
|
||||
except Exception as exc:
|
||||
_handle_asset_error(exc)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{asset_id}/versions/compare",
|
||||
response_model=AgentAssetVersionCompareRead,
|
||||
summary="比较两个规则表版本",
|
||||
description="对比两个 Excel 规则表版本的工作表变化与单元格级差异。",
|
||||
)
|
||||
def compare_agent_asset_spreadsheet_versions(
|
||||
asset_id: str,
|
||||
_: CurrentUser,
|
||||
db: DbSession,
|
||||
base_version: Annotated[str, Query(min_length=1, description="基准版本号")],
|
||||
target_version: Annotated[str, Query(min_length=1, description="对比版本号")],
|
||||
) -> AgentAssetVersionCompareRead:
|
||||
try:
|
||||
return AgentAssetService(db).compare_spreadsheet_versions(
|
||||
asset_id,
|
||||
base_version=base_version,
|
||||
target_version=target_version,
|
||||
)
|
||||
except Exception as exc:
|
||||
_handle_asset_error(exc)
|
||||
|
||||
Reference in New Issue
Block a user