1. merchant 增加 user 归属

2. 验证订单后,给对应的 user 增加结算 balance 和 details
3. 配送订单完成后,根据是否需要支付,切换到不同的状态。
This commit is contained in:
aaron 2025-01-19 15:03:29 +08:00
parent d8e0666d8a
commit 91e56c9eb3
5 changed files with 157 additions and 12 deletions

View File

@ -17,6 +17,7 @@ from app.core.response import success_response, error_response, ResponseModel
from datetime import datetime, timezone
from app.models.merchant import MerchantDB
from app.models.point import PointRecordDB
from app.core.account import AccountManager
router = APIRouter()
@ -151,7 +152,18 @@ async def verify_order(
current_user: UserDB = Depends(get_current_user)
):
"""核销订单"""
order = db.query(MerchantOrderDB).filter(
# 查询订单及相关信息
order = db.query(
MerchantOrderDB,
MerchantProductDB,
MerchantDB
).join(
MerchantProductDB,
MerchantOrderDB.merchant_product_id == MerchantProductDB.id
).join(
MerchantDB,
MerchantProductDB.merchant_id == MerchantDB.id
).filter(
MerchantOrderDB.order_id == order_id,
MerchantOrderDB.order_verify_code == verify_code,
MerchantOrderDB.verify_time.is_(None) # 未核销
@ -160,16 +172,25 @@ async def verify_order(
if not order:
return error_response(code=404, message="订单不存在或已核销")
# 更新核销时间和核销用户
order.verify_time = datetime.now(timezone.utc)
order.verify_user_id = current_user.userid
order.status = MerchantOrderStatus.VERIFIED # 更新为已核销状态
try:
# 更新核销时间和核销用户
order.MerchantOrderDB.verify_time = datetime.now(timezone.utc)
order.MerchantOrderDB.verify_user_id = current_user.userid
order.MerchantOrderDB.status = MerchantOrderStatus.VERIFIED
# 使用账户管理器处理余额变更
account_manager = AccountManager(db)
settlement_amount = float(order.MerchantProductDB.settlement_amount)
account_manager.change_balance(
user_id=order.MerchantDB.user_id,
amount=settlement_amount,
description=f"{order.MerchantProductDB.name} 订单核销"
)
db.commit()
return success_response(
message="核销成功",
data=MerchantOrderInfo.model_validate(order)
data=MerchantOrderInfo.model_validate(order.MerchantOrderDB)
)
except Exception as e:
db.rollback()

View File

@ -426,20 +426,24 @@ async def complete_order(
return error_response(code=400, message="只有已接单的订单才能标记为完成")
try:
# 更新订单状态和完成图片
order.status = OrderStatus.COMPLETED
# 根据订单金额决定状态
if order.final_amount > 0:
order.status = OrderStatus.UNPAID # 需要支付
else:
order.status = OrderStatus.COMPLETED # 无需支付,直接完成
# 保存完成图片
if complete_data.images:
order.complete_images = ",".join(complete_data.images)
db.commit()
return success_response(
message="订单已完成",
message="订单已完成" if order.final_amount == 0 else "请继续支付",
data=OrderInfo.model_validate(order)
)
except Exception as e:
db.rollback()
return error_response(code=500, message=f"完成订单失败: {str(e)}")
return error_response(code=500, message=f"操作失败: {str(e)}")
@router.post("/{orderid}/receive", response_model=ResponseModel)
async def receive_order(

64
app/core/account.py Normal file
View File

@ -0,0 +1,64 @@
from sqlalchemy.orm import Session
from app.models.user_account import UserAccountDB, AccountDetailDB, AccountDetailType
from decimal import Decimal
class AccountManager:
"""用户账户管理类"""
def __init__(self, db: Session):
self.db = db
def get_or_create_account(self, user_id: int) -> UserAccountDB:
"""获取或创建用户账户"""
account = self.db.query(UserAccountDB).filter(
UserAccountDB.user_id == user_id
).first()
if not account:
account = UserAccountDB(
user_id=user_id,
balance=0
)
self.db.add(account)
self.db.flush()
return account
def change_balance(self, user_id: int, amount: float, description: str) -> UserAccountDB:
"""
变更用户余额
Args:
user_id: 用户ID
amount: 变更金额正数为收入负数为支出
description: 变更说明
Returns:
更新后的账户信息
Raises:
Exception: 余额不足等异常
"""
# 转换为 Decimal 以确保精确计算
decimal_amount = Decimal(str(amount))
# 获取或创建账户
account = self.get_or_create_account(user_id)
# 检查余额是否充足(支出时)
if decimal_amount < 0 and account.balance + decimal_amount < 0:
raise Exception("账户余额不足")
# 更新余额
account.balance += decimal_amount
# 记录账户明细
detail = AccountDetailDB(
user_id=user_id,
amount=decimal_amount,
type=AccountDetailType.INCOME if decimal_amount > 0 else AccountDetailType.EXPENSE,
description=description
)
self.db.add(detail)
return account

View File

@ -25,6 +25,7 @@ class MerchantDB(Base):
__tablename__ = "merchants"
id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer, ForeignKey("users.userid"), nullable=False) # 归属用户
name = Column(String(100), nullable=False)
business_hours = Column(String(100), nullable=False) # 营业时间,如 "09:00-22:00"
address = Column(String(200), nullable=False)
@ -49,6 +50,7 @@ class MerchantImage(BaseModel):
from_attributes = True
class MerchantCreate(BaseModel):
user_id: int
name: str = Field(..., max_length=100)
business_hours: str = Field(..., max_length=100)
address: str = Field(..., max_length=200)
@ -70,6 +72,7 @@ class MerchantUpdate(BaseModel):
class MerchantInfo(BaseModel):
id: int
user_id: int
name: str
business_hours: str
address: str

View File

@ -0,0 +1,53 @@
from sqlalchemy import Column, Integer, DECIMAL, DateTime, ForeignKey, Enum, String
from sqlalchemy.sql import func
from .database import Base
from pydantic import BaseModel, Field
import enum
from typing import Optional
from datetime import datetime
class AccountDetailType(str, enum.Enum):
INCOME = "INCOME" # 收入
EXPENSE = "EXPENSE" # 支出
# 用户账户表
class UserAccountDB(Base):
__tablename__ = "user_accounts"
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)
create_time = Column(DateTime(timezone=True), server_default=func.now())
update_time = Column(DateTime(timezone=True), onupdate=func.now())
# 账户明细表
class AccountDetailDB(Base):
__tablename__ = "account_details"
id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer, ForeignKey("users.userid"), nullable=False)
amount = Column(DECIMAL(10, 2), nullable=False)
type = Column(Enum(AccountDetailType), nullable=False)
description = Column(String(200), nullable=False)
create_time = Column(DateTime(timezone=True), server_default=func.now())
# Pydantic 模型
class AccountInfo(BaseModel):
user_id: int
balance: float
create_time: datetime
update_time: Optional[datetime]
class Config:
from_attributes = True
class AccountDetailInfo(BaseModel):
id: int
user_id: int
amount: float
type: AccountDetailType
description: str
create_time: datetime
class Config:
from_attributes = True