ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java微信小程序名片管理系统:源码+数据库+教程全栈实战

Java微信小程序名片管理系统:源码+数据库+教程全栈实战 简介这份资源是面向高校学生与Java初学者的小程序名片管理系统完整项目包适用于课程设计、毕业设计及小程序开发练手场景。项目采用前后端分离思路前端为微信小程序后端基于SSM或SpringBoot框架配套MySQL数据库脚本开发环境涉及IDEA、微信开发者工具、Navicat与Maven覆盖企业信息、注册用户、客户名片、联系人名片、新闻通知、留言板及系统文件等管理模块功能完善且界面美观。压缩包共736个文件约11.2MB包含71个Java源文件、74个class、103个js、95个xml、60个html、42个css、36个wxss、28个wxml及71张png等涵盖后端逻辑、前端页面、样式资源与数据库脚本结构清晰便于二次开发。目前已有201人学习下载适合需要完整项目参考、快速搭建运行环境并理解名片管理业务实现的学习者。1. 从一张纸质名片到一套可运行的名片管理系统这套 Java 小程序方案到底解决什么问题展会现场换完一圈名片回到工位发现口袋里塞了三十多张纸片想找某个客户的联系方式得一张张翻——这个场景几乎每个做销售、做商务、做渠道的人都遇到过。基于微信小程序的名片管理系统本质就是把「收名片、存名片、查名片、换名片」这条链路搬到手机上用微信小程序做前端入口用 Java 做后端服务用数据库做持久化存储。用户打开小程序就能录入、检索、分享自己的名片管理者在后台能看到全量数据。这套方案适合两类人一类是想拿一个完整项目练手 Java Web 小程序全栈开发的学生或转行者另一类是中小团队想快速搭一套内部通讯录或客户名片库的开发者。标题里提到的源码、数据库、教程三件套意味着这不是一个只讲思路的空壳而是能跑起来、能改、能部署的完整工程。接下来我会按「先搞清楚架构和数据怎么设计再动手把后端和前端跑通最后说清楚哪些地方最容易翻车」的顺序把这套东西拆开讲透。2. 名片管理系统的技术选型与数据库设计为什么是 Spring Boot MySQL 原生小程序2.1 后端为什么选 Spring Boot 而不是裸 Servlet拿到「Java 小程序」这个组合后端框架的选择其实不多。常见做法是 Spring Boot 打底配 MyBatis 或 MyBatis-Plus 做持久层。原因很直接小程序端要的是 JSON 接口Spring Boot 的RestController天然就是干这个的一个注解把返回值序列化成 JSON省掉手写response.getWriter().write()的麻烦。裸 Servlet 也能做但你要自己处理 JSON 序列化、参数绑定、跨域、异常统一返回写到最后会发现造了一遍 Spring MVC 的轮子。MyBatis-Plus 在这里的价值更明显。名片实体字段不多但增删改查频繁用 MyBatis-Plus 的BaseMapper可以直接省掉大量单表 CRUD 的 XML。热搜词里出现的「mybatisplus根据java实体类生成创建表的sql语句」正好对应这个场景——实体类定好建表语句基本就出来了。我一般会先在实体类上把字段和类型敲定再反推 DDL这样能避免「表建好了发现字段对不上实体」的来回改。依赖上核心就三块spring-boot-starter-web提供 Web 能力mybatis-plus-boot-starter提供持久层mysql-connector-java提供驱动。版本上不用追最新选一个 Spring Boot 2.7.x 的稳定版配对应的 MyBatis-Plus 3.5.x 即可兼容性经过大量项目验证踩坑概率低。2.2 数据库表怎么设计三张表撑起整个系统名片管理系统的数据模型不复杂但要想清楚「用户」和「名片」的关系。常见做法是拆成三张表用户表存微信登录身份名片表存名片内容分类表存名片的标签分组。下面是我一般会用的建表结构。-- 用户表存微信 openid 和基本信息 CREATE TABLE sys_user ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, openid VARCHAR(64) NOT NULL COMMENT 微信openid唯一标识, nickname VARCHAR(64) DEFAULT NULL COMMENT 昵称, avatar_url VARCHAR(255) DEFAULT NULL COMMENT 头像地址, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id), UNIQUE KEY uk_openid (openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; -- 名片表核心业务表 CREATE TABLE biz_card ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, user_id BIGINT NOT NULL COMMENT 所属用户id, name VARCHAR(32) NOT NULL COMMENT 姓名, company VARCHAR(128) DEFAULT NULL COMMENT 公司, position VARCHAR(64) DEFAULT NULL COMMENT 职位, mobile VARCHAR(20) DEFAULT NULL COMMENT 手机号, email VARCHAR(64) DEFAULT NULL COMMENT 邮箱, address VARCHAR(255) DEFAULT NULL COMMENT 地址, category_id BIGINT DEFAULT NULL COMMENT 分类id, remark VARCHAR(255) DEFAULT NULL COMMENT 备注, is_public TINYINT DEFAULT 0 COMMENT 是否公开 0否 1是, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_name (name) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT名片表; -- 分类表给名片打标签 CREATE TABLE biz_category ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, user_id BIGINT NOT NULL COMMENT 所属用户id, category_name VARCHAR(32) NOT NULL COMMENT 分类名称, sort INT DEFAULT 0 COMMENT 排序, PRIMARY KEY (id), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT名片分类表;字段设计上有几个点值得说。openid加了唯一索引因为微信登录时同一个用户可能多次请求靠唯一约束兜底能防止重复插入。biz_card表上user_id和name都建了索引前者用于「查我的名片」后者用于「按姓名搜索」这两个是最高频的查询路径。字符集统一用utf8mb4因为名片里可能出现 emoji 或生僻字用utf8会截断报错这个坑我踩过不止一次。提示is_public字段控制名片是否对外可见如果要做「交换名片」功能查询时记得带上这个条件否则会把别人的私密名片也查出来。2.3 小程序端为什么用原生而不是 uniapp热搜词里有「uniapp 微信小程序打包」说明不少人在纠结要不要上 uniapp。我的判断是如果这个项目只发微信小程序一端原生开发更省心。原生小程序的wx.request、wx.login、wx.getUserProfile这些 API 直接可用不用经过 uniapp 的编译层调试时看到的报错就是微信开发者工具的原生报错定位问题快。uniapp 的优势在多端复用但你只发一端时这个优势用不上反而多了一层编译配置的心智负担。原生小程序的目录结构也简单pages放页面utils放请求封装app.js管全局登录态。请求封装我一般会写一个统一的request.js把 baseUrl、token 注入、错误提示都收在一处避免每个页面各写一遍wx.request。3. 后端接口从零跑通登录、名片增删改查的具体实现3.1 微信登录换 openid 的完整链路小程序端的登录不是账号密码而是wx.login拿到临时code传给后端后端拿code加上小程序的appid和secret去微信服务器换openid和session_key。这条链路是整套系统的入口走不通后面全白搭。RestController RequestMapping(/api/auth) public class AuthController { Value(${wx.appid}) private String appid; Value(${wx.secret}) private String secret; Autowired private SysUserService sysUserService; PostMapping(/login) public Result login(RequestBody LoginDTO loginDTO) { // 1. 用 code 换 openid String url https://api.weixin.qq.com/sns/jscode2session ?appid appid secret secret js_code loginDTO.getCode() grant_typeauthorization_code; String resp HttpUtil.get(url); JSONObject json JSONUtil.parseObj(resp); String openid json.getStr(openid); if (openid null) { // 微信返回错误码通常是 code 失效或 appid 配置错 return Result.fail(登录失败: json.getStr(errmsg)); } // 2. 查库没有就注册 SysUser user sysUserService.getByOpenid(openid); if (user null) { user new SysUser(); user.setOpenid(openid); user.setCreateTime(new Date()); sysUserService.save(user); } // 3. 生成 token 返回这里用简单 UUID生产建议 JWT String token UUID.randomUUID().toString(); RedisUtil.set(token: token, user.getId(), 7 * 24 * 3600); return Result.ok(MapUtil.builder() .put(token, token) .put(userId, user.getId()) .build()); } }这段代码的逻辑分三步换 openid、查库注册、发 token。参数上appid和secret从配置文件读不要硬编码在代码里否则换环境要改代码。grant_type固定是authorization_code写错会直接报错。返回的 token 我用了 Redis 存映射关系key 是 tokenvalue 是 userId过期时间七天。生产环境更推荐 JWT省掉 Redis 依赖但 JWT 的注销和续期要额外处理看项目规模取舍。小程序端调用就三行wx.login({ success: (res) { // res.code 就是临时凭证传给后端 wx.request({ url: http://localhost:8080/api/auth/login, method: POST, data: { code: res.code }, success: (r) { wx.setStorageSync(token, r.data.data.token); } }); } });wx.login拿到的code只能用一次五分钟内有效所以拿到后要立刻传给后端不要缓存。wx.setStorageSync把 token 存本地后续每个请求从 storage 里取出来塞进 header。3.2 名片增删改查接口与分页查询名片这块是业务核心接口设计上遵循 RESTful 风格POST 新增、PUT 修改、DELETE 删除、GET 查询。查询要支持分页和按姓名模糊搜索这是名片管理最常用的功能。RestController RequestMapping(/api/card) public class CardController { Autowired private BizCardService cardService; PostMapping public Result add(RequestBody BizCard card, RequestHeader(token) String token) { Long userId RedisUtil.get(token: token); if (userId null) { return Result.fail(未登录); } card.setUserId(userId); card.setCreateTime(new Date()); cardService.save(card); return Result.ok(card.getId()); } GetMapping(/page) public Result page(RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, RequestParam(required false) String keyword, RequestHeader(token) String token) { Long userId RedisUtil.get(token: token); // 构造分页条件按姓名或公司模糊匹配 PageBizCard page new Page(pageNum, pageSize); LambdaQueryWrapperBizCard wrapper new LambdaQueryWrapper(); wrapper.eq(BizCard::getUserId, userId); if (StrUtil.isNotBlank(keyword)) { wrapper.and(w - w.like(BizCard::getName, keyword) .or().like(BizCard::getCompany, keyword)); } wrapper.orderByDesc(BizCard::getCreateTime); return Result.ok(cardService.page(page, wrapper)); } }分页用 MyBatis-Plus 的Page对象pageNum和pageSize给了默认值前端不传也不会报错。keyword用LambdaQueryWrapper的and嵌套实现「姓名或公司包含关键词」的模糊匹配注意这里必须用and包起来否则or会和外层的eq(userId)平级导致查出别人的名片——这是个非常隐蔽的权限漏洞我见过真实项目里出过这个问题。参数说明pageNum从 1 开始pageSize建议前端限制在 50 以内防止有人传个 10000 把数据库拖垮。keyword为空时跳过模糊条件走全量分页。3.3 小程序端页面与请求封装小程序端页面不多核心就四个登录页、名片列表页、名片编辑页、名片详情页。请求封装统一放在utils/request.jsconst BASE_URL http://localhost:8080; function request(options) { const token wx.getStorageSync(token); return new Promise((resolve, reject) { wx.request({ url: BASE_URL options.url, method: options.method || GET, data: options.data || {}, header: { content-type: application/json, token: token // 每个请求自动带上 token }, success: (res) { if (res.data.code 200) { resolve(res.data.data); } else if (res.data.code 401) { // token 失效跳回登录页 wx.redirectTo({ url: /pages/login/login }); reject(res.data); } else { wx.showToast({ title: res.data.msg, icon: none }); reject(res.data); } }, fail: reject }); }); } module.exports { request };封装的价值在于token 注入只写一次错误提示统一处理401 自动跳登录。页面里调用就变成request({ url: /api/card/page, data: { pageNum: 1 } })干净很多。注意BASE_URL在开发时是localhost但真机调试时手机访问不到电脑的 localhost要换成电脑的局域网 IP这个坑几乎每个人第一次都会踩。4. 部署与联调避坑那些让你卡半天的常见问题4.1 小程序请求域名校验与本地调试现象开发者工具里请求正常真机预览时报「不在以下 request 合法域名列表中」。原因微信小程序正式环境要求所有请求走 HTTPS 且域名在后台白名单里本地http://localhost不在白名单。解决开发阶段在开发者工具「详情 → 本地设置」里勾选「不校验合法域名、web-view、TLS 版本以及 HTTPS 证书」真机预览时打开调试模式也能跳过校验。上线前必须把后端部署到 HTTPS 域名并配置到小程序后台这一步绕不过去。4.2 数据库连接报时区错误现象启动后端时报The server time zone value xxx is unrecognized。原因MySQL 8 的驱动对时区敏感JDBC URL 没指定时区时用了系统默认值服务器时区不标准就报错。解决JDBC URL 加上serverTimezoneAsia/Shanghai完整写法jdbc:mysql://localhost:3306/card_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse。useSSLfalse在本地开发时加上避免证书警告。4.3 中文乱码现象名片里存的中文在数据库里显示成问号或者接口返回乱码。原因三个环节都可能出问题——数据库字符集、连接字符集、响应编码。解决数据库和表都用utf8mb4JDBC URL 带characterEncodingutf8Spring Boot 的application.yml里配server.servlet.encoding.charsetutf-8和forcetrue。三处都对齐基本就不会乱码了。4.4 token 丢失导致接口全部 401现象登录成功但后续请求全部返回未登录。原因小程序端wx.request的 header 里 token 没带上或者后端从 header 取 token 的 key 和前端设置的不一致。解决检查前端header里的 key 和后端RequestHeader(token)里的名字是否完全一致大小写敏感。另外确认wx.setStorageSync存 token 的时机在请求之前异步问题也会导致第一次请求拿不到 token。4.5 分页查询 total 为 0 但列表有数据现象接口返回的列表有数据但total是 0前端分页组件显示异常。原因MyBatis-Plus 的分页插件没配置Page对象不会自动执行 count 查询。解决加一个配置类注册MybatisPlusInterceptor并添加PaginationInnerInterceptor指定数据库类型为 MySQL。漏了这一步分页功能就是残的列表能出来但总数永远是 0。5. 让这套系统真正好用几个提升体验的进阶技巧把基础功能跑通只是及格线真正让名片管理系统好用还得在几个细节上做文章。第一个是搜索体验。前面用的是like %keyword%数据量小的时候没问题但名片攒到几千张时全表扫描会明显变慢。我一般会加一个FULLTEXT索引或者引入简单的分词把姓名、公司、备注拼成一个搜索字段查询时走索引匹配。如果不想动数据库退而求其次的做法是限制搜索范围只搜姓名和公司别把备注也扫进去。第二个是名片分享。名片管理系统的价值一半在「管自己的」一半在「换别人的」。实现上可以生成一张带参数的小程序码参数里带上名片 id别人扫码后直接打开名片详情页点「保存到我的名片」就完成了一次交换。生成小程序码用微信的wxacode.getUnlimited接口后端调一次拿到图片流返回给前端。这里要注意scene参数有长度限制别把整个名片 JSON 塞进去只放 id 就够。第三个是数据导出。用户攒了一堆名片后会有导出需求常见做法是后端用 EasyExcel 生成 xlsx 文件接口返回文件流小程序端用wx.downloadFile下载后wx.openDocument打开。导出时记得按当前用户的userId过滤别把全库数据导出去了。验证这套系统是否真的跑通我的习惯是走一遍完整链路新用户登录 → 新增三张名片 → 按姓名搜索 → 修改其中一张 → 删除一张 → 分页查看剩余。每一步都看数据库里的数据变化而不是只看接口返回的code:200。接口返回成功但数据没落库的情况太常见了尤其是事务没配好的时候。最后说个我自己的教训早期做这类项目时我总想着把功能堆全结果每个功能都半吊子。后来改成先把「登录 名片增删改查 分页搜索」这条主链路打磨到没有明显 bug再往上加分享、导出、分类这些锦上添花的东西反而交付得更快。这套源码和数据库结构本身不复杂难的是把每个环节的边界情况都想到——比如空列表的展示、超长文本的截断、并发写入的重复。把这些处理好了它才是一个能拿得出手的项目而不是一个只能跑 demo 的壳子。希望帮到你。本文还有配套的精品资源点击获取
返回列表