127 lines
4.1 KiB
Python
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
|