尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

微信小程序“附近的人”功能实现:后端链路与隐私合规避坑指南

微信小程序“附近的人”功能实现:后端链路与隐私合规避坑指南 简介一份基于Xposed框架的微信附近人采集工具源码包适合具有一定Android逆向基础、关注Hook技术与网络协议模拟的开发者学习参考。资源围绕三个核心模块展开通过Xposed钩子拦截微信附近人界面UI组件并获取上下文信息模拟微信客户端HTTP请求协议头以获取附近人原始数据借助多线程队列完成采集处理并使用POI实时导出为Excel。源码中同时涉及微信客户端安全机制、数据包加解密、防刷机制等问题的应对思路对想深入了解安卓Hook开发与HTTP交互的读者较有参考价值。包体共15个文件主要包括6个Java源码、5个xlsx示例数据、1个XML配置及README说明压缩包仅38KB结构简洁、便于快速浏览。已有78人学习下载。需要提醒的是此类工具涉及用户隐私与平台安全边界仅建议用于技术学习与合规研究切勿用于非法用途。 看到“微信附近人采集工具[源码]”这个关键词组合我先说个立场如果你是想写个脚本去批量抓取微信里的“附近人”数据那种“采集”路数我不会教也劝你别碰。这不是技术能不能实现的问题——微信对用户位置和身份信息的保护机制非常严格绕过平台规则去拿用户数据轻则账号被处理重则面临隐私方面的合规风险完全是得不偿失。但换个角度看“附近的人”是社交、本地生活、社区服务类产品里非常核心的功能模块。你的真实需求很可能是在一个微信小程序里做出类似微信“附近的人”的效果用户授权上报位置服务端检索出附近用户前端展示列表和距离。这套需求完全可以用小程序官方能力加自建后端来实现合法且可落地。这篇文章就把完整链路拆开讲清楚从坐标系处理、距离计算、存储方案到后端接口、小程序端实现再到隐私合规和平台审核避坑全是能直接参考的实操内容。1. 先搞清楚“采集”在合规产品里应该怎么做1.1 合规的位置数据只能“用户主动给”不能“平台偷偷拿”在做“附近的人”的时候很多人第一反应是“怎么把数据拿过来”。在微信生态里答案只有一个让用户自己授权。小程序官方提供了一整套位置能力包括wx.getLocation、wx.chooseLocation、wx.onLocationChange等接口用户必须主动点击授权你才能拿“我自己”的经纬度。想拿其他用户的位置只有一种方式——对方也使用了你的产品并且主动上报了位置。所以合规产品里的“采集”本质是“上报-存储-检索”三步闭环而不是“抓取-解析-入库”。你在网上能搜到的那些所谓“微信附近人接口”“自动采集源码”基本都涉及对微信客户端的注入或协议破解属于严重违规我亲眼见过有人因为做这个东西被批量封号业务一夜归零。真想在自己的产品里做类似能力必须走官方位置接口加自建后端这条路。1.2 一个最小可用的“附近的人”功能需要哪些模块拆开来看功能其实不复杂用户上报当前经纬度这一步需要位置授权。服务端保存用户位置与最后活跃时间。查询接口接收经纬度和半径返回附近用户列表。前端以列表或地图形式展示显示距离信息。用户可控制开关关闭后立即从附近列表中消失。这个功能对实时性的要求其实不高能做到分钟级更新就已经很好。初期数据量就是几千到几万条后面增长再考虑更重的存储和检索方案。关键是把链路跑通而不是一上来就上重型架构。2. 技术选型与整体架构2.1 技术栈怎么选为什么我用 Redis GEO我给出一套比较省心的组合小程序端 Node.jsExpress Redis GEO。可能有朋友会问为什么不用 MySQL 或 MongoDB先说结论“附近的人”本质上是一个半径检索问题也就是给定一个点和一个半径找出这个圆内有哪些点位。数据量在百万以内时Redis GEO 是最合适的方案写入快、查询快、部署成本低。MySQL 8.0 也有空间索引支持ST_Distance_Sphere这类函数但它更适合和复杂业务查询耦合在一起的情况MongoDB 的 GeoJSON 查询体验也不错但属于多引入一套存储体系维护成本偏高。如果项目里已经有用得很熟的 MySQL而且数据量不大用空间索引也完全可以。这篇文章的主代码以 Redis GEO 为例因为它最直观理解以后迁移到 MySQL 也只是换一套查询语句的事。2.2 整体调用链路整个流程是这样的小程序端定位 - 通过wx.request把坐标上报到后端 - 后端写入 Redis GEO - 其他用户请求附近列表 - 后端执行半径查询 - 返回脱敏后的用户数据用户ID、昵称、距离- 小程序渲染列表。这里有个关键点必须反复强调后端返回给前端的数据永远不要包含精确经纬度。只返回“用户ID、昵称、距离文本”。精确坐标属于个人敏感信息用户授权给的是平台不是授权给平台里其他用户。一旦把精确坐标暴露给任意查询者你的产品就从“附近的人”变成了“实时定位监控”这在隐私合规上是绝对过不了关的。3. 核心原理坐标系、距离计算与空间检索3.1 坐标系为什么你的距离总是偏差几百米国内做 LBS 绕不开两套坐标系WGS84 和 GCJ-02。简单说WGS84 是 GPS 原始的全球坐标GCJ-02 是国内地图厂商使用的加密坐标俗称火星坐标。微信小程序wx.getLocation的type设为gcj02时返回的就是 GCJ-02腾讯地图、高德地图用的也是 GCJ-02。我的建议是全程统一使用 GCJ-02不要混用。如果你把 GCJ-02 坐标直接当成 WGS84 去算距离偏差可能达到几百米如果再把坐标展示到地图上点位会明显偏移。这段“路线偏差”的问题是很多新手排查半天都找不到原因的经典坑其实一开始统一坐标系就没事了。3.2 距离计算Haversine 公式了解一下球面上两点的距离不能用平面直角坐标系里的欧氏距离直接算因为地球是个球体。常用的球面距离公式是 Haversinea sin²(Δφ/2) cos φ1 × cos φ2 × sin²(Δλ/2)c 2 × atan2(√a, √(1−a))d R × c其中 R 取地球平均半径 6371 公里在代码里实现也不复杂先把经纬度转成弧度然后套公式。如果使用的是 Redis GEO 或 MySQL 空间函数库内部已经处理了球面计算不需要自己写公式。但理解原理仍然重要因为排查“距离为什么不对”的时候第一步是确认坐标系第二步就是确认计算方式两个都对了距离基本就不会错。3.3 空间检索三种方案对比这里把三种常见方案放在一起看方便你根据项目情况选择方案核心能力优点注意点Redis GEOGEOADD、GEORADIUS、GEOSEARCH性能极好部署简单支持百万级点位毫秒级查询GEO 本身不带业务属性用户信息要另外存MySQL 空间索引ST_Distance_Sphere、ST_Within和业务数据强耦合查询条件可以写得很灵活高并发写入时压力较大需要维护空间索引手动 GeoHash坐标编码为字符串按前缀匹配不依赖额外组件完全自控需要处理网格边界和精度问题工程量大对于大多数中小型产品Redis GEO 是完全够用的。我本地测试过几十万个点位做半径查询响应时间基本稳定在几十毫秒内只有当你把数据量推到几百万且并发很高时才需要考虑分片或者换更重的方案。4. 从零实现后端接口 小程序端完整链路4.1 后端接口设计整个后端只需要两个接口POST /api/location/report上报当前位置。GET /api/location/nearby查询附近用户。上报接口需要前端传用户ID、经纬度查询接口需要传当前经纬度、查询半径。真实项目中用户ID应该由后端从登录态中解析出来而不是直接信任前端传值我这里为了演示方便简化处理。4.2 Node.js Redis GEO 的实现先安装依赖npm install express redis uuid这里以 node-redis v4 的 API 为例实现上报接口const express require(express); const { createClient } require(redis); const app express(); app.use(express.json()); const redis createClient(); redis.connect(); const GEO_KEY nearby:users; // 上报位置 app.post(/api/location/report, async (req, res) { const { userId, lng, lat } req.body; if (!userId || lng undefined || lat undefined) { return res.status(400).json({ code: 1, message: 参数不完整 }); } await redis.geoAdd(GEO_KEY, { longitude: lng, latitude: lat, member: userId }); // 记录最后活跃时间24小时内有效 await redis.set(online:${userId}, String(Date.now()), { EX: 86400 }); res.json({ code: 0, message: ok }); });然后是附近查询接口// 查询附近 app.get(/api/location/nearby, async (req, res) { const { lng, lat, radius 5000 } req.query; if (lng undefined || lat undefined) { return res.status(400).json({ code: 1, message: 缺少经纬度 }); } const result await redis.geoSearch( GEO_KEY, { longitude: Number(lng), latitude: Number(lat) }, { radius: Number(radius), unit: m, sort: ASC } ); const users []; for (const item of result) { if (item.member req.query.userId) continue; // 排除自己 // 只返回脱敏信息和距离不返回坐标 users.push({ userId: item.member, distance: Math.round(item.distance), distanceText: formatDistance(item.distance) }); } res.json({ code: 0, data: users }); }); function formatDistance(m) { if (m 1000) return ${Math.round(m)}m; return ${(m / 1000).toFixed(1)}km; } app.listen(3000, () console.log(server running at 3000));这段代码里有两个容易被忽略的细节。第一geoSearch返回的结果里带有每个成员与中心点的距离单位是米直接拿来做距离展示不需要自己再算一遍。第二查询结果要排除自己否则你会在“附近的人”里看到自己。关于数据过期机制我的设计是这样GEO key 本身不设 TTL因为如果整个 key 过期所有用户位置会被一次性清空每个用户上报时就覆盖同一个 member 的坐标。另用一个online:{userId}的 key 记录最后活跃时间TTL 设为 24 小时。查询时如果要做更严格的过滤可以先扫描在线用户再查 GEO。这样既不丢数据又能自然淘汰长期不活跃的用户。4.3 小程序端实现小程序端的第一步是在app.json里声明位置权限的用途说明{ permission: { scope.userLocation: { desc: 你的位置信息将用于展示附近的人 } }, requiredPrivateInfos: [getLocation] }requiredPrivateInfos这个字段是后期审核要求加上的必须有否则在微信开发者工具里可能直接报错。接下来是获取定位并上报function reportLocation(userId) { wx.getLocation({ type: gcj02, success(res) { wx.request({ url: https://your.domain.com/api/location/report, method: POST, data: { userId, lng: res.longitude, lat: res.latitude } }); }, fail(err) { console.error(获取位置失败, err); wx.showToast({ title: 授权位置后才能使用附近的人, icon: none }); } }); }这里我把type固定为gcj02就是为了和后端、地图保持同一套坐标系避免后续距离偏移。接下来是查询并渲染列表function loadNearby(userId, lng, lat) { wx.request({ url: https://your.domain.com/api/location/nearby, data: { userId, lng, lat, radius: 5000 }, success(res) { if (res.data.code 0) { this.setData({ nearbyList: res.data.data }); } } }); }前端拿到列表后直接展示昵称和距离文本即可。如果要做地图打点请再次确认不要给地图传入其他人的精确坐标而是只展示“当前用户位置 附近用户距离范围”否则审核风险很高。4.4 列表展示和地图展示怎么选列表是初期最推荐的形式信息密度高、实现简单、滚动性能好。地图组件视觉效果好能直观展示空间关系但小程序里的map组件相对较重在列表滚动、坐标事件交互上要处理更多细节。我的习惯是先做列表快速验证业务模型。等用户量起来、业务方向确认后再考虑加一个“地图视角”开关用地图展示当前用户的周边范围。千万不要一上来就同时做两套否则前后端联调的工作量会翻倍。5. 隐私合规与平台审核避坑5.1 上线前必须做的配置这个环节非常重要至少有三件事必须提前处理好。第一小程序管理后台要申请位置相关接口权限也就是wx.getLocation并填写实际用途。用途说明要写得很具体比如“用户开启附近功能后获取其位置信息用于计算并展示与其他用户的距离”不要只写“用于附近的人”五个字太敷衍容易被驳回。第二小程序的隐私协议中要单独说明位置信息的采集、使用、存储方式包括存储时长和用户如何主动清除位置。这块不仅是审核要求也是产品对用户负责任的表现。第三功能层面必须支持用户主动关闭附近展示。关闭后后端要立即删除该用户在 Redis GEO 中的记录而不是仅仅在前端隐藏。我见过有些产品只在前端把按钮关掉后端数据还在用户隐私就存在隐患。5.2 审核被拒的常见原因我把审核时最容易踩的几个坑整理成了表格被拒原因典型表现规避方法隐私接口未声明一调用getLocation就被判违规在app.json和后台同时完善隐私声明强制授权用户拒绝位置授权后完全没法使用小程序允许游客模式位置作为可选功能用途说明太简单审核员看不懂你为什么要做“附近的人”写清产品场景、功能路径、数据使用范围展示他人精确坐标地图打点直接显示其他用户位置只返回距离文本不返回其他人的经纬度位置数据没有删除路径用户关闭功能后数据仍保留关闭时清理后端 GEO 记录提供联系方式引导5.3 我踩过的一个真实坑我之前做过一版类似功能为了省事把用户经纬度直接放在接口返回值里前端在地图上直接打点。结果审核被拒理由就是“展示用户精确位置”。后来改成服务端只返回距离前端只显示距离文本才通过。这个经验真的建议提前看位置数据不同于普通头像昵称一旦展示给陌生人带来的安全隐患非常大。产品设计上宁可保守也别惹麻烦。6. 常见问题与排查实录6.1 定位失败或者授权弹窗不出现常见原因有三个requiredPrivateInfos没配用户之前拒绝过授权代码里没有处理fail回调。处理方案是在页面启动时主动引导授权如果用户拒绝再通过wx.openSetting引导去设置页打开授权。注意不要在小程序一启动时就同时弹多个授权框体验会非常差。6.2 坐标看起来没问题但距离差了好几倍这个问题我排查过很多次基本就两类原因坐标系混用或者经纬度字段写反了。wx.getLocation返回的是longitude和latitude有的接口会简写成lng和lat复制代码时很容易对错位置。建议在请求后端前打一条日志实际打印出经纬度再核对。6.3 Redis GEO 里能查到人小程序里却看不到十有八九是后端接口字段名不匹配或者小程序的 request 合法域名没有配置。开发阶段可以在微信开发者工具里勾选“不校验合法域名”但上线前一定要把接口域名加到 request 合法域名列表并确保接口是 HTTPS。6.4 数据量变大后查询变慢怎么办先用数据说话明确用户量和 QPS 上限。Redis GEO 在百万级点位上是毫秒级响应一般产品根本到不了性能瓶颈。如果真遇到问题常见的优化思路是按城市拆多个 GEO key比如nearby:users:beijing、nearby:users:shanghai查询时根据目标坐标先定位城市再查对应 key。这个方案实现成本不高效果却很明显。6.5 真机调试和开发者工具定位结果不一致开发者工具里的定位是模拟的默认位置在北京或者你手动选择的城市真机则是真实 GPS。这也解释了为什么很多人开发时测试一切正常一上真机就发现距离计算不对。建议真机测试时去不同环境多试几次室内、室外、电梯里都跑一下看定位精度是否符合预期。我在实际做类似功能时踩过最多坑的不是后端代码而是隐私审核和坐标系。位置数据这个方向一旦口径不统一、用途没说清后面全是返工。最后再分享一个自用的小技巧上报位置时不要只存坐标把设备的精度值也一并存下来。如果某次定位精度是 500 米距离展示就不要精确到“301 米”显示“约 500 米内”会更合理。这在审核和用户感知上都更安全也更符合位置数据的真实精度水平。本文还有配套的精品资源点击获取
返回列表