完成提现功能。

This commit is contained in:
aaron 2025-01-24 23:01:31 +08:00
parent 25c2936039
commit 6e37ea3bfe
4 changed files with 260 additions and 1 deletions

View File

@ -0,0 +1,214 @@
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from app.models.withdraw import WithdrawDB, WithdrawCreate, WithdrawInfo, WithdrawStatus
from app.models.user_bank_card import UserBankCardDB
from app.models.user_account import UserAccountDB, AccountDetailDB, AccountDetailType
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 decimal import Decimal
from typing import List, Optional, Dict
from datetime import datetime
router = APIRouter()
@router.post("", response_model=ResponseModel)
async def create_withdraw(
withdraw: WithdrawCreate,
db: Session = Depends(get_db),
current_user: UserDB = Depends(get_current_user)
):
"""申请提现"""
# 验证银行卡
bank_card = db.query(UserBankCardDB).filter(
UserBankCardDB.id == withdraw.bank_card_id,
UserBankCardDB.user_id == current_user.userid
).first()
if not bank_card:
return error_response(code=404, message="银行卡不存在")
# 验证余额
account = db.query(UserAccountDB).filter(
UserAccountDB.user_id == current_user.userid
).first()
if not account or account.balance < withdraw.amount:
return error_response(code=400, message="余额不足")
try:
# 创建提现记录
withdraw_record = WithdrawDB(
user_id=current_user.userid,
bank_card_id=withdraw.bank_card_id,
amount=withdraw.amount,
status=WithdrawStatus.PENDING
)
db.add(withdraw_record)
# 更新账户余额
account.balance -= Decimal(str(withdraw.amount))
account.lock_balance += Decimal(str(withdraw.amount))
db.commit()
db.refresh(withdraw_record)
return success_response(data=WithdrawInfo.model_validate(withdraw_record))
except Exception as e:
db.rollback()
return error_response(code=500, message=f"申请提现失败: {str(e)}")
@router.post("/{withdraw_id}/approve", response_model=ResponseModel)
async def approve_withdraw(
withdraw_id: int,
db: Session = Depends(get_db),
admin: UserDB = Depends(get_admin_user)
):
"""通过提现申请"""
withdraw = db.query(WithdrawDB).filter(
WithdrawDB.id == withdraw_id,
WithdrawDB.status == WithdrawStatus.PENDING
).first()
if not withdraw:
return error_response(code=404, message="提现申请不存在或已处理")
try:
# 更新提现状态
withdraw.status = WithdrawStatus.APPROVED
# 更新账户余额
account = db.query(UserAccountDB).filter(
UserAccountDB.user_id == withdraw.user_id
).first()
account.lock_balance -= withdraw.amount
# 添加账户明细记录
detail = AccountDetailDB(
user_id=withdraw.user_id,
type=AccountDetailType.WITHDRAW,
amount=withdraw.amount,
balance=account.balance,
description=f"提现到银行卡(尾号{withdraw.bank_card.card_number[-4:]}"
)
db.add(detail)
db.commit()
return success_response(message="提现申请已通过")
except Exception as e:
db.rollback()
return error_response(code=500, message=f"处理失败: {str(e)}")
@router.post("/{withdraw_id}/reject", response_model=ResponseModel)
async def reject_withdraw(
withdraw_id: int,
remark: str,
db: Session = Depends(get_db),
admin: UserDB = Depends(get_admin_user)
):
"""驳回提现申请"""
withdraw = db.query(WithdrawDB).filter(
WithdrawDB.id == withdraw_id,
WithdrawDB.status == WithdrawStatus.PENDING
).first()
if not withdraw:
return error_response(code=404, message="提现申请不存在或已处理")
try:
# 更新提现状态
withdraw.status = WithdrawStatus.REJECTED
withdraw.remark = remark
# 返还锁定余额
account = db.query(UserAccountDB).filter(
UserAccountDB.user_id == withdraw.user_id
).first()
account.balance += withdraw.amount
account.lock_balance -= withdraw.amount
db.commit()
return success_response(message="提现申请已驳回")
except Exception as e:
db.rollback()
return error_response(code=500, message=f"处理失败: {str(e)}")
@router.get("/user", response_model=ResponseModel)
async def get_user_withdraws(
status: Optional[WithdrawStatus] = None,
db: Session = Depends(get_db),
current_user: UserDB = Depends(get_current_user)
):
"""获取提现记录列表"""
query = db.query(WithdrawDB).filter(WithdrawDB.user_id == current_user.userid)
if status:
query = query.filter(WithdrawDB.status == status)
withdraws = query.order_by(WithdrawDB.create_time.desc()).all()
return success_response(data=[WithdrawInfo.model_validate(w) for w in withdraws])
@router.get("", response_model=ResponseModel)
async def get_all_withdraws(
status: Optional[WithdrawStatus] = None,
start_time: Optional[datetime] = None,
end_time: Optional[datetime] = None,
skip: int = 0,
limit: int = 20,
db: Session = Depends(get_db),
admin: UserDB = Depends(get_admin_user)
):
"""
管理员获取所有提现记录
Args:
status: 提现状态筛选
start_time: 开始时间
end_time: 结束时间
skip: 跳过记录数
limit: 返回记录数
"""
# 构建查询
query = db.query(WithdrawDB).join(
UserBankCardDB,
WithdrawDB.bank_card_id == UserBankCardDB.id
)
# 应用筛选条件
if status:
query = query.filter(WithdrawDB.status == status)
if start_time:
query = query.filter(WithdrawDB.create_time >= start_time)
if end_time:
query = query.filter(WithdrawDB.create_time <= end_time)
# 计算总数
total = query.count()
# 分页
withdraws = query.order_by(WithdrawDB.create_time.desc())\
.offset(skip)\
.limit(limit)\
.all()
# 构建返回数据
withdraw_list = []
for w in withdraws:
withdraw_info = WithdrawInfo.model_validate(w)
# 添加额外信息
withdraw_info_dict = withdraw_info.model_dump()
withdraw_info_dict.update({
"bank_card_number": w.bank_card.card_number, # 银行卡号
"bank_name": w.bank_card.bank_name, # 银行名称
"name": w.bank_card.name # 持卡人姓名
})
withdraw_list.append(withdraw_info_dict)
return success_response(data={
"items": withdraw_list,
"total": total
})

