AI 交友小程序 + 会员订阅
用 CloudBase + 微信支付 + AI 编程,从零做一个带会员系统、推荐机制和线下活动的交友小程序。
背景
做完 AI 心情日记实战后,你已经有了小程序的基础能力。这一篇要上难度了:做一个 交友小程序。它的核心不是一个用户自己用,而是用户与用户之间的连接。
这个场景有三个新东西是你在基础案例里没接触过的:
- 会员订阅:年费玩家,不是单次购买
- 推荐机制:怎么展示用户、让用户认识
- 线下活动:活动发布 + 报名 + 人数限制
如果你能把这篇吃透,那市面上大部分小程序的付费模式你都能自己搭了。
完整链路只需要 4 步:
- 用户体系 + 资料采集:注册登录、完善个人资料
- 推荐系统:浏览其他用户信息、互动
- 会员系统(年费玩家):微信支付订阅 + 权益校验
- 线下活动:发布活动、报名、人数管理
注意:微信支付需要个体工商户或企业主体才能开通。如果你是个人开发者,这套案例中支付的流程仍然值得看——云调用 + 云函数的架构逻辑,换成其他支付平台(支付宝、聚合支付)思路是相通的。
项目概览
这个交友小程序的核心功能:
- 会员(年费玩家):年费解锁全部权益——全年免费参加所有线下活动、查看其他用户的详细信息
- 推荐:展示用户信息,促进用户间的认识与连接
- 线下活动:发布活动、报名参加、人数限制
技术选型
| 模块 | 技术 |
|---|---|
| 前端 | 微信小程序原生 + TDesign UI |
| 云服务 | CloudBase(云函数 + SQL 数据库 + 云存储) |
| 登录 | 手机号快速验证组件 |
| 支付 | 微信支付(云调用) |
| AI 工具 | CloudBase MCP + CloudBase Skill |
第一步:用户体系与资料采集
注册登录
沿用基础案例里的手机号登录方案——微信原生 getPhoneNumber 组件 + 云函数存储用户信息。
和心情日记不同之处在于,交友小程序需要更多的用户画像数据:昵称、头像、年龄、城市、职业、兴趣标签等。
云函数 register 在处理登录的同时,创建用户档案:
// 云函数 register(简化)
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database()
exports.main = async (event) => {
const { OPENID } = cloud.getWXContext()
const { phoneNumber, nickName, avatarUrl } = event
// 检查用户是否存在
const existing = await db.collection('users')
.where({ openid: OPENID })
.get()
if (existing.data.length > 0) {
return { isNew: false, user: existing.data[0] }
}
// 创建新用户
const user = {
openid: OPENID,
phoneNumber,
nickName,
avatarUrl,
isVip: false,
vipExpireAt: null,
createdAt: db.serverDate()
}
await db.collection('users').add({ data: user })
return { isNew: true, user }
}个人资料采集
用户注册后进入资料编辑页,填写兴趣标签(最多 5 个)、自我介绍、职业信息。这些数据是推荐系统的基础。
第二步:推荐系统
推荐系统最简单的第一版:按照距离 + 兴趣标签匹配度排序。
第一版推荐算法不要搞复杂。先按同城 + 共同标签数排序,够用了。等用户量上来再考虑协同过滤或者向量检索。
云函数 getRecommendations:
// 云函数 getRecommendations 伪逻辑
const currentUser = getUser(event)
const allUsers = await db.collection('users').get()
// 按城市 + 共同标签数排序
const scored = allUsers
.filter(u => u.openid !== currentUser.openid)
.map(u => ({
...u,
score: calcMatchScore(currentUser, u)
}))
.sort((a, b) => b.score - a.score)
return { data: scored.slice(0, 20) }前端展示卡片式布局:头像、昵称、年龄、城市、兴趣标签。点击可查看详情(非会员只能看基本信息)。
第三步:会员系统(年费玩家)
这是整个案例最复杂的部分。核心逻辑:
用户点击"开通年费会员"
→ 云函数发起微信支付统一下单
→ 用户支付成功
→ 微信回调通知
→ 云函数处理回调:更新 users 表 isVip=true, vipExpireAt=一年后
→ 用户解锁会员权益微信支付的接入方式
这里不是单次购买,而是订阅模式。不过在第一版,微信支付的接入方式是一样的——都是通过云调用(cloud.openapi.pay)。
关键区别在于:
| 维度 | 心情日记(无支付) | 单次购买场景 | 交友(会员订阅) |
|---|---|---|---|
| 支付类型 | 无 | 单次统一下单 | 年费订阅 |
| 订单表 | 无 | orders(单次商品) | member_orders(会员订单) |
| 权益校验 | 无 | 购买即解锁 | 检查 isVip + vipExpireAt |
| 到期处理 | 无 | 无 | 检查过期自动降级 |
支付云函数核心逻辑
// 云函数 createMemberOrder(简化)
const cloud = require('wx-server-sdk')
cloud.init()
exports.main = async (event) => {
const { OPENID } = cloud.getWXContext()
// 检查是否已是会员且未过期
const user = await getUser(OPENID)
if (user.isVip && user.vipExpireAt > Date.now()) {
return { code: 400, message: '已是会员' }
}
// 微信支付统一下单
const result = await cloud.openapi.pay.unifiedOrder({
subMchId: '你的商户号',
totalFee: 19900, // 199.00 元,以分为单位
body: '年费会员',
outTradeNo: generateOrderNo(),
spbillCreateIp: '127.0.0.1',
tradeType: 'JSAPI',
openid: OPENID,
})
return { code: 0, payment: result.payment }
}关于"受理关系不存在"的坑:如果你用直连商户(自己的商户号),不要传
sub_mch_id和sub_openid。那是服务商模式用的。直连场景传入这些字段会报错。
会员权益校验
关键点在于:每个需要会员权限的接口,都要做校验。
// 获取用户详情(需会员权限)
exports.main = async (event) => {
const { OPENID } = cloud.getWXContext()
const user = await getUser(OPENID)
if (!user.isVip || user.vipExpireAt < Date.now()) {
return { code: 403, message: '需要会员才能查看' }
}
// ... 返回用户详情
}前端根据 isVip 控制 UI 显示:非会员看到的信息打码或截断,引导开通会员。
第四步:线下活动
活动模块包含:
- 活动发布:管理员在后台发布(或社区用户发布需审核)
- 活动浏览:按时间排序,显示报名人数/总名额
- 活动报名:会员免费,非会员可付费单次参加
数据结构
活动表 events:
| 字段 | 类型 | 说明 |
|---|---|---|
| title | string | 活动标题 |
| description | string | 活动描述 |
| location | string | 地点 |
| startTime | Date | 开始时间 |
| maxParticipants | number | 最大参与人数 |
| currentCount | number | 当前报名人数 |
| price | number | 非会员费用(会员免费) |
| status | string | 报名中/进行中/已结束 |
报名逻辑
exports.main = async (event) => {
const { OPENID } = cloud.getWXContext()
const { eventId } = event
const user = await getUser(OPENID)
const eventData = await db.collection('events').doc(eventId).get()
if (eventData.currentCount >= eventData.maxParticipants) {
return { code: 400, message: '名额已满' }
}
// 会员免费
if (user.isVip && user.vipExpireAt > Date.now()) {
await db.collection('event_registrations').add({
data: { eventId, openid: OPENID, fee: 0, status: 'confirmed' }
})
// 更新报名人数
await db.collection('events').doc(eventId).update({
data: { currentCount: cloud.database().command.inc(1) }
})
return { code: 0, message: '报名成功(会员免费)' }
}
// 非会员需要支付
// ... 发起微信支付
}MCP 辅助开发要点
这个案例中 CloudBase MCP 能帮你做的事:
| 场景 | 提示词示例 |
|---|---|
| 建表 | "在 SQL 数据库中创建 users、events、orders 三张表" |
| 部署云函数 | "部署 register 云函数并开放 invoke 权限" |
| 查日志 | "查看 createMemberOrder 云函数最近的报错" |
| 配环境 | "配置云函数的微信支付商户号环境变量" |
CloudBase MCP 是 2025-2026 年开发 CloudBase 应用的关键变化。没有 MCP 之前,AI 写 CloudBase 代码质量很差(文档不熟、API 猜错);有了 MCP,AI 可以直接查文档、建表、部署——整个体验接近 Supabase 的开发效率了。
关键教训
- 会员系统不只是支付:真正的复杂度在权益校验和过期处理。你需要在每个需要权限的接口都做一次校验,前端也要根据会员状态控制 UI 展示
- 推荐系统从笨的开始:第一版不需要向量检索,按城市 + 标签匹配就已经能用了。先把产品 MVP 验证了再优化算法
- 微信支付的坑:直连 vs 服务商的区别是 AI 最搞不清楚的部分,需要人工确认
- 数据安全:用户手机号、详细资料等敏感信息,云函数层面要做好权限控制,不要让非会员能查到