当前位置: 首页 > news >正文

AI 软件开发实战教程(十):把微信群消息变成结构化发布

“AI 软件开发实战教程”系列第 10 篇:把“八点左右”“现在出发”等群聊表达转换成可计算的时间契约,并用 TDD 完成发布、查找、详情和安全群文案。

邻行最初面对的是这样的消息:

【车找人】 【时间】明早 8:05 【路线】万科~环普/中软/阿里/华为

它对熟悉小区的人很好理解,对系统却不够明确:

  • “明早”是哪一个日期;
  • 8:05 是必须准时,还是前后都可以;
  • 发布者准备等到什么时候;
  • 斜杠分隔的是多个终点,还是大范围方向;
  • 消息被重复发送时,是新需求还是同一次点击。

K2 的任务不是给微信群加一个更漂亮的输入框,而是把这些含义变成可以保存、筛选和验收的产品事实。

时间不是一个字段

每条出行信息至少需要四个时间事实:

预计出发时刻 可出发窗口开始 可出发窗口结束 停止产生新候选的截止时刻

只填写预计时间时,邻行默认:

预计 08:20 可出发 08:05–08:35 匹配截止 08:05

这里“匹配截止”不是最晚出发时间。它表示系统从该时刻起不再形成新候选,让用户还有机会改乘其他交通方式。

上一篇产品审阅中曾出现一个矛盾表达:“可出发到 08:35,最晚等到 08:20”。正式规则把含糊的“最晚等到”拆成两个概念后,页面也不再使用那句话。

先用纯函数固定所有边界

时间规则不应该先藏进表单或数据库模型。K2 先写一个没有数据库副作用的build_schedule,输入当前时刻和用户选择,输出完整时间契约。

测试固定当前时间为北京时间 08:00,覆盖:

  • 预计 09:00,得到 08:45–09:15,截止 08:45;
  • 预计 08:10,距离不足 15 分钟,截止等于 08:10并标记时间紧迫;
  • 选择现在出发,窗口为 08:00–08:15,截止 08:15;
  • 把现在出发窗口延长到 08:30,未手改截止时一起延长;
  • 用户单独提前截止时不再自动覆盖;
  • 预计时刻在过去、窗口不包含预计时刻、截止已到或晚于窗口结束时拒绝。

第一批测试因为apps.trips尚不存在而失败,随后才创建规则模块。

这种做法让时间错误可以精确定位。否则一个浏览器测试只会说“发布失败”,很难判断是时区、表单、数据库还是页面脚本造成。

“现在”不能保存成一个会变化的词

用户点击“现在出发”时,数据库不会保存字符串now

服务在提交瞬间读取一次服务器时间,保存确定的带时区时间戳:

window_start = 提交时刻 expected_at = 提交时刻 window_end = 提交时刻 + 15 分钟 matching_deadline = 提交时刻 + 15 分钟

以后打开详情,看到的是当时保存的具体时刻,而不是每次刷新都向后移动的“现在”。

数据库统一保存 UTC,日期和页面显示按Asia/Shanghai解释。测试也使用带时区的固定时刻,避免依赖运行机器的本地时区。

车找人和人找车不是同一张随意表单

两种信息共享日期、时间和路线,却有一个关键差异:

  • 车找人必须填写正整数座位;
  • 人找车不能提交座位。

这个规则同时存在于三层:

  1. 表单给出可修复错误;
  2. 发布服务拒绝绕过表单的非法调用;
  3. 数据库 CheckConstraint 保证车主座位大于零、乘客座位为空。

只在页面隐藏座位框是不够的。旧页面、手工请求或后续代码都可能继续提交值,服务和数据库仍要守住同一个事实。

地点必须来自社区词表

首版不采集自由文本说明、联系方式或精确住宅地址。

管理员维护大范围地点,例如:

  • 万科悦城;
  • 软件新城;
  • 国际医学中心。

地点显式属于社区,停用后不能用于新发布。表单使用封闭选择控件,服务再次检查起终点都属于当前用户社区、仍然启用并且不能相同。

地点别名模型也已经建立,管理员可以把“中软”“软件新城”等输入习惯整理到标准地点。当前社区地点很少,页面先使用原生封闭选择;大量地点时的搜索体验留到完整 UI 验收增强,但不能为了搜索方便改回任意文本。

重复点击不能创建两条信息

