Dish_Recognition/dao/meal_schedule.py

127 lines
4.1 KiB
Python

# coding=utf-8
"""当天排餐缓存:启动拉取,界面不展示,供识别比对。"""
import json
import os
import threading
from datetime import datetime
from dao.settings import (
DISH_BASE_CODE,
DISH_BASE_NAME,
DISH_MGMT_MEAL_ON_START,
DISH_SQLITE_PATH,
)
from service.mgmt_client import MgmtApiError, extract_dish_names_from_meals, fetch_today_meals
_CACHE_DIR = os.path.join(os.path.dirname(DISH_SQLITE_PATH) or '.', 'mgmt_cache')
_CACHE_FILE = os.path.join(_CACHE_DIR, 'today_meals.json')
class MealScheduleStore:
"""内存 + 本地文件缓存;不对 UI 暴露明细。"""
def __init__(self):
self._lock = threading.RLock()
self._raw = None
self._names = set()
self._date = None
self._loaded_at = None
self._error = None
self._load_file()
def _load_file(self):
try:
if not os.path.isfile(_CACHE_FILE):
return
with open(_CACHE_FILE, 'r', encoding='utf-8') as f:
data = json.load(f)
self._raw = data.get('raw')
self._date = data.get('date')
self._names = set(data.get('names') or [])
self._loaded_at = data.get('loaded_at')
except Exception as exc:
print(f'[WARN] meal cache load failed: {exc}')
def _save_file(self):
try:
os.makedirs(_CACHE_DIR, exist_ok=True)
with open(_CACHE_FILE, 'w', encoding='utf-8') as f:
json.dump({
'date': self._date,
'names': sorted(self._names),
'loaded_at': self._loaded_at,
'raw': self._raw,
}, f, ensure_ascii=False, indent=2)
except Exception as exc:
print(f'[WARN] meal cache save failed: {exc}')
def refresh(self, base_name=None, base_code=None, base_id=None, force=False):
if not force and not DISH_MGMT_MEAL_ON_START and self._names:
return {'ok': True, 'skipped': True, 'names': len(self._names)}
base_name = (base_name or DISH_BASE_NAME or '').strip() or None
base_code = (base_code or DISH_BASE_CODE or '').strip() or None
try:
data = fetch_today_meals(
base_name=base_name,
base_code=base_code,
base_id=base_id,
)
except MgmtApiError as exc:
self._error = str(exc)
print(f'[WARN] meal schedule fetch failed: {exc}')
return {'ok': False, 'error': str(exc)}
names = extract_dish_names_from_meals(data)
with self._lock:
self._raw = data
self._names = names
self._date = (data or {}).get('date') or datetime.now().strftime('%Y-%m-%d')
self._loaded_at = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
self._error = None
self._save_file()
print(
f"[OK] meal schedule loaded: date={self._date} "
f"base={base_name or base_code} dishes={len(names)}"
)
return {'ok': True, 'date': self._date, 'names': len(names)}
def today_dish_names(self):
with self._lock:
# 跨日自动视为空,避免用昨天菜单比对
today = datetime.now().strftime('%Y-%m-%d')
if self._date and self._date != today:
return set()
return set(self._names)
def has_schedule(self):
return bool(self.today_dish_names())
def is_in_today_menu(self, dish_name):
name = (dish_name or '').strip()
if not name:
return False
return name in self.today_dish_names()
def summary(self):
with self._lock:
return {
'date': self._date,
'loaded_at': self._loaded_at,
'dish_count': len(self._names),
'error': self._error,
}
# 进程内单例,供识别比对使用(不在界面展示)
_store = None
_store_lock = threading.Lock()
def get_meal_schedule_store():
global _store
with _store_lock:
if _store is None:
_store = MealScheduleStore()
return _store