物流 TMS 数据库接入企微查件机器人实战


今年 7 月,一个做物流的朋友问我:能不能在企微群里发个单号就查到物流轨迹?

他们有一套跑了好几年的 TMS 系统,PostgreSQL 数据库,200+ 张表。没有 API 文档,没有开发团队,只有一个只读账号。

客服每天要查件。已有查询系统,但有单独的链接,操作路径长。目标就是在企微群里直接查。

数据库调研

连上 PostgreSQL TMS 库。200+ 张表,第一步是找到业务对应的核心表。

经过和对方确认业务流程,定位到三张核心表:

  • business_express — 运单主表,一个单号一条记录
  • customerservice_track — 物流轨迹表,每次扫描/更新追加一行
  • business_shipperconsignee — 收发件人表,记录发件人/收件人信息

核心关联逻辑:business_express 的一条记录,对应 customerservice_track 的多条轨迹、business_shipperconsignee 的一条收发件信息。

SQL 验证

找到核心表后,写了验证 SQL。目标:输入单号 → 返回运单信息 + 最新轨迹。

SELECT
    e.business_customerinvoicecode,
    COALESCE(st.order_statusname, e.business_status),
    e.consignee_country,
    s.consignee_name,
    t.track_date,
    t.track_location,
    t.track_content
FROM business_express e
JOIN business_shipperconsignee s ON e.business_id = s.business_id
JOIN customerservice_track t ON e.business_id = t.business_id
LEFT JOIN basic_orderstatus st ON e.business_status = st.order_status
WHERE LOWER(e.business_customerinvoicecode) = LOWER($1)
ORDER BY t.track_date DESC
LIMIT 20

验证通过。只读查询,无写操作,无锁。

技术方案

确定了数据源后,搭建企微查件机器人。

调研阶段用 Python 验证可行性。但目标服务器是 Windows Server 2012,Go 单文件部署比维护 Python 环境省心,最终方案用了 Go:

企微群 @机器人 → Go Web 服务 → PostgreSQL TMS(只读)
  • Go(chi 路由 + pgx 驱动):编译成单 .exe 文件
  • NSSM 注册 Windows 服务:开机自启、异常重启
  • DeepSeek API:非查件类消息走 AI 闲聊兜底
  • 只读账号GRANT SELECT,不做写操作

踩坑记录

无 API 文档时的对接思路

朋友公司没有 API 文档。反正没有,直接给了数据库只读账号——连上数据库看懂表结构就能干活。

表结构理解成本

200+ 张表,真正的核心只有 3 张。难点不是 SQL,是找到对的那几张表。这一点需要和业务方反复确认流程,光看字段名猜是不够的。

Go 还是 Python?

调研时先用 Python 3.8 写了验证脚本和原型。目标服务器是 Windows Server 2012,上面已经装了 Python 3.8,跑 Python 方案技术上没问题。

但还是选了 Go。原因:

对比项 Python Go 1.20 单文件
运行时 已有 Python 3.8 不需要,编译即完成
进程管理 winsw/NSSM 包装 python 命令 直接注册 .exe
依赖 pip install + 维护虚拟环境 go build 一个文件
异常恢复 进程退出需额外处理 NSSM 自动拉起
版本限制 Python 3.8 在 Server 2012 上能跑 Go 1.20 是最后一个支持 Server 2012 的版本(Go 1.21 起要求 Windows 10+)

Server 2012 已经是十几年前的操作系统,Python 和 Go 都受版本限制。Go 1.20 够用,单文件部署比维护 Python 环境省心太多。部署环境决定了技术选型,不是技术流行度。

企微客服消息:按 send_time 取最新

和内部群聊机器人不同,企微客服(KF)消息回调不直接发消息内容——它先发一个 kf_msg_or_event 事件通知,再通过 SyncKFMsg API 把真正的消息拉回来。

sync_msg 返回一个消息列表,包含该客服账号下所有未读消息。这个列表可能包含:

  • 用户发的多条消息(只应该处理最新那条)
  • 之前已经处理过的消息(重复推送)
  • 非文本消息(图片、系统消息等)

所以不能简单地循环处理每条消息,逻辑是遍历列表,按 send_time 取最新一条:

var newest *wework.KFMsgItem
for i := range items {
    if items[i].MsgType != "text" || items[i].Origin != 3 {
        continue
    }
    if newest == nil || items[i].SendTime > newest.SendTime {
        newest = &items[i]
    }
}

过滤条件:MsgType == "text"(只处理文本消息)、Origin == 3(只处理客户发的消息,排除客服自己的回复)。找到最新消息后还需用 MsgID 去重,防止重复处理。

现状

能正常工作。

总结

  • 老系统数据接入的核心门槛不是技术,是理解业务表结构
  • 不需要对方提供 API 文档,有只读数据库账号就能接
  • 部署方案要跟着环境走:Windows Server 2012 → Go 1.20 单文件 + NSSM 服务
  • 从接到数据库到 SQL 验证通过,一个下午

这个方案适用于有老数据库、无 API 文档、想在 IM 里查数据的场景。