""" 数据库操作: 保存热点条目、写抓取日志、统计查询 """ from datetime import datetime from appPublic.uniqueID import getID from sqlor.dbpools import DBPools def now_str(): return datetime.now().strftime('%Y-%m-%d %H:%M:%S') async def get_enabled_sources(): """获取所有启用的来源""" db = DBPools() async with db.sqlorContext('sage') as sor: sources = await sor.R('hotspot_source', {}) return [s for s in sources if s.get('enabled') == '1'] async def save_items(items, source_id, fetch_time=None): """批量保存热点条目 (去重)""" if fetch_time is None: fetch_time = now_str() new_count = 0 dup_count = 0 db = DBPools() async with db.sqlorContext('sage') as sor: for item in items: url = item.get('url', '') if url: existing = await sor.sqlExe( "select id from hotspot_item where url=${u}$ limit 1", {'u': url}) if existing: dup_count += 1 continue await sor.C('hotspot_item', { 'id': getID(), 'source_id': source_id, 'title': item.get('title', '')[:500], 'url': (url or '')[:2000], 'summary': item.get('summary', '')[:1000], 'heat_score': item.get('heat_score', 100), 'heat_velocity': 0, 'publish_time': fetch_time, 'first_seen': fetch_time, 'last_updated': fetch_time, 'status': 'emerging', 'category': item.get('category', ''), 'tags': item.get('tags', ''), 'engagement_count': item.get('engagement_count', 0), 'comment_count': item.get('comment_count', 0), 'share_count': item.get('share_count', 0), 'sentiment': 'neutral', }) new_count += 1 return new_count, dup_count async def update_source_last_fetch(source_id): db = DBPools() async with db.sqlorContext('sage') as sor: await sor.U('hotspot_source', {'id': source_id, 'last_fetch': now_str()}) async def write_fetch_log(source_id, start_time, elapsed_ms, status, items_total, items_new, items_duplicate, error_msg='', response_code=0, response_size_bytes=0): db = DBPools() async with db.sqlorContext('sage') as sor: await sor.C('hotspot_fetch_log', { 'id': getID(), 'source_id': source_id, 'start_time': start_time, 'end_time': now_str(), 'duration_ms': elapsed_ms, 'status': status, 'items_total': items_total, 'items_new': items_new, 'items_duplicate': items_duplicate, 'error_msg': error_msg[:1000] if error_msg else '', 'response_code': response_code, 'response_size_bytes': response_size_bytes, }) async def get_stats(): """全维度统计""" from datetime import timedelta now = datetime.now() day_ago = (now - timedelta(hours=24)).strftime('%Y-%m-%d %H:%M:%S') db = DBPools() async with db.sqlorContext('sage') as sor: items = await sor.R('hotspot_item', {'order': 'heat_score desc'}) sources = await sor.R('hotspot_source', {}) logs = await sor.sqlExe( "select * from hotspot_fetch_log where start_time >= ${t}$", {'t': day_ago}) status_count = {'emerging': 0, 'rising': 0, 'hot': 0, 'cooling': 0, 'expired': 0} for item in items: s = item.get('status', 'emerging') status_count[s] = status_count.get(s, 0) + 1 return { 'total': len(items), **status_count, 'sources': sum(1 for s in sources if s.get('enabled') == '1'), 'failures': sum(1 for log in logs if log.get('status') == 'failed'), }