增加优惠券活动接口
This commit is contained in:
parent
683aa8e2c2
commit
3dc82e0986
209
app/api/endpoints/coupon_activity.py
Normal file
209
app/api/endpoints/coupon_activity.py
Normal file
@ -0,0 +1,209 @@
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func, and_
|
||||
from app.models.coupon_activity import (
|
||||
CouponActivityDB,
|
||||
CouponActivityCreate,
|
||||
CouponActivityUpdate,
|
||||
CouponActivityInfo
|
||||
)
|
||||
from app.models.coupon_receive_record import CouponReceiveRecordDB
|
||||
from app.models.coupon import CouponDB, UserCouponDB, CouponStatus, CouponInfo
|
||||
from app.models.database import get_db
|
||||
from app.api.deps import get_current_user, get_admin_user
|
||||
from app.models.user import UserDB
|
||||
from app.core.response import success_response, error_response, ResponseModel
|
||||
from typing import Optional, List
|
||||
from datetime import datetime, time
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("", response_model=ResponseModel)
|
||||
async def create_coupon_activity(
|
||||
activity: CouponActivityCreate,
|
||||
db: Session = Depends(get_db),
|
||||
admin: UserDB = Depends(get_admin_user)
|
||||
):
|
||||
"""创建优惠券活动(管理员)"""
|
||||
# 检查优惠券是否存在
|
||||
for coupon_id in activity.coupon_config.keys():
|
||||
coupon = db.query(CouponDB).filter(CouponDB.id == coupon_id).first()
|
||||
if not coupon:
|
||||
return error_response(code=404, message=f"优惠券ID {coupon_id} 不存在")
|
||||
# 检查数量是否大于0
|
||||
if activity.coupon_config[coupon_id] <= 0:
|
||||
return error_response(code=400, message=f"优惠券ID {coupon_id} 的数量必须大于0")
|
||||
|
||||
db_activity = CouponActivityDB(**activity.model_dump())
|
||||
db.add(db_activity)
|
||||
|
||||
try:
|
||||
db.commit()
|
||||
db.refresh(db_activity)
|
||||
return success_response(data=CouponActivityInfo.model_validate(db_activity))
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
return error_response(code=500, message=f"创建失败: {str(e)}")
|
||||
|
||||
@router.get("/{activity_id}", response_model=ResponseModel)
|
||||
async def get_coupon_activity(
|
||||
activity_id: int,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""获取优惠券活动详情"""
|
||||
activity = db.query(CouponActivityDB).filter(
|
||||
CouponActivityDB.id == activity_id
|
||||
).first()
|
||||
|
||||
if not activity:
|
||||
return error_response(code=404, message="活动不存在")
|
||||
|
||||
# 获取活动对应的优惠券
|
||||
coupons = db.query(CouponDB).filter(
|
||||
CouponDB.id.in_(activity.coupon_config.keys())
|
||||
).all()
|
||||
|
||||
result = CouponActivityInfo.model_validate(activity)
|
||||
result.coupons = [CouponInfo.model_validate(coupon) for coupon in coupons]
|
||||
|
||||
return success_response(data=result)
|
||||
|
||||
|
||||
@router.get("", response_model=ResponseModel)
|
||||
async def get_coupon_activities(
|
||||
is_active: Optional[bool] = None,
|
||||
skip: int = Query(0, ge=0),
|
||||
limit: int = Query(20, ge=1, le=100),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""获取优惠券活动列表"""
|
||||
query = db.query(CouponActivityDB)
|
||||
|
||||
if is_active is not None:
|
||||
query = query.filter(CouponActivityDB.is_active == is_active)
|
||||
|
||||
total = query.count()
|
||||
|
||||
activities = query.order_by(CouponActivityDB.create_time.desc())\
|
||||
.offset(skip)\
|
||||
.limit(limit)\
|
||||
.all()
|
||||
|
||||
return success_response(data={
|
||||
"total": total,
|
||||
"items": [CouponActivityInfo.model_validate(a) for a in activities]
|
||||
})
|
||||
|
||||
@router.post("/{activity_id}/receive", response_model=ResponseModel)
|
||||
async def receive_coupons(
|
||||
activity_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: UserDB = Depends(get_current_user)
|
||||
):
|
||||
"""领取优惠券"""
|
||||
# 查询活动
|
||||
activity = db.query(CouponActivityDB).filter(
|
||||
CouponActivityDB.id == activity_id,
|
||||
CouponActivityDB.is_active == True
|
||||
).first()
|
||||
|
||||
if not activity:
|
||||
return error_response(code=404, message="活动不存在或已结束")
|
||||
|
||||
# 检查领取时间
|
||||
current_time = datetime.now().time()
|
||||
if current_time < activity.daily_start_time or current_time > activity.daily_end_time:
|
||||
return error_response(code=400, message="不在领取时间范围内")
|
||||
|
||||
# 检查今日领取次数
|
||||
today = datetime.now().date()
|
||||
|
||||
receive_count = db.query(func.count(CouponReceiveRecordDB.id)).filter(
|
||||
CouponReceiveRecordDB.user_id == current_user.userid,
|
||||
CouponReceiveRecordDB.activity_id == activity_id,
|
||||
CouponReceiveRecordDB.receive_date == today
|
||||
).scalar()
|
||||
|
||||
if receive_count >= activity.daily_limit:
|
||||
return error_response(code=400, message="今日领取次数已达上限")
|
||||
|
||||
try:
|
||||
# 发放优惠券
|
||||
for coupon_id, count in activity.coupon_config.items():
|
||||
coupon = db.query(CouponDB).filter(CouponDB.id == coupon_id).first()
|
||||
if coupon:
|
||||
# 循环发放指定数量的优惠券
|
||||
for _ in range(count):
|
||||
# 当天晚上12点过期
|
||||
expire_time = datetime.combine(today, datetime.max.time())
|
||||
|
||||
user_coupon = UserCouponDB(
|
||||
user_id=current_user.userid,
|
||||
coupon_id=coupon.id,
|
||||
coupon_name=coupon.name,
|
||||
coupon_amount=coupon.amount,
|
||||
expire_time=expire_time,
|
||||
status=CouponStatus.UNUSED
|
||||
)
|
||||
db.add(user_coupon)
|
||||
|
||||
# 检查是否领取过优惠券
|
||||
receive_record = db.query(CouponReceiveRecordDB).filter(
|
||||
CouponReceiveRecordDB.user_id == current_user.userid,
|
||||
CouponReceiveRecordDB.activity_id == activity_id,
|
||||
CouponReceiveRecordDB.receive_date == today
|
||||
).first()
|
||||
|
||||
if receive_record:
|
||||
receive_record.receive_count += 1
|
||||
else:
|
||||
record = CouponReceiveRecordDB(
|
||||
user_id=current_user.userid,
|
||||
activity_id=activity_id,
|
||||
receive_date=today,
|
||||
receive_count=1
|
||||
)
|
||||
db.add(record)
|
||||
|
||||
db.commit()
|
||||
return success_response(message="领取成功")
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
return error_response(code=500, message=f"领取失败: {str(e)}")
|
||||
|
||||
@router.put("/{activity_id}", response_model=ResponseModel)
|
||||
async def update_coupon_activity(
|
||||
activity_id: int,
|
||||
activity: CouponActivityUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
admin: UserDB = Depends(get_admin_user)
|
||||
):
|
||||
"""更新优惠券活动(管理员)"""
|
||||
db_activity = db.query(CouponActivityDB).filter(
|
||||
CouponActivityDB.id == activity_id
|
||||
).first()
|
||||
|
||||
if not db_activity:
|
||||
return error_response(code=404, message="活动不存在")
|
||||
|
||||
# 检查优惠券是否存在
|
||||
if activity.coupon_config:
|
||||
for coupon_id, count in activity.coupon_config.items():
|
||||
coupon = db.query(CouponDB).filter(CouponDB.id == coupon_id).first()
|
||||
if not coupon:
|
||||
return error_response(code=404, message=f"优惠券ID {coupon_id} 不存在")
|
||||
# 检查数量是否大于0
|
||||
if count <= 0:
|
||||
return error_response(code=400, message=f"优惠券ID {coupon_id} 的数量必须大于0")
|
||||
|
||||
update_data = activity.model_dump(exclude_unset=True)
|
||||
for key, value in update_data.items():
|
||||
setattr(db_activity, key, value)
|
||||
|
||||
try:
|
||||
db.commit()
|
||||
db.refresh(db_activity)
|
||||
return success_response(data=CouponActivityInfo.model_validate(db_activity))
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
return error_response(code=500, message=f"更新失败: {str(e)}")
|
||||
@ -260,12 +260,18 @@ async def create_order(
|
||||
"path": f"/pages/order/detail/index?id={db_order.orderid}"
|
||||
})
|
||||
|
||||
# 超过晚上8点,则使用明天送达的文案
|
||||
if db_order.create_time.time() > datetime.time(20, 0, 0):
|
||||
success_text = settings.ORDER_SUCCESS_TOMORROW_TEXT
|
||||
else:
|
||||
success_text = settings.ORDER_SUCCESS_TODAY_TEXT
|
||||
|
||||
return success_response(
|
||||
message="订单创建成功",
|
||||
data={
|
||||
"order": OrderInfo.model_validate(db_order),
|
||||
"packages": [OrderPackageInfo.model_validate(p) for p in packages],
|
||||
"success_text" : settings.ORDER_SUCCESS_TEXT
|
||||
"success_text" : success_text
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
|
||||
@ -19,7 +19,8 @@ class Settings(BaseSettings):
|
||||
ORDER_DELIVERYMAN_SHARE_RATIO: float = 0.8 # 配送员分账比例
|
||||
|
||||
#订单创建成功文案
|
||||
ORDER_SUCCESS_TEXT: str = "订单预计今晚前送达,请注意查收"
|
||||
ORDER_SUCCESS_TODAY_TEXT: str = "订单预计今晚前送达,请注意查收"
|
||||
ORDER_SUCCESS_TOMORROW_TEXT: str = "订单预计明晚前送达,请注意查收"
|
||||
ORDER_PREORDER_PRICE_TEXT: str = "基础费3元 (含5件包裹) 超出部分0.5元/件"
|
||||
|
||||
# JWT 配置
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from app.api.endpoints import wechat,user, address, community, station, order, coupon, community_building, upload, merchant, merchant_product, merchant_order, point, config, merchant_category, log, account,merchant_pay_order, message, bank_card, withdraw, mp, point_product, point_product_order
|
||||
from app.api.endpoints import wechat,user, address, community, station, order, coupon, community_building, upload, merchant, merchant_product, merchant_order, point, config, merchant_category, log, account,merchant_pay_order, message, bank_card, withdraw, mp, point_product, point_product_order, coupon_activity
|
||||
from app.models.database import Base, engine
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from fastapi.responses import JSONResponse
|
||||
@ -47,7 +47,8 @@ app.include_router(community.router, prefix="/api/community", tags=["社区"])
|
||||
app.include_router(community_building.router, prefix="/api/community/building", tags=["社区楼栋"])
|
||||
app.include_router(station.router, prefix="/api/station", tags=["驿站"])
|
||||
app.include_router(order.router, prefix="/api/order", tags=["订单"])
|
||||
app.include_router(coupon.router, prefix="/api/coupon", tags=["服务券"])
|
||||
app.include_router(coupon.router, prefix="/api/coupon", tags=["抵扣券"])
|
||||
app.include_router(coupon_activity.router, prefix="/api/coupon-activities", tags=["抵扣券活动"])
|
||||
app.include_router(merchant.router, prefix="/api/merchant", tags=["商家"])
|
||||
app.include_router(merchant_category.router, prefix="/api/merchant-categories", tags=["商家分类"])
|
||||
app.include_router(merchant_product.router, prefix="/api/merchant/product", tags=["商家产品"])
|
||||
@ -78,6 +79,7 @@ async def validation_exception_handler(request, exc):
|
||||
@app.exception_handler(HTTPException)
|
||||
async def http_exception_handler(request, exc):
|
||||
logging.exception(f"HTTP异常: {str(exc)}")
|
||||
|
||||
return CustomJSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content=str(exc)
|
||||
|
||||
62
app/models/coupon_activity.py
Normal file
62
app/models/coupon_activity.py
Normal file
@ -0,0 +1,62 @@
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Boolean, JSON, Time
|
||||
from sqlalchemy.sql import func
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional, List, Dict
|
||||
from datetime import datetime, time
|
||||
from .database import Base
|
||||
|
||||
class CouponActivityDB(Base):
|
||||
__tablename__ = "coupon_activities"
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
name = Column(String(100), nullable=False) # 活动名称
|
||||
description = Column(String(500), nullable=True) # 活动描述
|
||||
start_time = Column(DateTime(timezone=True), nullable=False) # 活动开始时间
|
||||
end_time = Column(DateTime(timezone=True), nullable=False) # 活动结束时间
|
||||
daily_start_time = Column(Time, nullable=False) # 每日开始时间
|
||||
daily_end_time = Column(Time, nullable=False) # 每日结束时间
|
||||
daily_limit = Column(Integer, nullable=False, default=1) # 每日可领取次数
|
||||
is_active = Column(Boolean, nullable=False, default=True) # 是否激活
|
||||
coupon_config = Column(JSON, nullable=False) # 可领取的优惠券配置 {coupon_id: count}
|
||||
create_time = Column(DateTime(timezone=True), server_default=func.now())
|
||||
update_time = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
# Pydantic 模型
|
||||
class CouponActivityCreate(BaseModel):
|
||||
name: str = Field(..., max_length=100)
|
||||
description: Optional[str] = Field(None, max_length=500)
|
||||
start_time: datetime
|
||||
end_time: datetime
|
||||
daily_start_time: time # 每日开始时间 "HH:MM:SS"
|
||||
daily_end_time: time # 每日结束时间 "HH:MM:SS"
|
||||
daily_limit: int = Field(..., gt=0)
|
||||
coupon_config: Dict[int, int] # {coupon_id: count}
|
||||
is_active: bool = Field(default=True)
|
||||
|
||||
class CouponActivityUpdate(BaseModel):
|
||||
name: Optional[str] = Field(None, max_length=100)
|
||||
description: Optional[str] = Field(None, max_length=500)
|
||||
start_time: Optional[datetime] = None
|
||||
end_time: Optional[datetime] = None
|
||||
daily_start_time: Optional[time] = None
|
||||
daily_end_time: Optional[time] = None
|
||||
daily_limit: Optional[int] = Field(None, gt=0)
|
||||
coupon_config: Optional[Dict[int, int]] = None
|
||||
is_active: Optional[bool] = None
|
||||
|
||||
class CouponActivityInfo(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
description: Optional[str]
|
||||
start_time: datetime
|
||||
end_time: datetime
|
||||
daily_start_time: time
|
||||
daily_end_time: time
|
||||
daily_limit: int
|
||||
coupon_config: Dict[int, int]
|
||||
is_active: bool
|
||||
create_time: datetime
|
||||
update_time: Optional[datetime]
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
13
app/models/coupon_receive_record.py
Normal file
13
app/models/coupon_receive_record.py
Normal file
@ -0,0 +1,13 @@
|
||||
from sqlalchemy import Column, Integer, DateTime, ForeignKey, Date
|
||||
from sqlalchemy.sql import func
|
||||
from .database import Base
|
||||
|
||||
class CouponReceiveRecordDB(Base):
|
||||
__tablename__ = "coupon_receive_records"
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
user_id = Column(Integer, ForeignKey("users.userid"), index=True)
|
||||
activity_id = Column(Integer, ForeignKey("coupon_activities.id"))
|
||||
receive_date = Column(Date, nullable=False)
|
||||
receive_count = Column(Integer, nullable=False, default=1) # 领取次数
|
||||
create_time = Column(DateTime(timezone=True), server_default=func.now())
|
||||
Loading…
Reference in New Issue
Block a user