358 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
VWED.data 模块 - 数据存储和缓存
"""
import json
import uuid
from typing import Dict, Any, Optional, List
from sqlalchemy import text
from utils.logger import get_logger
class VWEDDataModule:
"""VWED.data 模块 - 数据存储和缓存"""
def __init__(self, script_id: str):
self.script_id = script_id
self._storage: Dict[str, Any] = {}
self.logger = get_logger(f"services.online_script.data_module.{script_id}")
def get(self, key: str, default=None):
"""获取数据"""
return self._storage.get(key, default)
def set(self, key: str, value: Any):
"""设置数据"""
self._storage[key] = value
def delete(self, key: str):
"""删除数据"""
if key in self._storage:
del self._storage[key]
def clear(self):
"""清空所有数据"""
self._storage.clear()
def keys(self):
"""获取所有键"""
return list(self._storage.keys())
def has(self, key: str) -> bool:
"""检查键是否存在"""
return key in self._storage
async def get_cache_param(self, key: str) -> Optional[str]:
"""
获取缓存数据
Args:
key: 缓存键
Returns:
str | None: 字符串类型JSON格式。如果数据不存在将返回None
Raises:
Exception: 本方法会抛出异常
"""
from sqlalchemy import select
from data.models.datacachesplit import VWEDDataCacheSplit
from data.session import get_async_session
async with get_async_session() as session:
query = select(VWEDDataCacheSplit).where(
VWEDDataCacheSplit.data_key == key,
VWEDDataCacheSplit.is_deleted == 0
)
result = await session.execute(query)
record = result.scalar_one_or_none()
if not record or not record.data_value:
return None
return record.data_value
async def put_cache_param(self, key: str, value: str) -> None:
"""
缓存数据
将参数中的key和value键值对在系统中进行缓存缓存的数据将持久化
之后可通过get_cache_param方法根据key获取对应的value。
Args:
key: 缓存键
value: 缓存值。如果要缓存一个对象需要先序列化为JSON字符串
Returns:
None
Note:
本方法不会抛出异常
"""
try:
from sqlalchemy import select, insert, update
from data.models.datacachesplit import VWEDDataCacheSplit
from data.session import get_async_session
async with get_async_session() as session:
# 查询是否已存在相同key的记录且未被删除
query = select(VWEDDataCacheSplit).where(
VWEDDataCacheSplit.data_key == key,
VWEDDataCacheSplit.is_deleted == 0
)
result = await session.execute(query)
existing_active_record = result.scalar_one_or_none()
# 如果有未删除的记录,则更新它
if existing_active_record:
stmt = update(VWEDDataCacheSplit).where(
VWEDDataCacheSplit.id == existing_active_record.id
).values(
data_value=json.dumps(value) if not isinstance(value, str) else value,
)
await session.execute(stmt)
else:
# 如果没有未删除的记录,创建新记录
stmt = insert(VWEDDataCacheSplit).values(
id=str(uuid.uuid4()),
data_key=key,
data_value=json.dumps(value) if not isinstance(value, str) else value,
is_deleted=0
)
await session.execute(stmt)
await session.commit()
except Exception:
# 本方法不会抛出异常,静默处理
pass
async def clear_cache_param(self, key: str) -> None:
"""
删除缓存数据
Args:
key: 缓存键
Returns:
None
Note:
本方法不抛出异常
"""
try:
from sqlalchemy import update, select
from data.models.datacachesplit import VWEDDataCacheSplit
from data.session import get_async_session
async with get_async_session() as session:
# 首先检查是否存在该条记录
query = select(VWEDDataCacheSplit).where(
VWEDDataCacheSplit.data_key == key,
VWEDDataCacheSplit.is_deleted == 0
)
result = await session.execute(query)
exists = result.scalar_one_or_none() is not None
if exists:
# 软删除具有指定键的记录
stmt = update(VWEDDataCacheSplit).where(
VWEDDataCacheSplit.data_key == key,
VWEDDataCacheSplit.is_deleted == 0
).values(
is_deleted=1,
)
await session.execute(stmt)
await session.commit()
except Exception:
# 本方法不抛出异常,静默处理
pass
async def get_all_cache_params(self) -> str:
"""
获取所有的缓存数据
获取缓存块的全部缓存数据并以JSON格式返回。
Returns:
str: 字符串类型JSON格式。如果数据不存在将返回空字符串
Raises:
Exception: 本方法会抛出异常
"""
from sqlalchemy import select
from data.models.datacachesplit import VWEDDataCacheSplit
from data.session import get_async_session
async with get_async_session() as session:
query = select(VWEDDataCacheSplit).where(
VWEDDataCacheSplit.is_deleted == 0
)
result = await session.execute(query)
records = result.scalars().all()
if not records:
return ""
# 构建缓存数据字典
cache_data = {}
for record in records:
try:
# 尝试将值解析为JSON如果失败则使用原始字符串
cache_data[record.data_key] = json.loads(record.data_value)
except (json.JSONDecodeError, TypeError):
cache_data[record.data_key] = record.data_value
return json.dumps(cache_data, ensure_ascii=False)
def get_settings_config_value(self, key: str) -> str:
"""获取 settings.py 的配置
Args:
key: 配置字段名
Returns:
str: JSON 字符串格式的配置值
"""
try:
from config.settings import settings
# 使用 getattr 获取配置值
config_value = getattr(settings, key, None)
if config_value is not None:
return json.dumps(config_value, ensure_ascii=False)
self.logger.warning(f"[{self.script_id}] 未找到配置项: {key}")
return "{}"
except Exception as e:
self.logger.error(f"[{self.script_id}] 获取设置配置失败: {e}")
return "{}"
async def jdbc_execute_sql(self, sql: str) -> bool:
"""执行 SQL 语句
通过 SQL 命令操作 RDS 系统数据库,可用于创建自定义数据表。
Args:
sql: SQL 语句
Returns:
bool: True表示执行成功False表示执行失败
"""
try:
from data.session import get_async_session
async with get_async_session() as session:
await session.execute(text(sql))
await session.commit()
self.logger.info(f"[{self.script_id}] SQL执行成功: {sql[:100]}...")
return True
except Exception as e:
self.logger.error(f"[{self.script_id}] SQL执行失败: {e}, SQL: {sql}")
return False
async def jdbc_query(self, sql: str) -> str:
"""使用 SQL 查询数据
Args:
sql: SQL 查询语句
Returns:
str: JSON格式的查询结果
"""
try:
from data.session import get_async_session
async with get_async_session() as session:
result = await session.execute(text(sql))
# 获取列名
columns = list(result.keys()) if result.keys() else []
# 获取所有行数据
rows = result.fetchall()
# 转换为字典列表
data = []
for row in rows:
row_dict = {}
for i, col in enumerate(columns):
row_dict[col] = str(row[i]) if row[i] is not None else None
data.append(row_dict)
return json.dumps(data, ensure_ascii=False)
except Exception as e:
self.logger.error(f"[{self.script_id}] SQL查询失败: {e}, SQL: {sql}")
return "[]"
async def jdbc_insert_or_update(self, sql: str, *sql_params: Any) -> int:
"""更新数据
更新数据记录,支持动态参数。
Args:
sql: SQL语句支持参数占位符 ?
sql_params: 动态参数列表
Returns:
int: 0表示更新失败大于0表示更新的记录数目
"""
try:
from data.session import get_async_session
# 将 ? 占位符转换为 :param_n 格式
param_sql = sql
params = {}
for i, param in enumerate(sql_params):
param_key = f"param_{i}"
param_sql = param_sql.replace("?", f":{param_key}", 1)
params[param_key] = param
async with get_async_session() as session:
result = await session.execute(text(param_sql), params)
await session.commit()
affected_rows = result.rowcount
self.logger.info(f"[{self.script_id}] SQL更新成功影响行数: {affected_rows}")
return affected_rows
except Exception as e:
self.logger.error(f"[{self.script_id}] SQL更新失败: {e}, SQL: {sql}, 参数: {sql_params}")
return 0
async def jdbc_query_count(self, sql: str) -> Optional[int]:
"""查询数据数量
执行自定义 SQL 语句查询满足条件数据的数量。
注意:只能使用 select count 语句。
Args:
sql: SQL count查询语句
Returns:
Optional[int]: None表示查询失败其他数字表示查询到的记录数目
"""
try:
# 简单验证SQL是否为count查询
sql_lower = sql.lower().strip()
if not sql_lower.startswith("select count"):
self.logger.error(f"[{self.script_id}] 只允许使用 select count 语句: {sql}")
return None
from data.session import get_async_session
async with get_async_session() as session:
result = await session.execute(text(sql))
count = result.scalar()
return int(count) if count is not None else 0
except Exception as e:
self.logger.error(f"[{self.script_id}] Count查询失败: {e}, SQL: {sql}")
return None