feat: 通知发送层切换到群机器人 webhook, 排版精简

- wecom.py 重写: HTTP POST webhook(替代 aibot WebSocket 按需连接), 接口 send_markdown/send_text 不变, Secret Block wecom-webhook 存 URL
- webhook markdown 支持标题/引用/font 三色, 报告改为 '## 标题 + 单行结论 + 引用详情' 紧凑排版, 结论行用 info/warning 色
- alert_failure 改标题式排版; device_check 结论用 font 色, 去掉负载行
- 移除 wecom-aibot-sdk 依赖与 wecom_capture_chatid.py(镜像下次构建生效, 无需立即重建)
This commit is contained in:
yeuimu
2026-09-07 10:09:11 +00:00
parent c545c1b87d
commit 9b130097f9
8 changed files with 283 additions and 277 deletions
+19 -31
View File
@@ -27,7 +27,7 @@ def _affected_units(diff: dict) -> int:
def _system_lines(name: str, report: dict, goods: dict, lines: list[str]) -> bool:
"""单系统的核验两段:①数量是否一致(订单+商品) ②状态不符多少单。返回是否需关注。"""
"""单系统一行结论:数量(远程 vs 本地,一致只显示一个数)+商品;状态不符进引用行。"""
vr = (report.get("verifyReport") or {}) if isinstance(report, dict) else {}
days_r = vr.get("days") or []
post = vr.get("post")
@@ -36,35 +36,27 @@ def _system_lines(name: str, report: dict, goods: dict, lines: list[str]) -> boo
else:
remote_t = sum(d.get("remote", 0) for d in days_r)
local_t = sum(d.get("local", 0) for d in days_r)
# 数量一致只看总量;状态差异属第二段,不混入数量结论
ok = remote_t == local_t
ok = remote_t == local_t # 数量一致只看总量;状态差异属第二段,不混入数量结论
if ok:
lines.append(f"**{name} 订单** 远程 {remote_t:,} · 本地 {local_t:,} ✅")
else:
lines.append(f"**{name} 订单** 远程 {remote_t:,} · 本地 {local_t:,} ⚠️ 不一致")
g = goods or {}
zero = g.get("zeroGoodsOrders", 0)
goods_txt = f"{g.get('goods', 0):,} 行" + (f"(缺 {zero:,})" if zero else "")
order_txt = f"订单 {remote_t:,} ✅" if ok else f"订单 远程 {remote_t:,} / 本地 {local_t:,} ⚠️"
lines.append(f"**{name}** {order_txt} · 商品 {goods_txt} {'✅' if not zero else '⚠️'}")
diff_days = [d for d in days_r if not d.get("ok")]
diff_units = sum(_affected_units(d.get("diff")) for d in diff_days)
if diff_units:
lines.append(f"> 核验时发现约 {diff_units:,} 单状态不符({len(diff_days)} 天),已自动刷新修复")
g = goods or {}
zero = g.get("zeroGoodsOrders", 0)
if zero:
lines.append(f"**{name} 商品** {g.get('goods', 0):,} 行 · {zero:,} 单缺商品 ⚠️")
return True
lines.append(f"**{name} 商品** {g.get('goods', 0):,} 行 · 无缺漏 ✅")
return not ok
lines.append(f"> 状态不符约 {diff_units:,} 单({len(diff_days)} 天),已自动刷新修复")
return (not ok) or bool(zero)
def build_daily_message(
days: int, window: str, riin: dict, sds: dict,
goods: dict, ocr_today: dict, ocr_pending: int, failures: list[dict],
) -> tuple[str, bool]:
"""拼装日报 markdown,返回 (消息, 是否需关注)。"""
lines = [f"📊 **数据核验日报** {datetime.now().strftime('%m-%d %H:%M')}"
f"(近 {days} 天,{window})", ""]
"""拼装日报 markdown,返回 (消息, 是否需关注)。单行结论 + 引用详情的紧凑排版。"""
lines = [f"## 📊 数据核验日报 {datetime.now():%m-%d %H:%M}(近 {days} 天 {window})"]
attention = False
for name, report in (("RIIN", riin), ("SDS", sds)):
@@ -73,30 +65,26 @@ def build_daily_message(
except Exception as err: # noqa: BLE001
lines.append(f"**{name}** 核验结果解析失败:{err}")
attention = True
lines.append("")
processed, failed = (ocr_today or {}).get("processed", 0), (ocr_today or {}).get("failed", 0)
lines.append(f"**OCR 今日** 已处理 {processed:,} 单 · 识别失败 {failed:,} 单" + (" ✅" if not failed else ""))
ocr_txt = f"**OCR 今日** 处理 {processed:,} · 识别失败 {failed:,}" + (" ✅" if not failed else " ⚠️")
if ocr_pending:
lines.append(f"> 累计待处理 {ocr_pending:,} 单")
lines.append("")
ocr_txt += f"(累计待处理 {ocr_pending:,})"
lines.append(ocr_txt)
if failures:
attention = True
lines.append(f"**失败任务(24h)** 共 {len(failures)} 次 🔴")
lines.append(f"**失败任务(24h)** {len(failures)} 次 🔴")
for f in failures[:6]:
start = (f.get("start") or "")[5:] # MM-DD HH:MM
lines.append(f"> {f.get('flow', '?')} · run「{f.get('name', '?')}」{f.get('state', '?')} · {start}")
lines.append(f"> {f.get('flow', '?')}「{f.get('name', '?')}」{f.get('state', '?')} · {start}")
if len(failures) > 6:
lines.append(f"> …等共 {len(failures)} 次")
else:
lines.append("**失败任务(24h)** 0 次 ✅")
lines.append("**失败任务(24h)** 0 ✅")
lines.append("")
if attention:
lines.append("⚠️ **存在需关注项,见上**")
else:
lines.append("✅ **数据全部一致,无异常**")
lines.append('<font color="warning">⚠️ 存在需关注项,见上</font>' if attention
else '<font color="info">✅ 数据全部一致,无异常</font>')
return "\n".join(lines), attention