diff --git a/app/api/endpoints/address.py b/app/api/endpoints/address.py index 051b6ad..b25bf4f 100644 --- a/app/api/endpoints/address.py +++ b/app/api/endpoints/address.py @@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends from sqlalchemy.orm import Session from sqlalchemy import and_ from typing import List, Optional -from app.models.address import AddressDB, AddressCreate, AddressUpdate, AddressInfo +from app.models.address import AddressDB, AddressCreate, AddressUpdate, AddressInfo, AddressType from app.models.community import CommunityDB from app.models.database import get_db from app.api.deps import get_current_user @@ -21,6 +21,7 @@ async def create_address( if address.is_default: db.query(AddressDB).filter( and_( + AddressDB.address_type == address.address_type, AddressDB.user_id == current_user.userid, AddressDB.is_default == True ) @@ -50,6 +51,7 @@ async def create_address( @router.get("", response_model=ResponseModel) async def get_addresses( community_id: Optional[int] = None, + address_type : Optional[AddressType] = AddressType.PICKUP, db: Session = Depends(get_db), current_user: UserDB = Depends(get_current_user) ): @@ -59,6 +61,9 @@ async def get_addresses( ).filter( AddressDB.user_id == current_user.userid ) + + if address_type: + addresses.filter(AddressDB.address_type == address_type) if community_id is not None: addresses = addresses.filter(AddressDB.community_id == community_id) @@ -143,12 +148,14 @@ async def delete_address( @router.post("/{address_id}/set-default", response_model=ResponseModel) async def set_default_address( address_id: int, + address_type: AddressType = AddressType.PICKUP, db: Session = Depends(get_db), current_user: UserDB = Depends(get_current_user) ): """设置默认地址""" db.query(AddressDB).filter( and_( + AddressDB.address_type == address_type, AddressDB.user_id == current_user.userid, AddressDB.is_default == True ) diff --git a/app/api/endpoints/merchant_order.py b/app/api/endpoints/merchant_order.py index 3fe3b2f..00b3fc4 100644 --- a/app/api/endpoints/merchant_order.py +++ b/app/api/endpoints/merchant_order.py @@ -8,6 +8,7 @@ from app.models.merchant_order import ( MerchantOrderInfo, MerchantOrderStatus ) +from app.models.address import AddressDB, AddressType from app.models.merchant_product import MerchantProductDB from app.models.database import get_db from app.api.deps import get_current_user, get_admin_user, get_merchant_user @@ -69,12 +70,16 @@ async def create_merchant_order( order_id = CommonUtils.generate_order_id('M') verify_code = CommonUtils.generate_verify_code() + # 创建订单 - pay_amount = float(product.sale_price) + pay_amount = float(product.sale_price) * order.qty db_order = MerchantOrderDB( order_id=order_id, user_id=current_user.userid, merchant_product_id=order.merchant_product_id, + unit_price=product.sale_price, + qty=order.qty, + address_id=order.address_id, order_amount=pay_amount, pay_amount=pay_amount, gift_points=int(float(product.sale_price) * (float(product.gift_points_rate) / 100) * settings.POINT_RATIO), @@ -285,69 +290,41 @@ async def get_order_detail( current_user: UserDB = Depends(get_current_user) ): """获取订单详情""" - query = db.query( - MerchantOrderDB, - MerchantProductDB.name.label('product_name'), - MerchantProductDB.image_url.label('product_image'), - MerchantProductDB.tags.label('product_tags'), - MerchantDB.name.label('merchant_name'), - MerchantDB.latitude.label('merchant_latitude'), - MerchantDB.longitude.label('merchant_longitude'), - MerchantDB.phone.label('merchant_phone') - ) - - if longitude is not None and latitude is not None: - query = query.add_columns( - text("ST_Distance_Sphere(point(merchants.longitude, merchants.latitude), " - "point(:lon, :lat)) as distance").params(lon=longitude, lat=latitude) - ) - else: - query = query.add_columns(text("NULL as distance")) - - order = query.join( - MerchantProductDB, - MerchantOrderDB.merchant_product_id == MerchantProductDB.id - ).join( - MerchantDB, - MerchantProductDB.merchant_id == MerchantDB.id - ).filter( + order = db.query(MerchantOrderDB).filter( MerchantOrderDB.order_id == order_id ).first() - if not order: return error_response(code=404, message="订单不存在") - - # 检查权限 - if order.MerchantOrderDB.user_id != current_user.userid: - return error_response(code=403, message="无权查看此订单") - - # 构建返回数据 + + product = db.query(MerchantProductDB).filter( + MerchantProductDB.id == order.merchant_product_id + ).first() + + if not product: + return error_response(code=404, message="商品不存在") + + + merchant = db.query(MerchantDB).filter( + MerchantDB.id == product.merchant_id + ).first() + + if not merchant: + return error_response(code=404, message="商家不存在") + + order_data = { - "id": order.MerchantOrderDB.id, - "order_id": order.MerchantOrderDB.order_id, - "user_id": order.MerchantOrderDB.user_id, - "merchant_product_id": order.MerchantOrderDB.merchant_product_id, - "order_amount": order.MerchantOrderDB.order_amount, - "status": order.MerchantOrderDB.status, - "order_verify_code": order.MerchantOrderDB.order_verify_code, - "verify_time": order.MerchantOrderDB.verify_time, - "verify_user_id": order.MerchantOrderDB.verify_user_id, - "create_time": order.MerchantOrderDB.create_time, - "update_time": order.MerchantOrderDB.update_time, - # 商品信息 - "product_name": order.product_name, - "product_tags": order.product_tags, - "product_image": process_image(order.product_image).thumbnail(width=800, height=800).format(ImageFormat.WEBP).build(), - # 商家信息 - "merchant_name": order.merchant_name, - "merchant_latitude": order.merchant_latitude, - "merchant_longitude": order.merchant_longitude, - "merchant_phone": order.merchant_phone, - # 距离信息 - "distance": round(order[8]) if order[8] else None + **order.model_dump(), + **product.model_dump(), + **merchant.model_dump() } + if order.address_id: + address = db.query(AddressDB).filter( + AddressDB.id == order.address_id + ).first() + order_data["address"] = address.model_dump() + return success_response(data=order_data) @router.post("/calculate-price", response_model=ResponseModel) diff --git a/app/models/address.py b/app/models/address.py index ef33671..570b6bf 100644 --- a/app/models/address.py +++ b/app/models/address.py @@ -1,9 +1,16 @@ from sqlalchemy import Column, Integer, String, ForeignKey, DateTime, Boolean, Enum from sqlalchemy.sql import func +from sqlalchemy.dialects.mysql import DECIMAL from pydantic import BaseModel, Field from .database import Base from typing import Optional from app.models.user import Gender # 复用用户模型中的性别枚举 +import enum + +# 地址类型枚举 +class AddressType(str, enum.Enum): + PICKUP = "PICKUP" # 代取地址 + COMMON = "COMMON" # 通用地址 # 数据库模型 class AddressDB(Base): @@ -20,6 +27,9 @@ class AddressDB(Base): phone = Column(String(11)) gender = Column(Enum(Gender), nullable=False, default=Gender.UNKNOWN) is_default = Column(Boolean, default=False) + address_type = Column(Enum(AddressType), nullable=False, default=AddressType.PICKUP) # 地址类型 + longitude = Column(DECIMAL(9,6), nullable=True) # 经度,精确到小数点后6位 + latitude = Column(DECIMAL(9,6), nullable=True) # 纬度,精确到小数点后6位 create_time = Column(DateTime(timezone=True), server_default=func.now()) update_time = Column(DateTime(timezone=True), onupdate=func.now()) @@ -32,6 +42,9 @@ class AddressCreate(BaseModel): phone: str = Field(..., pattern="^1[3-9]\d{9}$") gender: Gender = Gender.UNKNOWN is_default: bool = True + address_type: AddressType = AddressType.PICKUP # 地址类型,默认为代取 + longitude: Optional[float] = Field(None, ge=-180, le=180) # 经度 + latitude: Optional[float] = Field(None, ge=-90, le=90) # 纬度 class AddressUpdate(BaseModel): community_id: Optional[int] = None @@ -41,6 +54,9 @@ class AddressUpdate(BaseModel): phone: Optional[str] = Field(None, pattern="^1[3-9]\d{9}$") gender: Optional[Gender] = None is_default: Optional[bool] = None + address_type: Optional[AddressType] = None # 地址类型 + longitude: Optional[float] = Field(None, ge=-180, le=180) # 经度 + latitude: Optional[float] = Field(None, ge=-90, le=90) # 纬度 class AddressInfo(BaseModel): id: int @@ -53,6 +69,9 @@ class AddressInfo(BaseModel): phone: str gender: Gender is_default: bool + address_type: AddressType # 地址类型 + longitude: Optional[float] = None # 经度 + latitude: Optional[float] = None # 纬度 class Config: from_attributes = True \ No newline at end of file diff --git a/app/models/merchant_order.py b/app/models/merchant_order.py index b2f7ec5..fdb3b6b 100644 --- a/app/models/merchant_order.py +++ b/app/models/merchant_order.py @@ -24,6 +24,9 @@ class MerchantOrderDB(Base): qrcode_url = Column(String(200)) # 核销码二维码图片地址 user_id = Column(Integer, ForeignKey("users.userid"), nullable=False) merchant_product_id = Column(Integer, ForeignKey("merchant_products.id"), nullable=False) + address_id = Column(Integer, ForeignKey("delivery_addresses.id"), nullable=False) # 收货地址ID + qty = Column(Integer, nullable=False, default=1) # 购买数量 + unit_price = Column(DECIMAL(10,2), nullable=False) # 产品单价 order_amount = Column(DECIMAL(10,2), nullable=False) pay_amount = Column(DECIMAL(10,2), nullable=False, default=0) gift_points = Column(Integer, nullable=False, default=0) # 赠送的积分 @@ -39,16 +42,23 @@ class MerchantOrderDB(Base): refund_transaction_id = Column(String(64)) # 微信退款交易号 refund_time = Column(DateTime(timezone=True), nullable=True) + class MerchantOrderCreate(BaseModel): merchant_product_id: int + qty: int = Field(..., gt=0) # 购买数量,必须大于0 + address_id: int # 收货地址ID class MerchantOrderVerify(BaseModel): verify_code: str + class MerchantOrderInfo(BaseModel): id: int order_id: str user_id: int merchant_product_id: int + address_id: int # 收货地址ID + qty: int # 购买数量 + unit_price: float # 产品单价 order_amount: float pay_amount: float gift_points: int diff --git a/app/sql/v1.1.sql b/app/sql/v1.1.sql index 4770927..d4f52e7 100644 --- a/app/sql/v1.1.sql +++ b/app/sql/v1.1.sql @@ -52,4 +52,38 @@ ADD COLUMN latitude DECIMAL(9,6) COMMENT '纬度,精确到小数点后6位'; -- 为merchant_products表添加已售数量字段 ALTER TABLE merchant_products -ADD COLUMN sold_total INT NOT NULL DEFAULT 0 COMMENT '已售数量'; \ No newline at end of file +ADD COLUMN sold_total INT NOT NULL DEFAULT 0 COMMENT '已售数量'; + +-- 为delivery_addresses表添加地址类型和经纬度字段 +ALTER TABLE delivery_addresses +ADD COLUMN address_type ENUM('PICKUP', 'COMMON') NOT NULL DEFAULT 'PICKUP' COMMENT '地址类型:代取或通用', +ADD COLUMN longitude DECIMAL(9,6) COMMENT '经度,精确到小数点后6位', +ADD COLUMN latitude DECIMAL(9,6) COMMENT '纬度,精确到小数点后6位'; + + + +-- 添加address_id字段(先不添加外键约束) +ALTER TABLE merchant_orders +ADD COLUMN address_id INT COMMENT '收货地址ID' AFTER merchant_product_id; + +-- 添加qty字段 +ALTER TABLE merchant_orders +ADD COLUMN qty INT NOT NULL DEFAULT 1 COMMENT '购买数量' AFTER address_id; + +-- 添加unit_price字段 +ALTER TABLE merchant_orders +ADD COLUMN unit_price DECIMAL(10,2) NOT NULL COMMENT '产品单价' AFTER qty; + +-- 更新现有记录的address_id为NULL +UPDATE merchant_orders +SET address_id = NULL +WHERE address_id IS NOT NULL; + +-- 添加外键约束 +ALTER TABLE merchant_orders +ADD CONSTRAINT fk_merchant_orders_address +FOREIGN KEY (address_id) REFERENCES delivery_addresses(id); + +-- 修改address_id为NOT NULL +ALTER TABLE merchant_orders +MODIFY COLUMN address_id INT NOT NULL COMMENT '收货地址ID'; \ No newline at end of file diff --git a/jobs.sqlite b/jobs.sqlite index fe7f531..94f5fb1 100644 Binary files a/jobs.sqlite and b/jobs.sqlite differ