简介:这是一份微信小程序名片管理系统的完整源码与数据库工程,定位为毕业设计模板与业务型小程序开发参考,适合学生、个人开发者以及需要快速搭建员工名片库的企业技术团队。项目覆盖普通用户与管理员两类角色,包含员工名片、联系人名片、公司信息、留言板、新闻通知等常见业务模块,前端使用WXML/WXSS和JS编写,后端由Java实现,并配有SQL数据库脚本。压缩包共737个文件,大小26.1MB,除上述核心代码外,还包含PNG、JPG等界面图片以及HTML、XML等辅助文件,目录按页面、控制层、实体类等分层,便于定位与修改。目前已有302人学习浏览。通过阅读源码,可以掌握小程序登录注册、名片增删改查、权限分级、数据同步及后台接口设计的完整链路,也能借鉴数据库表关系和项目结构,为同类管理系统二次开发提供直观范本。
1. 微信小程序名片管理系统这个zip里,真正值钱的是那张名片表
拿到“微信小程序名片管理系统源码数据库.zip”这个文件,很多人第一反应是赶紧解压、赶紧导入开发者工具、赶紧跑起来看看页面长什么样。但按我的经验,这套源码包真正值钱的不是小程序前端那几十个页面文件,而是压缩包里那份数据库脚本——名片表设计得合理,后面所有功能都是往表里插数据、查数据;表设计得别扭,换名片、分组、搜索、扫码互存这些需求每加一个都要改一次表结构,越改越难受。
这套东西适合谁?一类是做外包交付的,客户要一个能扫码、能编辑、能展示的电子名片小程序,拿这套源码改改 Logo 和字段就能交付;另一类是运营或产品经理拿它当原型底座,测试“名片 + 私域引流”的流程是否跑得通。本篇按我实际落地这套系统的顺序来讲:先摸清工程边界,再建库导数据,然后跑通小程序的数据链路,最后把踩过的坑和增值玩法一次说清。
2. 源码包结构与技术栈摸底:先看懂工程边界,再决定从哪下手改
2.1 为什么这套名片系统的主流选型是原生小程序+MySQL,而不是更轻的云开发
名片管理系统的功能边界很清晰:用户登录、名片增删改查、扫码查看对方名片、可能再加一个分组收藏。这个量级的功能,用原生微信小程序开发完全够用,没必要上 Taro 或 uni-app 这类跨端框架——小程序端打包体积更小、调试更直接,改一个按钮样式不用等编译链路跑完。
后端和数据库的选择上,市面上流传的这类源码包最常见的是 PHP 或 Node.js 提供接口,数据落在 MySQL。MySQL 在这个场景下有三个实在的理由:第一,SQL 脚本是通用的交付格式,拿到 zip 后无论是用命令行、Navicat 还是 dbx 数据库工具都能导库,不挑环境;第二,名片数据量级小,一张表几万行对 MySQL 来说毫无压力,不需要引入更重的中间件;第三,团队成员接手时对 MySQL 的熟悉度普遍最高,后续维护成本最低。
还有一类是云开发方案,数据库用云端的 JSON 文档型存储,免运维。但这类方案和“源码 + 数据库 zip”的交付形态天然冲突——云开发没有独立的数据库脚本可以打包交付,你拿到手的 zip 里不会有“.sql”文件。所以这套标题既然强调了“数据库”,那对应的交付形态几乎可以确定是传统后端 + 关系型数据库。
2.2 拿到zip的第一步:按文件清单核对工程,别急着双击运行
解压后我一般不会急着打开开发者工具,而是先按下面这张表核对一遍文件结构,缺了哪块后面会出哪种问题,心里先有数。
| 文件/目录 | 用途 | 缺失时的典型症状 |
|---|---|---|
| project.config.json | 开发者工具的项目配置,含 appid、编译设置 | 工具无法识别项目,导入后白屏 |
| app.json | 小程序页面路由与窗口配置 | 启动后找不到首页,报错 page not found |
| pages/ 目录 | 名片列表、详情、编辑、登录等页面 | 跳转失效,编译报找不到路径 |
| utils/ 目录 | request 封装、工具函数 | 页面请求散落各处,改后端地址要全局替换 |
| api/ 或 server/ 目录 | 后端接口源码 | 前端能起,但所有数据请求都失败 |
| 数据库脚本(.sql 文件) | 建库建表语句和初始数据 | 后端连不上库,登录接口直接抛 500 |
解压时有一个高频坑:Windows 下解压到带中文或空格的路径,比如“桌面\新建文件夹 (2)”,某些老版本开发者工具的编译链路会间歇性读不到文件。我习惯把工程放到纯英文且无空格的目录,比如 D:\projects\card-system,避免这类连带问题。
2.3 前端工程的三个入口文件:app.json、project.config.json 与全局环境变量
改这套源码,最先动的一定是这三个文件。app.json 负责页面注册,新增页面时要在 pages 数组里加一行,漏了这步运行时会报“page not found”。project.config.json 里有 appid 配置:测试阶段可以填测试号,但测试号拿不到完整的手机号快捷登录能力,真机预览时很多接口会受限,所以尽早换成自己注册的小程序 appid。
第三个关键是全局环境变量。多数源码会在 app.js 里放一个 globalData,里面写着后端接口的 baseUrl。本地调试阶段这里填局域网地址,比如 http://192.168.1.100:8080,上线前要改成已备案并配置了 HTTPS 的正式域名。这个地址全工程只用改这一处,前提是页面里没有到处硬编码请求路径——拿到源码先全局搜一下“http://”,凡是出现在业务页面的,都建议改成引用全局变量,不然后面换环境时漏改一处就要排查半天。
// app.js 片段 App({ globalData: { baseUrl: 'http://192.168.1.100:8080/api' // 本地联调用局域网IP,上线换HTTPS域名 } });这段代码的作用是把后端地址收敛到一处。参数说明:本地调试用 http + 局域网 IP,是因为开发者工具模拟器可以直接访问你电脑上的后端服务;真机预览时手机必须和电脑在同一 Wi-Fi 下;正式上线则必须替换为小程序后台配置的合法 HTTPS 域名,否则真机请求会被拦截。
3. 数据库初始化与表结构设计:名片系统的地基是 user 表和 card 表的关联
3.1 名片主表字段设计:姓名、手机号之外的三个扩展位
名片表的字段设计直接决定了后面所有功能的实现成本。最基本的字段是 id、用户标识、姓名、手机号、公司、职位、头像、邮箱、备注,这些不用多说。我要强调的是三个容易被忽略的扩展位。
第一个是 status 状态位。名片系统必然会出现“用户删除了名片”或“管理员下架了某张违规名片”的场景,用 status 做软删除,比物理 DELETE 多条记录安全得多。第二个是 label 标签字段,名片分组、行业标签都靠它,用逗号分隔的字符串存最简单,数据量大了再拆关联表。第三个是备注字段,我给这个字段留 TEXT 类型,因为用户可能在里面粘贴很长一段客户描述,VARCHAR(255) 不够用。
CREATE TABLE `card` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `user_id` INT UNSIGNED NOT NULL COMMENT '关联用户表', `name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '姓名', `mobile` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '手机号', `company` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '公司名称', `position` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '职位', `email` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '邮箱', `avatar` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '头像URL', `label` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '标签,逗号分隔', `status` TINYINT NOT NULL DEFAULT 1 COMMENT '1启用 0停用', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`), KEY `idx_mobile` (`mobile`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='名片表';逻辑说明:user_id 关联用户表,一张名片归属于一个用户;status 做逻辑删除,列表查询默认加 status=1 条件。参数说明:mobile 加普通索引而不是唯一索引,因为同一手机号可能属于同一公司的多个员工名片,唯一索引会导致第二条插不进去;updated_at 用 ON UPDATE 时间戳,改名片时它会自动刷新,省一行更新代码。
3.2 从SQL脚本到本地库:字符集、DROP语句和导入顺序的细节
数据库初始化这一步,我见过最多的翻车场景有两个:一是建库时没指定 utf8mb4,导致后面所有中文都变成问号;二是 SQL 文件开头带着 DROP TABLE 语句,在别人正在用的库上执行直接把老表清了。拿到 SQL 文件先打开看一眼开头几行,这是必须养成的习惯。
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS card_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" mysql -u root -p card_system < card_system.sql逻辑说明:第一条命令创建数据库并指定字符集,utf8mb4 是目前最稳妥的中文方案,emoji 也能正常存;第二条命令把 SQL 文件导入到刚建好的库里。参数说明:-p 后面会提示输入密码,如果本机 MySQL 密码为空,可以去掉 -p 直接执行;这里刻意不用 Navicat 的可视化导入,是因为命令行方式能清楚看到每一条报错,方便定位哪张表的结构有问题。
导入完成后建议做一次验证:查询一下表数量和关键表的字段结构,确认导入前后一致。
USE card_system; SHOW TABLES; DESC card;3.3 一人多名片与换绑场景:为什么不能把openid直接做主键
很多初版源码会把微信 openid 直接写在名片表里当用户标识,这样省一张用户表。但实际运营中会立刻撞上两个需求:第一,一个微信用户可能创建多张名片,比如一个销售同时维护自己和个人品牌两个身份;第二,用户可能换微信登录,或者管理员要代客维护名片。openid 一旦成为名片表的主键,这两个场景都拆不动。
正确的结构是拆出独立的 user 表,card 表只存 user_id,业务上通过 user_id 间接关联 openid。这样一张名片换归属时只需要改 user_id,一个用户拥有多张名片也只需要在 card 表插多条记录。
CREATE TABLE `user` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `openid` VARCHAR(64) NOT NULL COMMENT '微信openid', `phone` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '手机号', `nickname` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '昵称', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_openid` (`openid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='微信用户表';逻辑说明:openid 在 user 表加唯一索引,同一个微信号只能注册一次,但可以在 card 表建多张名片。参数说明:如果后续要做“微信小程序登录获取手机号”,user 表的 phone 字段就是解密后回填的位置;nickname 字段按需保留,有的名片系统用不到可以删除。
3.4 后端增删改查的最小接口形态:token校验前置、参数校验后置
数据链路里最容易写歪的是后端接口的组织方式。小项目用单入口 + action 分发最直观,维护成本也最低。我习惯把公共逻辑放在最前面:先校验 token 拿到当前用户,再做参数校验,最后才操作数据库。
<?php // api/card.php 单入口分发示例 $action = $_GET['action'] ?? $_POST['action'] ?? ''; $userId = checkToken($_SERVER['HTTP_AUTHORIZATION'] ?? ''); switch ($action) { case 'list': $stmt = $pdo->prepare('SELECT * FROM card WHERE user_id = ? AND status = 1'); $stmt->execute([$userId]); echo json_encode(['code' => 0, 'data' => $stmt->fetchAll(PDO::FETCH_ASSOC)]); break; case 'save': // 先校验参数再落库,字段缺了直接返回错误 break; default: echo json_encode(['code' => 1, 'msg' => 'action not found']); }逻辑说明:checkToken 在分发之前执行,拿不到 userId 就提前返回 401,后续业务逻辑不用重复判断登录态。参数说明:switch 的 case 分支按业务继续加,名片增删改查对应 list/save/delete 三个动作就够;SQL 用预处理语句,不要拼接字符串,这是防注入的最低要求。
4. 小程序端跑通数据链路:从微信手机号登录到名片列表与编辑
4.1 微信小程序登录获取手机号:授权按钮、wx.login 与后端解密
名片系统的登录不能只拿 openid,因为名片上最核心的展示字段是手机号。这里的关键步骤是:用户点击带 open-type="getPhoneNumber" 的按钮,拿到加密数据;同时调 wx.login 拿到临时 code;前端把 code、encryptedData、iv 三个值交给后端,后端用 code 换 session_key 再解密手机号。
<!-- pages/login/login.wxml --> <button class="phone-btn" open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumber" >微信手机号一键登录</button>// pages/login/login.js Page({ onGetPhoneNumber(event) { if (event.detail.errMsg !== 'getPhoneNumber:ok') { wx.showToast({ title: '需要授权手机号才能创建名片', icon: 'none' }); return; } wx.login({ success: (res) => { wx.request({ url: getApp().globalData.baseUrl + '/login', method: 'POST', data: { code: res.code, encryptedData: event.detail.encryptedData, iv: event.detail.iv }, success: (r) => { wx.setStorageSync('token', r.data.token); wx.navigateBack(); } }); } }); } });逻辑说明:先判断授权结果是否成功,用户点拒绝就直接弹提示;授权成功后再走 wx.login 换 code,这里要注意 wx.login 每次返回的 code 都不同,且只能用一次。参数说明:code 由后端拿去调微信接口换 openid 和 session_key;encryptedData 和 iv 是手机号密文和解密向量,必须原样透传,不能在前端做任何字符串处理,否则后端解密必失败。
后端拿到 code 后,先调微信的 code2Session 接口,再用 session_key 配合 AES 算法解出手机号。解密失败时前端最常见的报错就是错误码 10002,这个放到下一章讲排查。
4.2 列表页的三个基本功:请求封装、onShow刷新、下拉刷新
名片列表页是用户打开小程序后看到的主界面,核心诉求就三个:请求路径统一、数据实时刷新、下拉能重新拉取。请求封装建议收敛到 utils/request.js,所有页面都走这一个入口,后端地址、token 注入、错误提示都只维护一份。
// utils/request.js const BASE_URL = getApp().globalData.baseUrl; function request(path, method, data) { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + path, method: method || 'GET', data: data || {}, header: { 'Authorization': wx.getStorageSync('token') || '' }, success: (res) => { if (res.data.code === 0) { resolve(res.data.data); } else { wx.showToast({ title: res.data.msg || '请求失败', icon: 'none' }); reject(res.data); } }, fail: (err) => { wx.showToast({ title: '网络异常,请确认后端已启动', icon: 'none' }); reject(err); } }); }); } module.exports = { request };逻辑说明:header 里注入 token,后端每收到一个请求都先校验它;业务 code 为 0 表示成功,其余情况统一弹错误提示。参数说明:BASE_URL 从 globalData 读取,切换环境只改 app.js 一处;如果后端接口要求 token 放在其他字段而不是 Authorization,改这里一个地方就能全局生效。
列表页的刷新有一个高频坑:很多人把数据请求写在 onLoad 里,结果从详情页返回列表时数据不刷新。因为 onLoad 只在页面第一次加载时执行一次,正确的位置是 onShow。
const { request } = require('../../utils/request'); Page({ data: { cards: [], loading: false }, onShow() { this.loadCards(); }, onPullDownRefresh() { this.loadCards().then(() => wx.stopPullDownRefresh()); }, loadCards() { this.setData({ loading: true }); return request('/cards', 'GET') .then((list) => this.setData({ cards: list, loading: false })); } });逻辑说明:onShow 每次进入页面都会触发,从详情页返回、从后台切回前台都会重新拉取列表,保证数据最新。参数说明:onPullDownRefresh 需要在 app.json 的 window 配置里把 enablePullDownRefresh 设为 true,否则下拉事件不会触发。
4.3 名片编辑和图片上传:chooseMedia 与 uploadFile 的参数对应关系
名片编辑页最常出问题的是头像上传。小程序端用 wx.chooseMedia 选图片,再用 wx.uploadFile 传到后端,这两个 API 是分开的。uploadFile 不是 wx.request,它不走统一的请求封装,所以 header 里的 token 要单独带上。
// pages/card/edit.js wx.chooseMedia({ count: 1, mediaType: ['image'], success: (res) => { const filePath = res.tempFiles[0].tempFilePath; wx.uploadFile({ url: getApp().globalData.baseUrl + '/upload', filePath: filePath, name: 'file', header: { 'Authorization': wx.getStorageSync('token') }, success: (resp) => { const data = JSON.parse(resp.data); this.setData({ avatar: data.url }); } }); } });逻辑说明:chooseMedia 返回临时文件路径,uploadFile 以 multipart/form-data 形式把文件传给后端。参数说明:name 必须和后端接收文件字段的参数名一致,后端用 $_FILES['file'] 取,名称对不上会一直报“未收到文件”;resp.data 如果是 JSON 字符串,需要 JSON.parse 之后再取数据。
5. 踩坑清单:这套名片源码最常见的六个翻车点与排查顺序
5.1 真机预览报 request:fail,先查局域网IP和合法域名,再查代码
现象:开发者工具里页面数据正常,一换真机预览所有请求全部失败,报错集中在“request:fail”开头。
原因:开发者工具默认不校验合法域名,但真机上微信会强制校验;另外手机和电脑不在同一局域网时,请求自然连不通。
解决:分两步排查。第一步确认手机和电脑连的是同一个 Wi-Fi,后端服务监听地址是 0.0.0.0 而不是仅本机回环;第二步在微信公众平台的小程序后台,把后端域名加入 request 合法域名列表,域名必须是 HTTPS 且已备案。本地联调阶段嫌麻烦可以在开发者工具里勾选“不校验合法域名”,但真机预览这个选项不生效。
5.2 中文名变问号:建库、连接、显示三层字符集要一起改
现象:数据库里手动插入的中文正常,但小程序提交过来的“张三”存进去变成“???”。
原因:utf8mb4 的坑往往不在建库这一层,而是后端 PDO 连接串没指定字符集,或者表本身还是老旧的 latin1 编码。
解决:三层字符集全部统一为 utf8mb4。第一层建库语句带上 DEFAULT CHARACTER SET utf8mb4;第二层后端连接串加 charset=utf8mb4,以 PDO 为例是 new PDO($dsn, $user, $pass, [PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"]);第三层确认每一张表的字符集,用 ALTER TABLE card CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci 强制转换。
5.3 获取手机号失败带错误码 10002:session_key 失效的典型症状
现象:用户第一次授权手机号成功,第二次再授权时后端报解密失败,错误码指向 10002。
原因:10002 对应的场景是 session_key 过期或无效。wx.login 拿到的 code 换出的 session_key 有时效,如果前端缓存了上一次的 session_key 反复用,第二次解密时微信服务端已经认为它失效了。
解决:每次调用获取手机号之前,强制重新走一遍 wx.login 拿新 code,后端用新 code 重新换 session_key 再解密。不要在登录成功之后把 session_key 存到 storage 里复用,这是最容易被忽略的细节。
5.4 扫小程序码进来看的是别人的名片:scene参数解析与缓存清空
现象:用户 A 扫用户 B 分享的小程序码,打开的详情页展示的却是 A 自己的名片,或者展示的还是上一次查看的名片。
原因:详情页没有解析 scene 参数,或者页面从全局变量/缓存里读了上次遗留的 cardId。
解决:小程序码生成的 scene 参数默认是 URL 编码的,在 onLoad 里先取 options.scene,再 decodeURIComponent 一次,解析出 cardId 之后用这个值去拉取详情,而不是从任何缓存里读。
onLoad(options) { const scene = decodeURIComponent(options.scene || ''); const cardId = scene.replace('cardId=', ''); if (cardId) { this.setData({ cardId: cardId }); this.loadDetail(cardId); } }逻辑说明:scene 参数携带的信息量有限,通常只放一个业务主键;解析后立即触发详情请求。参数说明:如果场景里既有名片 ID 又有来源渠道,比如 cardId=1001&source=wechat,建议用 URLSearchParams 解析,不要用字符串 replace。
5.5 导入SQL报语法错误或误删线上表:导入前的三道检查
现象:SQL 文件在本地导入正常,换到服务器上导入报语法错误;或者更严重,导入后原有的业务数据被清空。
原因:SQL 文件是旧版本 MySQL 导出,语法与新版不完全兼容;文件头部如果带了 DROP TABLE 语句,导入到已有数据的库上就是灾难。
解决:导入前做三道检查。第一道,用文本编辑器打开 SQL 文件,搜索“DROP”,确认是否有删表语句,有就注释掉或删掉;第二道,确认库名是否与后端配置一致,导错库是另一个高频事故;第三道,先在一台临时库上完整执行一遍,确认没有语法报错再操作目标库。这套流程五分钟就能跑完,但能避免绝大多数数据库层面的翻车。
5.6 解压后开发者工具打不开或白屏:目录层级与导入方式的坑
现象:zip 解压后,开发者工具导入项目时提示“找不到 app.json”,或者能打开但页面白屏。
原因:解压后工程多包了一层目录。比如压缩包内层是“card-system-master/”,解压后路径变成了“下载文件夹/card-system-master/”,导入时选错了层级,工具找不到 project.config.json。
解决:导入时逐层进入目录,确认选中的那一层直接包含 app.json、project.config.json,而不是还套着一个外层文件夹。另外,开发者工具要用“导入项目”功能选择目录,不要直接拖拽文件到工具窗口,拖拽方式经常拿不到正确的项目配置。
6. 把名片系统从“能用”做到“可运营”:带参小程序码、访客记录与订阅消息
6.1 用scene参数生成带参小程序码,扫码进详情页
基础版名片系统是用户收藏或手动搜索进入小程序,但可运营的版本必须让名片自己会传播。这里用微信的 wxacode.getUnlimited 接口给每张名片生成专属小程序码,把名片 ID 塞进 scene 参数。
前端扫码进入时,scene 会被当作查询参数传给详情页,解析出名片 ID 后直接定位到对应名片。这个设计比分享普通链接稳得多:普通链接在小程序内跳转经常被拦截,带参小程序码则是微信官方支持的传播形态。
生成小程序码时有两个参数要注意:一个是 scene,长度限制 32 个可见字符,所以放 cardId 这样短的主键,不要把整个名片 JSON 塞进去;另一个是 page,必须填写小程序内已存在的页面路径,否则扫码报错。
# 后端调用微信接口生成带参小程序码(伪代码示意) POST https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN body: { "scene": "cardId=1001", "page": "pages/detail/index", "width": 430 }逻辑说明:返回的是图片二进制流,后端保存到服务器并回传 URL,名片详情页直接读取这张图展示给用户。参数说明:width 控制码图尺寸,默认 430 像素足够清晰;如果页面路径带参数,参数只能走 scene,page 里不能带自定义查询串,这是微信接口的硬限制。
6.2 沉淀一张访问记录表,把名片变成轻量CRM的入口
名片被扫码、被打开,这些动作天然就是线索。给详情页加埋点,每次访问写一条记录,攒一段时间就有了一张可分析的访客表。
CREATE TABLE `card_visit` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `card_id` INT UNSIGNED NOT NULL COMMENT '被访问的名片', `visitor_openid` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '访客openid', `source_scene` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '访问来源', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_card_id` (`card_id`), KEY `idx_visitor_openid` (`visitor_openid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='名片访问记录表';逻辑说明:每一次详情页加载,前端把 scene 解析出的 cardId 和后端识别出的访客 openid 一起写入这张表。参数说明:source_scene 字段记录用户是从哪个渠道扫码进来,比如线下海报、微信好友、朋友圈,后续做渠道效果对比时直接按这个字段分组统计。
最后补一个运营级功能:名片被查看后,owner 收到一条服务通知。小程序端在用户查看名片时弹一次订阅消息授权,对方允许后,每次有新访客访问就推送模板消息。这一步实现不复杂,但非常提升真实使用感——名片不再是静态展示页,而是一个有即时反馈的获客入口。
我做这套系统时最后悔的是没在一开始就加上 card_visit 表,后来用户量起来了才补,历史数据全部缺失,渠道分析无从谈起。所以如果你也要做名片方向,访问记录表请放在建库的第一天就建好。希望帮到你。
本文还有配套的精品资源,点击获取