移动网络中,用户可能双击按钮、返回重试,浏览器也可能在超时后重新提交。

发布页每次展示时生成一个随机幂等键,提交时和用户一起形成数据库唯一约束:

unique(publisher, idempotency_key)

第一次请求创建稳定 UUID 信息;后续相同请求返回原信息。服务测试连续执行两次相同命令,证明数据库最终只有一条记录。

幂等键不是短编号。短编号是每条信息独立生成的四位随机辅助标识,用于群里讨论“哪一条信息”,不能稳定追踪同一个发布者。

大厅查询本身就是状态规则

默认大厅只显示:

同一社区 状态有效 当前时间早于匹配截止 当前时间早于预计出发

后台过期任务即使晚运行,查询也不会把已经截止或已经出发的信息继续当成可联系内容。

成员可以按日期、类型、起点、终点和本地时间范围筛选。首版没有“方向”控件,也没有关键字搜索。路线兼容关系属于后续候选规则,不应该变成另一个含糊筛选入口。

详情链接使用随机 UUID,并在查询时同时限制社区。知道另一个社区的 URL 也只会得到 404;链接本身不绕过登录和社区资格。

群文案是主动分享的结构化快照

发布成功后,页面生成可以预览和手动复制的群文案:

【车找人】 【日期】2026-08-03 【时间】08:05–08:35 【路线】万科悦城 → 软件新城 【座位】2 个 【状态】正在寻找 【编号】A7K2 【详情】https://example.com/trips/...

文案只包含用户已经主动选择公开的大范围信息。它不包含登录账号、微信号、喵码、精确地址或自由说明。

详情页 Open Graph 描述也遵守同样边界。自动测试同时扫描 HTML、群文案和浏览器页面,而不是只检查数据库字段。

浏览器连续发现三个真实问题

HTTP 测试通过后,Playwright 在 375×812 视口执行:

登录 → 打开发布页 → 选择车找人 → 填写日期、时间、地点和座位 → 发布进入详情 → 检查群文案无敏感资料 → 返回大厅看到刚发布的信息

第一次失败发生在发布类型。代码定义的是普通 ChoiceField,模板却把它当单选卡片迭代,浏览器实际得到没有正确标签的下拉选项,提交提示类型必填。将字段明确改为 RadioSelect 后修复。

第二次失败发生在路线标题。视觉箭头被标成读屏忽略,辅助技术只读出两个地点,没有表达方向。页面增加视觉隐藏的“到”,可访问名称变成“万科悦城 到 软件新城”。

第三次失败更接近业务:默认大厅一直为空。无查询参数时筛选表单处于未绑定状态,旧代码只在form.is_valid()为真时执行查询。修正后先执行默认开放信息查询,只有用户实际提交且筛选有效时才覆盖。

这些问题都不是增加更多单元测试能自然发现的。真实浏览器把表单语义、无障碍树和完整页面数据流连在一起。

如何更新验收状态

K2 完成后,自动通过的场景包括:

  • 车主和乘客结构化发布;
  • 默认窗口、截止、紧迫边界和过去时间拒绝;
  • 重复发布幂等;
  • 五类大厅筛选且没有方向控件;
  • 现在出发及窗口联动;
  • 发布表单和群文案没有自由文本与联系方式。

仍然不能提前关闭:

  • AC-12 的查询部分已通过,但 K6 还要把状态正式落为过期;
  • AC-41 已证明交换前只显示角色和随机短编号,交换后的微信号权限等待 K4;
  • 真实提醒、WebKit 和微信真机仍未验证。

任务完成不等于所有相邻能力都已经完成。

本节点结果

K2 收口时:

  • 65 个非浏览器测试通过;
  • 分支覆盖率 90.24%;
  • Ruff、格式、mypy strict、迁移和 Django 检查通过;
  • V0、K1、K2 共三条 Chromium 用户旅程通过;
  • 发布详情和群文案不包含测试登录账号、微信号或喵码;
  • WebKit、PostgreSQL 实跑和微信真机限制继续如实保留;
  • 结果只形成本地提交,不推送。

写在最后

把群消息结构化,不是把一段文字拆成几个输入框。

真正的工作是消除会影响匹配和提醒的歧义,同时不采集产品根本不需要的个人信息。日期、时间窗口、截止、地点、座位和状态都必须有单一含义,并能被数据库、查询和页面共同验证。

