系统演示截图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

微信表格自动处理机器人

用户通过个人微信或企业微信向机器人发送 Excel 文件并附上触发语,机器人按表头识别表格类型,
按客户代码归类单号,再把整理好的提醒消息发到对应的企业微信群。

快速开始

venv/Scripts/pip.exe install -r requirements.txt
venv/Scripts/python.exe app.py

启动后打开 http://127.0.0.1:9000 配置参数。首次运行会自动生成 .env

默认是演练模式DRY_RUN=true),只写日志不真发消息。确认试算结果正确后,
.env 里的 DRY_RUN 改成 false 再重启即可上线。

必须先配的三项

配置项 说明
QIWEI_TOKEN 开放接口的 X-QIWEI-TOKEN
QIWEI_GUID 默认 guid。回调里带 guid 时优先用回调值
CALLBACK_SECRET 回调鉴权密钥,需与消息推送端的 Authorization 头一致

把回调地址 http://<你的公网地址>:9000/api/data 配到消息推送后台,请求头
AuthorizationCALLBACK_SECRET 的值。

QIWEI_TOKEN 没配的话,取群昵称、下载文件、发消息全都会失败(接口会返回
header:X-QIWEI-TOKEN 不可为空)。启动时如果没配,日志里会有一条 ERROR 提示。

工作流程

  1. 建群映射 — 任意群消息进来时,若 data/rooms.json 里没有该群 ID,就调
    /room/batchGetRoomDetail 取群昵称并追加;已拿到昵称的不再重复请求。存储格式为
    {"群ID": "群昵称"}。取昵称失败时也会先把群 ID 记下来(值为空串),之后该群再来消息
    会自动重试,详见下文。
  2. 等待配对 — 同一个发送者在 PAIR_WINDOW_SECONDS(默认 300 秒)内既发了
    Excel 又发了触发语,才会启动处理。先发文件还是先发触发语都可以。触发语在
    「参数配置 → 触发关键词」里改,默认 开始处理,处理表格,自动处理,包含匹配
    (消息里含有任一关键词即命中)。匹配方式可以在 .env 里用 TRIGGER_MATCH_MODE
    改成 exact(必须完全相等)。
  3. 下载文件 — 个微(msgType=102)走 /cloud/wxDownloadAsync
    企微(msgType=15)走 /cloud/wxWorkDownloadAsync。两者都是异步的:接口先返回
    requestId,真实下载地址由后续 cmd=20000 回调的 msgData.cloudUrl 带回,
    requestId 对上号。
  4. 识别与归类 — 按表头判断类型,按客户代码把单号归到一起。
  5. 匹配群并发送 — 群昵称里包含该客户代码的群即为目标群。

支持的两种表格

一、无尾程面单补录提醒

表头:日期 / 客户代码 / 客户简称 / 客户单号 / 备注1 / 备注2

XM1FH1A015945
XM2252J012802

老板,无尾程面单的订单, 麻烦查一下原因, 不是取消单的话,赶紧补录一下面单,如果补录失败的话,尾程面单导PDF文件以PO号命名发给我们

二、已预报未入库提醒

表头:订单状态 / 客户代码 / ... / 预报日期 / ...(共 28 列)

XM23F7Y026387

这是截止于7.25号已预报未入库的订单目前国内仓库还未收到货物 请您注意一下切勿漏发订单导致无轨迹罚款或者延误哦!Y2872

日期取该客户所有记录中最近的 预报日期,格式为 月.日;结尾附客户代码。

两段话术都可以在 WebUI 里改。第二段支持 {date}(预报日期)和 {code}(客户代码)两个占位符。

WebUI 页面

  • 参数配置 — 只放触发关键词话术模板表头识别三组,各占一个面板,
    保存后写回 .env 并立即生效(根路径 / 直接跳到这里)
  • 群映射 — 只展示自动记录的 群ID→群昵称,不提供手工增改;昵称还没取到的群会单独列出,
    可以一键重试
  • 表格试算 — 上传或指定本地表格,只解析不发送,用来核对结果
  • 群发通知 — 手工发通知:输入一段文字,勾选要发的群,一键发送

