新增优惠券发放功能

This commit is contained in:
aaron 2025-04-06 20:20:43 +08:00
parent 02dcebd28e
commit 623a1f97c4
4 changed files with 178 additions and 39 deletions

View File

@ -9,7 +9,10 @@ from app.models.coupon import (
CouponInfo, CouponInfo,
UserCouponCreate, UserCouponCreate,
UserCouponInfo, UserCouponInfo,
CouponStatus CouponStatus,
CouponIssueRecordDB,
CouponIssueRecordCreate,
CouponIssueRecordInfo
) )
from app.models.database import get_db from app.models.database import get_db
from app.api.deps import get_admin_user, get_current_user from app.api.deps import get_admin_user, get_current_user
@ -101,20 +104,55 @@ async def issue_coupon(
if not coupon: if not coupon:
return error_response(code=404, message="优惠券不存在") return error_response(code=404, message="优惠券不存在")
# 查询用户信息
user = db.query(UserDB).filter(UserDB.userid == user_coupon.user_id).first()
if not user:
return error_response(code=404, message="用户不存在")
# 创建发放记录
issue_record = CouponIssueRecordDB(
coupon_id=coupon.id,
user_id=user_coupon.user_id,
operator_id=admin.userid,
count=user_coupon.count,
remark=f"{admin.nickname}: {user_coupon.remark}" if user_coupon.remark else f"{admin.nickname} 手动发放"
)
db.add(issue_record)
# 批量创建用户优惠券 # 批量创建用户优惠券
manager = CouponManager(coupon)
user_coupons = []
for _ in range(user_coupon.count): for _ in range(user_coupon.count):
manager = CouponManager(coupon) user_coupon_obj = manager.add_coupon(
manager.add_coupon(
user_id=user_coupon.user_id, user_id=user_coupon.user_id,
coupon_id=coupon.id, coupon_id=coupon.id,
count=user_coupon.count,
expire_time=user_coupon.expire_time expire_time=user_coupon.expire_time
) )
db.add(user_coupon_obj)
user_coupons.append(user_coupon_obj)
try: try:
db.commit() db.commit()
db.refresh(issue_record)
return success_response( return success_response(
message=f"成功发放 {user_coupon.count} 张优惠券" message=f"成功发放 {user_coupon.count} 张优惠券",
data={
"issue_record": CouponIssueRecordInfo(
id=issue_record.id,
coupon_id=issue_record.coupon_id,
user_id=issue_record.user_id,
operator_id=issue_record.operator_id,
count=issue_record.count,
issue_time=issue_record.issue_time,
remark=issue_record.remark,
coupon_name=coupon.name,
user_nickname=user.nickname,
operator_nickname=admin.nickname
),
"coupon_count": len(user_coupons)
}
) )
except Exception as e: except Exception as e:
db.rollback() db.rollback()
@ -164,10 +202,71 @@ async def get_all_coupons(
admin: UserDB = Depends(get_admin_user) admin: UserDB = Depends(get_admin_user)
): ):
"""获取所有优惠券列表(管理员)""" """获取所有优惠券列表(管理员)"""
coupons = db.query(CouponDB).order_by( query = db.query(CouponDB).order_by(
CouponDB.create_time.desc() CouponDB.create_time.desc()
)
total = query.count()
items = query.offset(skip).limit(limit).all()
return success_response(
data={
"total": total,
"items": [CouponInfo.model_validate(c) for c in items]
}
)
@router.get("/issue/records", response_model=ResponseModel)
async def get_issue_records(
skip: int = 0,
limit: int = 10,
coupon_id: Optional[int] = None,
user_id: Optional[int] = None,
db: Session = Depends(get_db),
admin: UserDB = Depends(get_admin_user)
):
"""获取优惠券发放记录(管理员)"""
query = db.query(CouponIssueRecordDB)
# 添加过滤条件
if coupon_id:
query = query.filter(CouponIssueRecordDB.coupon_id == coupon_id)
if user_id:
query = query.filter(CouponIssueRecordDB.user_id == user_id)
# 计算总数
total = query.count()
# 获取数据
records = query.order_by(
CouponIssueRecordDB.issue_time.desc()
).offset(skip).limit(limit).all() ).offset(skip).limit(limit).all()
# 构建响应数据
result = []
for record in records:
# 获取关联信息
coupon = db.query(CouponDB).filter(CouponDB.id == record.coupon_id).first()
user = db.query(UserDB).filter(UserDB.userid == record.user_id).first()
operator = db.query(UserDB).filter(UserDB.userid == record.operator_id).first()
record_info = CouponIssueRecordInfo(
id=record.id,
coupon_id=record.coupon_id,
user_id=record.user_id,
operator_id=record.operator_id,
count=record.count,
issue_time=record.issue_time,
remark=record.remark,
coupon_name=coupon.name if coupon else None,
user_nickname=user.nickname if user else None,
operator_nickname=operator.nickname if operator else None
)
result.append(record_info)
return success_response( return success_response(
data=[CouponInfo.model_validate(c) for c in coupons] data={
"total": total,
"items": result
}
) )

