This commit is contained in:
aaron 2025-04-05 21:03:08 +08:00
parent 28922e4cbf
commit 466660d772
6 changed files with 169 additions and 12 deletions

View File

@ -13,7 +13,8 @@ from app.models.order import (
OrderStatus,
OrderCancel,
OrderComplete,
OrderPriceResult
OrderPriceResult,
RefundOrderAmountRequest
)
from app.models.order_additional_fee import OrderAdditionalFeeDB, OrderAdditionalFeeInfo, AdditionalFeeResult
from app.models.database import get_db
@ -1571,7 +1572,61 @@ async def get_orders(
except Exception as e:
logging.exception(f"获取订单列表失败: {str(e)}")
return error_response(code=500, message=f"获取订单列表失败: {str(e)}")
@router.put("/admin/refund_order_amount", response_model=ResponseModel)
async def refund_order_amount(
request: RefundOrderAmountRequest,
db: Session = Depends(get_db),
admin_user: UserDB = Depends(get_admin_user)
):
"""减少订单金额"""
try:
order = db.query(ShippingOrderDB).filter(ShippingOrderDB.orderid == request.order_id).first()
if not order:
return error_response(code=404, message="订单不存在")
# 检查退款金额是否大于订单支付金额
if request.amount > order.final_amount:
return error_response(code=400, message="退款金额不能大于订单金额")
order.refund_amount = request.amount
order.refund_reason = request.reason
order.refund_user_id = admin_user.userid
order.final_amount -= request.amount
# 如果订单已经完成,则扣减配送员收益
order.delivery_share = calculate_delivery_share(order, db)
if order.status == OrderStatus.COMPLETED:
# 扣减配送员收益
sharing = db.query(CommunityProfitSharing).filter(
CommunityProfitSharing.community_id == order.address_community_id
).first()
if sharing:
reduct_share = -round(request.amount * (float(sharing.delivery_rate) / 100.0), 2)
manager = AccountManager(db)
manager.change_balance(
order.deliveryman_user_id,
reduct_share,
f"配送订单退款",
order.orderid
)
wechat = WeChatClient()
await wechat.apply_refund(
order_id=order.orderid,
total_amount=int(float(request.amount) * 100) if not settings.DEBUG else 1, # 转换为分
reason="后台退款"
)
db.commit()
return success_response(message="减少订单金额成功")
except Exception as e:
db.rollback()
logging.exception(f"减少订单金额失败: {str(e)}")
return error_response(code=500, message=f"减少订单金额失败: {str(e)}")
@router.get("/admin/list", response_model=ResponseModel)
async def get_admin_orders(
@ -1654,6 +1709,9 @@ async def get_admin_orders(
"complete_images": order.optimized_complete_images,
"completed_time": order.completed_time,
"final_amount": order.final_amount,
"refund_amount": order.refund_amount,
"refund_reason": order.refund_reason,
"refund_user_id": order.refund_user_id,
"packages": package_list,
"sub_orders": [PointProductOrderInfo.model_validate(sub_order) for sub_order in sub_orders],
"address": {

View File

@ -26,7 +26,7 @@ import logging
from app.core.security import get_password_hash
from app.models.order_additional_fee import OrderAdditionalFeeDB, AdditionalFeeResult
from app.api.endpoints.order import calculate_delivery_share
from app.models.wechat_payment import WechatPaymentRecordDB
from app.models.wechat_payment import WechatPaymentRecordDB, WechatRefundRecordDB
from app.core.wecombot import WecomBot
from app.models.order import OrderInfo
@ -400,14 +400,52 @@ async def refund_notify(
# 获取退款信息
out_trade_no = data.get("out_trade_no") # 订单号
out_refund_no = data.get("out_refund_no") # 退款单号
refund_id = data.get("refund_id") # 微信退款单号
transaction_id = data.get("transaction_id") # 微信支付交易号
status = data.get("refund_status")
success_time = data.get("success_time")
refund_amount = data.get("amount", {}).get("refund")
total_amount = data.get("amount", {}).get("total")
user_received_account = data.get("user_received_account")
refund_reason = "退款" # 默认退款原因
# 保存退款记录数据
refund_record = WechatRefundRecordDB(
out_trade_no=out_trade_no,
out_refund_no=out_refund_no,
refund_id=refund_id,
transaction_id=transaction_id,
refund_status=status,
success_time=datetime.fromisoformat(success_time.replace('Z', '+00:00')) if success_time else None,
refund_amount=float(refund_amount) / 100 if refund_amount else 0,
total_amount=float(total_amount) / 100 if total_amount else 0,
user_received_account=user_received_account,
refund_reason=refund_reason,
raw_data=data
)
db.add(refund_record)
if not all([out_trade_no, status]) or status != "SUCCESS":
return error_response(code=400, message="缺少必要的退款信息或退款未成功")
# 根据订单类型处理退款
if out_trade_no.startswith('M'): # 商家商品订单
if out_trade_no.startswith('D'): # 配送订单
order = db.query(ShippingOrderDB).filter(
ShippingOrderDB.orderid == out_trade_no
).first()
if not order:
return error_response(code=404, message="订单不存在或状态不正确")
# 更新退款相关字段
if refund_amount:
order.refund_amount = refund_amount
# 记录退款时间
order.refund_time = datetime.fromisoformat(success_time.replace('Z', '+00:00'))
elif out_trade_no.startswith('M'): # 商家商品订单
order = db.query(MerchantOrderDB).filter(
MerchantOrderDB.order_id == out_trade_no,
MerchantOrderDB.status == MerchantOrderStatus.REFUNDING
@ -418,7 +456,7 @@ async def refund_notify(
# 更新订单状态
order.status = MerchantOrderStatus.CANCELLED
order.refund_transaction_id = data.get("transaction_id")
order.refund_transaction_id = transaction_id
order.refund_time = datetime.fromisoformat(success_time.replace('Z', '+00:00'))
elif out_trade_no.startswith('P'): # 商家在线买单

View File

@ -93,6 +93,12 @@ class ShippingOrderDB(Base):
prepay_id = Column(String(64)) # 微信支付预支付ID
pay_time = Column(DateTime(timezone=True)) # 支付时间
transaction_id = Column(String(64)) # 微信支付交易号
# 退款相关字段
refund_amount = Column(Float, default=0) # 退款金额
refund_reason = Column(String(200), nullable=True) # 退款原因
refund_user_id = Column(Integer, ForeignKey("users.userid"), nullable=True) # 退款操作人
refund_time = Column(DateTime(timezone=True), nullable=True) # 退款时间
@property
def optimized_complete_images(self):
@ -109,7 +115,7 @@ class ShippingOrderDB(Base):
@property
def original_amount_with_additional_fee(self):
return self.original_amount + self.additional_fee_amount
class ShippingOrderPackageDB(Base):
__tablename__ = "shipping_order_packages"
@ -196,11 +202,11 @@ class OrderInfo(BaseModel):
completed_time: Optional[datetime] = None
is_first_order: bool
# def __init__(self, **data):
# super().__init__(**data)
# # 将逗号分隔的图片URL字符串转换为列表
# if self.complete_images and isinstance(self.complete_images, str):
# self.complete_images = self.complete_images.split(",")
# 退款相关字段
refund_amount: float = 0
refund_reason: Optional[str] = None
refund_user_id: Optional[int] = None
refund_time: Optional[datetime] = None
class Config:
from_attributes = True
@ -241,4 +247,9 @@ class OrderPriceResult(BaseModel):
price_info: OrderPriceInfo
used_coupon_id: Optional[int] = None # 使用的优惠券ID
used_points: Optional[int] = None # 使用的积分数
price_detail_text: Optional[str] = None # 价格详情文本
price_detail_text: Optional[str] = None # 价格详情文本
class RefundOrderAmountRequest(BaseModel):
order_id: str
amount: float
reason: str

View File

@ -46,5 +46,46 @@ class WechatPaymentRecordInfo(BaseModel):
create_time: datetime
update_time: Optional[datetime] = None
class Config:
from_attributes = True
class WechatRefundRecordDB(Base):
"""微信退款记录表"""
__tablename__ = "wechat_refund_records"
id = Column(Integer, primary_key=True, autoincrement=True)
out_trade_no = Column(String(32), nullable=False, index=True, comment='商户订单号')
out_refund_no = Column(String(64), nullable=False, index=True, comment='商户退款单号')
refund_id = Column(String(64), index=True, comment='微信退款单号')
transaction_id = Column(String(64), index=True, comment='微信支付交易号')
refund_status = Column(String(32), comment='退款状态')
success_time = Column(DateTime(timezone=True), comment='退款成功时间')
refund_amount = Column(DECIMAL(10, 2), comment='退款金额')
total_amount = Column(DECIMAL(10, 2), comment='订单总金额')
user_received_account = Column(String(256), comment='退款入账账户')
currency = Column(String(16), default='CNY', comment='货币类型')
refund_reason = Column(String(256), comment='退款原因')
raw_data = Column(JSON, comment='原始返回数据')
create_time = Column(DateTime(timezone=True), server_default=func.now(), comment='创建时间')
update_time = Column(DateTime(timezone=True), onupdate=func.now(), comment='更新时间')
# Pydantic 模型
class WechatRefundRecordInfo(BaseModel):
id: int
out_trade_no: str
out_refund_no: str
refund_id: Optional[str] = None
transaction_id: Optional[str] = None
refund_status: Optional[str] = None
success_time: Optional[datetime] = None
refund_amount: Optional[float] = None
total_amount: Optional[float] = None
user_received_account: Optional[str] = None
currency: str = 'CNY'
refund_reason: Optional[str] = None
raw_data: Optional[Dict[str, Any]] = None
create_time: datetime
update_time: Optional[datetime] = None
class Config:
from_attributes = True

View File

@ -176,4 +176,13 @@ ADD UNIQUE INDEX uix_fee_order_id (fee_order_id);
ALTER TABLE order_additional_fees
ADD COLUMN transaction_id VARCHAR(64) NULL COMMENT '微信支付交易号';
--====FINISH 4.5====
-- 为 shipping_orders 表添加 refund_amount 等字段
ALTER TABLE shipping_orders
ADD COLUMN refund_amount FLOAT DEFAULT 0,
ADD COLUMN refund_reason VARCHAR(200),
ADD COLUMN refund_user_id INT,
ADD CONSTRAINT fk_refund_user_id FOREIGN KEY (refund_user_id) REFERENCES users(userid);

Binary file not shown.