feat: 重构存储策略为 SQLite

- 新增 config/database.py: SQLite 数据库管理器
  - WAL 模式提升写入性能
  - 异步批量写入队列
  - 滚动清理机制(保留7天)

- 新增 core/storage_manager.py: 图片存储管理
  - 异步保存抓拍图片
  - 本地缓存断网容灾
  - 按日期分目录存储

- 更新 config/settings.py: 添加 SQLite 配置
  - SQLiteConfig 数据类
  - 环境变量支持

- 更新 core/result_reporter.py: 适配新存储
  - 使用 SQLite 替代 MySQL
  - AlertInfo 数据类重构
  - 断网自动缓存到本地
This commit is contained in:
2026-01-30 11:34:51 +08:00
parent 6dc3442cc2
commit ccb021677c
11 changed files with 1120 additions and 874 deletions

View File

@@ -10,7 +10,7 @@ from typing import List, Optional
@dataclass
class DatabaseConfig:
"""数据库配置类"""
"""数据库配置类MySQL - 云端)"""
host: str = "localhost"
port: int = 3306
username: str = "root"
@@ -21,6 +21,17 @@ class DatabaseConfig:
echo: bool = False
@dataclass
class SQLiteConfig:
"""SQLite 配置(边缘侧本地存储)"""
db_path: str = "./data/security_events.db"
image_dir: str = "./data/captures"
retention_days: int = 7
wal_mode: bool = True
batch_size: int = 100
flush_interval: float = 5.0
@dataclass
class RedisConfig:
"""Redis配置类"""
@@ -61,7 +72,7 @@ class VideoStreamConfig:
@dataclass
class InferenceConfig:
"""推理配置类"""
model_path: str = "./models/yolov8s.engine"
model_path: str = "./models/yolo11n.engine"
input_width: int = 480
input_height: int = 480
batch_size: int = 1
@@ -111,7 +122,6 @@ class Settings:
def _load_env_vars(self):
"""从环境变量加载配置"""
# 数据库配置
self.database = DatabaseConfig(
host=os.getenv("DB_HOST", "localhost"),
port=int(os.getenv("DB_PORT", "3306")),
@@ -120,14 +130,19 @@ class Settings:
database=os.getenv("DB_DATABASE", "edge_inference"),
)
# Redis配置
self.sqlite = SQLiteConfig(
db_path=os.getenv("SQLITE_DB_PATH", "./data/security_events.db"),
image_dir=os.getenv("SQLITE_IMAGE_DIR", "./data/captures"),
retention_days=int(os.getenv("SQLITE_RETENTION_DAYS", "7")),
wal_mode=os.getenv("SQLITE_WAL_MODE", "1") == "1",
)
self.redis = RedisConfig(
host=os.getenv("REDIS_HOST", "localhost"),
port=int(os.getenv("REDIS_PORT", "6379")),
password=os.getenv("REDIS_PASSWORD"),
)
# MQTT配置
self.mqtt = MQTTConfig(
broker_host=os.getenv("MQTT_BROKER_HOST", "localhost"),
broker_port=int(os.getenv("MQTT_BROKER_PORT", "1883")),
@@ -136,13 +151,11 @@ class Settings:
password=os.getenv("MQTT_PASSWORD"),
)
# 视频流配置
self.video_stream = VideoStreamConfig(
default_fps=int(os.getenv("VIDEO_DEFAULT_FPS", "5")),
reconnect_max_attempts=int(os.getenv("VIDEO_RECONNECT_ATTEMPTS", "5")),
)
# 推理配置
self.inference = InferenceConfig(
model_path=os.getenv("MODEL_PATH", "./models/yolo11n.engine"),
input_width=int(os.getenv("INPUT_WIDTH", "480")),
@@ -153,13 +166,11 @@ class Settings:
nms_threshold=float(os.getenv("NMS_THRESHOLD", "0.45")),
)
# 日志配置
self.log_level = os.getenv("LOG_LEVEL", "INFO")
self.log_dir = os.getenv("LOG_DIR", "./logs")
self.log_file_max_size = int(os.getenv("LOG_FILE_MAX_SIZE", "10485760")) # 10MB
self.log_file_max_size = int(os.getenv("LOG_FILE_MAX_SIZE", "10485760"))
self.log_file_backup_count = int(os.getenv("LOG_FILE_BACKUP_COUNT", "5"))
# 工作时间配置
self.working_hours = self._parse_working_hours()
def _parse_working_hours(self) -> List[dict]: