从 0 设计「附近免费好物」多端应用:小程序 + App 全平台选型与架构
2026年7月28日 · 18 分钟
先把产品需求翻译成工程语言
我们要做的产品,对标海外的 Snag:帮用户发现身边别人免费送出的物品(家具、家电、日用品),聚合成一个信息流,用户浏览、筛选、收藏、领取。落到中国区,还叠加了三条硬约束:
- 多端框架:一套代码尽量覆盖尽可能多的端;
- 发布小程序到各平台:微信、支付宝、抖音、百度、QQ、快手;
- 多平台账号登录 + 独立 App(iOS / Android)。
把它拆成技术命题:
| 产品能力 | 背后的技术需求 |
|---|---|
| 看"附近"的免费物品 | LBS 地理空间检索(按距离排序/半径过滤) |
| 用户自己发布物品 | UGC + 图片/视频上传 + 内容审核 |
| 一个信息流刷不停 | 分页/游标、Feed 排序、缓存 |
| 收藏、领取、订阅付费 | 账号体系、订单/权益、支付 |
| 全平台小程序 + App | 跨端框架 + 多端登录打通 |
| 中国区上线 | 备案、实名、内容合规 |
一句话定性:这是一个 LBS + UGC + 内容分发 + 订阅付费的消费级应用。 记住这个定性,后面每个选型都为它服务。
最关键的一个判断:这是一个「附近」应用
很多人上来就纠结用 Vue 还是 React,其实决定架构的心脏是"附近"两个字。"查我周围 3 公里内的物品,按距离排序"——这类地理空间查询,直接决定了数据库选型(下文第五节会给出 PostGIS 方案)。先把这个钉子钉住,其余都是围绕它的工程组织。
一、跨端框架选型:为什么是 uni-app
需求里"发全平台小程序 + App"这一条,直接把候选框架砍到了很短的名单。我们逐个对比:
| 框架 | 小程序(微信/支付宝/抖音/百度…) | App(iOS/Android) | 语言/生态 | 中国本土化 | 上手 | 结论 |
|---|---|---|---|---|---|---|
| uni-app | ✅ 全平台官方支持,覆盖最全 | ✅ 原生渲染打包 / uni-app x | Vue,DCloud | ★★★★★ | 低 | 主选 |
| Taro | ✅ 支持主流小程序 | ✅ 走 React Native | React(也支持Vue),京东 | ★★★★ | 中 | React 团队备选 |
| Flutter | ❌ 无官方小程序方案(mpflutter 不成熟) | ✅ 体验最好 | Dart | ★★★ | 中 | 有小程序需求直接出局 |
| React Native | ❌ 不能编译小程序 | ✅ | React | ★★★ | 中 | 缺小程序,出局 |
| 各端原生 | 每端各写一遍 | ✅ 最优 | 多套 | — | 高 | 成本爆炸,出局 |
判断逻辑:
- 需求明确要"发到各家小程序",Flutter / RN 当场出局——它们没有小程序编译目标。
- 剩下 uni-app 和 Taro,两者都能"一套代码 → 多端小程序 + App"。
- 选 uni-app 的理由:
- 多端覆盖最全,微信/支付宝/抖音/百度/QQ/快手小程序官方一线支持,中国区小程序生态踩坑最少;
- App 端方案成熟:可直接打包为原生 App,性能敏感页面用
uni-app x(uts 语言,接近原生渲染)补足; - 插件市场 + 中文文档 + 社区在国内最厚,登录/支付/地图这些"中国特色"能力有大量现成轮子;
- 团队招 Vue 开发者比 Dart 容易。
- 什么时候选 Taro:如果团队是重度 React 栈、且已有 React 组件资产要复用,Taro 更顺手;App 端走 RN 性能也不错,代价是配置更重、小程序踩坑略多。
结论:默认 uni-app(Vue3 + TS + Vite);纯 React 团队可换 Taro,架构其余部分不变。
二、客户端架构与「一套代码多端」的关键手段
uni-app 客户端
├── pages/ 页面(首页 Feed、附近、详情、发布、我的)
├── components/ 跨端复用组件
├── stores/ Pinia 状态管理(用户、位置、Feed)
├── api/ 请求层(统一拦截器:注入 token、错误处理)
├── utils/platform 平台差异封装(登录/支付/定位)
└── #ifdef 条件编译 抹平各端 API 差异
多端最大的痛点是各端 API 不一样(微信是 wx.、支付宝是 my.、App 是 plus.)。uni-app 用两招解决:
- 统一 API:绝大多数能力用
uni.前缀,框架帮你翻译到各端; - 条件编译
#ifdef:处理无法统一的部分,比如登录:
// utils/login.js —— 同一个函数,各端走各端的登录
export async function platformLogin() {
// #ifdef MP-WEIXIN
const { code } = await uni.login({ provider: 'weixin' });
return exchangeToken('wechat-mp', code);
// #endif
// #ifdef MP-ALIPAY
const { code } = await uni.login();
return exchangeToken('alipay-mp', code);
// #endif
// #ifdef APP-PLUS
const res = await uni.login({ provider: 'weixin' }); // App 拉起微信
return exchangeToken('wechat-app', res.authResult);
// #endif
}
上层业务只调 platformLogin(),不用关心跑在哪个端。
三、多平台登录体系(本项目最容易踩坑的一块)
"支持各平台登录"不是简单接几个 SDK,核心难点是:同一个人从不同端进来,要能识别成同一个账号。设计分两步。
3.1 各端登录方式矩阵
| 运行端 | 登录方式 | 客户端拿到什么 | 后端如何处理 |
|---|---|---|---|
| 微信小程序 | uni.login | code | 用 code 调 code2session 换 openid + unionid |
| 支付宝小程序 | getAuthCode | authCode | 换 alipay userId |
| 抖音小程序 | tt.login | code | 换 open_id |
| 百度/QQ/快手小程序 | 各端 login | code | 换对应 open_id |
| App(微信登录) | 微信开放平台 | authCode | 换 openid + unionid |
| App(iOS 必接) | Sign in with Apple | identityToken | 校验拿 apple user id |
| 全端兜底 | 手机号一键登录 / 短信验证码 | 手机号 | 作为主账号锚点 |
上架 iOS 有第三方登录时,Apple 强制要求同时提供 Sign in with Apple;手机号务必接入,它是打通所有身份的"锚点"。
3.2 统一账号模型(一个用户,多个身份)
后端不要直接用微信 openid 当用户 ID,而是抽象出"用户"和"身份"两张表:
-- 用户主体
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
phone VARCHAR(20) UNIQUE, -- 手机号锚点
nickname VARCHAR(64),
created_at TIMESTAMPTZ DEFAULT now()
);
-- 第三方身份,一个 user 可绑定多个
CREATE TABLE user_identities (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT REFERENCES users(id),
provider VARCHAR(32), -- wechat / alipay / apple / douyin ...
open_id VARCHAR(128), -- 该平台内的唯一 id
union_id VARCHAR(128), -- 微信生态跨应用打通用
UNIQUE(provider, open_id)
);
打通逻辑:
- 微信系(小程序 + App + 公众号)用 UnionID 归并——只要在同一微信开放平台账号下,UnionID 一致即同一人;
- 其它端登录后,引导绑定手机号,用手机号把不同 provider 的身份挂到同一个
users.id; - 后端统一签发自己的 JWT,客户端所有请求只认这个 token,与具体登录方式解耦。
这样无论用户用微信小程序还是 App 微信登录进来,看到的都是同一份收藏和数据。
四、后端技术选型
| 方案 | 优势 | 劣势 | 适用 |
|---|---|---|---|
| NestJS(Node/TS) | 与前端同语言,全栈 TS,生态足,开发快 | CPU 密集型偏弱 | 主选 |
| Go(Gin/Kratos) | 高并发、低延迟、部署简单 | 生态/开发效率略逊 | LBS/Feed 热点服务 |
| Spring Boot(Java) | 企业级成熟、稳 | 重、启动慢、人力贵 | 大厂/已有 Java 团队 |
选型结论:
- 主体用 NestJS:与 uni-app 的 TS 前端同一套语言与类型,能共享 DTO/校验模型,中小团队研发效率最高,模块化架构(Module/Provider/Guard)天然适合分层;
- 对性能敏感的"附近 Feed"服务,可用 Go 单独拆一个微服务:这是读多写少、并发最高的接口,Go 的并发模型能扛住热点。
初期不必上重微服务,先用 NestJS 模块化单体(用户/物品/LBS/审核/订单各一个 module),流量起来后再把 LBS 服务拆出去,避免过早优化。
五、数据与存储:LBS 是核心
5.1 主数据库:PostgreSQL + PostGIS
"查附近物品"是心脏功能,所以数据库首选 PostgreSQL + PostGIS 扩展——它有成熟的地理空间类型和索引:
CREATE EXTENSION postgis;
CREATE TABLE items (
id BIGSERIAL PRIMARY KEY,
title TEXT,
user_id BIGINT,
status SMALLINT, -- 审核/上架状态
geom GEOGRAPHY(POINT, 4326), -- 经纬度
created_at TIMESTAMPTZ DEFAULT now()
);
-- 地理空间索引,让"附近查询"走索引而非全表扫描
CREATE INDEX idx_items_geom ON items USING GIST (geom);
查"我周围 3 公里、已上架的物品,按距离排序":
SELECT id, title,
ST_Distance(geom, ST_MakePoint(:lng, :lat)::geography) AS dist
FROM items
WHERE status = 1
AND ST_DWithin(geom, ST_MakePoint(:lng, :lat)::geography, 3000)
ORDER BY dist
LIMIT 20;
为什么不用 MongoDB:我们需要事务(下单/领取)、关系(用户-物品-身份)和成熟的地理检索,PostgreSQL + PostGIS 一站式满足,比拼装方案更省心。
5.2 其余存储分工
| 组件 | 选型 | 职责 |
|---|---|---|
| 缓存/会话 | Redis | token、热点 Feed、限流、附近热区缓存 |
| 图片/视频 | 对象存储(腾讯云 COS / 阿里云 OSS) + CDN | UGC 媒体,前端直传 + 回源审核 |
| 搜索/筛选 | Elasticsearch(可选) | 关键词、多条件筛选;初期可用 PG 全文顶着 |
| 消息队列 | Kafka/RocketMQ(可选) | 异步审核、通知、埋点解耦 |
六、整体系统架构
┌──────────────────────────────────────────────┐
│ 客户端(uni-app 一套代码) │
│ 微信/支付宝/抖音/百度/QQ/快手 小程序 + iOS/Android App │
└───────────────────────┬──────────────────────┘
│ HTTPS
┌───────────▼───────────┐
│ CDN + API 网关/BFF │ 鉴权、限流、路由
└───────────┬───────────┘
┌───────────────────┼───────────────────┐
┌────▼────┐ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ 用户服务 │ │ 物品服务 │ │ LBS/Feed │ │ 审核服务 │
│(登录/账号)│ │(发布/详情)│ │(附近查询)│ │(内容合规)│
└────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘
└──────┬───────┴─────┬───────┴────────────┘
┌─────▼─────┐ ┌─────▼─────┐ ┌──────────┐ ┌─────────┐
│PostgreSQL │ │ Redis │ │对象存储OSS│ │ ES(可选)│
│ +PostGIS │ │(缓存/会话)│ │ +CDN │ │ (搜索) │
└───────────┘ └───────────┘ └──────────┘ └─────────┘
要点:客户端只面对统一的 BFF/网关,各端登录差异在客户端 #ifdef 里消化、在用户服务里归并,其余服务完全不感知调用来自哪个端。
七、中国区合规:不能省的一节
海外照抄一套技术就上线,在国内会直接被下架。作为专家必须提醒三件事:
- 内容安全审核(最硬性):UGC 的图片和文字必须审核。接入腾讯云/阿里云的内容安全服务,做"机审兜底 + 高危先审后发"。这是 UGC 产品能否活下来的红线。
- 备案与资质:域名 ICP 备案;各家小程序对"信息发布/二手交易"类目有资质要求,提交前先查平台类目规范。
- 实名与隐私:遵循《个人信息保护法》,发布隐私政策、遵循最小必要原则收集信息,敏感权限(定位)要有明确授权引导。
八、部署与 DevOps
- 容器化:整套后端用 Docker 打包(正好呼应本站的 Docker 系列),环境一致、随处可跑;
- 编排:流量起来后上 Kubernetes 做弹性伸缩,"附近 Feed"高峰可单独扩容;
- CI/CD:GitHub Actions / GitLab CI,推代码自动构建镜像并部署;
- 云厂商:优先腾讯云或阿里云(内容审核、短信、对象存储、地图一站式配齐,且合规链路顺);
- 可观测:日志 + 指标 + 链路追踪,尾延迟和错误率必须能看见。
选型总览
| 层次 | 选型 | 一句话理由 |
|---|---|---|
| 跨端框架 | uni-app(Vue3+TS) | 全平台小程序覆盖最全,App 方案成熟,中国区踩坑最少 |
| 状态管理 | Pinia | uni-app/Vue3 官方推荐,轻量 |
| 后端 | NestJS(+ Go 拆热点) | 与前端同 TS,开发快;高并发接口用 Go 补 |
| 主数据库 | PostgreSQL + PostGIS | "附近查询"是心脏,地理索引一站式满足 |
| 缓存 | Redis | 会话、热点 Feed、限流 |
| 媒体 | 对象存储 + CDN | UGC 图片/视频直传与分发 |
| 搜索 | Elasticsearch(可选) | 多条件筛选;初期 PG 全文顶替 |
| 登录 | 多 provider + UnionID/手机号归并 | 统一账号模型,一人多身份 |
| 部署 | Docker + K8s + 腾讯云/阿里云 | 环境一致、弹性伸缩、合规链路完整 |
小结
- 先给产品定性(LBS + UGC + 订阅),再让每个选型为它服务;
- 有"全平台小程序 + App"这条,跨端框架实际只在 uni-app / Taro 里二选一,默认 uni-app;
- 登录不是接 SDK,而是设计**"一人多身份"的统一账号模型**,用 UnionID + 手机号归并;
- 数据库因"附近"而定:PostgreSQL + PostGIS;
- 中国区的内容审核与备案是必须提前规划的红线,不是上线后再补的功能。
按这套方案,你可以用一支不大的 Vue/TS 团队,一套代码把小程序全平台 + iOS/Android App 都覆盖掉,把精力真正花在"帮用户找到身边好物"这件事上。