1. merchant 增加 user 归属
2. 验证订单后,给对应的 user 增加结算 balance 和 details 3. 配送订单完成后,根据是否需要支付,切换到不同的状态。
This commit is contained in:
parent
d8e0666d8a
commit
91e56c9eb3
@ -17,6 +17,7 @@ from app.core.response import success_response, error_response, ResponseModel
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from app.models.merchant import MerchantDB
|
from app.models.merchant import MerchantDB
|
||||||
from app.models.point import PointRecordDB
|
from app.models.point import PointRecordDB
|
||||||
|
from app.core.account import AccountManager
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@ -151,7 +152,18 @@ async def verify_order(
|
|||||||
current_user: UserDB = Depends(get_current_user)
|
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_id == order_id,
|
||||||
MerchantOrderDB.order_verify_code == verify_code,
|
MerchantOrderDB.order_verify_code == verify_code,
|
||||||
MerchantOrderDB.verify_time.is_(None) # 未核销
|
MerchantOrderDB.verify_time.is_(None) # 未核销
|
||||||
@ -160,16 +172,25 @@ async def verify_order(
|
|||||||
if not order:
|
if not order:
|
||||||
return error_response(code=404, message="订单不存在或已核销")
|
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:
|
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()
|
db.commit()
|
||||||
return success_response(
|
return success_response(
|
||||||
message="核销成功",
|
message="核销成功",
|
||||||
data=MerchantOrderInfo.model_validate(order)
|
data=MerchantOrderInfo.model_validate(order.MerchantOrderDB)
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
db.rollback()
|
db.rollback()
|
||||||
|
|||||||
@ -426,20 +426,24 @@ async def complete_order(
|
|||||||
return error_response(code=400, message="只有已接单的订单才能标记为完成")
|
return error_response(code=400, message="只有已接单的订单才能标记为完成")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 更新订单状态和完成图片
|
# 根据订单金额决定状态
|
||||||
order.status = OrderStatus.COMPLETED
|
if order.final_amount > 0:
|
||||||
|
order.status = OrderStatus.UNPAID # 需要支付
|
||||||
|
else:
|
||||||
|
order.status = OrderStatus.COMPLETED # 无需支付,直接完成
|
||||||
|
|
||||||
|
# 保存完成图片
|
||||||
if complete_data.images:
|
if complete_data.images:
|
||||||
order.complete_images = ",".join(complete_data.images)
|
order.complete_images = ",".join(complete_data.images)
|
||||||
|
|
||||||
db.commit()
|
db.commit()
|
||||||
return success_response(
|
return success_response(
|
||||||
message="订单已完成",
|
message="订单已完成" if order.final_amount == 0 else "请继续支付",
|
||||||
data=OrderInfo.model_validate(order)
|
data=OrderInfo.model_validate(order)
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
db.rollback()
|
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)
|
@router.post("/{orderid}/receive", response_model=ResponseModel)
|
||||||
async def receive_order(
|
async def receive_order(
|
||||||
|
|||||||
64
app/core/account.py
Normal file
64
app/core/account.py
Normal 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
|
||||||
@ -25,6 +25,7 @@ class MerchantDB(Base):
|
|||||||
__tablename__ = "merchants"
|
__tablename__ = "merchants"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
user_id = Column(Integer, ForeignKey("users.userid"), nullable=False) # 归属用户
|
||||||
name = Column(String(100), nullable=False)
|
name = Column(String(100), nullable=False)
|
||||||
business_hours = Column(String(100), nullable=False) # 营业时间,如 "09:00-22:00"
|
business_hours = Column(String(100), nullable=False) # 营业时间,如 "09:00-22:00"
|
||||||
address = Column(String(200), nullable=False)
|
address = Column(String(200), nullable=False)
|
||||||
@ -49,6 +50,7 @@ class MerchantImage(BaseModel):
|
|||||||
from_attributes = True
|
from_attributes = True
|
||||||
|
|
||||||
class MerchantCreate(BaseModel):
|
class MerchantCreate(BaseModel):
|
||||||
|
user_id: int
|
||||||
name: str = Field(..., max_length=100)
|
name: str = Field(..., max_length=100)
|
||||||
business_hours: str = Field(..., max_length=100)
|
business_hours: str = Field(..., max_length=100)
|
||||||
address: str = Field(..., max_length=200)
|
address: str = Field(..., max_length=200)
|
||||||
@ -70,6 +72,7 @@ class MerchantUpdate(BaseModel):
|
|||||||
|
|
||||||
class MerchantInfo(BaseModel):
|
class MerchantInfo(BaseModel):
|
||||||
id: int
|
id: int
|
||||||
|
user_id: int
|
||||||
name: str
|
name: str
|
||||||
business_hours: str
|
business_hours: str
|
||||||
address: str
|
address: str
|
||||||
|
|||||||
53
app/models/user_account.py
Normal file
53
app/models/user_account.py
Normal 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
|
||||||
Loading…
Reference in New Issue
Block a user