news 2026/10/22 23:41:55

微信小程序毕业设计仓储管理系统:Java后端与MySQL联调实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
微信小程序毕业设计仓储管理系统:Java后端与MySQL联调实战指南

简介:面向毕业设计或课程设计的微信小程序仓储管理系统源码包,基于Java、小程序与MySQL实现,由单一管理员维护,覆盖供应商、员工、商品分类、商品入库出库、货物采购、在线沟通等管理模块,适合搭建可演示项目的计算机专业学生。压缩包共1373个文件,大小约32.13MB;涵盖后端Java代码、前端页面组件、小程序页面、界面图片和数据库脚本,其中后端代码约147个文件,前端页面组件约160个,小程序页面相关118个,图片资源370余张,结构清晰,可直接导入开发工具运行。目前已有97人学习/下载,压缩包内含完整前后端源码、数据库文件和启动构建辅助脚本,可帮助读者配置环境完成部署,并在其基础上二次扩展。项目按供应商、商品、采购等模块划分,适合逐段阅读后端接口与小程序交互逻辑,用于毕业设计论文、答辩演示或课程实训参考。

1. 这个 zip 不只是源码:Java 后端 + 微信小程序 + MySQL 的仓储管理毕设该怎么用

先说一个多数人拿到「【微信小程序毕业设计】仓储管理系统源码(java+小程序+mysql+LW).zip」之后的第一反应:把压缩包解开,往 IDEA 里一扔,然后对着报错发呆。这套东西看起来是「一个项目」,拆开看其实是「一套交付件」——Java 后端、微信小程序前端、MySQL 数据库脚本,以及配套的 LW 说明文档。它能解决的问题很具体:让一个刚接触全栈的人在一两周内跑通仓库管理的主链路(商品入库、出库、库存查询),并把这个过程整理成能写进论文和答辩 PPT 的内容。适合两类人:正在做毕业设计的学生,以及想快速搭一套内部仓储演示系统来练手的前端工程师。这篇笔记讲的是把压缩包变成可运行项目的全过程,包括环境选型、表结构设计、接口联调,以及那些只有真正跑过才会翻车的细节。

2. 把环境盘明白:JDK、MySQL 与微信开发者工具的最小准备步骤

2.1 前后端分离怎么理解:Java 接口只认 HTTP,小程序只认 JSON

仓储管理系统这种毕设项目,最常见的组织方式是前后端分离:Java 后端用 Spring Boot(或 SSM 框架)暴露一组 HTTP 接口,小程序端通过 wx.request 向这些接口要数据。你打开压缩包后大概率会看到两个工程目录,一个是 Java 后端工程,一个是小程序工程目录;小程序目录里会有 pages、utils、app.js 这些文件,后端目录里则是一个标准的 Maven 工程。两者之间没有直接调用关系,只通过 JSON 字符串交流,所以第一步不是看代码,而是先确认自己机器上有哪些环境。

我一般会先跑一遍环境检查命令:java -version、mvn -v、mysql --version。JDK 优先用 8 或 11,对应的是 Spring Boot 2.x 工程;如果你解压出来的后端代码里 spring-boot-starter-parent 版本号是 3.x,那就得用 JDK 17。这是最容易踩的第一个坑——代码明明没什么问题,启动就报 UnsupportedClassVersionError,十有八九是 JDK 版本偏低。MySQL 用 5.7 或 8.0 都可以,但要注意 8.0 的驱动和密码校验方式跟 5.7 不一样,连接串里的驱动类名也要跟着改。

小程序端需要微信开发者工具,打开时选择「导入项目」,然后定位到小程序目录即可。这里有一个经验:毕设里的小程序工程很多是用原生小程序写的,少数用 uniapp 或 Taro。如果你看到的是 uniapp 工程,目录里会有 src 文件夹和一个 manifest.json;如果是原生小程序,直接有 app.json 和 pages 文件夹。两种项目导入方式不同——uniapp 要用 HBuilderX 打包成小程序再导入,原生工程才能直接导入开发者工具。解开压缩包后先看根目录结构,别急着导入。

2.2 数据库导入:通过命令行与 Navicat 两种方式跑 SQL 脚本

