ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

小程序名片管理系统源码实战:从数据库设计到前后端联调

小程序名片管理系统源码实战:从数据库设计到前后端联调 简介这份微信小程序名片管理系统源码包专为毕业设计、课程设计和小程序开发者准备围绕企业电子名片管理场景完整覆盖前端界面、后端服务与数据库设计可帮助读者快速搭建一个可运行的名片管理应用。压缩包共737个文件总大小26.1MB包含js、wxml、wxss等小程序前端代码java、class、jsp等后台服务逻辑sql数据库脚本以及png、jpg等UI素材目录结构清晰便于按模块查阅和二次开发。目前已有302人学习上手门槛适中适合正在做同类课题或想系统了解小程序前后端协作的读者。通过分析这套源码可以学习用户表、名片表、公司信息表等数据表的设计思路掌握登录注册、名片增删改查、权限管理、数据同步等核心功能并理解wx.request网络交互、API接口实现以及UI适配方法对完成毕业设计和理解真实业务系统都很有帮助。1. 微信小程序名片管理系统源码数据库.zip别只盯着“扫码传名片”这是一套被低估的线索数据结构上周帮一家做展会获客的公司调试一批从渠道商手里拿来的微信小程序名片管理系统源码数据库.zip团队把它当成“电子名片夹”来验收结果上线两周就抱怨用不起来。问题不在代码而在他们没读懂这个压缩包里“名片”到底是什么它不是一张图片而是一条与人脉、公司、跟进状态绑定的结构化线索数据。如果能把这个数据链路理清这套源码能做的事远不止“名片互换”它可以直接支撑销售团队做客户标签、跟进记录甚至商机评分。这套方案适合谁适合已经用纸质名片或者静态图片收集过客户信息、想用最低成本把线索电子化的团队也适合刚接手小程序开发、需要一套包含完整前后端与数据库的参考工程来练手的新手。接下来我会从数据模型、部署路径、业务参数、典型翻车现场到上线技巧把这个 zip 里的东西真正吃透。2. 先看数据库再碰代码名片管理系统的数据模型与核心链路很多人解压后第一件事是打开小程序代码看页面这是本末倒置。名片管理系统的技术含量不在扫码动画或名片模板特效而在数据库里那些表和字段之间的关系。只要数据模型立得住换一套前端 UI 或者后端语言都能跑模型立不住页面再漂亮数据一多就乱成一锅粥。2.1 名片不只是“一张图片”user / card / exchange 三张核心表常见的微信小程序名片管理系统数据库里至少会拆出三张业务表。第一张是用户表一般叫wx_user存的是扫码者的微信身份和服务端自定义信息第二张是名片表叫card存的是“谁的名片”以及名片上展示的公司、职位、电话、微信、邮箱第三张是交换记录表叫card_exchange存的是“谁在什么时间、什么场景下交换了哪张名片”这张表是后续做客户跟进和标签分析的数据来源。你可以用下面这段 SQL 快速搭建一个能用的骨架CREATE TABLE wx_user ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, openid VARCHAR(64) NOT NULL UNIQUE COMMENT 微信openid, unionid VARCHAR(64) DEFAULT NULL COMMENT 开放平台unionid, nickname VARCHAR(64) DEFAULT COMMENT 昵称, avatar_url VARCHAR(255) DEFAULT COMMENT 头像URL, phone VARCHAR(20) DEFAULT COMMENT 绑定手机号, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT微信用户表; CREATE TABLE card ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id INT UNSIGNED NOT NULL COMMENT 所属用户ID, holder_name VARCHAR(32) NOT NULL COMMENT 名片持有者姓名, company VARCHAR(128) DEFAULT COMMENT 公司名称, title VARCHAR(64) DEFAULT COMMENT 职位, mobile VARCHAR(20) DEFAULT COMMENT 手机号, wechat VARCHAR(64) DEFAULT COMMENT 微信号, email VARCHAR(128) DEFAULT COMMENT 邮箱, avatar_url VARCHAR(255) DEFAULT COMMENT 名片头像, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_user_id (user_id), CONSTRAINT fk_card_user FOREIGN KEY (user_id) REFERENCES wx_user(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT名片表; CREATE TABLE card_exchange ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, from_user_id INT UNSIGNED NOT NULL COMMENT 发起交换的用户, to_user_id INT UNSIGNED NOT NULL COMMENT 接收方用户, card_id INT UNSIGNED NOT NULL COMMENT 被保存的名片ID, scene VARCHAR(32) DEFAULT COMMENT 交换场景如展会/拜访, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_from_user (from_user_id), KEY idx_to_user (to_user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT名片交换记录表;这段 SQL 的逻辑是wx_user做主用户体系card挂接在user_id下解决“同一用户可以录入多张名片”的问题。card_exchange是典型的关联表它不冗余名片内容只存card_id和交换双方的用户 ID这样后续统计“谁的名片最受欢迎”时只查这一张表即可。参数方面字符集用utf8mb4是为了兼容中文和 emoji很多老源码写成utf8会导致生僻字和特殊符号入库变成乱码这是第一个要改的地方。2.2 微信用户体系怎么和名片关联openid、unionid、手机号的取舍名片系统必然要接微信登录。常见的做法是首次进入小程序时调用wx.login换取 openid把 openid 作为wx_user表唯一键。但如果你的团队同时运营小程序和公众号或者未来要把 H5 数据合并进来就需要在表里预留unionid字段通过微信开放平台将两个平台的账号打通。这里有个容易踩的节点wx.getUserProfile拿到的昵称和头像不等于用户授权了手机号。手机号授权必须走button open-typegetPhoneNumber获取动态 code再交给服务端换取真实手机号。很多新手在数据库里直接给phone字段加索引并当作登录凭据但手机号在未授权时是空字符串索引反而会拖慢写入速度。我的建议是phone不要设唯一索引只允许openid唯一手机号作为后续补充字段。这样既能保证微信登录链路稳定又不会因为用户拒绝授权导致写入失败。2.3 用 dbx 数据库工具快速审查数据库文件如果 zip 里直接带了一个.db或.sqlite文件而不是 MySQL 初始化脚本你不需要急着打开代码。我一般会先用 dbx 数据库工具把文件读一遍重点看三件事第一表字段是否和前端代码里出现的参数名一一对应第二是否有创建card_exchange这种关联表第三时间字段是DATETIME还是INT时间戳这决定后续做报表时能不能直接按天分组。dbx 这类工具的方便之处在于可视化浏览表结构和导出建表语句。如果你手上的源码包没有附带数据库文件只有一个init.sql也建议用文本编辑器先搜索CREATE TABLE关键词把表清单列出来再对照小程序端的request请求路径判断接口和表是否对齐。这一步能帮你提前发现“前端提交了company但数据库字段叫company_name”这类常见脱节问题。3. 把 zip 解压到能跑通前后端联调的最小步骤这一章的目标是让你在一台普通开发机上把“小程序端 服务端 MySQL”完整跑起来不做业务改动先看到名片列表能加载出来。别一上来就改页面先跑通最小闭环。3.1 解压与目录辨识小程序端、服务端、SQL脚本分别在哪拿到微信小程序名片管理系统源码数据库.zip第一步不是双击解压而是先看压缩包内的根目录结构。常见的布局是client或miniprogram放小程序源码server或service放后端接口根目录下有一个.sql文件。用命令行解压可以避开某些 GUI 工具对中文件名的编码问题mkdir -p card-system cd card-system unzip ../微信小程序名片管理系统源码数据库.zip -x *.DS_Store -x __MACOSX/* find . -maxdepth 2 -type d | sort第一个命令创建项目目录并解压排除 Mac 系统残留文件第二个命令列出两层目录结构让你快速定位client和server。我习惯用-x排除隐藏文件否则导入工程时经常把.DS_Store一起传到 Git 里这是大多数源码包刚拿到时的隐藏坑。解压后先看有没有package.json或requirements.txt有的话说明后端需要安装依赖没有的话可能是一个 PHP 或其他直跑项目。这个细节直接决定你要不要启动一个额外的 Node 进程。3.2 小程序端配置appid、域名白名单与顶部导航栏高度适配打开小程序端目录第一步是把project.config.json里的appid换成你自己的测试号或者正式 AppID。然后打开app.js或config.js找到baseUrl指向你本机的局域网 IP 加后端端口例如http://192.168.1.10:8080。如果你的手机和电脑在同一网段真机预览可以直接请求局域网地址不用急着配 HTTPS 域名。这里要给新手提个醒开发环境的微信开发者工具默认勾选了“不校验合法域名”你可以在详情设置里打开它但一旦预览手机上的微信客户端不认这个开关必须把baseUrl配到已备案的 HTTPS 域名否则请求会直接失败。很多人拿着本机 IP 在真机预览时怎么也登不上就是卡在这。顶部导航栏高度也是小程序开发的高频问题。如果源码里用了自定义导航栏你需要读取wx.getSystemInfoSync()得到状态栏高度再计算导航栏标题高度。常见做法是在app.js里注入全局数据const sysInfo wx.getSystemInfoSync(); App({ globalData: { statusBarHeight: sysInfo.statusBarHeight, navBarHeight: 44 } });然后在每个自定义导航页面的onLoad中读取this.globalData.statusBarHeight动态设置容器padding-top。不能写死 64因为 iPhone 的灵动岛和普通机型状态栏高度最多差 20 多个像素写死就会导致标题栏压住返回按钮。3.3 服务端接口联调扫码保存名片的完整链路扫码保存名片的核心接口一般就两个一个根据名片 ID 获取名片详情一个把名片写入当前用户的“人脉列表”。我先给你一个小程序端的调用示例重点是请求封装不要写散。function saveCard(cardId, scene) { wx.request({ url: ${baseUrl}/api/card/save, method: POST, data: { cardId: cardId, scene: scene || scan }, header: { Authorization: Bearer ${wx.getStorageSync(token)} }, success(res) { if (res.data.code 0) { wx.showToast({ title: 已保存到人脉 }); } else { wx.showModal({ content: res.data.msg, showCancel: false }); } }, fail(err) { console.error(saveCard failed, err); } }); }这里的Authorization头是服务端鉴权入口token一般在wx.login成功后由服务端返回并存入本地存储。后端收到请求后逻辑如下解析 token 拿到用户在wx_user表的 ID然后向card_exchange插入一条记录from_user_id是扫码人to_user_id是名片所有者card_id是被保存的名片。注意保存逻辑要有幂等性同一用户对同一张名片重复扫码不应该插入两条相同的交换记录应该在card_exchange上加一个UNIQUE KEY (to_user_id, card_id)或者在代码里先查再插。服务端接口如果要对入库数据做保护至少要有后端参数校验。常见做法是写一个简单的中间件拒绝cardId非数字或长度异常的请求避免直接拼接 SQL。这一步虽然看起来基础但能筛掉大量扫描端口的攻击请求。4. 三个必调的业务参数从“能跑”到“能用”跑通最小闭环只是第一步。接下来这三个参数和业务逻辑如果不调系统会处于“能用但难用”的状态手机号拿不到、名片字段混乱、列表越滑越卡。每一条都是真实客户现场反馈过的高优先级问题。4.1 微信手机号授权getPhoneNumber 从“免费”到“收费”的账微信官方在 2023 年调整了手机号快速验证的收费策略基础库新版本下通过button open-typegetPhoneNumber获取真实手机号每次成功获取都要按调用量计费。很多源码包还停留在旧的免费逻辑上前端拿到code后传给后端后端调用phonenumber.getPhoneNumber换取手机号。如果你接手的是这类代码务必要去微信小程序后台看当前版本的调用价格再决定是否继续用这条路。另一个现实问题微信手机号验证只能验证当前微信绑定的手机号不能帮用户填入其他号码。名片场景里用户想录入的往往是客户的公司座机或另一个手机号这时就不该依赖微信授权而是做成名片表单里的一个普通input字段只让微信验证作为“一键填充”加速录入。我的建议是把手机号字段拆成两个维度一个是“微信绑定手机号”一个是“名片展示手机号”后者允许任意填写前者只做身份校验。4.2 名片字段映射与校验不设默认值会被脏数据坑源码包里字段常常命名不一致前端叫companyName后端接口字段叫enterprise数据库列叫company。三层映射不一致是这套系统里最普遍的脏数据来源。我建议你拿到源码后第一件事是做一张字段映射表以数据库列为基准统一前后端命名。下面是一个常见映射示例数据库字段后端接口字段小程序端字段是否必填校验规则holder_nameholderNameholderName是1-32字符companycompanycompanyName否默认“待完善”mobilemobilemobile否支持手机号和座机正则宽松wechatwechatwechat否不做唯一校验titletitletitle否默认“-”这里的参数要点是“宽松校验 默认值兜底”。名片是半公开信息用户可能只填姓名就提交如果后端强制要求所有字段非空会让录入率直线下降。正确的做法是姓名必填其余字段允许为空但存默认值前端展示时替换成“未填写”这样数据库不会出现空字符串导致排序异常的问题。4.3 列表加载更多分页参数与滚动刷新名片列表一旦超过几十张前端一次性渲染所有数据会明显卡顿。源码包里如果只有wx.request直接查全部数据你需要改成标准分页。后端接口接收page和pageSize返回总数和当前页列表小程序端在页面触底时自动加载下一页。Page({ data: { list: [], page: 1, pageSize: 20, hasMore: true }, onReachBottom() { if (!this.data.hasMore) return; wx.request({ url: ${baseUrl}/api/card/list, data: { page: this.data.page, pageSize: this.data.pageSize }, success: (res) { const items res.data.data.list; this.setData({ list: this.data.list.concat(items), page: this.data.page 1, hasMore: items.length this.data.pageSize }); } }); } });这段代码的关键是hasMore判断当返回数量不足pageSize时说明没有下一页了要停掉触底请求否则会出现无限请求同一个空翻转页。另一个细节是concat而不是setData直接覆盖否则翻页后列表会丢失前面的数据。通常页码从 1 开始接口对应LIMIT (page-1)*pageSize, pageSize如果你看到源码拼接 SQL 时用了OFFSET要注意偏移量越大性能越差数据量上万后建议改成“游标分页”也就是记住最后一条记录的主键 ID 再往后查这个优化可以后续再做但至少当前分页结构要能支撑你跑通。5. 避坑这套名片系统源码在落地时最容易翻车的五个点以下问题来自我帮人调试类似源码工程时的血泪经验。每一条都是真实发生过的按“现象 → 原因 → 解决”的方式整理你可以直接对照排查。5.1 现象SQL 导入 MySQL 时报错或者表名变成了乱码原因常见的源码包里的.sql文件是 UTF-8 编码但有些人用 Windows 记事本打开再另存为导致文件变成带 BOM 的 UTF-8 或 GBKMySQL 客户端默认字符集不匹配就报错。解决用命令行导入前先指定字符集。打开命令行执行mysql -u root -p --default-character-setutf8mb4 init.sql如果依然报错用iconv把编码转成 UTF-8 无 BOM 再导入iconv -f GBK -t UTF-8 init.sql init_utf8.sql5.2 现象真机预览时扫码跳转名片详情失败模拟器一切正常原因开发者工具模拟器不校验业务域名真机微信要求小程序后台配置 request 合法域名或者打开调试模式。如果源码包的baseUrl用的是局域网 IP真机自然无法请求。解决临时调试可以在微信小程序后台开启“开发调试”模式正式环境把baseUrl换成 HTTPS 域名并备案同时在小程序管理后台的“开发设置-服务器域名”里添加 request 和 uploadFile 合法域名。这个配置与实际接口路径无关只要域名一致即可。5.3 现象getPhoneNumber 返回 errno 13003 或 code 无效原因一种是基础库版本太旧前端没走button open-typegetPhoneNumber而是直接调用wx.getPhoneNumber另一种是后端换取手机号时用的access_token是小程序令牌但调用了开放平台的接口。解决前端必须用按钮触发授权得到code后传到后端后端使用client_credential的小程序凭据调用jscode2session换取 openid再调用手机号相关接口。如果你的业务只是展示名片强烈建议不要在这个接口上绑定登录态否则用户拒绝授权就进不来系统。5.4 现象名片列表数据量到 1 万条后首页打开白屏或加载极慢原因源码里接口一次性返回全部列表没有分页或者是分页查询中存在ORDER BY created_at DESC但没有给created_at加索引。数据量小时没问题过万后 MySQL 排序字段全表扫描性能断崖。解决先看表结构有没有索引没有就在card_exchange的created_at字段加KEY。同时把前端列表改成上一章讲的分页加载。记住不能只靠前端分页骗自己后端 SQL 也要执行EXPLAIN确认查询走了索引。5.5 现象微信审核拒绝理由是“类目与小程序涉及内容不匹配”原因名片管理小程序如果涉及用户自行上传名片、交换用户信息属于“社交-陌生人交友”或“工具-信息查询”类目不同类目需要不同资质。很多源码包没有准备好《增值电信业务经营许可证》或非经营性 ICP 备案审核就会卡住。解决先确认真实业务是不是只面向企业内员工使用。如果是可以走“企业微信”或“微信企业内部”的分类上传企业认证资质如果是面向公众的销售线索采集必须备齐相应资质。这条属于业务合规代码层帮不了你但提前了解能避免开发完才发现不能上架的尴尬。6. 最后一步从“能跑”到“能交付”我建议你强制自己做的三件事这一章不是总结而是你准备把系统交给同事或客户前我强烈建议你花半天时间做的三件“不讨喜但保命”的事。第一做一次全量备份演练。不要只在服务器上跑mysqldump要实打实地在另一台干净机器上恢复备份。具体命令是mysqldump -u root -p --single-transaction --default-character-setutf8mb4 card_system backup_$(date %F).sql mysql -u root -p -e CREATE DATABASE card_system_restore DEFAULT CHARSET utf8mb4 mysql -u root -p card_system_restore backup_$(date %F).sql--single-transaction能在 InnoDB 引擎下不做锁表备份对在线业务影响最小。恢复完看一下前台能否正常登录这一步验证的不只是备份文件而是“服务器挂了之后你多久能爬起来”。第二在服务端加一个日志中间件记录每个接口的耗时和状态码。不用引入复杂框架就用 pulic 中间件打点app.use(/api, (req, res, next) { const start Date.now(); res.on(finish, () { console.log(${req.method} ${req.url} ${res.statusCode} ${Date.now() - start}ms); }); next(); });这个日志能帮你发现“某个页面越来越慢是不是某个接口变慢”也比出问题时对着黑匣子猜更高效。第三二次开发优先动数据库不要直接改页面。如果你想加一个“客户来源”下拉框应该先在card表加source字段再后端接口加校验最后前端引字段。顺序反了前端先写死后面数据库结构一变接口就报 500。这也是我常和团队说的小程序页面改起来太容易很容易让人忽略模型层等到数据对不上再来翻数据库才是真的返工。做这套系统这几年我的习惯是永远保留一份“最小可跑版本”的 SQL 初始化脚本名字就叫init.sql无论大改多少次那份脚本永远能恢复出一套能登录、能存名片的原型。希望这个习惯也能帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进