From 9c73efe1eb2daa6c03d0954ae5a4d68b43a3eb5c Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Mon, 13 Apr 2026 15:48:43 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E5=BC=BA=E5=88=B6=E8=BD=AC=E6=8D=A2=20+=20ca?= =?UTF-8?q?melCase=20=E9=98=B2=E5=BE=A1=E6=80=A7=E8=BD=AC=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- algorithms.py | 46 +++++++++++++++++++++++++++++++++++++++++++++ core/config_sync.py | 9 +++++++++ 2 files changed, 55 insertions(+) diff --git a/algorithms.py b/algorithms.py index 3e15344..09e1cc6 100644 --- a/algorithms.py +++ b/algorithms.py @@ -1551,6 +1551,30 @@ class NonMotorVehicleParkingAlgorithm(BaseAlgorithm): class AlgorithmManager: + # 参数类型定义,用于三级合并后的类型强制转换 + _PARAM_TYPES = { + "leave_post": { + "confirm_on_duty_sec": int, "confirm_off_duty_sec": int, + "confirm_leave_sec": int, "leave_countdown_sec": int, "cooldown_sec": int, + }, + "intrusion": { + "cooldown_seconds": int, "confirm_seconds": int, + "confirm_intrusion_seconds": int, "confirm_clear_seconds": int, + }, + "illegal_parking": { + "confirm_vehicle_sec": int, "parking_countdown_sec": int, + "confirm_clear_sec": int, "cooldown_sec": int, + }, + "vehicle_congestion": { + "count_threshold": int, "confirm_congestion_sec": int, + "confirm_clear_sec": int, "cooldown_sec": int, + }, + "non_motor_vehicle_parking": { + "confirm_vehicle_sec": int, "parking_countdown_sec": int, + "confirm_clear_sec": int, "cooldown_sec": int, + }, + } + def __init__(self, working_hours: Optional[List[Dict]] = None): self.algorithms: Dict[str, Dict[str, Any]] = {} self.working_hours = working_hours or [] @@ -1609,6 +1633,19 @@ class AlgorithmManager: self._global_params = global_params_map or {} logger.info(f"全局参数已更新: {list(self._global_params.keys())}") + def _coerce_param_types(self, algorithm_type: str, params: dict) -> dict: + """强制转换参数类型,防止字符串型数字导致算法异常""" + type_map = self._PARAM_TYPES.get(algorithm_type, {}) + for key, expected_type in type_map.items(): + if key in params and params[key] is not None: + try: + if not isinstance(params[key], expected_type): + params[key] = expected_type(params[key]) + except (ValueError, TypeError): + logger.warning(f"参数类型转换失败: {algorithm_type}.{key}={params[key]!r}, 删除使用默认值") + del params[key] + return params + def get_min_alarm_duration(self, algorithm_type: str) -> Optional[int]: """从全局参数获取最小告警持续时间(秒) @@ -1718,6 +1755,9 @@ class AlgorithmManager: else: params = {} + # 强制转换参数类型(防止字符串型数字) + params = self._coerce_param_types(algo_code, params) + if roi_id not in self.algorithms: self.algorithms[roi_id] = {} @@ -1918,6 +1958,9 @@ class AlgorithmManager: params = json.loads(params_str) if isinstance(params_str, str) else params_str algo_code = bind_config.get("algo_code") + # 强制转换参数类型(防止字符串型数字) + params = self._coerce_param_types(algo_code, params) + # 获取现有算法实例 existing_algo = self.algorithms[roi_id][key].get(algo_code) @@ -2105,6 +2148,9 @@ class AlgorithmManager: if params: algo_params.update(params) + # 强制转换参数类型(防止字符串型数字) + algo_params = self._coerce_param_types(algorithm_type, algo_params) + # 从 params 中提取告警等级(前端配置下发) configured_alarm_level = algo_params.get("alarm_level") diff --git a/core/config_sync.py b/core/config_sync.py index f1bd0e4..1ff9090 100644 --- a/core/config_sync.py +++ b/core/config_sync.py @@ -15,6 +15,7 @@ import json import logging import os import platform +import re import socket # 禁用系统代理(Clash 等代理工具会干扰 Redis TCP 长连接) @@ -53,6 +54,12 @@ def _build_keepalive_options(): return opts +def _camel_to_snake(name: str) -> str: + """将 camelCase 转换为 snake_case""" + s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) + return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() + + # ==================== Redis Key 常量 ==================== # 云端 Redis Keys @@ -648,6 +655,8 @@ class ConfigSyncManager: if global_params and isinstance(global_params, dict): for algo_code, params_dict in global_params.items(): if isinstance(params_dict, dict): + # 防御性转换:camelCase → snake_case + params_dict = {_camel_to_snake(k): v for k, v in params_dict.items()} self._db_manager.save_global_params(algo_code, params_dict) logger.info(f"全局参数同步完成: {list(global_params.keys())}")