from __future__ import annotations from dataclasses import dataclass from sqlalchemy import text from services.database_service_base import DatabaseServiceBase from utils.logger import session_logger class DeviceIdentityInitializationError(ValueError): pass @dataclass(frozen=True) class DeviceIdentityInitializationResult: imei: str device_id: str serial_number: str activated: bool class DeviceIdentityInitializer(DatabaseServiceBase): def __init__(self): super().__init__(service_name="device_identity_initializer") async def initialize_by_imei(self, imei: str) -> DeviceIdentityInitializationResult: normalized_imei = str(imei or "").strip() if not normalized_imei: raise DeviceIdentityInitializationError("imei is required") db_session = await self.get_session() try: mapping_result = await db_session.execute( text( """ SELECT imei, device_id, serial_number, status FROM device_imei_mapping WHERE imei = :imei LIMIT 1 """ ), {"imei": normalized_imei}, ) mapping = mapping_result.mappings().first() if mapping is None: raise DeviceIdentityInitializationError("imei mapping not found") device_id = str(mapping["device_id"]).strip() serial_number = str(mapping["serial_number"]).strip() if not device_id or not serial_number: raise DeviceIdentityInitializationError("imei mapping is incomplete") device_auth_result = await db_session.execute( text( """ SELECT device_id, serial_number FROM device_auth WHERE device_id = :device_id LIMIT 1 """ ), {"device_id": device_id}, ) device_auth = device_auth_result.mappings().first() if device_auth is None: await db_session.execute( text( """ INSERT INTO device_auth (device_id, serial_number, is_active) VALUES (:device_id, :serial_number, 1) """ ), {"device_id": device_id, "serial_number": serial_number}, ) elif str(device_auth["serial_number"]) != serial_number: raise DeviceIdentityInitializationError("device_auth serial_number conflict") else: await db_session.execute( text( """ UPDATE device_auth SET is_active = 1 WHERE device_id = :device_id """ ), {"device_id": device_id}, ) await db_session.execute( text( """ UPDATE device_imei_mapping SET status = 'activated', activated_at = COALESCE(activated_at, CURRENT_TIMESTAMP) WHERE imei = :imei """ ), {"imei": normalized_imei}, ) await db_session.commit() session_logger.info( device_id, "device_identity_initializer", f"IMEI identity initialized: imei={normalized_imei}", ) return DeviceIdentityInitializationResult( imei=normalized_imei, device_id=device_id, serial_number=serial_number, activated=str(mapping["status"]) == "activated", ) except DeviceIdentityInitializationError: await db_session.rollback() raise except Exception as exc: await db_session.rollback() session_logger.error( normalized_imei, "device_identity_initializer", f"initialize identity failed: {exc}", ) raise finally: await db_session.close() device_identity_initializer = DeviceIdentityInitializer()