其余变量(接口地址、Token、guid、触发匹配方式、配对时间窗、发送行为、行过滤、目录、
日志、清理策略等)都只从 .env 读取,页面上不暴露。需要调整就直接编辑 .env 后重启服务。
这样既避免误操作把密钥、接口地址改坏,也让 WebUI 只专注在日常真正要调的内容上。

上线前建议先用「表格试算」跑一遍,确认客户归类和群匹配都对。

关于是否要过滤已处理的记录(请先确认)

需求文档没有提到按备注筛选,所以默认不过滤,所有行都会处理。但样例
1.23-7.23.xlsx 是 1 月 23 日到 7 月 23 日的 6 个月流水,共 70985 行,其中:

  • 备注1已通知 94.4%、已通知代理 3.3%、没群,已通知雪纯 2.3%
  • 备注2已补录 39.5%、已补录-验 30.1%、已取消 25.8%、已退款 1.5%

也就是说约 97% 的记录看起来已经处理完了。按原样全量发送,会把几个月前就已补录或
已取消的单号重新催一遍,其中一个客户有 4315 个单号,会被拆成 44 条消息发到同一个群。

如果确实只想催没处理完的,在 .env 里填上 EXCLUDE_REMARK_KEYWORDS
例如 已取消,已补录,已退款,已签收,尾程已发货,海外仓已打印标签。填上之后同一个文件的
结果是:1019 个有效单号、258 个客户,单客户最多 120 个单号——量级合理得多。

建议先用「表格试算」对比一下两种配置的结果,再决定上线用哪种。

群发通知

和表格自动处理无关,是给运营手工发通知用的。在输入框里写一段文字,下面会列出所有已记录的群,
勾选后一键发送。带全选/全不选和按群名过滤,方便群多的时候操作。

几个约束:

  • 同样受 DRY_RUN 约束。演练模式下点发送只记日志,页面会明确告知"实际一条都没发"。
  • 只能发给已记录的群。后端会校验群 ID 是否在 data/rooms.json 里,不接受任意 ID——
    否则这个页面就成了"给任何人发消息"的入口(WebUI 默认还没有密码)。
  • 昵称还没取到的群不能勾选,因为那通常意味着接口没配好。
  • 真实发送前有二次确认弹窗;每条之间间隔 SEND_INTERVAL_SECONDS,页面会按勾选数量预估耗时。
  • 逐群报告结果。全部成功才清空输入框;只要有一个群失败就保留内容和勾选,方便改完重试。
    失败的群会单独列出错误原因。

群映射页面看不到群?

群映射完全靠自动记录,收到群消息就写入。如果发了消息却看不到记录,按下面顺序查:

1. 回调有没有进来。logs/app.log 有没有 发现新群 <群ID>。没有的话说明回调
根本没到,检查消息推送后台的回调地址、Authorization 头是否等于 CALLBACK_SECRET
(不匹配会记 回调鉴权失败)。

2. QIWEI_TOKEN 配了没。 这是最常见的原因。没配的话日志里会看到:

获取群昵称失败 room_id=12641087403099: 接口 /room/batchGetRoomDetail 业务失败: code=500 msg=header:X-QIWEI-TOKEN 不可为空
QIWEI_TOKEN 为空,无法调用接口。请在 .env 里填好 QIWEI_TOKEN 后重启服务

这种情况下群 ID 已经记下来了,会出现在页面的「待补昵称的群」里,只是没有昵称。
.env 里的 QIWEI_TOKEN 填好重启,然后点页面上的「立即重试获取昵称」即可补齐;
不点也行,该群下次再来消息时会自动重试。

3. 群本身有没有名称。 接口调用成功但 roomName 为空,也会留在「待补昵称」里。
匹配客户靠的是群昵称,所以这种群必须先在微信里给它起个包含客户代码的名字。

