This commit is contained in:
QuangMinh_123
2026-05-27 13:50:27 +07:00
parent 7aebcf9567
commit 2683cdb882
30 changed files with 2091 additions and 17 deletions

View File

@@ -0,0 +1,293 @@
from config.database import get_connection, release_connection
def _row_to_dict(row):
"""
Chuyển đổi một dòng kết quả từ tuple (từ câu lệnh JOIN) thành dictionary.
"""
if not row:
return None
device_dict = {
"id": str(row[0]),
"device_type_id": str(row[1]),
"name": row[2],
"description": row[3],
"ip_address": row[4],
"port": row[5],
"latitude": row[6],
"longitude": row[7],
"color": row[8],
"avatar_url": row[9],
"is_active": row[10],
"created": row[11].isoformat() if row[11] else None,
"modified": row[12].isoformat() if row[12] else None
}
# Nếu câu truy vấn có JOIN với device_type
if len(row) > 13:
device_dict["device_type_name"] = row[13]
device_dict["device_type_icon"] = row[14]
return device_dict
def find_all_devices():
"""
Lấy danh sách tất cả các thiết bị trong database, JOIN với bảng device_type
để hiển thị tên loại thiết bị và icon đại diện.
"""
conn = get_connection()
cur = None
try:
cur = conn.cursor()
cur.execute("""
SELECT
d.id, d.device_type_id, d.name, d.description, d.ip_address, d.port,
d.latitude, d.longitude, d.color, d.avatar_url, d.is_active, d.created, d.modified,
dt.name as device_type_name, dt.icon_url as device_type_icon
FROM device d
JOIN device_type dt ON d.device_type_id = dt.id
ORDER BY d.created DESC
""")
rows = cur.fetchall()
devices = []
for row in rows:
devices.append(_row_to_dict(row))
return devices
finally:
if cur:
cur.close()
release_connection(conn)
def find_device_by_id(device_id):
"""
Tìm thiết bị theo ID, JOIN với device_type để lấy thông tin chi tiết.
"""
conn = get_connection()
cur = None
try:
cur = conn.cursor()
cur.execute("""
SELECT
d.id, d.device_type_id, d.name, d.description, d.ip_address, d.port,
d.latitude, d.longitude, d.color, d.avatar_url, d.is_active, d.created, d.modified,
dt.name as device_type_name, dt.icon_url as device_type_icon
FROM device d
JOIN device_type dt ON d.device_type_id = dt.id
WHERE d.id = %s
""", (device_id,))
row = cur.fetchone()
return _row_to_dict(row) if row else None
finally:
if cur:
cur.close()
release_connection(conn)
def find_device_by_name(name):
"""
Tìm thiết bị theo tên (không phân biệt hoa thường) để phục vụ validate trùng tên.
"""
conn = get_connection()
cur = None
try:
cur = conn.cursor()
cur.execute("""
SELECT id, device_type_id, name, description, ip_address, port, latitude, longitude, color, avatar_url, is_active, created, modified
FROM device
WHERE LOWER(name) = LOWER(%s)
""", (name,))
row = cur.fetchone()
return _row_to_dict(row) if row else None
finally:
if cur:
cur.close()
release_connection(conn)
def find_device_by_ip(ip_address):
"""
Tìm thiết bị theo địa chỉ IP để phục vụ validate tránh trùng lặp IP thiết bị.
"""
conn = get_connection()
cur = None
try:
cur = conn.cursor()
cur.execute("""
SELECT id, device_type_id, name, description, ip_address, port, latitude, longitude, color, avatar_url, is_active, created, modified
FROM device
WHERE ip_address = %s
""", (ip_address,))
row = cur.fetchone()
return _row_to_dict(row) if row else None
finally:
if cur:
cur.close()
release_connection(conn)
def insert_device(data):
"""
Tạo mới một thiết bị và cấu hình mặc định (MonitorConfig & AlertConfig)
trong cùng một database transaction.
"""
conn = get_connection()
cur = None
try:
cur = conn.cursor()
# 1. Thêm thiết bị vào bảng device
cur.execute("""
INSERT INTO device (device_type_id, name, description, ip_address, port, latitude, longitude, color, avatar_url, is_active)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
RETURNING id, device_type_id, name, description, ip_address, port, latitude, longitude, color, avatar_url, is_active, created, modified
""", (
data["device_type_id"],
data["name"],
data.get("description"),
data["ip_address"],
data.get("port"),
data["latitude"],
data["longitude"],
data["color"],
data.get("avatar_url"),
data.get("is_active", True)
))
device_row = cur.fetchone()
device_id = device_row[0]
# 2. Thêm cấu hình giám sát mặc định (MonitorConfig) cho thiết bị vừa tạo
# enable_ping mặc định bật True để thực hiện Ping giám sát
cur.execute("""
INSERT INTO monitor_config (device_id, enable_ping, ping_count, ping_timeout, ping_interval, enable_snmp)
VALUES (%s, %s, %s, %s, %s, %s)
""", (
device_id,
True, # enable_ping
3, # ping_count
5, # ping_timeout (giây)
60, # ping_interval (giây)
False # enable_snmp
))
# 3. Thêm cấu hình cảnh báo mặc định (AlertConfig) cho thiết bị vừa tạo
cur.execute("""
INSERT INTO alert_config (device_id, is_enabled, fail_threshold, cooldown_minutes, notify_web, notify_email)
VALUES (%s, %s, %s, %s, %s, %s)
""", (
device_id,
True, # is_enabled
3, # fail_threshold (số lần fail liên tiếp trước khi alert)
30, # cooldown_minutes (thời gian tránh spam alert)
True, # notify_web (hiển thị thông báo trên web)
False # notify_email (mặc định tắt gửi email)
))
# 4. Lấy lại thiết bị kèm thông tin DeviceType đã JOIN để trả về đầy đủ dữ liệu
cur.execute("""
SELECT
d.id, d.device_type_id, d.name, d.description, d.ip_address, d.port,
d.latitude, d.longitude, d.color, d.avatar_url, d.is_active, d.created, d.modified,
dt.name as device_type_name, dt.icon_url as device_type_icon
FROM device d
JOIN device_type dt ON d.device_type_id = dt.id
WHERE d.id = %s
""", (device_id,))
full_row = cur.fetchone()
# Commit toàn bộ transaction
conn.commit()
return _row_to_dict(full_row)
except Exception:
conn.rollback()
raise
finally:
if cur:
cur.close()
release_connection(conn)
def update_device_db(device_id, data):
"""
Cập nhật thông tin chi tiết của một thiết bị.
"""
conn = get_connection()
cur = None
try:
cur = conn.cursor()
update_fields = []
params = []
fields_list = [
"device_type_id", "name", "description",
"ip_address", "port", "latitude",
"longitude", "color", "avatar_url", "is_active"
]
for key in fields_list:
if key in data:
update_fields.append(f"{key} = %s")
params.append(data[key])
if not update_fields:
return find_device_by_id(device_id)
update_fields.append("modified = CURRENT_TIMESTAMP")
sql = f"""
UPDATE device
SET {', '.join(update_fields)}
WHERE id = %s
RETURNING id, device_type_id, name, description, ip_address, port, latitude, longitude, color, avatar_url, is_active, created, modified
"""
params.append(device_id)
cur.execute(sql, tuple(params))
row = cur.fetchone()
conn.commit()
if row:
# Lấy chi tiết có kèm JOIN device_type để trả về đầy đủ
return find_device_by_id(device_id)
return None
except Exception:
conn.rollback()
raise
finally:
if cur:
cur.close()
release_connection(conn)
def delete_device_db(device_id):
"""
Xóa thiết bị khỏi database. Do có khóa ngoại với Cascade Delete,
tất cả các dòng liên quan trong monitor_config, alert_config, device_status, alert_log sẽ tự động bị xóa.
"""
conn = get_connection()
cur = None
try:
cur = conn.cursor()
cur.execute("""
DELETE FROM device
WHERE id = %s
""", (device_id,))
conn.commit()
except Exception:
conn.rollback()
raise
finally:
if cur:
cur.close()
release_connection(conn)