| |
| """ |
| 处理数据库中 'settings' 表的交互,用于存储和检索应用程序的配置项。 |
| 主要用于管理上下文 TTL (Time-To-Live) 等设置。 |
| """ |
| import logging |
| import asyncio |
| from typing import Optional |
| from sqlalchemy.ext.asyncio import AsyncSession |
| from sqlalchemy import text, select, update, insert |
| from sqlalchemy.exc import SQLAlchemyError |
| import sqlalchemy |
| from app import config |
|
|
| |
| |
| |
| from app.core.database.models import Setting |
|
|
| logger = logging.getLogger('my_logger') |
|
|
| |
| async def get_setting(db: AsyncSession, key: str, default: Optional[str] = None) -> Optional[str]: |
| """ |
| 从数据库的 'settings' 表中异步获取指定键 (key) 的设置值。 |
| 使用传入的 SQLAlchemy AsyncSession。 |
| |
| Args: |
| db (AsyncSession): SQLAlchemy 异步数据库会话。 |
| key (str): 要获取的设置项的键名。 |
| default (Optional[str], optional): 如果在数据库中找不到对应的键, |
| 或者在获取过程中发生错误时,返回的默认值。 |
| 默认为 None。 |
| |
| Returns: |
| Optional[str]: 如果找到设置项,则返回其字符串形式的值;否则返回指定的默认值。 |
| """ |
| try: |
| |
| stmt = select(Setting.value).where(Setting.key == key) |
| |
| result = await db.execute(stmt) |
| |
| value = result.scalar_one_or_none() |
| |
| return value if value is not None else default |
| except SQLAlchemyError as e: |
| |
| logger.error(f"获取设置 '{key}' 失败: {e}", exc_info=True) |
| return default |
| except Exception as e: |
| logger.error(f"获取设置 '{key}' 时发生意外错误: {e}", exc_info=True) |
| return default |
|
|
| async def set_setting(db: AsyncSession, key: str, value: str): |
| """ |
| 在数据库的 'settings' 表中异步设置或更新指定键 (key) 的值。 |
| 如果键已存在,则更新其值;如果不存在,则插入新行。 |
| 使用传入的 SQLAlchemy AsyncSession。 |
| |
| Args: |
| db (AsyncSession): SQLAlchemy 异步数据库会话。 |
| key (str): 要设置或更新的设置项的键名。 |
| value (str): 要设置的值(将作为字符串存储)。 |
| """ |
| try: |
| |
| stmt_update = ( |
| update(Setting) |
| .where(Setting.key == key) |
| .values(value=value) |
| .execution_options(synchronize_session=False) |
| ) |
| result = await db.execute(stmt_update) |
|
|
| |
| if result.rowcount == 0: |
| |
| |
| stmt_insert = insert(Setting).values(key=key, value=value) |
| |
| |
| |
| try: |
| await db.execute(stmt_insert) |
| logger.info(f"设置 '{key}' 不存在,已插入新值 '{value}'") |
| except sqlalchemy.exc.IntegrityError: |
| |
| |
| logger.warning(f"尝试插入设置 '{key}' 时发生冲突,可能已被并发插入。") |
| |
| pass |
|
|
| else: |
| logger.info(f"设置 '{key}' 已更新为 '{value}'") |
|
|
| |
| await db.commit() |
| except SQLAlchemyError as e: |
| await db.rollback() |
| logger.error(f"设置 '{key}' 失败: {e}", exc_info=True) |
| except Exception as e: |
| await db.rollback() |
| logger.error(f"设置 '{key}' 时发生意外错误: {e}", exc_info=True) |
|
|
|
|
| async def get_ttl_days(db: AsyncSession) -> int: |
| """ |
| 从数据库异步获取上下文 TTL (Time-To-Live) 的天数设置。 |
| 此函数会处理从数据库获取的值(字符串)到整数的转换, |
| 并处理无效值或获取失败的情况,确保返回一个有效的非负整数。 |
| |
| Args: |
| db (AsyncSession): SQLAlchemy 异步数据库会话。 |
| |
| Returns: |
| int: 上下文的 TTL 天数。如果数据库中没有设置、设置无效或获取失败, |
| 则返回在 `app.config` 中定义的 `DEFAULT_CONTEXT_TTL_DAYS`。 |
| 返回值保证是一个非负整数。 |
| """ |
| |
| |
| value_str = await get_setting(db, 'context_ttl_days', str(config.DEFAULT_CONTEXT_TTL_DAYS)) |
| try: |
| |
| val = int(value_str) |
| |
| return max(0, val) |
| except (ValueError, TypeError): |
| |
| |
| logger.warning(f"无效的 TTL 设置值 '{value_str}',将使用默认值 {config.DEFAULT_CONTEXT_TTL_DAYS}") |
| return config.DEFAULT_CONTEXT_TTL_DAYS |
|
|
|
|
| async def set_ttl_days(db: AsyncSession, days: int): |
| """ |
| 在数据库中异步设置上下文 TTL (Time-To-Live) 的天数。 |
| |
| Args: |
| db (AsyncSession): SQLAlchemy 异步数据库会话。 |
| days (int): 要设置的 TTL 天数。必须是一个非负整数。 |
| |
| Raises: |
| ValueError: 如果提供的 `days` 参数不是一个非负整数。 |
| """ |
| |
| if not isinstance(days, int) or days < 0: |
| |
| logger.error(f"尝试设置无效的 TTL 天数: {days}") |
| raise ValueError("TTL 天数必须是非负整数") |
| |
| |
| await set_setting(db, 'context_ttl_days', str(days)) |
|
|