View File

@ -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
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
from app.models.database import Base, engine
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
@ -33,6 +33,7 @@ app.add_middleware(RequestLoggerMiddleware)
app.include_router(wechat.router,prefix="/api/wechat",tags=["微信"])
app.include_router(user.router, prefix="/api/user", tags=["用户"])
app.include_router(bank_card.router, prefix="/api/bank-cards", tags=["用户银行卡"])
app.include_router(withdraw.router, prefix="/api/withdraw", tags=["提现"])
app.include_router(point.router, prefix="/api/point", tags=["用户积分"])
app.include_router(account.router, prefix="/api/account", tags=["账户"])
app.include_router(address.router, prefix="/api/address", tags=["配送地址"])

View File

@ -17,6 +17,7 @@ class UserAccountDB(Base):
id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer, ForeignKey("users.userid"), unique=True, nullable=False)
balance = Column(DECIMAL(10, 2), nullable=False, default=0)
lock_balance = Column(DECIMAL(10, 2), nullable=False, default=0)
create_time = Column(DateTime(timezone=True), server_default=func.now())
update_time = Column(DateTime(timezone=True), onupdate=func.now())

43
app/models/withdraw.py Normal file
View File

@ -0,0 +1,43 @@
from sqlalchemy import Column, String, Integer, DateTime, ForeignKey, Enum, DECIMAL
from sqlalchemy.sql import func
from pydantic import BaseModel, Field
from typing import Optional
from datetime import datetime
from .database import Base
import enum
from sqlalchemy.orm import relationship
class WithdrawStatus(str, enum.Enum):
PENDING = "PENDING" # 已申请
APPROVED = "APPROVED" # 已通过
REJECTED = "REJECTED" # 已驳回
class WithdrawDB(Base):
__tablename__ = "withdraws"
id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer, ForeignKey("users.userid"), nullable=False)
bank_card_id = Column(Integer, ForeignKey("user_bank_cards.id"), nullable=False)
amount = Column(DECIMAL(10, 2), nullable=False)
status = Column(Enum(WithdrawStatus), nullable=False, default=WithdrawStatus.PENDING)
remark = Column(String(200)) # 备注(驳回原因等)
create_time = Column(DateTime(timezone=True), server_default=func.now())
update_time = Column(DateTime(timezone=True), onupdate=func.now())
# 关联关系
bank_card = relationship("UserBankCardDB", backref="withdraws")
class WithdrawCreate(BaseModel):
bank_card_id: int
amount: float = Field(..., gt=0)
class WithdrawInfo(BaseModel):
id: int
bank_card_id: int
amount: float
status: WithdrawStatus
remark: Optional[str]
create_time: datetime
class Config:
from_attributes = True