压缩包里一般会带一个 .sql 文件,名字可能是 warehouse.sql、db_storage.sql 之类。这就是整个系统的数据基础。把它导入 MySQL 的方法有两种,我建议先学会命令行方式,因为答辩时老师可能会问,而且命令行方式最不容易出错。

在 MySQL 命令行中执行导入操作,命令是:

mysql -u root -p < warehouse.sql

执行后会提示输入密码,输完回车,如果屏幕上没有报错,说明脚本执行成功。这条命令的原理是把 warehouse.sql 里的建库、建表、插入初始数据的语句,全部交给 MySQL 客户端一条条执行。需要留意的是,mysql 命令后面跟的不是数据库名,而是直接接 SQL 文件路径;如果 scripts 文件里本身包含了 CREATE DATABASE 语句,导入后会自动建库,不需要你提前建库。

用 Navicat 导入则是:连接 MySQL → 右键「运行 SQL 文件」→ 选择文件 → 开始。两种方式效果一样,只是命令行方式更容易排查编码问题。导入完成后,执行 SHOW DATABASES;查看是否存在新库,然后用 USE 库名; SHOW TABLES; 确认表建好了。如果表不全或者报错,大概率是 SQL 文件里带了中文注释且文件编码不是 UTF-8,重新用记事本另存为 UTF-8 编码再导入。这一步完成后,后端工程里的数据库账号密码才能跟实际环境对上。

2.3 Spring Boot 后端的最小启动命令与第一个健康检查接口

环境齐了以后,后端工程要改的只有一个核心文件:application.yml(或者 application.properties)。里面的数据源配置是启动的钥匙:

server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/warehouse_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true

这里要解释两个参数:serverTimezone=Asia/Shanghai 解决 MySQL 8.0 下时间类型差 8 小时的问题,缺失的话 LocalDateTime 查出来总是慢 8 小时;map-underscore-to-camel-case 让数据库里的下划线字段自动映射成 Java 的驼峰属性,没有它你会发现实体对象查出来全是 null。这就是毕设项目里最常见的黑匣子之一——代码一条 SQL 都没问题,数据却出不来,其实只是配置没打开。

启动后端有两种方式。在 IDEA 里直接运行启动类(带 @SpringBootApplication 的那个类),或者在项目根目录执行:

mvn spring-boot:run

等日志出现 Started Application 后,在浏览器访问http://localhost:8080。如果你看不到页面,不要慌——先访问一个具体的接口,比如http://localhost:8080/api/health,大多数毕设项目会有一个健康检查接口返回 JSON。如果连接口都 404,先看控制台有没有报错;最常见的报错是数据库连不上,其次是端口被占用。把 application.yml 里的账号密码跟 MySQL 实际账号对一遍,再启动基本就能过。

3. 表结构先立住:仓储库存的表设计与一条流水查询

3.1 从需求到建表:warehouse、product、stock、stock_log、inbound、outbound 六张表

仓储管理系统的业务其实很朴素:有几个仓库,仓库里放商品,商品有库存,有入库单和出库单。对应到表结构,我一般建议先画一遍关系再建表:仓库表(warehouse)和商品表(product)相对独立,库存表(stock)是两者多对多关系的具体化,而所有数量的增减都要落到流水表(stock_log)里。

一个能应付大多数毕设场景的建表脚本如下:

