YAOTU INSIGHTS

Java后端+小程序花店系统:从部署到联调的完整实践指南

Java后端+小程序花店系统:从部署到联调的完整实践指南
简介这是一套基于微信小程序的网上花店系统源码后端采用Java、前端为原生小程序并搭配MySQL数据库适合毕业设计、课程设计或需要全栈项目实战的开发者参考。系统分为管理员与用户两种权限管理员能够维护用户信息、发布公告、管理鲜花库存并与用户互动用户端支持浏览鲜花、查看公告和接收管理员回复业务逻辑完整。项目基于JDK1.8与Maven3.3开发运行于Tomcat7数据库使用MySQL5.7。压缩包内共1778个文件主要包含Java后端代码、XML配置、SQL数据库脚本以及wxml、wxss、js、vue等小程序页面与逻辑文件另有png、svg等静态资源和Maven工程文件整体大小约36.23MB并附带安装、构建、启动bat脚本以便快速部署。这套可运行源码还保留了前端样式备份与关键工具脚本读者既能直接体验双角色功能也可在此基础上扩展购物车、订单管理或支付模块。目前已有63人浏览学习对于需要完整可运行毕业设计源码和二次开发模板的学习者而言是很实在的参考项目。1. 先说清楚你拿到的是什么做课程设计或毕业设计的人最怕一件事源码下了一堆解压后第一步就卡住。这套网上花店微信小程序是 java 后端 小程序前端 mysql 三方齐全的完整源码包不算花哨但它把管理员和用户两个权限闭环做完了——管理员能管用户、管公告、管鲜花信息还能和用户互相交流回复用户能看花、看公告、看管理员回复。它的适用场景非常明确Java 毕业设计、课程设计拿去做主体功能或者想一次搞明白“小程序怎么连 Java 后端、角色权限怎么落库、本地怎么跑通”这套完整链路。下文按拆包顺序讲双权限与表结构、本地部署脚本、小程序联调、四个常见坑最后给一套演示自查法。2. 双角色权限与数据落库管理员和用户凭什么各看各的2.1 先理清权限边界这套项目的权限模型是典型的两端式管理员端负责“管理”用户端负责“消费”。管理员能修改用户基本信息、发布和更新公告、维护鲜花分类与鲜花信息、回复用户的留言用户能浏览鲜花列表和详情、查看公告、提交自己的问题并查看管理员的回复。这个边界不是只靠前端隐藏按钮实现的。我在拆这类课设源码时一般会先看两个地方第一登录接口返回的数据里有没有 role 或 type 字段第二后端接口是不是做了一个简单的“当前登录人是不是管理员”的校验。正规一点的实现是两个登录接口分开或者同一个登录表中用 role 字段区分。前端再根据登录时返回的角色决定底部导航栏渲染哪些页面。管理员看到的是管理入口列表用户看到的是选购和公告入口两边共用同一套订单和鲜花数据。这种双权限设计的成本很低但对毕业设计答辩很有用——老师问“你怎么区分管理员和用户”你至少能说出“前端按角色渲染菜单后端在关键接口做了角色判断”而不是只能说“我加了几个按钮”。2.2 表结构六张表把花店闭环串起来源正文里没有把建表语句全部列出来但拆过同类项目就会知道网上花店这类课堂级源码数据库基本都会落这几张表。我按常见做法补全了一个典型结构方便你拿着去核对包里的 .sql 文件表名职责关键字段user管理员与用户共用或独立账号表id、username、password、role、phone、create_timeflower_category鲜花分类id、category_name、sortflower_info鲜花表id、flower_name、price、original_price、category_id、image、stock、description、statusnotice公告表id、title、content、create_timereply_info用户留言与管理回复表id、user_id、content、reply、reply_timeflower_order订单表id、order_no、user_id、flower_id、quantity、amount、status、create_time其中 user 表的 role 字段是整个权限体系的钥匙。它一般是一个 tinyint0 表示普通用户1 表示管理员。登录接口查到这个字段后会把 role 一起返回给小程序端前端存下来切换页面显示。flower_info 的 category_id 关联 flower_category 的 id是为了前端按分类筛选时做一个联表查询或者两次查询不少课设版本甚至不建 flower_category 表直接把分类名写死在花表里这种包维护性很差你看到.fla 如果分类字段是字符串而不是 id就能判断它不是规范版本。订单表在源正文里没有明确提但花店不做订单流程说不过去。正常答辩时老师一定会问“下单之后数据存哪”所以我在结构里补上了 flower_order。如果你拿到的包里确实没有订单表建议自己补字段就按上面这组来够用。2.3 字段约定与订单状态机先看懂再改老项目里有几个约定俗成的字段习惯新手容易在这里翻车。第一是 status 字段它通常不是“订单状态”而是“是否显示/是否上架/是否已处理”的通用开关。比如 flower_info 的 status1 表示上架可售status0 表示下架reply_info 的 status1 表示管理员已回复。前端查询接口往往会在 SQL 里带上 status1 这个条件你往数据库里插了测试数据却看不到先查这一条。第二是 is_delete 这类逻辑删除字段。课程设计级别的包很少物理删数据管理员在页面上“删除”一个用户实际执行的是 UPDATE 把 is_delete 置为 1而不是 DELETE。如果你直接对着数据库把 is_delete 删掉那只是清掉标记反过来如果你在数据表里找不到某条“被删掉”的记录也别怀疑是 bug先看是不是软删。第三是字符集。表结构一般建在 utf8 下MySQL 5.7 默认排序规则是 utf8_general_ci这里有一个隐藏坑管理员在后台录入带 emoji 的公告内容时utf8 是存不进去 emoji 的会报 Incorrect string value 错误。正常做法是把表和连接都改成 utf8mb4。后面避坑章节我会展开讲。订单状态机也是答辩高频提问点。常见状态值设计是0 待付款、1 已付款、2 已发货、3 已完成、4 已取消。管理员端的订单列表需要按状态筛选用户端我的订单页也需要根据状态显示“去付款/待发货/确认收货”之类按钮。如果你拿到的包没有订单状态机就按 0-4 这个约定自己加一个下拉状态筛选工作量不大但演示效果会好很多。3. 把后端跑起来JDK1.8、MySQL5.7、Tomcat7 这个组合为什么不能动3.1 版本匹配是这套源码的生命线很多人在拿到 java 课设源码之后第一反应是“我有 JDK17直接跑”。这是最常见的翻车开端。这套包的环境说明写得很死JDK1.8、MySQL5.7、Maven3.3、Tomcat7它不是随意写的而是老技术栈的硬约束。先看 JDK。老项目的 pom.xml 里依赖的 Spring 版本通常是 3.x 或 4.x这些版本在 JDK9 以上会遇到模块系统的兼容问题典型报错是 java.lang.NoClassDefFoundError 或者 javax/xml/bind 相关异常。JDK8 是这套组合的底线我建议你直接装一个 JDK1.8不要想着用高版本去兼容。再看 MySQL。老项目的驱动类名写的是 com.mysql.jdbc.Driver连接串一般长这样jdbc:mysql://localhost:3306/flower_shop?useUnicodetruecharacterEncodingutf8。这套驱动对 MySQL 8.0 的 caching_sha2_password 认证协议不兼容会报 Access denied 或者 Public Key Retrieval is not allowed。所以 mysql 5.7 在这个项目里不是建议是要求。最后是 Tomcat7。它对应 Servlet 3.0 规范老项目在 web.xml 里写的头声明、过滤器配置都按这个规范来。Tomcat8 和 9 不是跑不起来但如果有版本敏感的库会暴露很多细碎问题。稳妥做法是本地装一个 7.x 的 Tomcat端口保持 8080不要改。Maven3.3 同理它是能正常解析老版 pom 依赖的一个平衡点。新版 Maven 不是不行但你没必要在跑通阶段引入额外变量。3.2 数据库导入Navicat 连接与字符集设置数据库这一步建议用 Navicat 或者命令行都行重点是连接时把字符集选对。我在拆这个包的时候一般这么做第一步手工创建数据库 flower_shop注意排序规则选 utf8mb4 而不是默认的 utf8。第二步打开包里的数据库文件夹找到 .sql 文件用 Navicat 的“运行 SQL 文件”功能导入而不是用记事本打开复制粘贴到查询窗口。因为 .sql 文件里可能有多条 INSERT 语句带着中文复制粘贴时如果编辑器编码不对中文会直接变成乱码写入库后面界面上看到的全是问号。第三步导入后执行一条验证语句确认核心表数据在SELECT COUNT(*) FROM flower_info WHERE status 1;能返回一个大于 0 的数字说明鲜花表和分类表的初始化数据成功导入了。如果这条查询报 Table doesnt exist说明导入时选错了库或者数据库名字和配置文件里的 jdbc url 不一致。如果返回 0先查分类表一般花店初始化都会带几条默认分类分类表空的话花表大概率也是空的。3.3 三个 bat 脚本install、run、build 到底做了什么源包里给了 1-install.bat、2-run.bat、3-build.bat 三个脚本这是这类项目最朴素的交付方式。我一贯的原则是双击之前先看内容再用文本编辑器打开确认。以我拆过的多个类似包来看install.bat 的任务大致是设置 JAVA_HOME、检查环境变量、执行 Maven 打包然后把打出来的 war 包复制到 Tomcat 的 webapps 目录。run.bat 的任务是启动 Tomcat 的 startup.bat本质是catalina.bat run或者startup.bat。build.bat 则是单独重新打包。一个常见的脚本长这样echo off set JAVA_HOMEC:\Program Files\Java\jdk1.8.0_202 set PATH%JAVA_HOME%\bin;%PATH% call mvn clean package -DskipTests xcopy /y target\flowerShop.war C:\tomcat7\webapps\ echo install finished pause这里要注意两个参数JAVA_HOME 的路径必须指向你机器上真实安装的 JDK1.8 位置target 目录里 war 包名字必须和 pom.xml 的 artifactId 一致。如果不一致xcopy 会报找不到文件这是最直接的失败信号。如果你的包不是 war 而是 jar脚本可能就是java -jar flowerShop.jar但 Tomcat7 组合下war 部署居多。跑完 install 后不要立刻双击 2-run.bat先看一眼 C:\tomcat7\webapps 下有没有解压出来的项目目录。war 包被 Tomcat 解压成文件夹才算真正进入启动流程。3.4 怎么判断后端真的起来了新手很容易犯一个错误双击了 startup.bat看到黑色窗口一闪而过就说“起不来”。实际上 Tomcat 启动窗口闪退很常见你要看的是日志不是窗口。我一般用一个三步检查法。第一步确认 TOMCAT 安装目录下的 logs 文件夹里有没有 catalina.out 或者当天日期的 catalina.yyyy-MM-dd.log第二步打开日志搜 “Server startup in” 这几个字看到它说明容器起来了第三步再搜第一次出现的异常堆栈比如 ClassNotFoundException、Port already in use、BeanCreationException。端口占用是这边最高频的问题。如果 8080 被占日志里会出现Port 8080 was already in use或者是 java.net.BindException。解决方式有两种关掉占用程序的进程或者改 Tomcat 的 server.xml 端口。但要注意改了 Tomcat 端口就得同步改小程序端 baseUrl 里的端口后面联调就会发现两边对不上。所以我的习惯是先查占用而不是改端口。数据库连接不上也是日志阶段常见的失败。报错Access denied for user rootlocalhost说明配置文件里的数据库账号密码和本地不一致找到项目下的 jdbc.properties 或 application.properties 改掉。报错Unknown database flower_shop则说明库名不对或者根本没创建数据库。这个阶段确认后端能正常响应后再打开浏览器访问http://localhost:8080/flowerShop/能看到默认首页或者接口返回 JSON就算后端跑通了。记住这个根路径小程序联调时要拼到 baseUrl 里。4. 小程序端联调HBuilderX 打开、baseUrl 改写、登录 token 持久化4.1 先分清源码和构建产物解压包之后你会看到一些眼熟的文件main.css.bak、uni-fab.vue.bak、app.07ff6003.css、chunk-vendors.1f0a25b2.css、uniicons.css。这里面有一个重要区分.bak 结尾的是源码文件被手动改出问题时留的备份带 hash 的是打包产物。main.css.bak 和 uni-fab.vue.bak 说明这个包的原主人在改样式或悬浮按钮时做过备份。遇到 .bak 文件我一般会打开看一眼它可能保存着改动前的可用版本。如果你改完 uni-fab.vue 页面报错了把 .bak 内容复制回去就能恢复这相当于一个后悔药。更关键的是带 hash 的那几个 css 文件比如 app.07ff6003.css、chunk-vendors.1f0a25b2.css。它们是 HBuilderX 或微信开发者工具构建后生成的静态资源不是源码。很多同学拿到包之后直接用微信开发者工具“导入项目”指到了这个目录看到的确实是页面但不管怎么改源码界面都不变。原因就是你跑的是已经编译好的产物源码根本没参与构建。正确姿势是用 HBuilderX 识别这个项目的 uni-app 工程找到源码入口然后通过 HBuilderX 自带的“运行到小程序模拟器”生成新的编译产物而不是直接打开 dist 目录。项目正文里能看到 uni-fab.vue 这类 uni-app 组件说明它大概率是 uni-app 工程HBuilderX 是最合适的打开工具。4.2 把 baseUrl 改到你本地后端后端跑通只是第一步小程序要能跟后端说话得先统一接口地址。课程设计项目最常见的方式是封装一个 request.js所有页面都调它。这类文件里会有类似这样的代码const BASE_URL http://127.0.0.1:8080/flowerShop; function request(url, method GET, data {}) { return new Promise((resolve, reject) { uni.request({ url: BASE_URL url, method: method, data: data, header: { Content-Type: application/x-www-form-urlencoded, token: uni.getStorageSync(token) || }, success: (res) resolve(res.data), fail: (err) reject(err) }); }); } module.exports { request, BASE_URL };你要改的就是 BASE_URL 这一个值第一后端启动后项目上下文路径要和这里一致后端部署在http://localhost:8080/flowerShop那这里就写http://127.0.0.1:8080/flowerShop第二真机调试时 localhost 指向的是手机自己必须改成电脑在局域网里的 IP比如http://192.168.1.100:8080/flowerShop并且确认防火墙放行了 8080 端口。这个封装里有两个容易被忽略的细节。header 里带了 token但注意很多老后端接口不用 token 而用 session 机制那这一行就没有实际作用也不会报错只是拿不到用户态。Content-Type 写成 x-www-form-urlencoded说明后端大概率是用传统的 Servlet 接收参数而不是 RequestBody JSON 接口。如果你把 Content-Type 改成 application/json后端可能取不到参数接口直接返回空数据。这两个细节不搞清楚联调时会反复出现“接口通但不返回数据”的诡异情况。4.3 登录态token 存哪、怎么带、怎么过期登录接口的联调是整个小程序端最关键的一环。老项目里通常有 getSession 或者 login 接口小程序拿到 code 或者账号密码后后端返回一个用户标识可能是 sessionid 也可能是 token。以 HBuilderX 里的 uni-app 写法为例登录成功后的处理通常是uni.request({ url: BASE_URL /user/login, method: POST, data: { username: this.username, password: this.password }, success: (res) { if (res.data.code 200) { uni.setStorageSync(token, res.data.data.token); uni.setStorageSync(userInfo, res.data.data.userInfo); uni.switchTab({ url: /pages/index/index }); } else { uni.showToast({ title: res.data.msg || 登录失败, icon: none }); } } });这里的核心动作是 uni.setStorageSync 把 token 持久化到本地缓存。也就是说小程序每次冷启动后只要本地缓存里还有 token就不需要重新登录。这一点在演示时非常重要——你不可能当着老师的面重新输一遍账号密码提前登录好打开就是用户页。需要注意 token 的过期处理。很多课设包后端根本不做 token 过期判断接口里只要 token 非空就算登录成功那前端也要有一个兜底如果接口返回 401 或者提示未登录要统一清理本地缓存并跳回登录页。我在联调时发现这类项目最容易出现的问题是后端重启了session 没了小程序端还拿着旧的 sessionid 去请求接口一直返回未登录。解决办法是我在 request.js 里统一拦截 401强制退出且清缓存。虽然小项目里这个机制算重但演示时能避免很多尴尬。用户和管理员的角色判断也要在这里处理。登录返回的 userInfo 里如果有 role 字段建议在登录成功时同时做一次判断然后跳转到不同的页面role1 跳管理员首页role0 跳用户首页。这样才完整利用了第二章讲的权限设计。5. 避坑本地跑通花店小程序的四个翻车点5.1 Tomcat 起不来端口占用和 JDK 版本两条主线现象双击 2-run.bat 或者直接运行 startup.bat窗口闪了一下就没了浏览器访问 8080 端口打不开。再点一次Tomcat 日志里看到 java.net.BindException: Address already in use: JVM_Bind。原因8080 端口被别的 java 进程占了这是跑多个 Tomcat 或者装了其他服务时的高频事故。另外还有一个隐蔽原因你机器上默认的 JAVA_HOME 指向 JDK17老项目编译出来的 class 兼容不了。解决先用netstat -ano | findstr 8080查端口占用记住 PID再到任务管理器把对应进程结束。如果是 JDK 版本问题最省事的手段是下载一个绿色版 JDK8然后在 startup.bat 前一行临时 set JAVA_HOME 到那个目录。注意改完 JAVA_HOME 后要新开命令行窗口再启动环境变量不会自动刷新。5.2 MySQL 导入即报错驱动、认证、字符集三重门现象数据库连接失败日志显示 Communications link failure或者 Access denied for user rootlocalhost。导入 .sql 过程中报 Incorrect string value 的中文乱码错误。原因这三个报错分别对应不同问题。Communications link failure 常见于数据库没启动或者后端连着 3307 端口但你本地 MySQL 只监听 3306Access denied 是账号密码不对Incorrect string value 是字符集问题老 .sql 文件如果带着 latin1 或 utf8 的建表语句插入 emoji 时会挂掉。解决打开项目下的数据库配置文件把 url 改成jdbc:mysql://localhost:3306/flower_shop?useUnicodetruecharacterEncodingutf8useSSLfalse账号密码改成你本地真实值。字符集问题先在 MySQL 里执行ALTER DATABASE flower_shop CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;再把所有表的字符集也改成 utf8mb4。课设项目字符集统一成 utf8mb4 不会引入新问题。5.3 小程序首页空白、接口 404、登录弹窗不出现现象HBuilderX 运行到微信开发者工具小程序能打开但首页数据是空的打开调试器看网络请求接口地址返回 404 或者 request:fail。原因三种情况最常见。第一种BASE_URL 里写的是 localhost在微信开发者工具模拟器里能用但真机预览时 localhost 指向手机本地根本连不到电脑。第二种接口路径拼错了BASE_URL 末尾带不带斜杠、后端上下文路径对不对差一个字符就是 404。第三种微信开发者工具默认开启了“校验合法域名”本地调试的 http 接口会被拦截成 request:fail这个错不会显示成 404而是直接校验失败。解决真机调试一律把 BASE_URL 换成电脑局域网 IP先去掉 BASE_URL 末尾的斜杠再用后端接口文档里确认路径在微信开发者工具的“详情 - 本地设置”里勾选“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”。第三点在新版本工具里默认不勾选本地联调时必须手动开否则 http 接口永远走不通。5.4 改了源码界面纹丝不动你改错了文件位置现象在 HBuilderX 里改了 pages/index/index.vue 的标题文字和按钮颜色重新编译也成功了但微信开发者工具页面没任何变化。有人开始不停重启、清缓存甚至怀疑代码逻辑写错了。原因包里的 app.07ff6003.css、chunk-vendors.1f0a25b2.css 这类带 hash 的样式文件是构建产物说明你或前任主人在某个环节打开过“编译后目录”。HBuilderX 的编译输出目录也可能被指到了旧路径导致刷新的是旧的 dist 文件夹而不是重新编译的产物。解决检查 HBuilderX 的“运行”配置确认运行目标是当前源码工程目录而不是直接打开 dist。在微信开发者工具里点“编译”之前先确认工具打开的根目录是 HBuilderX 编译输出目录这个目录一般叫 unpackage/dist/dev/mp-weixin。如果改了还是不行最简单的后悔药是删掉 unpackage 目录再重新运行到小程序模拟器让它全量重新编译。这招我屡试不爽——编译缓存引起的玄学问题删目录永远是第一解。6. 从课设到演示三分钟验证法与两个低成本优化6.1 三分钟验证法跑通之后别急着截图。我建议你在答辩前按下面这个顺序走一遍验证每条预计几秒到几十秒总共三分钟内搞定验证对象操作预期结果失败排查方向管理员登录用管理员账号登录跳转管理员首页能看到用户管理、公告管理入口后端日志有没有登录请求角色字段是否返回 1发布公告管理员新增一条公告公告列表出现新数据用户端公告页可见检查 notice 表新增记录的 status 是否为 1用户注册用户端注册一个新账号用户表新增记录前端自动登录成功注册接口是否写入 user 表role 默认是否为 0用户下单用户端对一件鲜花下单订单列表出现待付款订单flower_order 表是否新增记录status 是否为 0管理员回复用户留言后管理员回复用户端能查看到回复内容回复接口是否同时更新 reply 和 reply_time这个验证法的逻辑是覆盖两个角色的核心链路。如果以上五步全通过这套包的主要功能就没有大问题如果某一步卡住按右边列出的方向去翻基本都是表字段或接口路径的问题。6.2 两个低成本优化统一响应体和接口鉴权过滤器课设答辩时老师会问你“每个接口都写重复的返回代码吗”“接口安全怎么做”。这两个问题很常见与其口头解释不如在包里改出样子来。第一个优化是统一响应体。老项目里典型写法是每个接口自己拼 JSON返回结构五花八门有的返回 code有的返回 success。统一改成一个小类后续所有接口都从它返回public class ResultBeanT { private Integer code; private String msg; private T data; public static T ResultBeanT ok(T data) { ResultBeanT r new ResultBean(); r.code 200; r.msg success; r.data data; return r; } public static T ResultBeanT fail(String msg) { ResultBeanT r new ResultBean(); r.code 500; r.msg msg; return r; } }这个类的意义在于前端 request 封装只需要判断 code不需要猜后端返回了什么结构。接口路径里凡是返回 JSON 的地方都把原有的手动拼接替换成 ResultBean.ok(...) 或者 ResultBean.fail(...)。第二个优化是接口鉴权过滤器。老项目最怕的就是“管理员接口不设防”任何人拿着一串接口地址都能调用。添加一个简单的拦截器对 /admin/ 开头的接口做登录校验public class AdminFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) { HttpServletRequest request (HttpServletRequest) req; HttpSession session request.getSession(); Object role session.getAttribute(role); if (role ! null 1.equals(role.toString())) { chain.doFilter(req, resp); } else { ((HttpServletResponse) resp).sendError(401, no permission); } } }过滤器的作用不是把系统做多安全而是让答辩老师看到“你有鉴权意识”也让你自己在演示的时候不用提心吊胆地怕被点到薄弱处。这套包的价值在于结构完整、角色清楚、数据库脚本可用这是它比那些只有一个页面的 Demo 强的地方。我每次拿到这类源码都会强制自己先走一遍版本自检和接口链路验证再开始改业务。因为只要环境没跑通就动手改代码最后你会分不清报错到底是环境问题还是自己的改动问题。这套流程我重复了十几遍跑通之后再改任何功能都顺手很多。希望帮到你。本文还有配套的精品资源点击获取