feat: 平台治理与权限体系完善,存储进度/GPU预留/审批中心与日志整合

- 平台治理: 租户用户权限层次、资源ACL、审批中心与审批模板、访问申请
- 存储: MinIO 存储进度迁移、对象存储安全加固与测试
- 计算: GPU 资源预留、compute 轮询与同步增强
- 权限: permission v2 迁移、权限安全验收测试
- 日志: 后端运行日志中文说明、操作日志整合
- 数据处理/评测: 数据转换与模型评测优化

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
wuyongtao
2026-08-21 09:49:48 +08:00
parent 080ef6ab00
commit 6f0e82f351
94 changed files with 9547 additions and 1045 deletions

View File

@@ -21,7 +21,7 @@ from typing import Any, Callable, Optional, TypeVar
from fastapi import Request
from app.core.logging import get_logger, request_id_var
from app.core.logging import get_client_ip, get_logger, mask_sensitive_string, request_id_var
logger = get_logger("app.audit")
@@ -67,12 +67,25 @@ def audit_log(
detail=detail,
trace_id=trace_id,
duration_ms=elapsed_ms,
kwargs=kwargs,
args=args,
)
return result
except Exception:
logger.error(
"审计日志记录失败 action=%s", action, exc_info=True
except Exception as exc:
_record_audit(
action=action,
actor_id=_extract_actor_id(kwargs),
target_type=target_type,
target_id=_extract_target_id(None, kwargs, extract_target_id),
detail=_build_detail(detail_template, kwargs),
trace_id=trace_id,
duration_ms=(time.perf_counter() - started_at) * 1000,
result="failure",
reason=_safe_exception_reason(exc),
kwargs=kwargs,
args=args,
)
logger.warning("业务操作失败 action=%s reason=%s", action, _safe_exception_reason(exc))
raise
return async_wrapper # type: ignore
@@ -94,12 +107,25 @@ def audit_log(
detail=detail,
trace_id=trace_id,
duration_ms=elapsed_ms,
kwargs=kwargs,
args=args,
)
return result
except Exception:
logger.error(
"审计日志记录失败 action=%s", action, exc_info=True
except Exception as exc:
_record_audit(
action=action,
actor_id=_extract_actor_id(kwargs),
target_type=target_type,
target_id=_extract_target_id(None, kwargs, extract_target_id),
detail=_build_detail(detail_template, kwargs),
trace_id=trace_id,
duration_ms=(time.perf_counter() - started_at) * 1000,
result="failure",
reason=_safe_exception_reason(exc),
kwargs=kwargs,
args=args,
)
logger.warning("业务操作失败 action=%s reason=%s", action, _safe_exception_reason(exc))
raise
return sync_wrapper # type: ignore
@@ -144,18 +170,36 @@ def _record_audit(
detail: str,
trace_id: str,
duration_ms: float,
*,
kwargs: dict[str, Any] | None = None,
args: tuple[Any, ...] = (),
result: str = "success",
reason: str | None = None,
) -> None:
"""通过已有的 record_audit 方法写入审计日志"""
try:
from app.db.platform_store import get_platform_store
store = get_platform_store()
kwargs = kwargs or {}
request = _extract_request(args, kwargs)
current_user = kwargs.get("current_user") or kwargs.get("user") or {}
request_id = request.headers.get("X-Request-ID") if request else None
request_id = request_id or trace_id
client_ip = get_client_ip(request) or None
detail_text = f"{detail} trace_id={trace_id} duration_ms={duration_ms:.1f}" if detail else f"trace_id={trace_id} duration_ms={duration_ms:.1f}"
store.record_audit(
action=action,
actor_id=actor_id,
target_type=target_type or None,
target_id=target_id,
detail=f"{detail} trace_id={trace_id} duration_ms={duration_ms:.1f}" if detail else f"trace_id={trace_id} duration_ms={duration_ms:.1f}",
tenant_id=str(current_user.get("tenant_id") or "") or None,
detail=mask_sensitive_string(detail_text),
result=result,
reason=mask_sensitive_string(reason or "") or None,
request_id=request_id,
session_id=str(current_user.get("session_id") or "") or None,
ip=client_ip,
)
except Exception:
logger.error("写入审计日志失败 action=%s", action, exc_info=True)
@@ -170,6 +214,19 @@ def _extract_actor_id(kwargs: dict) -> Optional[str]:
return None
def _extract_request(args: tuple[Any, ...], kwargs: dict[str, Any]) -> Request | None:
for value in tuple(kwargs.values()) + tuple(args):
if isinstance(value, Request):
return value
return None
def _safe_exception_reason(exc: Exception) -> str:
"""Keep audit failures useful without recording credentials or tokens."""
value = getattr(exc, "detail", None) or str(exc) or exc.__class__.__name__
return mask_sensitive_string(str(value))[:500]
# ==================== 预定义的审计操作常量 ====================
class AuditActions: