feat: 实现配置热更新机制
数据库扩展: - roi_configs 新增算法参数字段(working_hours, confirm_on_duty_sec等) - 新增 config_update_log 表记录配置变更日志 Redis缓存: - ROI/摄像头配置缓存到 Redis(TTL 1小时) - sync_all_to_redis() 批量同步配置 - notify_config_change() 发布配置变更通知 热更新: - AlgorithmManager 订阅 Redis config_update 频道 - load_from_redis() 从 Redis 加载算法参数 - reload_algorithm() 热更新单个算法 - reload_all_algorithms() 重新加载所有算法 配置模型: - ROIInfo 添加算法参数字段
This commit is contained in:
@@ -162,6 +162,23 @@ class SQLiteManager:
|
||||
alert_cooldown INTEGER DEFAULT 300,
|
||||
enabled BOOLEAN DEFAULT 1,
|
||||
extra_params TEXT,
|
||||
working_hours TEXT,
|
||||
confirm_on_duty_sec INTEGER DEFAULT 10,
|
||||
confirm_leave_sec INTEGER DEFAULT 10,
|
||||
cooldown_sec INTEGER DEFAULT 300,
|
||||
target_class TEXT DEFAULT 'person',
|
||||
updated_at TEXT
|
||||
)
|
||||
""")
|
||||
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS config_update_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
config_type TEXT NOT NULL,
|
||||
config_id TEXT,
|
||||
old_value TEXT,
|
||||
new_value TEXT,
|
||||
updated_by TEXT,
|
||||
updated_at TEXT
|
||||
)
|
||||
""")
|
||||
@@ -469,14 +486,21 @@ class SQLiteManager:
|
||||
cursor.execute("""
|
||||
INSERT OR REPLACE INTO roi_configs (
|
||||
roi_id, camera_id, roi_type, coordinates, algorithm_type,
|
||||
alert_threshold, alert_cooldown, enabled, extra_params, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
alert_threshold, alert_cooldown, enabled, extra_params,
|
||||
working_hours, confirm_on_duty_sec, confirm_leave_sec,
|
||||
cooldown_sec, target_class, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""", (
|
||||
roi_id, camera_id, roi_type, str(coordinates), algorithm_type,
|
||||
kwargs.get('alert_threshold', 3),
|
||||
kwargs.get('alert_cooldown', 300),
|
||||
kwargs.get('enabled', True),
|
||||
str(kwargs.get('extra_params')) if kwargs.get('extra_params') else None,
|
||||
str(kwargs.get('working_hours')) if kwargs.get('working_hours') else None,
|
||||
kwargs.get('confirm_on_duty_sec', 10),
|
||||
kwargs.get('confirm_leave_sec', 10),
|
||||
kwargs.get('cooldown_sec', 300),
|
||||
kwargs.get('target_class', 'person'),
|
||||
now
|
||||
))
|
||||
self._conn.commit()
|
||||
@@ -494,12 +518,19 @@ class SQLiteManager:
|
||||
if row:
|
||||
columns = ['roi_id', 'camera_id', 'roi_type', 'coordinates',
|
||||
'algorithm_type', 'alert_threshold', 'alert_cooldown',
|
||||
'enabled', 'extra_params', 'updated_at']
|
||||
'enabled', 'extra_params', 'working_hours',
|
||||
'confirm_on_duty_sec', 'confirm_leave_sec', 'cooldown_sec',
|
||||
'target_class', 'updated_at']
|
||||
result = dict(zip(columns, row))
|
||||
try:
|
||||
result['coordinates'] = eval(result['coordinates'])
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
if result.get('working_hours'):
|
||||
result['working_hours'] = eval(result['working_hours'])
|
||||
except:
|
||||
pass
|
||||
return result
|
||||
return None
|
||||
except Exception as e:
|
||||
@@ -513,7 +544,9 @@ class SQLiteManager:
|
||||
cursor.execute("SELECT * FROM roi_configs WHERE camera_id = ?", (camera_id,))
|
||||
columns = ['roi_id', 'camera_id', 'roi_type', 'coordinates',
|
||||
'algorithm_type', 'alert_threshold', 'alert_cooldown',
|
||||
'enabled', 'extra_params', 'updated_at']
|
||||
'enabled', 'extra_params', 'working_hours',
|
||||
'confirm_on_duty_sec', 'confirm_leave_sec', 'cooldown_sec',
|
||||
'target_class', 'updated_at']
|
||||
results = []
|
||||
for row in cursor.fetchall():
|
||||
r = dict(zip(columns, row))
|
||||
@@ -521,6 +554,11 @@ class SQLiteManager:
|
||||
r['coordinates'] = eval(r['coordinates'])
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
if r.get('working_hours'):
|
||||
r['working_hours'] = eval(r['working_hours'])
|
||||
except:
|
||||
pass
|
||||
results.append(r)
|
||||
return results
|
||||
except Exception as e:
|
||||
@@ -534,7 +572,9 @@ class SQLiteManager:
|
||||
cursor.execute("SELECT * FROM roi_configs ORDER BY camera_id, roi_id")
|
||||
columns = ['roi_id', 'camera_id', 'roi_type', 'coordinates',
|
||||
'algorithm_type', 'alert_threshold', 'alert_cooldown',
|
||||
'enabled', 'extra_params', 'updated_at']
|
||||
'enabled', 'extra_params', 'working_hours',
|
||||
'confirm_on_duty_sec', 'confirm_leave_sec', 'cooldown_sec',
|
||||
'target_class', 'updated_at']
|
||||
results = []
|
||||
for row in cursor.fetchall():
|
||||
r = dict(zip(columns, row))
|
||||
@@ -542,6 +582,11 @@ class SQLiteManager:
|
||||
r['coordinates'] = eval(r['coordinates'])
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
if r.get('working_hours'):
|
||||
r['working_hours'] = eval(r['working_hours'])
|
||||
except:
|
||||
pass
|
||||
results.append(r)
|
||||
return results
|
||||
except Exception as e:
|
||||
@@ -558,6 +603,58 @@ class SQLiteManager:
|
||||
except Exception as e:
|
||||
logger.error(f"删除ROI配置失败: {e}")
|
||||
return False
|
||||
|
||||
def log_config_update(
|
||||
self,
|
||||
config_type: str,
|
||||
config_id: Optional[str],
|
||||
old_value: Any,
|
||||
new_value: Any,
|
||||
updated_by: str = "system"
|
||||
):
|
||||
"""记录配置更新日志"""
|
||||
try:
|
||||
cursor = self._conn.cursor()
|
||||
now = datetime.now().isoformat()
|
||||
cursor.execute("""
|
||||
INSERT INTO config_update_log (
|
||||
config_type, config_id, old_value, new_value, updated_by, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?)
|
||||
""", (
|
||||
config_type,
|
||||
config_id,
|
||||
str(old_value) if old_value else None,
|
||||
str(new_value) if new_value else None,
|
||||
updated_by,
|
||||
now
|
||||
))
|
||||
self._conn.commit()
|
||||
logger.info(f"配置更新日志已记录: {config_type}/{config_id}")
|
||||
except Exception as e:
|
||||
logger.error(f"记录配置更新日志失败: {e}")
|
||||
|
||||
def get_config_update_log(
|
||||
self,
|
||||
config_type: Optional[str] = None,
|
||||
limit: int = 100
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""获取配置更新日志"""
|
||||
try:
|
||||
cursor = self._conn.cursor()
|
||||
query = "SELECT * FROM config_update_log WHERE 1=1"
|
||||
params = []
|
||||
if config_type:
|
||||
query += " AND config_type = ?"
|
||||
params.append(config_type)
|
||||
query += " ORDER BY id DESC LIMIT ?"
|
||||
params.append(limit)
|
||||
cursor.execute(query, params)
|
||||
columns = ['id', 'config_type', 'config_id', 'old_value', 'new_value',
|
||||
'updated_by', 'updated_at']
|
||||
return [dict(zip(columns, row)) for row in cursor.fetchall()]
|
||||
except Exception as e:
|
||||
logger.error(f"获取配置更新日志失败: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def get_sqlite_manager() -> SQLiteManager:
|
||||
|
||||
Reference in New Issue
Block a user