资讯详情

资讯详情

企业微信消息接入实战:从微信群聊到自动生成车辆计划

做拌合楼生产管理系统做到第二十四期终于轮到处理微信消息这个事了。说实话这个功能单看技术含量并不高但它背后是一条完整的业务链路现场调度员在微信群里说一句明天早上六点A工地要40方C35这句话怎么才能自动变成系统里的车辆计划、排车序号和司机派单这一篇就把这条链路从消息接入、解析、生成计划到异常兜底完整拆开讲清楚。先说个背景。搅拌站/拌合楼的调度场景里信息源非常杂电话、微信群、对讲机、纸质单据都有。以前调度员的日常就是盯着手机群消息看到一句加车需求就手动打开系统录任务录完再排车一天这种重复操作至少几十次忙中出错是常事。我们做这套系统的目标很明确——把微信消息变成一条可执行的车辆计划让调度员从录入员变成审核员。所以这篇博客我会按实际的开发顺序走先讲业务上车辆计划产生的流程和边界再讲选型为什么最终走企业微信而不是个人微信/公众号然后是回调接入、消息解析、车辆计划生成和状态流转最后是重复/乱序/失败这些生产环境避不开的坑以及几件真实上线后的意外。整个系列前二十几篇都在讲硬件对接、配方管理、磅房称重这些这一篇算是一个轻量集成的典型案例但它解决的问题其实是所有中小型搅拌站都绕不开的痛点。1. 先把业务看懂微信消息在这个系统里到底扮演什么角色1.1 车辆计划是怎么从一句话变成一条任务的要写代码之前我建议任何开发都先去调度室坐半天。站在旁边看调度员接电话、看微信群、记录方量、排车会发现整个业务流程大概是这样需求方工地施工员或老板发出要货信息哪个工地、什么标号、方量多少、大概几点要。调度员手动在系统里建一条生产任务填好工地名称、混凝土标号、方量、坍落度、是否泵送等信息。系统根据方量和搅拌车容量自动拆成多车生成车辆计划也就是第几车、几点几分出发、拉多少方、去哪个工地。任务下发给搅拌楼控制室和生产班组的同时司机收到运单消息装料完成、过磅、出站、到达工地这些节点再一步步回传状态。我们在系统里早就有第2到第4步的完整链路缺的就是第1步到第2步之间的自动化。以前这条链路靠调度员人肉中转——看到微信消息然后手工录单。这个环节恰恰是错误率最高的工地名称容易记混老张工地到底是大张庄还是张庄、方量容易看漏、发消息的时间不固定还会造成漏记。1.2 为什么选择微信接收而不是做App或网页端填报项目初期开玩笑说给工地统一装个App算了后来发现根本不现实。工地上的人员流动性大一个施工员手机里有七八个工地的群今天在A项目明天调到B项目你让他专门装一个App注册账号并维护项目关联关系学习成本和使用意愿都极低。而微信早就成了工地对接的默认工具信息本来就在那里。还有个关键原因消息必须由需求方自己发出来才能明确责任。以前调度员手工录单录错了领导和需求方都会认为是系统的问题现在认消息来源谁发的消息就记录谁再配合群里的上下文溯源非常清晰。所以不做独立填报端直接吃掉现成的微信消息用最低的接入成本完成业务闭环。1.3 边界划分哪些消息能自动生成哪些必须先留给人审这个功能不能一上来就想着全自动。我们一开始就定义了准入边界这也是后面解析规则设计的依据能自动处理格式化的加车消息包含工地、标号、方量、时间、变更消息加车/减车/改方量、特定关键词的查询消息。必须人工介入非结构化的口语消息比如明天要多几车这种没头没尾的、备注里带泵送特殊要求但没说明参数的、完全匹配不到工地和标号的消息。必须走审批涉及超大方量比如单次超过某阈值、夜间施工、异常标号的消息直接推到待审核列表不自动落计划。自动化的目的是降低调度员的工作量而不是制造新的不可控因素。把规则的边界划清楚后面解析模块才好写。2. 技术选型对照个人微信、公众号、企业微信到底该走哪条路2.1 个人微信路由功能上可行生产环境别碰最原始的思路是直接接管个人微信的网页版协议拿到消息后往自己的服务端塞。技术圈子里确实有过一些开源的微信机器人方案原理是用个人微信的网页版接口做Hook实现自动收发消息。但这里面有两个迈不过去的坎封号风险极高。个人微信的协议不在官方开放范围内行为一旦被风控识别轻则限制登录重则封号。调度群是生产命脉赌不起。消息可达性没有保障。手机断网、账号被踢下线、微信版本升级导致协议字段变化这套东西随时可能静默失效半夜两三点突然不工作了第二天调度就乱套。所以个人微信这条路从立项第一天就被否了顶多拿它做原型验证不能上生产。2.2 公众号模板消息适合通知推送不适合接收业务指令很多人会想到公众号。公众号确实有官方接口可以接收用户消息也能通过模板消息做通知触达。但在搅拌站这个场景里它有明显的天花板用户必须关注公众号才能发消息工地人员注册关注是个额外门槛。公众号的消息接收能力偏客服场景用户发一条、48小时内客服能回一条本质上是对话窗口不是业务数据通道。模板消息有数量和场景限制不能随便拿来做车辆计划的实时推送。热搜词里有个微信模版消息跳转小程序 miniprogramstate formal说明很多人在这上面研究怎么跳小程序但模板消息做业务表单填写交互还是太重。另外公众号消息回调和用户身份打通都比较麻烦尤其是一个用户可能同时属于多个工地权限映射要自己维护一套。2.3 本地部署变小困难选用企业微信自建应用最终我们选了企业微信自建应用作为接收消息的载体。理由很实际企业微信完全开放。可以注册企业、创建自建应用、配置API接收消息URL所有接口都有官方文档没有协议逆向的风险。人员组织天然匹配。企业微信里可以建客户群、内部群搅拌站可以把每个工地的人拉进对应的企业微信群或外部联系人群发送方和项目的绑定关系天然存在。消息可靠性更高。企业微信官方回调有重试机制应用消息支持被动回复同时还支持主动推送后面做车辆计划已生成的确认通知非常顺手。认证成本低。个人/小企业注册企业微信基本零门槛对于搅拌站这种传统行业很友好。开发成本可控。加解密的SDK官方有做一遍接入后就能长期复用包括后续的司机运单通知、异常消息推送都可以走同一个应用。方案比选如下通道接入成本稳定性身份与工地映射是否推荐个人微信协议低但有风险差封号/掉线风险弱需自维护不推荐公众号模板消息中较好一般需引导关注备选企业微信自建应用中好强天然组织架构推荐3. 企业微信回调接入URL验证、消息加解密的完整链路3.1 接收消息服务器的配置步骤企业微信自建应用的消息接收有两个入口一个是指令回调一个是消息回调。做接收用户消息需要在自建应用的接收消息页面配置URL服务端暴露的一个HTTP接口例如https://your-domain.com/wework/callbackToken自己生成的随机字符串用于签名校验EncodingAESKey43位随机字符串用于消息体加解密配置时企业微信会要求做一次URL验证它往你的URL上发一个GET请求带msg_signature、timestamp、nonce、echostr四个参数你需要用TokenEncodingAESKey对echostr解密把原文返回给企业微信验证通过后配置才生效。这个阶段最容易踩的坑是解密后的echostr不是直接返回的而是要先对收到的密文做AES解密得到明文再把明文的字符串原样返回。很多第一次接入的人在这里直接返回了密文导致URL验证一直失败。另外要确保服务端接口响应时间在5秒以内否则企业微信会判定验证失败。我们当时用的Java开发加解密逻辑直接参考企业微信官方文档给出的加解密库示例核心步骤实际上是用msg_signature校验签名确认是微信服务器发的请求。对echostr用AES解密得到随机串。把随机串返回给微信。由于这套逻辑在不同语言下都有现成SDKJava/Python/Go都有不建议自己造轮子直接集成官方示例代码最省事。3.2 消息体是什么格式XML和JSON的坑企业微信回调推给我们的消息体是XML格式默认长这样xml ToUserName![CDATA[ww1234567890]]/ToUserName FromUserName![CDATA[zhangsan]]/FromUserName CreateTime1710000000/CreateTime MsgType![CDATA[text]]/MsgType Content![CDATA[明天早上六点A工地要40方C35]]/Content MsgId1234567890123456/MsgId AgentID1000002/AgentID /xml拿到XML后如果开启了安全模式则还需要解密才能得到真实的XML内容。解密后的XML字段里对我们有用的核心是FromUserName发送人的企业微信账号ID不能直接看出姓名需要调用企业微信的通讯录接口根据userid换取姓名和部门然后再映射到哪个工地的人。Content消息文本原始信息。MsgId消息的唯一标识用来做去重。CreateTime消息发送时间戳。注意企业微信回调的消息并不是一收到就万事大吉。如果我们的接口返回非200或超时企业微信会按一定策略重试推送。所以接收这个动作本身要做得非常轻——先把消息原样落库落一张wechat_message_raw表返回200给微信然后再异步做解析和业务处理。这样即使后续解析代码出现bug原始消息也不会丢。3.3 身份映射让人和工地对上号企业微信里收到的FromUserName是账号体系里的userid不是手机号也不是姓名。我们做的第一步是根据userid调用企业微信通讯录接口拿成员详情得到姓名、手机号、所属部门。搅拌站的业务组织是部门例如A项目部下面有具体的人而工地往往对应部门或外部联系人标签。我们的映射逻辑是如果发送人属于某内部部门且该部门绑定了工地编码直接用部门ID关联工地。如果发送人不在任何部门则查他所在的外部联系人群通过群属性判断对应哪个工地。两边都没有则后台标记未识别工地消息进入人工审核池。这块逻辑一开始想简单了总觉得把工地的企业微信部门建好就完事。结果实际跑起来发现很多工地是临时车牌、代理商或者施工队他们不在内部组织架构里。后来我们统一规定所有需求方必须由站里业务员拉入各自的客户群然后在这些客户群的群名称上统一加前缀例如AZZ-XX工地解析时直接按群名匹配工地才彻底解决映射问题。4. 从消息到车辆计划解析规则、置信度打分与状态机4.1 先约定消息格式再谈解析理想情况是需求方按照模板发消息比如工地A项目标号C35方量40时间明早6点泵送是。现实是工地上的人压根不会按你的模板输入。他们更多的会发A工地明天要40方C35老地方来两车C30明天上午xx工地加10方急所以我们的解析思路不能是严格格式识别而是一种关键词抽取模糊匹配****置信度判断的混合方案。我们给调度员设计了一个简单口诀并且在群里公示一句话加车把工地、标号、方量说清楚就行越多越好。但对那些不按套路来的消息解析器也得具备容错能力。4.2 解析流程正则、词典匹配与置信度打分解析器的设计分成四层第一层预处理。把全角转半角、去除多余空格、统一大小写、过滤掉你好麻烦谢谢这类语气词然后按标点和换行切分成若干个语义块。第二层字段抽取。用正则表达式把数字和单位先拎出来例如提取\d方或\d\.?\d*\s*(m3|立方米|方)来识别方量提取标号的写法常见的有C15/C20/C25/C30/C35/C40/C45/C50连在一起的c35也要能识别。时间识别更麻烦一些明天早上明天下午后天6点六点明早我们维护了一张时间词表把常见中文时间表达映射成具体时间以当前时间为基准推算。第三层工地的模糊匹配。工地名称是我们业务数据里最头疼的一个环节。同一个项目的叫法有好几种比如万科一标、万科A区、万科一期在车队的嘴里还可能叫老万。针对这种情况我们建了一张工地别名表由调度员维护把系统里的标准工地名称和微信群里出现过的别名都关联起来解析时先走精确匹配精确不到就走子串匹配比如消息里包含万科A能匹配到标准名子串还是匹配不到就按编辑距离/前缀匹配算法给候选列表然后按置信度排序。第四层置信度打分。每个字段的识别结果都有一个置信度方量解析数字单位齐全为高置信度只有数字没单位则中置信度按默认单位方处理。工地名称精确匹配高子串匹配中模糊匹配低。标号能识别到C数字为高识别不到不直接判失败允许默认标号或走人工确认。整体规则是工地必须识别出来高或中置信度才尝试生成计划如果工地识别不到直接进人工审核池方量和标号缺少一个生成草稿计划在审核列表里给出未识别字段标记调度员一键补齐即可。4.3 车辆计划生成一条消息可以拆成多条派车任务这一步是把需求落到计划的地方。消息解析完成后得到一个结构化对象{ projectCode: PJ001, projectName: A工地, grade: C35, totalVolume: 40, requireTime: 2025-03-12 06:00:00, sourceMsgId: 1234567890123456 }接下来系统根据当前搅拌站正在运行的设备产能、罐车数量、单车容量来拆分车辆计划。我们站里的搅拌车一般是12方和15方两种罐容但司机实际拉的方量会受路况、工地高低、限载等因素影响系统中每辆车都有计划方量。拆分逻辑不搞太复杂先按一个基本公式车辆数 ceil(总方量 / 单车平均方量)然后把车辆计划逐条插入计划表每条计划带序号。比如总方量40方、单车平均14方就拆成3车第1车14方、第2车14方、第3车12方。这三车的预计发车时间按顺序递推第一车按需求时间往前推留出生产搅拌和装料的时间比如提前40分钟后续每车间隔按搅拌站生产节拍通常15分钟一车错开。这个拆分规则不是死的。实际操作中经常会碰到工地要求两车一起到那就不能按节拍间隔排需要把这两辆车的到达时间设为相同。我们在计划生成时支持一个同时到达的备注标记如果消息里带了一起同时连发等关键词就把所有车的发车时间统一到同一个时间点搅拌楼生产时集中出料。4.4 状态机从消息到生产任务的完整流转车辆计划不是落库就完事它还要驱动后续的调度、称重、打料、过磅、出场、运单回传等环节。我们给这条链路设计了一个状态机状态编码说明处理动作WAIT_PARSE消息已入库待解析解析服务消费该消息PARSED解析成功待生成计划写计划前校验字段完整性PLAN_GENERATED车辆计划已生成通知调度员确认PLAN_CONFIRMED调度员确认通过正式进入生产任务池AUDIT_REJECTED消息被驳回记录原因人工另行处理PARSE_FAILED解析失败进入人工审核池从WAIT_PARSE到PLAN_CONFIRMED之间任何一个环节都有定时任务做兜底扫描消息进来后超过30秒没有解析就告警超过5分钟没有确认就在调度员的企业微信上再推送一条提醒超过30分钟未处理升级到生产主管那里。这个状态机帮了大忙。因为消息接收是异步的推送时机不可控调试的时候经常出现数据库里有消息但页面没看到计划的情况全靠状态表定位现在是卡在解析还是卡在审核。所以如果你也在做类似的集成强烈建议一开始就把消息的状态字段建全别等上线后再补。5. 可靠性设计重复消息、乱序消息、失败消息的三重兜底5.1 重复消息msgId去重与业务幂等并行企业微信回调有重试机制如果我们的服务端返回失败或者微信在短时间内没收到200响应它就会重推同一条消息。如果不做去重同一条要40方C35的消息可能被解析两次、生成两份车辆计划这在生产上是要出事故的。我们做了两层去重第一层是数据库层面。接收消息时把MsgId设为wechat_message_raw表的唯一索引重复插入直接忽略从源头防止消息重复入库。这招简单粗暴有效。第二层是业务层面。即便同一消息只入库一次解析后生成车辆计划前还要做一次业务主键判断如果同一条原始消息已经生成了计划状态为PLAN_GENERATED以后就不允许再次生成。不要小看这第二层因为第一层只能挡掉完全相同的MsgId但还有一些场景是不同MsgId但内容高度相似——比如发送者在群里发了一次后觉得没发出去又复制粘贴发了一次。这种业务上的语义重复就只能靠同时间、同工地、同标号、同方量的组合条件去查最近半小时内是否有类似计划有就提示合并或确认。5.2 消息乱序时间窗口内的最终覆盖微信消息不发生乱序因为每条消息都是实时推送的。真正可能乱序的是业务状态比如解析服务消费很快但生成计划的异步任务因为数据库锁或者网络问题导致后一条消息先处理完了先发的那条反而后生成。我们用来解决乱序的方法不复杂所有消息处理前都记录一个lastMsgTime字段等于CreateTime。生成车辆计划时如果检测到同一工地上一条消息的下单时间晚于本条则等价于新消息先落地旧消息后落地这时对旧消息只做提示不自动覆盖新计划。如果两条消息确实时间邻近5分钟以内由调度员在审核页面里对比后手动确认结果。其实在设计阶段我们认真考虑过要不要引入全局序号或分布式锁后来发现生产上绝大多数情况用不上过度设计反而增加复杂度。真实场景里信息量是稀疏的同工地同一个小时内一般不会有两条完全冲突的加车消息用时间窗口加人工确认已经足够稳。5.3 解析失败往哪走人工审核池和二次提醒解析器再强也不可能覆盖所有口语表达。我们有一个专门的人工审核页面调度员打开就能看到所有PARSE_FAILED和待确认的消息。每条消息右侧会显示原始消息全文解析出的部分字段哪些成功、哪些缺失操作按钮一键复制字段生成计划 / 手动修正后生成 / 标记为无效消息刚开始上线的时候这个审核列表一天能刷出三四十条调度员觉得反而添乱。后来我们对解析器迭代了两轮把高频表达模式比如再来两车加10方老地方明天早上等全部沉淀到规则库里审核量从三四十条降到一天三五条以内调度员才开始真正信任这套系统。这里有个产品设计上的小心得尽量让审核动作在一屏之内完成不要让人切换好几个页面。调度员在电脑前可能同时在盯砼车、接电话、回微信任何一步跨页面的操作都会增加他的反感和出错概率。我们的审核页面用大卡片展示消息原文和解析结果点一下补全按钮直接生成计划整个操作不超过五秒。5.4 消息丢失的补偿定时巡检和超时预警企业微信回调是推模式没有拉消息的通用接口理论上如果我们的服务在消息推来时宕机企业微信重试几次后也就放弃了消息就会静默丢失。这个风险必须正视。我们做的兜底方案是定时巡检加人工补录兜底系统有一个守护任务每5分钟检查一次企业微信回调地址的访问日志如果发现某个时间段内消息量骤降但业务高峰期比如早上6-8点本应有很多消息就触发告警通知开发人员。同时页面上给调度员一个消息补录入口如果现场发现某次加车需求没有生成计划可以直接把微信群里的原始消息文本粘进来走同一条解析链路生成计划并标记为手工补单这样重复入库、去重、状态流转的逻辑可以完全复用。上线半年多真正因为服务宕机导致消息丢失的事故出现过一次发生在凌晨升级数据库的时候。好在第二天上午调度员巡检时发现早高峰的量不对靠手工补录把5条加车需求补了回来没有影响工地打料。从那次以后我对系统再可靠也要留人工后门这句话有了更深的理解。6. 上线实测总结三个真实意外和现在跑下来的体会6.1 意外一领导手滑发错群上线第一天老板在企业微信的一个客户群里发了一句明天XX路工地要加10方C40本来是想发给另一个调度的结果消息直接进了我们解析服务系统自动生成了一条车辆计划。那天调度员看到计划的时候一脸懵因为XX路工地根本没有需求。查了半天才发现是老板发错了群。从那以后我们加了一个规则只解析指定群/指定部门的消息其他来源一律不自动生成计划。企业微信回调里能拿到群聊标识ChatId我们建了一个允许自动生成计划的白名单群列表。不在白名单里的消息即使字段全齐也只进审核池不自动落计划。6.2 意外二标号的行业黑话有工地的人发消息说C35P8其实表示的是C35混凝土P8是抗渗等级。我们的第一版解析器直接用正则提取了C35但把P8漏后面了。结果计划生成后生产部门的人看到计划里只有标号没有抗渗要求以为是普通C35差点按普通标号打了。这个坑提示我们标号解析不只是匹配C数字还得处理后缀比如P抗渗、F抗冻、膨胀剂、纤维等。我们一开始纠结要不要把P8解析到备注字段后来干脆建了一个concrete_requirement字典表把所有常见的附加需求关键词映射到生产备注里。解析时先抽标号再顺带扫描附加需求词命中就自动写入计划的备注列。6.3 意外三emoji和语音消息一开始忽略了Emoji问题。有工人发消息习惯带个OK表情或大拇指这些Emoji在Content里是有实际字符的会让正则匹配部分失效。后来在预处理阶段直接把Emoji字符连同表情符号全部过滤掉再进入解析逻辑。语音消息是另一个坑。企业微信回调的消息类型默认是text如果用户在群里发语音MsgType会变成voiceContent里只有媒体ID没有文字转写结果。我们的处理是语音消息一律不解析推给调度员一个该消息为语音请手动处理的提示。技术上讲可以通过企业微信的会话存档或ASR服务把语音转成文字但考虑到成本和必要性我们暂时没有做先把文本消息跑稳再说。6.4 效率提升的实测算账这里可以给一个实际数据参考。以前调度员处理一条加车消息的平均耗时大约3到5分钟要看群、读消息、开系统、录单、排车高峰期同时来三四条消息基本就手忙脚乱。上线这个功能后格式化消息从进来到生成待审核计划的时间是8秒左右调度员只需要看一眼审核列表、点确认单条处理周期压到30秒以内。更重要的是由于消息原文和生成结果强绑定以往常见的记错工地名称少录10方标号看错这类人为出错大幅下降。当然这不是说系统全对而是即使出错了也能很快反查到是哪个群、哪个人、什么时候发的原始消息。现在这个功能上线稳定运行后我又把同样的消息驱动模式复用到两个新场景一个是司机端的运单回传确认司机在企业微信里发一句到工地卸完系统自动更新车辆状态另一个是实验室的试块提醒试验员在群里发一组试块编号系统自动按龄期推送压试提醒。底层都是同一套消息接收-解析-业务映射-状态流转的骨架区别只在于解析规则和业务表结构不同。最后做一个经验性收尾。这个模块做下来我最大的感受是集成微信消息这类功能七分在业务规则设计三分在技术实现。技术上的回调接入、加解密、消息去重都是标准操作真正决定项目成败的是你能否把工地人员五花八门的表达习惯收敛成一套可管理的规则能否把解析失败、消息丢失这类异常情况设计成可人工接管而不是彻底卡死能否让调度员从录入员平稳过渡到审核员。如果你所在的项目也有类似的场景建议先从最核心的一条消息类型做起来跑通之后再把解析规则慢慢喂大别一上来就追求全自动全覆盖。系统的信任是积累出来的每天少录几条、少错几单调度室的同事自然就会把它当成真正的生产工具。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →