增加 企业微信通知。

This commit is contained in:
aaron 2025-02-24 11:19:38 +08:00
parent d5fac2687a
commit 5fc85eaa39
5 changed files with 88 additions and 53 deletions

View File

@ -38,6 +38,8 @@ from app.core.imageprocessor import process_image, ImageFormat
from app.core.point_manager import PointManager from app.core.point_manager import PointManager
from datetime import time from datetime import time
from app.core.wecombot import WecomBot from app.core.wecombot import WecomBot
from app.models.config import ConfigDB
router = APIRouter() router = APIRouter()
def calculate_price(price_request: OrderPriceCalculateRequest,user: UserDB,db: Session) -> OrderPriceResult: def calculate_price(price_request: OrderPriceCalculateRequest,user: UserDB,db: Session) -> OrderPriceResult:
@ -271,8 +273,9 @@ async def create_order(
).all() ).all()
# 发送企业微信消息 # 发送企业微信消息
wecom_bot = WecomBot(settings.WECOM_WEBHOOK_URL) wecom_bot = WecomBot()
wecom_bot.send_order_notification(db_order, OrderStatus.CREATED) await wecom_bot.send_order_notification(db, db_order, OrderStatus.CREATED)
#发送订单创建成功的消息 #发送订单创建成功的消息
if current_user.mp_openid: if current_user.mp_openid:
@ -679,6 +682,10 @@ async def cancel_order(
db.commit() db.commit()
# 发送企业微信消息
wecom_bot = WecomBot()
await wecom_bot.send_order_notification(db, order, OrderStatus.CANCELLED)
# 发送模板消息 # 发送模板消息
if current_user.mp_openid: if current_user.mp_openid:
await mp_client.send_template_message( await mp_client.send_template_message(
@ -876,6 +883,10 @@ async def deliveryman_cancel_order(
db.commit() db.commit()
# 发送企业微信消息
wecom_bot = WecomBot()
await wecom_bot.send_order_notification(db, order, OrderStatus.CANCELLED)
# 发送模板消息 # 发送模板消息
if order.userid: if order.userid:
order_user = db.query(UserDB).filter( order_user = db.query(UserDB).filter(
@ -952,6 +963,10 @@ async def complete_order(
db.commit() db.commit()
# 发送企业微信消息
wecom_bot = WecomBot()
await wecom_bot.send_order_notification(db, order, OrderStatus.COMPLETED)
# 发送模板消息 # 发送模板消息
if order.userid: if order.userid:
order_user = db.query(UserDB).filter( order_user = db.query(UserDB).filter(
@ -1020,6 +1035,10 @@ async def receive_order(
db.commit() db.commit()
# 发送企业微信消息
wecom_bot = WecomBot()
await wecom_bot.send_order_notification(db, order, OrderStatus.RECEIVED)
# 发送模板消息 # 发送模板消息
if order.userid: if order.userid:
order_user = db.query(UserDB).filter( order_user = db.query(UserDB).filter(

View File

@ -229,28 +229,6 @@ async def payment_notify(
order.transaction_id = transaction_id order.transaction_id = transaction_id
order.pay_time = datetime.fromisoformat(success_time.replace('Z', '+00:00')) 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"): # 商家商品订单 elif out_trade_no.startswith("M"): # 商家商品订单
# 查询商户订单 # 查询商户订单

View File

@ -5,11 +5,16 @@ from datetime import datetime
from app.core.utils import CommonUtils from app.core.utils import CommonUtils
from app.models.order import OrderStatus from app.models.order import OrderStatus
from app.models.order import ShippingOrderDB 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: 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 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: if mentioned_mobile_list:
data["text"]["mentioned_mobile_list"] = 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消息 发送markdown消息
@ -60,9 +65,9 @@ class WecomBot:
"content": content "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 "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 "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", "msgtype": "template_card",
"template_card": 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: try:
headers = {'Content-Type': 'application/json'} headers = {'Content-Type': 'application/json'}
response = requests.post(
self.webhook_url, async with aiohttp.ClientSession() as session:
async with session.post(
webhook_url or self.webhook_url,
headers=headers, headers=headers,
data=json.dumps(data) data=json.dumps(data)
) ) as response:
result = response.json() result = await response.json()
if result.get('errcode') == 0: if result.get('errcode') == 0:
return True return True
@ -145,7 +152,7 @@ class WecomBot:
print(f"发送异常: {str(e)}") print(f"发送异常: {str(e)}")
return False 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: Returns:
bool: 是否发送成功 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: try:
if notify_type == OrderStatus.CREATED: if notify_type == OrderStatus.CREATED:
title = "🆕 新订单通知" title = "🆕 新订单通知"
color = "green" color = "green"
elif notify_type == OrderStatus.RECEIVED:
title = "🚚 已接单通知"
color = "blue"
elif notify_type == OrderStatus.COMPLETED: elif notify_type == OrderStatus.COMPLETED:
title = "✅ 订单完成通知" title = "配送完成通知"
color = "blue" color = "blue"
elif notify_type == OrderStatus.CANCELLED: elif notify_type == OrderStatus.CANCELLED:
title = "❌ 订单取消通知" title = "❌ 订单取消通知"
@ -175,17 +204,23 @@ class WecomBot:
> 下单时间: {CommonUtils.get_asia_datetime(shipping_order.create_time)} > 下单时间: {CommonUtils.get_asia_datetime(shipping_order.create_time)}
> 包裹数量: {shipping_order.package_count} > 包裹数量: {shipping_order.package_count}
> 订单金额: ¥{shipping_order.original_amount} > 订单金额: ¥{shipping_order.original_amount}
> 积分抵扣: ¥{shipping_order.point_discount_amount}
> 实付金额: ¥{shipping_order.final_amount} > 实付金额: ¥{shipping_order.final_amount}
> 配送方式: {shipping_order.delivery_method}
**收件信息** **收件信息**
> 姓名: {shipping_order.address_customer_name} > 姓名: {shipping_order.address_customer_name}
> 电话: {shipping_order.address_customer_phone} > 电话: {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} > 地址: {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: except Exception as e:
print(f"发送订单通知异常: {str(e)}") logging.exception(f"发送企业微信消息失败: {str(e)}")
return False raise e

View File

@ -20,6 +20,7 @@ class CommunityDB(Base):
latitude = Column(DECIMAL(9,6), nullable=False) # 纬度精确到小数点后6位 latitude = Column(DECIMAL(9,6), nullable=False) # 纬度精确到小数点后6位
status = Column(Enum(CommunityStatus), nullable=False, default=CommunityStatus.UNOPEN) status = Column(Enum(CommunityStatus), nullable=False, default=CommunityStatus.UNOPEN)
qy_group_qrcode = Column(String(200), nullable=True) # 企业微信群二维码地址 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()) create_time = Column(DateTime(timezone=True), server_default=func.now())
update_time = Column(DateTime(timezone=True), onupdate=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) latitude: float = Field(..., ge=-90, le=90)
status: CommunityStatus = Field(default=CommunityStatus.UNOPEN) status: CommunityStatus = Field(default=CommunityStatus.UNOPEN)
qy_group_qrcode: Optional[str] = Field(None, max_length=200) qy_group_qrcode: Optional[str] = Field(None, max_length=200)
webot_webhook: Optional[str] = Field(None, max_length=200)
class CommunityUpdate(BaseModel): class CommunityUpdate(BaseModel):
name: Optional[str] = Field(None, max_length=100) name: Optional[str] = Field(None, max_length=100)
@ -45,7 +47,7 @@ class CommunityUpdate(BaseModel):
latitude: Optional[float] = Field(None, ge=-90, le=90) latitude: Optional[float] = Field(None, ge=-90, le=90)
status: Optional[CommunityStatus] = None status: Optional[CommunityStatus] = None
qy_group_qrcode: Optional[str] = Field(None, max_length=200) qy_group_qrcode: Optional[str] = Field(None, max_length=200)
webot_webhook: Optional[str] = Field(None, max_length=200)
class CommunityInfo(BaseModel): class CommunityInfo(BaseModel):
id: int id: int
name: str name: str
@ -54,6 +56,7 @@ class CommunityInfo(BaseModel):
longitude: float longitude: float
status: CommunityStatus status: CommunityStatus
qy_group_qrcode: Optional[str] = None qy_group_qrcode: Optional[str] = None
webot_webhook: Optional[str] = None
optimized_qy_group_qrcode: Optional[str] = None optimized_qy_group_qrcode: Optional[str] = None
distance: Optional[float] = None # 距离,单位:米 distance: Optional[float] = None # 距离,单位:米