diff --git a/app/api/endpoints/order.py b/app/api/endpoints/order.py index e80f658..95dca7b 100644 --- a/app/api/endpoints/order.py +++ b/app/api/endpoints/order.py @@ -38,6 +38,8 @@ 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 + router = APIRouter() def calculate_price(price_request: OrderPriceCalculateRequest,user: UserDB,db: Session) -> OrderPriceResult: @@ -271,9 +273,10 @@ async def create_order( ).all() # 发送企业微信消息 - wecom_bot = WecomBot(settings.WECOM_WEBHOOK_URL) - wecom_bot.send_order_notification(db_order, OrderStatus.CREATED) + wecom_bot = WecomBot() + await wecom_bot.send_order_notification(db, db_order, OrderStatus.CREATED) + #发送订单创建成功的消息 if current_user.mp_openid: await mp_client.send_template_message( @@ -679,6 +682,10 @@ async def cancel_order( db.commit() + # 发送企业微信消息 + wecom_bot = WecomBot() + await wecom_bot.send_order_notification(db, order, OrderStatus.CANCELLED) + # 发送模板消息 if current_user.mp_openid: await mp_client.send_template_message( @@ -876,6 +883,10 @@ async def deliveryman_cancel_order( db.commit() + # 发送企业微信消息 + wecom_bot = WecomBot() + await wecom_bot.send_order_notification(db, order, OrderStatus.CANCELLED) + # 发送模板消息 if order.userid: order_user = db.query(UserDB).filter( @@ -952,6 +963,10 @@ async def complete_order( db.commit() + # 发送企业微信消息 + wecom_bot = WecomBot() + await wecom_bot.send_order_notification(db, order, OrderStatus.COMPLETED) + # 发送模板消息 if order.userid: order_user = db.query(UserDB).filter( @@ -1020,6 +1035,10 @@ async def receive_order( db.commit() + # 发送企业微信消息 + wecom_bot = WecomBot() + await wecom_bot.send_order_notification(db, order, OrderStatus.RECEIVED) + # 发送模板消息 if order.userid: order_user = db.query(UserDB).filter( diff --git a/app/api/endpoints/wechat.py b/app/api/endpoints/wechat.py index 23eebf5..c5fb83b 100644 --- a/app/api/endpoints/wechat.py +++ b/app/api/endpoints/wechat.py @@ -228,28 +228,6 @@ async def payment_notify( order.completed_time = datetime.now() order.transaction_id = transaction_id order.pay_time = datetime.fromisoformat(success_time.replace('Z', '+00:00')) - - # 计算配送员分账金额 - # if order.deliveryman_user_id: - # deliveryman_user = db.query(UserDB).filter( - # UserDB.userid == order.deliveryman_user_id - # ).first() - # if deliveryman_user: - # deliveryman_share = order.final_amount * deliveryman_user.delivery_commission_rate / 100 - # else: - # deliveryman_share = 0 - # else: - # deliveryman_share = 0 - - # # 使用账户管理器处理分账 - # if deliveryman_share > 0: - # account_manager = AccountManager(db) - # account_manager.change_balance( - # user_id=order.deliveryman_user_id, - # amount=deliveryman_share, - # description=f"配送订单结算", - # transaction_id=order.orderid - # ) elif out_trade_no.startswith("M"): # 商家商品订单 diff --git a/app/core/config.py b/app/core/config.py index 4f54af6..2770ce2 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -1,7 +1,7 @@ from typing import Optional from pydantic_settings import BaseSettings -class Settings(BaseSettings): +class Settings(BaseSettings): DEBUG: bool = True # 开发模式标志 API_V1_STR: str = "/api/v1" PROJECT_NAME: str = "FastAPI 项目" diff --git a/app/core/wecombot.py b/app/core/wecombot.py index 0e84dc3..6521c9e 100644 --- a/app/core/wecombot.py +++ b/app/core/wecombot.py @@ -5,11 +5,16 @@ from datetime import datetime from app.core.utils import CommonUtils from app.models.order import OrderStatus from app.models.order import ShippingOrderDB +import aiohttp +from app.models.community import CommunityDB +from app.models.user import UserDB +from sqlalchemy.orm import Session +import logging class WecomBot: """企业微信机器人工具类""" - def __init__(self, webhook_url: str): + def __init__(self, webhook_url: str = None): """ 初始化机器人 @@ -18,7 +23,7 @@ class WecomBot: """ self.webhook_url = webhook_url - def send_text(self, content: str, mentioned_list: List[str] = None, mentioned_mobile_list: List[str] = None) -> bool: + async def send_text(self, content: str, mentioned_list: List[str] = None, mentioned_mobile_list: List[str] = None, webhook_url: str = None) -> bool: """ 发送文本消息 @@ -42,9 +47,9 @@ class WecomBot: if mentioned_mobile_list: data["text"]["mentioned_mobile_list"] = mentioned_mobile_list - return self._send(data) + return await self._send(data, webhook_url) - def send_markdown(self, content: str) -> bool: + async def send_markdown(self, content: str, webhook_url: str = None) -> bool: """ 发送markdown消息 @@ -60,9 +65,9 @@ class WecomBot: "content": content } } - return self._send(data) + return await self._send(data, webhook_url) - def send_image(self, base64: str, md5: str) -> bool: + async def send_image(self, base64: str, md5: str, webhook_url: str = None) -> bool: """ 发送图片消息 @@ -80,9 +85,9 @@ class WecomBot: "md5": md5 } } - return self._send(data) + return await self._send(data) - def send_news(self, articles: List[Dict[str, str]]) -> bool: + async def send_news(self, articles: List[Dict[str, str]], webhook_url: str = None) -> bool: """ 发送图文消息 @@ -98,9 +103,9 @@ class WecomBot: "articles": articles } } - return self._send(data) + return await self._send(data, webhook_url) - def send_template_card(self, template_card: Dict) -> bool: + async def send_template_card(self, template_card: Dict, webhook_url: str = None) -> bool: """ 发送模板卡片消息 @@ -114,9 +119,9 @@ class WecomBot: "msgtype": "template_card", "template_card": template_card } - return self._send(data) + return await self._send(data) - def _send(self, data: Dict) -> bool: + async def _send(self, data: Dict, webhook_url: str = None) -> bool: """ 发送消息 @@ -128,12 +133,14 @@ class WecomBot: """ try: headers = {'Content-Type': 'application/json'} - response = requests.post( - self.webhook_url, - headers=headers, - data=json.dumps(data) - ) - result = response.json() + + async with aiohttp.ClientSession() as session: + async with session.post( + webhook_url or self.webhook_url, + headers=headers, + data=json.dumps(data) + ) as response: + result = await response.json() if result.get('errcode') == 0: return True @@ -145,7 +152,7 @@ class WecomBot: print(f"发送异常: {str(e)}") return False - def send_order_notification(self, shipping_order: ShippingOrderDB, notify_type: OrderStatus = OrderStatus.CREATED) -> bool: + def send_order_notification(self,db: Session, shipping_order: ShippingOrderDB, notify_type: OrderStatus = OrderStatus.CREATED) -> bool: """ 发送订单通知 @@ -156,12 +163,34 @@ class WecomBot: Returns: bool: 是否发送成功 """ + + # 获取webhook + webhook = None + if shipping_order.address_community_id: + community = db.query(CommunityDB).filter( + CommunityDB.id == shipping_order.address_community_id + ).first() + if community: + webhook = community.webot_webhook + + if not webhook: + raise ValueError("不支持的通知类型") + + deliveryman = None + if shipping_order.deliveryman_user_id: + deliveryman = db.query(UserDB).filter( + UserDB.userid == shipping_order.deliveryman_user_id + ).first() + try: if notify_type == OrderStatus.CREATED: title = "🆕 新订单通知" color = "green" + elif notify_type == OrderStatus.RECEIVED: + title = "🚚 已接单通知" + color = "blue" elif notify_type == OrderStatus.COMPLETED: - title = "✅ 订单完成通知" + title = "✅ 配送完成通知" color = "blue" elif notify_type == OrderStatus.CANCELLED: title = "❌ 订单取消通知" @@ -175,17 +204,23 @@ class WecomBot: > 下单时间: {CommonUtils.get_asia_datetime(shipping_order.create_time)} > 包裹数量: {shipping_order.package_count}个 > 订单金额: ¥{shipping_order.original_amount} -> 积分抵扣: ¥{shipping_order.point_discount_amount} > 实付金额: ¥{shipping_order.final_amount} -> 配送方式: {shipping_order.delivery_method} **收件信息** > 姓名: {shipping_order.address_customer_name} -> 电话: {shipping_order.address_customer_phone} -> 地址: {shipping_order.address_community_name} | {shipping_order.address_community_building_name} {shipping_order.address_detail} +> 电话: {shipping_order.address_customer_phone[:3]}\*\*\*\*{shipping_order.address_customer_phone[7:]} +> 地址: {shipping_order.address_community_name} | {shipping_order.address_community_building_name} {shipping_order.address_detail[0:3]} \*\*\*\* """ - return self.send_markdown(content) + + if shipping_order.deliveryman_user_id: + content += f""" +**配送员信息** +> 姓名: {deliveryman.nickname} +> 电话: {deliveryman.phone[:3]}\*\*\*\*{deliveryman.phone[7:]} +""" + + return self.send_markdown(content, webhook) except Exception as e: - print(f"发送订单通知异常: {str(e)}") - return False \ No newline at end of file + logging.exception(f"发送企业微信消息失败: {str(e)}") + raise e \ No newline at end of file diff --git a/app/models/community.py b/app/models/community.py index 8b643e2..8627231 100644 --- a/app/models/community.py +++ b/app/models/community.py @@ -20,6 +20,7 @@ class CommunityDB(Base): latitude = Column(DECIMAL(9,6), nullable=False) # 纬度,精确到小数点后6位 status = Column(Enum(CommunityStatus), nullable=False, default=CommunityStatus.UNOPEN) qy_group_qrcode = Column(String(200), nullable=True) # 企业微信群二维码地址 + webot_webhook = Column(String(200), nullable=True) # 企业微信机器人webhook create_time = Column(DateTime(timezone=True), server_default=func.now()) update_time = Column(DateTime(timezone=True), onupdate=func.now()) @@ -37,6 +38,7 @@ class CommunityCreate(BaseModel): latitude: float = Field(..., ge=-90, le=90) status: CommunityStatus = Field(default=CommunityStatus.UNOPEN) qy_group_qrcode: Optional[str] = Field(None, max_length=200) + webot_webhook: Optional[str] = Field(None, max_length=200) class CommunityUpdate(BaseModel): name: Optional[str] = Field(None, max_length=100) @@ -45,7 +47,7 @@ class CommunityUpdate(BaseModel): latitude: Optional[float] = Field(None, ge=-90, le=90) status: Optional[CommunityStatus] = None qy_group_qrcode: Optional[str] = Field(None, max_length=200) - + webot_webhook: Optional[str] = Field(None, max_length=200) class CommunityInfo(BaseModel): id: int name: str @@ -54,6 +56,7 @@ class CommunityInfo(BaseModel): longitude: float status: CommunityStatus qy_group_qrcode: Optional[str] = None + webot_webhook: Optional[str] = None optimized_qy_group_qrcode: Optional[str] = None distance: Optional[float] = None # 距离,单位:米