from decimal import Decimal from config import settings from fastapi import FastAPI, Depends from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel, field_validator from typing import List, Optional from datetime import datetime, timedelta, date from SQLAlchemy import ItemTrackManager, ItemTrack from auth import get_current_user app = FastAPI(title=settings.TITLE, version=settings.VERSION, debug=settings.DEBUG) itemTrackManager = ItemTrackManager(settings.DATABASE_URL) # CORS 配置 app.add_middleware( CORSMiddleware, allow_credentials=True, allow_origin_regex=r"http[s]?://172\.30\.0\..*:5173|http[s]?://.*\.yuany3721\.site|http[s]?://yuany3721\.site", allow_methods=["GET", "POST"], allow_headers=["*"], ) # 认证依赖 - 所有路由都需要认证 dependencies = [Depends(get_current_user)] @app.get("/item_track/") async def root(): return {"api": "item_track", "message": "tracking items backend", "auth": "Protected by Authentik OIDC"} @app.get("/item_track/get_all_items", dependencies=dependencies) async def get_all_items(): """ 获取所有未删除的商品 Returns: list: 商品列表 """ items = itemTrackManager.get_all_items() res = [item.__dict__ for item in items] for r in res: r.pop("_sa_instance_state", None) if isinstance(r["discontinued_at"], datetime): r["till_now"] = (datetime.now() - r["discontinued_at"]).days r["inuse"] = False else: r["till_now"] = (datetime.now() - r["start_at"]).days r["inuse"] = True return res @app.get("/item_track/get_item/{item_id}", dependencies=dependencies) async def get_item(item_id: int): """ 获取指定ID的商品 Args: item_id: 商品ID Returns: ItemTrack: 商品数据 """ item = itemTrackManager.get_item_by_id(item_id) if item: res = item.__dict__ res.pop("_sa_instance_state", None) if isinstance(res["discontinued_at"], datetime): res["till_now"] = (datetime.now() - res["discontinued_at"]).days res["inuse"] = False else: res["till_now"] = (datetime.now() - res["start_at"]).days res["inuse"] = True return res return {} class CreateItemRequest(BaseModel): name: str price: Decimal = Decimal("0") start_at: date = datetime.now().date() discontinued_at: Optional[date] = None discontinued_price: Decimal = Decimal("0") notes: str = "" @field_validator('discontinued_at', mode='before') def handle_empty_string(cls, v): if v == "": return None return v @app.post("/item_track/create_item", dependencies=dependencies) async def create_item(item: CreateItemRequest): """ 创建新商品 Args: item: 商品数据 Returns: BaseModel: 创建的商品 """ return itemTrackManager.create_item( name=item.name, price=item.price, start_at=item.start_at, discontinued_at=item.discontinued_at, discontinued_price=item.discontinued_price, notes=item.notes, ) class UpdateItemRequest(BaseModel): id: int name: str price: Decimal = Decimal("0") start_at: date = datetime.now().date() discontinued_at: Optional[date] = None discontinued_price: Decimal = Decimal("0") notes: str = "" @field_validator('discontinued_at', mode='before') def handle_empty_string(cls, v): if v == "": return None return v @app.post("/item_track/update_item", dependencies=dependencies) async def update_item(item: UpdateItemRequest): """ 更新商品信息 Args: item: 商品数据 Returns: BaseModel: 更新的商品 """ print(item) return itemTrackManager.update_item( id=item.id, name=item.name, price=item.price, start_at=item.start_at, discontinued_at=item.discontinued_at, discontinued_price=item.discontinued_price, notes=item.notes, ) class DeleteItemRequest(BaseModel): id: int @app.post("/item_track/delete_item", dependencies=dependencies) async def delete_item(request: DeleteItemRequest): """ 软删除商品 Args: id: 商品ID Returns: bool: 是否删除成功 """ return itemTrackManager.delete_item(request.id) if __name__ == "__main__": import uvicorn uvicorn.run("main:app", host="0.0.0.0", port=7023, reload=settings.DEBUG)