☰
Java+小程序名片系统:可控落地的全栈实践指南
2026/10/9 7:54:16 网站建设 项目流程

简介:这是一套面向高校计算机专业学生与Java全栈初学者的微信小程序名片管理系统实战项目,适用于课程设计、毕业设计及小程序开发入门实践。系统采用前后端分离架构,前端基于微信小程序原生开发,后端可选SSM或SpringBoot框架,配套MySQL 5.7数据库脚本、Navicat可视化配置说明及Tomcat部署指南,覆盖从环境搭建、代码调试到上线运行的完整开发链路。资源包共736个文件,含71个Java业务类(如KehumingpianInfoController、YonghuService等)、103个JS逻辑文件、71个PNG图标资源、42个CSS/WXSS样式文件及关键SQL建库脚本,总大小11.2MB,结构清晰、模块职责分明。已有200人学习下载,提供开箱即用的可运行工程,包含完整源码、数据库初始化脚本、软件工具清单与常见兼容性避坑提示(如MySQL 8.0适配问题),显著降低调试门槛,助力快速掌握小程序+Java后端协同开发核心流程。

1. 微信小程序名片管理系统:为什么用 Java 后端 + 小程序前端组合,比纯云开发更可控、更适合中小团队落地?

你手上刚拿到一个叫「微信小程序-基于小程序的名片管理系统(Java)」的压缩包,解压后看到src/main/java、sql/、miniprogram/三个主目录——这不是一个“套壳模板”,而是一套能真正跑起来、改得动、上线不翻车的闭环系统。它解决的不是“怎么写个 Hello World 小程序”,而是销售、HR、行政人员每天要手动录入、查找、同步、导出联系人时的真实痛点:微信里加的人没法结构化管理,Excel 表格传着传着就版本混乱,用第三方 SaaS 又担心数据留在别人服务器上。这套方案用 Java 写后端 API(Spring Boot),MySQL 存结构化数据,小程序前端做轻量交互,三者边界清晰、日志可查、权限可配、扩容有路。它不追求“五分钟上线”,但能让你在三天内完成本地调试 → 内网测试 → 域名备案 → 正式部署的完整链路。适合正在带 3~10 人小团队的技术负责人、想把内部工具从 Excel 迁出的业务方,或正在准备 Java 全栈面试、需要一个“有数据库、有接口、有真实业务逻辑”的可讲项目的学生。别被“源码+教程”这几个字骗了——真正值钱的是里面对微信登录态校验、手机号脱敏存储、分页查询性能优化、小程序端 token 自动续期这些细节的处理方式。


2. 搭建本地开发环境:从 JDK 到小程序开发者工具,一步不跳过的最小可行配置

2.1 安装与验证 Java + Maven + MySQL 环境(JDK 8 或 11 是硬性要求)

这套系统基于 Spring Boot 2.3.x 构建,官方明确要求 JDK 8u202+ 或 JDK 11。JDK 17 虽然新,但部分依赖(如druid-spring-boot-starter)在该版本下存在兼容问题,我踩过坑:启动时报NoSuchMethodError: javax.servlet.http.HttpServletRequest.getHttpServletMapping()。所以请严格按 README.md 里的提示安装 JDK 11(推荐 OpenJDK 11.0.20)。

# 验证 JDK 版本(必须显示 11.x) java -version # 输出应为类似: # openjdk version "11.0.20" 2023-07-18 # OpenJDK Runtime Environment (build 11.0.20+8-post-Ubuntu-1ubuntu122.04) # OpenJDK 64-Bit Server VM (build 11.0.20+8-post-Ubuntu-1ubuntu122.04, mixed mode, sharing) # 验证 Maven(3.6.3+ 即可,不要用 3.9.x,Spring Boot 2.3 不兼容) mvn -v

提示:Windows 用户务必检查JAVA_HOME是否指向 JDK 目录(不是 JRE),且PATH中包含%JAVA_HOME%\bin。Mac 用户若用 Homebrew 安装,执行brew install openjdk@11 && sudo ln -sfn /opt/homebrew/opt/openjdk@11/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk-11.jdk。

MySQL 推荐 5.7.32 或 8.0.28(避免 8.0.33+,因默认caching_sha2_password插件与mysql-connector-java:8.0.22不匹配)。建库语句在sql/init_db.sql中,但注意:脚本里CREATE DATABASE语句未指定字符集,直接执行会导致中文乱码。正确做法是先手动建库:

CREATE DATABASE wx_card_system CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

