Files
kami_apple_exchage/backend/app/services/order_business_service.py
danial 5c486e34d3 docs(项目): 添加项目文档并进行代码调整
- 新增 CODEBUDDY.md、GEMINI.md、GEMINI_CN.md 等项目文档
- 更新 Dockerfile 和其他配置文件
- 优化部分代码结构,如 orders.py、tasks.py 等
- 新增 .dockerignore 文件
2025-09-12 19:38:24 +08:00

232 lines
7.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
订单业务逻辑服务层
"""
from datetime import datetime
from typing import Any, Optional
from sqlalchemy.ext.asyncio import AsyncSession
import pandas as pd
import tempfile
import os
from app.core.log import get_logger
from app.models.orders import Orders, OrderResultStatus
from app.repositories.order_repository import OrderRepository
from app.repositories.link_repository import LinkRepository
from app.schemas.order import (
OrderStatsResponse,
)
logger = get_logger(__name__)
class OrderService:
"""订单业务服务"""
def __init__(self, db: AsyncSession):
self.db = db
self.order_repo = OrderRepository(db)
self.link_repo = LinkRepository(db)
async def get_order_statistics(self) -> OrderStatsResponse:
"""获取订单统计信息"""
stats = await self.order_repo.get_order_statistics()
total = sum(stats.values())
return OrderStatsResponse(
total=total,
pending=stats.get("pending", 0),
processing=stats.get("processing", 0),
success=stats.get("success", 0),
failed=stats.get("failure", 0),
last_update=datetime.now().isoformat(),
)
async def get_order_list(
self,
skip: int = 0,
limit: int = 100,
status_filter: OrderResultStatus | None = None,
) -> list[Orders]:
"""获取订单列表(包含所有关联数据)"""
return await self.order_repo.get_orders_with_relations(
skip, limit, status_filter
)
async def get_order_detail(self, order_id: str) -> Orders | None:
"""获取订单详情(包含所有关联数据)"""
return await self.order_repo.get_order_with_full_details(order_id)
async def export_orders_to_excel(
self, status_filter: str | None = None
) -> tuple[bytes, str]:
"""导出订单数据为Excel文件"""
# 转换状态过滤器
status = None
if status_filter:
try:
status = OrderResultStatus(status_filter)
except ValueError:
pass
# 获取所有订单用于导出
orders = await self.order_repo.get_orders_with_relations(0, 10000, status)
if not orders:
raise ValueError("没有找到订单数据")
# 准备导出数据
export_data = []
for order in orders:
# 处理礼品卡信息(如果存在)
card_codes = []
if hasattr(order, "gift_cards") and order.gift_cards:
card_codes = [
gc.card_code
for gc in order.gift_cards
if hasattr(gc, "card_code") and gc.card_code
]
# 处理用户信息(如果存在)
user_name = ""
user_email = ""
user_phone = ""
user_address = ""
if hasattr(order, "user_data") and order.user_data:
user_name = getattr(order.user_data, "full_name", "")
user_email = getattr(order.user_data, "email", "")
user_phone = getattr(order.user_data, "phone", "")
# 构建地址
street = getattr(order.user_data, "street_address", "")
city = getattr(order.user_data, "city", "")
state = getattr(order.user_data, "state", "")
zip_code = getattr(order.user_data, "zip_code", "")
user_address = f"{street}, {city}, {state} {zip_code}".strip(", ")
export_data.append(
{
"订单ID": order.id,
"订单号": getattr(order, "order_number", "") or "",
"状态": order.status.value,
"礼品卡号": ", ".join(card_codes),
"用户姓名": user_name,
"邮箱": user_email,
"电话": user_phone,
"地址": user_address,
"失败原因": getattr(order, "failure_reason", "") or "",
"最终订单URL": getattr(order, "final_order_url", "") or "",
"创建时间": order.created_at.strftime("%Y-%m-%d %H:%M:%S"),
}
)
# 创建Excel文件
df = pd.DataFrame(export_data)
with tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") as tmp_file:
excel_path = tmp_file.name
with pd.ExcelWriter(excel_path, engine="openpyxl") as writer:
df.to_excel(writer, sheet_name="订单数据", index=False)
# 读取文件内容
with open(excel_path, "rb") as f:
excel_content = f.read()
# 删除临时文件
os.unlink(excel_path)
# 生成文件名
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"订单数据_{timestamp}.xlsx"
return excel_content, filename
# 订单处理相关方法
async def get_pending_orders(self) -> list[Orders]:
"""获取pending状态的订单"""
from sqlalchemy import select
query = select(Orders).where(Orders.status == OrderResultStatus.PENDING)
result = await self.db.execute(query)
return list(result.scalars().all())
async def associate_link_to_order(self, order: Orders, link_id: str) -> bool:
"""将链接关联到订单"""
try:
order.links_id = link_id
await self.db.commit()
logger.info(f"成功关联链接 {link_id} 到订单 {order.id}")
return True
except Exception as e:
await self.db.rollback()
logger.error(f"关联链接到订单失败: {e}")
return False
async def update_order_status(
self, order_id: str, status: OrderResultStatus
) -> bool:
"""更新订单状态"""
try:
await self.order_repo.update_by_id(
order_id,
status=status,
updated_at=datetime.now(),
)
logger.info(f"订单状态更新成功: {order_id} -> {status.value}")
return True
except Exception as e:
logger.error(f"更新订单状态失败: {order_id}, error: {e}")
return False
async def create_order(self, user_data_id: str, links_id: str) -> str:
"""创建新订单"""
try:
order = await self.order_repo.create(
user_data_id=user_data_id,
links_id=links_id,
status=OrderResultStatus.PENDING,
)
logger.info(f"成功创建订单: {order.id}, 用户数据ID: {user_data_id}")
return order.id
except Exception as e:
logger.error(f"创建订单失败: {e}")
raise
async def process_single_order(self, order_id: str) -> bool:
"""处理单个订单(关联链接并更新状态)"""
try:
# 获取订单
order = await self.order_repo.get_by_id(order_id)
if not order:
logger.error(f"订单未找到: {order_id}")
return False
# 检查订单状态
if order.status != OrderResultStatus.PENDING:
logger.warning(
f"订单状态非pending跳过处理: {order_id}, status: {order.status.value}"
)
return False
if order.links_id:
return False
best_link = await self.get_best_link_for_order()
if not best_link:
logger.warning(f"没有可用链接,无法处理订单: {order_id}")
return False
# 关联链接
if not await self.associate_link_to_order(order, best_link.id):
logger.error(f"关联链接失败,无法处理订单: {order_id}")
return False
except Exception as e:
logger.error(f"处理订单失败: {order_id}, error: {e}")
return True