View File

@ -1,47 +1,47 @@
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from app.models.coupon import CouponDB,UserCouponDB from app.models.coupon import CouponDB, UserCouponDB
from app.models.user import UserDB from app.models.user import UserDB
from datetime import datetime from datetime import datetime
from typing import Optional, List
class CouponManager: class CouponManager:
def __init__(self, db: Session): def __init__(self, coupon: CouponDB = None):
self.db = db self.coupon = coupon
# 发放优惠券 # 发放优惠券
def add_coupon( def add_coupon(
self, self,
user_id: int, user_id: int,
coupon_id: int, coupon_id: int,
count: int, count: int = 1,
expire_time: datetime expire_time: datetime = None
): ) -> UserCouponDB:
try: """
# 检查优惠券是否存在 为用户发放优惠券
coupon = self.db.query(CouponDB).filter(CouponDB.id == coupon_id).first()
if not coupon: Args:
raise ValueError("优惠券不存在") user_id: 用户ID
coupon_id: 优惠券ID
count: 发放数量
expire_time: 过期时间
# 检查用户是否存在 Returns:
user = self.db.query(UserDB).filter(UserDB.userid == user_id).first() UserCouponDB: 创建的用户优惠券对象
if not user: """
raise ValueError("用户不存在") # 使用传入的优惠券对象
coupon = self.coupon
for _ in range(count):
# 发放优惠券 # 发放优惠券
user_coupon = UserCouponDB( user_coupon = UserCouponDB(
user_id=user_id, user_id=user_id,
coupon_id=coupon_id, coupon_id=coupon_id,
coupon_name=coupon.name, coupon_name=coupon.name,
coupon_amount=coupon.amount, coupon_amount=coupon.amount,
coupon_type=coupon.coupon_type, coupon_type=coupon.coupon_type,
expire_time=expire_time, expire_time=expire_time,
) )
self.db.add(user_coupon)
return user_coupon
self.db.commit()
except Exception as e:
self.db.rollback()
raise e

View File

@ -5,6 +5,7 @@ from sqlalchemy.sql import func
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from .database import Base from .database import Base
import enum import enum
from sqlalchemy.orm import relationship
class CouponStatus(str, enum.Enum): class CouponStatus(str, enum.Enum):
UNUSED = "UNUSED" UNUSED = "UNUSED"
@ -41,6 +42,23 @@ class UserCouponDB(Base):
create_time = Column(DateTime(timezone=True), server_default=func.now()) create_time = Column(DateTime(timezone=True), server_default=func.now())
update_time = Column(DateTime(timezone=True), onupdate=func.now()) update_time = Column(DateTime(timezone=True), onupdate=func.now())
class CouponIssueRecordDB(Base):
"""优惠券发放记录表"""
__tablename__ = "coupon_issue_records"
id = Column(Integer, primary_key=True, autoincrement=True)
coupon_id = Column(Integer, ForeignKey("coupons.id"), nullable=False)
user_id = Column(Integer, ForeignKey("users.userid"), nullable=False)
operator_id = Column(Integer, ForeignKey("users.userid"), nullable=False) # 操作人ID
count = Column(Integer, nullable=False, default=1) # 发放数量
issue_time = Column(DateTime(timezone=True), nullable=False, server_default=func.now())
remark = Column(String(200), nullable=True) # 备注信息
# 关联关系
coupon = relationship("CouponDB", backref="issue_records")
user = relationship("UserDB", foreign_keys=[user_id], backref="received_coupon_records")
operator = relationship("UserDB", foreign_keys=[operator_id], backref="operated_coupon_records")
# Pydantic 模型 # Pydantic 模型
class CouponCreate(BaseModel): class CouponCreate(BaseModel):
name: str = Field(..., max_length=100) name: str = Field(..., max_length=100)
@ -66,6 +84,7 @@ class UserCouponCreate(BaseModel):
user_id: int user_id: int
coupon_id: int coupon_id: int
expire_time: datetime expire_time: datetime
remark: Optional[str] = None
count: int = Field(..., gt=0, description="发放数量") count: int = Field(..., gt=0, description="发放数量")
class UserCouponInfo(BaseModel): class UserCouponInfo(BaseModel):
@ -80,5 +99,26 @@ class UserCouponInfo(BaseModel):
status: CouponStatus status: CouponStatus
create_time: datetime create_time: datetime
class Config:
from_attributes = True
class CouponIssueRecordCreate(BaseModel):
coupon_id: int
user_id: int
count: int = 1
remark: Optional[str] = None
class CouponIssueRecordInfo(BaseModel):
id: int
coupon_id: int
user_id: int
operator_id: int
count: int
issue_time: datetime
remark: Optional[str] = None
coupon_name: Optional[str] = None
user_nickname: Optional[str] = None
operator_nickname: Optional[str] = None
class Config: class Config:
from_attributes = True from_attributes = True

Binary file not shown.