下一篇进入 K3:当一条车找人和一条人找车先后出现时,系统怎样用明确规则形成“可能同路”的候选,保存可解释原因,并为双方创建同一个业务事件下的两条站内提醒。

关键代码与操作

下面的简化测试固定同一个幂等键连续发布两次,核心断言是数据库最终只有一条信息:

deftest_repeated_publish_returns_one_trip(user,command):first=publish_trip(user=user,command=command,now=NOW)second=publish_trip(user=user,command=command,now=NOW)assertsecond.pk==first.pkassertTripPost.objects.count()==1

验证命令:make bugfix TEST=tests/trips/test_publishing.py::test_repeated_publish_command_returns_one_trip

这类测试比只禁用提交按钮更可靠,因为重试请求和旧客户端仍会经过服务与数据库。

本篇验证摘要

  • 日期、预计时刻、可出发窗口和匹配截止被保存为含义明确的时间事实;
  • 车主座位、社区地点和起终点规则同时由表单、服务与数据库约束保护;
  • 发布使用幂等键,重复提交只返回同一条信息;
  • 大厅和详情从查询层限制社区与有效状态,不能靠隐藏模板代替权限;
  • 移动浏览器已经走通发布、详情、群文案和大厅回看,文案不包含账号或联系方式。

附录:相关工具与仓库

gstack

仓库:garrytan/gstack
地址:https://github.com/garrytan/gstack

dev-harness

仓库:Dev-Wiki/dev-harness
地址:https://github.com/Dev-Wiki/dev-harness

UI UX Pro Max Skill

仓库:nextlevelbuilder/ui-ux-pro-max-skill
地址:https://github.com/nextlevelbuilder/ui-ux-pro-max-skill

http://www.cnnetsun.cn/news/4153790.html

相关文章:

  • 5 分钟给页面加上开源图标库:Remix Icon 引入与定制教程
  • CSP-J初赛通关指南:从进制转换到栈队列的算法思维构建
  • Agently框架:从零构建可工程化的大模型智能体应用
  • 新手小白学习计算机的第十二天(老王专场)
  • AI又“幻觉“了?我在提示词发布流程加了一道“安检门“
  • GetQzonehistory:免费完整导出QQ空间全部历史说说,三步完成备份
  • 如何用 REFramework 做出你的第一个 RE 引擎游戏 mod?搞懂这 3 层架构就够
  • 太阳黑子预测:物理约束驱动的时序建模方法
  • 从克隆到即时生效:COM3D2 实时编辑器 Maid Fiddler 完整使用指南
  • 拉格朗日乘数法原理与Python实战:从KKT条件到资源优化调度
  • Java面试进阶:从JVM到微服务的核心技术解析
  • KKCE: 网站测速、TCPING、在线ping、DNS查询-快快测
  • Flowable工作流引擎适配达梦数据库:从原理到实战的完整指南
  • 大厂技术面试:核心准备与应对策略
  • Agent 双入口 AI 短剧平台解析:人机协同创作工具怎么选
  • 等变多智能体强化学习在车路协同交通优化中的应用与实践
  • AI智能体驱动仿星器多目标优化:自主搜索有限β平衡的工程实践
  • 最小二乘法原理与实战:从数学建模到美赛应用全解析
  • 树莓派 FFmpeg 实战笔记(二):命令行、源码编译与硬件编码的真相
  • Vue3 后台管理选型:vue3-antd-admin 这套模板能省多少事
  • 告别几KB龟速!2026百度网盘加速全教程:PanDownload直链解析突破限速
  • 案例驱动的多智能体框架:破解电商搜索相关性难题
  • 技术写作中AI的陷阱与人工主导的高质量内容生产流程
  • 区块链运维实战:从国赛题目解析到企业级部署与监控
  • 大模型应用新范式:训练接口层实现跨模型性能迁移
  • 慧知租车换电开源SaaS平台:一套可私有化部署的换电租车一体化解决方案
  • 微信聊天记录导出与个人数据管理:开源项目「留痕」实用指南
  • AI电商工作台:从商品建档到批量生成营销素材的技术实现
  • 智能体编程的上下文工程:从Mise en Place哲学到高效AI编码实践
  • 华为昇腾算力实战指南:从CUDA迁移到国产AI芯片的完整路径