四轮外部评审,十二条意见全部属实——afterCommit 不是异步任务
人脸审批钉钉卡片功能做完后,交给外部评审挑刺。四轮下来共十二条意见,我们逐条核实——全部属实,然后逐条修复。评审意见全对这种事并不常见,更难得的是其中几条属于”教科书不会讲、不炸一次不会信”的坑:afterCommit 根本不异步、vant Toast 是单例、surefire 版本太老会让测试”假装通过”。逐条记录。
人脸审批钉钉卡片功能做完后,交给外部评审挑刺。四轮下来共十二条意见,我们逐条核实——全部属实,然后逐条修复。评审意见全对这种事并不常见,更难得的是其中几条属于”教科书不会讲、不炸一次不会信”的坑:afterCommit 根本不异步、vant Toast 是单例、surefire 版本太老会让测试”假装通过”。逐条记录。
一天之内,智慧园区平台的钉钉通知从”一种告警卡片”扩成了”五张卡片”的体系:告警卡深链到 H5 办理页,人脸审批卡带上申请事由,视频申请审批卡深链到 PC 列表。上线前把全景彻底捋了一遍——线上代码里所有会发钉钉卡片的场景一共五种,再无其他。这篇记录体系的设计、深链的取舍、旁路发送的架构,以及那份”明天照着测就行”的排障手册。
需求一句话:站内信要能点——实时监控申请、回放申请、入侵告警工单这三类消息,申请人、审批人、处理人收到后点击,要跳到对应的列表页。一期用前端硬编码映射快速落地,二期按上级要求改成流程配置驱动。真正值得记录的是二期定下来的那条核心语义——**jumpRoute 字段始终携带,空串等于明确不跳转、绝不回落旧映射**——以及它背后被评审揪出来的盲区。
现场在发布管理页面提交了一次文字插播,随后抛来两个问题:这次到底成没成功?为什么在 ROMA 平台的设备命令日志里搜不到任何记录?第一个问题要证明”发生过”,第二个问题要证明”本来就不该有”。这次排查的价值不在结论本身,而在于用代码、配置、进程、日志四级互相独立的证据,把一次成功钉死,同时给”查不到日志”一个经得起追问的解释。
测试同事反馈:在钉钉里打开 H5 微应用,免登之后没有进入应用,而是停在账号密码登录页。更诡异的是,刷新一下有时又能进去;主动退出再打开,又可能卡住。最后查出来的真相是:钉钉授权和后端登录其实都成功了,token 已经拿到手,是 H5 自己在登录成功之后抛出异常,把用户卡在了门外——一种”半登录态”。
BPM 审批流要跑起来,地基不是流程图,而是一个看起来朴素的问题:给一个员工 ID,算出这张单该批给谁。这次改造把原先”部门管理员”的散装机制整体角色化,落地两个负责人反查接口。中间有三个决策值得展开:主部门锚点的回填、递归 CTE 的”最近一级优先”、以及同步绑定与手动绑定的免疫隔离。
园区平台的信息发布模块,同一天在同一个接口上连续爆出两个不同的 500:下午五点半一个 NullPointerException,二十分钟后一个 DateTimeParseException。表面是两个不相干的异常,根因却是同一个模式——前端把非法值带进了请求体,后端在关键路径上既没有校验也没有空值保护,@Transactional 回滚,数据未落库。这篇记录两个 bug 的完整触发链,以及那条最值得记住的结论:前端校验的对象,和最终提交的对象,根本不是同一个。
告警有个期限,到期没人理,级别就往上抬——需求一句话就说完了。但落地时它分裂成两条业务线:人工确认线的期限在告警侧(告警产生时刻 + N 分钟),自动工单线的期限在 BPM 侧(审批节点配置的处理时限)。两版方案迭代下来,最终的形态是一条 UPDATE 语句覆盖两条线,而中间踩掉的几个坑,比 SQL 本身更值得记录。
把告警系统和 BPM 工单打通,听起来是”调一个发起流程的接口”就能做完的事。真正做起来才发现,难点从来不在发起,而在失败的那一瞬间:远程流程已经发起成功、本地事务却回滚了,那条没有告警指向的孤儿工单谁来收?本文记录这次改造里三个相互咬合的设计——契约、补偿、防线——以及一个”什么都没发生”的典型 bug。
园区平台的消防设备地图功能上线那天,楼层平面图一张也显示不出来。从 dev 联调一路追到生产 Nginx,最后发现这不是一个 bug,而是三层断点叠在一起:库表迁移没跑、前端 env 缺配置、Nginx 缺路由。任何一层单独修好,页面依然是坏的——这大概是”新功能第一次把文件服务暴露到浏览器侧”时最典型的事故形态。