diff --git a/backend/alembic/versions/059_chat_log_resource.py b/backend/alembic/versions/059_chat_log_resource.py new file mode 100644 index 00000000..8d4a40f1 --- /dev/null +++ b/backend/alembic/versions/059_chat_log_resource.py @@ -0,0 +1,35 @@ +"""empty message + +Revision ID: db1a95567cbb +Revises: fb2e8dd19158 +Create Date: 2025-12-30 12:29:14.290394 + +""" +from alembic import op +import sqlalchemy as sa +import sqlmodel.sql.sqltypes +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'db1a95567cbb' +down_revision = 'fb2e8dd19158' +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table('sys_logs_resource', + sa.Column('id', sa.BIGINT(), + sa.Identity(always=True, start=1, increment=1, minvalue=1, maxvalue=999999999999999, + cycle=False, cache=1), autoincrement=True, nullable=False), + sa.Column('resource_id', sa.VARCHAR(length=255), autoincrement=False, nullable=True), + sa.Column('module', sa.VARCHAR(length=255), autoincrement=False, nullable=True), + sa.Column('resource_name', sa.VARCHAR(length=255), autoincrement=False, nullable=True), + sa.Column('log_id', sa.BIGINT(), autoincrement=False, nullable=True), + sa.PrimaryKeyConstraint('id', name=op.f('sys_logs_resource_pkey')) + ) + # ### end Alembic commands ### +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('sys_logs_resource') + # ### end Alembic commands ### diff --git a/backend/common/audit/models/log_model.py b/backend/common/audit/models/log_model.py index 6eec6966..975191be 100644 --- a/backend/common/audit/models/log_model.py +++ b/backend/common/audit/models/log_model.py @@ -44,6 +44,14 @@ class OperationType(str, Enum): ANALYSIS = "analysis" PREDICTION = "prediction" +class SystemLogsResource(SQLModel, table=True): + __tablename__ = "sys_logs_resource" + id: Optional[int] = Field(default=None, primary_key=True) + log_id: Optional[int] = Field(default=None,sa_type=BigInteger()) + resource_id: Optional[str] = Field(default=None) + resource_name: Optional[str] = Field(default=None) + module: Optional[str] = Field(default=None) + class SystemLog(SQLModel, table=True): __tablename__ = "sys_logs" diff --git a/backend/common/audit/schemas/logger_decorator.py b/backend/common/audit/schemas/logger_decorator.py index a6b18470..0bf11d52 100644 --- a/backend/common/audit/schemas/logger_decorator.py +++ b/backend/common/audit/schemas/logger_decorator.py @@ -2,14 +2,14 @@ import time import functools import json import inspect -from typing import Callable, Any, Optional, Dict, Union +from typing import Callable, Any, Optional, Dict, Union, List from fastapi import Request, HTTPException from datetime import datetime from pydantic import BaseModel from sqlmodel import Session, select import traceback from sqlbot_xpack.audit.curd.audit import build_resource_union_query -from common.audit.models.log_model import OperationType, OperationStatus, SystemLog +from common.audit.models.log_model import OperationType, OperationStatus, SystemLog, SystemLogsResource from common.audit.schemas.request_context import RequestContext from apps.system.crud.user import get_user_by_account from apps.system.schemas.system_schema import UserInfoDTO, BaseUserDTO @@ -18,42 +18,36 @@ from sqlalchemy import and_, select from common.core.db import engine - -def get_resource_name_by_id_and_module(session, resource_id: Any, module: str) -> Optional[str]: +def get_resource_name_by_id_and_module(session, resource_id: Any, module: str) -> List[Dict[str, str]]: resource_union_query = build_resource_union_query() resource_alias = resource_union_query.alias("resource") - if isinstance(resource_id, list): - # 处理列表情况 - if not resource_id: - return None + # 统一处理为列表 + if not isinstance(resource_id, list): + resource_id = [resource_id] - # 构建查询,使用 IN 条件 - query = select( - resource_alias.c.name - ).where( - and_( - resource_alias.c.id.in_([str(id_) for id_ in resource_id]), - resource_alias.c.module == module - ) + if not resource_id: + return [] + + # 构建查询,使用 IN 条件 + query = select( + resource_alias.c.id, + resource_alias.c.name, + resource_alias.c.module + ).where( + and_( + resource_alias.c.id.in_([str(id_) for id_ in resource_id]), + resource_alias.c.module == module ) - # 执行查询并获取所有结果 - results = session.execute(query).scalars().all() + ) - # 用逗号连接所有名称 - return ",".join(results) if results else None - else: - # 处理单个字符串情况 - query = select( - resource_alias.c.name - ).where( - and_( - resource_alias.c.id == str(resource_id), - resource_alias.c.module == module - ) - ) - return session.execute(query).scalar() + results = session.execute(query).fetchall() + return [{ + 'resource_id': str(row.id), + 'resource_name': row.name or '', + 'module': row.module or '' + } for row in results] class LogConfig(BaseModel): operation_type: OperationType @@ -390,6 +384,7 @@ class SystemLogger: remark: Optional[str] = None, oid: int = -1, opt_type_ref : OperationType = None, + resource_info_list : Optional[List] = None, ) -> Optional[SystemLog]: """Create log records""" try: @@ -422,7 +417,6 @@ class SystemLogger: error_message=error_message, module=config.module, resource_id=str(resource_id), - resource_name=str(resource_name), request_method=request.method if request else None, request_path=request.url.path if request else None, request_params=request_params, @@ -430,13 +424,39 @@ class SystemLogger: remark=remark ) + with Session(engine) as session: session.add(log) - session.query(SystemLog).filter(SystemLog.resource_id == str(resource_id)).update({ - SystemLog.resource_name: str(resource_name) - }, synchronize_session='fetch') session.commit() session.refresh(log) + # 统一处理不同类型的 resource_id_info + if isinstance(resource_id, list): + resource_ids = [str(rid) for rid in resource_id] + else: + resource_ids = [str(resource_id)] + # 批量添加 SystemLogsResource + resource_entries = [] + for resource_id_details in resource_ids: + resource_entry = SystemLogsResource( + resource_id=resource_id_details, + log_id=log.id, + module=config.module + ) + resource_entries.append(resource_entry) + if resource_entries: + session.bulk_save_objects(resource_entries) + session.commit() + + if config.operation_type == OperationType.DELETE and resource_info_list is not None: + # 批量更新 SystemLogsResource 表的 resource_name + for resource_info in resource_info_list: + session.query(SystemLogsResource).filter( + SystemLogsResource.resource_id == resource_info['resource_id'], + SystemLogsResource.module == resource_info['module'], + ).update({ + SystemLogsResource.resource_name: resource_info['resource_name'] + }, synchronize_session='fetch') + session.commit() return log except Exception as e: @@ -472,6 +492,7 @@ def system_log(config: Union[LogConfig, Dict]): remark = None oid = -1 opt_type_ref = None + resource_info_list = None try: # Get current request @@ -515,7 +536,7 @@ def system_log(config: Union[LogConfig, Dict]): resource_name = input_account if config.operation_type == OperationType.DELETE: with Session(engine) as session: - resource_name = get_resource_name_by_id_and_module(session, resource_id, config.module) + resource_info_list = get_resource_name_by_id_and_module(session, resource_id, config.module) if config.operation_type == OperationType.CREATE_OR_UPDATE: opt_type_ref = OperationType.UPDATE if resource_id is not None else OperationType.CREATE @@ -530,10 +551,6 @@ def system_log(config: Union[LogConfig, Dict]): result, "result" ) - if config.operation_type != OperationType.DELETE: - with Session(engine) as session: - resource_name = get_resource_name_by_id_and_module(session, resource_id, config.module) - return result except Exception as e: status = OperationStatus.FAILED @@ -568,7 +585,8 @@ def system_log(config: Union[LogConfig, Dict]): remark=remark, request=request, oid=oid, - opt_type_ref=opt_type_ref + opt_type_ref=opt_type_ref, + resource_info_list=resource_info_list ) except Exception as log_error: print(f"[SystemLogger] Log creation failed: {log_error}") @@ -581,6 +599,7 @@ def system_log(config: Union[LogConfig, Dict]): request = None resource_id = None resource_name = None + resource_info_list = None result = None try: @@ -602,7 +621,7 @@ def system_log(config: Union[LogConfig, Dict]): # Obtain client information if config.operation_type == OperationType.DELETE: with Session(engine) as session: - resource_name = get_resource_name_by_id_and_module(session, resource_id, config.module) + resource_info_list = get_resource_name_by_id_and_module(session, resource_id, config.module) # Execute the original function result = func(*args, **kwargs) @@ -615,10 +634,6 @@ def system_log(config: Union[LogConfig, Dict]): "result" ) - if config.operation_type != OperationType.DELETE: - with Session(engine) as session: - resource_name = get_resource_name_by_id_and_module(session, resource_id, config.module) - return result except Exception as e: @@ -652,7 +667,8 @@ def system_log(config: Union[LogConfig, Dict]): error_message=error_message, resource_id=resource_id, resource_name=resource_name, - request=request + request=request, + resource_info_list=resource_info_list ) ) else: diff --git a/backend/locales/en.json b/backend/locales/en.json index 212ee1a7..c784baea 100644 --- a/backend/locales/en.json +++ b/backend/locales/en.json @@ -176,7 +176,9 @@ "log_setting": "Login Authentication", "setting": "Settings", "system_management": "System Management", - "opt_log": "Operation Log" + "opt_log": "Operation Log", + "prediction": "Data prediction", + "analysis": "Data analysis" }, "i18n_table_not_exist": "Table not exist" } \ No newline at end of file diff --git a/backend/locales/ko-KR.json b/backend/locales/ko-KR.json index 4a6b7001..d46cc2ee 100644 --- a/backend/locales/ko-KR.json +++ b/backend/locales/ko-KR.json @@ -176,7 +176,9 @@ "log_setting": "로그인 인증", "setting": "설정", "system_management": "시스템 관리", - "opt_log": "작업 로그" + "opt_log": "작업 로그", + "prediction": "데이터 예측", + "analysis": "데이터 분석" }, "i18n_table_not_exist": "현재 테이블이 존재하지 않습니다" } \ No newline at end of file diff --git a/backend/locales/zh-CN.json b/backend/locales/zh-CN.json index edebe9cd..2dc2a1a5 100644 --- a/backend/locales/zh-CN.json +++ b/backend/locales/zh-CN.json @@ -176,7 +176,9 @@ "log_setting": "登录认证", "setting": "设置", "system_management": "系统管理", - "opt_log": "操作日志" + "opt_log": "操作日志", + "prediction": "数据预测", + "analysis": "数据分析" }, "i18n_table_not_exist": "当前表不存在" diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 83681ccd..4795d518 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -83,11 +83,11 @@ url = "https://test.pypi.org/simple" explicit = true [tool.uv.sources] -sqlbot-xpack = { index = "testpypi" } torch = [ { index = "pytorch-cpu", extra = "cpu" }, { index = "pytorch-cu128", extra = "cu128" }, ] +sqlbot-xpack = { path = "../../sqlbot-xpack" } [tool.uv] conflicts = [ diff --git a/frontend/src/api/audit.ts b/frontend/src/api/audit.ts index 649d22a2..c59a34b5 100644 --- a/frontend/src/api/audit.ts +++ b/frontend/src/api/audit.ts @@ -5,8 +5,7 @@ export const audit = { request.get(`/system/audit/page/${pageNum}/${pageSize}${params}`), getOptions: () => request.get(`/system/audit/get_options`), export2Excel: (params: any) => - request.get(`/system/audit/export`, { - params, + request.get(`/system/audit/export${params}`, { responseType: 'blob', requestOptions: { customError: true }, }),