Files
cmautobuy/client/src/current_client_service.py
T

84 lines
2.5 KiB
Python

"""当前 Windows Client 身份的本地保存与读取服务。"""
import hashlib
import platform
import uuid
from dataclasses import dataclass
from typing import Callable, Optional
from .settings_repository import SettingsRepository
CLIENT_ID_KEY = "admin.client_id"
CLIENT_NAME_KEY = "admin.client_name"
def generate_client_device_id() -> str:
"""根据本机特征生成稳定设备号,只保留不可逆哈希。"""
fingerprint = "|".join(
(
platform.system(),
platform.node(),
platform.machine(),
f"{uuid.getnode():012x}",
)
)
digest = hashlib.sha256(fingerprint.encode("utf-8")).hexdigest()[:16]
return f"CLIENT-{digest.upper()}"
@dataclass(frozen=True)
class CurrentClientSettings:
"""本地已经确认保存的当前 Client 信息。"""
client_id: str
client_name: str
class CurrentClientService:
"""保证设备号只生成一次,并与名称一起原子保存。"""
def __init__(
self,
repository: SettingsRepository,
device_id_factory: Callable[[], str] = generate_client_device_id,
):
self._repository = repository
self._device_id_factory = device_id_factory
def load(self) -> CurrentClientSettings:
"""读取已保存的信息;缺少的字段返回空字符串。"""
client_id = self._clean_text(self._repository.get(CLIENT_ID_KEY, ""))
client_name = self._clean_text(
self._repository.get(CLIENT_NAME_KEY, "")
)
return CurrentClientSettings(client_id, client_name)
def save(self, client_name: str) -> CurrentClientSettings:
"""保存名称;设备号不存在时生成,存在时保持不变。"""
normalized_name = self._clean_text(client_name)
if len(normalized_name) > 50:
raise ValueError("设备名最多 50 个字")
existing = self.load()
client_id = existing.client_id
if not client_id:
client_id = self._clean_text(self._device_id_factory())
if not client_id:
raise ValueError("生成的设备号不能为空")
self._repository.set_many(
{
CLIENT_ID_KEY: client_id,
CLIENT_NAME_KEY: normalized_name,
}
)
return CurrentClientSettings(client_id, normalized_name)
@staticmethod
def _clean_text(value: object) -> str:
return value.strip() if isinstance(value, str) else ""