FindMyGrocery:基于LBS的智能杂货购物App实战解析
如果你有过周末临时想做饭、却为了几样配菜跑了三家超市的经历你应该能理解我为什么要做 FindMyGrocery。这个名字拆开看其实很直白——Find 是“找”My 是“你的”Grocery 是“杂货生鲜”合起来的意思就是帮你最快找到想要的杂货。它的核心定位是一款基于位置的智能杂货购物应用打开 App 输入你想买的商品它能告诉你附近哪家店有货、哪家店更便宜、路线怎么走最顺甚至预计几点到店不用排长队。这篇文章不打算写官方式的项目说明书而是把这个产品从想法到 MVP 拆开来讲包括功能设计取舍、核心技术点、落地时踩过的坑。如果你正在做生活服务类 App或者对“地图 本地数据 推荐”这类产品从 0 到 1 感兴趣这篇应该能给你一些真实参考。1. 整体设计思路与需求拆解1.1 用户到底在为什么事情头疼做任何产品之前我都习惯先列“痛到必须解决”的场景而不是先想功能。FindMyGrocery 最开始源自一次真实的糟糕体验——家里缺酱油和鸡蛋我先后跑了家附近的两家连锁超市第一家没货第二家有货但排队排了二十分钟。回来后我在想如果有个东西能让我打开就看到附近哪家有货、哪家不用排队是不是就能少浪费这一小时。把这类琐碎吐槽收敛成需求其实就三条不知道哪里有货导致白跑一趟不知道哪家便宜导致买贵了不知道门店当前的营业状态和拥挤程度导致时间白耗。这三条对应到产品功能上分别是实时库存查询、价格比较、门店状态展示。这里有个很重要的产品判断第一版不能功能铺太开我把范围死死锁在“帮用户找到一个有货且价优的店”其他所有功能都排到二期以后。很多同类产品死掉不是因为功能太少而是因为哪个都想做最后哪个都做不透。1.2 为什么选杂货品类又为什么走数据聚合路线选杂货这个品类不是拍脑袋。杂货生鲜是消费频次最高的线下零售品类用户一周至少会买一两次而且决策很轻——就是“今天吃啥、缺啥、下楼买”。这种高频、轻决策的场景最适合用来验证 LBS 类工具产品的可行性。相反如果一开始做服装、数码这种低频高决策品类用户根本想不起来打开 App冷启动很难做起来。市面上不是没有生鲜电商、商超到家 App但它们解决的是“线上下单、送到家”跟 FindMyGrocery 解决的根本不是同一类问题。FindMyGrocery 的价值在于聚合和导航它不做配送也不自己备货而是把城市里现有的线下零售信息结构化。基于常见实践来看这类产品的核心竞争力不是供应链而是两个数据能力一是门店商品数据能不能及时拿到二是检索结果的排序能不能让用户真的满意。想清楚“不做什么”比“做什么”更重要。做线下数据聚合的人很容易被商家拉着做电商代运营、做配送这些诱惑我全部挡住了。1.3 核心功能优先级排序我用的判断方法很朴素把每个候选功能放进“用户使用频率 × 影响大小 × 实现成本”三个维度里打分。第一版最终只留下四个功能附近门店搜索按距离和商品关键词商品库存查询有货 / 少量 / 无货价格对比同一商品在不同店的售价购物清单联动地图一键生成到店顺序。至于店铺会员积分、优惠券推送、社区 UGC 评价一期统统不做。理由很简单MVP 阶段最重要的是验证“用户愿不愿意为了知道哪里有货而打开这个 App”其他都是后话。后来数据也证实了这个判断早期留存用户里使用频率最高的就是这个“查询—到店”闭环而评论、积分这类功能几乎没人点。2. 核心技术点解析与方案选型2.1 地理检索不只是“找附近的店”FindMyGrocery 第一个绕不开的技术点是“附近门店搜索”。听起来简单就是按经纬度算距离但真做起来有讲究。我选型时对比了 GeoHash 和 PostGIS 两种方案最终选了 PostGIS原因是GeoHash 对“矩形范围查询”很友好但做“按半径圈选 按价格过滤 按距离排序”这类组合条件时自己拼字符串前缀反而麻烦。用 PostGIS 的话一个 SQL 就能搞定SELECT id, name, address, ST_Distance(geom, ST_SetSRID(ST_MakePoint(116.4074, 39.9042), 4326)::geography) AS distance FROM stores WHERE store_type grocery AND ST_DWithin(geom, ST_SetSRID(ST_MakePoint(116.4074, 39.9042), 4326)::geography, 5000) AND price_level IS NOT NULL ORDER BY distance LIMIT 20;这段 SQL 的意思是以某个坐标为圆心取半径 5 公里内的杂货店按距离升序取前 20 家。ST_DWithin 天然走空间索引5 公里内查询耗时基本在几十毫秒级别。真正的坑在数据入库前的坐标清洗商家填的门店地址是文本必须靠地理编码服务转成坐标而中文地址经常出现“XX 路与 XX 路交叉口”“XX 大厦 1 层”这类非标准写法直接转换很容易偏出几条街。所以我在入库前会加一层地址清洗脚本先做行政区划匹配再做关键词补偿最后把明显不在城市范围内的坐标打回人工复核。2.2 库存数据实时同步的难点与折中如果说定位是骨架库存数据就是 FindMyGrocery 的血液。但这块是整个项目里最麻烦的环节因为线下超市的库存不像电商那样天然有开放接口。基于常见实践可落地的接入方式大致有三种。第一种是接入连锁商超的开放 API。现在不少大型连锁有供应链数据合作能拿到指定门店的 SKU 级库存但接口限流严重通常只能覆盖高频 SKU不可能全量同步。第二种是电子价签或收银系统的数据合作精度高但需要门店部署对应硬件前期覆盖范围很难铺开。第三种是众包采集用户到店后在 App 里点击“有货 / 无货”标记再用算法平滑处理多用户反馈精度一般但冷启动时最现实。我的方案是“API 为主、众包兜底”对头部连锁店走 API 定时同步对中小门店开放店员和普通用户上报入口后台再用投票权重法把冲突标记做聚合。所谓投票权重法比如同一个商品 10 分钟内出现 5 条“有货”和 1 条“无货”我会结合上报者的历史准确率加权得出一个“高概率有货”的置信度。这套逻辑单独看并不复杂但它决定了后续排序和状态展示的可靠性。2.3 价格比较让数据在同一坐标系里可比价格对比的坑比想象中多。同一瓶酱油可能有“标价”和“会员价”两种同一款酸奶有“单瓶价”和“整排价”。如果直接比数值用户会骂数据不准。我在这层做了一套标准化处理每个 SKU 维护一个单位基准价也就是元/100ml 或元/100g展示时优先显示基准价同时保留原包装价。举个例子A 店卖某洗发水 750ml 装 39.9 元基准价 39.9 / 7.5 5.32 元/100mlB 店卖同款 500ml 装 29.9 元基准价 29.9 / 5 5.98 元/100ml。这样用户一眼能看出哪家更划算。为了防止用户拿便利店和大型超市比价来抬杠我还在排序规则里加入了 store_type 权重同类型门店之间优先比价跨类型比价只做参考展示。这套规则花了一天写出来却把“比价”从噱头变成了可用功能。2.4 购物清单到路线规划的联动逻辑清单功能本身不难但 FindMyGrocery 把它跟地图联动起来就有意思了。用户在清单里勾选商品后系统会做两件事一是按商品匹配各门店库存找出“能一次覆盖最多清单项”的门店组合二是对选中的多个门店做路径排序避免走回头路。第一个能力听起来很像算法题里的集合覆盖问题但实际场景门店数量不大单次覆盖最多几十家所以早期我用穷举就能算。门店多了以后换成了贪心策略先选覆盖商品数最多的店再在剩余商品里重复选店直到清单覆盖完或者用户手动停止。实测下来贪心策略对 5 到 8 家门店规模的场景已经足够快也足够解释了。第二个能力直接调用地图服务的多点路径规划接口传入门店坐标序列拿回一条优化过的顺序即可。真正需要自己处理的反而是“用户指定某家非去不可”这类硬约束系统得手动把这家店往路线里插。3. 实操过程与核心环节实现3.1 冷启动阶段的数据从哪来项目刚启动时最怕的就是“App 里什么都没有”。我给自己定的目标是先覆盖一个中等城市的三个核心商圈大概 50 家门店。这个阶段没什么高级手段就是拼执行。第一人工采集头部连锁的门店基础数据地址、电话、营业时间这些公开信息直接从官网和地图服务整理同步没有捷径。第二通过商超电子版促销海报抓取 SKU 和促销价。很多超市每周都会发电子海报我写了个解析脚本把海报里的商品名、规格、价格提取出来入库。这个方法很笨但能在一两周内积累几千个有效 SKU。第三拉早期用户做上报。上架第一周我找了 20 个朋友当体验官让他们去超市买东西时顺手标记“有货 / 无货”两周回传了 300 多条标记用来校准置信度足够了。做数据活性还有一个很关键的动作SKU 归一化。超市海报里“海天生抽 500ml”“海天味极鲜 500ml”可能是不同条码但用户搜索“海天酱油”时两个都得能出来。所以我单独建了一张 SKU 别名表把同义词、品牌词、规格词拆开维护搜索时做同义词扩展。3.2 后端接口设计与数据模型后端我用了典型的单体服务架构接口按业务领域拆分没有一上来就上微服务。核心表结构大约五张门店表 stores、SKU 表 skus、门店库存表 store_inventory、商品价格表 product_prices、用户上报表 user_reports。其中 store_inventory 是最高频的查询表我做了 (store_id, sku_id) 联合索引并且把库存状态用 0/1/2 枚举表示 无货 / 少量 / 有货而不是存一个浮动的数字。数字看起来很精确但小样本上报场景下反而容易误导枚举再配合置信度字段展示逻辑反而干净。接口设计上我总结出一个原则接口返回的数据一定要“帮客户端少算一次”。比如门店列表接口除了返回距离还会把一个已经算好的 display_distance 字段一并返回避免客户端重复计算和格式化门店详情接口直接在服务端把营业状态 open / closed 算好再返回。少让客户端做判断线上错误就少一半。3.3 移动端地图交互的取舍移动端我选的是 Flutter 跨平台方案一个原因是开发效率高另一个原因是后续想上 Web 端能省点事。地图组件用的是高德地图 SDK在 Flutter 里通过平台通道封装了一层。界面交互上我坚持一个原则首屏只做一件事——输入商品名看地图上的结果点。筛选、排序、历史记录全部收起。因为用户打开 App 的动机是“找东西”任何多余的入口都会稀释这个动机。地图上的门店气泡会显示三样信息距离、“有货 / 无货”状态、相对最低价标识。这三个信息是经过真实用户访谈验证的最初我在气泡上放了门店评分和评论数结果用户根本不看。他们只关心“有没有、贵不贵、远不远”。评分和评论还是放进详情页更合适。3.4 推荐排序怎么调才靠谱“Smartest Way”这个定位最终要落在排序逻辑上。我的经验是不要用单一指标排序而是用混合打分score 0.4 × 距离分 0.35 × 库存置信度分 0.25 × 价格分距离分用指数衰减5 公里外基本趋近于 0价格分用固定区间映射价格越低分越高库存置信度分就是前面说的上报投票加权结果。这样用户看到的结果一般是“近且大概率有货”而不是“最便宜但未必有货”。这套权重不是拍脑袋定的。第一版我把价格权重调成 0.4结果出现了大量用户点击后发现门店太远、直接流失的情况。调成现在的比例后到店转化率明显上升。排序权重这种事不同城市、不同品类都会有差异需要有专门的实验埋点来持续调整。4. 常见问题与排查技巧实录4.1 坐标偏移导致“5 公里内找不到店”我第一次全量导入门店数据后发现不少用户反馈“明明旁边就有超市App 里却没有”。排查下来是地址转坐标的精度问题中文地址里“某路 100 号”被地理编码服务解析偏差了好几百米导致 ST_DWithin 计算时门店被排除在 5 公里圈外。解决思路分两步先对门店坐标做人工抽样复核抽样比例不低于 20%再把地理编码服务换成带“地址补全”能力的那款把短地址先补全成标准地址再转坐标。从那以后我养成了一个习惯凡是依赖外部坐标数据的业务入库前必须写一个坐标合理性校验脚本判断经纬度是否落在对应行政区范围内越界的直接进人工复核池。4.2 库存状态“有货”却总是扑空早期用户经常抱怨App 上写着有货到店一看是空货架。这个问题实际上很难 100% 避免因为库存本来就是实时变化的任何数据源都有滞后。我能做的是让“有货”这个状态更保守——置信度低于阈值的 SKU 一律不显示为“有货”改显示“可能有货”。另外在详情页加了一个“更新时间”让用户看到这条信息是 5 分钟前还是 3 小时前的。信息透明本身就是一种信任建设这个策略上线后投诉率降了不少因为用户不会再把“信息滞后”归因于“App 骗人”而是理解为“情况在变化”。4.3 商家不配合数据源接不上这是整个项目里最现实的问题。中小商家对“开放数据”这件事没有概念比较有效的办法是用利益换合作——在 App 里给商家提供免费的“门店客流预估”和“商品热度榜”作为交换。实际操作中我帮一家店做了周边竞争分析报告老板直接同意提供价签数据给我做测试。数据合作这种事讲技术不如算账让对方看到“你能帮我带来什么”比讲一百句愿景都管用。4.4 空间查询性能慢门店数据量到 10 万级之后不带索引的空间查询会慢到不可接受。我做性能优化时做了三件事第一确认所有空间查询字段都建了 GIST 索引过滤条件用 ST_DWithin 而不是 ST_Distance第二按城市维度做分区查询时先按城市裁一次范围第三对热门查询结果做 5 分钟 Redis 缓存比如“北京国贸周边 2 公里内门店列表”这种高频查询直接走缓存数据库压力能减少一大半。4.5 问题速查表问题现象可能原因排查思路解决措施门店搜不到坐标偏移 / 地址编码失败检查原始地址与坐标的行政区域是否匹配地址补全 人工复核池库存显示不准数据滞后 / 上报冲突看更新时间与置信度状态保守化 显示更新时间距离算错坐标系混用检查 WGS84 与 GCJ02 是否统一全链路统一坐标系比价不公平规格不同检查是否用基准价比较单位基准价 类型权重列表加载慢无索引 / 缓存击穿看慢查询日志GIST 索引 Redis 缓存4.6 三个从实战里得来的心得地图类产品最容易被低估的是数据清洗“脏数据进、脏数据出”后面所有算法都会被带偏。所以别为了技术炫技引入重型组件早期我一度想用实时流处理框架做库存同步后来发现定时任务加消息队列就够用了。还有所有展示给用户的信息都要有对应的数据来源和时间戳否则出了问题连排查入口都没有。这几条是我被用户反复质疑数据准确性之后才真正刻进脑子里的。5. 后续扩展方向与商业化思考FindMyGrocery 做到这个阶段已经有能力往两个方向延伸。一个是智慧购物清单结合用户历史购买记录做周期性提醒比如“你家的洗衣液估计快用完了”再把周边超市的线上优惠券导流进来形成闭环。另一个是面向商家的数据增值服务把脱敏后的周边客群画像、商品热度趋势做成月度报告。这条路我很看好因为商家确实需要这些数据只是以前没有人用这种角度帮他们整理。技术上也可以再往前走比如引入图像识别做货架商品识别用户拍一张货架照片就能自动识别缺货情况省去手动上报的成本。我研究过可行性难点在于货架环境复杂、商品外观相似准确率短期很难达到商用级别所以大概率会先用众包标记来过渡。最后再分享一点个人体会像 FindMyGrocery 这种偏线下数据的产品最怕的不是技术难度而是对线下世界复杂性的低估。你永远想不到一个门店的营业时间会随着节假日怎么变也想不到同一件商品在不同门店的条码可能不一样。我的做法是保持简单小步快跑每个版本只验证一个核心假设把数据质量当成第一优先级来盯。做这类产品会有大量琐碎的数据维护工作但每当你看到用户因为 App 少跑了一次冤枉路又会觉得这些东西值回票价。如果你也打算做类似方向建议先挑一个细分品类、一个城市扎下去把一个闭环跑通再谈扩张。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →