From 085501b946d1cd529fe8ea02f7537e6e5023d6fc Mon Sep 17 00:00:00 2001 From: QuangMinh_123 Date: Tue, 9 Dec 2025 14:10:13 +0700 Subject: [PATCH] UserPhoneDone --- ...bl-user_avatar-insert-update-delete-get.py | 40 ++++-- apps/crud.py | 124 ++++++++++++++++-- apps/filters.py | 2 +- apps/requirements.txt | 4 +- apps/validators.py | 11 ++ 5 files changed, 158 insertions(+), 23 deletions(-) create mode 100644 apps/validators.py diff --git a/apps/ailbl-user_avatar-insert-update-delete-get.py b/apps/ailbl-user_avatar-insert-update-delete-get.py index e0cc2de..575f79a 100644 --- a/apps/ailbl-user_avatar-insert-update-delete-get.py +++ b/apps/ailbl-user_avatar-insert-update-delete-get.py @@ -1,5 +1,8 @@ import crud from flask import jsonify, request +from filters import PhonePage +from helpers import CORS_HEADERS +from validators import validate_phone_number @@ -34,14 +37,21 @@ def main(): def make_insert_request(): try: user_id = request.headers.get("X-User") # Lay user_id tu header X-User - # Lay file tu form-data voi key la 'avatar' - file = request.files.get("avatar") - if not user_id or not file: - return jsonify({"error": "user_id or file is required"}), 400 + if not user_id: + return jsonify({"error": "user_id or file is required"}), 400, CORS_HEADERS - response, status = crud - return jsonify(response), status + data = request.get_json() + if not data: + return jsonify({"error": "phone_number is required"}), 400, CORS_HEADERS + + phone_number = data.get("phone_number") + if not validate_phone_number(phone_number): + return jsonify({"error": "Invalid phone number"}), 400, CORS_HEADERS + + response, status = crud.create_phone(user_id, data) + + return jsonify(response), status, CORS_HEADERS except Exception as e: return jsonify({"error": str(e)}), 500 @@ -53,8 +63,15 @@ def make_delete_request(): user_id = request.headers.get("X-User") # Lay user_id tu header X-User if not user_id: return jsonify({"error": "user_id is required"}), 400 - # Call CRUD function to delete avatar + phone_id = request.header.get("X-Fission-Params-UserPhoneId") + if not phone_id: + return jsonify({"error": "phone_id is required"}), 400 + + # Kiem tra so dien thoai co ton tai trong db khong ? + if not crud.exists_phone(user_id, phone_id): + return jsonify({"error": "Phone not found"}), 404 + response, status = crud return jsonify(response), status except Exception as e: @@ -65,9 +82,12 @@ def make_get_request(): try: user_id = request.headers.get("X-User") if not user_id: - return jsonify({"error": "user_id is required"}), 400 + return jsonify({"error": "user_id is required"}), 400, CORS_HEADERS - return crud - # return jsonify(response), status + # Lấy tham số filter và phân trang từ request + paging = PhonePage.from_request_queries() # Sử dụng default_factory để lấy filter và paging + + response = crud.filter_phone(user_id, paging) + return jsonify(response), 200, CORS_HEADERS except Exception as e: return jsonify({"error": str(e)}), 500 diff --git a/apps/crud.py b/apps/crud.py index 5c6ce0b..8ae646a 100644 --- a/apps/crud.py +++ b/apps/crud.py @@ -1,38 +1,140 @@ import io -from flask import Response -from helpers import S3_BUCKET, get_secret, s3_client +from flask import jsonify, request +from helpers import init_db_connection, CORS_HEADERS from PIL import Image # Create&Update function to upload or update user avatar S3/Minio -def create_phone(user_id: str, file): +def create_phone(user_id: str, data): try: + conn = init_db_connection() + cursor = conn.cursor() + # Câu truy vấn SQL để thêm số điện thoại vào bảng UserPhone + query = """ + INSERT INTO UserPhone (user_id, PhoneNumber, Prefix, Created, Modified) + VALUES (%s, %s, %s, NOW(), NOW()) + """ + # Lấy các trường từ data + phone_number = data.get("phone_number") + prefix = data.get("prefix", None) # Nếu không có prefix, có thể để null + + # Thực thi câu truy vấn SQL + cursor.execute(query, (user_id, phone_number, prefix)) + conn.commit() # Lưu thay đổi vào cơ sở dữ liệu - return result, 200 + result = cursor.fetchall(), + + return result, 200, CORS_HEADERS except Exception as e: return {"error": str(e)}, 500 + -def get_phone(user_id: str): # Read function to get user avatar from S3/Minio +def filter_phone(user_id: str, paging): # Read function to get user avatar from S3/Minio try: + conn = init_db_connection() + cursor = conn.cursor() + #Xay dung dieu kien loc + conditions = ["user_id = %(user_id)s"] + values = {"user_id": user_id} # Điều kiện cơ bản cho user_id + + # Lọc theo phone_number + if paging.filter.phone_number: + conditions.append("LOWER(PhoneNumber) LIKE %(phone_number)s") + values["phone_number"] = f"%{paging.filter.phone_number.lower()}%" - return Response( + # Lọc theo prefix + if paging.filter.prefix: + conditions.append("Prefix = %(prefix)s") + values["prefix"] = paging.filter.prefix + + # Lọc theo ngày tạo + if paging.filter.created_from: + conditions.append("Created >= %(created_from)s") + values["created_from"] = paging.filter.created_from + + if paging.filter.created_to: + conditions.append("Created <= %(created_to)s") + values["created_to"] = paging.filter.created_to + + # Lọc theo ngày sửa đổi + if paging.filter.modified_from: + conditions.append("Modified >= %(modified_from)s") + values["modified_from"] = paging.filter.modified_from + + if paging.filter.modified_to: + conditions.append("Modified <= %(modified_to)s") + values["modified_to"] = paging.filter.modified_to - ), 200 - + # Ket hop dieu kien loc + where_clause = "AND".join(conditions) + if where_clause: + where_clause = "WHERE" + where_clause + + #Sap xep ket qua neu co: + order_clause="" + if paging.sortby: + direction="ASC" if paging.asc else "DESC" + order_clause = f"ORDER BY {paging.sortby} {direction}" + + # Gop Truy van + sql = f""" + SELECT *, COUNT(*) OVER() AS total + FROM ailbl_user_phone + {where_clause} + {order_clause} + LIMIT %(limit)s OFFSET%(offset)s + """ + values["limit"] = paging.size + values["offset"] = paging.page * paging.size + + cursor.execute(sql, values) # Thuc Thi Cau Truy Van + phones = cursor.fetchall() + + return phones, 200, CORS_HEADERS + except Exception as e: return {"error": str(e)}, 500 + finally: + if conn: + conn.close() # Delete Function to delete user avatar from S3/Minio -def delete_phone(user_id: str) -> dict: +def delete_phone(phone_id: str, user_id: str) -> dict: try: + conn = init_db_connection() + cursor = conn.cursor() - - return result, 200 + query = "DELETE FROM ailbl_user_phone WHERE id = %s AND user_id = %s" + cursor.execute(query, (phone_id, user_id)) + conn.commit() # Save DB + return {"message": "Phone deleted successfully"}, 200, CORS_HEADERS + except Exception as e: return {"error": str(e)}, 500 + + +def exists_phone( phone_id:str, user_id:str): + try: + conn = init_db_connection() + cursor = conn.cursor() + + cursor.execute(""" + SELECT 1 + FROM ailbl_user_phone + WHERE id = %s AND user_id = %s; + """, (phone_id, user_id)) + + row = cursor.fetchone() # Co ket qua thi tra ve du lieu 1 dong + return row is not None # Nếu có dòng dữ liệu, trả về True (tồn tại số điện thoại), nếu không, trả về False + except Exception as e: + return False + finally: + if conn: + conn.close() + diff --git a/apps/filters.py b/apps/filters.py index 7fa1860..98d3536 100644 --- a/apps/filters.py +++ b/apps/filters.py @@ -41,7 +41,7 @@ class Page: @dataclasses.dataclass -class PhonePage(Page): +class PhonePage(Page): # Ke thua sortby: Optional[str] = None filter: PhoneFilter = dataclasses.field( default_factory=PhoneFilter.from_request_queries diff --git a/apps/requirements.txt b/apps/requirements.txt index f922a61..3347361 100644 --- a/apps/requirements.txt +++ b/apps/requirements.txt @@ -1,4 +1,6 @@ -# Flask==3.1.0 +Flask==3.1.0 +phonenumbers==8.12.17 +requests==2.25.1 # psycopg2-binary==2.9.10 # pydantic==2.11.3 # minio==7.2.5 diff --git a/apps/validators.py b/apps/validators.py new file mode 100644 index 0000000..57affae --- /dev/null +++ b/apps/validators.py @@ -0,0 +1,11 @@ +# validators.py +import phonenumbers +from phonenumbers import NumberParseException + +def validate_phone_number(phone_number: str) -> bool: + """Kiểm tra tính hợp lệ của số điện thoại""" + try: + parsed_number = phonenumbers.parse(phone_number) # Phân tích số điện thoại + return phonenumbers.is_valid_number(parsed_number) # Kiểm tra tính hợp lệ của số + except NumberParseException: + return False # Trả về False nếu số không hợp lệ