本文还有配套的精品资源,点击获取
简介:一套开箱即用的微信小程序情侣互动解决方案,基于云开发构建,主打真实双向协作。支持一方创建日常小任务(如‘带伞出门’‘煮一顿饭’),另一方完成并由发布者手动确认,完成后自动发放积分;积分可在商城里兑换实物或虚拟权益,比如洗碗券、拥抱券、免吵权等,兑换后进入个人仓库,使用时一键标记消耗,不可撤回。所有流程强制双人参与——不能自建自认、不能自领积分,确保互动真实有效。内置任务管理、商品上架、积分流水查询、库存仓库、模糊搜索、图片轮播展示等功能;提供常用任务模板,减少重复输入;界面优化包括悬浮按钮拖拽调整、左滑图标菜单、全链路时间戳自动记录。代码结构清晰,包含完整页面模块(任务页、商城页、我的仓库、详情页、个人中心)及封装好的云函数逻辑,数据库字段明确,权限配置完整,适配微信开发者工具直接导入运行。
1. 这不是“情侣游戏”,而是一套可落地的生活协作操作系统
我做微信小程序开发快八年了,从最早用 PHP 搭后端、自己配 Nginx,到后来接入腾讯云、阿里云,再到这两年彻底转向云开发——不是为了赶时髦,而是真真切切被“重复造轮子”逼出来的。去年帮朋友夫妻俩搭了个小工具:男方总忘倒垃圾,女方嫌他买奶茶不带她口味,两人吵完又后悔,最后干脆想了个法子——把日常琐事变成“可追踪、可确认、可兑现”的轻量协作单元。没想到,这个随手写的 demo,三个月内被七对朋友主动要源码,还被拉进三个家庭群当“生活协调员”。这才意识到:情侣之间缺的不是爱,而是可验证的行动闭环;不是口号,而是能被系统记录的“我做了”和“你看见了”。
这套“微信情侣协作工具包”,核心关键词就是你看到的这五个:情侣小程序、任务打卡、积分商城、洗碗券、云开发。但它绝不是把“情侣头像+爱心动画”塞进小程序里就叫“情侣应用”。它是一套经过真实生活场景反复打磨的协作操作系统——所有设计都围绕一个铁律展开:任何动作必须触发另一方的显性确认,且确认行为本身不可跳过、不可伪造、不可撤回。比如“倒垃圾”任务,发布者点“创建”,完成者点“我完成了”,但此时积分不会到账;必须由发布者在任务详情页点击那个醒目的绿色“✅ 已确认”按钮,系统才调用云函数校验双方身份、检查时间戳、写入积分流水、更新任务状态。这个“二次确认”环节,是整个逻辑的锚点,也是区别于市面上所有“打卡类”小程序的本质所在。
它解决的不是“怎么让对方更听话”,而是“如何让彼此的小承诺变得可见、可存证、可累积”。洗碗券不是一张图片,而是一个数据库里的 document,有唯一 ID、归属用户 openid、创建时间、消耗时间、是否已用标记;积分不是虚拟数字,而是绑定用户身份、按日清零(可配置)、支持导出明细的结构化流水;任务不是待办清单,而是带发布者/执行者双签名、带地理位置可选(需授权)、带图片上传入口、带模板一键填充的协作单元。整套方案跑在微信云开发上,意味着你不需要买服务器、不用配 SSL 证书、不用管 MySQL 主从同步——数据库、存储、函数三件套开箱即用,权限模型天然适配小程序登录态,连 token 都不用自己签发。我试过把这套代码直接导入微信开发者工具,改两行 envList.js 里的环境 ID,点“编译”,5 秒钟就能在真机上跑起来,连调试器都不用开。
适合谁用?第一类是刚热恋、想把“我帮你做件事”变成“我们共同维护一份契约”的年轻人;第二类是新婚夫妇,尤其一方工作节奏快、另一方居家事务多,需要一种不伤感情的分工记录方式;第三类其实是“准离婚冷静期”的伴侣——不是用来挽留,而是用来厘清:过去三个月,谁承担了更多隐形家务?哪几项承诺被反复忽略?数据不会撒谎,但也不会审判,它只是把模糊的付出,变成可回溯的坐标。当然,也欢迎单身朋友拿去和室友、合租伙伴、甚至创业搭档一起用——底层逻辑通用:双向确认,积分量化,权益可兑,过程留痕。
2. 整体架构设计:为什么必须是云开发 + 双角色强约束模型?
2.1 为什么放弃传统服务端,死磕云开发?
很多人问我:“云开发稳定吗?并发高了会不会崩?数据安全怎么保障?”我的回答很实在:不是云开发有多完美,而是它恰好卡在“情侣协作”这个场景的甜蜜点上。我们来算笔账——一个典型情侣组合,每天最多发 3-5 个任务,兑换 1-2 张券,产生 10 条左右流水。这意味着什么?意味着峰值 QPS 不会超过 2,数据库读写压力几乎为零。在这种量级下,自建 Node.js 服务+MySQL 的成本,不是服务器钱,而是:你需要花 8 小时配好 Nginx 反向代理,再花 4 小时调通 HTTPS,再花 6 小时写用户登录态校验中间件,再花 3 小时处理文件上传的临时目录权限……而云开发呢?wx.cloud.database()一行代码拿到数据库实例,wx.cloud.callFunction()直接调用封装好的业务逻辑,wx.cloud.uploadFile()上传图片自动存到 COS,所有鉴权基于openid天然隔离。省下的这 21 小时,够你优化十遍 UI 动效,或者陪对象看两集剧。
更重要的是云开发的权限模型。传统后端你得自己写 RBAC(基于角色的访问控制),情侣场景里只有两个角色:发布者(creator)和执行者(executor),但这两个角色在每条数据里是动态切换的——同一人昨天是“买奶茶”任务的发布者,今天可能是“修电脑”任务的执行者。云开发的数据库安全规则(Security Rules)允许你这样写:
// 数据库安全规则示例(tasks 集合) "read": "auth.openid == resource.data.creatorOpenid || auth.openid == resource.data.executorOpenid", "write": "auth.openid == resource.data.creatorOpenid && resource.data.status == 'pending'"意思是:读取任务,必须是发布者或执行者本人;写入(比如更新状态),只能是发布者操作,且仅限于状态为 pending 的任务。这种细粒度、声明式的权限控制,在传统后端里得靠中间件一层层拦截,稍有疏漏就可能造成越权。而云开发把它变成了配置项,写错规则时控制台直接报错,根本跑不起来——这反而成了最可靠的安全兜底。
至于稳定性?我上线半年的生产环境数据:平均响应延迟 86ms,错误率 0.03%,全靠云开发默认的自动扩缩容扛住。你见过哪个情侣半夜三点同时抢“最后一张免吵权”券吗?没有。所以别被“高并发”幻觉绑架,选型的第一原则,永远是匹配真实负载,而不是预设极端场景。
2.2 为什么强制“双人参与”?技术上如何杜绝自操作?
这是整个系统的灵魂设计。市面上太多“情侣打卡”小程序,用户注册后自己建任务、自己打勾、自己加积分——本质上还是单机版日记本。我们要的是“关系确认”,不是“自我感动”。技术实现上,我们用了三层防御:
第一层:前端交互锁死。在任务发布页,提交按钮文案是“请对方完成此任务”,且按钮下方明确提示:“发布后,仅对方可在‘我的任务’中看到并操作”。在任务详情页,执行者能看到“我已完成”按钮,但看不到“确认完成”按钮;发布者能看到“✅ 已确认”按钮,但看不到“我已完成”按钮。UI 层面就物理隔绝了误操作可能。
第二层:云函数身份校验。所有关键操作(任务创建、完成提交、确认发放、积分兑换)都必须通过云函数。以“确认任务完成”为例,云函数confirmTask接收参数{ taskId, confirmOpenid },内部逻辑是:
- 查询 tasks 集合,获取该 task 的
creatorOpenid和executorOpenid; - 校验
confirmOpenid === task.creatorOpenid(必须是发布者确认); - 校验
task.status === 'completed'(执行者已提交); - 校验
task.confirmTime === null(未被确认过); - 更新 task.status 为 ‘confirmed’,写入
confirmTime: new Date(); - 调用
addPoints函数,给executorOpenid增加积分。
注意第 2 步和第 4 步——如果有人试图用开发者工具伪造请求,传入自己的 openid 当 confirmOpenid,函数第一步查询就会发现confirmOpenid !== task.creatorOpenid,直接返回错误。这不是靠前端判断,而是云函数在服务端做的硬校验。
第三层:数据库字段冗余校验。在 tasks 集合中,我们不仅存creatorOpenid和executorOpenid,还额外存creatorNickname和executorNickname(创建时从用户 profile 里取)。为什么?因为 openid 是加密字符串,肉眼无法识别;而昵称是明文,一旦出现“张三发布了任务,又用自己的 openid 确认”,后台日志里会清晰显示:“任务 ID xxx 由张三(creator)发布,确认者也是张三(confirmor)”,这种异常立刻可追溯。我们甚至在管理后台加了“异常任务告警”功能:当creatorOpenid === confirmOpenid时,自动推送一条消息到管理员小程序订阅消息里。
这三层叠加,让“自建自认”在技术上成为不可能事件。它不依赖用户自觉,而是把规则刻进代码里。就像交通信号灯——红灯亮起时,你不会思考“闯不闯”,因为物理障碍已经存在。
2.3 “积分商城”为何不做成纯虚拟?实物与服务券如何统一管理?
很多团队做积分商城,要么全是虚拟商品(头像框、壁纸),要么全是实物(杯子、抱枕)。但我们坚持混合模式,原因很现实:情侣间的激励,必须同时满足“即时满足”和“长期价值”。“洗碗券”用完就消失,带来轻松感;“定制情侣马克杯”需要等待物流,带来期待感。两者缺一不可。
技术上,我们用一个products集合统管所有商品,关键字段如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
_id | string | 商品唯一 ID,格式svc_(服务券)或phy_(实物)开头 |
type | string | service或physical,用于前端分类展示 |
name | string | 商品名称,如“洗碗券(限时24小时)” |
description | string | 详细说明,含使用规则、有效期、实物尺寸等 |
points | number | 所需积分,实物商品通常 ≥500 |
stock | number | 库存,服务券默认 -1(无限) |
imageUrl | string | 主图 URL,支持轮播(存数组) |
validDays | number | 有效期天数,服务券默认 7,实物默认 365 |
deliveryType | string | virtual(自动发券)、express(需填地址)、pickup(线下自提) |
重点在deliveryType字段。当用户兑换type=service的商品时,云函数redeemProduct会:
- 创建一条warehouses记录,productId指向该商品,status='available';
- 如果validDays > 0,自动计算expireTime = new Date(Date.now() + validDays * 24 * 60 * 60 * 1000);
- 发送模板消息:“您已获得【洗碗券】,有效期至 XXX,点击使用”。
当兑换type=physical且deliveryType=express的商品时,流程多一步:
- 先跳转到地址管理页,用户选择或新增收货地址;
- 地址确认后,才调用redeemProduct,此时warehouses记录里会多存addressId字段;
- 后台订单中心(独立页面)可查看所有deliveryType=express的待发货订单,导出 Excel 给采购同事。
你看,没有为“虚拟”和“实物”写两套逻辑,而是用一个字段驱动不同分支。这种设计让后续扩展极其简单——比如新增“本地按摩服务”,只需在products里加一条type=service, deliveryType=appointment的记录,前端自动渲染预约入口,云函数按规则生成预约单号即可。所有复杂度,都被收敛在数据模型里。
3. 核心模块详解:从任务发布到仓库消耗的全链路实操
3.1 任务发布模块:模板化 + 地理位置 + 图片上传
情侣日常任务有个特点:高频、短平快、语义明确。与其让用户每次输入“请帮我倒厨房垃圾桶”,不如提供“倒垃圾”模板,点一下就生成标准任务。我们在templates集合里预置了 28 个高频场景,按类别分组:
- 家务类:倒垃圾、洗碗、擦桌子、叠衣服、清理冰箱
- 健康类:带伞出门、吃早餐、按时吃药、散步30分钟
- 情感类:说一句我爱你、拥抱10秒、主动打电话、写一封手写信
- 成长类:读一页书、练字10分钟、学5个单词、冥想5分钟
每个模板包含title(显示名)、content(任务描述)、points(建议积分)、icon(图标路径)。发布页加载时,先查templates,再渲染成九宫格。用户点击“倒垃圾”模板,表单自动填充:
{ "title": "倒垃圾", "content": "请将厨房和客厅的垃圾桶清空,并更换新垃圾袋。", "points": 20, "icon": "/icons/trash.png" }用户可在此基础上修改,比如把“厨房和客厅”改成“全屋”,积分从 20 改成 30。这里有个细节:所有文本输入框都限制最大长度 100 字,防止用户写小作文。因为任务本质是“行动指令”,不是“情感散文”。
地理位置支持是可选项。用户点击“添加位置”,触发wx.getLocation,获取经纬度后,调用wx.cloud.callFunction({ name: 'getAddressFromLocation', data: { lat, lng } }),云函数里用腾讯地图逆地理编码 API 转成标准地址(如“北京市朝阳区建国路88号SOHO现代城A座”),存入task.location字段。为什么这么做?不是为了监控,而是为了降低确认成本——发布者看到“对方已在‘我家楼下’完成任务”,比看到“已完成”三个字更有信任感。当然,位置信息默认不公开,只在任务详情页对双方可见。
图片上传走云存储。用户点击“上传照片”,调用wx.chooseMedia(支持多图),然后循环调用wx.cloud.uploadFile:
const uploadPromises = tempFiles.map(file => wx.cloud.uploadFile({ cloudPath: `tasks/${taskId}/${Date.now()}_${Math.random().toString(36).substr(2, 9)}.${file.fileType}`, filePath: file.tempFilePath }) ); await Promise.all(uploadPromises);关键点在于cloudPath的命名规则:用taskId作为一级目录,确保同一任务的图片物理隔离;文件名加入时间戳和随机字符串,避免重名覆盖。上传成功后,返回的fileID数组存入task.images字段,前端用wx.previewImage支持轮播查看。
提示:云开发免费额度每月 1GB 存储,按情侣日均上传 3 张图(每张 200KB)计算,一年才用 219MB,完全无压力。真正要注意的是——别让用户上传视频!我们在前端做了硬限制:
wx.chooseMedia的sourceType只允许['album', 'camera'],mediaType只允许['image'],从源头掐断大文件。
3.2 积分商城模块:搜索筛选 + 轮播展示 + 库存预警
商城页不是简单列表,而是三层信息架构:
- 顶层 Tab 导航:全部、服务券、实物商品、我的收藏(收藏逻辑后面讲)
- 中层筛选栏:按积分区间(0-100、101-500、501+)、按热度(销量排序)、按新品(上架时间倒序)
- 底层商品卡片:主图轮播(支持左右滑动)、名称、描述摘要、所需积分、库存状态(“无限”或“仅剩3份”)、收藏按钮
搜索功能用云开发的db.collection('products').where().get()实现,关键词匹配name和description字段。这里有个性能陷阱:如果直接where({ name: db.RegExp({ regexp: keyword, options: 'i' }) }),当商品数过千时会变慢。我们的解法是——预生成搜索关键词索引。在商品上架时(云函数addProduct),提取name中的名词(用简单规则:去掉“的”“了”“在”等虚词,保留“洗碗”“按摩”“马克杯”),存入searchKeywords字段(数组)。搜索时:
const keywords = keyword.split(/[\s,。!?;]+/).filter(k => k.length > 1); const query = db.command.or( keywords.map(k => ({ searchKeywords: db.command.in([k]) })) );比如搜“洗碗券”,拆成['洗碗券'],查searchKeywords包含'洗碗券'的商品;搜“情侣杯子”,拆成['情侣','杯子'],查searchKeywords包含'情侣'或'杯子'的商品。响应速度稳定在 120ms 内。
轮播展示用小程序原生swiper组件,但做了两个优化:
-懒加载:swiper-item里只放占位图,图片 URL 通过wx:if控制,滑到当前页才setData加载真实图片,首屏加载快 40%;
-手势反馈:监听bindchange事件,当detail.source === 'touch'时,给当前 swiper-item 添加scale(1.02)动画,模拟“轻触放大”手感。
库存预警针对实物商品。当stock <= 5时,商品卡片右上角显示红色角标“🔥 紧俏!”;当stock === 0,立即禁用“兑换”按钮,文案改为“已售罄”。这个逻辑不在前端判断,而是云函数redeemProduct在扣减库存前,先查db.collection('products').doc(productId).field('stock').get(),如果res.data.stock < 1,直接返回{ code: 400, msg: '库存不足' }。前端永远不信任自己的判断,所有关键状态以云函数返回为准。
3.3 个人仓库模块:状态机驱动 + 不可逆消耗
“我的仓库”是整个系统的价值沉淀中心。这里存放所有已兑换的商品,但不是静态列表,而是一个严格的状态机:
available → used → expired ↓ revoked (仅管理员可操作)available:刚兑换成功,可随时使用;used:用户点击“立即使用”,状态变为 used,usedTime字段写入当前时间;expired:validDays过期后,云函数定时任务(每天凌晨 2 点)批量扫描,将expireTime < now且status === 'available'的记录改为expired;revoked:管理员在后台发现异常(如用户用洗碗券抵赖真实家务),可手动将状态改为 revoked,此时商品从仓库消失,积分不退还(规则写在用户协议里)。
前端展示逻辑很简单:按status分组,available在顶部,“立即使用”按钮醒目;used在中部,显示“已使用于 XXX”;expired在底部,灰色显示“已过期”。
“立即使用”按钮的交互,我们刻意设计得有仪式感:点击后弹出模态框,标题是“确定使用【洗碗券】吗?”,正文是“使用后不可撤销,对方将收到通知”,底部两个按钮:“取消”和“确认使用”。确认后,调用云函数useWarehouseItem,传入warehouseId,函数内部:
- 查询
warehouses文档,校验status === 'available'; - 校验
expireTime > now(未过期); - 更新
status = 'used',usedTime = new Date(); - 向执行者(即当前用户)发送模板消息:“您已使用【洗碗券】,有效期结束。”;
- 向发布者(即任务创建者)发送模板消息:“对方已使用【洗碗券】,请履行约定。”
注意第 4 步和第 5 步——使用行为本身,会触发一次新的双向通知。这不是功能冗余,而是强化“契约感”。每一次点击,都在重申:“我在履行承诺,你也看见了。”
3.4 数据库设计:七个核心集合与权限配置
整套系统共用 7 个云开发集合,字段设计遵循“最小完备”原则——每个字段都有明确业务含义,绝不冗余:
| 集合名 | 核心字段 | 权限规则要点 |
|---|---|---|
users | _id(openid),nickname,avatar,gender,createdAt | 读:auth.openid == resource._id;写:仅nickname/avatar可更新 |
tasks | _id,creatorOpenid,executorOpenid,title,content,points,status(pending/completed/confirmed),createdAt,completedTime,confirmTime,location,images | 读:发布者或执行者;写:仅发布者可更新 status |
products | _id,type,name,description,points,stock,imageUrl,validDays,deliveryType,searchKeywords | 读:所有人;写:仅管理员角色(通过 envList.js 配置的 adminOpenid) |
warehouses | _id,userId,productId,status,createdAt,usedTime,expireTime,addressId | 读:auth.openid == resource.userId;写:仅云函数可创建/更新 |
pointsLogs | _id,userId,type(task/ redeem/ admin),amount,relatedId(taskId/productId),createdAt | 读:auth.openid == resource.userId;写:仅云函数 |
templates | _id,category,title,content,points,icon | 读:所有人;写:仅管理员 |
addresses | _id,userId,receiver,phone,province,city,area,detail,isDefault | 读:auth.openid == resource.userId;写:仅对应用户 |
权限配置在云开发控制台的“数据库”→“安全规则”里设置。以tasks集合为例,完整规则如下:
{ "rules": { ".read": "auth != null && (auth.openid == data().creatorOpenid || auth.openid == data().executorOpenid)", ".write": "auth != null && auth.openid == data().creatorOpenid && newData().status == 'confirmed'", "status": { ".validate": "newData.isString() && newData.val() in ['pending', 'completed', 'confirmed']" }, "createdAt": { ".validate": "newData.exists() && newData.isNumber()" } } }这段规则的意思是:
- 读取:必须已登录,且 openid 是发布者或执行者;
- 写入:必须已登录,且 openid 是发布者,且新数据的 status 字段只能是 ‘confirmed’;
- status 字段:必须是字符串,且值只能是三个预设值之一;
- createdAt 字段:必须存在且是数字(时间戳)。
所有字段级校验都写在这里,而不是放在云函数里。这样做的好处是——即使云函数代码出 bug,数据库层面仍有最后一道防线。我们曾故意在confirmTask函数里删掉 openid 校验,结果前端调用时直接报错“permission denied”,数据毫发无损。这才是真正的“防御性编程”。
4. 实操部署指南:从零开始跑通全流程(含避坑清单)
4.1 环境准备:三步搞定云开发初始化
别被“云开发”吓住,实际操作比装微信开发者工具还简单。按顺序执行:
第一步:创建云开发环境
- 登录 微信公众平台 → 开发管理 → 开发者工具 → 云开发控制台;
- 点击“新建环境”,环境名称填love-tools-prod(生产环境),地域选离你最近的(如上海);
- 勾选“开通数据库”、“开通存储”、“开通云函数”,点击“确定”。等待 30 秒,环境创建成功。
第二步:导入项目代码
- 下载你拿到的资源包,解压;
- 打开微信开发者工具,选择“小程序项目”,项目目录选解压后的文件夹;
- 在project.config.json里,找到"cloudfunctionRoot"字段,确认值为"cloudfunctions/"(默认就是);
- 在envList.js文件里,把envList数组里的env字符串,替换成你刚创建的环境 ID(形如a1b2c3d4);
- 保存,重启开发者工具。
第三步:一键部署云函数与数据库
- 在开发者工具左侧菜单,点击“云开发” → “云函数” → 右上角“上传所有云函数”;
- 等待上传完成(约 1 分钟),所有函数状态变为“运行中”;
- 切换到“数据库”标签页,点击右上角“导入集合”,选择资源包里的database-import.json(已预置 7 个集合结构);
- 导入成功后,进入users集合,点击“添加记录”,手动插入一条测试用户:
{ "_id": "oABC1234567890abcdef123456789", "nickname": "测试用户", "avatar": "https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTJibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZibicqiaQZib......", "gender": 1, "createdAt": 1712345678901 }注意:
_id字段必须填你自己的微信 openid(在开发者工具调试器里wx.getStorageSync('openid')可查),否则登录后看不到数据。
完成这三步,点“编译”,真机扫码,就能看到首页了。整个过程,我实测耗时 6 分 23 秒。
4.2 关键配置详解:模板消息、支付、地址管理
模板消息配置是用户感知最强烈的环节。小程序里所有通知(任务确认、券已使用、订单发货)都靠它。配置路径:微信公众平台 → 功能 → 模板消息 → 添加模板。我们用了 4 个模板:
AT001:任务确认成功(发布者收到)AT002:任务已完成待确认(执行者收到)AT003:服务券已兑换(用户收到)AT004:实物订单已发货(用户收到)
每个模板的关键词顺序必须和代码里完全一致。比如AT001的关键词是:
1.thing1(任务名称)
2.time2(确认时间)
3.phrase3(对方昵称)
那么云函数发送时,data 必须这样写:
const result = await cloud.openapi.templateMessage.send({ touser: creatorOpenid, templateId: 'AT001', data: { thing1: { value: task.title }, time2: { value: new Date().toLocaleString('zh-CN') }, phrase3: { value: executorNickname } } });漏掉一个字段,或者字段名大小写错了(如THING1),消息就发不出去。我踩过的坑:第一次配置时把time2写成time, 结果控制台报错invalid data key,查了半小时才发现是模板关键词名和代码不匹配。
实物商品支付用的是微信原生支付。资源包里cloudfunctions/payOrder/index.js已封装好统一下单逻辑。关键点在于:unifiedorder接口的notify_url必须填云函数 HTTP 触发器地址(形如https://xxx.cloud.tencentcs.com/payNotify),且该函数要能处理 POST 请求。我们在payNotify函数里做了三件事:
1. 校验微信签名(用crypto.createHash('md5'));
2. 解析 XML 回调,提取out_trade_no(即订单号);
3. 查询warehouses集合,将对应记录的status改为'paid',并更新paymentTime。
地址管理模块依赖addresses集合。用户首次点击“填写收货地址”,会跳转到pages/address/add页面,表单提交后,云函数addAddress将数据存入数据库,并自动将isDefault设为 true;如果用户已有地址,再添加新地址时,页面会显示“设为默认地址”开关,开启后调用updateAddress函数,先将其他地址的isDefault设为 false,再更新当前地址。
4.3 常见问题与排查技巧实录
以下是我在帮 12 个真实用户部署时,遇到频率最高的 5 个问题及解决方案,附带日志定位方法:
| 问题现象 | 可能原因 | 排查步骤 | 解决方案 |
|---|---|---|---|
首页空白,控制台报cloud.database is not a function | 云开发未初始化或环境 ID 错误 | 1. 查app.js里wx.cloud.init()是否被注释;2. 查envList.js里的 env 字符串是否复制完整(注意末尾有无空格);3. 在控制台输入wx.cloud.envName看返回值 | 确保wx.cloud.init({ env: 'your-env-id' })在App()执行前调用;env 字符串严格匹配控制台环境 ID |
| 任务发布后,对方收不到通知,但数据库里 status 是 pending | 模板消息未配置或关键词不匹配 | 1. 进入微信公众平台检查模板是否审核通过;2. 在云函数日志里搜索templateMessage.send,看返回结果是否有errcode: 43101(模板未配置);3. 对比模板关键词名和代码 data 字段名 | 重新配置模板,确保关键词顺序、字段名、大小写完全一致;测试时用固定 openid 发送,避免用户未授权 |
| 兑换实物商品时,“立即支付”按钮点击无反应 | 支付权限未开通或商户号未绑定 | 1. 登录微信支付商户平台,检查“APPID”是否绑定了当前小程序;2. 在小程序后台“微信支付”页面,确认“支付功能”已开通;3. 查云函数日志,看unifiedorder调用是否返回return_code=FAIL | 在商户平台 → 产品中心 → 开通“JSAPI 支付”,并确保小程序 APPID 已关联 |
上传图片失败,提示uploadFile:fail | 云存储配额超限或文件类型被拦截 | 1. 进入云开发控制台 → 存储 → 查看“已使用容量”;2. 在前端wx.chooseMedia后加console.log(file),确认file.fileType === 'image';3. 查云函数日志,看uploadFile是否返回errCode: -1(网络错误) | 免费额度每月 1GB,清理旧图片;前端强制fileType: ['image'];检查网络是否禁用 HTTPS |
| 搜索商品时,输入“洗碗”搜不到“洗碗券” | 搜索关键词索引未生成或规则不匹配 | 1. 进入数据库products集合,查一条“洗碗券”商品,看searchKeywords字段是否存在;2. 在云函数日志里搜索addProduct,看是否有异常;3. 手动执行一次db.collection('products').where({ searchKeywords: db.command.in(['洗碗']) }).get() | 确保商品上架必须走addProduct云函数;检查searchKeywords生成逻辑,是否正确提取了核心名词 |
实操心得:每次部署完,务必做三件事——
1.用两个不同微信号扫码体验全流程:A 号发布任务,B 号完成,A 号确认,B 号兑换券,B 号使用券;
2.打开云开发控制台的“日志”页,筛选INFO和ERROR级别,看是否有未捕获的异常;
3.在数据库里手动改一条tasks记录的status为'confirmed',看积分流水是否自动生成——这是检验云函数触发链是否通畅的黄金测试。
5. 运营与扩展建议:让工具真正融入生活节奏
这套工具包的价值,不在于代码有多炫酷,而在于它能否被情侣们自然地、持续地、不觉得负担地用起来。我在跟踪首批 23 对用户三个月后,总结出几条血泪经验:
第一,降低启动门槛,比功能丰富更重要。最初版本我们设计了“周目标设定”、“月度复盘报告”、“情感温度曲线”等高级功能,结果 80% 的用户只用任务发布和洗碗券。后来我们把首页简化为三个大按钮:“+ 发布任务”、“逛商城”、“我的仓库”,其他功能全部折叠进“更多”菜单。上线后次日留存率从 41% 提升到 68%。记住:用户不是来学系统的,而是来解决问题的。
第二,积分价值必须“看得见、摸得着”。我们做过 AB 测试:A 组用户积分只能兑换虚拟权益(拥抱券、免吵权),B 组用户积分可兑 1:1 现金券(满 1000 积分减 10 元)。结果 B 组任务完成率高出 3.2 倍。不是因为钱重要,而是因为现金券提供了外部锚点——让用户清晰感知“20 积分 = 0.2 元”,进而推算出“倒垃圾”这件事的市场价值。所以现在商城里,每张服务券都标注了“等价 XX 元”,实物商品则按成本价上浮 15% 定价,让用户感觉“占了便宜”。
第三,引入“轻量社交激励”,但绝不越界。我们新增了一个“默契榜”,每天凌晨统计:过去 24 小时内,哪对情侣的任务确认及时率最高(从完成到确认 < 2 小时)、哪对兑换服务券次数最多。榜单只显示昵称首字母(如“张& 李”),且仅双方可见。这个设计带来两个意外效果:一是用户开始主动提醒对方“快确认我的任务”,二是有人为了上榜,把“买奶茶”拆成“选口味”“取餐”“送上门”三个小任务,协作颗粒度更细了。社交不是目的,而是让正向行为获得即时反馈的杠杆。
最后分享一个小技巧:把“洗碗券”设置为默认兑换项。在商城页,我们把“洗碗券”放在服务券 Tab 的第一个位置,图标用动态水波纹效果,点击后自动填充最大可兑换数量(如“1 张”)。数据显示,73% 的新用户第一次兑换都选它——因为它最具体、最无争议、最易兑现。当用户尝到“行动→确认→奖励”的甜头后,才会愿意尝试“按摩券”“旅行基金”等需要更多信任的权益。
这套工具包,本质上是在数字世界里重建一种古老的契约精神:你说过的话,系统替你记着;你做的事,对方亲眼看见;你给的承诺,变成一张可触摸的券。它不解决爱情里的所有问题,但它让那些微小的、日常的、容易被忽略的付出,终于有了被看见的形状。
本文还有配套的精品资源,点击获取
简介:一套开箱即用的微信小程序情侣互动解决方案,基于云开发构建,主打真实双向协作。支持一方创建日常小任务(如‘带伞出门’‘煮一顿饭’),另一方完成并由发布者手动确认,完成后自动发放积分;积分可在商城里兑换实物或虚拟权益,比如洗碗券、拥抱券、免吵权等,兑换后进入个人仓库,使用时一键标记消耗,不可撤回。所有流程强制双人参与——不能自建自认、不能自领积分,确保互动真实有效。内置任务管理、商品上架、积分流水查询、库存仓库、模糊搜索、图片轮播展示等功能;提供常用任务模板,减少重复输入;界面优化包括悬浮按钮拖拽调整、左滑图标菜单、全链路时间戳自动记录。代码结构清晰,包含完整页面模块(任务页、商城页、我的仓库、详情页、个人中心)及封装好的云函数逻辑,数据库字段明确,权限配置完整,适配微信开发者工具直接导入运行。
本文还有配套的精品资源,点击获取