diff --git a/app/api/endpoints/coupon.py b/app/api/endpoints/coupon.py index 37996de..8f6aa13 100644 --- a/app/api/endpoints/coupon.py +++ b/app/api/endpoints/coupon.py @@ -9,7 +9,10 @@ from app.models.coupon import ( CouponInfo, UserCouponCreate, UserCouponInfo, - CouponStatus + CouponStatus, + CouponIssueRecordDB, + CouponIssueRecordCreate, + CouponIssueRecordInfo ) from app.models.database import get_db from app.api.deps import get_admin_user, get_current_user @@ -101,20 +104,55 @@ async def issue_coupon( if not coupon: 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): - manager = CouponManager(coupon) - manager.add_coupon( + user_coupon_obj = manager.add_coupon( user_id=user_coupon.user_id, coupon_id=coupon.id, - count=user_coupon.count, expire_time=user_coupon.expire_time ) + db.add(user_coupon_obj) + user_coupons.append(user_coupon_obj) try: db.commit() + db.refresh(issue_record) + 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: db.rollback() @@ -164,10 +202,71 @@ async def get_all_coupons( admin: UserDB = Depends(get_admin_user) ): """获取所有优惠券列表(管理员)""" - coupons = db.query(CouponDB).order_by( + query = db.query(CouponDB).order_by( 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() + # 构建响应数据 + 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( - data=[CouponInfo.model_validate(c) for c in coupons] + data={ + "total": total, + "items": result + } ) \ No newline at end of file diff --git a/app/core/coupon_manager.py b/app/core/coupon_manager.py index 151b2de..8645c54 100644 --- a/app/core/coupon_manager.py +++ b/app/core/coupon_manager.py @@ -1,47 +1,47 @@ 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 datetime import datetime +from typing import Optional, List class CouponManager: - def __init__(self, db: Session): - self.db = db + def __init__(self, coupon: CouponDB = None): + self.coupon = coupon # 发放优惠券 def add_coupon( self, user_id: int, coupon_id: int, - count: int, - expire_time: datetime - ): - try: - # 检查优惠券是否存在 - coupon = self.db.query(CouponDB).filter(CouponDB.id == coupon_id).first() - if not coupon: - raise ValueError("优惠券不存在") + count: int = 1, + expire_time: datetime = None + ) -> UserCouponDB: + """ + 为用户发放优惠券 + + Args: + user_id: 用户ID + coupon_id: 优惠券ID + count: 发放数量 + expire_time: 过期时间 - # 检查用户是否存在 - user = self.db.query(UserDB).filter(UserDB.userid == user_id).first() - if not user: - raise ValueError("用户不存在") - - for _ in range(count): - # 发放优惠券 - user_coupon = UserCouponDB( - user_id=user_id, - coupon_id=coupon_id, - coupon_name=coupon.name, - coupon_amount=coupon.amount, - coupon_type=coupon.coupon_type, - expire_time=expire_time, - ) - self.db.add(user_coupon) - - self.db.commit() - except Exception as e: - self.db.rollback() - raise e + Returns: + UserCouponDB: 创建的用户优惠券对象 + """ + # 使用传入的优惠券对象 + coupon = self.coupon + + # 发放优惠券 + user_coupon = UserCouponDB( + user_id=user_id, + coupon_id=coupon_id, + coupon_name=coupon.name, + coupon_amount=coupon.amount, + coupon_type=coupon.coupon_type, + expire_time=expire_time, + ) + + return user_coupon diff --git a/app/models/coupon.py b/app/models/coupon.py index 2bd7289..12d4bb5 100644 --- a/app/models/coupon.py +++ b/app/models/coupon.py @@ -5,6 +5,7 @@ from sqlalchemy.sql import func from pydantic import BaseModel, Field from .database import Base import enum +from sqlalchemy.orm import relationship class CouponStatus(str, enum.Enum): UNUSED = "UNUSED" @@ -41,6 +42,23 @@ class UserCouponDB(Base): create_time = Column(DateTime(timezone=True), server_default=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 模型 class CouponCreate(BaseModel): name: str = Field(..., max_length=100) @@ -66,6 +84,7 @@ class UserCouponCreate(BaseModel): user_id: int coupon_id: int expire_time: datetime + remark: Optional[str] = None count: int = Field(..., gt=0, description="发放数量") class UserCouponInfo(BaseModel): @@ -80,5 +99,26 @@ class UserCouponInfo(BaseModel): status: CouponStatus 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: from_attributes = True \ No newline at end of file diff --git a/jobs.sqlite b/jobs.sqlite index 39a2835..07da51e 100644 Binary files a/jobs.sqlite and b/jobs.sqlite differ