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}")
|