This commit is contained in:
aaron 2025-03-22 09:48:27 +08:00
parent e79785bce6
commit e77c600262
6 changed files with 105 additions and 58 deletions

View File

@ -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
)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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 '已售数量';
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';

Binary file not shown.