失败后不会每条消息都去打接口,冷却时间由 ROOM_NAME_RETRY_SECONDS(默认 300 秒)控制。

客户代码与群的匹配规则

以「群昵称包含客户代码」为准,并做了两点加固:

  • 边界校验 — 匹配位置两侧不能紧跟英文字母或数字,避免 Y21 误命中 Y2138群
    中文不算代码字符,所以 Y181客户群 能正常命中 Y181
  • 精确优先 — 群昵称正好等于客户代码时,优先用这个群。

样例数据里有约 12 个客户(共 1159 个)的 客户代码 是乱码,真代码在 客户简称
(如代码 FVFBUQ、简称 Y584)。归类仍按 客户代码,但找群时会回退用简称里的代码再试
一次,避免这些客户被整体跳过。该行为由 FALLBACK_MATCH_BY_ALIAS 控制。

目录结构

app.py               Flask 入口:回调接收 + WebUI
config.py            配置管理,读写 .env
processor.py         业务编排:回调分发、配对、处理、下发
excel_processor.py   表头识别、归类、话术生成
matcher.py           客户代码 -> 群 的查找
room_store.py        群映射存储与匹配
notifier.py          群发通知
downloader.py        异步下载 + requestId 配对
cleaner.py           下载目录定时清理
wx_api.py            开放接口封装
logger.py            日志
data/rooms.json      群映射
downloads/           下载的表格
logs/                日志
tests/               测试

下载目录定时清理

下载回来的表格会一直堆在 downloads/,所以内置了定时清理,默认每 24 小时扫一次,
保留 7 天,并且总容量不超过 500 MB
。服务启动时也会先扫一次,重启就能回收空间。

变量 默认值 说明
CLEANUP_ENABLED true 总开关
CLEANUP_INTERVAL_HOURS 24 多久扫一次。下限 0.05 小时(3 分钟)
CLEANUP_KEEP_DAYS 7 保留天数。0 = 每次扫描都清空
CLEANUP_MAX_TOTAL_MB 500 总容量上限,超了从最旧的开始删。0 = 不限制

两道防线是叠加的:先按天数删过期的,再看总量有没有超上限。所以即使某天来了特别多文件,
占用也不会突破 CLEANUP_MAX_TOTAL_MB

清理只针对下载目录里的 .xlsx / .xlsm / .xls,其他文件不碰。如果 DOWNLOAD_DIR 被误配成
项目根目录或盘符根目录,清理会直接拒绝执行并记 error 日志,不会误删项目文件。

清理动作会记到 logs/app.log,例如 清理下载目录:删除 2 个文件,释放 4.8 MB(保留 7.0 天)
想确认是否生效就看日志。

CLEANUP_KEEP_DAYS 设成 0 时要注意:清理会删掉目录里所有表格,包括刚下载还在处理的那个。
解析是一次性读进内存的,正常不会有影响,但没必要的话不建议设 0。

测试

venv/Scripts/python.exe -m pytest tests -q

注意事项

  • WebUI 默认没有密码,任何能访问该端口的人都能改配置、看群映射、手动发消息。
    只要不是仅本机访问,就应该设置 WEBUI_PASSWORD,并且不要把端口直接暴露到公网。
  • 大表格(7 万行)解析约 2 秒,处理在后台线程跑,不阻塞回调响应。
  • 下载目录会自动定时清理,默认保留 7 天、总量不超过 500 MB,详见上文。
  • 单个客户单号过多时按 MAX_ORDERS_PER_MESSAGE(默认 100)分条发送;拆出的条数超过
    WARN_MESSAGES_PER_CUSTOMER(默认 10)会记一条警告,方便发现异常量级。
  • 每条消息之间间隔 SEND_INTERVAL_SECONDS(默认 1.5 秒),避免触发频率限制。
  • 未匹配到群的客户会被跳过并记警告日志,不会误发到别的群。
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