[deploy] PyMySQL 替换 + Tailscale 组网 + config.yaml + 路径修复
- db_layer.py: mysql-connector-python(19MB) → PyMySQL(45KB), 去掉连接池 - config_loader.py: 修复路径解析 (2级→3级 dirname), FAM-Core + FAM-Edge 均修复 - Tailscale 组网完成: Oracle=100.74.137.126, NAS=100.70.234.39, 互通验证通过 - NAS Python 依赖: flask/gunicorn/pymysql/PyYAML 全部安装成功 - NAS 代码部署: /volume1/web/sentinel-home-ai/, FAM-Core 7 模块导入 PASS - NAS DB 连接验证: PyMySQL → MariaDB 10.11.11, 6 张表可见, PASS - config.yaml 创建: FAM-Core + FAM-Edge + FAM-UI (Tailscale IP + 密码) - requirements.txt: mysql-connector-python → PyMySQL
This commit is contained in:
@@ -24,7 +24,7 @@ def load_config(config_path=None):
|
||||
"""加载 YAML 配置文件,自动解析 ${ENV_VAR} 引用"""
|
||||
if config_path is None:
|
||||
config_path = os.path.join(
|
||||
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
|
||||
'config', 'config.yaml'
|
||||
)
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"""
|
||||
数据库访问层 - MariaDB 连接管理与 CRUD 操作
|
||||
使用 PyMySQL (纯 Python, ~45KB) 连接 MariaDB 服务器
|
||||
"""
|
||||
import json
|
||||
import mysql.connector
|
||||
from mysql.connector import pooling
|
||||
import pymysql
|
||||
from datetime import datetime
|
||||
from typing import Optional, List, Dict, Any
|
||||
|
||||
@@ -13,7 +13,6 @@ from .logger import setup_logger
|
||||
logger = setup_logger('fam-core.db')
|
||||
|
||||
_config = None
|
||||
_pool = None
|
||||
|
||||
|
||||
def get_config():
|
||||
@@ -23,29 +22,22 @@ def get_config():
|
||||
return _config
|
||||
|
||||
|
||||
def get_pool():
|
||||
"""获取数据库连接池(单例)"""
|
||||
global _pool
|
||||
if _pool is None:
|
||||
cfg = get_config().get('database', {})
|
||||
_pool = pooling.MySQLConnectionPool(
|
||||
host=cfg.get('host', '127.0.0.1'),
|
||||
port=cfg.get('port', 3306),
|
||||
user=cfg.get('user', 'root'),
|
||||
password=cfg.get('password', ''),
|
||||
database=cfg.get('database', 'sentinel_home_ai'),
|
||||
charset='utf8mb4',
|
||||
collation='utf8mb4_unicode_ci',
|
||||
pool_name='fam_core_pool',
|
||||
pool_size=5,
|
||||
autocommit=False
|
||||
)
|
||||
return _pool
|
||||
|
||||
|
||||
def get_conn():
|
||||
"""从连接池获取一个连接"""
|
||||
return get_pool().get_connection()
|
||||
"""获取数据库连接(单 worker gunicorn,无需连接池)"""
|
||||
cfg = get_config().get('database', {})
|
||||
kwargs = dict(
|
||||
host=cfg.get('host', '127.0.0.1'),
|
||||
port=cfg.get('port', 3306),
|
||||
user=cfg.get('user', 'root'),
|
||||
password=cfg.get('password', ''),
|
||||
database=cfg.get('database', 'sentinel_home_ai'),
|
||||
charset='utf8mb4',
|
||||
autocommit=False
|
||||
)
|
||||
unix_socket = cfg.get('unix_socket')
|
||||
if unix_socket:
|
||||
kwargs['unix_socket'] = unix_socket
|
||||
return pymysql.connect(**kwargs)
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -73,7 +65,7 @@ def get_pending_tasks(limit=10) -> List[Dict]:
|
||||
"""获取待处理任务"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||
cursor.execute(
|
||||
"SELECT * FROM process_tasks WHERE status = 'PENDING' ORDER BY created_at ASC LIMIT %s",
|
||||
(limit,)
|
||||
@@ -87,7 +79,7 @@ def get_tasks_by_status(status: str, limit=10) -> List[Dict]:
|
||||
"""按状态获取任务"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||
cursor.execute(
|
||||
"SELECT * FROM process_tasks WHERE status = %s ORDER BY created_at ASC LIMIT %s",
|
||||
(status, limit)
|
||||
@@ -130,7 +122,7 @@ def get_task(task_id: int) -> Optional[Dict]:
|
||||
"""获取单个任务"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||
cursor.execute("SELECT * FROM process_tasks WHERE task_id = %s", (task_id,))
|
||||
return cursor.fetchone()
|
||||
finally:
|
||||
@@ -207,7 +199,7 @@ def query_event_details(person: str, queried_date: str) -> List[Dict]:
|
||||
"""查询某人在某天的事件明细"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||
cursor.execute(
|
||||
"""SELECT frame_timestamp, camera_name, person, action, clothing,
|
||||
is_attention_event
|
||||
@@ -225,7 +217,7 @@ def get_recent_events(limit=20, offset=0, date_filter=None) -> List[Dict]:
|
||||
"""获取事件列表(分页 + 日期筛选)"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||
if date_filter:
|
||||
cursor.execute(
|
||||
"""SELECT me.event_id, me.task_id, me.event_start_time, me.event_end_time,
|
||||
@@ -281,7 +273,7 @@ def get_chat_history(limit=20, date_filter=None, person_filter=None) -> List[Dic
|
||||
"""获取对话历史"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||
conditions = []
|
||||
params = []
|
||||
if date_filter:
|
||||
@@ -326,7 +318,7 @@ def get_unnamed_members() -> List[Dict]:
|
||||
"""获取未命名成员列表"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||
cursor.execute(
|
||||
"""SELECT fm.abstract_label, fm.feature_description, fm.first_seen_at,
|
||||
(SELECT COUNT(*) FROM event_details ed WHERE ed.person = fm.abstract_label) AS event_count
|
||||
@@ -343,7 +335,7 @@ def get_all_members(include_named=True, include_unnamed=True) -> List[Dict]:
|
||||
"""获取所有成员"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||
conditions = []
|
||||
if include_named and include_unnamed:
|
||||
pass # 全部
|
||||
@@ -420,7 +412,7 @@ def get_known_members_context() -> str:
|
||||
"""获取已命名+未命名成员清单,用于注入 VLM Prompt"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||
cursor.execute(
|
||||
"SELECT abstract_label, real_name, feature_description FROM family_members WHERE is_active = TRUE"
|
||||
)
|
||||
@@ -446,7 +438,7 @@ def get_compute_provider_stats() -> List[Dict]:
|
||||
"""获取 compute_provider 分布统计"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||
cursor.execute(
|
||||
"""SELECT
|
||||
JSON_UNQUOTE(JSON_EXTRACT(item, '$')) AS provider,
|
||||
|
||||
Reference in New Issue
Block a user