refactor: fix reverse dependencies — logger_manager→utils, dashboard_service extraction
- Move SystemLogger implementation to utils/logger_manager.py (pure utility, no deps) - services/logger_manager.py becomes a backward-compat re-export shim - database/manager.py and database/vendor_manager.py now import from utils layer - Extract get_dashboard_stats() to services/dashboard_service.py - services/task_runner.py no longer imports from routes layer - routes/dashboard_routes.py get_dashboard_stats() delegates to service layer Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@ from .ppt_reports import PPTReport # noqa: F401 - 確保 ppt_reports 表被 Bas
|
||||
from .vendor_models import VendorStockout # noqa: F401 - 確保 vendor_stockout 表被 Base.metadata 管理
|
||||
|
||||
# 🚩 導入優化後的日誌管理模組
|
||||
from services.logger_manager import SystemLogger
|
||||
from utils.logger_manager import SystemLogger
|
||||
|
||||
# 初始化資料庫模組專用 Logger
|
||||
sys_log = SystemLogger("Database").get_logger()
|
||||
|
||||
@@ -12,7 +12,7 @@ from datetime import datetime
|
||||
from .vendor_models import Base, VendorStockout, VendorList, VendorEmail, EmailSendLog
|
||||
|
||||
# 導入日誌管理模組
|
||||
from services.logger_manager import SystemLogger
|
||||
from utils.logger_manager import SystemLogger
|
||||
|
||||
# 初始化日誌
|
||||
sys_log = SystemLogger("VendorDatabase").get_logger()
|
||||
|
||||
@@ -465,16 +465,9 @@ def get_full_dashboard_data():
|
||||
|
||||
|
||||
def get_dashboard_stats():
|
||||
"""計算看板統計數據 (供通知使用)"""
|
||||
data = get_full_dashboard_data()
|
||||
if data:
|
||||
return {
|
||||
'new': data['today_new_products'],
|
||||
'up': len(data['increase_items_all']),
|
||||
'down': len(data['decrease_items_all']),
|
||||
'delisted': data['today_delisted_count']
|
||||
}
|
||||
return {'new': 0, 'up': 0, 'down': 0, 'delisted': 0}
|
||||
"""計算看板統計數據 (供通知使用) — backward-compat wrapper"""
|
||||
from services.dashboard_service import get_dashboard_stats as _get_dashboard_stats
|
||||
return _get_dashboard_stats()
|
||||
|
||||
|
||||
# ==========================================
|
||||
|
||||
22
services/dashboard_service.py
Normal file
22
services/dashboard_service.py
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
看板統計服務
|
||||
提供 get_dashboard_stats() 給 task_runner 等 service 層使用,
|
||||
避免 services 直接 import routes 層(反向依賴)。
|
||||
"""
|
||||
|
||||
|
||||
def get_dashboard_stats():
|
||||
"""計算看板統計數據 (供通知使用)"""
|
||||
# lazy import 避免循環依賴;routes 在 Flask app 啟動後才可用
|
||||
from routes.dashboard_routes import get_full_dashboard_data
|
||||
data = get_full_dashboard_data()
|
||||
if data:
|
||||
return {
|
||||
'new': data['today_new_products'],
|
||||
'up': len(data['increase_items_all']),
|
||||
'down': len(data['decrease_items_all']),
|
||||
'delisted': data['today_delisted_count']
|
||||
}
|
||||
return {'new': 0, 'up': 0, 'down': 0, 'delisted': 0}
|
||||
@@ -1,53 +1,4 @@
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from logging.handlers import RotatingFileHandler
|
||||
|
||||
class SystemLogger:
|
||||
"""
|
||||
自定義系統日誌管理器
|
||||
封裝 logging 模組,提供統一的日誌格式與輸出設定 (Console + File)
|
||||
"""
|
||||
def __init__(self, name="System", log_file='system.log'):
|
||||
self.logger = logging.getLogger(name)
|
||||
self.logger.setLevel(logging.DEBUG)
|
||||
|
||||
# 避免重複添加 Handler (防止日誌重複輸出)
|
||||
if not self.logger.handlers:
|
||||
# 取得專案根目錄 (假設此檔案位於 services/ 目錄下,往上兩層為根目錄)
|
||||
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
log_dir = os.path.join(base_dir, 'logs')
|
||||
|
||||
# 確保 logs 目錄存在
|
||||
if not os.path.exists(log_dir):
|
||||
try:
|
||||
os.makedirs(log_dir)
|
||||
except OSError:
|
||||
pass # 忽略無法建立目錄的錯誤,改由 Console 輸出
|
||||
|
||||
file_path = os.path.join(log_dir, log_file)
|
||||
|
||||
# 1. 檔案輸出 (RotatingFileHandler) - 支援自動切割
|
||||
try:
|
||||
file_handler = RotatingFileHandler(
|
||||
file_path,
|
||||
maxBytes=10*1024*1024, # 10MB 切割
|
||||
backupCount=5,
|
||||
encoding='utf-8'
|
||||
)
|
||||
file_handler.setLevel(logging.DEBUG)
|
||||
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
file_handler.setFormatter(formatter)
|
||||
self.logger.addHandler(file_handler)
|
||||
except Exception as e:
|
||||
print(f"⚠️ Logger Init Warning: 無法建立日誌檔案 ({e})")
|
||||
|
||||
# 2. 控制台輸出 (StreamHandler)
|
||||
console_handler = logging.StreamHandler(sys.stdout)
|
||||
console_handler.setLevel(logging.INFO)
|
||||
console_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
||||
console_handler.setFormatter(console_formatter)
|
||||
self.logger.addHandler(console_handler)
|
||||
|
||||
def get_logger(self):
|
||||
return self.logger
|
||||
# backward-compat re-export — 實作已移至 utils/logger_manager.py
|
||||
# 保留此檔案確保既有 `from services.logger_manager import SystemLogger` 不受影響
|
||||
from utils.logger_manager import SystemLogger # noqa: F401
|
||||
from utils.logger_manager import * # noqa: F401, F403
|
||||
|
||||
@@ -39,7 +39,7 @@ def run_momo_task_with_notification():
|
||||
import services.notification_manager
|
||||
importlib.reload(services.notification_manager)
|
||||
from services.notification_manager import NotificationManager
|
||||
from routes.dashboard_routes import get_dashboard_stats
|
||||
from services.dashboard_service import get_dashboard_stats
|
||||
|
||||
stats = get_dashboard_stats()
|
||||
|
||||
|
||||
53
utils/logger_manager.py
Normal file
53
utils/logger_manager.py
Normal file
@@ -0,0 +1,53 @@
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from logging.handlers import RotatingFileHandler
|
||||
|
||||
class SystemLogger:
|
||||
"""
|
||||
自定義系統日誌管理器
|
||||
封裝 logging 模組,提供統一的日誌格式與輸出設定 (Console + File)
|
||||
"""
|
||||
def __init__(self, name="System", log_file='system.log'):
|
||||
self.logger = logging.getLogger(name)
|
||||
self.logger.setLevel(logging.DEBUG)
|
||||
|
||||
# 避免重複添加 Handler (防止日誌重複輸出)
|
||||
if not self.logger.handlers:
|
||||
# 取得專案根目錄 (假設此檔案位於 utils/ 目錄下,往上兩層為根目錄)
|
||||
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
log_dir = os.path.join(base_dir, 'logs')
|
||||
|
||||
# 確保 logs 目錄存在
|
||||
if not os.path.exists(log_dir):
|
||||
try:
|
||||
os.makedirs(log_dir)
|
||||
except OSError:
|
||||
pass # 忽略無法建立目錄的錯誤,改由 Console 輸出
|
||||
|
||||
file_path = os.path.join(log_dir, log_file)
|
||||
|
||||
# 1. 檔案輸出 (RotatingFileHandler) - 支援自動切割
|
||||
try:
|
||||
file_handler = RotatingFileHandler(
|
||||
file_path,
|
||||
maxBytes=10*1024*1024, # 10MB 切割
|
||||
backupCount=5,
|
||||
encoding='utf-8'
|
||||
)
|
||||
file_handler.setLevel(logging.DEBUG)
|
||||
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
file_handler.setFormatter(formatter)
|
||||
self.logger.addHandler(file_handler)
|
||||
except Exception as e:
|
||||
print(f"⚠️ Logger Init Warning: 無法建立日誌檔案 ({e})")
|
||||
|
||||
# 2. 控制台輸出 (StreamHandler)
|
||||
console_handler = logging.StreamHandler(sys.stdout)
|
||||
console_handler.setLevel(logging.INFO)
|
||||
console_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
||||
console_handler.setFormatter(console_formatter)
|
||||
self.logger.addHandler(console_handler)
|
||||
|
||||
def get_logger(self):
|
||||
return self.logger
|
||||
Reference in New Issue
Block a user