358 lines
12 KiB
Python
Raw Normal View History

2025-09-25 10:52:52 +08:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
VWED.data 模块 - 数据存储和缓存
"""
import json
import uuid
2025-09-29 09:35:08 +08:00
from typing import Dict, Any, Optional, List
from sqlalchemy import text
from utils.logger import get_logger
2025-09-25 10:52:52 +08:00
class VWEDDataModule:
"""VWED.data 模块 - 数据存储和缓存"""
def __init__(self, script_id: str):
self.script_id = script_id
self._storage: Dict[str, Any] = {}
2025-09-29 09:35:08 +08:00
self.logger = get_logger(f"services.online_script.data_module.{script_id}")
2025-09-25 10:52:52 +08:00
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
2025-09-29 09:35:08 +08:00
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