再执行init_db.sql中的建表语句。表结构含user_info(用户主表)、contact_info(名片主表)、user_contact_relation(多对多关系表),字段命名直白(real_name,mobile,company,position,avatar_url),无冗余设计。

2.2 导入后端项目到 IDE 并启动 Spring Boot 应用

用 IntelliJ IDEA 打开项目根目录(含pom.xml的文件夹),IDE 会自动识别为 Maven 项目。关键配置在application.yml:

spring: datasource: url: jdbc:mysql://localhost:3306/wx_card_system?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false username: root password: your_mysql_password # ← 必须改成你本地 MySQL 的密码 redis: host: localhost port: 6379 password:

参数说明:serverTimezone=Asia/Shanghai是强制项,否则时间字段(如create_time)存入数据库会偏移 8 小时;allowPublicKeyRetrieval=true是 MySQL 8.0+ 必需参数,否则连接报错;useSSL=false在本地开发可关闭,生产环境必须启用并配置证书。

启动类WxCardApplication.java,右键 Run。成功标志是控制台输出:

Started WxCardApplication in 3.2 seconds (JVM running for 3.8)

此时访问http://localhost:8080/swagger-ui.html应能看到 Swagger 接口文档(含/api/user/login、/api/contact/list等全部 12 个接口),这是验证后端是否跑通的黄金标准。

2.3 配置微信开发者工具并导入小程序前端

小程序代码在miniprogram/目录下,无需构建,直接用 微信开发者工具 (稳定版,非 Nightly)打开。关键配置在project.config.json:

