diff --git a/app/api/endpoints/merchant_order.py b/app/api/endpoints/merchant_order.py index d493e07..527225d 100644 --- a/app/api/endpoints/merchant_order.py +++ b/app/api/endpoints/merchant_order.py @@ -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() diff --git a/app/api/endpoints/order.py b/app/api/endpoints/order.py index 4ad2263..5767486 100644 --- a/app/api/endpoints/order.py +++ b/app/api/endpoints/order.py @@ -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( diff --git a/app/core/account.py b/app/core/account.py new file mode 100644 index 0000000..a1a9313 --- /dev/null +++ b/app/core/account.py @@ -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 \ No newline at end of file diff --git a/app/models/merchant.py b/app/models/merchant.py index 4a32977..85fb10d 100644 --- a/app/models/merchant.py +++ b/app/models/merchant.py @@ -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 diff --git a/app/models/user_account.py b/app/models/user_account.py new file mode 100644 index 0000000..a8a6c3a --- /dev/null +++ b/app/models/user_account.py @@ -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 \ No newline at end of file