小i约球诞生记02:做附近球友匹配时,LBS 能力怎么选

本文是「小i约球诞生记」系列文章之一。

小i约球是一个微信小程序,主要帮乒乓球、羽毛球、台球、网球爱好者找到附近合适的球友。本文从一个具体技术或产品问题切入,记录约球小程序从 0 到 1 过程中的设计取舍。

官网:https://iyueqiu.cn

小程序:微信搜索「小i约球」

前言

做“附近球友”这个功能时,我一开始以为 LBS 很简单。

拿到用户经纬度,查附近的人,按距离排一下,就结束了。

但真正放到约球场景里,会发现 LBS 不是一个单点能力,而是一组能力的组合:

  • 小程序怎么拿定位
  • 用户怎么选择常用约球地点
  • 数据库存什么坐标格式
  • 附近查询交给谁做
  • 地图怎么展示点位
  • 列表和地图是不是同一套数据
  • 距离、球类、场景筛选怎么组合

可选方案

做这个功能时,大概有几种方案。

方案一:只存文本地址

最简单的方式是只让用户填写地址,比如“海淀区西二旗附近”“某某羽毛球馆”。

优点是实现非常简单。

缺点也很明显:

  • 不能准确算距离
  • 不能做地图点位
  • 不能按半径查询
  • 地址文本不统一,后期很难清洗
  • 同一个地点可能有很多种写法

这个方案只适合非常早期的人工运营,不适合真正做附近匹配。

方案二:前端拿经纬度,后端普通字段存储

第二种方式是用小程序拿到经纬度,然后存在普通字段里:

{,latitude: 39.9,,longitude: 116.3,}。

这个方案比纯文本好很多,至少可以自己算距离,也可以在地图上显示点位。

但如果后端只是普通字段,附近查询就比较麻烦。你要么全量拉出来自己算,要么自己做经纬度范围过滤,再二次计算距离。

用户量小的时候能跑,数据量稍微大一点就会很难受。

方案三:使用云开发数据库 Geo 能力

小i约球最后选择的是这个方案。

用户选点或定位后,普通地址信息继续保留,同时额外存一个数据库 GeoPoint:

const location = db.Geo.Point(longitude, latitude)。

这样数据库里同时有两类字段:

  • `address`:用于展示地点名称、详细地址、原始经纬度
  • `location`:用于数据库地理位置查询

这种设计的好处是比较均衡。

展示层可以拿 `address`,查询层可以拿 `location`。前端不用自己全量算距离,后端也不用额外接一个地图服务。

对一个微信小程序 + 云开发项目来说,这是最顺手的方案。

为什么没有一开始接外部地图服务

外部地图服务当然也能做 LBS,比如地点检索、路线规划、行政区划、POI、逆地址解析等。

但小i约球第一阶段没有把它作为核心依赖。

原因是第一阶段需要解决的问题并不复杂:

  • 用户选择一个约球地点
  • 系统存下坐标
  • 查询附近球友
  • 地图展示点位

这些能力小程序和云开发已经能覆盖。

如果一开始就接外部地图服务,会多出不少成本:

  • key 管理
  • 请求配额
  • 域名配置
  • 坐标系处理
  • 接口封装
  • 失败兜底

所以我的选择是:第一阶段尽量用微信生态已有能力,等需要更复杂的 POI、路线、行政区、地理围栏时,再考虑接入外部地图服务。

小程序端:定位和选点是两件事

这里是很容易混淆的地方。

小程序里至少有两个和位置相关的能力:

  • `getLocation`
  • `chooseLocation`

它们解决的问题不一样。

`getLocation` 解决的是“用户现在在哪里”。

比如首页打开时,可以拿当前定位作为默认中心点,用来展示附近球友。

`chooseLocation` 解决的是“用户想把约球名片放在哪里”。

这个位置不一定是当前定位。一个用户可能现在在公司,但想发布家附近的羽毛球约球名片;也可能人在家里,但想发布常去球馆的乒乓球名片。

所以在小i约球里,这两个能力不能混用:

  • 首页默认定位:用 `getLocation`
  • 发布约球名片:用 `chooseLocation`

这是产品场景决定的,不只是技术接口差异。

小程序权限:定位不是无条件可用

小程序定位能力还涉及权限配置。

在 `app.json` 里,需要声明相关用途,比如:

{,"permission": {,"scope.userLocation": {,"desc": "用于查找附近球友和球馆",},},,"requiredPrivateInfos": [,"chooseLocation",,"getLocation",],}。

这里有两个现实问题。

第一,用户可能拒绝授权。

所以首页不能把定位当成绝对前提。定位失败时,要么用历史位置,要么展示默认城市或默认数据,要么引导用户开启定位。