CREATE TABLE warehouse ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(64) NOT NULL, address VARCHAR(128), manager VARCHAR(32), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE product ( id INT PRIMARY KEY AUTO_INCREMENT, code VARCHAR(64) NOT NULL UNIQUE, name VARCHAR(128) NOT NULL, spec VARCHAR(64), unit VARCHAR(16), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE stock ( id INT PRIMARY KEY AUTO_INCREMENT, warehouse_id INT NOT NULL, product_id INT NOT NULL, quantity INT NOT NULL DEFAULT 0, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_wh_product (warehouse_id, product_id), KEY idx_product (product_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE stock_log ( id INT PRIMARY KEY AUTO_INCREMENT, warehouse_id INT NOT NULL, product_id INT NOT NULL, change_type TINYINT NOT NULL COMMENT '1入库 2出库 3盘点', before_qty INT NOT NULL, change_qty INT NOT NULL, after_qty INT NOT NULL, operator VARCHAR(32), remark VARCHAR(255), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_wh_time (warehouse_id, create_time) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

库存表上的唯一索引 uk_wh_product 是这张表的设计核心,它保证了同一个仓库同一个商品只能有一行记录。出库单、入库单在真实系统里还要拆主表和明细表,但毕设里只需要一张表加一个订单号字段就够用了。注意所有表都指定了 utf8mb4 而不是 utf8——前者才能正常存中文和表情符号,这是 MySQL 建表级最常见的问题。写代码时不用严格遵循这个脚本的命名,但表关系要跟这个一致。

3.2 为什么单独建库存流水表:库存数量不能硬改,要做加法校验

很多人第一次写仓储系统时,会直接把「修改库存」操作写成 UPDATE stock SET quantity = 新值。这在演示时能跑通,但答辩压力一上来就会被问住:如果系统崩溃了,你怎么知道刚才扣的库存合不合理?如果两个人同时操作一个商品,后写的人会不会覆盖先写的人的数据?这些问题的标准答案都指向同一件事——库存数量不允许直接覆盖,只允许通过流水表推导。

正确做法是:每次出库或入库时,先查出当前库存,计算变化后的值,然后同时插入一条流水记录和更新库存记录。两个操作必须放在同一个事务里,保证要么同时成功,要么同时回滚。放在 Spring Boot 里就是加一个 @Transactional 注解,把库存扣减和流水插入绑在一起。这不是复杂的设计,但它是仓储系统和普通的增删改查系统拉开差距的地方。

我在给毕设同学评审的时候经常问一个追加题:如果库存不够出库怎么办?答案是业务层要先做判断,数量不足直接抛异常回滚。很多人把判断留在前端,后端接口直接减数量,这就是明显的扣分点。库存这种核心数据,校验一定在后端做,前端提示只是用户体验,不是安全边界。这一点理解透了,答辩时关于并发和数据一致性的问题基本都能接住。

3.3 一条能应付提问的查询:按仓库查库存怎么把关联条件写全

库存查询是小程序端最常用的接口,也是面试和答辩时最容易现场写的一条 SQL。它的难点不在 JOIN,而在「查到结果后要能回答为什么这么写」。

SELECT p.code AS product_code, p.name AS product_name, p.spec, p.unit, s.quantity, w.name AS warehouse_name FROM stock s JOIN product p ON s.product_id = p.id JOIN warehouse w ON s.warehouse_id = w.id WHERE s.warehouse_id = #{warehouseId} ORDER BY p.code;

这条 SQL 的逻辑是:库存表 stock 是中间表,商品表和仓库表是维度表,通过 JOIN 拿到商品的编码名称和仓库名称。WHERE 条件里的 warehouseId 是动态传入的,MyBatis 里对应一个 @Param 注解或 XML 里的 #{warehouseId}。ORDER BY p.code 让商品排序稳定,避免同一次查询翻页时顺序乱跳。

这里要记住一个优化习惯:查询条件里尽量用 stock 表上的索引字段做过滤,因为 WHERE 后面的 warehouse_id 是库存表联合索引的第一列,数据库能直接走索引定位到少量行,再 JOIN 商品和仓库两张小表成本很低。很多新手会把筛选条件写在 WHERE 的 JOIN 表字段上,或者在大表上做函数运算,导致索引失效。这条 SQL 如果答得好,基本等于告诉老师你理解索引的基本使用场景。

4. 小程序端从登录到请求封装:把仓储页面接上 Java 接口

4.1 手机号登录的完整链路:个人主体做不了 getPhoneNumber 时的替代方案

微信小程序的登录是仓储系统里躲不开的第一个硬骨头,也是热搜词「微信小程序登录获取手机号」背后最常见的问题。现在小程序获取手机号的官方接口 getPhoneNumber 要求账号已完成微信认证,而且每次调用都涉及费用和审核,个人主体小程序的同学们根本没有这个权限。所以毕设里的登录功能,常见做法是拆成两层:第一层用 wx.login 换取 code,第二层把 code 传到后端,由后端调用微信接口换取 openid,再用 openid 作为用户标识。

小程序端核心代码:

// pages/login/login.js login() { wx.login({ success: async (res) => { if (res.code) { const loginRes = await request({ url: '/api/auth/login', method: 'POST', data: { code: res.code } }); wx.setStorageSync('token', loginRes.data.token); wx.setStorageSync('userInfo', loginRes.data.userInfo); wx.switchTab({ url: '/pages/index/index' }); } } }); }

这段代码的逻辑是:wx.login 获取一个临时凭据 code,这个 code 的有效期很短,只能使用一次。小程序把 code 发给自己的后端,后端再用 code 去微信服务器换 openid 和 session_key。为什么不让小程序直接拿 openid?因为 code 换 openid 的过程需要一个 AppSecret,这个密钥不能放在小程序代码里,否则任何人都能反编译拿到。后端拿到 openid 后,查数据库里有这个用户就登录,没有就自动注册,然后签发一个自己的 token 返回给小程序。

参数说明:request 是小程序端封装好的请求方法,url 里的 /api/auth/login 是后端 Controller 的映射地址;token 存进 Storage 后,后续所有接口请求都会在 header 里带上 Authorization。这个方法在毕设和个人演示项目里完全够用,也比只写一个「游客登录」看起来完整得多。如果你确实需要「手机号登录」的功能来应对老师的要求,可以在登录页单独放一个按钮调用 getPhoneNumber,后端用 code2Session 的 openid 匹配用户——但要注意个人主体小程序真机上是拿不到真实手机号的,这就是现实约束。

4.2 顶部导航栏高度适配:自定义导航栏不能被状态栏挡住

仓储管理小程序一般会做底部 TabBar(首页、入库、出库、我的),顶部用自定义导航栏来放标题和搜索框。很多同学用自定义导航栏后,发现标题跑到手机状态栏里去了,或者在不同机型上位置不一样。这就是热搜词「微信小程序顶部导航栏高度」对应的具体问题,也是小程序端少数必须适配原生机型的难点。

微信小程序的自定义导航栏,核心代码在页面的 json 配置文件里:

{ "navigationStyle": "custom", "usingComponents": {} }

然后在页面 onLoad 里读取胶囊按钮的位置:

// utils/nav.js function getNavBarInfo() { const windowInfo = wx.getWindowInfo(); const menuRect = wx.getMenuButtonBoundingClientRect(); const statusBarHeight = windowInfo.statusBarHeight; const navBarHeight = (menuRect.top - statusBarHeight) * 2 + menuRect.height; return { statusBarHeight: statusBarHeight, navBarHeight: navBarHeight, menuRect: menuRect }; }

这里的原理是:菜单胶囊按钮(右上角那个圆圈)垂直方向上永远居中于导航栏中心,所以用胶囊的 top 减去状态栏高度,就得到胶囊距离状态栏底部的距离,这个距离乘 2 再加上胶囊高度,就等于整个自定义导航栏的高度。不同手机的菜单按钮位置不完全一样,但每台手机算出来的结果都能适配自己。这个计算方式比写死一个 44px 或 48px 靠谱得多。

实际使用时,导航栏容器结构是这样:外层 view 的高度等于 statusBarHeight + navBarHeight,顶部 padding 留出 statusBarHeight,内部再放一个高为 navBarHeight 的标题容器。这样标题就会垂直居中且不被状态栏压住。很多毕设项目在开发者工具里看着正常,一上真机标题就跑偏了,就是少了这一步动态计算。

4.3 统一请求封装:把 wx.request 变成带 token、带错误拦截的 promise

小程序端第二个必做的封装是请求方法。如果每个页面都直接写 wx.request,一个项目里就会出现几十处重复代码,而且 token 过期、网络异常的处理方式各不相同,答辩时被问到「你的错误处理在哪里」,回答会非常含糊。我一般会在 utils/request.js 里统一封装:

// utils/request.js const BASE_URL = 'http://127.0.0.1:8080/api'; 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.statusCode === 200 && res.data.code === 0) { resolve(res.data.data); } else if (res.statusCode === 401) { wx.removeStorageSync('token'); wx.navigateTo({ url: '/pages/login/login' }); reject(new Error('登录已过期')); } else { reject(new Error(res.data.msg || '请求失败')); } }, fail: (err) => { reject(new Error('网络异常,请检查后端服务')); } }); }); } module.exports = request;

这段封装的三个关键点后面答辩时要能讲清楚:第一,通过 Promise 把回调风格变成 async/await 可读的写法;第二,所有请求自动带上 token,后端靠这个识别用户身份;第三,统一处理 401 状态码,登录过期就清 token 并跳回登录页。BASE_URL 里的 127.0.0.1 只在开发者工具模拟器里有效,真机调试时同一局域网要用电脑的 IP 替换,这个我在下一章会专门讲。

封装完成之后,列表页加载数据的代码就可以写得很干净:

const list = await request({ url: '/stock/list', data: { warehouseId: 1 } }); this.setData({ stockList: list });

页面里不再出现 wx.request,所有接口调用走统一入口。这也是很多公司小程序项目里入职第一天就被要求写好的基础代码,毕设里提前用了反而是加分项。

5. 联调避坑:Java 后端与小程序对接的五个常见问题

5.1 第一个坎:本地开发时小程序请求 Java 后端为什么总失败

现象:小程序开发者工具里点登录,控制台报http://127.0.0.1:8080/api/... 不在以下 request 合法域名列表中,或者直接是statusCode: 500。原因有两个层级:微信小程序出于安全考虑,要求 wx.request 的域名必须是小程序后台配置过的 HTTPS 地址,本地开发用的是 http 协议,默认会被拦截;就算你在工具里勾选了「不校验合法域名」,后端如果没开启跨域,浏览器或小程序发起跨源请求时响应会被吞掉。

解决分两步。第一步,在微信开发者工具「详情 → 本地设置」里勾选「不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书」。这个开关只对开发者工具有效,真机预览时依然会被拦,所以真机调试要在「开发版」详情里打开调试模式。第二步,在后端加一个 WebMvcConfigurer 配置类,允许小程序域名的跨域请求:

@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }

注意 allowCredentials(true) 时,allowedOrigins 不能写成 "",必须用 allowedOriginPatterns(""),这是 Spring 对 Cookie 跨域的限制。很多接口 500 的根本原因其实是 OPTIONS 预检请求没过,加上这个配置后,请求链路就通了。

5.2 时间字段和金额字段:JSON 序列化时两个最容易翻车的类型

现象:后端查询出来的 LocalDateTime 字段在小程序端显示成2025-06-01T14:30:00,或者 BigDecimal 类型的金额变成了12.30而不是12.3,前端展示很不自然。原因在于 Java 默认的 JSON 序列化对 LocalDateTime 输出的是 ISO 格式,对 BigDecimal 输出的是科学计数法的场景很少,但保留精度问题很常见。

解决方式是在 Jackson 配置里统一时间格式,同时对金额字段做序列化处理。最常见做法是在 application.yml 里配置:

spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai

这只对 java.util.Date 生效,对 Java 8 的 LocalDateTime 无效。LocalDateTime 要在实体字段上加注解:

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime createTime;

而金额字段建议用 BigDecimal 且在前端做个保留两位小数的显示工具,不要把 Math.round 写在 Java 里。这背后是毕业设计里很少讲的精度教训:金额不能用 double 计算,0.1 在 double 里是个无限循环小数;数据库里如果有金额字段就用 DECIMAL(10,2),Java 里用 BigDecimal,JSON 序列化时再用 toString 而不是数值输出。这一条讲出来,答辩时能挡住大部分追问。

5.3 数据权限校验:为什么不能让前端直接传仓库 id 就改库存

现象:小程序端入库时向/api/inbound/save提交 warehouseId、productId、quantity,后端一收到就执行库存增加。看起来业务闭环了,但问题很大——任何人都可以伪造一个请求,往任意仓库加任意数量的库存。这属于典型的越权问题,也是仓储系统区别于普通增删改查系统的安全边界。

解决办法是后端不仅要校验参数,还要校验身份。入参里去掉了 operatorId 字段,改为从 token 里解析当前登录用户,这样操作记录里留下的操作人是谁,由后端说了算,前端假冒不了。

@PostMapping("/inbound/save") public Result save(@RequestBody InboundDTO dto, @RequestHeader("Authorization") String token) { UserInfo user = authService.getUserByToken(token.replace("Bearer ", "")); if (user == null) { return Result.error(401, "请先登录"); } inboundService.create(dto, user.getId()); return Result.ok(); }

代码里 @RequestHeader 从请求头里拿出 token,再用 token 换用户信息。参数校验也不能省:quantity 必须大于零,productId 必须存在,warehouseId 必须存在,否则直接抛异常。答辩时如果老师问「你怎么防止用户直接调接口」,把这一讲就能体现出工程意识。很多毕设死在「功能都能跑但安全为零」这个评价上,这工夫不能省。

5.4 图片和文件字段:临时路径不能直接存数据库,要用自己的服务器落盘

现象:仓储系统如果需要上传商品图片,小程序端拿到的是wxfile://tmp_xxx.jpg或http://tmp/xxx这种临时路径,直接把这个路径放进 product 表里,页面一刷新图片就消失了。原因是微信在小程序运行期间生成的临时文件,生命周期很短,小程序重启后就没有了。

解决方案分两步。第一步,小程序端用 wx.uploadFile 把临时文件上传到自己的 Java 后端;第二步,后端把文件写入服务器本地目录或对象存储,然后返回一个可访问的 URL。后端接收文件的代码段:

@PostMapping("/file/upload") public Result upload(@RequestParam("file") MultipartFile file) { String fileName = UUID.randomUUID().toString().replace("-", "") + "." + StringUtils.getFilenameExtension(file.getOriginalFilename()); String dir = "/data/upload/"; File dest = new File(dir + fileName); file.transferTo(dest); return Result.ok("http://your-server/static/" + fileName); }

代码里 UUID 重命名是为了防止文件名包含中文或特殊字符导致存储和访问出问题。文件落地后还要在 Spring Boot 里配置静态资源映射,让/static/**指向这个上传目录。这部分比较繁琐,如果只是毕设需求,我都建议简化:直接把图片放在后端 resources/static 目录里,上传用相对路径写,答辩演示足够用。但无论如何,直接存 wxfile 路径一定是翻车现场。注意你的数据库里永远只存可访问的完整 URL,这个 URL 是后端返回的,不是小程序生成的。

5.5 真机调试与模拟器:localhost 与局域网 IP 的差异

现象:开发者工具里所有接口正常,点击「真机预览」后所有请求全部超时,但电脑上的后端日志显示根本没收到请求。原因很简单:手机上的小程序访问 127.0.0.1 时的 127.0.0.1 是手机自己,不是电脑。这个小知识点是每年毕设季咨询量最大的问题之一。

解决方法是把请求地址从 localhost 改成电脑在局域网里的 IP,并确保手机和电脑连同一个 WiFi。查询电脑 IP 的命令:

ipconfig

Windows 下找到无线局域网适配器的 IPv4 地址,比如 192.168.1.105,然后把 request.js 里的 BASE_URL 换成http://192.168.1.105:8080/api,后端启动时注意监听 0.0.0.0 而不仅是 127.0.0.1。Spring Boot 默认监听所有网卡,通常不用改;但如果防火墙没有放行 8080 端口,手机请求依然会被拦截。

这里还要提一个最常见的误解:开发版小程序在真机上不勾选「不校验合法域名」开关,打到 http 地址一定会失败。真机调试需要进入「开发版」详情 → 打开调试模式,或在小程序后台把本地 IP 配进合法域名——后者只适合有域名备案的场景,毕设阶段一般用前者就行。

6. 答辩收尾:把运行环境讲清楚,再送你一个启动时自动初始化菜单的小技巧

前面几章把环境、数据库、接口和联调都过完了,最后再说两个能在答辩现场抬分的小事。第一个是务必把启动顺序整理成一句话:「先启动 MySQL,再启动 Java 后端,等 Started Application 出现,最后打开小程序开发者工具并勾选不校验域名」。这句话说明白,老师就知道你是真的自己在跑项目,而不是只会背概念。

第二个是很多毕设仓储系统里都有角色菜单表,但需求文档里又没写清楚菜单由谁维护。常见的做法是直接用 SQL 语句插入,但这样改菜单要重新导数据库。我习惯在 Spring Boot 里放一个启动初始化组件,每次启动时自动检查菜单表是否有数据,没有就插入默认菜单。这样做的好处是:数据库换一台机器也能自动恢复,答辩演示时不至于因为忘记导入菜单数据而白屏。核心代码是一个 ApplicationRunner:

@Component public class MenuDataInitializer implements ApplicationRunner { @Autowired private JdbcTemplate jdbcTemplate; @Override public void run(ApplicationArguments args) { Integer count = jdbcTemplate.queryForObject( "SELECT COUNT(*) FROM sys_menu", Integer.class); if (count != null && count == 0) { jdbcTemplate.update("INSERT INTO sys_menu(name, path, sort) VALUES (?, ?, ?)", "入库管理", "/pages/inbound/inbound", 1); jdbcTemplate.update("INSERT INTO sys_menu(name, path, sort) VALUES (?, ?, ?)", "出库管理", "/pages/outbound/outbound", 2); jdbcTemplate.update("INSERT INTO sys_menu(name, path, sort) VALUES (?, ?, ?)", "库存查询", "/pages/stock/stock", 3); } } }

这段代码里的 queryForObject 负责查菜单数量,update 负责插入。关键是「幂等」——先查数量,数量为零才插入,反复重启也不会重复。这个思路我在很多项目里都用,不管是不是毕设项目,启动自检加初始化的组合能省掉大量「环境没配好的玄学问题」。我个人吃过一次亏:在之前的项目里忘写数量判断,每次重启菜单多一套,后来凡是初始化逻辑我都先想一遍这个操作能不能安全执行多次。希望这个习惯也能帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 8:26:35

基于Node.js和Vue的短剧推荐系统设计与实现:从选题到答辩全流程

这几天又有人私信我&#xff1a;毕业设计到底做什么题好&#xff1f;说实话&#xff0c;每次看到“XX管理系统”“XX商城”这类题目&#xff0c;我都想多问一句&#xff1a;你要是答辩老师&#xff0c;连续看了八个一模一样的管理系统&#xff0c;还会觉得这个选题有价值吗&…

作者头像 李华
网站建设 2026/10/18 7:03:15

JSP员工管理系统实战:从数据库到Tomcat部署的完整闭环

简介&#xff1a;本资源是一套完整的基于JSP技术开发的企业员工信息管理系统毕业设计项目&#xff0c;面向计算机专业本科生及Web开发初学者&#xff0c;解决传统手工管理员工信息效率低、易出错、协同难等实际问题。压缩包共10个文件&#xff0c;含JSP源码&#xff08;rlzy.zi…

作者头像 李华
网站建设 2026/10/22 23:41:33

微信小程序+Spring Boot:陕西农产品团购平台毕设全流程解析

最近是毕设开题的高峰期&#xff0c;几乎每天都有人来问小程序相关的题目。我接触过太多做了两三个月还卡在登录环节、或者答辩前一周后台接口才勉强跑通的案例&#xff0c;这次专门把"基于手机端的陕西地区特色农产品团购平台"这个小程序毕设项目的完整脉络拆一遍&a…

作者头像 李华
网站建设 2026/10/19 3:27:19

RabbitMQ消息优先级机制:从原理到实战,保障大数据链路核心SLA

做大数据链路的人&#xff0c;基本都遇到过这种场景&#xff1a;业务方在群里催处理线上告警&#xff0c;RabbitMQ队列里却堆着几百万条普通日志。默认情况下消息谁先到谁先被消费&#xff0c;日志一多&#xff0c;告警硬生生排到了几分钟之后。这显然不合理。RabbitMQ的消息优…

作者头像 李华
网站建设 2026/10/19 16:21:03

Claude 记忆管理工具 claude-mem 实战:原理、配置与工作流

1. 这个项目到底解决了什么痛点先说个我自己的经历。用 Claude 写代码、写文档、做方案&#xff0c;最烦的一件事就是它“记性太差”。今天跟它讨论完一个项目的架构选型&#xff0c;明天再打开对话&#xff0c;它一脸茫然&#xff0c;好像我们昨天根本没聊过。重新描述一遍上下…

作者头像 李华
网站建设 2026/10/17 17:26:20

Java全栈开发面试复盘:从基础数据类型到高并发项目实战

那场面试让我印象很深&#xff1a;面试官放下我的简历&#xff0c;问的第一个问题不是项目经历&#xff0c;而是一道听起来很“送分”的基础题——说说 Java 的八种基本数据类型。我差点脱口而出“int、long、float、double、char、boolean、byte、short”&#xff0c;但话到嘴…

作者头像 李华