274 lines
9.7 KiB
Python
274 lines
9.7 KiB
Python
# coding=utf-8
|
||
"""本机基地编码:注册/解析 bases,供模板 source_base_id 与 API 上报使用。"""
|
||
import datetime
|
||
import os
|
||
|
||
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 可能为空)
|
||
"""
|
||
import dao.settings as cfg
|
||
code = (cfg.DISH_BASE_CODE or '').strip() or 'BASE_LOCAL_01'
|
||
name = (cfg.DISH_BASE_NAME or '').strip() or code
|
||
region = (getattr(cfg, 'DISH_BASE_REGION', None) 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_active_base():
|
||
"""当前生效基地:优先本地已设默认,否则回落环境变量默认值。"""
|
||
import dao.settings as cfg
|
||
cached = _read_cached()
|
||
return {
|
||
'id': cached.get('id'),
|
||
'base_code': (cached.get('base_code') or cfg.DISH_BASE_CODE or '').strip(),
|
||
'base_name': (cached.get('base_name') or cfg.DISH_BASE_NAME or '').strip(),
|
||
'online': bool(cached.get('online')),
|
||
'source': cached.get('source') or 'cache',
|
||
}
|
||
|
||
|
||
def apply_runtime_base(base_id=None, base_code=None, base_name=None):
|
||
"""把基地写入运行时 settings / 环境变量(不落库)。"""
|
||
import dao.settings as cfg
|
||
|
||
code = (base_code or '').strip()
|
||
name = (base_name or '').strip()
|
||
if code:
|
||
cfg.DISH_BASE_CODE = code
|
||
os.environ['DISH_BASE_CODE'] = code
|
||
if name:
|
||
cfg.DISH_BASE_NAME = name
|
||
os.environ['DISH_BASE_NAME'] = name
|
||
cfg.DISH_DEVICE_NAME = name
|
||
os.environ['DISH_DEVICE_NAME'] = name
|
||
if code:
|
||
cfg.DISH_DEVICE_CODE = code
|
||
os.environ['DISH_DEVICE_CODE'] = code
|
||
try:
|
||
import service.result_api as result_api
|
||
if code:
|
||
result_api.RESULT_API_BASE_ID = code
|
||
if name:
|
||
result_api.RESULT_API_BASE_NAME = name
|
||
except Exception:
|
||
pass
|
||
return {
|
||
'id': base_id,
|
||
'base_code': code or cfg.DISH_BASE_CODE,
|
||
'base_name': name or cfg.DISH_BASE_NAME,
|
||
}
|
||
|
||
|
||
def set_default_base(base_id=None, base_code=None, base_name=None, region=None, contact=None):
|
||
"""设为默认基地:持久化本地 + 更新运行时配置。"""
|
||
code = (base_code or '').strip()
|
||
name = (base_name or '').strip()
|
||
if not name and not code:
|
||
raise ValueError('基地名称或编码不能为空')
|
||
bid = None
|
||
if base_id is not None and str(base_id).strip() != '':
|
||
try:
|
||
bid = int(base_id)
|
||
except (TypeError, ValueError):
|
||
bid = None
|
||
_cache_local(bid, code or name, name or code, region=region, contact=contact)
|
||
info = apply_runtime_base(base_id=bid, base_code=code or name, base_name=name or code)
|
||
print(f"[OK] default base set: {info.get('base_name')} ({info.get('base_code')})")
|
||
return info
|
||
|
||
|
||
def bootstrap_default_base():
|
||
"""启动时把本地已保存的默认基地应用到运行时。"""
|
||
cached = _read_cached()
|
||
name = (cached.get('base_name') or '').strip()
|
||
code = (cached.get('base_code') or '').strip()
|
||
# 仅当本地确实设过默认时覆盖 env 默认值
|
||
if name or (code and code != 'BASE_LOCAL_01'):
|
||
return apply_runtime_base(
|
||
base_id=cached.get('id'),
|
||
base_code=code,
|
||
base_name=name,
|
||
)
|
||
return get_active_base()
|
||
|
||
|
||
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}) [未注册到总库]"
|
||
|