diff --git a/app/api/endpoints/coupon_activity.py b/app/api/endpoints/coupon_activity.py index 2ed2262..81ef7da 100644 --- a/app/api/endpoints/coupon_activity.py +++ b/app/api/endpoints/coupon_activity.py @@ -175,12 +175,14 @@ async def check_activity_can_receive( return False, f"优惠券活动已领完", activity # 检查用户领取次数 - user_receive_count = db.query(func.count(CouponReceiveRecordDB.id)).filter( + record = db.query(CouponReceiveRecordDB).filter( CouponReceiveRecordDB.user_id == current_user.userid, CouponReceiveRecordDB.activity_id == activity_id - ).scalar() - if user_receive_count >= activity.user_limit: - return False, "你已经领过了", activity + ).first() + + if record: + if record.receive_count >= activity.user_limit: + return False, "你已经领过了", activity return True, "可领取", activity diff --git a/app/api/endpoints/dashboard.py b/app/api/endpoints/dashboard.py index d6526dc..d08bae6 100644 --- a/app/api/endpoints/dashboard.py +++ b/app/api/endpoints/dashboard.py @@ -40,10 +40,10 @@ async def get_dashboard_info( # 查询已下单用户数 all_orders = db.query(ShippingOrderDB).all() - completed_orders = [order for order in all_orders if order.status == OrderStatus.COMPLETED or order.status == OrderStatus.UNPAID] + completed_orders = [order for order in all_orders if order.status == OrderStatus.COMPLETED] has_order_user_count = len(set([order.userid for order in all_orders if order.status != OrderStatus.CANCELLED])) - has_order_completed_user_count = len(set([order.userid for order in completed_orders if order.status == OrderStatus.COMPLETED or order.status == OrderStatus.UNPAID])) + has_order_completed_user_count = len(set([order.userid for order in completed_orders if order.status == OrderStatus.COMPLETED])) has_paid_user_count = len(set([order.userid for order in completed_orders if order.status == OrderStatus.COMPLETED and order.final_amount > 0])) order_count = len(completed_orders) @@ -169,8 +169,8 @@ async def get_deliveryman_dashboard_info( result = [] for deliveryman in deliverymans: - orders = db.query(ShippingOrderDB).filter(ShippingOrderDB.deliveryman_user_id == deliveryman.UserDB.userid, ShippingOrderDB.status.in_([OrderStatus.COMPLETED, OrderStatus.UNPAID])).all() - today_orders = db.query(ShippingOrderDB).filter(ShippingOrderDB.deliveryman_user_id == deliveryman.UserDB.userid, ShippingOrderDB.status.in_([OrderStatus.COMPLETED, OrderStatus.UNPAID]), ShippingOrderDB.completed_time >= datetime.now().date()).all() + orders = db.query(ShippingOrderDB).filter(ShippingOrderDB.deliveryman_user_id == deliveryman.UserDB.userid, ShippingOrderDB.status==OrderStatus.COMPLETED).all() + today_orders = [order for order in orders if order.completed_time.date() == datetime.now().date()] result.append({ "deliveryman_id": deliveryman.UserDB.userid, "deliveryman_name": deliveryman.UserDB.nickname, diff --git a/app/api/endpoints/order.py b/app/api/endpoints/order.py index dc46d32..147099b 100644 --- a/app/api/endpoints/order.py +++ b/app/api/endpoints/order.py @@ -228,18 +228,6 @@ async def pre_order( message += f"周{day}, " message = message[:-2] return error_response(code=400, message=message) - - # 检查是否有配送员在线 - # deliveryman_online = db.query(UserDB).filter( - # UserDB.roles.contains(UserRole.DELIVERYMAN), - # UserDB.community_id == request.community_id, - # UserDB.is_delivering == True - # ).first() - - # if deliveryman_online is None: - # print(f"没有配送员在线, 无法下单") - # return error_response(code=400, message="没有配送员在线, 无法下单") - # 检查是否有未支付的订单 unpay_order = db.query(ShippingOrderDB).filter( @@ -326,7 +314,7 @@ async def create_order( more_station_price=price_info.more_station_price, coupon_id=coupon_id, final_amount=price_info.final_amount, - status=OrderStatus.CREATED, + status=OrderStatus.UNPAID if price_info.final_amount > 0 else OrderStatus.CREATED, delivery_method=order.delivery_method, delivery_date=order.delivery_date, is_first_order=is_first_order @@ -634,7 +622,7 @@ async def deliveryman_get_order_status_count( ): """获取社区订单状态数量""" - status_list = [OrderStatus.CREATED, OrderStatus.RECEIVED, OrderStatus.DELIVERING, OrderStatus.UNPAID, OrderStatus.COMPLETED] + status_list = [OrderStatus.CREATED, OrderStatus.RECEIVED, OrderStatus.DELIVERING, OrderStatus.COMPLETED] result = [] for status in status_list: @@ -1236,11 +1224,8 @@ async def deliveryman_complete_order( return error_response(code=400, message="只有配送中的订单才能标记为完成") try: - # 根据订单金额决定状态 - if order.final_amount > 0: - order.status = OrderStatus.UNPAID # 需要支付 - else: - order.status = OrderStatus.COMPLETED # 无需支付,直接完成 + + order.status = OrderStatus.COMPLETED # 无需支付,直接完成 # 保存完成图片 if complete_data.images: diff --git a/app/api/endpoints/order_additional_fee.py b/app/api/endpoints/order_additional_fee.py index 1ab3bfa..ccf8bcf 100644 --- a/app/api/endpoints/order_additional_fee.py +++ b/app/api/endpoints/order_additional_fee.py @@ -20,6 +20,7 @@ from app.core.qcloud import qcloud_manager from fastapi import BackgroundTasks from app.core.mpmessage import sent_order_status_change_message from app.api.endpoints.order import calculate_delivery_share +from app.core.utils import CommonUtils router = APIRouter() @@ -60,7 +61,8 @@ async def create_additional_fee_request( reason=fee_request.reason, photo_urls=fee_request.photo_urls, additional_fee_amount=fee_request.additional_fee_amount, - result=AdditionalFeeResult.PENDING + result=AdditionalFeeResult.PENDING, + fee_order_id=CommonUtils.generate_order_id('AD') ) db.add(db_fee_request) diff --git a/app/api/endpoints/wechat.py b/app/api/endpoints/wechat.py index b4957de..d128538 100644 --- a/app/api/endpoints/wechat.py +++ b/app/api/endpoints/wechat.py @@ -24,9 +24,15 @@ from app.core.point_manager import PointRecordType from app.core.account import AccountManager 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 router = APIRouter() +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) + class PhoneNumberRequest(BaseModel): login_code: str # 登录凭证 phone_code: str # 手机号验证码 @@ -136,7 +142,17 @@ async def create_payment( return error_response(code=404, message="订单不存在") description = "蜂快到家-配送费用" amount = order.final_amount - + + if order_prefix == "AD": # 加价订单 + order = db.query(OrderAdditionalFeeDB).filter( + OrderAdditionalFeeDB.fee_order_id == request.order_id + ).first() + if not order: + logger.error(f"加价订单不存在: {request.order_id}") + return error_response(code=404, message="订单不存在") + description = "蜂快到家-加价费用" + amount = order.additional_fee_amount + elif order_prefix == "M": # 商家商品订单 order = db.query(MerchantOrderDB).filter( MerchantOrderDB.order_id == request.order_id @@ -192,20 +208,20 @@ async def payment_notify( data = await wechat.verify_payment_notify(request) if not data: - print(f"回调数据验证失败: {data}") + logger.error(f"回调数据验证失败: {data}") return error_response(code=400, message="回调数据验证失败") - print(f"回调数据验证成功: {data}") + logger.info(f"回调数据验证成功: {data}") # 获取订单信息 out_trade_no = data.get("out_trade_no") transaction_id = data.get("transaction_id") trade_state_desc = data.get("trade_state_desc") success_time = data.get("success_time") - print(f"out_trade_no: {out_trade_no}") - print(f"transaction_id: {transaction_id}") - print(f"trade_state_desc: {trade_state_desc}") - print(f"success_time: {success_time}") + logger.info(f"out_trade_no: {out_trade_no}") + logger.info(f"transaction_id: {transaction_id}") + logger.info(f"trade_state_desc: {trade_state_desc}") + logger.info(f"success_time: {success_time}") if not all([out_trade_no, transaction_id, trade_state_desc]) or trade_state_desc != "支付成功": return error_response(code=400, message="缺少必要的订单信息或支付未成功") @@ -222,12 +238,42 @@ async def payment_notify( if trade_state_desc == "支付成功": # 更新订单状态 + if order.deliveryman_user_id: + order.status = OrderStatus.COMPLETED # 支付成功后状态为已完成 + else: + order.status = OrderStatus.CREATED # 支付成功后状态为已创建 + order.pay_status = True - order.status = OrderStatus.COMPLETED # 支付成功后状态为已完成 order.completed_time = datetime.now() order.transaction_id = transaction_id order.pay_time = datetime.fromisoformat(success_time.replace('Z', '+00:00')) + + elif out_trade_no.startswith("AD"): # 加价订单 + additon_fee_order = db.query(OrderAdditionalFeeDB).filter( + OrderAdditionalFeeDB.fee_order_id == out_trade_no + ).first() + if not additon_fee_order: + return error_response(code=404, message="订单不存在") + + if trade_state_desc == "支付成功": + # 更新订单状态 + additon_fee_order.result = AdditionalFeeResult.ACCEPTED + additon_fee_order.transaction_id = transaction_id + + order = db.query(ShippingOrderDB).filter( + ShippingOrderDB.orderid == additon_fee_order.orderid + ).first() + if not order: + return error_response(code=404, message="订单不存在") + order.additional_fee_amount += float(additon_fee_order.additional_fee_amount) + order.final_amount += float(additon_fee_order.additional_fee_amount) + + logger.info(f"加价订单支付成功: {additon_fee_order.orderid}") + + # 重新计算配送员配送费用 + order.delivery_share = calculate_delivery_share(order, db) + elif out_trade_no.startswith("M"): # 商家商品订单 # 查询商户订单 @@ -299,13 +345,28 @@ async def payment_notify( else: return error_response(code=400, message="未知的订单类型") + # 在支付回调处理中添加记录 + payment_record = WechatPaymentRecordDB( + out_trade_no=out_trade_no, + transaction_id=transaction_id, + trade_state=data.get("trade_state"), + trade_state_desc=trade_state_desc, + success_time=datetime.fromisoformat(success_time) if success_time else None, + total_amount=float(data.get("amount", {}).get("total", 0)) / 100, + payer_total=float(data.get("amount", {}).get("payer_total", 0)) / 100, + payer_openid=data.get("payer", {}).get("openid"), + bank_type=data.get("bank_type"), + promotion_detail=data.get("promotion_detail"), + raw_data=data + ) + db.add(payment_record) db.commit() # 返回成功 return success_response(message="支付成功") except Exception as e: - print(f"处理支付回调失败: {str(e)}") + logger.error(f"处理支付回调失败: {str(e)}") db.rollback() return error_response(code=500, message=f"处理支付回调失败: {str(e)}") @@ -321,10 +382,10 @@ async def refund_notify( data = await wechat.verify_payment_notify(request) if not data: - print(f"退款回调数据验证失败: {data}") + logger.error(f"退款回调数据验证失败: {data}") return error_response(code=400, message="回调数据验证失败") - print(f"退款回调数据验证成功: {data}") + logger.info(f"退款回调数据验证成功: {data}") # 获取退款信息 out_trade_no = data.get("out_trade_no") # 订单号 @@ -378,6 +439,6 @@ async def refund_notify( return success_response(message="退款处理成功") except Exception as e: - print(f"处理退款回调失败: {str(e)}") + logger.error(f"处理退款回调失败: {str(e)}") db.rollback() return error_response(code=500, message=f"处理退款回调失败: {str(e)}") \ No newline at end of file diff --git a/app/models/order_additional_fee.py b/app/models/order_additional_fee.py index f4db832..885c6e7 100644 --- a/app/models/order_additional_fee.py +++ b/app/models/order_additional_fee.py @@ -16,6 +16,7 @@ class OrderAdditionalFeeDB(Base): id = Column(Integer, primary_key=True, autoincrement=True) orderid = Column(String(32), ForeignKey("shipping_orders.orderid"), nullable=False, index=True) + fee_order_id = Column(String(32), nullable=False, unique=True) # 子订单号,用于支付标识 order_user_id = Column(Integer, ForeignKey("users.userid"), nullable=False) deliveryman_id = Column(Integer, ForeignKey("users.userid"), nullable=False) reason = Column(String(200), nullable=False) @@ -24,6 +25,7 @@ class OrderAdditionalFeeDB(Base): result = Column(Enum(AdditionalFeeResult), nullable=False, default=AdditionalFeeResult.PENDING) create_time = Column(DateTime(timezone=True), server_default=func.now()) update_time = Column(DateTime(timezone=True), onupdate=func.now()) + transaction_id = Column(String(64), nullable=True) # 微信支付交易号 # Pydantic 模型 class OrderAdditionalFeeCreate(BaseModel): diff --git a/app/models/wechat_payment.py b/app/models/wechat_payment.py new file mode 100644 index 0000000..0f2aa23 --- /dev/null +++ b/app/models/wechat_payment.py @@ -0,0 +1,50 @@ +# app/models/wechat_payment.py +from sqlalchemy import Column, Integer, String, DateTime, DECIMAL, JSON +from sqlalchemy.sql import func +from pydantic import BaseModel, Field +from typing import Optional, Dict, Any, List +from datetime import datetime +from .database import Base + +class WechatPaymentRecordDB(Base): + """微信支付记录表""" + __tablename__ = "wechat_payment_records" + + id = Column(Integer, primary_key=True, autoincrement=True) + out_trade_no = Column(String(32), nullable=False, index=True, comment='商户订单号') + transaction_id = Column(String(64), index=True, comment='微信支付交易号') + trade_state = Column(String(32), comment='交易状态') + trade_state_desc = Column(String(256), comment='交易状态描述') + success_time = Column(DateTime(timezone=True), comment='支付成功时间') + total_amount = Column(DECIMAL(10, 2), comment='订单总金额') + payer_total = Column(DECIMAL(10, 2), comment='用户实际支付金额') + currency = Column(String(16), default='CNY', comment='货币类型') + payer_openid = Column(String(128), comment='支付者openid') + attach = Column(String(128), comment='附加数据') + bank_type = Column(String(32), comment='付款银行类型') + promotion_detail = Column(JSON, 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 WechatPaymentRecordInfo(BaseModel): + id: int + out_trade_no: str + transaction_id: Optional[str] = None + trade_state: Optional[str] = None + trade_state_desc: Optional[str] = None + success_time: Optional[datetime] = None + total_amount: Optional[float] = None + payer_total: Optional[float] = None + currency: str = 'CNY' + payer_openid: Optional[str] = None + attach: Optional[str] = None + bank_type: Optional[str] = None + promotion_detail: Optional[List[Dict[str, Any]]] = None + raw_data: Optional[Dict[str, Any]] = None + create_time: datetime + update_time: Optional[datetime] = None + + class Config: + from_attributes = True \ No newline at end of file diff --git a/app/sql/v1.1.sql b/app/sql/v1.1.sql index 526e08d..a9737b0 100644 --- a/app/sql/v1.1.sql +++ b/app/sql/v1.1.sql @@ -162,4 +162,18 @@ ADD CONSTRAINT fk_merchant_community FOREIGN KEY (community_id) REFERENCES communities(id) ---====FINISH 3.24==== \ No newline at end of file +--====FINISH 3.24==== + +-- 为 order_additional_fees 表添加 fee_order_id 字段 +ALTER TABLE order_additional_fees +ADD COLUMN fee_order_id VARCHAR(32) NOT NULL COMMENT '子订单号,用于支付标识'; + +-- 创建唯一索引 +ALTER TABLE order_additional_fees +ADD UNIQUE INDEX uix_fee_order_id (fee_order_id); + +-- 为 order_additional_fees 表添加 transaction_id 字段 +ALTER TABLE order_additional_fees +ADD COLUMN transaction_id VARCHAR(64) NULL COMMENT '微信支付交易号'; + + diff --git a/app/tasks/daily_tasks.py b/app/tasks/daily_tasks.py index a552b11..69e8b97 100644 --- a/app/tasks/daily_tasks.py +++ b/app/tasks/daily_tasks.py @@ -54,7 +54,7 @@ async def daily_community_order_statistics(): communities c ON o.address_community_id = c.id WHERE o.completed_time BETWEEN :start_time AND :end_time - AND o.status in ('COMPLETED', 'UNPAID') + AND o.status = 'COMPLETED' GROUP BY c.id, c.name ORDER BY diff --git a/jobs.sqlite b/jobs.sqlite index b6c79a2..0c998a1 100644 Binary files a/jobs.sqlite and b/jobs.sqlite differ