# coding=utf-8 """本机基地编码:注册/解析 bases,供模板 source_base_id 与 API 上报使用。""" import datetime from dao.database import is_server_available, local_session_scope, server_session_scope from dao.settings import DISH_BASE_CODE, DISH_BASE_NAME, DISH_BASE_REGION from models.App_settings import AppSetting from models.Bases import BaseInfo SETTING_BASE_ID = 'local_base_id' SETTING_BASE_CODE = 'local_base_code' SETTING_BASE_NAME = 'local_base_name' def _set_setting(session, key, value): row = session.get(AppSetting, key) if row: row.value = str(value) if value is not None else '' else: session.add(AppSetting(key=key, value=str(value) if value is not None else '')) def _get_setting(session, key, default=None): row = session.get(AppSetting, key) if row and row.value not in (None, ''): return row.value return default def _cache_local(base_id, base_code, base_name, region=None, contact=None): """把基地信息写入本地 SQLite(AppSetting + bases 镜像,避免 FK 问题)。""" with local_session_scope() as session: _set_setting(session, SETTING_BASE_ID, base_id if base_id is not None else '') _set_setting(session, SETTING_BASE_CODE, base_code or '') _set_setting(session, SETTING_BASE_NAME, base_name or '') if base_id is None: return existing = session.get(BaseInfo, base_id) now = datetime.datetime.now() if existing: existing.base_code = base_code existing.base_name = base_name existing.region = region existing.contact = contact existing.is_active = 1 existing.last_heartbeat = now else: session.add(BaseInfo( id=base_id, base_code=base_code, base_name=base_name, region=region, contact=contact, is_active=1, register_time=now, last_heartbeat=now, )) def _read_cached(): with local_session_scope() as session: raw_id = _get_setting(session, SETTING_BASE_ID) base_id = None if raw_id is not None: try: base_id = int(raw_id) except (TypeError, ValueError): base_id = None return { 'id': base_id, 'base_code': _get_setting(session, SETTING_BASE_CODE, DISH_BASE_CODE), 'base_name': _get_setting(session, SETTING_BASE_NAME, DISH_BASE_NAME), 'online': False, 'source': 'cache', } def resolve_local_base(force_refresh=False): """ 解析本机基地: - 先从管理系统拉取基地列表写入 MySQL(可关 DISH_MGMT_SYNC_ON_START) - 有 MySQL:按 DISH_BASE_CODE 查找;找不到再注册 - 无 MySQL:用本地缓存 / 环境变量(source_base_id 可能为空) """ code = (DISH_BASE_CODE or '').strip() or 'BASE_LOCAL_01' name = (DISH_BASE_NAME or '').strip() or code region = (DISH_BASE_REGION or '').strip() or None # 启动/刷新时同步管理系统基地(失败不阻断本机解析) try: from dao.base_sync import sync_bases_from_mgmt sync_bases_from_mgmt(force=force_refresh) except Exception as exc: print(f"[WARN] mgmt bases sync skipped: {exc}") if not is_server_available(): cached = _read_cached() cached['base_code'] = code or cached.get('base_code') cached['base_name'] = name or cached.get('base_name') cached['online'] = False cached['source'] = 'offline' print(f"[WARN] base registry offline, code={cached.get('base_code')}, id={cached.get('id')}") return cached try: with server_session_scope() as session: row = ( session.query(BaseInfo) .filter(BaseInfo.base_code == code) .first() ) now = datetime.datetime.now() if row: # 已从管理系统同步的基地:保留管理系统名称,除非环境变量显式覆盖默认名 if name and name != '本地基地' and name != code: row.base_name = name if region: row.region = region row.is_active = 1 row.last_heartbeat = now created = False else: row = BaseInfo( base_code=code, base_name=name, region=region, is_active=1, register_time=now, last_heartbeat=now, ) session.add(row) session.flush() created = True info = { 'id': row.id, 'base_code': row.base_code, 'base_name': row.base_name, 'region': row.region, 'contact': row.contact, 'external_id': getattr(row, 'external_id', None), 'branch_name': getattr(row, 'branch_name', None), 'online': True, 'source': 'mysql', 'created': created, } _cache_local( info['id'], info['base_code'], info['base_name'], region=info.get('region'), contact=info.get('contact'), ) action = 'registered' if info.get('created') else 'resolved' print(f"[OK] base {action}: {info['base_code']} (id={info['id']})") return info except Exception as exc: print(f"[WARN] base registry failed: {exc}") cached = _read_cached() cached['online'] = False cached['source'] = 'error' cached['error'] = str(exc) return cached def touch_base_heartbeat(base_id=None): """同步成功后刷新基地心跳。""" if base_id is None: cached = _read_cached() base_id = cached.get('id') if not base_id or not is_server_available(): return False try: with server_session_scope() as session: row = session.get(BaseInfo, base_id) if not row: return False row.last_heartbeat = datetime.datetime.now() code, name, region = row.base_code, row.base_name, row.region _cache_local(base_id, code, name, region=region) return True except Exception as exc: print(f"[WARN] base heartbeat failed: {exc}") return False def get_base_status_text(base_info=None): info = base_info or _read_cached() code = info.get('base_code') or DISH_BASE_CODE name = info.get('base_name') or DISH_BASE_NAME or code bid = info.get('id') if info.get('online'): return f"基地:{code}({name}) ID={bid}" if bid: return f"基地:{code}({name}) ID={bid} [离线缓存]" return f"基地:{code}({name}) [未注册到总库]"