{ "description": "名片管理系统", "setting": { "urlCheck": false, "es6": true, "enhance": true, "postcss": true, "minified": false, "newFeature": true, "coverView": true, "nodeModules": true, "autoAudits": false, "compileHotReLoad": true, "lazyload": true, "preloadBackgroundData": true, "uploadWithSourceMap": true, "sizeLimit": 2097152 }, "appid": "wx1234567890abcdef", // ← 替换为你自己的测试号 AppID "projectname": "wx-card-system", "libVersion": "2.24.4" }

注意:appid必须替换成你在 微信公众平台 申请的测试号(路径:开发管理 → 开发者工具 → 测试号)的 AppID。填错会导致wx.login()失败,控制台报errCode: 40013(appid 不存在)。测试号无需企业认证,5 分钟内可生成。

小程序启动后,首页会调用app.js中的login()方法,向http://localhost:8080/api/user/login发送code请求。若后端正常,返回{ "code": 200, "data": { "token": "xxx", "userInfo": {...} } },则登录成功,进入联系人列表页。


3. 核心业务流程跑通:从微信授权登录到名片增删改查的端到端链路

3.1 微信登录态设计:为什么不用wx.checkSession()而用 Redis Token 续期?

小程序端调用wx.login()获取临时code,传给后端/api/user/login接口。后端用该code向微信服务器换取openid和session_key(https://api.weixin.qq.com/sns/jscode2session),但绝不把session_key存库或返回前端——这是安全红线。实际流程是:

  1. 后端用openid查询数据库,若存在则生成 JWT Token(有效期 2 小时);
  2. 若不存在,则插入新用户记录(nickName,avatarUrl来自wx.getUserProfile()后续获取);
  3. Token 存入 Redis,Key 为token:{uuid},Value 为{"openid":"xxx","userId":123},过期时间设为 2 小时;
  4. 前端将 Token 存入wx.setStorageSync('token', token),后续所有请求 Header 带Authorization: Bearer xxx。
// WxLoginController.java 片段 @PostMapping("/login") public Result login(@RequestBody LoginRequest request) { String code = request.getCode(); // 1. 调用微信接口换取 openid String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appid + "&secret=" + secret + "&js_code=" + code + "&grant_type=authorization_code"; String response = HttpUtil.get(url); // 使用 Hutool 工具类 JSONObject json = JSON.parseObject(response); String openid = json.getString("openid"); // 2. 查询或创建用户 User user = userService.findByOpenid(openid); if (user == null) { user = new User(); user.setOpenid(openid); user.setCreateTime(new Date()); userService.save(user); } // 3. 生成 Token 并存 Redis String token = JwtUtil.generateToken(user.getId(), openid); redisTemplate.opsForValue().set("token:" + token, JSON.toJSONString(Map.of("openid", openid, "userId", user.getId())), Duration.ofHours(2)); return Result.success(Map.of("token", token, "userInfo", user)); }

逻辑说明:JwtUtil.generateToken()使用 HS256 算法,密钥写在application.yml的jwt.secret字段。Redis 存储是为支持 Token 主动注销(删除 Key 即失效),比单纯 JWT 更可控。

3.2 名片列表分页加载:如何避免小程序端“滚动即卡顿”的玄学问题?

小程序端contact-list.js使用onReachBottom()触发分页,但原始代码中page参数从 1 开始,而 MyBatis-Plus 的Page对象默认从 0 开始。现象:第一页数据重复,第二页开始漏数据。修复方法是在 Controller 层统一转换:

@GetMapping("/list") public Result list(@RequestParam Integer page, @RequestParam Integer size) { // page 从 1 转为 0 起始 Page<ContactInfo> pageInfo = new Page<>(page - 1, size); IPage<ContactInfo> result = contactService.page(pageInfo, new QueryWrapper<ContactInfo>().eq("user_id", getCurrentUserId())); return Result.success(result); }

参数说明:getCurrentUserId()从 Token 解析出userId,确保用户只能查自己名片。QueryWrapper构造器避免 SQL 注入,比拼接字符串安全。

前端分页关键代码:

// contact-list.js data: { contacts: [], page: 1, size: 10, hasMore: true }, onReachBottom() { if (!this.data.hasMore) return; this.setData({ page: this.data.page + 1 }); this.loadContacts(); }, loadContacts() { wx.request({ url: 'http://localhost:8080/api/contact/list', method: 'GET', data: { page: this.data.page, size: this.data.size }, header: { 'Authorization': 'Bearer ' + wx.getStorageSync('token') }, success: (res) => { const newContacts = res.data.data.records; if (newContacts.length < this.data.size) { this.setData({ hasMore: false }); } this.setData({ contacts: this.data.contacts.concat(newContacts) }); } }); }

避坑点:wx.request默认不携带 Cookie,Token 必须显式写入header;records是 MyBatis-Plus 分页返回的List字段名,不是data。

3.3 名片新增与图片上传:为什么用七牛云而非微信临时素材?

原始代码中图片上传走的是wx.uploadFile()直传后端,但生产环境必须改用对象存储。原因有三:

  1. 微信临时素材有效期仅 3 天,且调用量有限制;
  2. 后端接收图片再转存,增加服务器 IO 和带宽压力;
  3. 小程序端无法直接读取服务器文件路径,需额外接口返回 URL。

本系统已集成七牛云 SDK(qiniu-java-sdk),流程如下:

  1. 小程序端调用/api/upload/token获取上传凭证(含uploadToken、bucket、domain);
  2. 直接wx.uploadFile()上传到七牛up.qiniup.com;
  3. 七牛回调后端/api/upload/callback,保存图片 URL 到contact_info.avatar_url。
// UploadController.java @GetMapping("/token") public Result getToken() { Auth auth = Auth.create(accessKey, secretKey); String upToken = auth.uploadToken(bucketName); return Result.success(Map.of( "uploadToken", upToken, "bucket", bucketName, "domain", "https://your-bucket.qiniucs.com" // ← 替换为你的七牛域名 )); }

参数说明:accessKey/secretKey在application.yml中配置;bucketName是七牛空间名;domain必须是已绑定的 HTTPS 域名,否则小程序image组件无法加载。


4. 避坑指南:5 个让新手当天就放弃的典型问题与血泪解决方案

4.1 现象:小程序端wx.request报错request:fail net::ERR_CONNECTION_REFUSED

原因:后端服务未启动,或前端请求地址写成http://127.0.0.1:8080(iOS 真机无法解析127.0.0.1),或 Windows 防火墙阻止了 8080 端口。
解决:

  • 确认后端控制台有Started...日志;
  • 小程序端将请求地址改为本机局域网 IP(如http://192.168.1.100:8080),并在微信开发者工具中勾选「不校验合法域名」;
  • Windows 用户执行netsh advfirewall firewall add rule name="Allow Port 8080" dir=in action=allow protocol=TCP localport=8080。

4.2 现象:登录后进入名片页空白,控制台无报错,Network 查看/api/contact/list返回 401

原因:Token 过期未自动刷新,或AuthorizationHeader 未正确设置(常见于复制粘贴时多了一个空格)。
解决:

  • 在utils/request.js的interceptors.request.use中添加 Token 拦截:
config.header['Authorization'] = 'Bearer ' + wx.getStorageSync('token') || '';
  • 在响应拦截器中捕获 401,跳转登录页:
if (res.statusCode === 401) { wx.removeStorageSync('token'); wx.navigateTo({ url: '/pages/login/login' }); }

4.3 现象:MySQL 中contact_info表create_time字段全是0000-00-00 00:00:00

原因:MySQL 5.7 默认sql_mode含NO_ZERO_DATE,而实体类ContactInfo.java中@TableField(fill = FieldFill.INSERT)未指定默认值。
解决:

  • 修改application.yml,在spring.datasource.url后追加&zeroDateTimeBehavior=convertToNull;
  • 或在实体类中为createTime字段加注解:
@TableField(fill = FieldFill.INSERT) private Date createTime; // 并在 Mapper XML 中添加默认值 <insert id="insert" parameterType="com.example.entity.ContactInfo"> INSERT INTO contact_info (..., create_time, ...) VALUES (..., NOW(), ...) </insert>

4.4 现象:真机调试时点击“获取用户信息”按钮无反应,控制台报wx.getUserProfile is not a function

原因:基础库版本过低(低于 2.21.0),或未在app.json的permission中声明scope.userInfo。
解决:

  • 微信开发者工具右上角「详情」→「本地设置」→ 勾选「调整基础库版本」,设为2.24.4;
  • 检查app.json是否含:
"permission": { "scope.userInfo": { "desc": "用于完善您的资料" } }

4.5 现象:打包上传提示“代码包大小超过 2MB”,无法提交审核

原因:miniprogram/下存在node_modules或dist文件夹,或图片未压缩。
解决:

  • 删除miniprogram/node_modules(小程序不依赖 npm 包,所有 JS 都已编译进utils/);
  • 用 TinyPNG 压缩miniprogram/images/下所有 PNG/JPG;
  • 在project.config.json中确认"minified": true,并勾选「上传时压缩代码」。

5. 生产部署实操:Nginx 反向代理 + Spring Boot JAR 包部署 + 域名 HTTPS 化

5.1 后端打成可执行 JAR 包并 systemd 托管

不要用 IDE 直接运行,生产环境必须打 JAR。在项目根目录执行:

mvn clean package -Dmaven.test.skip=true # 输出 target/wx-card-system-1.0.jar

上传至服务器(如 Ubuntu 22.04),创建 systemd 服务:

sudo vim /etc/systemd/system/wx-card.service
[Unit] Description=WeChat Card System Backend After=network.target [Service] Type=simple User=ubuntu WorkingDirectory=/home/ubuntu/wx-card ExecStart=/usr/bin/java -jar /home/ubuntu/wx-card/wx-card-system-1.0.jar --spring.profiles.active=prod Restart=always RestartSec=10 Environment=JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64 [Install] WantedBy=multi-user.target
sudo systemctl daemon-reload sudo systemctl enable wx-card sudo systemctl start wx-card sudo systemctl status wx-card # 应显示 active (running)

关键点:--spring.profiles.active=prod激活生产配置,此时application-prod.yml中的spring.redis.host、spring.datasource.password等才生效;Environment=JAVA_HOME必须指向服务器 JDK 路径,否则启动失败。

5.2 Nginx 配置反向代理与 HTTPS 强制跳转

假设你已通过腾讯云 DNS 解析card.yourdomain.com到服务器 IP,并申请了免费 SSL 证书(推荐 Let's Encrypt)。Nginx 配置如下:

upstream card_backend { server 127.0.0.1:8080; } server { listen 80; server_name card.yourdomain.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name card.yourdomain.com; ssl_certificate /etc/nginx/ssl/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256; location / { proxy_pass http://card_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 静态资源(如 Swagger UI) location /swagger-ui.html { proxy_pass http://card_backend; } }
sudo nginx -t && sudo systemctl reload nginx

验证:浏览器访问https://card.yourdomain.com/swagger-ui.html应正常显示接口文档;小程序端将request地址改为https://card.yourdomain.com/api/...即可。

5.3 小程序端域名配置与合法 TLS 证书校验

微信要求所有wx.request域名必须在「小程序后台 → 开发管理 → 开发者工具 → 服务器域名」中备案。添加https://card.yourdomain.com(注意必须是 HTTPS,且不能带路径)。
关键细节:

  • 域名必须已通过 ICP 备案(个人主体可备);
  • SSL 证书必须由受信任 CA 签发(Let's Encrypt 可用,自签名证书会被微信拒绝);
  • Nginx 的ssl_certificate必须包含完整证书链(fullchain.pem),否则 iOS 真机会报net::ERR_CERT_AUTHORITY_INVALID。

5.4 数据库备份与监控:用 crontab + mysqldump 实现每日自动备份

在服务器上创建备份脚本/home/ubuntu/backup_db.sh:

#!/bin/bash DATE=$(date +%Y%m%d) mysqldump -uroot -pYourPassword wx_card_system | gzip > /home/ubuntu/backup/wx_card_system_$DATE.sql.gz # 保留最近 7 天 find /home/ubuntu/backup -name "wx_card_system_*.sql.gz" -mtime +7 -delete

添加定时任务:

chmod +x /home/ubuntu/backup_db.sh crontab -e # 添加一行: 0 2 * * * /home/ubuntu/backup_db.sh

安全提醒:-pYourPassword明文有风险,生产环境应使用.my.cnf配置文件([client]段落写password=xxx),并chmod 600 ~/.my.cnf。


6. 进阶技巧:如何用一套后端支撑多个小程序(销售版/HR版/客户版)而不改代码?

这是我带团队落地 3 个同类项目后总结出的最省力架构——不靠分支管理,而靠运行时租户隔离。核心是复用同一套 Java 后端 JAR,通过X-Tenant-ID请求头区分业务方,所有数据表加tenant_id字段,MyBatis-Plus 自动注入查询条件。

6.1 数据库层面:为每张业务表增加tenant_id并建立联合索引

以contact_info表为例,执行 SQL:

ALTER TABLE contact_info ADD COLUMN tenant_id VARCHAR(32) NOT NULL DEFAULT 'default'; ALTER TABLE contact_info ADD INDEX idx_tenant_id (tenant_id); -- 修改实体类 ContactInfo.java private String tenantId;

6.2 后端拦截器:自动解析X-Tenant-ID并注入 MyBatis-Plus 全局条件

创建TenantHandlerInterceptor.java:

@Component public class TenantHandlerInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String tenantId = request.getHeader("X-Tenant-ID"); if (StringUtils.isNotBlank(tenantId)) { TenantContext.setTenantId(tenantId); } return true; } }

配合TenantContext.java(ThreadLocal 存储)和MyMetaObjectHandler.java(自动填充tenant_id):

@Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, "tenantId", String.class, TenantContext.getTenantId()); // 调用 insert 方法时,自动填充 } }

6.3 全局 SQL 过滤:用 MyBatis-Plus 的ISqlInjector注入tenant_id = ?条件

在MybatisPlusConfig.java中:

@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() { @Override public Expression getTenantId() { return new LongValue(TenantContext.getTenantId()); } @Override public Field getTenantIdField() { return new Field("tenant_id"); } @Override public boolean ignoreTable(String tableName) { // 白名单表(如 sys_user)不加 tenant_id 条件 return Arrays.asList("sys_user", "sys_role").contains(tableName); } })); return interceptor; }

6.4 小程序端适配:不同版本共用同一套代码,只改app.js初始化逻辑

在app.js的onLaunch中:

// 根据小程序 AppID 动态设置 tenant_id const appidMap = { 'wx1234567890abcdef': 'sales-team', // 销售版 'wx0987654321fedcba': 'hr-dept', // HR 版 'wxfedcba9876543210': 'customer-vip' // VIP 客户版 }; const tenantId = appidMap[wx.getAccountInfoSync().miniProgram.appId] || 'default'; // 后续所有 request 自动带上 header wx.request({ url: 'https://card.yourdomain.com/api/contact/list', header: { 'X-Tenant-ID': tenantId } });

效果:三个小程序(销售/HR/客户)共用一个后端 JAR,数据库物理隔离(tenant_id作为分区键),运维成本降为 1/3。我去年用这招,把原本要维护 3 套后端的项目,压缩成 1 个 Git 仓库、1 台服务器、1 个数据库实例,上线后零事故。

这套名片系统真正的价值,从来不是“能跑起来”,而是它把 Java 后端的工程规范(分层、事务、缓存)、小程序前端的性能敏感点(分页、图片、登录态)、以及生产环境的落地细节(HTTPS、备份、监控)全串起来了。它不炫技,但每一步都经得起推敲。我带新人时总说:别急着改功能,先把login → list → add → upload这条主链路在本地、测试机、生产环境各跑三遍,把每个401、500、ERR_CONNECTION_REFUSED都亲手修一遍——那之后,你就真算入了门。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询