我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

Java+小程序名片系统:可控落地的全栈实践指南

Java+小程序名片系统:可控落地的全栈实践指南 简介这是一套面向高校计算机专业学生与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 写后端 APISpring BootMySQL 存结构化数据小程序前端做轻量交互三者边界清晰、日志可查、权限可配、扩容有路。它不追求“五分钟上线”但能让你在三天内完成本地调试 → 内网测试 → 域名备案 → 正式部署的完整链路。适合正在带 310 人小团队的技术负责人、想把内部工具从 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.208-post-Ubuntu-1ubuntu122.04) # OpenJDK 64-Bit Server VM (build 11.0.208-post-Ubuntu-1ubuntu122.04, mixed mode, sharing) # 验证 Maven3.6.3 即可不要用 3.9.xSpring Boot 2.3 不兼容 mvn -v提示Windows 用户务必检查JAVA_HOME是否指向 JDK 目录不是 JRE且PATH中包含%JAVA_HOME%\bin。Mac 用户若用 Homebrew 安装执行brew install openjdk11 sudo ln -sfn /opt/homebrew/opt/openjdk11/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.ymlspring: datasource: url: jdbc:mysql://localhost:3306/wx_card_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueuseSSLfalse username: root password: your_mysql_password # ← 必须改成你本地 MySQL 的密码 redis: host: localhost port: 6379 password:参数说明serverTimezoneAsia/Shanghai是强制项否则时间字段如create_time存入数据库会偏移 8 小时allowPublicKeyRetrievaltrue是 MySQL 8.0 必需参数否则连接报错useSSLfalse在本地开发可关闭生产环境必须启用并配置证书。启动类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: 40013appid 不存在。测试号无需企业认证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_keyhttps://api.weixin.qq.com/sns/jscode2session但绝不把session_key存库或返回前端——这是安全红线。实际流程是后端用openid查询数据库若存在则生成 JWT Token有效期 2 小时若不存在则插入新用户记录nickName,avatarUrl来自wx.getUserProfile()后续获取Token 存入 RedisKey 为token:{uuid}Value 为{openid:xxx,userId:123}过期时间设为 2 小时前端将 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_typeauthorization_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 起始 PageContactInfo pageInfo new Page(page - 1, size); IPageContactInfo result contactService.page(pageInfo, new QueryWrapperContactInfo().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默认不携带 CookieToken 必须显式写入headerrecords是 MyBatis-Plus 分页返回的List字段名不是data。3.3 名片新增与图片上传为什么用七牛云而非微信临时素材原始代码中图片上传走的是wx.uploadFile()直传后端但生产环境必须改用对象存储。原因有三微信临时素材有效期仅 3 天且调用量有限制后端接收图片再转存增加服务器 IO 和带宽压力小程序端无法直接读取服务器文件路径需额外接口返回 URL。本系统已集成七牛云 SDKqiniu-java-sdk流程如下小程序端调用/api/upload/token获取上传凭证含uploadToken、bucket、domain直接wx.uploadFile()上传到七牛up.qiniup.com七牛回调后端/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:8080iOS 真机无法解析127.0.0.1或 Windows 防火墙阻止了 8080 端口。解决确认后端控制台有Started...日志小程序端将请求地址改为本机局域网 IP如http://192.168.1.100:8080并在微信开发者工具中勾选「不校验合法域名」Windows 用户执行netsh advfirewall firewall add rule nameAllow Port 8080 dirin actionallow protocolTCP localport8080。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后追加zeroDateTimeBehaviorconvertToNull或在实体类中为createTime字段加注解TableField(fill FieldFill.INSERT) private Date createTime; // 并在 Mapper XML 中添加默认值 insert idinsert parameterTypecom.example.entity.ContactInfo INSERT INTO contact_info (..., create_time, ...) VALUES (..., NOW(), ...) /insert4.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.skiptrue # 输出 target/wx-card-system-1.0.jar上传至服务器如 Ubuntu 22.04创建 systemd 服务sudo vim /etc/systemd/system/wx-card.service[Unit] DescriptionWeChat Card System Backend Afternetwork.target [Service] Typesimple Userubuntu WorkingDirectory/home/ubuntu/wx-card ExecStart/usr/bin/java -jar /home/ubuntu/wx-card/wx-card-system-1.0.jar --spring.profiles.activeprod Restartalways RestartSec10 EnvironmentJAVA_HOME/usr/lib/jvm/java-11-openjdk-amd64 [Install] WantedBymulti-user.targetsudo systemctl daemon-reload sudo systemctl enable wx-card sudo systemctl start wx-card sudo systemctl status wx-card # 应显示 active (running)关键点--spring.profiles.activeprod激活生产配置此时application-prod.yml中的spring.redis.host、spring.datasource.password等才生效EnvironmentJAVA_HOME必须指向服务器 JDK 路径否则启动失败。5.2 Nginx 配置反向代理与 HTTPS 强制跳转假设你已通过腾讯云 DNS 解析card.yourdomain.com到服务器 IP并申请了免费 SSL 证书推荐 Lets 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 签发Lets 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]段落写passwordxxx并chmod 600 ~/.my.cnf。6. 进阶技巧如何用一套后端支撑多个小程序销售版/HR版/客户版而不改代码这是我带团队落地 3 个同类项目后总结出的最省力架构——不靠分支管理而靠运行时租户隔离。核心是复用同一套 Java 后端 JAR通过X-Tenant-ID请求头区分业务方所有数据表加tenant_id字段MyBatis-Plus 自动注入查询条件。6.1 数据库层面为每张业务表增加tenant_id并建立联合索引以contact_info表为例执行 SQLALTER 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.javaComponent 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.javaThreadLocal 存储和MyMetaObjectHandler.java自动填充tenant_idComponent 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都亲手修一遍——那之后你就真算入了门。希望帮到你。本文还有配套的精品资源点击获取
返回列表