95 lines
2.5 KiB
Python
95 lines
2.5 KiB
Python
"""
|
||
AI Box Sophon Adapter — 告警记录清理
|
||
|
||
当告警记录超过配置的 max_records 时,自动清理最早的记录及其关联的视频文件。
|
||
"""
|
||
import logging
|
||
import os
|
||
import shutil
|
||
|
||
from sqlalchemy import select, func, asc
|
||
|
||
from .models.orm import AlarmRecord
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
async def trim_alarm_records(db_session, upload_root: str, max_records: int) -> int:
|
||
"""
|
||
裁剪告警记录,保留最新的 max_records 条
|
||
|
||
删除超出限制的最早记录,同时清理关联的视频文件目录。
|
||
|
||
Args:
|
||
db_session: SQLAlchemy 异步 session
|
||
upload_root: 上传文件根目录(如 ./data/uploads)
|
||
max_records: 最大保留条数,<= 0 表示不限制
|
||
|
||
Returns:
|
||
实际删除的记录数
|
||
"""
|
||
if max_records <= 0:
|
||
return 0
|
||
|
||
# 查询当前总记录数
|
||
count_result = await db_session.execute(
|
||
select(func.count()).select_from(AlarmRecord)
|
||
)
|
||
total = count_result.scalar() or 0
|
||
|
||
if total <= max_records:
|
||
return 0
|
||
|
||
# 超出数量
|
||
overflow = total - max_records
|
||
|
||
# 查询最早的 overflow 条记录(按 created_at 升序)
|
||
stmt = (
|
||
select(AlarmRecord)
|
||
.order_by(asc(AlarmRecord.created_at)) # type: ignore
|
||
.limit(overflow)
|
||
)
|
||
result = await db_session.execute(stmt)
|
||
old_records = result.scalars().all()
|
||
|
||
deleted_count = 0
|
||
for record in old_records:
|
||
# 清理关联的视频文件目录
|
||
if record.video_file:
|
||
_delete_video_dir(upload_root, record.video_file)
|
||
|
||
await db_session.delete(record)
|
||
deleted_count += 1
|
||
|
||
await db_session.commit()
|
||
|
||
if deleted_count > 0:
|
||
logger.info(
|
||
f"[AlarmTrim] 已清理 {deleted_count} 条告警记录 "
|
||
f"(保留最新 {max_records} 条,原共 {total} 条)"
|
||
)
|
||
|
||
return deleted_count
|
||
|
||
|
||
def _delete_video_dir(upload_root: str, video_id: str) -> None:
|
||
"""
|
||
删除告警关联的视频文件目录
|
||
|
||
视频存储路径: {upload_root}/aibox_sophon_adapter/{video_id}/
|
||
|
||
Args:
|
||
upload_root: 上传根目录
|
||
video_id: 视频唯一标识(VideoId)
|
||
"""
|
||
video_dir = os.path.join(upload_root, "aibox_sophon_adapter", video_id)
|
||
|
||
if not os.path.isdir(video_dir):
|
||
return
|
||
|
||
try:
|
||
shutil.rmtree(video_dir)
|
||
logger.debug(f"[AlarmTrim] 已删除视频目录: {video_dir}")
|
||
except OSError as e:
|
||
logger.warning(f"[AlarmTrim] 删除视频目录失败 [{video_dir}]: {e}")
|