1735 lines
64 KiB
Python
1735 lines
64 KiB
Python
from app.core.account import AccountManager
|
||
from fastapi import APIRouter, Depends
|
||
from sqlalchemy.orm import Session
|
||
from typing import List, Optional
|
||
from app.models.order import (
|
||
ShippingOrderDB,
|
||
ShippingOrderPackageDB,
|
||
OrderCreate,
|
||
OrderInfo,
|
||
OrderPackageInfo,
|
||
OrderPriceCalculateRequest,
|
||
OrderPriceInfo,
|
||
OrderStatus,
|
||
OrderCancel,
|
||
OrderComplete,
|
||
OrderPriceResult
|
||
)
|
||
from app.models.order_additional_fee import OrderAdditionalFeeDB, OrderAdditionalFeeInfo, AdditionalFeeResult
|
||
from app.models.database import get_db
|
||
from app.api.deps import get_current_user, get_deliveryman_user, get_admin_user
|
||
from app.models.user import UserDB,UserRole
|
||
from app.core.response import success_response, error_response, ResponseModel
|
||
from app.models.coupon import UserCouponDB, CouponStatus, CouponType
|
||
from app.models.point_product_order import PointProductOrderDB, PointProductOrderInfo, PointProductOrderStatus
|
||
from datetime import datetime, timezone
|
||
from app.core.config import settings
|
||
from app.models.address import AddressDB
|
||
from sqlalchemy.orm import joinedload
|
||
from app.models.community import CommunityDB
|
||
from app.models.community_building import CommunityBuildingDB, CommunityBuildingInfo
|
||
from app.models.station import StationDB
|
||
from app.models.point import PointRecordDB, PointRecordType
|
||
from app.core.utils import CommonUtils
|
||
import logging
|
||
from sqlalchemy import func
|
||
from app.core.mpclient import mp_client
|
||
from datetime import timedelta
|
||
from app.core.imageprocessor import process_image, ImageFormat
|
||
from app.core.point_manager import PointManager
|
||
from datetime import time
|
||
from app.core.wecombot import WecomBot
|
||
from app.models.config import ConfigDB
|
||
from app.core.mpmessage import sent_order_status_change_message
|
||
from fastapi import BackgroundTasks
|
||
from app.core.coupon_manager import CouponManager
|
||
from app.core.redis_client import redis_client
|
||
from app.models.timeperiod import TimePeriodDB
|
||
from app.models.community_timeperiod import CommunityTimePeriodDB
|
||
from app.models.community_profit_sharing import CommunityProfitSharing
|
||
|
||
router = APIRouter()
|
||
|
||
def calculate_price(price_request: OrderPriceCalculateRequest,user: UserDB,db: Session) -> OrderPriceResult:
|
||
"""
|
||
计算订单价格,自动使用优惠券或积分抵扣
|
||
|
||
Args:
|
||
price_request: 价格计算请求
|
||
user: 用户信息
|
||
|
||
Returns:
|
||
OrderPriceResult: 包含价格信息和使用的优惠券/积分信息
|
||
"""
|
||
# 计算所有包裹中的取件码总数
|
||
pickup_code_count = 0
|
||
pickup_images_count = 0
|
||
package_count = 0
|
||
if price_request.pickup_images:
|
||
pickup_images_count = price_request.pickup_images_count
|
||
|
||
if price_request.packages:
|
||
pickup_code_count = sum(
|
||
# 如果package.pickup_codes是空字符串,则取0
|
||
0 if len(package.pickup_codes.split(',')) == 0 else len(package.pickup_codes.split(','))
|
||
for package in price_request.packages
|
||
if package.pickup_codes
|
||
)
|
||
|
||
package_count = pickup_code_count + pickup_images_count
|
||
|
||
result = OrderPriceResult(
|
||
price_info=OrderPriceInfo(
|
||
package_count=package_count,
|
||
pickup_images_count=pickup_images_count,
|
||
pickup_code_count=pickup_code_count,
|
||
coupon_discount_amount=0,
|
||
points_discount_amount=0,
|
||
original_amount=0,
|
||
base_delivery_amount=0,
|
||
more_station_price=0,
|
||
final_amount=0
|
||
)
|
||
)
|
||
|
||
# 默认定价
|
||
base_price = settings.ORDER_BASE_PRICE
|
||
extra_package_price = settings.ORDER_EXTRA_PACKAGE_PRICE
|
||
extra_package_threshold = settings.ORDER_EXTRA_PACKAGE_THRESHOLD
|
||
base_more_station_price = 0
|
||
|
||
# 获取小区定价
|
||
if price_request.community_id > 0:
|
||
community = db.query(CommunityDB).filter(
|
||
CommunityDB.id == price_request.community_id
|
||
).first()
|
||
if community:
|
||
base_price = float(community.base_price)
|
||
extra_package_price = float(community.extra_package_price)
|
||
extra_package_threshold = int(community.extra_package_threshold)
|
||
base_more_station_price = float(community.more_station_price)
|
||
|
||
# 是否有多驿站
|
||
more_station_price = float(community.more_station_price) * (len(price_request.packages) - 1) if len(price_request.packages) > 1 else 0
|
||
base_delivery_amount = round(base_price + extra_package_price * max(0, package_count - extra_package_threshold), 2)
|
||
original_amount = round(more_station_price + base_delivery_amount, 2)
|
||
|
||
result.price_info.package_count = package_count
|
||
result.price_info.pickup_images_count = pickup_images_count
|
||
result.price_info.pickup_code_count = pickup_code_count
|
||
result.price_info.original_amount = original_amount
|
||
result.price_info.more_station_price = more_station_price
|
||
result.price_info.final_amount = original_amount
|
||
result.price_info.base_delivery_amount = base_delivery_amount
|
||
|
||
remaining_amount = original_amount
|
||
|
||
# 1. 查找用户可用的优惠券(按金额从大到小排序)
|
||
available_coupon = db.query(UserCouponDB).filter(
|
||
UserCouponDB.user_id == user.userid,
|
||
UserCouponDB.coupon_type == CouponType.CASH,
|
||
UserCouponDB.status == CouponStatus.UNUSED,
|
||
UserCouponDB.expire_time > datetime.now()
|
||
).order_by(UserCouponDB.coupon_amount.desc()).first()
|
||
|
||
# 2. 如果有可用优惠券,优先使用优惠券
|
||
if available_coupon:
|
||
coupon_discount = min(remaining_amount, available_coupon.coupon_amount)
|
||
result.price_info.coupon_discount_amount = coupon_discount
|
||
result.price_info.coupon_id = available_coupon.id
|
||
result.used_coupon_id = available_coupon.id
|
||
remaining_amount -= coupon_discount
|
||
# 3. 如果没有优惠券,且用户有积分,则使用积分抵扣
|
||
elif user.points > 0:
|
||
# 计算最大可抵扣金额
|
||
max_points_discount = user.points // settings.POINT_RATIO # 使用整除
|
||
points_discount_amount = min(remaining_amount, max_points_discount)
|
||
|
||
if points_discount_amount > 0:
|
||
result.price_info.points_discount_amount = points_discount_amount
|
||
result.used_points = int(points_discount_amount * settings.POINT_RATIO)
|
||
remaining_amount -= points_discount_amount
|
||
|
||
# 计算最终金额
|
||
result.price_info.final_amount = round(remaining_amount, 2)
|
||
|
||
# 计算价格详情
|
||
result.price_detail_text = f"* 基础费{round(base_price, 1)}元(含1个驿站和{extra_package_threshold}件包裹), 超件{round(extra_package_price, 1)}元/件\r\n* 超过1个驿站, 增加配送路程, 加收{round(base_more_station_price, 1)}元/驿站"
|
||
|
||
return result
|
||
|
||
def calculate_delivery_share(order: ShippingOrderDB, db: Session) -> float:
|
||
# 获取对应小区的分润
|
||
sharing = db.query(CommunityProfitSharing).filter(
|
||
CommunityProfitSharing.community_id == order.address_community_id
|
||
).first()
|
||
if sharing:
|
||
return round(order.original_amount_with_additional_fee * (float(sharing.delivery_rate) / 100.0), 2)
|
||
else:
|
||
return 0
|
||
|
||
def format_delivery_time(delivery_date: datetime, time_period_name: str) -> str:
|
||
if delivery_date == datetime.now().date():
|
||
return f"今日 {time_period_name}"
|
||
elif delivery_date == datetime.now().date() + timedelta(days=1):
|
||
return f"明日 {time_period_name}"
|
||
elif delivery_date == datetime.now().date() - timedelta(days=1):
|
||
return f"昨日 {time_period_name}"
|
||
else:
|
||
return f"{delivery_date.strftime('%m-%d')} {time_period_name}"
|
||
|
||
def has_consecutive_weekdays(weekdays):
|
||
if not weekdays or len(weekdays) <= 1:
|
||
return True
|
||
|
||
days = sorted(weekdays)
|
||
|
||
# 处理环绕情况
|
||
if 1 in days and 7 in days and abs(days.index(1) - days.index(7)) == 1:
|
||
# 特殊处理周日和周一的连接
|
||
temp = days.copy()
|
||
temp.remove(1 if days.index(1) == 0 else 7)
|
||
return all(temp[i] - temp[i-1] == 1 for i in range(1, len(temp)))
|
||
|
||
# 普通情况
|
||
return all(days[i] - days[i-1] == 1 for i in range(1, len(days)))
|
||
|
||
@router.post("/pre-order", response_model=ResponseModel)
|
||
async def pre_order(
|
||
request: OrderPriceCalculateRequest,
|
||
db: Session = Depends(get_db),
|
||
current_user: UserDB = Depends(get_current_user)
|
||
):
|
||
|
||
try:
|
||
# 检查delivery_date是否是今天以前的日期,则提示重新选择
|
||
if request.delivery_date and request.delivery_date < datetime.now().date():
|
||
return error_response(code=400, message="配送日期不能选择过去的时间")
|
||
|
||
# 检查是否在服务时间
|
||
community = db.query(CommunityDB).filter(
|
||
CommunityDB.id == request.community_id
|
||
).first()
|
||
|
||
if community:
|
||
# 检查是否在服务时间
|
||
if community.weekdays and request.delivery_date:
|
||
if request.delivery_date.isoweekday() not in community.weekdays:
|
||
|
||
#排序
|
||
sorted_weekdays = sorted(community.weekdays)
|
||
|
||
if has_consecutive_weekdays(sorted_weekdays):
|
||
message = f"服务时间为: 周{sorted_weekdays[0]}-{sorted_weekdays[-1]}"
|
||
else:
|
||
message = f"服务时间为: "
|
||
for day in sorted_weekdays:
|
||
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(
|
||
ShippingOrderDB.userid == current_user.userid,
|
||
ShippingOrderDB.status == OrderStatus.UNPAID
|
||
).first()
|
||
|
||
if unpay_order:
|
||
return error_response(code=400, message="存在未完成的订单", data={
|
||
"orderid": unpay_order.orderid
|
||
})
|
||
|
||
"""预下单 - 计算价格"""
|
||
price_info = calculate_price(request, current_user, db)
|
||
return success_response(data=price_info)
|
||
except Exception as e:
|
||
logging.exception(f"预下单失败: {str(e)}")
|
||
return error_response(code=500, message=f"预下单失败: {str(e)}")
|
||
|
||
@router.post("", response_model=ResponseModel)
|
||
async def create_order(
|
||
background_tasks: BackgroundTasks,
|
||
order: OrderCreate,
|
||
db: Session = Depends(get_db),
|
||
current_user: UserDB = Depends(get_current_user)
|
||
):
|
||
"""创建配送订单"""
|
||
|
||
# 计算订单价格
|
||
price_result = calculate_price(order.price_request, current_user, db)
|
||
price_info = price_result.price_info
|
||
|
||
# 生成订单号
|
||
orderid = CommonUtils.generate_order_id('D')
|
||
|
||
# 计算原始金额
|
||
original_amount = price_info.original_amount
|
||
|
||
# 计算优惠券折扣
|
||
coupon_discount = price_info.coupon_discount_amount
|
||
coupon_id = price_info.coupon_id
|
||
|
||
# 查询用户优惠券
|
||
if coupon_id:
|
||
user_coupon = db.query(UserCouponDB).filter(
|
||
UserCouponDB.id == coupon_id,
|
||
).first()
|
||
|
||
if user_coupon:
|
||
coupon_discount = user_coupon.coupon_amount
|
||
coupon_id = user_coupon.id
|
||
# 更新优惠券状态
|
||
user_coupon.status = CouponStatus.USED
|
||
|
||
# 查询地址信息
|
||
address = db.query(AddressDB).filter(
|
||
AddressDB.id == order.addressid
|
||
).first()
|
||
|
||
# 是否为新人订单
|
||
is_first_order = db.query(ShippingOrderDB).filter(
|
||
ShippingOrderDB.userid == current_user.userid,
|
||
ShippingOrderDB.status != OrderStatus.CANCELLED
|
||
).count() == 0
|
||
|
||
# 创建订单
|
||
db_order = ShippingOrderDB(
|
||
orderid=orderid,
|
||
userid=current_user.userid,
|
||
address_customer_name=address.name,
|
||
address_customer_phone=address.phone,
|
||
address_customer_gender=address.gender,
|
||
address_community_id=address.community_id,
|
||
address_community_building_id=address.community_building_id,
|
||
address_community_name=address.community_name,
|
||
address_community_building_name=address.community_building_name,
|
||
address_detail=address.address_detail,
|
||
package_count=price_info.package_count,
|
||
pickup_images_count=price_info.pickup_images_count,
|
||
pickup_code_count=price_info.pickup_code_count,
|
||
original_amount=original_amount,
|
||
coupon_discount_amount=coupon_discount,
|
||
point_discount_amount=price_info.points_discount_amount,
|
||
more_station_price=price_info.more_station_price,
|
||
coupon_id=coupon_id,
|
||
final_amount=price_info.final_amount,
|
||
status=OrderStatus.CREATED,
|
||
delivery_method=order.delivery_method,
|
||
delivery_date=order.delivery_date,
|
||
is_first_order=is_first_order
|
||
)
|
||
|
||
# 获取社区配送时段
|
||
community_time_period = db.query(CommunityTimePeriodDB,
|
||
TimePeriodDB.name.label('time_period_name'),
|
||
TimePeriodDB.from_time.label('time_period_from_time'),
|
||
TimePeriodDB.to_time.label('time_period_to_time')).filter(
|
||
CommunityTimePeriodDB.id == order.community_time_period_id
|
||
).join(TimePeriodDB,
|
||
CommunityTimePeriodDB.time_period_id == TimePeriodDB.id).first()
|
||
|
||
if community_time_period:
|
||
db_order.time_period_id = community_time_period.CommunityTimePeriodDB.time_period_id
|
||
db_order.time_period_name = community_time_period.time_period_name
|
||
db_order.time_period_from_time = community_time_period.time_period_from_time
|
||
db_order.time_period_to_time = community_time_period.time_period_to_time
|
||
|
||
# 获取取件图片
|
||
if order.price_request.pickup_images:
|
||
db_order.pickup_images = order.price_request.pickup_images
|
||
|
||
db.add(db_order)
|
||
|
||
# 创建订单包裹
|
||
if order.price_request.packages:
|
||
for package in order.price_request.packages:
|
||
# 如果包裹有取件码,则创建包裹
|
||
if len(package.pickup_codes) > 0:
|
||
if not package.station_name:
|
||
station = db.query(StationDB).filter(
|
||
StationDB.id == package.station_id
|
||
).first()
|
||
if station:
|
||
station_name = station.name
|
||
else:
|
||
station_name = "未知驿站"
|
||
else:
|
||
station_name = package.station_name
|
||
|
||
# 对package.pickup_codes中分割的取件码,进行排序
|
||
sorted_pickup_codes = CommonUtils.sort_strings_by_first_number(package.pickup_codes.split(","))
|
||
|
||
db_package = ShippingOrderPackageDB(
|
||
orderid=orderid,
|
||
station_id=package.station_id,
|
||
station_name=station_name,
|
||
pickup_codes=','.join(sorted_pickup_codes)
|
||
)
|
||
db.add(db_package)
|
||
|
||
try:
|
||
# 如果使用了优惠券,更新优惠券状态
|
||
if price_result.used_coupon_id:
|
||
coupon = db.query(UserCouponDB).filter(
|
||
UserCouponDB.id == price_result.used_coupon_id,
|
||
).first()
|
||
if coupon:
|
||
coupon.status = CouponStatus.USED
|
||
coupon.used_time = datetime.now()
|
||
|
||
# 如果使用了积分,扣减用户积分并记录
|
||
if price_result.used_points:
|
||
# 扣减用户积分
|
||
current_user.points -= price_result.used_points
|
||
|
||
# 记录积分变动
|
||
point_record = PointRecordDB(
|
||
user_id=current_user.userid,
|
||
points=-price_result.used_points, # 负数表示扣减
|
||
type=PointRecordType.CONSUME_DEDUCT,
|
||
order_id=db_order.orderid,
|
||
description=f"配送订单抵扣积分"
|
||
)
|
||
db.add(point_record)
|
||
|
||
db.commit()
|
||
db.refresh(db_order)
|
||
|
||
# 查询包裹信息
|
||
packages = db.query(ShippingOrderPackageDB).filter(
|
||
ShippingOrderPackageDB.orderid == orderid
|
||
).all()
|
||
|
||
# 发送企业微信消息
|
||
wecom_bot = WecomBot()
|
||
background_tasks.add_task(
|
||
wecom_bot.send_order_notification,
|
||
db,
|
||
db_order,
|
||
OrderStatus.CREATED
|
||
)
|
||
|
||
#发送订单创建成功的公众号消息
|
||
if current_user.mp_openid:
|
||
data={
|
||
"character_string13": db_order.orderid,
|
||
"time4": db_order.create_time.strftime("%Y-%m-%d %H:%M:%S"),
|
||
"thing10": db_order.address_customer_name,
|
||
"thing15": f"{db_order.address_community_name} {db_order.address_community_building_name} {db_order.address_detail}",
|
||
"thing9": db_order.status.status_text
|
||
}
|
||
|
||
background_tasks.add_task(
|
||
sent_order_status_change_message,
|
||
openid=current_user.mp_openid,
|
||
template_id=settings.DELIVERY_ORDER_CREATED_TEMPLATE_ID,
|
||
data=data,
|
||
orderid=db_order.orderid
|
||
)
|
||
|
||
# 添加到新订单队列
|
||
if db_order.address_community_id:
|
||
background_tasks.add_task(
|
||
redis_client.push_new_order_to_queue,
|
||
db_order.address_community_id,
|
||
db_order.orderid,
|
||
db
|
||
)
|
||
|
||
# 今日订单加入今日选中的配送时段的 Redis 消息队列
|
||
background_tasks.add_task(
|
||
redis_client.push_order_to_community_period_queue,
|
||
db_order.delivery_date,
|
||
order.community_time_period_id,
|
||
db_order.orderid
|
||
)
|
||
|
||
return success_response(
|
||
message="订单创建成功",
|
||
data={
|
||
"order": OrderInfo.model_validate(db_order),
|
||
"packages": [OrderPackageInfo.model_validate(p) for p in packages],
|
||
"delivery_time": format_delivery_time(db_order.delivery_date, db_order.time_period_name),
|
||
"success_text" : f"预计 {format_delivery_time(db_order.delivery_date, db_order.time_period_name)} 送达,请注意查收"
|
||
}
|
||
)
|
||
except Exception as e:
|
||
db.rollback()
|
||
logging.exception(f"订单创建失败: {str(e)}")
|
||
return error_response(code=500, message=f"订单创建失败: {str(e)}")
|
||
|
||
@router.get("/{orderid}", response_model=ResponseModel)
|
||
async def get_order_detail(
|
||
orderid: str,
|
||
db: Session = Depends(get_db),
|
||
current_user: UserDB = Depends(get_current_user)
|
||
):
|
||
|
||
"""获取订单详情"""
|
||
# 使用 join 查询获取订单和相关地址信息
|
||
order = db.query(
|
||
ShippingOrderDB
|
||
).filter(
|
||
ShippingOrderDB.orderid == orderid
|
||
).first()
|
||
|
||
if not order:
|
||
return error_response(code=404, message="订单不存在")
|
||
|
||
# 如果有配送员 id,则获取配送员信息
|
||
if order.deliveryman_user_id:
|
||
deliveryman_user = db.query(UserDB).filter(
|
||
UserDB.userid == order.deliveryman_user_id
|
||
).first()
|
||
|
||
if deliveryman_user:
|
||
deliveryman_user_name = deliveryman_user.nickname
|
||
deliveryman_user_avatar = deliveryman_user.optimized_avatar
|
||
deliveryman_user_phone = deliveryman_user.phone
|
||
delivery_count = db.query(ShippingOrderDB).filter(
|
||
ShippingOrderDB.deliveryman_user_id == order.deliveryman_user_id,
|
||
ShippingOrderDB.status == OrderStatus.COMPLETED
|
||
).count()
|
||
else:
|
||
delivery_count = 0
|
||
deliveryman_user_name = None
|
||
deliveryman_user_avatar = None
|
||
deliveryman_user_phone = None
|
||
|
||
|
||
# 查询包裹信息
|
||
packages = db.query(
|
||
ShippingOrderPackageDB
|
||
).filter(
|
||
ShippingOrderPackageDB.orderid == orderid
|
||
).all()
|
||
|
||
if packages:
|
||
# 构建包裹信息,包含驿站名称
|
||
package_list = [{
|
||
"id": p.id,
|
||
"orderid": p.orderid,
|
||
"station_id": p.station_id,
|
||
"station_name": p.station_name,
|
||
"pickup_codes": CommonUtils.sort_strings_by_first_number(p.pickup_codes.split(",")),
|
||
"create_time": p.create_time
|
||
} for p in packages]
|
||
else:
|
||
package_list = []
|
||
|
||
|
||
# 获取加价请求
|
||
request = db.query(OrderAdditionalFeeDB,
|
||
UserDB.nickname.label("deliveryman_name"),
|
||
UserDB.phone.label("deliveryman_phone"),
|
||
UserDB.avatar.label("deliveryman_avatar")).join(UserDB,
|
||
OrderAdditionalFeeDB.deliveryman_id == UserDB.userid).filter(
|
||
OrderAdditionalFeeDB.orderid == order.orderid,
|
||
OrderAdditionalFeeDB.result == AdditionalFeeResult.PENDING
|
||
).first()
|
||
|
||
order_additional_fee = None
|
||
if request:
|
||
order_additional_fee = {
|
||
"id": request.OrderAdditionalFeeDB.id,
|
||
"orderid": request.OrderAdditionalFeeDB.orderid,
|
||
"order_user_id": request.OrderAdditionalFeeDB.order_user_id,
|
||
"deliveryman_id": request.OrderAdditionalFeeDB.deliveryman_id,
|
||
"deliveryman_name": request.deliveryman_name,
|
||
"deliveryman_phone": request.deliveryman_phone,
|
||
"deliveryman_avatar": request.deliveryman_avatar,
|
||
"reason": request.OrderAdditionalFeeDB.reason,
|
||
"photo_urls": request.OrderAdditionalFeeDB.photo_urls,
|
||
"additional_fee_amount": request.OrderAdditionalFeeDB.additional_fee_amount,
|
||
"result": request.OrderAdditionalFeeDB.result,
|
||
}
|
||
|
||
# 计算配送时间
|
||
delivery_time = format_delivery_time(order.delivery_date, order.time_period_name)
|
||
|
||
# 计算配送员配送费用
|
||
deliveryman_share = order.delivery_share if order.delivery_share > 0 else calculate_delivery_share(order, db)
|
||
|
||
base_delivery_amount = order.original_amount - (order.more_station_price if order.more_station_price is not None else 0)
|
||
|
||
# 构建响应数据
|
||
order_data = {
|
||
"orderid": order.orderid,
|
||
"userid": order.userid,
|
||
"pickup_images": order.optimized_pickup_images,
|
||
"package_count": order.package_count,
|
||
"pickup_code_count": order.pickup_code_count,
|
||
"pickup_images_count": order.pickup_images_count,
|
||
"original_amount": order.original_amount,
|
||
"base_delivery_amount": base_delivery_amount,
|
||
"coupon_discount_amount": order.coupon_discount_amount,
|
||
"point_discount_amount": order.point_discount_amount,
|
||
"more_station_price": order.more_station_price,
|
||
"additional_fee_amount": order.additional_fee_amount,
|
||
"coupon_id": order.coupon_id,
|
||
"final_amount": order.final_amount,
|
||
"deliveryman_share": deliveryman_share,
|
||
"status": order.status,
|
||
"complete_images": order.optimized_complete_images,
|
||
"packages": package_list,
|
||
"is_first_order": order.is_first_order,
|
||
"cancel_reason": order.cancel_reason,
|
||
"order_additional_fee": order_additional_fee,
|
||
|
||
"create_time": order.create_time,
|
||
"complete_time": order.completed_time,
|
||
"pickup_time": order.pickup_time,
|
||
"received_time": order.received_time,
|
||
|
||
"delivery_date": order.delivery_date,
|
||
"delivery_method": order.delivery_method,
|
||
"deliveryman_user_id": order.deliveryman_user_id,
|
||
"deliveryman_nickname": deliveryman_user_name,
|
||
"deliveryman_avatar": deliveryman_user_avatar,
|
||
"deliveryman_phone": deliveryman_user_phone,
|
||
"delivery_count": delivery_count,
|
||
# 地址相关信息
|
||
"address_name": order.address_customer_name,
|
||
"address_phone": order.address_customer_phone,
|
||
"address_detail": order.address_detail,
|
||
"address_gender": order.address_customer_gender,
|
||
"building_id": order.address_community_building_id,
|
||
"building_name": order.address_community_building_name,
|
||
"community_id": order.address_community_id,
|
||
"community_name": order.address_community_name,
|
||
|
||
# 配送时段
|
||
"time_period_id": order.time_period_id,
|
||
"time_period_name": order.time_period_name,
|
||
"time_period_from_time": order.time_period_from_time,
|
||
"time_period_to_time": order.time_period_to_time,
|
||
|
||
# 配送时间
|
||
"delivery_time": delivery_time
|
||
}
|
||
|
||
return success_response(data=order_data)
|
||
|
||
# 提供一个接口,传入community_id,返回订单状态数量
|
||
@router.get("/status/count", response_model=ResponseModel)
|
||
async def deliveryman_get_order_status_count(
|
||
community_id: int,
|
||
time_period_id: Optional[int] = None,
|
||
db: Session = Depends(get_db),
|
||
current_user: UserDB = Depends(get_current_user)
|
||
):
|
||
|
||
"""获取社区订单状态数量"""
|
||
status_list = [OrderStatus.CREATED, OrderStatus.RECEIVED, OrderStatus.DELIVERING, OrderStatus.UNPAID, OrderStatus.COMPLETED]
|
||
|
||
result = []
|
||
for status in status_list:
|
||
query = db.query(
|
||
ShippingOrderDB.status
|
||
).filter(
|
||
ShippingOrderDB.address_community_id == community_id,
|
||
ShippingOrderDB.status == status
|
||
)
|
||
|
||
# 如果传入了时间周期id,则过滤时间周期
|
||
if time_period_id:
|
||
query = query.filter(
|
||
ShippingOrderDB.time_period_id == time_period_id
|
||
)
|
||
|
||
# 不是待接单的订单,需要过滤配送员
|
||
if status != OrderStatus.CREATED:
|
||
query = query.filter(
|
||
ShippingOrderDB.deliveryman_user_id == current_user.userid
|
||
)
|
||
|
||
# 待接单的订单,只显示今天以及今天以前的订单
|
||
if status == OrderStatus.CREATED:
|
||
query = query.filter(
|
||
ShippingOrderDB.delivery_date <= datetime.now().date()
|
||
)
|
||
|
||
count = query.count()
|
||
result.append({
|
||
"status": status,
|
||
"count": count
|
||
})
|
||
|
||
return success_response(data=result)
|
||
|
||
|
||
# 提供一个接口,传入 community_id 返回每栋楼栋的订单数量
|
||
@router.get("/community_building/count", response_model=ResponseModel)
|
||
async def deliveryman_get_community_building_order_count(
|
||
community_id: int,
|
||
status: str,
|
||
time_period_id: Optional[int] = None,
|
||
db: Session = Depends(get_db),
|
||
current_user: UserDB = Depends(get_current_user)
|
||
):
|
||
"""获取社区每栋楼栋的订单数量"""
|
||
# 查询当前社区所有楼栋
|
||
community_buildings = db.query(
|
||
CommunityBuildingDB
|
||
).filter(
|
||
CommunityBuildingDB.community_id == community_id
|
||
).all()
|
||
|
||
if not community_buildings:
|
||
return error_response(code=404, message="社区不存在")
|
||
|
||
|
||
# 查询每个楼栋的订单数量
|
||
query = db.query(
|
||
ShippingOrderDB.address_community_building_id,
|
||
func.count(ShippingOrderDB.orderid)
|
||
).filter(
|
||
ShippingOrderDB.address_community_building_id.in_(
|
||
[building.id for building in community_buildings]
|
||
)
|
||
).filter(
|
||
ShippingOrderDB.status.in_(status.split(","))
|
||
)
|
||
|
||
# 如果传入了时间周期id,则过滤时间周期
|
||
if time_period_id:
|
||
query = query.filter(
|
||
ShippingOrderDB.time_period_id == time_period_id
|
||
)
|
||
|
||
# 如果订单状态不是待接单,则需要过滤快递员
|
||
if OrderStatus.CREATED not in status:
|
||
query = query.filter(
|
||
ShippingOrderDB.deliveryman_user_id == current_user.userid
|
||
)
|
||
|
||
# 待接单的订单,只显示今天以及今天以前的订单
|
||
if OrderStatus.CREATED in status:
|
||
query = query.filter(
|
||
ShippingOrderDB.delivery_date <= datetime.now().date()
|
||
)
|
||
|
||
building_order_count = query.group_by(
|
||
ShippingOrderDB.address_community_building_id
|
||
).all()
|
||
|
||
# 没有订单的楼栋,订单数量为0
|
||
result = []
|
||
building_order_count_dict = dict(building_order_count)
|
||
|
||
for building in community_buildings:
|
||
if building.id not in building_order_count_dict:
|
||
result.append({
|
||
"building_id": building.id,
|
||
"building_name": building.building_name,
|
||
"order_count": 0
|
||
})
|
||
else:
|
||
result.append({
|
||
"building_id": building.id,
|
||
"building_name": building.building_name,
|
||
"order_count": building_order_count_dict[building.id]
|
||
})
|
||
|
||
|
||
return success_response(data=result)
|
||
|
||
|
||
@router.get("/user/list", response_model=ResponseModel)
|
||
async def get_user_orders(
|
||
status: Optional[OrderStatus] = None,
|
||
skip: int = 0,
|
||
limit: int = 20,
|
||
db: Session = Depends(get_db),
|
||
current_user: UserDB = Depends(get_current_user)
|
||
):
|
||
"""获取用户订单列表"""
|
||
try:
|
||
# 查询订单
|
||
query = db.query(
|
||
ShippingOrderDB
|
||
).filter(
|
||
ShippingOrderDB.userid == current_user.userid
|
||
)
|
||
|
||
# 添加状态过滤
|
||
if status:
|
||
query = query.filter(ShippingOrderDB.status == status)
|
||
|
||
# 获取总数
|
||
total = query.count()
|
||
|
||
# 分页查询
|
||
results = query.order_by(
|
||
ShippingOrderDB.create_time.desc()
|
||
).offset(skip).limit(limit).all()
|
||
|
||
orders = []
|
||
for order in results:
|
||
# 查询订单包裹信息
|
||
packages = db.query(
|
||
ShippingOrderPackageDB
|
||
).filter(
|
||
ShippingOrderPackageDB.orderid == order.orderid
|
||
).all()
|
||
|
||
# 格式化包裹信息
|
||
if packages:
|
||
package_list = [{
|
||
"id": package.id,
|
||
"station_id": package.station_id,
|
||
"station_name": package.station_name,
|
||
"pickup_codes": package.pickup_codes
|
||
} for package in packages]
|
||
else:
|
||
package_list = []
|
||
|
||
#查询子订单
|
||
sub_orders = db.query(PointProductOrderDB).filter(
|
||
PointProductOrderDB.delivery_order_id == order.orderid
|
||
).all()
|
||
|
||
|
||
orders.append({
|
||
"orderid": order.orderid,
|
||
"userid": order.userid,
|
||
"status": order.status,
|
||
"pickup_images": order.optimized_pickup_images,
|
||
"package_count": order.package_count,
|
||
"pickup_code_count": order.pickup_code_count,
|
||
"pickup_images_count": order.pickup_images_count,
|
||
"create_time": order.create_time,
|
||
"delivery_method": order.delivery_method,
|
||
"original_amount": order.original_amount,
|
||
"coupon_discount_amount": order.coupon_discount_amount,
|
||
"point_discount_amount": order.point_discount_amount,
|
||
"final_amount": order.final_amount,
|
||
"is_first_order": order.is_first_order,
|
||
"packages": package_list,
|
||
"sub_orders": [PointProductOrderInfo.model_validate(sub_order) for sub_order in sub_orders],
|
||
"address": {
|
||
"name": order.address_customer_name,
|
||
"phone": order.address_customer_phone,
|
||
"gender": order.address_customer_gender,
|
||
"community_id": order.address_community_id,
|
||
"community_name": order.address_community_name,
|
||
"building_id": order.address_community_building_id,
|
||
"building_name": order.address_community_building_name,
|
||
"address_detail": order.address_detail
|
||
},
|
||
"complete_images": order.optimized_complete_images,
|
||
"received_time": order.received_time,
|
||
"pickup_time": order.pickup_time,
|
||
"completed_time": order.completed_time
|
||
})
|
||
|
||
return success_response(data={
|
||
"total": total,
|
||
"items": orders
|
||
})
|
||
|
||
except Exception as e:
|
||
return error_response(code=500, message=f"获取订单列表失败: {str(e)}")
|
||
|
||
|
||
@router.post("/{orderid}/user/cancel", response_model=ResponseModel)
|
||
async def cancel_order(
|
||
background_tasks: BackgroundTasks,
|
||
orderid: str,
|
||
db: Session = Depends(get_db),
|
||
current_user: UserDB = Depends(get_current_user)
|
||
):
|
||
"""取消订单"""
|
||
# 查询订单
|
||
order = db.query(ShippingOrderDB).filter(
|
||
ShippingOrderDB.orderid == orderid,
|
||
ShippingOrderDB.userid == current_user.userid
|
||
).first()
|
||
|
||
if not order:
|
||
return error_response(code=404, message="订单不存在")
|
||
|
||
# 检查订单状态是否可取消
|
||
if order.status not in [OrderStatus.CREATED, OrderStatus.RECEIVED]:
|
||
return error_response(code=400, message="当前订单状态不可取消")
|
||
|
||
try:
|
||
# 更新订单状态、取消原因和取消用户
|
||
order.status = OrderStatus.CANCELLED
|
||
order.cancel_reason = "用户主动取消"
|
||
order.cancel_user_id = current_user.userid # 记录取消订单的用户ID
|
||
|
||
# 如果使用了优惠券,返还优惠券
|
||
if order.coupon_id:
|
||
coupon = db.query(UserCouponDB).filter(
|
||
UserCouponDB.id == order.coupon_id
|
||
).first()
|
||
if coupon:
|
||
coupon.status = CouponStatus.UNUSED
|
||
coupon.used_time = None
|
||
|
||
# 检查子订单是否全部取消
|
||
sub_orders = db.query(PointProductOrderDB).filter(
|
||
PointProductOrderDB.delivery_order_id == order.orderid
|
||
).all()
|
||
|
||
if sub_orders:
|
||
for sub_order in sub_orders:
|
||
sub_order.status = PointProductOrderStatus.CANCELLED
|
||
#返还积分
|
||
point_manager = PointManager(db)
|
||
point_manager.add_points(
|
||
user_id=current_user.userid,
|
||
points=sub_order.order_point_amount,
|
||
description=f"兑换订单取消返还",
|
||
order_id=order.orderid
|
||
)
|
||
|
||
|
||
# 如果使用了积分,返还积分
|
||
if order.point_discount_amount > 0:
|
||
# 返还积分
|
||
return_points = int(order.point_discount_amount * settings.POINT_RATIO)
|
||
|
||
point_manager = PointManager(db)
|
||
point_manager.add_points(
|
||
user_id=current_user.userid,
|
||
points=return_points,
|
||
description=f"配送订单取消返还",
|
||
order_id=order.orderid
|
||
)
|
||
|
||
db.commit()
|
||
|
||
# 发送企业微信消息
|
||
wecom_bot = WecomBot()
|
||
background_tasks.add_task(
|
||
wecom_bot.send_order_notification,
|
||
db,
|
||
order,
|
||
OrderStatus.CANCELLED
|
||
)
|
||
|
||
# 发送模板消息
|
||
if current_user.mp_openid:
|
||
data={
|
||
"character_string1": order.orderid,
|
||
"time19": CommonUtils.get_current_time(),
|
||
"thing5": "用户主动取消订单"
|
||
}
|
||
|
||
background_tasks.add_task(
|
||
sent_order_status_change_message,
|
||
openid=current_user.mp_openid,
|
||
template_id=settings.DELIVERY_ORDER_CANCELLED_TEMPLATE_ID,
|
||
data=data,
|
||
orderid=order.orderid
|
||
)
|
||
|
||
return success_response(
|
||
message="订单取消成功",
|
||
data=OrderInfo.model_validate(order)
|
||
)
|
||
except Exception as e:
|
||
db.rollback()
|
||
return error_response(code=500, message=f"取消订单失败: {str(e)}")
|
||
|
||
|
||
@router.get("/deliveryman/list", response_model=ResponseModel)
|
||
async def deliveryman_orders(
|
||
status: Optional[str] = None,
|
||
building_id: Optional[int] = None,
|
||
community_id: Optional[int] = None,
|
||
time_period_id: Optional[int] = None,
|
||
skip: int = 0,
|
||
limit: int = 20,
|
||
db: Session = Depends(get_db),
|
||
deliveryman: UserDB = Depends(get_deliveryman_user)
|
||
):
|
||
"""获取配送员订单列表"""
|
||
# 基础查询
|
||
query = db.query(
|
||
ShippingOrderDB
|
||
)
|
||
|
||
# 状态筛选
|
||
if status:
|
||
statuses = status.split(",")
|
||
query = query.filter(ShippingOrderDB.status.in_(statuses))
|
||
|
||
# 如果订单状态不是待接单,则需要过滤快递员
|
||
if OrderStatus.CREATED not in statuses:
|
||
query = query.filter(ShippingOrderDB.deliveryman_user_id == deliveryman.userid)
|
||
|
||
# 待接单的订单,只显示今天以及今天以前的订单
|
||
if OrderStatus.CREATED in statuses:
|
||
query = query.filter(ShippingOrderDB.delivery_date <= datetime.now().date())
|
||
|
||
# 楼栋筛选
|
||
if building_id:
|
||
query = query.filter(ShippingOrderDB.address_community_building_id == building_id)
|
||
|
||
# 小区筛选
|
||
if community_id:
|
||
query = query.filter(ShippingOrderDB.address_community_id == community_id)
|
||
|
||
# 配送时段筛选
|
||
if time_period_id:
|
||
query = query.filter(ShippingOrderDB.time_period_id == time_period_id)
|
||
|
||
|
||
# 获取总数
|
||
total = query.count()
|
||
|
||
# 获取分页数据
|
||
results = query.order_by(
|
||
ShippingOrderDB.delivery_date.desc(),
|
||
ShippingOrderDB.time_period_from_time.asc()
|
||
).offset(skip).limit(limit).all()
|
||
|
||
# 格式化返回数据
|
||
orders = []
|
||
for order in results:
|
||
# 查询订单包裹信息
|
||
packages = db.query(
|
||
ShippingOrderPackageDB
|
||
).filter(
|
||
ShippingOrderPackageDB.orderid == order.orderid
|
||
).all()
|
||
|
||
# 格式化包裹信息
|
||
if packages:
|
||
package_list = [{
|
||
"id": package.id,
|
||
"station_id": package.station_id,
|
||
"station_name": package.station_name,
|
||
"pickup_codes": CommonUtils.sort_strings_by_first_number(package.pickup_codes.split(","))
|
||
} for package in packages]
|
||
else:
|
||
package_list = []
|
||
|
||
# 查询子订单
|
||
sub_orders = db.query(PointProductOrderDB).filter(
|
||
PointProductOrderDB.delivery_order_id == order.orderid
|
||
).all()
|
||
|
||
# 获取加价请求
|
||
requests = db.query(OrderAdditionalFeeDB,
|
||
UserDB.nickname.label("deliveryman_name"),
|
||
UserDB.phone.label("deliveryman_phone"),
|
||
UserDB.avatar.label("deliveryman_avatar")).join(UserDB,
|
||
OrderAdditionalFeeDB.deliveryman_id == UserDB.userid).filter(
|
||
OrderAdditionalFeeDB.orderid == order.orderid
|
||
).all()
|
||
|
||
order_additional_fees = []
|
||
if requests:
|
||
for fee in requests:
|
||
order_additional_fees.append({
|
||
"id": fee.OrderAdditionalFeeDB.id,
|
||
"orderid": fee.OrderAdditionalFeeDB.orderid,
|
||
"order_user_id": fee.OrderAdditionalFeeDB.order_user_id,
|
||
"deliveryman_id": fee.OrderAdditionalFeeDB.deliveryman_id,
|
||
"deliveryman_name": fee.deliveryman_name,
|
||
"deliveryman_phone": fee.deliveryman_phone,
|
||
"deliveryman_avatar": fee.deliveryman_avatar,
|
||
"reason": fee.OrderAdditionalFeeDB.reason,
|
||
"photo_urls": fee.OrderAdditionalFeeDB.photo_urls,
|
||
"additional_fee_amount": fee.OrderAdditionalFeeDB.additional_fee_amount,
|
||
"result": fee.OrderAdditionalFeeDB.result,
|
||
})
|
||
|
||
delivery_time = format_delivery_time(order.delivery_date, order.time_period_name)
|
||
|
||
orders.append({
|
||
"orderid": order.orderid,
|
||
"userid": order.userid,
|
||
"status": order.status,
|
||
"pickup_images": order.optimized_pickup_images,
|
||
"package_count": order.package_count,
|
||
"pickup_code_count": order.pickup_code_count,
|
||
"pickup_images_count": order.pickup_images_count,
|
||
"create_time": order.create_time,
|
||
"delivery_method": order.delivery_method,
|
||
"delivery_time": delivery_time,
|
||
"original_amount": order.original_amount,
|
||
"coupon_discount_amount": order.coupon_discount_amount,
|
||
"point_discount_amount": order.point_discount_amount,
|
||
"final_amount": order.final_amount,
|
||
"is_first_order": order.is_first_order,
|
||
"packages": package_list,
|
||
"order_additional_fees": order_additional_fees,
|
||
"sub_orders": [PointProductOrderInfo.model_validate(sub_order) for sub_order in sub_orders],
|
||
"address": {
|
||
"name": order.address_customer_name,
|
||
"phone": order.address_customer_phone,
|
||
"gender": order.address_customer_gender,
|
||
"community_id": order.address_community_id,
|
||
"community_name": order.address_community_name,
|
||
"building_id": order.address_community_building_id,
|
||
"building_name": order.address_community_building_name,
|
||
"address_detail": order.address_detail
|
||
},
|
||
|
||
"complete_images": order.optimized_complete_images,
|
||
"received_time": order.received_time,
|
||
"pickup_time": order.pickup_time,
|
||
"completed_time": order.completed_time,
|
||
"time_period_id": order.time_period_id,
|
||
"time_period_name": order.time_period_name,
|
||
"time_period_from_time": order.time_period_from_time,
|
||
"time_period_to_time": order.time_period_to_time
|
||
})
|
||
|
||
return success_response(data={
|
||
"total": total,
|
||
"items": orders
|
||
})
|
||
|
||
@router.post("/{orderid}/deliveryman/cancel", response_model=ResponseModel)
|
||
async def deliveryman_cancel_order(
|
||
background_tasks: BackgroundTasks,
|
||
orderid: str,
|
||
cancel_data: OrderCancel,
|
||
db: Session = Depends(get_db),
|
||
deliveryman: UserDB = Depends(get_deliveryman_user)
|
||
):
|
||
"""配送员取消订单"""
|
||
order = db.query(ShippingOrderDB).filter(
|
||
ShippingOrderDB.orderid == orderid
|
||
).first()
|
||
|
||
if not order:
|
||
return error_response(code=404, message="订单不存在")
|
||
|
||
# 检查订单状态
|
||
if order.status != OrderStatus.CREATED and order.status != OrderStatus.RECEIVED:
|
||
return error_response(code=400, message="只有未接单或者已接单的订单才能取消")
|
||
|
||
try:
|
||
# 更新订单状态、取消原因和取消用户
|
||
order.status = OrderStatus.CANCELLED
|
||
order.cancel_reason = cancel_data.reason
|
||
order.cancel_user_id = deliveryman.userid
|
||
|
||
# 如果使用了优惠券,返还优惠券
|
||
if order.coupon_id:
|
||
coupon = db.query(UserCouponDB).filter(
|
||
UserCouponDB.id == order.coupon_id
|
||
).first()
|
||
if coupon:
|
||
coupon.status = CouponStatus.UNUSED
|
||
coupon.used_time = None
|
||
|
||
# 检查子订单是否全部取消
|
||
sub_orders = db.query(PointProductOrderDB).filter(
|
||
PointProductOrderDB.delivery_order_id == order.orderid,
|
||
PointProductOrderDB.status != PointProductOrderStatus.CANCELLED
|
||
).all()
|
||
|
||
if sub_orders:
|
||
for sub_order in sub_orders:
|
||
sub_order.status = PointProductOrderStatus.CANCELLED
|
||
#返还积分
|
||
point_manager = PointManager(db)
|
||
point_manager.add_points(
|
||
user_id=order.userid,
|
||
points=sub_order.order_point_amount,
|
||
description=f"兑换订单取消返还",
|
||
order_id=order.orderid
|
||
)
|
||
|
||
# 如果使用了积分,返还积分
|
||
if order.point_discount_amount > 0:
|
||
# 返还积分
|
||
return_points = int(order.point_discount_amount * settings.POINT_RATIO)
|
||
|
||
point_manager = PointManager(db)
|
||
point_manager.add_points(
|
||
user_id=order.userid,
|
||
points=return_points,
|
||
description=f"配送订单取消返还",
|
||
order_id=order.orderid
|
||
)
|
||
|
||
db.commit()
|
||
|
||
# 发送企业微信消息
|
||
wecom_bot = WecomBot()
|
||
|
||
background_tasks.add_task(
|
||
wecom_bot.send_order_notification,
|
||
db,
|
||
order,
|
||
OrderStatus.CANCELLED
|
||
)
|
||
|
||
# 发送模板消息
|
||
if order.userid:
|
||
order_user = db.query(UserDB).filter(
|
||
UserDB.userid == order.userid
|
||
).first()
|
||
|
||
if order_user.mp_openid:
|
||
data={
|
||
"character_string1": order.orderid,
|
||
"time19": CommonUtils.get_current_time(),
|
||
"thing5": order.cancel_reason
|
||
}
|
||
|
||
background_tasks.add_task(
|
||
sent_order_status_change_message,
|
||
openid=order_user.mp_openid,
|
||
template_id=settings.DELIVERY_ORDER_CANCELLED_TEMPLATE_ID,
|
||
data=data,
|
||
orderid=order.orderid
|
||
)
|
||
|
||
return success_response(
|
||
message="订单取消成功",
|
||
data=OrderInfo.model_validate(order)
|
||
)
|
||
except Exception as e:
|
||
db.rollback()
|
||
return error_response(code=500, message=f"取消订单失败: {str(e)}")
|
||
|
||
@router.post("/{orderid}/deliveryman/complete", response_model=ResponseModel)
|
||
async def deliveryman_complete_order(
|
||
background_tasks: BackgroundTasks,
|
||
orderid: str,
|
||
complete_data: OrderComplete,
|
||
db: Session = Depends(get_db),
|
||
current_user: UserDB = Depends(get_deliveryman_user)
|
||
):
|
||
"""完成订单"""
|
||
# 查询订单
|
||
order = db.query(ShippingOrderDB).filter(
|
||
ShippingOrderDB.orderid == orderid,
|
||
ShippingOrderDB.deliveryman_user_id == current_user.userid
|
||
).first()
|
||
|
||
# 订单用户
|
||
order_user = db.query(UserDB).filter(
|
||
UserDB.userid == order.userid
|
||
).first()
|
||
|
||
if not order:
|
||
return error_response(code=404, message="订单不存在")
|
||
|
||
# 检查订单状态
|
||
if order.status != OrderStatus.DELIVERING:
|
||
return error_response(code=400, message="只有配送中的订单才能标记为完成")
|
||
|
||
try:
|
||
# 根据订单金额决定状态
|
||
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)
|
||
|
||
# 更新完成时间
|
||
order.completed_time = datetime.now()
|
||
|
||
# 使用账户管理器处理分账
|
||
if order.delivery_share > 0:
|
||
account_manager = AccountManager(db)
|
||
account_manager.change_balance(
|
||
user_id=order.deliveryman_user_id,
|
||
amount= order.delivery_share,
|
||
description=f"配送订单{order.orderid[-4:]}收益",
|
||
transaction_id=orderid
|
||
)
|
||
|
||
db.commit()
|
||
|
||
# 如果当前订单是首单,如果有邀请人,给邀请人发放优惠券
|
||
if order.is_first_order and order_user.referral_code:
|
||
# 查询邀请人
|
||
invite_user = db.query(UserDB).filter(
|
||
UserDB.user_code == order_user.referral_code
|
||
).first()
|
||
|
||
if invite_user:
|
||
expire_time = datetime.now() + timedelta(days=settings.FIRST_ORDER_REFERRAL_COUPON_EXPIRE_DAYS)
|
||
manager = CouponManager(db)
|
||
manager.add_coupon(
|
||
user_id=invite_user.userid,
|
||
coupon_id=settings.FIRST_ORDER_REFERRAL_COUPON_ID,
|
||
expire_time=expire_time,
|
||
count=settings.FIRST_ORDER_REFERRAL_COUPON_COUNT
|
||
)
|
||
|
||
# 发送企业微信消息
|
||
wecom_bot = WecomBot()
|
||
background_tasks.add_task(
|
||
wecom_bot.send_order_notification,
|
||
db,
|
||
order,
|
||
OrderStatus.COMPLETED
|
||
)
|
||
|
||
# 发送模板消息
|
||
if order.userid:
|
||
order_user = db.query(UserDB).filter(
|
||
UserDB.userid == order.userid
|
||
).first()
|
||
if order_user.mp_openid:
|
||
deliveryman_user = db.query(UserDB).filter(
|
||
UserDB.userid == order.deliveryman_user_id
|
||
).first()
|
||
|
||
data={
|
||
"character_string13": order.orderid,
|
||
"thing3": deliveryman_user.nickname,
|
||
"time5" : CommonUtils.get_current_time()
|
||
}
|
||
|
||
background_tasks.add_task(
|
||
sent_order_status_change_message,
|
||
openid=order_user.mp_openid,
|
||
template_id=settings.DELIVERY_ORDER_COMPLETED_TEMPLATE_ID,
|
||
data=data,
|
||
orderid=order.orderid
|
||
)
|
||
|
||
return success_response(
|
||
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)}")
|
||
|
||
@router.post("/{orderid}/deliveryman/receive", response_model=ResponseModel)
|
||
async def deliveryman_receive_order(
|
||
background_tasks: BackgroundTasks,
|
||
orderid: str,
|
||
db: Session = Depends(get_db),
|
||
deliveryman: UserDB = Depends(get_deliveryman_user)
|
||
):
|
||
"""接单(仅配送员可用)"""
|
||
|
||
if not deliveryman.is_auth:
|
||
return error_response(code=400, message="请先完成实名认证")
|
||
|
||
# 查询订单
|
||
order = db.query(ShippingOrderDB).filter(
|
||
ShippingOrderDB.orderid == orderid
|
||
).first()
|
||
|
||
if not order:
|
||
return error_response(code=404, message="订单不存在")
|
||
|
||
# 检查订单状态
|
||
if order.status != OrderStatus.CREATED:
|
||
return error_response(code=400, message="只能接待新创建的订单")
|
||
|
||
# 检查订单是否已被接单
|
||
if order.deliveryman_user_id is not None:
|
||
return error_response(code=400, message="订单已被其他配送员接单")
|
||
|
||
# 检查子订单是否全部处理
|
||
sub_orders = db.query(PointProductOrderDB).filter(
|
||
PointProductOrderDB.delivery_order_id == order.orderid
|
||
).all()
|
||
|
||
try:
|
||
|
||
# 更新订单状态和配送员ID
|
||
order.status = OrderStatus.RECEIVED
|
||
order.deliveryman_user_id = deliveryman.userid
|
||
order.received_time = datetime.now()
|
||
|
||
# 接单就确认收益
|
||
order.delivery_share = calculate_delivery_share(order, db)
|
||
|
||
db.commit()
|
||
|
||
# 发送企业微信消息
|
||
wecom_bot = WecomBot()
|
||
|
||
background_tasks.add_task(
|
||
wecom_bot.send_order_notification,
|
||
db,
|
||
order,
|
||
OrderStatus.RECEIVED
|
||
)
|
||
|
||
# 发送模板消息
|
||
if order.userid:
|
||
order_user = db.query(UserDB).filter(
|
||
UserDB.userid == order.userid
|
||
).first()
|
||
if order_user.mp_openid:
|
||
deliveryman_user = db.query(UserDB).filter(
|
||
UserDB.userid == order.deliveryman_user_id
|
||
).first()
|
||
|
||
data={
|
||
"character_string9": order.orderid,
|
||
"time8": CommonUtils.get_current_time(),
|
||
"thing3": deliveryman_user.nickname
|
||
}
|
||
|
||
background_tasks.add_task(
|
||
sent_order_status_change_message,
|
||
openid=order_user.mp_openid,
|
||
template_id=settings.DELIVERY_ORDER_RECEIVED_TEMPLATE_ID,
|
||
data=data,
|
||
orderid=order.orderid
|
||
)
|
||
|
||
return success_response(
|
||
message="接单成功",
|
||
data=OrderInfo.model_validate(order)
|
||
)
|
||
except Exception as e:
|
||
db.rollback()
|
||
return error_response(code=500, message=f"接单失败: {str(e)}")
|
||
|
||
@router.post("/{orderid}/deliveryman/pickup", response_model=ResponseModel)
|
||
async def deliveryman_pickup_order(
|
||
orderid: str,
|
||
db: Session = Depends(get_db),
|
||
deliveryman: UserDB = Depends(get_deliveryman_user)
|
||
):
|
||
"""标记订单为已取货(仅配送员可用)"""
|
||
# 查询订单
|
||
order = db.query(ShippingOrderDB).filter(
|
||
ShippingOrderDB.orderid == orderid,
|
||
ShippingOrderDB.deliveryman_user_id == deliveryman.userid # 必须是该配送员的订单
|
||
).first()
|
||
|
||
if not order:
|
||
return error_response(code=404, message="订单不存在")
|
||
|
||
# 检查订单状态
|
||
if order.status != OrderStatus.RECEIVED:
|
||
return error_response(code=400, message="只有已接单的订单才能标记为已取货")
|
||
|
||
# 检查子订单是否全部处理
|
||
sub_orders = db.query(PointProductOrderDB).filter(
|
||
PointProductOrderDB.delivery_order_id == order.orderid
|
||
).all()
|
||
|
||
for sub_order in sub_orders:
|
||
if sub_order.status != PointProductOrderStatus.PENDING:
|
||
return error_response(code=400, message="请先处理兑换商品子订单")
|
||
|
||
try:
|
||
# 更新订单状态为配送中
|
||
order.status = OrderStatus.DELIVERING
|
||
order.pickup_time = datetime.now()
|
||
|
||
db.commit()
|
||
|
||
return success_response(
|
||
message="已标记为取货",
|
||
data=OrderInfo.model_validate(order)
|
||
)
|
||
except Exception as e:
|
||
db.rollback()
|
||
return error_response(code=500, message=f"操作失败: {str(e)}")
|
||
|
||
@router.get("/platform/list", response_model=ResponseModel)
|
||
async def get_orders(
|
||
db: Session = Depends(get_db),
|
||
status: Optional[OrderStatus] = None,
|
||
user_id: Optional[int] = None,
|
||
order_id: Optional[str] = None,
|
||
skip: int = 0,
|
||
limit: int = 10):
|
||
"""获取订单列表"""
|
||
try:
|
||
# 构建基础查询
|
||
query = db.query(ShippingOrderDB)
|
||
|
||
# 添加用户ID过滤
|
||
if user_id:
|
||
query = query.filter(ShippingOrderDB.userid == user_id)
|
||
|
||
# 添加状态过滤
|
||
if status:
|
||
query = query.filter(ShippingOrderDB.status == status)
|
||
|
||
# 添加订单号过滤
|
||
if order_id:
|
||
query = query.filter(ShippingOrderDB.orderid == order_id)
|
||
|
||
# 获取总数
|
||
total = query.count()
|
||
|
||
# 分页查询
|
||
results = query.order_by(
|
||
ShippingOrderDB.create_time.desc()
|
||
).offset(skip).limit(limit).all()
|
||
|
||
orders = []
|
||
for order in results:
|
||
# 查询订单包裹信息
|
||
packages = db.query(
|
||
ShippingOrderPackageDB
|
||
).filter(
|
||
ShippingOrderPackageDB.orderid == order.orderid
|
||
).all()
|
||
|
||
# 格式化包裹信息
|
||
package_list = [{
|
||
"id": package.id,
|
||
"station_id": package.station_id,
|
||
"station_name": package.station_name,
|
||
"pickup_codes": package.pickup_codes
|
||
} for package in packages]
|
||
|
||
|
||
item = {
|
||
"orderid": order.orderid,
|
||
"userid": order.userid,
|
||
"status": order.status,
|
||
"pickup_images": order.optimized_pickup_images,
|
||
"package_count": order.package_count,
|
||
"pickup_code_count": order.pickup_code_count,
|
||
"pickup_images_count": order.pickup_images_count,
|
||
"create_time": order.create_time,
|
||
"delivery_method": order.delivery_method,
|
||
"original_amount": order.original_amount,
|
||
"coupon_discount_amount": order.coupon_discount_amount,
|
||
"point_discount_amount": order.point_discount_amount,
|
||
"cancel_reason": order.cancel_reason,
|
||
"is_delivery_cancel": order.cancel_user_id == order.deliveryman_user_id,
|
||
"complete_images": order.optimized_complete_images,
|
||
"completed_time": order.completed_time,
|
||
"final_amount": order.final_amount,
|
||
"packages": package_list,
|
||
"address": {
|
||
"name": order.address_customer_name,
|
||
"phone": order.address_customer_phone,
|
||
"gender": order.address_customer_gender,
|
||
"community_id": order.address_community_id,
|
||
"community_name": order.address_community_name,
|
||
"building_id": order.address_community_building_id,
|
||
"building_name": order.address_community_building_name,
|
||
"address_detail": order.address_detail
|
||
},
|
||
}
|
||
|
||
# 查询配送员
|
||
deliveryman = db.query(UserDB).filter(
|
||
UserDB.userid == order.deliveryman_user_id
|
||
).first()
|
||
|
||
if deliveryman:
|
||
item["deliveryman"] = {
|
||
"name": deliveryman.nickname,
|
||
"phone": deliveryman.phone
|
||
}
|
||
|
||
orders.append(item)
|
||
|
||
return success_response(data={
|
||
"total": total,
|
||
"items": orders
|
||
})
|
||
|
||
except Exception as e:
|
||
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(
|
||
status: Optional[OrderStatus] = None,
|
||
user_id: Optional[int] = None,
|
||
order_id: Optional[str] = None,
|
||
skip: int = 0,
|
||
limit: int = 20,
|
||
db: Session = Depends(get_db),
|
||
admin_user: UserDB = Depends(get_admin_user)
|
||
):
|
||
"""获取订单列表(管理员接口)
|
||
|
||
Args:
|
||
status: 订单状态过滤
|
||
user_id: 用户ID过滤
|
||
order_id: 订单号过滤
|
||
skip: 跳过记录数
|
||
limit: 返回记录数
|
||
"""
|
||
try:
|
||
# 构建基础查询
|
||
query = db.query(ShippingOrderDB)
|
||
|
||
# 添加用户ID过滤
|
||
if user_id:
|
||
query = query.filter(ShippingOrderDB.userid == user_id)
|
||
|
||
# 添加状态过滤
|
||
if status:
|
||
query = query.filter(ShippingOrderDB.status == status)
|
||
|
||
# 添加订单号过滤
|
||
if order_id:
|
||
query = query.filter(ShippingOrderDB.orderid == order_id)
|
||
|
||
# 获取总数
|
||
total = query.count()
|
||
|
||
# 分页查询
|
||
results = query.order_by(
|
||
ShippingOrderDB.create_time.desc()
|
||
).offset(skip).limit(limit).all()
|
||
|
||
orders = []
|
||
for order in results:
|
||
# 查询订单包裹信息
|
||
packages = db.query(
|
||
ShippingOrderPackageDB
|
||
).filter(
|
||
ShippingOrderPackageDB.orderid == order.orderid
|
||
).all()
|
||
|
||
# 格式化包裹信息
|
||
package_list = [{
|
||
"id": package.id,
|
||
"station_id": package.station_id,
|
||
"station_name": package.station_name,
|
||
"pickup_codes": package.pickup_codes
|
||
} for package in packages]
|
||
|
||
# 查询子订单
|
||
sub_orders = db.query(PointProductOrderDB).filter(
|
||
PointProductOrderDB.delivery_order_id == order.orderid
|
||
).all()
|
||
|
||
item = {
|
||
"orderid": order.orderid,
|
||
"userid": order.userid,
|
||
"status": order.status,
|
||
"pickup_images": order.optimized_pickup_images,
|
||
"package_count": order.package_count,
|
||
"pickup_code_count": order.pickup_code_count,
|
||
"pickup_images_count": order.pickup_images_count,
|
||
"create_time": order.create_time,
|
||
"delivery_method": order.delivery_method,
|
||
"original_amount": order.original_amount,
|
||
"coupon_discount_amount": order.coupon_discount_amount,
|
||
"point_discount_amount": order.point_discount_amount,
|
||
"complete_images": order.optimized_complete_images,
|
||
"completed_time": order.completed_time,
|
||
"final_amount": order.final_amount,
|
||
"packages": package_list,
|
||
"sub_orders": [PointProductOrderInfo.model_validate(sub_order) for sub_order in sub_orders],
|
||
"address": {
|
||
"name": order.address_customer_name,
|
||
"phone": order.address_customer_phone,
|
||
"gender": order.address_customer_gender,
|
||
"community_id": order.address_community_id,
|
||
"community_name": order.address_community_name,
|
||
"building_id": order.address_community_building_id,
|
||
"building_name": order.address_community_building_name,
|
||
"address_detail": order.address_detail
|
||
},
|
||
}
|
||
|
||
# 查询配送员
|
||
deliveryman = db.query(UserDB).filter(
|
||
UserDB.userid == order.deliveryman_user_id
|
||
).first()
|
||
|
||
if deliveryman:
|
||
item["deliveryman"] = {
|
||
"name": deliveryman.nickname,
|
||
"phone": deliveryman.phone
|
||
}
|
||
|
||
orders.append(item)
|
||
|
||
return success_response(data={
|
||
"total": total,
|
||
"items": orders
|
||
})
|
||
|
||
except Exception as e:
|
||
logging.exception(f"获取订单列表失败: {str(e)}")
|
||
return error_response(code=500, message=f"获取订单列表失败: {str(e)}")
|
||
|
||
|
||
# 获取配送员订单数量汇总
|
||
@router.get("/deliveryman/summary", response_model=ResponseModel)
|
||
async def deliveryman_order_summary(
|
||
db: Session = Depends(get_db),
|
||
deliveryman: UserDB = Depends(get_deliveryman_user)
|
||
):
|
||
"""获取配送员订单数量汇总"""
|
||
|
||
# 查询配送员总订单数量
|
||
total = db.query(ShippingOrderDB).filter(
|
||
ShippingOrderDB.deliveryman_user_id == deliveryman.userid,
|
||
ShippingOrderDB.status == OrderStatus.COMPLETED
|
||
).count()
|
||
|
||
today = datetime.now().date()
|
||
yesterday = today - timedelta(days=1)
|
||
today_start = datetime.combine(today, datetime.min.time())
|
||
today_end = datetime.combine(today, datetime.max.time())
|
||
yesterday_start = datetime.combine(yesterday, datetime.min.time())
|
||
yesterday_end = datetime.combine(yesterday, datetime.max.time())
|
||
|
||
# 查询配送员昨日、今日订单数量
|
||
yesterday_total = db.query(ShippingOrderDB).filter(
|
||
ShippingOrderDB.deliveryman_user_id == deliveryman.userid,
|
||
ShippingOrderDB.status == OrderStatus.COMPLETED,
|
||
ShippingOrderDB.completed_time.between(yesterday_start, yesterday_end)
|
||
).count()
|
||
|
||
today_total = db.query(ShippingOrderDB).filter(
|
||
ShippingOrderDB.deliveryman_user_id == deliveryman.userid,
|
||
ShippingOrderDB.status == OrderStatus.COMPLETED,
|
||
ShippingOrderDB.completed_time.between(today_start, today_end)
|
||
).count()
|
||
|
||
|
||
return success_response(data={
|
||
"total_count": total,
|
||
"yesterday_count": yesterday_total,
|
||
"today_count": today_total
|
||
})
|
||
|
||
|
||
@router.get("/deliveryman/check_new_order", response_model=ResponseModel)
|
||
async def deliveryman_check_new_orders(
|
||
db: Session = Depends(get_db),
|
||
deliveryman: UserDB = Depends(get_deliveryman_user)
|
||
):
|
||
"""检查新订单"""
|
||
|
||
# 从Redis获取新订单ID列表
|
||
order_ids = redis_client.pop_new_orders_from_queue(deliveryman.userid)
|
||
|
||
if not order_ids:
|
||
return success_response(data={
|
||
"has_new_order": False,
|
||
"order_ids": []
|
||
})
|
||
else:
|
||
return success_response(data={
|
||
"has_new_order": len(order_ids) > 0,
|
||
"order_ids": order_ids
|
||
})
|