Mini Project: POS System
เอาล่ะ !! หลังจากเราเรียนรู้วิธีการสร้าง API กันมาคร่าว ๆ แล้ว เราจะนำความรู้มาสร้างเป็น Application ของเรากัน โดยขอให้ทุกคนเริ่มต้นด้วย Skeleton Code นี้
from typing import Union
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class Product:
@staticmethod
def is_valid_barcode(barcode_no: str) -> bool:
return len(barcode_no) == 13 and barcode_no.isdigit()
class ProductDTO(BaseModel):
barcode_no: str
name: str
price: float
def __init__(self, barcode_no: str, name: str, price: float) -> None:
if not Product.is_valid_barcode(barcode_no):
raise ValueError("Barcode number is invalid")
self.barcode_no = barcode_no
self.name = name
self.price = price
def to_dict(self) -> dict:
return {
"barcode_no": self.barcode_no,
"name": self.name,
"price": self.price
}
class POSController:
def __init__(self) -> None:
self.products_list: list[Product] = []
def add_product(self, product: Product):
if self.get_product_by_barcode(product.barcode_no):
raise ValueError(f"Product with the barcode '{product.barcode_no}' already exists")
self.products_list.append(product)
def get_product_by_barcode(self, barcode_no: str) -> Union[Product, None]:
for product in self.products_list:
if product.barcode_no == barcode_no:
return product
return None
def get_all_products(self) -> list[dict]:
return [product.to_dict() for product in self.products_list]
def delete_product_by_barcode(self, barcode_no: str):
product = self.get_product_by_barcode(barcode_no)
if not product:
raise ValueError("Product not found")
self.products_list.remove(product)
controller = POSController()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.post("/products", response_model=Union[Product.ProductDTO, dict])
def create_product(product: Product.ProductDTO):
try:
new_product = Product(product.barcode_no, product.name, product.price)
controller.add_product(new_product)
return new_product.to_dict()
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
@app.get("/products/{barcode_no}", response_model=Union[Product.ProductDTO, dict])
def get_product_by_barcode(barcode_no: str):
try:
product = controller.get_product_by_barcode(barcode_no)
if not product:
raise HTTPException(status_code=404, detail="Product not found")
return product.to_dict()
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
@app.get("/products")
def get_all_products():
return controller.get_all_products()
@app.delete("/products/{barcode_no}")
def delete_product_by_barcode(barcode_no: str):
try:
controller.delete_product_by_barcode(barcode_no)
return {"message": "Product deleted successfully"}
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))Controller Class 🤖
สำหรับ Best Practice; เราจะไม่ใส่ Bussiness Logic (ตรรกะการทำงานของโปรแกรม) ไว้ในส่วน View (ฟังก์ชันที่ Handle Request) แต่ละให้ View เรียกใช้ Controller เป็น Interface แทน ทำให้ลดความซับซ้อนของโปรแกรมได้ เช่น เมื่อมีการเรียก Endpoint GET /products ฟังก์ชัน get_all_products() จะเรียก Method get_all_products() ใน Controller
สังเกตว่า Logic จะอยู่ใน Controller Class และเรา Abstract Logic ที่ซับซ้อนเหล่านั้นด้วย Method เหมือนกับที่เราใช่ Method ของ FastAPI โดยที่เราไม่ต้องรู้ว่าข้างในมัน Implement ยังไงนั่นแหละ (คิดสภาพว่าเราต้องมา Implement Logic พวกนั้นเองสิ 😰)
Requirements 😎
ตอนนี้เรามี Endpoint ต่อไปนี้
GET /
-
Read Root
GET /products
-
Return ข้อมูล Products ทั้งหมดที่มีในระบบ POS
GET /products/{barcode_no}
barcode_no: str
Return ข้อมูล Product ตามหมายเลข barcode_no ใน Path Variable
POST /products
เพิ่ม Product ใหม่เข้าไปในระบบ
DELETE /products/{barcode_no}
barcode_no: str
ลบ Product ตามหมายเลข barcode_no ใน Path Variable
ให้สร้าง Endpoint เพิ่มเติมตามความเหมาะสม โดยให้รองรับระบบต่อไปนี้
POST /orders
POST /ordersสามารถซื้อสินค้าได้ โดยการซื้อสินค้ารับ Request Body ตามรูปแบบที่กำหนด การส่งคำสั่งในการซื้อสินค้าหนึ่งครั้ง นับเป็น 1 Order
และ Return เป็นรายละเอียดของ Order ซึ่งต้องมีการแสดงรายละเอียดของสินค้า และราคารวมทั้ง Order ดังตัวอย่าง
GET /products/{barcode_no}/order
GET /products/{barcode_no}/orderสามารถตรวจสอบได้ว่า สินค้าใด ๆ มีประวัติการซื้อเป็นอย่างไรบ้าง โดยต้องระบุเวลาที่ซื้อ จำนวนที่ซื้อ และราคารวมต่อ Order และยอดขายทั้งหมดของที่สินค้าประเภทนั้นขายได้ กำหนดให้มีรูปแบบ Response ตามที่กำหนด
GET /customers/{customer_name}/orders
GET /customers/{customer_name}/ordersสามารถตรวจสอบได้ว่าลูกค้า (customer) ใด ๆ มีการซื้อสินค้าใด ๆ ไปบ้าง และรวมราคาว่าลูกค้าคนนั้นซื้อของในระบบเท่าไร (
total_spending)
Last updated
Was this helpful?