mirror of
https://git.oceanpay.cc/danial/kami_apple_exchage.git
synced 2025-12-18 22:29:09 +00:00
- 新增 CODEBUDDY.md、GEMINI.md、GEMINI_CN.md 等项目文档 - 更新 Dockerfile 和其他配置文件 - 优化部分代码结构,如 orders.py、tasks.py 等 - 新增 .dockerignore 文件
232 lines
7.9 KiB
Python
232 lines
7.9 KiB
Python
"""
|
||
订单业务逻辑服务层
|
||
"""
|
||
|
||
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
|