From e669722706702dc1dde542060e0724c2ae6a6b63 Mon Sep 17 00:00:00 2001 From: zhangtao <9480807882@qq.com> Date: Wed, 8 Oct 2025 01:32:14 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E9=85=8D=E7=BD=AE=E5=92=8C=E7=94=A8=E6=88=B7=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E7=9B=B8=E5=85=B3=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat: 添加中间件系统配置获取功能 fix: 修复用户模型和CRUD基础类的问题 docs: 更新README和界面文本 style: 清理无用代码和注释 --- backend/README.md | 6 +- .../app/api/v1/module_system/dict/service.py | 6 +- .../api/v1/module_system/params/service.py | 68 +++++++++++++- .../app/api/v1/module_system/user/model.py | 13 ++- .../app/api/v1/module_system/user/service.py | 11 +-- backend/app/config/setting.py | 61 +++++------- backend/app/core/base_crud.py | 10 +- backend/app/core/base_model.py | 22 ++++- backend/app/core/middlewares.py | 92 +++++++++++++------ backend/app/core/redis_crud.py | 4 +- backend/app/scripts/data/system_param.json | 80 +++------------- backend/app/utils/ai_util.py | 6 +- backend/app/utils/upload_util.py | 22 +---- backend/env/.env.dev | 6 +- backend/env/.env.prod | 6 +- .../src/views/application/workflow/index.vue | 2 +- .../system/dict/components/DataDrawer.vue | 6 +- 17 files changed, 232 insertions(+), 189 deletions(-) diff --git a/backend/README.md b/backend/README.md index b4a88694..25840c0f 100644 --- a/backend/README.md +++ b/backend/README.md @@ -391,6 +391,6 @@ http://localhost:8000/mcp 在系统配置中设置以下参数: -- `QWEN_API_KEY`: Qwen API密钥 -- `QWEN_BASE_URL`: Qwen API基础URL -- `QWEN_MODEL`: Qwen模型名称 \ No newline at end of file +- `OPENAI_API_KEY`: OpenAI API密钥 +- `OPENAI_BASE_URL`: OpenAI API基础URL +- `OPENAI_MODEL`: OpenAI模型名称 diff --git a/backend/app/api/v1/module_system/dict/service.py b/backend/app/api/v1/module_system/dict/service.py index 1996d8eb..e00d4f09 100644 --- a/backend/app/api/v1/module_system/dict/service.py +++ b/backend/app/api/v1/module_system/dict/service.py @@ -12,9 +12,9 @@ from app.core.redis_crud import RedisCURD from app.core.exceptions import CustomException from app.core.logger import logger from app.api.v1.module_system.auth.schema import AuthSchema -from app.api.v1.module_system.dict.schema import DictDataCreateSchema,DictDataOutSchema,DictDataUpdateSchema,DictTypeCreateSchema,DictTypeOutSchema,DictTypeUpdateSchema -from app.api.v1.module_system.dict.param import DictDataQueryParam, DictTypeQueryParam -from app.api.v1.module_system.dict.crud import DictDataCRUD, DictTypeCRUD +from .schema import DictDataCreateSchema,DictDataOutSchema,DictDataUpdateSchema,DictTypeCreateSchema,DictTypeOutSchema,DictTypeUpdateSchema +from .param import DictDataQueryParam, DictTypeQueryParam +from .crud import DictDataCRUD, DictTypeCRUD class DictTypeService: diff --git a/backend/app/api/v1/module_system/params/service.py b/backend/app/api/v1/module_system/params/service.py index 638802e0..677441fb 100644 --- a/backend/app/api/v1/module_system/params/service.py +++ b/backend/app/api/v1/module_system/params/service.py @@ -216,4 +216,70 @@ class ParamsService: logger.error(f"解析系统配置数据失败: {e}") continue - return configs \ No newline at end of file + return configs + + @classmethod + async def get_system_config_for_middleware(cls, redis: Redis) -> Dict[str, Any]: + """获取中间件所需的系统配置 + + 返回: + Dict: 包含演示模式、IP白名单、API白名单和IP黑名单的配置字典 + """ + # 定义需要获取的配置键 + config_keys = [ + f"{RedisInitKeyConfig.SYSTEM_CONFIG.key}:demo_enable", + f"{RedisInitKeyConfig.SYSTEM_CONFIG.key}:ip_white_list", + f"{RedisInitKeyConfig.SYSTEM_CONFIG.key}:white_api_list_path", + f"{RedisInitKeyConfig.SYSTEM_CONFIG.key}:ip_black_list" + ] + + # 批量获取配置 + config_values = await RedisCURD(redis).mget(config_keys) + + # 初始化默认配置 + config_result = { + "is_demo_mode": False, + "demo_ip_white_list": [], + "api_white_list": [], + "ip_black_list": [] + } + + # 解析演示模式配置 + if config_values[0]: + try: + demo_config = json.loads(config_values[0]) + config_result["is_demo_mode"] = demo_config.get("config_value", False) if isinstance(demo_config, dict) else False + except json.JSONDecodeError: + logger.error(f"解析演示模式配置失败") + + # 解析IP白名单配置 + if config_values[1]: + try: + ip_white_config = json.loads(config_values[1]) + demo_ip_white_list = ip_white_config.get("config_value", []) if isinstance(ip_white_config, dict) else [] + # 确保是列表类型 + config_result["demo_ip_white_list"] = demo_ip_white_list if isinstance(demo_ip_white_list, list) else [] + except json.JSONDecodeError: + logger.error(f"解析IP白名单配置失败") + + # 解析API路径白名单 + if config_values[2]: + try: + white_api_config = json.loads(config_values[2]) + api_white_list = white_api_config.get("config_value", []) if isinstance(white_api_config, dict) else [] + # 确保是列表类型 + config_result["api_white_list"] = api_white_list if isinstance(api_white_list, list) else [] + except json.JSONDecodeError: + logger.error(f"解析API白名单配置失败") + + # 解析IP黑名单 + if config_values[3]: + try: + black_ip_config = json.loads(config_values[3]) + ip_black_list = black_ip_config.get("config_value", []) if isinstance(black_ip_config, dict) else [] + # 确保是列表类型 + config_result["ip_black_list"] = ip_black_list if isinstance(ip_black_list, list) else [] + except json.JSONDecodeError: + logger.error(f"解析IP黑名单配置失败") + + return config_result \ No newline at end of file diff --git a/backend/app/api/v1/module_system/user/model.py b/backend/app/api/v1/module_system/user/model.py index 65448283..0e59642d 100644 --- a/backend/app/api/v1/module_system/user/model.py +++ b/backend/app/api/v1/module_system/user/model.py @@ -7,7 +7,7 @@ from datetime import datetime from typing import Optional, List -from sqlalchemy import Boolean, String, Integer, DateTime, ForeignKey +from sqlalchemy import Boolean, String, Integer, DateTime, ForeignKey, Text from sqlalchemy.orm import relationship, Mapped, mapped_column from app.api.v1.module_system.dept.model import DeptModel @@ -62,13 +62,15 @@ class UserPositionsModel(MappedBase): ) -class UserModel(CreatorMixin): +class UserModel(MappedBase): """ 用户模型 """ __tablename__ = "system_users" __table_args__ = ({'comment': '用户表'}) + id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment='主键ID') + username: Mapped[str] = mapped_column(String(32),nullable=False,unique=True,comment="用户名/登录账号") password: Mapped[str] = mapped_column(String(255),nullable=False,comment="密码哈希") name: Mapped[str] = mapped_column(String(32),nullable=False,comment="昵称") @@ -84,3 +86,10 @@ class UserModel(CreatorMixin): dept: Mapped[Optional["DeptModel"]] = relationship(back_populates="users",foreign_keys=[dept_id],lazy="selectin") roles: Mapped[List["RoleModel"]] = relationship(secondary="system_user_roles",back_populates="users",lazy="selectin") positions: Mapped[List["PositionModel"]] = relationship(secondary="system_user_positions",back_populates="users",lazy="selectin") + + description: Mapped[Optional[str]] = mapped_column(Text, nullable=True, default=None, comment="备注/描述") + created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, default=datetime.now, comment='创建时间') + updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, default=datetime.now, onupdate=datetime.now, comment='更新时间') + + creator_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey('system_users.id', ondelete="SET NULL", onupdate="CASCADE"), nullable=True, index=True, comment="创建人ID") + creator: Mapped[Optional["UserModel"]] = relationship(foreign_keys=[creator_id],lazy="selectin",remote_side=[id]) \ No newline at end of file diff --git a/backend/app/api/v1/module_system/user/service.py b/backend/app/api/v1/module_system/user/service.py index 3fc501b6..c2f6c38d 100644 --- a/backend/app/api/v1/module_system/user/service.py +++ b/backend/app/api/v1/module_system/user/service.py @@ -83,9 +83,8 @@ class UserService: # 创建用户 if data.password: data.password = PwdUtil.set_password_hash(password=data.password) - # user_dict = data.model_dump(exclude_unset=True, exclude={"role_ids", "position_ids"}) - # new_user = await UserCRUD(auth).create(data=user_dict) - new_user = await UserCRUD(auth).create(data=data) + user_dict = data.model_dump(exclude_unset=True, exclude={"role_ids", "position_ids"}) + new_user = await UserCRUD(auth).create(data=user_dict) # 设置角色和岗位 if data.role_ids and len(data.role_ids) > 0: @@ -175,7 +174,7 @@ class UserService: async def get_current_user_info_service(cls, auth: AuthSchema) -> Dict: """获取当前用户信息""" # 获取用户基本信息 - if not auth.user: + if not auth.user or not auth.user.id: raise CustomException(msg="用户不存在") user = await UserCRUD(auth).get_by_id_crud(id=auth.user.id) # 获取部门名称 @@ -210,7 +209,7 @@ class UserService: @classmethod async def update_current_user_info_service(cls, auth: AuthSchema, data: CurrentUserUpdateSchema) -> Dict: """更新当前用户信息""" - if not auth.user: + if not auth.user or not auth.user.id: raise CustomException(msg="用户不存在") user = await UserCRUD(auth).get_by_id_crud(id=auth.user.id) if not user: @@ -247,7 +246,7 @@ class UserService: @classmethod async def change_user_password_service(cls, auth: AuthSchema, data: UserChangePasswordSchema) -> Dict: """修改用户密码""" - if not auth.user: + if not auth.user or not auth.user.id: raise CustomException(msg="用户不存在") if not data.old_password or not data.new_password: raise CustomException(msg='密码不能为空') diff --git a/backend/app/config/setting.py b/backend/app/config/setting.py index 727a235b..8b762aed 100755 --- a/backend/app/config/setting.py +++ b/backend/app/config/setting.py @@ -5,7 +5,6 @@ from functools import lru_cache from pathlib import Path from typing import Any, ClassVar, Dict, List, Optional, Literal from pydantic_settings import BaseSettings, SettingsConfigDict -from pydantic_validation_decorator import Pattern from uvicorn.config import LifespanType from urllib.parse import quote_plus @@ -163,28 +162,28 @@ class Settings(BaseSettings): GZIP_MIN_SIZE: int = 1000 # 最小压缩大小(字节) GZIP_COMPRESS_LEVEL: int = 9 # 压缩级别(1-9) - # ================================================= # - # ***************** 演示模型配置 ***************** # - # ================================================= # - DEMO_ENABLE: bool # 是否开启演示模式 - DEMO_WHITE_LIST_PATH: List[str] = [ # 演示白名单 - "/api/v1/system/auth/login", - "/api/v1/system/auth/token/refresh", - "/api/v1/system/auth/captcha/get", - "/api/v1/system/auth/logout", - "/api/v1/system/config/info", - "/api/v1/system/user/current/info", - "/api/v1/system/notice/available", - ] - DEMO_BLACK_LIST_PATH: List[str] = [ # 演示黑名单 - "/auth/login" - ] - DEMO_IP_WHITE_LIST: List[str] = [ # 演示白名单IP - "127.0.0.1", - "117.10.167.220", - "223.104.208.30", - "42.80.102.171" - ] + # # ================================================= # + # # ***************** 演示模型配置 ***************** # + # # ================================================= # + # DEMO_ENABLE: bool # 是否开启演示模式 + # DEMO_WHITE_LIST_PATH: List[str] = [ # 演示白名单 + # "/api/v1/system/auth/login", + # "/api/v1/system/auth/token/refresh", + # "/api/v1/system/auth/captcha/get", + # "/api/v1/system/auth/logout", + # "/api/v1/system/config/info", + # "/api/v1/system/user/current/info", + # "/api/v1/system/notice/available", + # ] + # DEMO_BLACK_LIST_PATH: List[str] = [ # 演示黑名单 + # "/auth/login" + # ] + # DEMO_IP_WHITE_LIST: List[str] = [ # 演示白名单IP + # "127.0.0.1", + # "117.10.167.220", + # "223.104.208.30", + # "42.80.102.171" + # ] # ================================================= # # ***************** 静态文件配置 ***************** # @@ -217,16 +216,6 @@ class Settings(BaseSettings): ] MAX_FILE_SIZE: int = 10 * 1024 * 1024 # 最大文件大小(10MB) - # ================================================= # - # ***************** 对象存储配置 ***************** # - # ================================================= # - UPLOAD_METHOD: Literal['local','oss'] = "local" # 上传方式, local或者oss - ALI_OSS_KEY: str = 'xxxx' - ALI_OSS_SECRET: str = 'xxxx' - ALI_OSS_END_POINT: str = 'xxxx' - ALI_OSS_PRE: str = 'xxxx' - ALI_OSS_BUCKET: str = 'xxxx' - # ================================================= # # ***************** Swagger配置 ***************** # # ================================================= # @@ -256,9 +245,9 @@ class Settings(BaseSettings): # ******************* AI大模型配置 ****************** # # ================================================= # # https://bailian.console.aliyun.com/?spm=5176.29619931.J_AHgvE-XDhTWrtotIBlDQQ.13.74cd521clrmQ7o&tab=api#/api/?type=model&url=https%3A%2F%2Fhelp.aliyun.com%2Fdocument_detail%2F2712576.html&renderType=iframe - QWEN_BASE_URL: str - QWEN_API_KEY: str - QWEN_MODEL: str + OPENAI_BASE_URL: str + OPENAI_API_KEY: str + OPENAI_MODEL: str # ================================================= # # ******************* 其他配置 ******************* # diff --git a/backend/app/core/base_crud.py b/backend/app/core/base_crud.py index 13cc5c6c..03207182 100644 --- a/backend/app/core/base_crud.py +++ b/backend/app/core/base_crud.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from pydantic import BaseModel -from typing import TypeVar, Sequence, Generic, Dict, Any, List, Optional, Type +from typing import TypeVar, Sequence, Generic, Dict, Any, List, Optional, Type, Union from sqlalchemy.sql.elements import ColumnElement from sqlalchemy.orm import selectinload from sqlalchemy.engine import Result @@ -58,7 +58,7 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): if hasattr(self.model, "creator_id"): sql = sql.options(selectinload(self.model.creator)) - # sql = await self.__filter_permissions(sql) + sql = await self.__filter_permissions(sql) result: Result = await self.db.execute(sql) obj = result.scalars().first() @@ -173,7 +173,7 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): except Exception as e: raise CustomException(msg=f"分页查询失败: {str(e)}") - async def create(self, data: CreateSchemaType) -> ModelType: + async def create(self, data: Union[CreateSchemaType, Dict]) -> ModelType: """ 创建新对象 @@ -194,8 +194,6 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): if hasattr(self.model, "creator_id") and self.current_user: # 设置创建人ID obj.creator_id = self.current_user.id - # 设置创建人对象 - obj.creator = self.current_user self.db.add(obj) await self.db.flush() @@ -204,7 +202,7 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): except Exception as e: raise CustomException(msg=f"创建失败: {str(e)}") - async def update(self, id: int, data: UpdateSchemaType) -> ModelType: + async def update(self, id: int, data: Union[UpdateSchemaType, Dict]) -> ModelType: """ 更新对象 diff --git a/backend/app/core/base_model.py b/backend/app/core/base_model.py index 3ff51d50..272557d1 100644 --- a/backend/app/core/base_model.py +++ b/backend/app/core/base_model.py @@ -6,7 +6,7 @@ from datetime import datetime from typing import Optional -from sqlalchemy import Integer, DateTime, Text +from sqlalchemy import ForeignKey, Integer, DateTime, Text from sqlalchemy.ext.asyncio import AsyncAttrs from sqlalchemy.orm import relationship, DeclarativeBase, Mapped, declared_attr, mapped_column @@ -55,11 +55,27 @@ class CreatorMixin(ModelMixin): """ __abstract__ = True - creator_id: Mapped[Optional[int]] = mapped_column(Integer, nullable=True, index=True, comment="创建人ID") + # creator_id: Mapped[Optional[int]] = mapped_column(Integer, nullable=True, index=True, comment="创建人ID") + creator_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey('system_users.id', ondelete="SET NULL", onupdate="CASCADE"), nullable=True, index=True, comment="创建人ID") @declared_attr def creator(cls) -> Mapped[Optional["UserModel"]]: # type: ignore - """创建人关联关系(延迟加载,避免循环依赖)""" + """ + 创建人关联关系(延迟加载,避免循环依赖) + SQLAlchemy ORM 中的这些加载策略用于控制关联对象的加载行为,它们的主要区别如下: + + 1.select (默认):延迟加载,当首次访问关联属性时执行单独的 SELECT 语句获取关联数据。 + 2.joined :预先加载,使用 LEFT OUTER JOIN 在主查询中一次性加载关联数据,适合一对一和多对一关系。 + 3.selectin :预加载优化,先查询主对象,然后使用 IN 子句批量查询所有关联对象,适合一对多和多对多关系。 + 4.subquery :使用子查询方式预加载关联数据,在某些复杂查询场景下有用。 + 5.raise :访问关联属性时抛出异常,禁止加载关联数据。 + 6.raise_on_sql :允许访问关联对象属性,但执行 SQL 时抛出异常。 + 7.noload :不加载关联数据,访问时返回空集合或 None。 + 8.immediate :立即加载,在主对象加载后立即执行额外查询获取关联数据,类似 select 但不延迟。 + 9.write_only :专为写入优化,不允许读取关联数据,只可添加新记录,适合只写不读的场景。 + 10.dynamic :返回动态查询对象而非实际结果集,允许进一步过滤和分页,适合处理大量关联数据。 + """ + # 其他模型保持原有配置 return relationship( "UserModel", primaryjoin=f"{cls.__name__}.creator_id == UserModel.id", diff --git a/backend/app/core/middlewares.py b/backend/app/core/middlewares.py index 440103cb..bbddc692 100644 --- a/backend/app/core/middlewares.py +++ b/backend/app/core/middlewares.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import time +import json from typing import Any from starlette.middleware.cors import CORSMiddleware from starlette.types import ASGIApp @@ -13,6 +14,7 @@ from app.common.response import ErrorResponse from app.config.setting import settings from app.core.logger import logger from app.core.exceptions import CustomException +from app.api.v1.module_system.params.service import ParamsService class CustomCORSMiddleware(CORSMiddleware): @@ -46,33 +48,71 @@ class RequestLogMiddleware(BaseHTTPMiddleware): logger.info(request_info) try: - - if settings.DEMO_ENABLE: - # 在演示环境中,只有白名单内的IP或路径才能执行非GET请求 - if request.method != "GET": - path = request.scope.get("path") - - request_ip = None - x_forwarded_for = request.headers.get('X-Forwarded-For') - if x_forwarded_for: - # 取第一个 IP 地址,通常为客户端真实 IP - request_ip = x_forwarded_for.split(',')[0].strip() - else: - # 若没有 X-Forwarded-For 头,则使用 request.client.host - request_ip = request.client.host if request.client else None - - # 检查IP是否在白名单,或路径是否在白名单,或用户是否在白名单 - if (request_ip in settings.DEMO_IP_WHITE_LIST) or (path in settings.DEMO_WHITE_LIST_PATH): - response = await call_next(request) - else: - # 非白名单用户,禁止操作 - return ErrorResponse(msg="演示环境,禁止操作") - else: - # GET请求在演示环境中总是允许的 - response = await call_next(request) + # 初始化响应变量 + response = None + + # 获取请求路径 + path = request.scope.get("path") + + # 尝试获取客户端真实IP + request_ip = None + x_forwarded_for = request.headers.get('X-Forwarded-For') + if x_forwarded_for: + # 取第一个 IP 地址,通常为客户端真实 IP + request_ip = x_forwarded_for.split(',')[0].strip() else: - # 非演示环境,正常处理请求 + # 若没有 X-Forwarded-For 头,则使用 request.client.host + request_ip = request.client.host if request.client else None + + # 检查是否启用演示模式 + is_demo_mode = False + demo_ip_white_list = [] + api_white_list = [] + ip_black_list = [] + + try: + # 从应用实例获取Redis连接 + redis = request.app.state.redis + if not redis: + raise Exception("无法获取Redis连接") + + # 使用ParamsService获取系统配置 + system_config = await ParamsService.get_system_config_for_middleware(redis) + + # 提取配置值 + is_demo_mode = system_config["is_demo_mode"] + demo_ip_white_list = system_config["demo_ip_white_list"] + api_white_list = system_config["api_white_list"] + ip_black_list = system_config["ip_black_list"] + + except Exception as e: + logger.warning(f"获取系统配置失败: {e}") + + # 检查是否需要拦截请求 + should_block = False + + # 1. 首先检查IP是否在黑名单中 + if request_ip and request_ip in ip_black_list: + should_block = True + logger.warning(f"黑名单IP访问被拒绝: {request_ip}, 路径: {path}") + + # 2. 如果不在黑名单中,检查是否在演示模式下需要拦截 + elif is_demo_mode in ["true", "True"] and request.method != "GET": + # 在演示模式下,非GET请求需要检查白名单 + is_ip_whitelisted = request_ip in demo_ip_white_list + is_path_whitelisted = path in api_white_list + + if not is_ip_whitelisted and not is_path_whitelisted: + should_block = True + + if should_block: + # 拦截请求 + return ErrorResponse(msg="演示环境,禁止操作") + else: + # 正常处理请求 response = await call_next(request) + + # 计算处理时间并添加到响应头 process_time = round(time.time() - start_time, 5) response.headers["X-Process-Time"] = str(process_time) @@ -90,7 +130,7 @@ class RequestLogMiddleware(BaseHTTPMiddleware): return response except CustomException as e: - logger.error(f"系统异常: {str(e)}") + logger.error(f"中间件处理异常: {str(e)}") return ErrorResponse(msg=f"系统异常,请联系管理员", data=str(e)) diff --git a/backend/app/core/redis_crud.py b/backend/app/core/redis_crud.py index 3c9fc5bb..176e3aa6 100644 --- a/backend/app/core/redis_crud.py +++ b/backend/app/core/redis_crud.py @@ -14,11 +14,11 @@ class RedisCURD: """初始化""" self.redis = redis - async def mget(self, *keys: tuple)-> list: + async def mget(self, keys: list) -> list: """批量获取缓存 Args: - *keys: 可变参数,接收多个键名 + keys: 键名列表 Returns: list: 返回缓存值列表 diff --git a/backend/app/scripts/data/system_param.json b/backend/app/scripts/data/system_param.json index 04d31c77..6a5aabd3 100644 --- a/backend/app/scripts/data/system_param.json +++ b/backend/app/scripts/data/system_param.json @@ -63,84 +63,39 @@ "creator_id": 1 }, { - "config_name": "帮助文档", - "config_key": "sys_help_doc", - "config_value": "https://service.fastapiadmin.com", + "config_name": "演示模式启用", + "config_key": "demo_enable", + "config_value": "false", "config_type": true, "status": true, - "description": "帮助文档", + "description": "是否开启演示模式", "creator_id": 1 }, { - "config_name": "隐私政策", - "config_key": "sys_web_privacy", - "config_value": "https://github.com/1014TaoTao/fastapi_vue3_admin/blob/master/LICENSE", - "config_type": true, - "status": true, - "description": "隐私政策", - "creator_id": 1 - }, - { - "config_name": "用户协议", - "config_key": "sys_web_clause", - "config_value": "https://github.com/1014TaoTao/fastapi_vue3_admin/blob/master/LICENSE", - "config_type": true, - "status": true, - "description": "用户协议", - "creator_id": 1 - }, - { - "config_name": "源码代码", - "config_key": "sys_git_code", - "config_value": "https://github.com/1014TaoTao/fastapi_vue3_admin.git", - "config_type": true, - "status": true, - "description": "源码代码", - "creator_id": 1 - }, - { - "config_name": "项目版本", - "config_key": "sys_web_version", - "config_value": "2.0.0", - "config_type": true, - "status": true, - "description": "项目版本", - "creator_id": 1 - }, - { - "config_name": "白名单接口", + "config_name": "接口白名单", "config_key": "white_api_list_path", "config_value": "[\"/api/v1/system/auth/login\", \"/api/v1/system/auth/token/refresh\", \"/api/v1/system/auth/captcha/get\", \"/api/v1/system/auth/logout\", \"/api/v1/system/config/info\", \"/api/v1/system/user/current/info\", \"/api/v1/system/notice/available\"]", "config_type": true, "status": true, - "description": "演示模式白名单路径列表", - "creator_id": 1 - }, - { - "config_name": "黑名单接口", - "config_key": "black_api_list_path", - "config_value": "[\"/auth/login\"]", - "config_type": true, - "status": true, - "description": "演示模式黑名单路径列表", + "description": "接口白名单", "creator_id": 1 }, { "config_name": "访问IP白名单", "config_key": "ip_white_list", - "config_value": "[\"127.0.0.1\", \"117.10.167.220\", \"223.104.208.30\", \"42.80.102.171\"]", + "config_value": "[\"127.0.0.1\"]", "config_type": true, "status": true, "description": "演示模式IP白名单列表", "creator_id": 1 }, { - "config_name": "权限认证白名单接口", - "config_key": "token_request_path_exclude", - "config_value": "[\"api/v1/auth/login\"]", + "config_name": "访问IP黑名单", + "config_key": "ip_black_list", + "config_value": "[]", "config_type": true, "status": true, - "description": "无需JWT认证的路径白名单", + "description": "访问IP黑名单", "creator_id": 1 }, { @@ -176,7 +131,7 @@ "config_value": "[\"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"HEAD\", \"OPTIONS\"]", "config_type": true, "status": true, - "description": "需要记录操作日志的HTTP方法列表", + "description": "操作日志记录方法", "creator_id": 1 }, { @@ -185,16 +140,7 @@ "config_value": "[\"get_captcha_for_login\"]", "config_type": true, "status": true, - "description": "忽略记录操作日志的函数列表", - "creator_id": 1 - }, - { - "config_name": "演示模式启用", - "config_key": "demo_enable", - "config_value": "False", - "config_type": true, - "status": true, - "description": "是否开启演示模式", + "description": "忽略操作日志函数", "creator_id": 1 } ] \ No newline at end of file diff --git a/backend/app/utils/ai_util.py b/backend/app/utils/ai_util.py index b9937072..4e5f6c9c 100644 --- a/backend/app/utils/ai_util.py +++ b/backend/app/utils/ai_util.py @@ -11,7 +11,7 @@ from app.core.logger import logger class AIClient: def __init__(self): - self.model = settings.QWEN_MODEL + self.model = settings.OPENAI_MODEL # 创建一个不带冲突参数的httpx客户端 self.http_client = httpx.AsyncClient( timeout=30.0, @@ -20,8 +20,8 @@ class AIClient: # 使用自定义的http客户端 self.client = AsyncOpenAI( - api_key=settings.QWEN_API_KEY, - base_url=settings.QWEN_BASE_URL, + api_key=settings.OPENAI_API_KEY, + base_url=settings.OPENAI_BASE_URL, http_client=self.http_client ) diff --git a/backend/app/utils/upload_util.py b/backend/app/utils/upload_util.py index aae5b3df..ac4ae7d7 100644 --- a/backend/app/utils/upload_util.py +++ b/backend/app/utils/upload_util.py @@ -164,24 +164,4 @@ class UploadUtil: # 解析文件路径 filename = cls.generate_file(Path(file_path)) return str(filename) - - @classmethod - async def upload_file_oss(cls, file: UploadFile, oss_folder): - end_point = settings.ALI_OSS_END_POINT - access_key_id = settings.ALI_OSS_KEY - access_key_secret = settings.ALI_OSS_SECRET - access_pre = settings.ALI_OSS_PRE - auth = oss2.Auth(access_key_id, access_key_secret) - bucket = oss2.Bucket(auth, end_point, settings.ALI_OSS_BUCKET) - pic_data = file.file.read() - if not file.filename: - raise CustomException(msg='文件名为空') - file_name = oss_folder + str(time.time()) + file.filename.rsplit(".", 1)[-1] - target_file_name = oss_folder + str(time.time()) + Path(file_name).suffix - bucket.put_object(target_file_name, pic_data) - - # 后期优化 - file_url = f'{access_pre}/{target_file_name}' - filepath = file_url - # 返回相对路径 - return file.filename, filepath, file_url \ No newline at end of file + \ No newline at end of file diff --git a/backend/env/.env.dev b/backend/env/.env.dev index 6b6d837f..d621b1a9 100644 --- a/backend/env/.env.dev +++ b/backend/env/.env.dev @@ -58,6 +58,6 @@ MONGO_DB_NAME = "admin" LOGGER_LEVEL = 'DEBUG' # 日志级别 # https://bailian.console.aliyun.com/?spm=5176.29619931.J_AHgvE-XDhTWrtotIBlDQQ.13.74cd521clrmQ7o&tab=api#/api/?type=model&url=https%3A%2F%2Fhelp.aliyun.com%2Fdocument_detail%2F2712576.html&renderType=iframe -QWEN_BASE_URL = https://dashscope.aliyuncs.com/compatible-mode/v1 -QWEN_API_KEY = sk-e688534f2d984e7fa2eb46add409422f -QWEN_MODEL = qwen-plus \ No newline at end of file +OPENAI_BASE_URL = https://dashscope.aliyuncs.com/compatible-mode/v1 +OPENAI_API_KEY = sk-e688534f2d984e7fa2eb46add409422f +OPENAI_MODEL = qwen-plus diff --git a/backend/env/.env.prod b/backend/env/.env.prod index 82057dd5..ec8e2e9b 100644 --- a/backend/env/.env.prod +++ b/backend/env/.env.prod @@ -59,6 +59,6 @@ LOGGER_LEVEL = 'INFO' # 日志级别 # https://bailian.console.aliyun.com/?spm=5176.29619931.J_AHgvE-XDhTWrtotIBlDQQ.13.74cd521clrmQ7o&tab=api#/api/?type=model&url=https%3A%2F%2Fhelp.aliyun.com%2Fdocument_detail%2F2712576.html&renderType=iframe -QWEN_BASE_URL = https://dashscope.aliyuncs.com/compatible-mode/v1 -QWEN_API_KEY = sk-e688534f2d984e7fa2eb46add409422f -QWEN_MODEL = qwen-plus \ No newline at end of file +OPENAI_BASE_URL = https://dashscope.aliyuncs.com/compatible-mode/v1 +OPENAI_API_KEY = sk-e688534f2d984e7fa2eb46add409422f +OPENAI_MODEL = qwen-plus diff --git a/frontend/src/views/application/workflow/index.vue b/frontend/src/views/application/workflow/index.vue index 61118cce..50f978c7 100644 --- a/frontend/src/views/application/workflow/index.vue +++ b/frontend/src/views/application/workflow/index.vue @@ -363,7 +363,7 @@ async function handleOpenDialog(type: 'create' | 'update' | 'detail', id?: numbe Object.assign(formData, response.data.data); } } else { - dialogVisible.title = "新增公告通知"; + dialogVisible.title = "新增工作流"; formData.id = undefined; } dialogVisible.visible = true; diff --git a/frontend/src/views/system/dict/components/DataDrawer.vue b/frontend/src/views/system/dict/components/DataDrawer.vue index 7fa33d53..9ca85596 100644 --- a/frontend/src/views/system/dict/components/DataDrawer.vue +++ b/frontend/src/views/system/dict/components/DataDrawer.vue @@ -405,14 +405,14 @@ async function handleOpenDialog(type: 'create' | 'update' | 'detail', id?: numbe if (id) { const response = await DictAPI.getDictDataDetail(id); if (type === 'detail') { - dialogVisible.title = "公告通知详情"; + dialogVisible.title = "字典数据详情"; Object.assign(detailFormData.value, response.data.data); } else if (type === 'update') { - dialogVisible.title = "修改公告通知"; + dialogVisible.title = "修改字典数据"; Object.assign(formData, response.data.data); } } else { - dialogVisible.title = "新增公告通知"; + dialogVisible.title = "新增字典数据"; formData.id = undefined; formData.dict_type = props.dictType; }