简介这是一套面向计算机专业本科生的高分毕业设计级微信小程序水果电商系统适用于毕设开发、课程设计与期末大作业实战。系统基于JavaSpringBoot构建后端服务Vue实现管理后台界面微信小程序提供用户端MySQL 5.7支撑核心数据存储技术栈成熟、文档完整、开箱即用。资源包共1212个文件涵盖101个Java后端逻辑文件、127个Vue组件、243个JS交互脚本、51个WXML页面结构及57个WXSS样式文件辅以SQL建库脚本、Navicat数据库配置说明及多套CSS/图标资源整体38.87MB结构清晰、模块解耦明确。已有163人学习下载适合零基础快速部署调试。读者可直接获取完整可运行源码、全量数据库脚本、前后端分离工程结构、小程序项目配置及配套工具链说明无需二次修改即可完成本地运行与功能验证显著降低毕设落地门槛。1. 为什么这个「微信小程序水果店」毕业设计比你想象中更值得深挖不是所有带“SpringBootVueMySQL”的毕设都配叫高分——真正拉开差距的从来不是堆砌技术名词而是在微信小程序这个封闭生态里把 Java 后端的事务一致性、Vue 前端的路由懒加载与状态管理、MySQL 的库存扣减防超卖、以及微信登录/支付/消息推送这四条线拧成一股绳。我去年帮三个学生重构过类似项目发现90%的“能跑通”版本一压测就崩在库存扣减下单成功但库存没减、一上线就卡在微信手机号授权回调前端拿不到 code、后端验签失败、一打包就报Cannot find module vueVue 打包产物没正确嵌入 SpringBoot 静态资源路径。这不是代码写得丑而是对微信小程序的运行机制、SpringBoot 的资源加载顺序、Vue CLI 的构建生命周期缺乏穿透式理解。如果你正卡在「部署后白屏」「支付回调收不到」「数据库字段改了但前端列表不更新」这些具体问题上这篇笔记就是为你写的——它不讲 SpringBoot 是什么只告诉你怎么让 Vue 的router.push真正跳转到微信小程序的pages/order/detail页面怎么用 MySQL 的SELECT ... FOR UPDATE在高并发下锁住一行库存记录怎么把wx.login()拿到的 code 安全传给 SpringBoot 并完成 session_key 解密。适合正在赶毕设、想拿高分、或刚入职需要快速接手同类项目的 Java/Vue 全栈新人。2. 后端SpringBoot 如何扛住微信小程序的高频请求与事务边界微信小程序的请求模式和传统网页完全不同单页应用SPA但无浏览器缓存控制权、冷启动频繁、用户行为碎片化刷3秒商品页→点立即购买→切后台→5分钟后回来付款。这就要求 SpringBoot 不是简单暴露 REST API而必须在连接复用、事务粒度、异常兜底三处做针对性加固。下面拆解最常翻车的三个模块登录态管理、库存扣减、支付回调。2.1 微信登录态别再用 Session用 Redis JWT 做无状态鉴权微信小程序不支持 CookieHttpSession在分布式部署下失效。常见错误是直接把wx.login()返回的code发给后端后端用appidsecretcode调用微信接口换session_key然后存在内存 Map 里——这根本没法水平扩展且session_key有效期仅 2 小时续期逻辑缺失。✅ 正确做法前端调wx.login()获取code连同encryptedData和iv一起 POST 到/api/auth/login后端用WxMaServicecom.github.binarywang.wxjava校验签名并解密手机号若用户授权生成JWT Token含openid,unionid,expToken 存 Rediskey:token:${jwt}, value:openid, expire: 7天同时返回给前端存入wx.setStorageSync(token, token)// com.example.fruitshop.controller.AuthController.java PostMapping(/login) public ResultLoginResp login(RequestBody LoginReq req) { // 1. 校验 code 有效性调用微信接口 WxMaJsCode2SessionResult session wxMaService.jsCode2Session(req.getCode()); String openid session.getOpenid(); // 2. 若传了 encryptedData解密手机号需用户已授权 String phone null; if (StringUtils.isNotBlank(req.getEncryptedData())) { try { phone wxMaService.getUserService() .getPhoneNoInfo(session.getSessionKey(), req.getEncryptedData(), req.getIv()) .getPhoneNumber(); } catch (WxErrorException e) { log.warn(手机号解密失败openid{}, openid, e); } } // 3. 生成 JWT使用 jjwt-api 0.11.5 String jwt Jwts.builder() .setSubject(openid) .claim(phone, phone) .setExpiration(new Date(System.currentTimeMillis() 7 * 24 * 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS256, your-secret-key-here) .compact(); // 4. 写入 Rediskey: token:xxx, value: openid, TTL7d redisTemplate.opsForValue().set(token: jwt, openid, Duration.ofDays(7)); return Result.success(new LoginResp(jwt, openid, phone)); }参数说明your-secret-key-here必须替换成 32 字节随机密钥可用openssl rand -base64 32生成Redis key 命名带token:前缀便于批量清理JWT 过期时间设为 7 天而非微信session_key的 2 小时因session_key仅用于首次解密后续鉴权靠 JWT。2.2 库存扣减MySQL 行锁 乐观锁双保险防超卖不丢单微信小程序用户习惯“疯狂点击立即购买”极易触发并发下单。单纯UPDATE product SET stock stock - 1 WHERE id ? AND stock 0在高并发下会漏锁——因为 WHERE 条件判断和 UPDATE 执行不是原子的。必须用SELECT ... FOR UPDATE显式加行锁且锁范围要精确到单个商品 ID。✅ 正确流程以OrderService.createOrder()为例开启事务TransactionalSELECT * FROM product WHERE id ? FOR UPDATE—— 锁住该商品行检查stock orderQuantity不满足则抛InsufficientStockExceptionUPDATE product SET stock stock - ? WHERE id ?插入订单主表 订单明细表// com.example.fruitshop.service.OrderService.java Transactional(rollbackFor Exception.class) public Order createOrder(Long productId, Integer quantity, String openid) { // 1. 加行锁注意必须走主键索引否则升级为表锁 Product product productMapper.selectByIdForUpdate(productId); // XML 中写 select ... for update if (product null || product.getStock() quantity) { throw new InsufficientStockException(库存不足商品ID productId); } // 2. 扣减库存此时行锁仍在 int updated productMapper.decreaseStock(productId, quantity); if (updated ! 1) { throw new RuntimeException(库存扣减失败); } // 3. 创建订单关联 openid Order order new Order(); order.setProductId(productId); order.setQuantity(quantity); order.setOpenid(openid); order.setStatus(UNPAID); order.setCreateTime(LocalDateTime.now()); orderMapper.insert(order); return order; }关键细节selectByIdForUpdate对应的 XML 必须显式写FOR UPDATEMyBatis 不自动加decreaseStock的 SQL 是UPDATE product SET stock stock - #{quantity} WHERE id #{id}绝对不要在 SELECT 后 sleep() 或做耗时操作否则锁持有时间过长拖垮吞吐量。2.3 支付回调用幂等性 Key 状态机杜绝重复发货微信支付回调地址notify_url可能被重复推送网络抖动、超时重试若不做幂等处理会导致同一笔订单创建多个发货单。常见错误是只校验out_trade_no是否存在但未考虑“创建订单成功→支付回调→发货→又来一次回调”的场景。✅ 正确方案回调接收接口/api/pay/notify入口加synchronized锁按out_trade_no分桶解析微信回调 XML校验签名WXPayUtil.verifySignature(xmlStr, mchKey)查询订单状态仅当订单为UNPAID时才更新为PAID并触发发货更新后插入幂等日志表pay_notify_log字段out_trade_no,notify_id,status,create_time// com.example.fruitshop.controller.PayController.java PostMapping(value /notify, produces MediaType.TEXT_PLAIN_VALUE) public String handlePayNotify(HttpServletRequest request) { try { // 1. 读取回调XML String xmlStr IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8); // 2. 校验签名必须防止伪造回调 MapString, String notifyMap WXPayUtil.xmlToMap(xmlStr); if (!WXPayUtil.isSignatureValid(notifyMap, your-mch-key-here)) { return xmlreturn_code![CDATA[FAIL]]/return_codereturn_msg![CDATA[签名失败]]/return_msg/xml; } // 3. 幂等校验按 out_trade_no 加锁 String outTradeNo notifyMap.get(out_trade_no); synchronized (outTradeNo.intern()) { // 字符串常量池锁避免新建对象 // 查询订单 Order order orderMapper.selectByOutTradeNo(outTradeNo); if (order null || !UNPAID.equals(order.getStatus())) { return xmlreturn_code![CDATA[SUCCESS]]/return_codereturn_msg![CDATA[OK]]/return_msg/xml; } // 4. 更新订单状态 发货此处可发 MQ 异步处理 order.setStatus(PAID); order.setPayTime(LocalDateTime.now()); orderMapper.updateById(order); // 5. 记录幂等日志 PayNotifyLog log new PayNotifyLog(); log.setOutTradeNo(outTradeNo); log.setNotifyId(notifyMap.get(result_code)); // 微信回调唯一ID log.setStatus(PROCESSED); payNotifyLogMapper.insert(log); } return xmlreturn_code![CDATA[SUCCESS]]/return_codereturn_msg![CDATA[OK]]/return_msg/xml; } catch (Exception e) { log.error(支付回调处理异常, e); return xmlreturn_code![CDATA[FAIL]]/return_codereturn_msg![CDATA[系统错误]]/return_msg/xml; } }参数说明your-mch-key-here是微信商户平台的 API 密钥32位synchronized (outTradeNo.intern())利用字符串常量池实现轻量级分桶锁比 Redis 分布式锁更简单幂等日志表必须建唯一索引UNIQUE KEY uk_out_trade_no (out_trade_no)。3. 前端Vue 如何适配微信小程序的运行约束与性能瓶颈Vue CLI 打包出的 SPA 无法直接在微信小程序环境运行——小程序没有window、document、localStorage且要求所有页面路径在app.json中静态声明。因此真正的“Vue 微信小程序”不是把 Vue 项目扔进去而是用 Vue 语法写小程序逻辑再通过uni-app或mpvue编译成小程序原生代码。本项目采用uni-app国内生态最成熟它允许你用 Vue 语法开发一键编译到微信小程序、H5、App 多端。3.1 项目结构uni-app 的标准目录与微信特有配置uni-app项目根目录下必须有pages.json替代小程序app.json、manifest.json配置 AppID、uni.scss全局样式变量。关键区别在于pages目录下的.vue文件会被编译成小程序的pages/xxx/xxx.wxmlstatic目录资源图片、字体直接映射到小程序static/components下的.vue组件需用import引入不能用import// pages.json核心决定小程序导航栏、页面路径 { pages: [ { path: pages/index/index, style: { navigationBarTitleText: 鲜果直送, enablePullDownRefresh: true, onReachBottomDistance: 50 } }, { path: pages/product/detail, style: { navigationBarTitleText: 商品详情, usingComponents: true } } ], subNVue: [], tabBar: { color: #7A7E83, selectedColor: #3cc51f, borderStyle: black, backgroundColor: #ffffff, list: [ { pagePath: pages/index/index, iconPath: static/tabbar/home.png, selectedIconPath: static/tabbar/home-active.png, text: 首页 } ] } }注意tabBar的pagePath必须和pages数组中的path完全一致iconPath图片必须放在static目录下且尺寸为 81px×81pxusingComponents: true表示启用自定义组件如购物车悬浮按钮。3.2 微信能力调用登录、获取手机号、支付三步闭环微信小程序的 API 调用必须在真机调试模拟器无效。uni-app封装了uni.login、uni.getUserInfo已废弃、uni.getPhoneNumber需用户主动授权等 API但必须配合后端完成完整链路。✅ 完整流程以“一键授权手机号”为例前端调uni.getPhoneNumber({ withCredentials: true })→ 用户点击授权按钮微信返回code非wx.login()的 code和encryptedData前端将code和encryptedData发给后端/api/auth/bind-phone接口后端用WxMaService.getUserService().getPhoneNoInfo()解密// pages/user/bind-phone.vue export default { methods: { async bindPhone() { try { // 1. 调用微信获取手机号需 button 绑定 open-typegetPhoneNumber const res await uni.getPhoneNumber({ withCredentials: true // 必须为 true否则 encryptedData 为空 }) const { code, encryptedData, iv } res.detail // 2. 发送给后端绑定 const { data } await this.$http.post(/api/auth/bind-phone, { code, encryptedData, iv }) uni.showToast({ title: 绑定成功, icon: success }) this.$store.commit(SET_PHONE, data.phone) // Vuex 存储 } catch (err) { console.error(获取手机号失败, err) uni.showToast({ title: 授权失败请重试, icon: none }) } } } }血泪经验withCredentials: true是关键漏写则encryptedData为空字符串uni.getPhoneNumber必须由button open-typegetPhoneNumber触发不能在onLoad自动调用否则微信拦截解密失败常见原因是session_key过期需重新wx.login()换新 key。3.3 性能优化懒加载、分包、骨架屏让首屏快于 1 秒微信小程序包大小限制 2MB主包超过需分包。uni-app默认不开启分包导致pages/product/list.vue和pages/order/detail.vue全部打进主包首屏加载慢。✅ 三步优化路由懒加载pages.json中pages数组的path改为异步导入分包配置在pages.json的subPackages字段声明分包路径骨架屏pages/index/index.vue用v-if/v-else切换 loading 状态// pages.json分包配置 { subPackages: [ { root: subPackages/product, pages: [ { path: list/list, style: { navigationBarTitleText: 商品列表 } } ] } ] }!-- pages/index/index.vue -- template view classindex !-- 骨架屏 -- view v-if!loaded classskeleton view classskeleton-item v-fori in 3 :keyi/view /view !-- 真实内容 -- view v-else swiper :indicator-dotstrue :autoplaytrue swiper-item v-foritem in banners :keyitem.id image :srcitem.url modeaspectFill / /swiper-item /swiper view classgoods-list navigator v-forg in goods :keyg.id :url/subPackages/product/list/list?category g.category image :srcg.cover / text{{ g.name }}/text /navigator /view /view /view /template script export default { data() { return { loaded: false, banners: [], goods: [] } }, onLoad() { // 1. 先展示骨架屏 this.loaded false // 2. 请求数据uni.request 自动带上 cookie不需手动加 token uni.request({ url: https://api.fruitshop.com/api/product/banner, header: { Authorization: Bearer uni.getStorageSync(token) }, success: (res) { this.banners res.data this.goods res.data.goods this.loaded true // 数据加载完才显示真实内容 } }) } } /script提示subPackages下的页面 URL 必须带前缀/subPackages/xxx/uni.request的header必须手动传Authorization因为小程序不自动携带 Cookie骨架屏 CSS 用background: linear-gradient(...)模拟灰色块比v-show更节省渲染。4. 数据库MySQL 5.7 如何设计才能支撑水果店的实时库存与订单查询水果店业务看似简单但数据库设计稍有不慎就会成为性能瓶颈凌晨补货时批量更新库存、大促时订单表每秒写入上百条、用户搜索“苹果”要秒出结果。本项目采用 MySQL 5.7兼容性最好云服务器默认版本不追求最新特性而聚焦索引、分区、字符集三大落地细节。4.1 表结构设计用BIGINT主键、TINYINT状态、JSON扩展字段微信小程序用户量不大毕设场景但表结构必须预留扩展性。常见错误是用VARCHAR(255)存商品描述浪费空间、用INT存订单状态未来加状态要改表、用TEXT存商品规格无法建索引。✅ 推荐结构精简版表名字段类型说明productidBIGINT UNSIGNED PK避免负数支持 2^64 行nameVARCHAR(100)商品名加索引stockINT DEFAULT 0库存禁止 NULLpriceDECIMAL(10,2)精确到分不用 FLOATspecsJSON规格如{weight:500g,origin:山东}MySQL 5.7 支持orderidBIGINT UNSIGNED PK同上out_trade_noVARCHAR(64) UNIQUE微信支付单号建唯一索引openidVARCHAR(64)用户标识建索引statusTINYINT DEFAULT 00UNPAID, 1PAID, 2SHIPPED...查状态快create_timeDATETIME建联合索引(status, create_time)-- 创建 product 表utf8mb4 支持 emoji如 CREATE TABLE product ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, name varchar(100) NOT NULL COMMENT 商品名称, stock int(11) NOT NULL DEFAULT 0 COMMENT 库存, price decimal(10,2) NOT NULL COMMENT 价格, specs json DEFAULT NULL COMMENT 规格 JSON, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_name (name) USING BTREE, KEY idx_stock (stock) USING BTREE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品表; -- 创建 order 表重点status create_time 联合索引 CREATE TABLE order ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, out_trade_no varchar(64) NOT NULL UNIQUE COMMENT 微信支付单号, openid varchar(64) NOT NULL COMMENT 用户openid, status tinyint(4) NOT NULL DEFAULT 0 COMMENT 订单状态0-待支付1-已支付2-已发货, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_status_ctime (status,create_time) USING BTREE, KEY idx_openid (openid) USING BTREE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单表;参数说明utf8mb4是必须的否则微信昵称里的 emoji如 ‍会乱码TINYINT比VARCHAR(20)查状态快 10 倍idx_status_ctime索引让“查今天待发货订单”WHERE status1 AND create_time 2024-06-01走索引扫描而非全表扫描。4.2 索引优化为什么WHERE name LIKE %苹果%永远走不了索引微信小程序搜索框输入“苹果”后端执行SELECT * FROM product WHERE name LIKE %苹果%这是典型的索引失效场景——%在左边B树索引无法定位。✅ 正确方案方案1推荐用LIKE 苹果%前缀匹配要求用户输入后自动补全如输入“苹”→提示“苹果、苹果汁、苹果醋”方案2建全文索引FULLTEXT(name)但 MySQL 5.7 对中文支持差需配合ngram插件方案3毕设够用加CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci让LIKE对中文更友好-- 为 name 字段加前缀索引节省空间 ALTER TABLE product ADD KEY idx_name_prefix (name(20)); -- 查询时强制用前缀用户输入“苹果”后端拼成 苹果% SELECT * FROM product WHERE name LIKE 苹果%;避坑name字段长度 100但索引只取前 20 字符name(20)足够覆盖大部分商品名COLLATE utf8mb4_unicode_ci让ORDER BY name按中文拼音排序而非乱码。4.3 数据安全备份脚本 敏感字段加密毕业答辩不露怯毕设演示时数据库里若出现admin账号密码明文、用户手机号裸奔答辩老师一眼看出安全漏洞。MySQL 本身不提供字段级加密需在应用层处理。✅ 实施步骤密码字段用BCryptPasswordEncoderSpring Security加密永远不存明文手机号字段用 AES-128-CBC 加密密钥存application.yml绝不硬编码每日备份Linux 下写 crontab用mysqldump导出 SQL# application.yml密钥单独配置 fruitshop: crypto: phone-key: a1b2c3d4e5f67890 # 16字节 AES 密钥 phone-iv: 0123456789abcdef # 16字节 IV// 工具类 PhoneCrypto.java Component public class PhoneCrypto { private final String key; private final String iv; public PhoneCrypto(Value(${fruitshop.crypto.phone-key}) String key, Value(${fruitshop.crypto.phone-iv}) String iv) { this.key key; this.iv iv; } public String encrypt(String phone) { try { Cipher cipher Cipher.getInstance(AES/CBC/PKCS5Padding); SecretKeySpec secretKey new SecretKeySpec(key.getBytes(), AES); IvParameterSpec ivSpec new IvParameterSpec(iv.getBytes()); cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec); byte[] encrypted cipher.doFinal(phone.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(encrypted); } catch (Exception e) { throw new RuntimeException(手机号加密失败, e); } } public String decrypt(String encryptedPhone) { try { Cipher cipher Cipher.getInstance(AES/CBC/PKCS5Padding); SecretKeySpec secretKey new SecretKeySpec(key.getBytes(), AES); IvParameterSpec ivSpec new IvParameterSpec(iv.getBytes()); cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec); byte[] decoded Base64.getDecoder().decode(encryptedPhone); byte[] decrypted cipher.doFinal(decoded); return new String(decrypted, StandardCharsets.UTF_8); } catch (Exception e) { throw new RuntimeException(手机号解密失败, e); } } }注意key和iv必须 16 字节可用openssl rand -hex 16生成加密后存入数据库的phone_encrypted字段VARCHAR(255)解密在UserMapper.selectById()后手动调用不在 SQL 层解密避免慢查询。5. 避坑指南95% 的毕设翻车都发生在这 5 个地方别等答辩前夜才发现问题。以下是我帮学生 debug 时记录的真实踩坑案例按现象→原因→解决三步写清每个都能省你 3 小时。5.1 现象微信小程序真机调试白屏控制台报Cannot find module vue原因uni-app项目npm run build:mp-weixin打包后dist/build/mp-weixin目录被直接拖进微信开发者工具但node_modules未复制且project.config.json中miniprogramRoot指向错误路径。解决确保project.config.json的miniprogramRoot为./dist/build/mp-weixin/末尾斜杠不能少删除dist/build/mp-weixin/node_modulesuni-app 打包已内置依赖此目录是冗余的微信开发者工具 → 详情 → 本地设置 → 取消勾选“使用 npm 模块”uni-app 不走 npm5.2 现象SpringBoot 启动报错Caused by: java.lang.ClassNotFoundException: javax.xml.bind.DatatypeConverter原因JDK 11 移除了 JAXBJava EE 模块而wxjava依赖javax.xml.bind。解决在pom.xml中添加 JAXB 依赖dependency groupIdjavax.xml.bind/groupId artifactIdjaxb-api/artifactId version2.3.1/version /dependency dependency groupIdruntime/groupId artifactIdcom.sun.xml.bind/artifactId version2.3.1/version /dependency5.3 现象MySQL 插入中文乱码日志显示????原因MySQL 服务端、数据库、表、连接四层字符集不统一常见是my.cnf中character-set-serverutf8错误应为utf8mb4。解决修改/etc/my.cnf[client] default-character-set utf8mb4 [mysql] default-character-set utf8mb4 [mysqld] character-set-server utf8mb4 collation-server utf8mb4_unicode_ci init_connectSET NAMES utf8mb4 skip-character-set-client-handshake 1重启 MySQLsudo systemctl restart mysqld重建数据库CREATE DATABASE fruitshop CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;5.4 现象Vue 页面跳转后微信小程序顶部导航栏高度异常文字被遮挡原因pages.json中navigationStyle: custom时未在页面.vue中手动设置padding-top: var(--status-bar-height)。解决在pages/index/index.vue的style中加.content { padding-top: var(--status-bar-height); /* 其他样式 */ }确保pages.json的style中navigationStyle为custom且navigationBarTextStyle设为black否则状态栏文字看不见5.5 现象支付回调成功但订单状态没变pay_notify_log表无记录原因微信回调地址notify_url配置为http://localhost:8080/api/pay/notify内网地址微信服务器无法访问。解决使用natapp.cn或cpolar.com映射内网端口免费隧道微信商户平台 → 产品中心 → 开发配置 →notify_url填https://xxx.ngrok.io/api/pay/notify务必关闭 SpringBoot 的 HTTPS 重定向server.http.port8080server.ssl.enabledfalse否则隧道转发失败6. 高分技巧用 Docker 一键部署 自动化测试让答辩老师眼前一亮毕设答辩时老师最想看到的不是“我写了 5000 行代码”而是“我解决了真实工程问题”。部署和测试环节最容易拉开差距——别人还在手动导 SQL、改配置你已经docker-compose up -d一键拉起全套环境别人靠截图证明功能你用 Jest Cypress 跑通 20 个核心用例。这才是高分的关键。6.1 Docker 化部署3 个文件搞定 MySQL SpringBoot Nginx微信小程序后端必须有公网域名微信支付要求本地 IP 不行。Docker 能屏蔽环境差异让老师在自己电脑上git clone docker-compose up即可运行。核心是docker-compose.yml、Dockerfile、.env三文件。# docker-compose.yml version: 3.8 services: db: image: mysql:5.7 container_name: fruitshop-mysql environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: fruitshop MYSQL_USER: app MYSQL_PASSWORD: app123 ports: - 3306:3306 volumes: - ./mysql-data:/var/lib/mysql - ./init.sql:/docker-entrypoint-initdb.d/init.sql backend: build: ./backend container_name: fruitshop-backend ports: - 8080:8080 environment: - SPRING_PROFILES_ACTIVEdocker - SPRING_DATASOURCE_URLjdbc:mysql://db:3306/fruitshop?useSSLfalseserverTimezoneAsia/Shanghai - SPRING_DATASOURCE_USERNAMEapp - SPRING_DATASOURCE_PASSWORDapp123 depends_on: - db nginx: image: nginx:alpine container_name: fruitshop-nginx ports: - 80:80 volumes: - ./nginx.conf:/etc/nginx/nginx.conf - ./dist/build/mp-weixin:/usr/share/nginx/html# backend/Dockerfile FROM openjdk:11-jre-slim VOLUME /tmp ARG JAR_FILEtarget/fruitshop-0.0.1-SNAPSHOT.jar COPY ${JAR_FILE} app.jar ENTRYPOINT [java,-Djava.security.egdfile:/dev/./urandom,-jar,/app.jar]# .env敏感信息外置 MYSQL_ROOT_PASSWORDroot123 SPRING_DATASOURCE_PASSWORDapp123验证方法docker-compose up -d后访问 http://localhost/swagger本文还有配套的精品资源点击获取