feat: 微信订阅消息模板字段适配,填入 6 个真实模板 ID
- bootstrap.py 写入 6 个微信订阅消息模板 ID - wechat_notification_service 新增 TEMPLATE_FIELD_MAP 按模板字段名发送 - reminder_repository 新增 _build_extra_data 从 title/content 提取结构化数据 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8c8d730184
commit
1a3b67c423
@ -5,6 +5,7 @@
|
||||
被 ReminderService(提醒服务)调用。
|
||||
"""
|
||||
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import and_, func, select
|
||||
@ -110,15 +111,77 @@ class ReminderRepository:
|
||||
user = session.execute(select(User).where(User.id == receiver_id)).scalar_one_or_none()
|
||||
if user is None or not user.open_id:
|
||||
return
|
||||
extra_data = self._build_extra_data(reminder)
|
||||
wechat_notification_service.send_subscribe_message(
|
||||
user.open_id,
|
||||
reminder.reminder_type,
|
||||
reminder.reminder_title,
|
||||
reminder.reminder_content,
|
||||
extra_data=extra_data,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _build_extra_data(self, reminder: SystemReminder) -> dict:
|
||||
"""根据提醒类型从 title/content 中提取结构化数据,用于填充微信模板字段。
|
||||
|
||||
参数:
|
||||
reminder: 系统提醒对象
|
||||
|
||||
返回:
|
||||
包含模板所需字段数据的字典
|
||||
"""
|
||||
title = reminder.reminder_title or ""
|
||||
content = reminder.reminder_content or ""
|
||||
now_str = datetime.now().strftime("%Y-%m-%d %H:%M")
|
||||
|
||||
order_no_match = re.search(r'[\d-]{10,}', title + content)
|
||||
order_no = order_no_match.group() if order_no_match else title
|
||||
|
||||
base: dict[str, str] = {
|
||||
"order_no": order_no,
|
||||
"change_time": now_str,
|
||||
"assign_time": now_str,
|
||||
"timeout_time": now_str,
|
||||
"check_time": now_str,
|
||||
"submit_time": now_str,
|
||||
}
|
||||
|
||||
rtype = reminder.reminder_type
|
||||
|
||||
if rtype == "order_status_change":
|
||||
base["status_text"] = content[:50] if content else title
|
||||
|
||||
elif rtype == "order_approval_needed":
|
||||
base["submitter"] = "业务员"
|
||||
base["order_info"] = title
|
||||
base["remark"] = "请尽快审批"
|
||||
|
||||
elif rtype == "task_assigned":
|
||||
base["task_no"] = order_no
|
||||
base["task_type"] = "运输任务"
|
||||
base["assign_date"] = now_str.split(" ")[0]
|
||||
|
||||
elif rtype == "logistics_timeout":
|
||||
hours_match = re.search(r'(\d+)\s*小时', content)
|
||||
base["timeout_info"] = f"超过{hours_match.group(1) if hours_match else '48'}小时未流转"
|
||||
base["remark"] = "请及时跟进"
|
||||
|
||||
elif rtype == "arrears":
|
||||
amount_match = re.search(r'([\d.]+)\s*元', content)
|
||||
customer_match = re.search(r'客户[::](.+?)[,,]', content)
|
||||
base["customer_info"] = (customer_match.group(1) if customer_match else "") + "欠款"
|
||||
base["arrears_amount"] = amount_match.group(1) if amount_match else "0"
|
||||
base["order_date"] = now_str
|
||||
base["due_date"] = ""
|
||||
|
||||
elif rtype == "inactive_customer":
|
||||
days_match = re.search(r'(\d+)\s*天', content)
|
||||
last_time_match = re.search(r'最近订单时间[::](.+?)[。,]', content)
|
||||
base["customer_name"] = title.replace("沉默客户提醒 - ", "")
|
||||
base["inactive_info"] = f"超过{days_match.group(1) if days_match else '90'}天未下单"
|
||||
base["last_order_time"] = last_time_match.group(1).strip() if last_time_match else "暂无"
|
||||
|
||||
return base
|
||||
|
||||
def find_active_reminder(
|
||||
self,
|
||||
session: Session,
|
||||
|
||||
@ -41,12 +41,12 @@ DEFAULT_CONFIGS = [
|
||||
{"config_key": "allow_cancel_statuses", "config_value": "draft,pending_approve,approved,pending_factory", "config_name": "允许取消的订单状态", "remark": "逗号分隔的状态编码列表", "status": 1},
|
||||
{"config_key": "report_category_mapping", "config_value": "{}", "config_name": "报表分类映射", "remark": "按产品分类配置统计口径", "status": 1},
|
||||
{"config_key": "logistics_trace_provider", "config_value": "internal", "config_name": "物流轨迹服务商", "remark": "internal=内置,kdniao=快递100", "status": 1},
|
||||
{"config_key": "wechat_template_order_status", "config_value": "", "config_name": "微信模板-订单状态变更", "remark": "微信小程序订阅消息模板ID", "status": 1},
|
||||
{"config_key": "wechat_template_approval", "config_value": "", "config_name": "微信模板-审批通知", "remark": "微信小程序订阅消息模板ID", "status": 1},
|
||||
{"config_key": "wechat_template_task_assigned", "config_value": "", "config_name": "微信模板-任务分配", "remark": "微信小程序订阅消息模板ID", "status": 1},
|
||||
{"config_key": "wechat_template_logistics_timeout", "config_value": "", "config_name": "微信模板-物流超时", "remark": "微信小程序订阅消息模板ID", "status": 1},
|
||||
{"config_key": "wechat_template_arrears", "config_value": "", "config_name": "微信模板-欠款提醒", "remark": "微信小程序订阅消息模板ID", "status": 1},
|
||||
{"config_key": "wechat_template_inactive_customer", "config_value": "", "config_name": "微信模板-沉默客户", "remark": "微信小程序订阅消息模板ID", "status": 1},
|
||||
{"config_key": "wechat_template_order_status", "config_value": "lA4NkXqSYHXr4XvULlmJUSqS1pM8dFrYq5_poA-bM7g", "config_name": "微信模板-订单状态变更", "remark": "微信小程序订阅消息模板ID", "status": 1},
|
||||
{"config_key": "wechat_template_approval", "config_value": "Zcd8dxVRBPGpQIO1fhMjgWDy2mS012KoOeKbJK9mrAk", "config_name": "微信模板-审批通知", "remark": "微信小程序订阅消息模板ID", "status": 1},
|
||||
{"config_key": "wechat_template_task_assigned", "config_value": "rz5ndHgRxaqCpikfH0qYFMA1w5YeT1IZ6b19e9F1W5c", "config_name": "微信模板-任务分配", "remark": "微信小程序订阅消息模板ID", "status": 1},
|
||||
{"config_key": "wechat_template_logistics_timeout", "config_value": "N07iQmxpk3iaAyRNqy7NdUUNAXD0dDaE99DcbpgSwrM", "config_name": "微信模板-物流超时", "remark": "微信小程序订阅消息模板ID", "status": 1},
|
||||
{"config_key": "wechat_template_arrears", "config_value": "uSSHcFChGkJSSGj9XwjVTUiZ51yWuaPMRYeLY9nrJME", "config_name": "微信模板-欠款提醒", "remark": "微信小程序订阅消息模板ID", "status": 1},
|
||||
{"config_key": "wechat_template_inactive_customer", "config_value": "B0htDO1tdyFCXXpw7lQaK5dcDkcUtrRNdJpWcsc7T3M", "config_name": "微信模板-沉默客户", "remark": "微信小程序订阅消息模板ID", "status": 1},
|
||||
]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@ -27,7 +27,49 @@ class WeChatNotificationService:
|
||||
"inactive_customer": "wechat_template_inactive_customer",
|
||||
}
|
||||
|
||||
def send_subscribe_message(self, open_id: str, reminder_type: str, title: str, content: str, biz_url: str = "") -> bool:
|
||||
# 每个模板的微信字段名映射
|
||||
# 格式: reminder_type -> { "微信模板字段名": "extra_data 中的 key" }
|
||||
TEMPLATE_FIELD_MAP: dict[str, dict[str, str]] = {
|
||||
"order_status_change": {
|
||||
"thing1": "order_no",
|
||||
"thing2": "status_text",
|
||||
"time3": "change_time",
|
||||
"thing4": "order_no",
|
||||
},
|
||||
"order_approval_needed": {
|
||||
"thing1": "submitter",
|
||||
"thing2": "order_info",
|
||||
"time3": "submit_time",
|
||||
"thing5": "remark",
|
||||
},
|
||||
"task_assigned": {
|
||||
"thing1": "task_no",
|
||||
"thing2": "task_type",
|
||||
"date3": "assign_date",
|
||||
"time5": "assign_time",
|
||||
},
|
||||
"logistics_timeout": {
|
||||
"thing1": "order_no",
|
||||
"thing2": "timeout_info",
|
||||
"time3": "timeout_time",
|
||||
"thing4": "remark",
|
||||
},
|
||||
"arrears": {
|
||||
"time1": "order_date",
|
||||
"thing2": "customer_info",
|
||||
"amount3": "arrears_amount",
|
||||
"time4": "due_date",
|
||||
"thing5": "order_no",
|
||||
},
|
||||
"inactive_customer": {
|
||||
"thing1": "customer_name",
|
||||
"thing2": "inactive_info",
|
||||
"thing3": "last_order_time",
|
||||
"time4": "check_time",
|
||||
},
|
||||
}
|
||||
|
||||
def send_subscribe_message(self, open_id: str, reminder_type: str, title: str = "", content: str = "", extra_data: dict | None = None, biz_url: str = "") -> bool:
|
||||
settings = get_settings()
|
||||
if not settings.wechat_app_id or not settings.wechat_app_secret:
|
||||
return False
|
||||
@ -40,15 +82,24 @@ class WeChatNotificationService:
|
||||
if not access_token:
|
||||
return False
|
||||
|
||||
field_map = self.TEMPLATE_FIELD_MAP.get(reminder_type)
|
||||
if not field_map:
|
||||
return False
|
||||
|
||||
data = extra_data or {}
|
||||
now_str = time.strftime("%Y-%m-%d %H:%M")
|
||||
payload_data = {}
|
||||
for wx_field, data_key in field_map.items():
|
||||
value = data.get(data_key, "")
|
||||
if not value:
|
||||
value = title if data_key == "order_no" else now_str
|
||||
payload_data[wx_field] = {"value": str(value)[:100]}
|
||||
|
||||
payload = {
|
||||
"touser": open_id,
|
||||
"template_id": template_id,
|
||||
"page": biz_url,
|
||||
"data": {
|
||||
"thing1": {"value": title[:20]},
|
||||
"thing2": {"value": content[:100]},
|
||||
"time3": {"value": time.strftime("%Y-%m-%d %H:%M")},
|
||||
},
|
||||
"data": payload_data,
|
||||
}
|
||||
|
||||
url = f"https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token={access_token}"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user