From 5c8f1acace4af192745bddb2eefd487153c8d817 Mon Sep 17 00:00:00 2001 From: wystan_rin Date: Sun, 10 Nov 2024 18:00:58 +0800 Subject: [PATCH] developing --- __init__.py | 0 api/tronscan.py | 26 +++++++++-------- config/db.py | 7 +++++ custom_decorators.py | 17 +++++++++++ repositories/order.py | 17 +++++++++++ requirements.txt | 3 ++ services/order.py | 28 ++++++++++++++++++ services/payment.py | 21 ++++++++++++++ utils/__init__.py | 0 utils/datetime.py | 35 ++++++++++++++++++++++ utils/db_connection.py | 66 ++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 208 insertions(+), 12 deletions(-) create mode 100644 __init__.py create mode 100644 config/db.py create mode 100644 custom_decorators.py create mode 100644 repositories/order.py create mode 100644 requirements.txt create mode 100644 services/order.py create mode 100644 services/payment.py create mode 100644 utils/__init__.py create mode 100644 utils/datetime.py create mode 100644 utils/db_connection.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/tronscan.py b/api/tronscan.py index bba386f..3be1c96 100644 --- a/api/tronscan.py +++ b/api/tronscan.py @@ -1,5 +1,7 @@ import requests +from custom_decorators import singleton + trc20token_info = { "usdt": {"tokenId": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", "tokenAbbr": "USDT", @@ -9,7 +11,7 @@ trc20token_info = { "vip": True} } - +@singleton class Tronscan: def __init__(self, api_key): self.api_key = api_key @@ -25,19 +27,19 @@ class Tronscan: return response.json() def transactions(self, start=0, limit=10, start_timestamp=None, end_timestamp=None, - fromAddress=None, toAddress=None, tokens=None, block=None, - type=None, method=None): + from_address=None, to_address=None, tokens=None, block=None, + type_=None, method=None): """ Get a list of transactions. :param start: Start number. Default 0 :param limit: Number of items per page. Default 10 :param start_timestamp: Start time :param end_timestamp: End time - :param fromAddress: Sender's address. - :param toAddress: Recipient's address. + :param from_address: Sender's address. + :param to_address: Recipient's address. :param tokens: Tokens involved :param block: Block - :param type: Transaction type + :param type_: Transaction type :param method: Method called in a smart contract signature. Only one value can be specified each time. :return: Getx a list of transactions. """ @@ -52,16 +54,16 @@ class Tronscan: params["start_timestamp"] = start_timestamp if end_timestamp is not None: params["end_timestamp"] = end_timestamp - if fromAddress is not None: - params["fromAddress"] = fromAddress - if toAddress is not None: - params["toAddress"] = toAddress + if from_address is not None: + params["fromAddress"] = from_address + if to_address is not None: + params["toAddress"] = to_address if tokens is not None: params["tokens"] = tokens if block is not None: params["block"] = block - if type is not None: - params["type"] = type + if type_ is not None: + params["type"] = type_ if method is not None: params["method"] = method diff --git a/config/db.py b/config/db.py new file mode 100644 index 0000000..595714f --- /dev/null +++ b/config/db.py @@ -0,0 +1,7 @@ +config = { + 'user': 'your_mysql_username', + 'password': 'your_mysql_password', + 'host': 'localhost', + 'database': 'your_database_name', + 'autocommit': False, +} diff --git a/custom_decorators.py b/custom_decorators.py new file mode 100644 index 0000000..947e4e8 --- /dev/null +++ b/custom_decorators.py @@ -0,0 +1,17 @@ +def singleton(cls): + """ + Decorator for making a class a singleton. + This ensures that only one instance of the class exists. + """ + instances = {} # Dictionary to store the instance of the singleton class + + def get_instance(*args, **kwargs): + """ + If an instance of the class does not exist, create one and store it. + If it exists, return the existing instance. + """ + if cls not in instances: + instances[cls] = cls(*args, **kwargs) + return instances[cls] + + return get_instance diff --git a/repositories/order.py b/repositories/order.py new file mode 100644 index 0000000..e6fbb9b --- /dev/null +++ b/repositories/order.py @@ -0,0 +1,17 @@ +from custom_decorators import singleton + + +@singleton +class OrderRepository: + def __init__(self): + self.db = None + + def create(self, order_id, from_address, to_address): + pass + + def update_status(self, order_id, status): + # 更新状态和时间 + pass + + def get_creation_time(self, order_id): + pass diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1e21fb7 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +loguru==0.7.2 +mysql-connector-python==9.1.0 +Requests==2.32.3 diff --git a/services/order.py b/services/order.py new file mode 100644 index 0000000..c32c7e6 --- /dev/null +++ b/services/order.py @@ -0,0 +1,28 @@ +import uuid + +from custom_decorators import singleton +from repositories.order import OrderRepository +from utils.datetime import current, current_timestamp, is_time_difference_greater_than + + +@singleton +class OrderService: + def __init__(self): + self.order_repo = OrderRepository() + + def create_order(self, from_address, to_address, *args, **kwargs): + date_str = current().strftime('%Y%m%d%H%M%S') + unique_id = str(uuid.uuid4()).split('-')[0] + order_id = f"{date_str}-{unique_id}" + self.order_repo.create(order_id, from_address, to_address) + return order_id + + def finish_order(self, order_id): + # 判断支付时间是否超过订单存活时间 + order_creation_time = self.order_repo.get_creation_time(order_id) + if is_time_difference_greater_than(current_timestamp(), order_creation_time, minutes=15): + status = 2 + else: + status = 1 + self.order_repo.update_status(order_id, status) + return status diff --git a/services/payment.py b/services/payment.py new file mode 100644 index 0000000..06fc204 --- /dev/null +++ b/services/payment.py @@ -0,0 +1,21 @@ +from api import Tronscan +from custom_decorators import singleton +from utils.datetime import current_timestamp + + +@singleton +class Payment: + def __init__(self, api_key): + self.tronscan = Tronscan(api_key) + + def check_payment(self, quant, from_address, to_address, order_create_timestamp): + result = self.tronscan.token_trc20_transfers(from_address=from_address, + to_address=to_address, + start_timestamp=order_create_timestamp, + end_timestamp=int(current_timestamp() * 1000)) + token_transfers = result['token_transfers'] + token_transfer = token_transfers[-1] + confirmed = token_transfer['confirmed'] + correct_quant = quant == (token_transfer['quant'] / 6) + confirmed = confirmed and correct_quant + return confirmed diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/datetime.py b/utils/datetime.py new file mode 100644 index 0000000..40a2c50 --- /dev/null +++ b/utils/datetime.py @@ -0,0 +1,35 @@ +import datetime + + +def current(): + return datetime.datetime.now() + +def current_timestamp(): + datetime.datetime.now().timestamp() + +def is_time_difference_greater_than(timestamp1, timestamp2, hours=0, minutes=0, seconds=0): + """ + 判断两个时间戳的时间差是否大于指定的小时、分钟和秒数 + + 参数: + timestamp1 (int): 第一个时间戳 + timestamp2 (int): 第二个时间戳 + hours (int): 要比较的小时数,默认是0小时 + minutes (int): 要比较的分钟数,默认是0分钟 + seconds (int): 要比较的秒数,默认是0秒 + + 返回: + bool: 如果时间差大于指定的小时、分钟和秒数返回True,否则返回False + """ + # 将时间戳转换为 datetime 对象 + time1 = datetime.fromtimestamp(timestamp1) + time2 = datetime.fromtimestamp(timestamp2) + + # 计算时间差 + time_difference = abs(time2 - time1) + + # 计算指定的时间差值 + threshold = datetime.timedelta(hours=hours, minutes=minutes, seconds=seconds) + + # 判断时间差是否大于指定的时间 + return time_difference > threshold \ No newline at end of file diff --git a/utils/db_connection.py b/utils/db_connection.py new file mode 100644 index 0000000..b6758c2 --- /dev/null +++ b/utils/db_connection.py @@ -0,0 +1,66 @@ +from loguru import logger +from mysql.connector import connect, Error, OperationalError +from mysql.connector import errors as db_errors + +from custom_decorators import singleton + + +@singleton +class Database: + def __init__(self, config): + self.connection = None + self.config = config + self.connect() + + def connect(self): + """Establish a new database connection.""" + try: + self.connection = connect(**self.config) + if self.connection.is_connected(): + logger.info("Connected to MySQL database") + except Error as e: + logger.info(f"Error while connecting to MySQL: {e}") + self.connection = None + + def get_connection(self): + """Get the database connection, with reconnection logic.""" + if self.connection is None or not self.connection.is_connected(): + logger.info("Reconnecting to the database...") + self.connect() + return self.connection + + def close_connection(self): + if self.connection and self.connection.is_connected(): + self.connection.close() + logger.info("MySQL connection is closed") + + def execute_query(self, query, params=None): + """Execute a query with optional parameters, supports transactions.""" + cursor = None + try: + connection = self.get_connection() + cursor = connection.cursor() + cursor.execute(query, params) + return cursor + except OperationalError as e: + logger.info(f"Operational error: {e}. Attempting to reconnect...") + self.connect() + cursor = self.get_connection().cursor() + cursor.execute(query, params) + return cursor + except db_errors.Error as e: + logger.info(f"Database error: {e}") + raise + finally: + if cursor: + cursor.close() + + def commit(self): + """Commit the current transaction.""" + if self.connection: + self.connection.commit() + + def rollback(self): + """Rollback the current transaction.""" + if self.connection: + self.connection.rollback()