diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/deployment.xml b/.idea/deployment.xml new file mode 100644 index 0000000..4f73c50 --- /dev/null +++ b/.idea/deployment.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..03d9549 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..a6218fe --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/payment.iml b/.idea/payment.iml new file mode 100644 index 0000000..f617e98 --- /dev/null +++ b/.idea/payment.iml @@ -0,0 +1,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..a171b33 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app.py b/app.py deleted file mode 100644 index 8b3525b..0000000 --- a/app.py +++ /dev/null @@ -1,7 +0,0 @@ -from flask import Flask - -from config import get_config - -config = get_config() - -app = Flask(__name__) diff --git a/.gitignore b/payment_backend/.gitignore similarity index 100% rename from .gitignore rename to payment_backend/.gitignore diff --git a/README.md b/payment_backend/README.md similarity index 100% rename from README.md rename to payment_backend/README.md diff --git a/__init__.py b/payment_backend/__init__.py similarity index 100% rename from __init__.py rename to payment_backend/__init__.py diff --git a/api/__init__.py b/payment_backend/api/__init__.py similarity index 100% rename from api/__init__.py rename to payment_backend/api/__init__.py diff --git a/api/tronscan.py b/payment_backend/api/tronscan.py similarity index 100% rename from api/tronscan.py rename to payment_backend/api/tronscan.py diff --git a/payment_backend/app.py b/payment_backend/app.py new file mode 100644 index 0000000..48aa46c --- /dev/null +++ b/payment_backend/app.py @@ -0,0 +1,42 @@ +from flask import Flask, request, jsonify + +from config import get_config +from services.order import OrderService + +config = get_config() + +app = Flask(__name__) +order_service = OrderService() # 获取单例实例 + +@app.route('/createOrder', methods=['POST']) +def create_order(): + data = request.get_json() + phone = data.get('phone', None) + email = data.get('email', None) + address = data.get('address', None) + try: + payment_method = data['paymentMethod'] + except KeyError: + return jsonify({ + "message": "Unsupported payment method. Currently, only USDT payments are supported." + }), 400 + + addresses = order_service.get_user_addresses(phone, email, address, payment_method) + + if not addresses: + return jsonify({ + "message": "No payment address associated with you was found. Please provide a payment address." + }), 400 + + if len(addresses) == 1: + order_id = order_service.create_order(addresses[0]) + return jsonify({"order_id": order_id}), 200 + + # 多个地址的情况 + return jsonify({ + "message": "请选择一个地址进行下单。", + "addresses": addresses + }), 200 + +if __name__ == '__main__': + app.run(debug=True) diff --git a/config/__init__.py b/payment_backend/config/__init__.py similarity index 100% rename from config/__init__.py rename to payment_backend/config/__init__.py diff --git a/config/db.py b/payment_backend/config/db.py similarity index 100% rename from config/db.py rename to payment_backend/config/db.py diff --git a/config/param.ini b/payment_backend/config/param.ini similarity index 89% rename from config/param.ini rename to payment_backend/config/param.ini index 42a7e9c..93bc4de 100644 --- a/config/param.ini +++ b/payment_backend/config/param.ini @@ -10,3 +10,4 @@ password: 'your_mysql_password' host: 'localhost' database: 'your_database_name' autocommit: false +allow_multi_statements: True \ No newline at end of file diff --git a/config/utils.py b/payment_backend/config/utils.py similarity index 100% rename from config/utils.py rename to payment_backend/config/utils.py diff --git a/custom_decorators.py b/payment_backend/custom_decorators.py similarity index 100% rename from custom_decorators.py rename to payment_backend/custom_decorators.py diff --git a/database.py b/payment_backend/database.py similarity index 100% rename from database.py rename to payment_backend/database.py diff --git a/payment_backend/models.py b/payment_backend/models.py new file mode 100644 index 0000000..1dd08ea --- /dev/null +++ b/payment_backend/models.py @@ -0,0 +1,93 @@ +from utils.database import pack_params + + +class User: + def __init__(self, id_=None, name=None, phone=None, email=None, address=None, payment_method=None): + self.id = id_ + self.name = name + self.phone = phone + self.email = email + self.address = address + self.payment_method = payment_method + + def insert_sql(self, params_format="list"): + params_sql, params = pack_params(params_format=params_format, param_sql="{param}", join_str=",", + name=self.name, phone=self.phone, email=self.email, address=self.address, + payment_method=self.payment_method) + return f"INSERT INTO user ({params_sql}) VALUES ({','.join('%s' for _ in params)})", params + + def select_sql(self, condition="AND", params_format="list"): + params_sql, params = pack_params(params_format=params_format, param_sql="{param}=%s", join_str=f" {condition} ", + name=self.name, phone=self.phone, email=self.email, address=self.address, + payment_method=self.payment_method) + return f"SELECT id, name, phone, email, address, payment_method FROM user WHERE {params_sql}", params + + def exists_sql(self, condition="AND", params_format="list"): + params_sql, params = pack_params(params_format=params_format, param_sql="{param}=%s", join_str=f" {condition} ", + name=self.name, phone=self.phone, email=self.email, address=self.address, + payment_method=self.payment_method) + return f"SELECT id FROM user WHERE {params_sql} LIMIT 1", params + + def params(self, format="dict"): + if format == "list": + params = [] + elif format == "dict": + params = {} + else: + raise ValueError("format must be list or dict") + if self.uid: + if format == "list": + params.append(self.uid) + elif format == "dict": + params["uid"] = self.uid + if self.name: + if format == "list": + params.append(self.name) + elif format == "dict": + params["name"] = self.name + if self.phone: + if format == "list": + params.append(self.phone) + elif format == "dict": + params["phone"] = self.phone + if self.email: + if format == "list": + params.append(self.email) + elif format == "dict": + params["email"] = self.email + if self.address: + if format == "list": + params.append(self.address) + elif format == "dict": + params["address"] = self.address + if self.payment_method: + if format == "list": + params.append(self.payment_method) + elif format == "dict": + params["payment_method"] = self.payment_method + return params + + def get_difference(self, other_user): + different_attrs = {} + if self.uid != other_user.uid: + different_attrs["uid"] = (self.uid, other_user.uid) + if self.name != other_user.name: + different_attrs["name"] = (self.name, other_user.name) + if self.phone != other_user.phone: + different_attrs["phone"] = (self.phone, other_user.phone) + if self.email != other_user.email: + different_attrs["email"] = (self.email, other_user.email) + if self.address != other_user.address: + different_attrs["address"] = (self.address, other_user.address) + if self.payment_method != other_user.payment_method: + different_attrs["payment_method"] = (self.payment_method, other_user.payment_method) + return different_attrs + + def __eq__(self, other): + if isinstance(other, User): + return ((self.name, self.phone, self.email, self.address, self.payment_method) + == (other.name, other.phone, other.email, other.address, other.payment_method)) + return False + + def __hash__(self): + return hash((self.name, self.phone, self.email, self.address, self.payment_method)) diff --git a/repositories/__init__.py b/payment_backend/repositories/__init__.py similarity index 100% rename from repositories/__init__.py rename to payment_backend/repositories/__init__.py diff --git a/repositories/order.py b/payment_backend/repositories/order.py similarity index 100% rename from repositories/order.py rename to payment_backend/repositories/order.py diff --git a/payment_backend/repositories/user.py b/payment_backend/repositories/user.py new file mode 100644 index 0000000..b580fdc --- /dev/null +++ b/payment_backend/repositories/user.py @@ -0,0 +1,74 @@ +import itertools + +from custom_decorators import singleton +from database import Database +from models import User + + +@singleton +class UserRepository: + def __init__(self, config): + self.db = Database(config['MYSQL']) + + def get_or_create(self, user): + users = [] + cursor = self.db.execute_query(*user.select_sql(condition="OR")) + same_users = cursor.fetchall() + new_user = not len(same_users) + # 对用户已存在的属性判断是否有新属性 + update_user = set() + update_sqls = [] + update_params_list = [] + delete_params = [] + exist_conflicting_attr = False + for same_user in same_users: + exist_conflicting_attr = False + different_attrs = user.get_difference(same_user) + # 用于判断是否有新属性 + update_sql_params = [] + update_params = [] + for k, v in different_attrs.items(): + new_attr, exist_attr = v + if exist_attr is None: + setattr(same_user, k, new_attr) + update_sql_params.append(f"{k}=%s") + update_params.append(new_attr) + else: + # 出现冲突的属性,考虑新增一行记录 + exist_conflicting_attr = True + break + + if same_user in update_user: + delete_params.append((same_user.id,)) + else: + users.append(same_user) + exist_new_attr = bool(update_params) + if exist_new_attr: + update_user.add(same_user) + update_sqls.append(f'UPDATE user SET {",".join(update_sql_params)} WHERE id=%s;') + update_params.append(same_user.id) + update_params_list.append(update_params) + sql_flag = False + try: + if delete_params: + sql_flag = True + self.db.get_connection().cursor().executemany("DELETE FROM user WHERE id=%s", delete_params) + if update_user: + sql_flag = True + self.db.get_connection().cursor().execute("".join(update_sqls), + list(itertools.chain.from_iterable(update_params_list)), + multi=True) + if sql_flag: + self.db.commit() + except Exception: + self.db.rollback() + raise + if new_user or exist_conflicting_attr: + try: + self.db.execute_query(*user.insert_sql()) + self.db.commit() + except Exception: + self.db.rollback() + raise + users.append(user) + return users diff --git a/requirements.txt b/payment_backend/requirements.txt similarity index 100% rename from requirements.txt rename to payment_backend/requirements.txt diff --git a/services/__init__.py b/payment_backend/services/__init__.py similarity index 100% rename from services/__init__.py rename to payment_backend/services/__init__.py diff --git a/services/order.py b/payment_backend/services/order.py similarity index 66% rename from services/order.py rename to payment_backend/services/order.py index b86a9a9..1272db4 100644 --- a/services/order.py +++ b/payment_backend/services/order.py @@ -1,7 +1,9 @@ import uuid from custom_decorators import singleton +from models import User from repositories.order import OrderRepository +from repositories.user import UserRepository from services.payment import PaymentService from utils.datetime import current, current_timestamp, is_time_difference_greater_than @@ -9,14 +11,27 @@ from utils.datetime import current, current_timestamp, is_time_difference_greate @singleton class OrderService: def __init__(self, config): - self.order_repo = OrderRepository(config) + self.config = config self.payment_service = PaymentService() + self.order_repo = OrderRepository(config) + self.user_repo = UserRepository(config) - def create_order(self, from_address, to_address): + def get_user_addresses(self, phone=None, email=None, address=None, payment_method=None): + if address is None: + if phone or email: + users = self.user_repo.get_or_create(User(phone=phone, email=email)) + addresses = set(user.address for user in users if address) + return list(addresses) + raise ValueError('A phone number, email, or address is required.') + return [address] + + def create_order(self, address=None): 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) + + self.order_repo.create(order_id, address, + self.config['PaymentAddresses']) return order_id def finish_order(self, order_id): diff --git a/services/payment.py b/payment_backend/services/payment.py similarity index 100% rename from services/payment.py rename to payment_backend/services/payment.py diff --git a/services/user.py b/payment_backend/services/user.py similarity index 100% rename from services/user.py rename to payment_backend/services/user.py diff --git a/tests/test_tronscan.py b/payment_backend/tests/test_tronscan.py similarity index 100% rename from tests/test_tronscan.py rename to payment_backend/tests/test_tronscan.py diff --git a/utils/__init__.py b/payment_backend/utils/__init__.py similarity index 100% rename from utils/__init__.py rename to payment_backend/utils/__init__.py diff --git a/payment_backend/utils/database.py b/payment_backend/utils/database.py new file mode 100644 index 0000000..f173ff3 --- /dev/null +++ b/payment_backend/utils/database.py @@ -0,0 +1,16 @@ +def pack_params(params_format="list", param_sql="{param}=%s", join_str=" AND ", **kwargs): + if params_format == "list": + params = [] + elif params_format == "dict": + params = {} + else: + raise ValueError("Unknown params format") + param_sqls = [] + for k, v in kwargs.items(): + if v is not None: + if params_format == "list": + params.append(v) + elif params_format == "dict": + params[k] = v + param_sqls.append(param_sql.format(param=k)) + return join_str.join(param_sqls), params diff --git a/utils/datetime.py b/payment_backend/utils/datetime.py similarity index 100% rename from utils/datetime.py rename to payment_backend/utils/datetime.py diff --git a/utils/tronscan.py b/payment_backend/utils/tronscan.py similarity index 100% rename from utils/tronscan.py rename to payment_backend/utils/tronscan.py diff --git a/repositories/user.py b/repositories/user.py deleted file mode 100644 index 4eec1e9..0000000 --- a/repositories/user.py +++ /dev/null @@ -1,40 +0,0 @@ -from custom_decorators import singleton -from database import Database -from utils.database import pack_params - - -@singleton -class UserRepository: - def __init__(self, config): - self.db = Database(config['MYSQL']) - - def get_user(self, phone=None, email=None, address=None): - params_sql, params = pack_params(phone=phone, email=email, address=address) - sql = f"SELECT name, phone, email, address FROM user WHERE {params_sql}" - cursor = self.db.execute_query(sql, params) - users = cursor.fetchall() - return users - - def create_user(self, phone=None, email=None, address=None): - _, params = pack_params(phone=phone, email=email, address=address) - sql = f"INSERT INTO user (phone, email, address) VALUES (%s, %s, %s)" - self.db.execute_query(sql, params) - self.db.commit() - - def record_exists(self, phone=None, email=None, address=None): - params_sql, params = pack_params(phone=phone, email=email, address=address) - sql = f"SELECT EXISTS(SELECT 1 FROM user WHERE {params_sql} LIMIT 1)" - cursor = self.db.execute_query(sql, params) - result = cursor.fetchone() - return bool(result[0]) - - def create_if_not_exists(self, phone=None, email=None, address=None): - if not self.record_exists(phone=phone, email=email, address=address): - self.create_user(phone=phone, email=email, address=address) - - def get_and_create_if_not_exists(self, phone=None, email=None, address=None): - users = self.get_user(phone=phone, email=email, address=address) - if len(users) == 0: - self.create_user(phone=phone, email=email, address=address) - else: - return users diff --git a/utils/database.py b/utils/database.py deleted file mode 100644 index 128d6a5..0000000 --- a/utils/database.py +++ /dev/null @@ -1,10 +0,0 @@ -def pack_params(**kwargs): - params = [] - param_sqls = [] - flag = False - for k, v in kwargs.items(): - flag = True - params.append(v) - param_sqls.append(f"{k}=%s") - if flag: - return " AND ".join(param_sqls), params