第二,不同定位能力的审核和精度不一样。

低精度定位获取门槛低,但偏差可能比较大。对资讯类应用影响不大,但对约球这种线下场景,偏差几公里就很明显。这里的重点是:定位能力不能只看“能不能拿到”,还要看精度是否符合线下见面的场景。

云开发 Geo 能力适合做什么

云开发数据库的 Geo 能力,最适合做“给定一个点,查附近的数据”。

在小i约球里,它主要用在三个场景。

1. 查附近球友名片

用户打开首页时,根据用户位置查附近的约球名片。

这里除了地理位置,还要叠加业务条件:

  • 不看自己的名片
  • 按球类筛选
  • 过滤无效数据
  • 只查普通约球名片

这说明真实查询不会只有 `geoNear`,一定是“地理条件 + 业务条件”的组合。

2. 发布名片时找潜在匹配对象

用户发布新名片后,也可以反向查询附近已有球友。

这和首页查询方向不同。

首页查询是:我在哪里,附近有什么。 发布匹配是:我发了一个新点,附近谁可能对它感兴趣。

同样是 Geo 查询,但产品含义不同。

3. 查附近本地群

本地群也可以看作一种地理对象。

一个群如果对应某个街道、球馆或区域,就可以给它存一个位置。用户查附近群时,也可以用 Geo 能力做半径查询。

这说明位置能力最好设计成底层能力,而不是只服务某一个页面。

小程序 map 组件能做什么

小程序自带 `map` 组件,已经能满足第一阶段需求。

最核心的几个能力是:

  • 设置中心点 `longitude` / `latitude`
  • 设置缩放级别 `scale`
  • 传入 `markers`
  • 监听 marker 点击
  • 监听地图视野变化

小i约球首页地图大概就是这种结构:

<map,longitude="{{user.location.longitude || 116.397428}}",latitude="{{user.location.latitude || 39.90923}}",markers="{{allMarkers}}",bindmarkertap="onMarkerTap",bindregionchange="onRegionChange",scale="13",/>。

这已经能完成基础的附近球友地图:

  • 用户当前位置作为中心点
  • 附近球友作为 marker
  • 点击 marker 进入详情
  • 缩放地图时调整 marker 展示方式

对第一阶段来说,不需要自己接一套 Web 地图库。

map 组件的限制

不过小程序 `map` 组件也不是没有限制。

最典型的是 marker 展示。

普通 marker 只能传图标、位置、大小等属性。如果你想实现更复杂的效果,比如:

  • 用用户头像作为 marker
  • 不同球类不同图标
  • 教练、陪练、球馆、群聊带不同角标
  • 缩放级别高时显示角标,缩放级别低时隐藏角标

就需要自己做更多处理。

坐标系和精度问题

小程序里常见坐标类型是 `gcj02`。

如果你的数据全部来自微信小程序的定位和选点,内部保持一致就好。

真正容易出问题的是这些情况:

  • 外部地图服务返回的是另一种坐标系
  • 后台导入的数据来自不同来源
  • 用户定位是低精度
  • 地址文本和经纬度不一致

小i约球第一阶段尽量避免引入多套坐标来源,就是为了降低这类问题。

但即便如此,定位精度仍然会影响体验。低精度定位可能偏几公里,这对“附近球友”来说不是小问题。这个问题会在后面的 Location 接口踩坑里展开。

半径怎么选

附近查询一定要面对一个问题:多远算附近?

这个值不是纯技术问题。

对线下运动来说,半径和城市密度、运动类型、用户习惯都有关系。

比如:

  • 乒乓球可能更依赖固定球馆
  • 羽毛球对场馆和时间更敏感
  • 台球用户可能愿意走远一点
  • 一线城市直线 5km 和实际通勤 5km 不是一回事

第一阶段可以先用一个相对保守的范围,比如 10km。它不一定完美,但足够验证产品闭环。

后面更合理的方式,是把半径做成可配置:

  • 用户自己选择活动范围
  • 不同城市使用不同默认半径
  • 不同球类使用不同默认半径
  • 根据数据密度动态扩大或缩小范围

总结

LBS 选型不能脱离业务场景。

如果只是做一个地图展示,可能一个 `map` 组件就够了。 如果只是存一个地址,文本字段也能应付。 但如果要做“附近球友匹配”,就必须把定位、选点、Geo 存储、附近查询、地图点位和权限降级一起考虑。

小i约球第一阶段选择了微信小程序能力 + 云开发 Geo 能力 + 小程序 map 组件。

这套方案的核心价值不是最强,而是足够贴合早期产品:成本低、集成顺、验证快。

---

本文属于「小i约球诞生记」系列。

项目官网:https://iyueqiu.cn 微信小程序:搜索「小i约球」