init commit

This commit is contained in:
2025-06-13 21:10:20 +09:00
commit d52c611afb
269 changed files with 37162 additions and 0 deletions

184
lottery/webapp/services.py Normal file
View File

@@ -0,0 +1,184 @@
import json
import logging
from django.core.exceptions import ImproperlyConfigured
from .models import APISettings, Client, Invoice
import requests
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
if not logger.handlers:
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
class APIClient:
"""
Класс для подключения к API и получения данных.
Данные API_URL и API_KEY берутся из модели APISettings.
"""
def __init__(self):
self.settings = APISettings.objects.first()
if not self.settings:
logger.error("Настройки API не сконфигурированы. Заполните модель APISettings.")
raise ImproperlyConfigured("Настройки API не сконфигурированы.")
self.api_url = self.settings.api_url.rstrip("/")
self.api_key = self.settings.api_key
self.headers = {"X-API-Key": self.api_key}
logger.debug(f"APIClient инициализирован с базовым URL: {self.api_url}")
def get_clients(self):
url = f"{self.api_url}/api/clients"
logger.debug(f"Запрос клиентов по адресу: {url} с заголовками: {self.headers}")
try:
response = requests.get(url, headers=self.headers, timeout=10)
response.raise_for_status()
data = response.json()
logger.debug("Запрос клиентов успешно выполнен. Данные получены.")
return data
except requests.exceptions.RequestException as e:
logger.error(f"Ошибка при запросе клиентов: {e}")
return None
def get_invoices(self):
url = f"{self.api_url}/api/invoices"
logger.debug(f"Запрос счетов по адресу: {url} с заголовками: {self.headers}")
try:
response = requests.get(url, headers=self.headers, timeout=10)
response.raise_for_status()
data = response.json()
logger.debug("Запрос счетов успешно выполнен. Данные получены.")
return data
except requests.exceptions.RequestException as e:
logger.error(f"Ошибка при запросе счетов: {e}")
return None
class API_SYNC:
"""
Класс для импорта и обновления данных из API.
Методы sync_clients и sync_invoices обновляют существующие записи или создают новые.
"""
def __init__(self):
self.logger = logger
def sync_clients(self):
api_client = APIClient()
data = api_client.get_clients()
new_or_updated = 0
if isinstance(data, dict):
clients_list = data.get("member", [])
self.logger.debug(f"Извлечен список клиентов из ключа 'member', найдено записей: {len(clients_list)}")
elif isinstance(data, list):
clients_list = data
self.logger.debug(f"Получен список клиентов (list), количество записей: {len(clients_list)}")
else:
self.logger.error("Неожиданный формат данных клиентов от API: %s", type(data))
return new_or_updated
for index, item in enumerate(clients_list, start=1):
self.logger.debug(f"Обработка записи клиента {index}: {item}")
if isinstance(item, str):
try:
item = json.loads(item)
self.logger.debug(f"Запись клиента {index} успешно преобразована из строки в словарь.")
except Exception as e:
self.logger.error("Невозможно преобразовать запись клиента %s в словарь: %s; ошибка: %s", index, item, e)
continue
if isinstance(item, dict):
club_card_number = item.get("club_card_num")
if not club_card_number:
self.logger.warning("Запись клиента %s пропущена: отсутствует club_card_num. Запись: %s", index, item)
continue
# Используем update_or_create для обновления существующей записи
obj, created = Client.objects.update_or_create(
club_card_number=club_card_number,
defaults={
'name': item.get("name"),
'telegram_id': item.get("telegram_id"),
}
)
new_or_updated += 1
if created:
self.logger.info("Запись клиента %s создана: club_card_num %s.", index, club_card_number)
else:
self.logger.info("Запись клиента %s обновлена: club_card_num %s.", index, club_card_number)
else:
self.logger.error("Запись клиента %s имеет неожиданный тип: %s. Значение: %s", index, type(item), item)
return new_or_updated
def sync_invoices(self):
api_client = APIClient()
data = api_client.get_invoices()
new_or_updated = 0
if isinstance(data, dict):
invoices_list = data.get("member", [])
self.logger.debug(f"Извлечен список счетов из ключа 'member', найдено записей: {len(invoices_list)}")
elif isinstance(data, list):
invoices_list = data
self.logger.debug(f"Получен список счетов (list), количество записей: {len(invoices_list)}")
else:
self.logger.error("Неожиданный формат данных счетов от API: %s", type(data))
return new_or_updated
for index, invoice in enumerate(invoices_list, start=1):
self.logger.debug(f"Обработка счета {index}: {invoice}")
if isinstance(invoice, str):
try:
invoice = json.loads(invoice)
self.logger.debug(f"Счет {index} успешно преобразован из строки в словарь.")
except Exception as e:
self.logger.error("Невозможно преобразовать счет %s в словарь: %s; ошибка: %s", index, invoice, e)
continue
if not isinstance(invoice, dict):
self.logger.error("Счет %s имеет неожиданный тип: %s", index, type(invoice))
continue
api_id = invoice.get("@id")
if not api_id:
self.logger.warning("Счет %s пропущен: отсутствует '@id'.", index)
continue
# Извлекаем данные клиента из вложенного объекта
client_data = invoice.get("client", {})
client_name = client_data.get("name")
client_club_card_number = client_data.get("club_card_num")
# Приводим даты (при необходимости, можно использовать парсинг)
created_at = invoice.get("created_at")
closed_at = invoice.get("closed_at")
if closed_at in [None, "N/A"]:
closed_at = None
defaults = {
'invoice_type': invoice.get("@type"),
'created_at': created_at,
'closed_at': closed_at,
'ext_id': invoice.get("ext_id"),
'ext_type': invoice.get("ext_type"),
'client_name': client_name,
'client_club_card_number': client_club_card_number,
'sum': invoice.get("sum"),
'company_number': invoice.get("comp_num"),
'bonus': invoice.get("bonus"),
'start_bonus': invoice.get("start_bonus"),
'deposit_sum': invoice.get("deposit_sum"),
'notes': invoice.get("notes"),
}
obj, created = Invoice.objects.update_or_create(
api_id=api_id,
defaults=defaults
)
new_or_updated += 1
if created:
self.logger.info("Счет %s создан: api_id=%s", index, api_id)
else:
self.logger.info("Счет %s обновлен: api_id=%s", index, api_id)
return new_or_updated