修改设备端对接
This commit is contained in:
190
talkingq-url/services/card_service.py
Normal file
190
talkingq-url/services/card_service.py
Normal file
@@ -0,0 +1,190 @@
|
||||
import asyncio
|
||||
from typing import Optional, Dict
|
||||
import time
|
||||
from sqlalchemy import select, update, insert
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from utils.logger import session_logger
|
||||
from pydantic import BaseModel
|
||||
from database.models import Card as DBCard
|
||||
from services.database_service_base import DatabaseServiceBase
|
||||
|
||||
class Card(BaseModel):
|
||||
card_id: Optional[int] = None
|
||||
card_uuid: str
|
||||
device_id: Optional[str] = None
|
||||
card_name: Optional[str] = None
|
||||
status: int = 0
|
||||
total_swaps: int = 0
|
||||
created_at: Optional[float] = None
|
||||
updated_at: Optional[float] = None
|
||||
|
||||
@classmethod
|
||||
def from_db_model(cls, db_model: DBCard):
|
||||
"""从数据库模型创建卡片对象"""
|
||||
return cls(
|
||||
card_id=db_model.card_id,
|
||||
card_uuid=db_model.card_uuid,
|
||||
device_id=db_model.device_id,
|
||||
card_name=db_model.card_name,
|
||||
status=db_model.status,
|
||||
total_swaps=db_model.total_swaps,
|
||||
created_at=db_model.created_at.timestamp() if db_model.created_at else None,
|
||||
updated_at=db_model.updated_at.timestamp() if db_model.updated_at else None
|
||||
)
|
||||
|
||||
class CardService(DatabaseServiceBase):
|
||||
def __init__(self):
|
||||
super().__init__(service_name="card")
|
||||
self.cards: Dict[str, Card] = {}
|
||||
self.lock = asyncio.Lock()
|
||||
|
||||
async def _load_card_from_db(self, card_uuid: str, async_session: AsyncSession) -> Optional[Card]:
|
||||
"""从数据库加载卡片信息"""
|
||||
try:
|
||||
query = select(DBCard).where(DBCard.card_uuid == card_uuid)
|
||||
result = await async_session.execute(query)
|
||||
db_card = result.scalar_one_or_none()
|
||||
|
||||
if db_card:
|
||||
card = Card.from_db_model(db_card)
|
||||
async with self.lock:
|
||||
self.cards[card_uuid] = card
|
||||
return card
|
||||
return None
|
||||
except Exception as e:
|
||||
session_logger.error("card", "service", f"从数据库加载卡片失败: {str(e)}")
|
||||
return None
|
||||
|
||||
async def _save_card_to_db(self, card: Card, async_session: AsyncSession):
|
||||
"""保存卡片信息到数据库"""
|
||||
try:
|
||||
query = select(DBCard).where(DBCard.card_uuid == card.card_uuid)
|
||||
result = await async_session.execute(query)
|
||||
existing_card = result.scalar_one_or_none()
|
||||
|
||||
if existing_card:
|
||||
stmt = update(DBCard).where(
|
||||
DBCard.card_uuid == card.card_uuid
|
||||
).values(
|
||||
device_id=card.device_id,
|
||||
card_name=card.card_name,
|
||||
status=card.status,
|
||||
total_swaps=card.total_swaps
|
||||
)
|
||||
else:
|
||||
stmt = insert(DBCard).values(
|
||||
card_uuid=card.card_uuid,
|
||||
device_id=card.device_id,
|
||||
card_name=card.card_name,
|
||||
status=card.status,
|
||||
total_swaps=card.total_swaps
|
||||
)
|
||||
|
||||
await async_session.execute(stmt)
|
||||
await async_session.commit()
|
||||
|
||||
session_logger.info("card", "service", f"卡片已保存到数据库: {card.card_uuid}")
|
||||
except Exception as e:
|
||||
await async_session.rollback()
|
||||
session_logger.error("card", "service", f"保存卡片到数据库失败: {str(e)}")
|
||||
raise
|
||||
|
||||
async def get_card_by_uuid(self, card_uuid: str, force_refresh: bool = False) -> Optional[Card]:
|
||||
"""根据UUID获取卡片信息"""
|
||||
await self._init_database()
|
||||
|
||||
card = None
|
||||
|
||||
if not force_refresh:
|
||||
async with self.lock:
|
||||
card = self.cards.get(card_uuid)
|
||||
|
||||
if not card:
|
||||
db_session = await self.db_manager.get_session()
|
||||
try:
|
||||
card = await self._load_card_from_db(card_uuid, db_session)
|
||||
finally:
|
||||
await db_session.close()
|
||||
|
||||
return card
|
||||
|
||||
async def get_card_by_device_id(self, device_id: str) -> list[Card]:
|
||||
"""根据设备ID获取卡片列表"""
|
||||
await self._init_database()
|
||||
|
||||
db_session = await self.db_manager.get_session()
|
||||
try:
|
||||
query = select(DBCard).where(DBCard.device_id == device_id)
|
||||
result = await db_session.execute(query)
|
||||
db_cards = result.scalars().all()
|
||||
|
||||
cards = []
|
||||
for db_card in db_cards:
|
||||
card = Card.from_db_model(db_card)
|
||||
async with self.lock:
|
||||
self.cards[card.card_uuid] = card
|
||||
cards.append(card)
|
||||
|
||||
return cards
|
||||
except Exception as e:
|
||||
session_logger.error(device_id, "card", f"根据设备ID获取卡片失败: {str(e)}")
|
||||
return []
|
||||
finally:
|
||||
await db_session.close()
|
||||
|
||||
async def activate_card(self, card_uuid: str, device_id: str, card_name: Optional[str] = None) -> Card:
|
||||
"""激活卡片并绑定到设备"""
|
||||
await self._init_database()
|
||||
|
||||
db_session = await self.db_manager.get_session()
|
||||
try:
|
||||
# 检查卡片是否已存在
|
||||
existing_card = await self.get_card_by_uuid(card_uuid)
|
||||
|
||||
if existing_card:
|
||||
# 更新现有卡片
|
||||
existing_card.device_id = device_id
|
||||
existing_card.card_name = card_name
|
||||
existing_card.status = 1 # 激活状态
|
||||
await self._save_card_to_db(existing_card, db_session)
|
||||
session_logger.info(device_id, "card", f"卡片已激活并绑定到设备: {card_uuid}")
|
||||
return existing_card
|
||||
else:
|
||||
# 创建新卡片
|
||||
new_card = Card(
|
||||
card_uuid=card_uuid,
|
||||
device_id=device_id,
|
||||
card_name=card_name,
|
||||
status=1, # 激活状态
|
||||
total_swaps=0
|
||||
)
|
||||
await self._save_card_to_db(new_card, db_session)
|
||||
session_logger.info(device_id, "card", f"新卡片已创建并激活: {card_uuid}")
|
||||
return new_card
|
||||
finally:
|
||||
await db_session.close()
|
||||
|
||||
async def increment_swap_count(self, card_uuid: str) -> Optional[Card]:
|
||||
"""增加卡片交换次数"""
|
||||
await self._init_database()
|
||||
|
||||
card = await self.get_card_by_uuid(card_uuid)
|
||||
if card:
|
||||
card.total_swaps += 1
|
||||
db_session = await self.db_manager.get_session()
|
||||
try:
|
||||
await self._save_card_to_db(card, db_session)
|
||||
session_logger.info("card", "service", f"卡片交换次数已增加: {card_uuid}, 总次数: {card.total_swaps}")
|
||||
return card
|
||||
finally:
|
||||
await db_session.close()
|
||||
return None
|
||||
|
||||
async def check_card_ownership(self, card_uuid: str, device_id: str) -> bool:
|
||||
"""检查卡片是否属于指定设备"""
|
||||
card = await self.get_card_by_uuid(card_uuid)
|
||||
if card and card.device_id == device_id:
|
||||
return True
|
||||
return False
|
||||
|
||||
card_service = CardService()
|
||||
29
talkingq-url/services/device_target_cache.py
Normal file
29
talkingq-url/services/device_target_cache.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import asyncio
|
||||
from typing import Dict, Optional
|
||||
|
||||
|
||||
class DeviceTargetCache:
|
||||
def __init__(self):
|
||||
self.targets: Dict[str, str] = {}
|
||||
self.lock = asyncio.Lock()
|
||||
|
||||
async def get_target(self, device_id: str) -> Optional[str]:
|
||||
async with self.lock:
|
||||
return self.targets.get(device_id)
|
||||
|
||||
async def set_target(self, device_id: str, target_device_id: str):
|
||||
async with self.lock:
|
||||
self.targets[device_id] = target_device_id
|
||||
|
||||
async def remove_target(self, device_id: str):
|
||||
async with self.lock:
|
||||
if device_id in self.targets:
|
||||
del self.targets[device_id]
|
||||
|
||||
async def get_all_targets(self) -> list:
|
||||
async with self.lock:
|
||||
return list(self.targets.items())
|
||||
|
||||
|
||||
# 单例实例
|
||||
device_target_cache = DeviceTargetCache()
|
||||
31
talkingq-url/services/offline_audio_cache.py
Normal file
31
talkingq-url/services/offline_audio_cache.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import asyncio
|
||||
from typing import Dict, List
|
||||
|
||||
|
||||
class OfflineAudioCache:
|
||||
def __init__(self):
|
||||
self.offline_audio: Dict[str, List[str]] = {}
|
||||
self.lock = asyncio.Lock()
|
||||
|
||||
async def add_audio_url(self, device_id: str, audio_url: str):
|
||||
async with self.lock:
|
||||
if device_id not in self.offline_audio:
|
||||
self.offline_audio[device_id] = []
|
||||
self.offline_audio[device_id].append(audio_url)
|
||||
|
||||
async def get_audio_urls(self, device_id: str) -> List[str]:
|
||||
async with self.lock:
|
||||
return self.offline_audio.get(device_id, [])
|
||||
|
||||
async def clear_audio_urls(self, device_id: str):
|
||||
async with self.lock:
|
||||
if device_id in self.offline_audio:
|
||||
del self.offline_audio[device_id]
|
||||
|
||||
async def has_pending_audio(self, device_id: str) -> bool:
|
||||
async with self.lock:
|
||||
return device_id in self.offline_audio and len(self.offline_audio[device_id]) > 0
|
||||
|
||||
|
||||
# 单例实例
|
||||
offline_audio_cache = OfflineAudioCache()
|
||||
35
talkingq-url/services/target_audio_cache.py
Normal file
35
talkingq-url/services/target_audio_cache.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import asyncio
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
|
||||
class TargetAudioCache:
|
||||
def __init__(self):
|
||||
self.audio_data_cache: Dict[str, List[bytes]] = {}
|
||||
self.lock = asyncio.Lock()
|
||||
|
||||
async def add_audio_data(self, device_id: str, audio_data: bytes):
|
||||
async with self.lock:
|
||||
if device_id not in self.audio_data_cache:
|
||||
self.audio_data_cache[device_id] = []
|
||||
self.audio_data_cache[device_id].append(audio_data)
|
||||
|
||||
async def get_audio_data(self, device_id: str) -> Optional[bytes]:
|
||||
async with self.lock:
|
||||
if device_id not in self.audio_data_cache:
|
||||
return None
|
||||
# 合并所有音频数据
|
||||
combined_audio = b''.join(self.audio_data_cache[device_id])
|
||||
return combined_audio
|
||||
|
||||
async def clear_audio_data(self, device_id: str):
|
||||
async with self.lock:
|
||||
if device_id in self.audio_data_cache:
|
||||
del self.audio_data_cache[device_id]
|
||||
|
||||
async def has_cached_audio(self, device_id: str) -> bool:
|
||||
async with self.lock:
|
||||
return device_id in self.audio_data_cache and len(self.audio_data_cache[device_id]) > 0
|
||||
|
||||
|
||||
# 单例实例
|
||||
target_audio_cache = TargetAudioCache()
|
||||
Reference in New Issue
Block a user