恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
微信小程序+Java后端鲜花销售毕设开发全攻略
首页
资讯中心
/
微信小程序+Java后端鲜花销售毕设开发全攻略
微信小程序+Java后端鲜花销售毕设开发全攻略
发布时间:2026/10/8 0:50:55
简介一份基于微信小程序与Java后端SSMMySQL的鲜花销售毕业设计完整工程包面向计算机相关专业毕业生、课程设计及小程序实战学习者覆盖管理员、用户、商家三个角色实现了首页、鲜花信息与分类管理、订单及系统管理等核心业务模块。整个压缩包共1341个文件约12.51MB以png图片、Java源码、Vue页面、JavaScript逻辑、wxml/wxss小程序页面及SQL脚本为主同时包含bat启动脚本与说明文档便于直接部署和二次开发。资源提供完整源码和数据库其中附带数据库脚本、构建运行批处理及详细说明可帮助读者理解SSM框架在小程序后台中的应用也能快速跑通前后端联调。目前已有235人学习下载适合需要完整毕业设计案例和项目实战经验的人群参考。1. 毕业设计选它不只是为了“能答辩”这一句又到一年毕设季很多人在选题页面反复横跳。如果你手头拿到的题目是“基于微信小程序java后端的鲜花销售”先别急着把它归为“又一套商城系统”。这个项目真正跑通之后你会发现它把前后端分离、微信登录、订单流程、数据库表设计这些面试常问的东西全串起来了。对Java基础还不算扎实、又想快速拥有一套能演示、能讲清楚的项目的人来说它是性价比很高的一个。下面按我实际跑通的路径来讲项目拆解、本地启动、前后端对接、常见坑以及怎么把它改成你自己的毕设。2. 先把项目拆开看后端、小程序与数据库各管什么拿到压缩包解压后不要急着双击 README先按“数据库脚本、后端工程、小程序前端”三个目录去认识它。这类毕设工程的典型结构是一个 Spring Boot 后端提供 RESTful 接口一个原生微信小程序作为前端一个 MySQL 脚本负责初始化数据。三者通过 HTTP 接口关联数据表通过外键和业务逻辑维系。下面把每一块说清楚免得你后面改的时候不知道动哪里。2.1 管理员端功能清单商品、分类、订单、轮播是标配后端管理接口对应的是一个管理后台。虽然这个压缩包不一定自带 Vue 后台页面但接口层是完整的。常见做法是后端提供一组/admin/**接口管理员登录后可以对商品做增删改查、上下架管理商品分类处理订单状态待发货、发货、完成还能配置首页轮播图。我拿到的这份工程里管理员登录走的是admin表密码用 MD5 加密存储初始账号在 SQL 脚本里就能看到。商品这块一般包含名称、主图、轮播图列表、详情富文本或图片、分类ID、价格、库存、销量。这些字段在商品表flower或product里都能对上。订单表则记录用户 ID、收货地址、总金额、状态、下单时间子表order_item存每个商品的数量和单价。主表负责汇总子表负责明细是标准的一对多设计。分类表通常很简单id、名称、图片、排序号。轮播图表也是 id、图片、跳转链接。所以如果你调研需求会发现“鲜花销售”本质上就是一个通用商城把商品名词换掉就能套用到其他垂直品类。这也是为什么这类项目容易被改造成蛋糕店、零食店、二手交易。答辩时你把这一层讲清楚老师就知道你不是只会照抄。2.2 用户端小程序登录、浏览、加购、下单一条线用户端是原生微信小程序页面大概包括首页轮播分类入口商品列表、分类页、商品详情页、购物车页、确认订单页、订单列表页、个人中心页。逻辑上是一条完整链路用户打开首页看商品点进详情加入购物车结算下单模拟支付然后在订单列表看到状态变化。这一步覆盖了小程序开发里最核心的页面传参、本地缓存、请求封装和组件复用。登录部分小程序端用wx.login拿code传给后端后端替小程序向微信服务器换openid然后生成自定义 token 返回给前端。有些版本还会调getPhoneNumber组件获取手机号但注意这个接口需要小程序认证为企业主体个人主体没法用这点在避坑章节再强调。购物车数据通常存在后端表里cart表也有简化版存在本地wx.setStorageSync里。这份工程既然带了数据库多半走的是后端购物车。这样订单表直接引用购物车数据生成逻辑闭环更完整。如果你拿到的版本是本地购物车也无妨但要注意换设备后购物车会丢答辩时可以提一句这是简化方案。2.3 数据库设计概览五张核心表怎么关联打开 SQL 脚本重点看这几张表表名核心字段作用adminid, username, password管理员登录userid, openid, nickname, avatar, phone用户信息categoryid, name, sort商品分类flowerid, category_id, name, price, stock, image, detail商品主体cartid, user_id, flower_id, quantity, checked购物车orderid, order_no, user_id, total_amount, status, address_id订单主表order_itemid, order_id, flower_id, quantity, price订单明细bannerid, image, link首页轮播关系很好懂user与order是一对多order与order_item是一对多flower与category是多对一。购物车里一条记录对应一个用户和一个商品。这种设计是教科书级别的范式不花哨但适合答辩——老师问外键、索引、三范式你都能指着表回答。这里有一个细节订单金额在order表里冗余了一份而不是每次都去order_item里 sum。这在商城系统里是常见做法因为订单生成后商品价格可能变动必须把下单那一刻的金额快照保存下来。面试如果被问到“商品改价了老订单怎么算”答案就在这里。另外商品表flower我一般建议多一个status字段0 下架 1 上架和sales字段销量这两列在毕设演示中非常有用。下架商品在前端列表不展示销量可以在商品详情页展示。SQL 脚本里如果缺少自己ALTER TABLE补上即可不影响原逻辑。数据库字符集一般设置成utf8mb4否则表情符号存不进去。商品名称、用户昵称里可能出现 emoji用utf8会报错或变问号。稍后避坑章节再展开。3. 从本地跑起来JDK、MySQL与微信开发者工具的三步顺序很多人拿到源码第一步就卡在环境上。这个项目我建议按“后端→数据库→小程序”的顺序依次启动每一步验证通过再进下一步不要在没跑通后端的情况下就去点小程序不然接口报错你会分不清是前端问题还是后端问题。3.1 Spring Boot 后端启动前的环境准备这类项目的后端技术栈基本是 Spring Boot 2.x MyBatis / MyBatis-Plus Maven。打开pom.xml确认依赖版本。建议用 JDK 1.8Spring Boot 2.x 搭配很稳如果装了 JDK 17有些旧依赖会因javax命名空间问题加载失败。我一般会在 IDEA 里把 Project Structure 里的 SDK 切到 1.8Maven Runner 里的 JRE 也选 1.8避免编译期报错。启动前要在 IDEA 里做两件事一是确认 Maven 已经配了阿里云镜像否则下载依赖能磨掉你半小时耐心二是打开application.yml看一眼端口和数据源配置。server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/flower_shop?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的数据库密码 driver-class-name: com.mysql.cj.jdbc.Driver这里的serverTimezoneAsia/Shanghai很关键MySQL 8 以上不写它连接时大概率报时区错误。characterEncodingutf8则保证中文正常写入。如果你的 MySQL 是 5.7驱动用com.mysql.jdbc.Driver也行但新版工程一般都用cj驱动。参数说明useUnicodetrue让驱动按 Unicode 传输字符配合characterEncodingutf8才能保证中文不乱码。MySQL 8 建议用utf8mb4但驱动对utf8兼容。启动方式有两种IDEA 里直接运行Application.java的 main 方法或命令行mvn spring-boot:run。控制台出现Started Application in x.xxx seconds且没有红色异常就说明后端起来了。此时浏览器访问http://localhost:8080/api/xxx具体路径看 Controller 注解能看到 JSON说明接口正常。3.2 导入数据库脚本并核对账号配置数据库脚本一般在工程根的sql目录下文件名类似flower_shop.sql。用 Navicat 或命令行导入。命令行导入命令mysql -uroot -p flower_shop.sql导入后执行show tables;你应该能看到 8 张左右的表。如果一张表都没看到检查 SQL 脚本里是否带了CREATE DATABASE flower_shop而当前登录用户没有建库权限。常见做法是脚本里有建库语句导入后要切换库use flower_shop;。这里有一个容易忽略的点脚本里的管理员初始密码是加密后的 MD5 值除非你知道原始密码否则不要直接改admin表的 password 字段。登录不了后台时最稳妥的办法是找代码里的MD5Util或DigestUtils手动把新密码加密后 update 进表。我见过太多人直接把这个字段改成明文然后卡在“密码错误”上花半天找原因。另外检查application.yml里的数据库名、用户名、密码是否与本地一致。数据库名一般是flower_shop如果脚本里建库名不一样要么改 yml要么改脚本二选一不要两处都留着不同。你可以用DESC user;看下表结构验证脚本是否完整。如果表里已经带了测试数据启动后首页就会有商品省得自己造数。3.3 小程序端导入微信开发者工具后端和数据库都就绪后打开微信开发者工具选择“导入项目”目录指向小程序端文件夹通常叫miniprogram或frontend。这里需要两个关键配置一是你自己的测试 AppID如果没有用测试号也可以但登录相关功能受限二是本地开发时在“详情→本地设置”里勾选“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”。小程序端的请求地址一般在utils/config.js或app.js里常见写法const BASE_URL http://127.0.0.1:8080/api; module.exports { BASE_URL }注意微信开发者工具里请求localhost有时会因 IPv6 解析慢建议直接用127.0.0.1。如果你后端起在别的机器或云上这里就要改成服务器 IP 或域名并配好微信后台的 request 合法域名。本地开发勾选不校验域名就行但真机预览时手机无法访问你电脑的localhost需要把后端部署到可访问的服务器或者用内网穿透工具这里不展开。导入后编译如果看到首页轮播图和商品列表都出来了说明前后端已经打通。如果商品列表空白打开调试器看 Console 报错最常见的是请求 404 或请求被拦截。还有一个适配问题如果页面顶部出现重影或按钮和胶囊重叠是因为自定义导航栏没有适配状态栏高度。通常用wx.getSystemInfoSync()获取statusBarHeight再用wx.getMenuButtonBoundingClientRect()拿到胶囊的位置动态计算导航栏高度才能保证在刘海屏和普通屏上都不错位。4. 小程序与Java后端联调登录换取openid与Token鉴权这一章是毕设答辩的重头戏。老师问“怎么知道你登录了”“接口安全怎么做的”你只要能说清楚登录时序和 token 校验基本就能过关。下面按实际代码路径讲。4.1 wx.login 拿到 code后端换 openid 的流程小程序端点击微信登录时调用wx.login获取一个临时凭证code这个 code 只能使用一次有效期五分钟。把 code 通过接口 POST 给后端后端拿着 code 去微信接口jscode2session换取openid和session_key。openid 是用户在微信体系下的唯一 ID用它去user表里查查不到就注册新用户。后端代码如下伪代码逻辑具体类名以源码为准PostMapping(/auth/login) public Result login(RequestBody LoginRequest req) { // 1. 用小程序的 appid 和 secret以及前端传的 code请求微信接口 String url https://api.weixin.qq.com/sns/jscode2session?appid APPID secret SECRET js_code req.getCode() grant_typeauthorization_code; String response HttpUtil.get(url); // 2. 解析返回的 openid JSONObject obj JSON.parseObject(response); String openid obj.getString(openid); // 3. 查表没有则注册 User user userMapper.selectByOpenid(openid); if (user null) { user new User(); user.setOpenid(openid); userMapper.insert(user); } // 4. 生成自己的登录态 token String token UUID.randomUUID().toString().replace(-, ); redis.set(token_ token, String.valueOf(user.getId()), 7 * 24 * 3600); return Result.success(token); }逻辑说明后端不信任前端传来的任何身份信息只用code向微信服务器兑换身份这是安全边界的第一条。grant_typeauthorization_code是固定值表示 OAuth2 授权码模式。如果你没有 Redis也可以用session或直接返回token并存在内存 Map 里但项目重启就失效毕设够用答辩时记得说明缺点。参数说明APPID和SECRET在小程序后台的“开发→开发管理→开发设置”里获取。注意 SECRET 不要硬编码在 Web 端也不要在小程序代码里出现否则任何人都能拿去调用接口。我见过源码里把这两个值写在util.js里的这种做法答辩时会被扣分至少把它挪到后端配置里。小程序端对应代码wx.login({ success: async (res) { const code res.code; const loginRes await request({ url: /auth/login, method: POST, data: { code: code } }); wx.setStorageSync(token, loginRes.data.token); } })这里把token存到本地缓存后续每个请求在 header 里带上Authorization: token后端拦截器统一校验。4.2 前后端分离项目的接口路径与参数约定后端 Controller 一般会分成两类用户端/api/**和管理端/admin/**。用户端接口例如GET /api/banner、GET /api/flower/list?categoryId1、POST /api/cart/add、POST /api/order/submit。管理端需要管理员身份通常会有一个/admin/login登录后返回管理员 token后续管理操作携带该 token。统一返回格式非常重要推荐约定成{ code: 0, message: success, data: { } }code0表示成功非 0 为业务异常。前端在request封装里统一拦截function request(options) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL options.url, method: options.method || GET, data: options.data || {}, header: { Content-Type: application/json, Authorization: wx.getStorageSync(token) || }, success: (res) { if (res.data.code 0) { resolve(res.data); } else { wx.showToast({ title: res.data.message, icon: none }); reject(res.data); } }, fail: (err) reject(err) }) }) }这段封装的价值在于所有页面不用关心 token 怎么传统一在 header 里带所有后端返回的错误在success回调里统一弹 toast。页面代码只管调用request不需要散落一堆try/catch。参数说明BASE_URL不要写在每个页面里url以/开头拼接。如果你遇到“接口通了但登录不了”优先检查 header 里的 token 名是否和后端拦截器取的 header 名一致。4.3 常见做法用 Token 还是 Session 做认证微信小程序没有 Cookie 概念所以传统 Session 方式要么手动把JSESSIONID存起来再传要么干脆用 Token。Token 方案在前后端分离项目里是主流毕设中用起来也简单登录成功后返回一个随机字符串后端用一个 Map 或 Redis 存这个 token 对应的用户 ID下一次请求带着 token后端查 Map 就知道是谁。Component public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token null || token.isEmpty()) { response.setStatus(401); return false; } String userId tokenStore.get(token); if (userId null) { response.setStatus(401); return false; } request.setAttribute(userId, userId); return true; } }这个拦截器要注册到 WebMvcConfigurer 里并排除登录接口和商品浏览接口否则游客没有 token连商品列表都看不了。参数说明tokenStore是一个MapString, String生产环境应该换成 Redis 并设置过期时间。答辩时老师如果问“你的 token 永远不会过期吗”你回答“目前用内存存储重启失效生产环境会替换为 Redis 并设置 7 天过期”就是一次不错的追问应对。管理端接口建议单独写一个AdminAuthInterceptor不能和用户端共用同一个 token 存储。管理员的 token 要有独立的过期机制否则普通用户伪造 token 就能调用后台接口。5. 避坑与常见问题五个最让人翻车的点这一章是我跑这类毕设项目时的血泪经验汇总每一条都是真实发生过的。按“现象→原因→解决”的顺序写你照着排查能省半天时间。5.1 现象微信开发者工具请求后端接口报“不在以下合法域名列表中”原因微信小程序出于安全考虑要求所有wx.request的域名必须是 HTTPS 且在后台配置过。本地开发时后端是http://127.0.0.1:8080肯定不满足。解决开发阶段在微信开发者工具右上角“详情→本地设置”勾选“不校验合法域名”。真机预览时没有这个选项要么把后端部署到带 HTTPS 的服务器并配置域名要么用内网穿透。注意勾选后要重新编译。如果依然报错检查BASE_URL里是否带了/api后又拼接了/api形成/api/api双前缀。这种玄学问题最费时间建议先打印实际请求的完整 URL。5.2 现象数据库导入后中文乱码商品名称变成“???”原因导入 SQL 脚本时客户端连接字符集不是 UTF-8或者脚本本身的编码是 GBK。最典型的是用命令行导入时没加--default-character-setutf8mb4。解决重新导入并显式指定字符集mysql -uroot -p --default-character-setutf8mb4 flower_shop.sql如果已经导入了可以改表字符集ALTER TABLE flower CONVERT TO CHARACTER SET utf8mb4;同时检查application.yml里的连接串是否有characterEncodingutf8并确认 MySQL 的my.cnf里[client]和[mysqld]都设置了default-character-setutf8mb4。改完重启后端重新查一遍数据才有意义。这里多说一句你看到源码里中文正常不代表你本地复制过来也正常编码问题要最先查。5.3 现象商品图片上传后小程序端显示不出来原因图片路径存的是绝对路径比如D:\upload\1.jpg或者存的是没有带域名的相对路径但后端配置了虚拟路径映射。前端拿到\upload\1.jpg拼上BASE_URL后变成http://127.0.0.1:8080/api/upload/1.jpg而后端静态资源映射路径是/upload/**映射到磁盘目录接口前缀/api把路径吃掉了。解决第一后端存图片时只存相对路径/upload/xxx.jpg不要存绝对路径。第二静态资源映射配置要确认拦截路径。Spring Boot 中常见做法Configuration public class WebConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceLocations(file:D:/upload/); } }这里的file:D:/upload/是图片在服务器本地的物理路径注意结尾要有/。如果你把图片放在项目目录下推荐用file:./upload/这种相对写法避免部署到别的机器还要改路径。前端拼图片地址时我一般会让后端直接返回完整 URL也就是在序列化商品对象时把image字段加工成http://ip:8080/upload/...。源码里如果没这么做前端拼的时候要小心双斜杠问题。5.4 现象并发下单时库存变成负数订单和库存对不上原因下单逻辑是“查询库存→判断充足→UPDATE库存→插入订单”中间没有事务和锁。两个人同时买同一款鲜花都读到库存 5各自减 1UPDATE 最终是 4但实际上应该卖出 2 单。更糟的是如果订单插入失败库存已经减了但订单没生成数据不一致。解决在 Service 方法上加Transactional并且用乐观锁或悲观锁控制库存更新。最简单的做法是 UPDATE 语句里加上库存条件UPDATE flower SET stock stock - 1 WHERE id ? AND stock 0如果影响行数为 0说明库存不足直接抛业务异常回滚订单。同时Transactional保证扣库存和插订单在同一个事务里任何一步失败都回滚。代码示意Transactional(rollbackFor Exception.class) public Order submitOrder(SubmitRequest req) { // 1. 扣库存受影响行数为0则抛异常 int rows flowerMapper.deductStock(req.getFlowerId(), req.getQuantity()); if (rows 0) { throw new BusinessException(库存不足); } // 2. 创建订单主表和明细表 // 3. 返回订单对象 }参数说明rollbackFor Exception.class很关键只写Transactional默认只回滚 RuntimeExceptionchecked exception比如IOException会导致库存扣了但订单没建这就是经典的血泪坑。5.5 现象个人版小程序获取手机号按钮功能点击无效原因button open-typegetPhoneNumber这个能力需要小程序主体是企业或个体工商户个人主体的小程序无法调用点击按钮会收到getPhoneNumber:fail no permission或类似错误。很多简历上写了“获取用户手机号”但用个人 AppID 演示时直接翻车。解决如果只是做毕设演示建议把手机号获取改成可编辑的输入框让用户手动填接口里校验 11 位格式。或者申请一个企业主体小程序学生可借用实验室、导师公司的资质才能完整走通。答辩时把这一点提前说明个人开发者无法使用该接口所以才退化为手动输入这是合规做法不是偷懒。如果源码里已经使用了getPhoneNumber你拿到后不要硬调先把该功能隐藏避免现场爆红。6. 改造成你自己的毕设换主题、加状态机与交付习惯前面几章解决了“跑起来”和“避坑”最后一章讲怎么把它变成“你的项目”。毕竟答辩时老师会问你做了什么如果你回答“把鲜花换成了蛋糕”那至少说明你理解了项目的可维护性。6.1 替换商品分类、图片与文案的最快路径打开数据库category表里把“鲜花”“绿植”改成你的主题分类比如“蛋糕”“甜品”“烘焙工具”。flower表里的商品名称、图片 URL、价格、库存都改成你自己的数据。图片资源直接替换upload目录下的文件保持原文件名映射最省事。小程序端的页面文案在pages目录下的.wxml文件里搜索“鲜花”关键字全局替换。改完这几处项目主题就变了前后端逻辑一行不用动。建议做一张“替换清单”表格把需要改的位置列出来相当于一份自定义文档。答辩时拿出这个比只会说“我改了图片”要有说服力得多。6.2 给毕设答辩加分的验证点订单状态机原项目订单状态可能只是简单的下拉框选择。你可以把它升级成状态机待付款→待发货→已发货→已完成以及取消、退款两个分支。后端用一张状态枚举表维护前端根据状态显示按钮。实现并不难但在代码里体现“状态流转”的概念能让评委觉得你有工程意识。public enum OrderStatusEnum { WAIT_PAY(0, 待付款), WAIT_SHIP(1, 待发货), SHIPPED(2, 已发货), FINISHED(3, 已完成), CANCELLED(4, 已取消); // 构造方法、getter... }在order表增加一个字段或者直接复用原 status 字段把 update 操作封装成changeStatus(orderId, fromStatus, toStatus)如果当前状态不是fromStatus就抛异常。用这套逻辑把之前简单 update 的代码替换掉项目深度至少提升一个档次。6.3 动手前先备份改完每个点都要回归最后说一个我自己的习惯。每次拿到新的毕设源码第一件事不是双击运行而是先看数据库脚本和配置文件再启动项目。任何一次改动之前我都会先备份一份原始代码哪怕只是改一个字段名。从那以后我每次跑这类项目都会把“后端启动→小程序编译→下单流程”强制走一遍确认没有回归问题再继续下一步。希望帮到你。本文还有配套的精品资源点击获取