资讯动态

微信小程序商城Java后端实战:Spring Boot+MySQL+原生开发

发布时间:2026/10/18 11:25:16 来源:尧图企业网站定制
简介这是一套面向计算机专业本科生的高分毕业设计与课程实践项目完整实现了一个微信小程序购物商城系统涵盖前端小程序界面、Java后端服务、MySQL数据库及配套文档特别适合零基础入门到中阶开发能力提升。资源包共1247个文件包含195个JS逻辑文件小程序交互核心、70个Java类SpringMVC/SSM架构后端控制器与业务层、88个CSS与18个WXSS样式文件多端适配UI、315张PNG/GIF图片商品与界面素材、12个DB/SQL文件含建表语句与初始化数据整体压缩包仅36.23MB轻量易部署。已有499人学习下载项目获导师高度认可实测得分98分。用户可直接获取带详细注释的源码、结构清晰的数据库设计说明、功能完整的前后端联调方案以及LoginAction、GoodsAction、CarAction等关键模块的典型业务实现范例是期末大作业、毕业设计选题与Java小程序全栈学习的优质参考范本。1. 微信小程序购物商城为什么“Java后端源码文档数据库”组合是毕业设计与中小项目落地的黄金闭环你手头正赶一个课程设计、毕设选题或是接了个小公司定制商城的需求——界面要能扫码进、商品能加购、订单能生成、后台能查数据。但一搜“微信小程序商城”满屏是“uni-app跨端”“云开发免服务器”“Node.js轻量部署”再点开几个所谓“带源码”的仓库发现后端只有空壳接口、数据库字段没注释、文档里写着“自行配置MySQL”连登录态怎么校验都得翻三篇博客拼凑。这不是技术问题是交付断层前端能跑通后端像黑匣子文档是摆设数据库建表逻辑和Java代码对不上号答辩前两天还在改MyBatis的resultMap映射错误。本方案不讲“高大上”的微服务拆分或秒杀架构而是聚焦一个可独立部署、前后端职责清晰、数据库结构完整、所有代码可调试、文档能照着填参数的最小可行商城系统。它用Spring Boot写Java后端非Spring CloudMySQL存核心数据非MongoDB或云数据库抽象层微信小程序原生开发非WXML转译所有模块——用户登录、商品列表、购物车、下单支付模拟、订单管理、后台管理——全部打通且每一步都有对应源码位置、配置项说明、SQL执行顺序。适合两类人一是需要交一份“看得见、摸得着、讲得清”的毕设作品的学生二是想快速验证一个本地化商城MVP、不依赖第三方云函数或SaaS后台的个体开发者。它不承诺“上线即百万用户”但保证你clone下来改3个配置跑通全部流程答辩时能指着数据库表说清“为什么order_item表必须有外键指向order_id”。2. 搭建Java后端从Spring Boot初始化到RESTful接口分层实现2.1 初始化Spring Boot工程选型依据与关键依赖锁定我们不使用Spring Initializr网页版一键生成易漏关键配置而是用Maven命令行创建可复现的脚手架。核心诉求是轻量、可控、无隐藏依赖。因此排除Spring WebFlux异步非必需、Spring Security OAuth2学生项目用JWT足矣、Lombok虽省代码但调试时变量名丢失答辩演示易翻车。# 在终端执行生成基础工程注意spring-boot-starter-web mybatis-spring-boot-starter 是刚需 mvn archetype:generate -DgroupIdcom.example.mall \ -DartifactIdmall-backend \ -DarchetypeArtifactIdmaven-archetype-quickstart \ -DinteractiveModefalse随后在pom.xml中手动添加以下不可替换的核心依赖版本统一为2.7.18避坑点见2.4节dependencies !-- Spring Web基础 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId version2.7.18/version /dependency !-- MyBatis整合 -- dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.1/version /dependency !-- MySQL驱动必须8.0否则时区报错 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version scoperuntime/scope /dependency !-- JWT工具不引入Spring Security自己手写Filter -- dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version0.11.5/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-impl/artifactId version0.11.5/version scoperuntime/scope /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-jackson/artifactId version0.11.5/version scoperuntime/scope /dependency /dependencies逻辑说明mybatis-spring-boot-starter自动装配SqlSessionFactory比纯MyBatis XML配置少写50%样板代码mysql-connector-java必须8.0因老版本不支持serverTimezoneGMT%2B8参数导致时间字段插入为0000-00-00JWT三件套独立引入避免Spring Security的复杂拦截链干扰登录态调试。2.2 数据库建模从E-R图到可执行SQL脚本含字段注释商城核心实体就五个用户user、商品product、分类category、订单order、订单项order_item。拒绝“一张大表全搞定”的野路子严格遵循第三范式。例如商品价格不能存在订单表里否则历史价格无法追溯必须在order_item中冗余记录下单时的价格。以下是mall.sql建表语句已实测通过直接复制到MySQL客户端执行-- 创建数据库字符集强制utf8mb4支持emoji CREATE DATABASE IF NOT EXISTS mall DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE mall; -- 用户表密码用BCrypt加密存储非明文 CREATE TABLE user ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 主键ID, username VARCHAR(50) NOT NULL UNIQUE COMMENT 用户名, password VARCHAR(100) NOT NULL COMMENT BCrypt加密密码, nickname VARCHAR(50) COMMENT 昵称, phone VARCHAR(20) COMMENT 手机号, avatar_url VARCHAR(255) COMMENT 头像URL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; -- 分类表支持二级分类parent_id为0表示一级 CREATE TABLE category ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 分类名称, parent_id BIGINT DEFAULT 0 COMMENT 父分类ID0为一级, sort_order INT DEFAULT 0 COMMENT 排序序号, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品分类; -- 商品表status1表示上架0为下架 CREATE TABLE product ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL COMMENT 商品名称, description TEXT COMMENT 商品描述, price DECIMAL(10,2) NOT NULL COMMENT 销售价格, original_price DECIMAL(10,2) COMMENT 原价用于划线, stock INT NOT NULL DEFAULT 0 COMMENT 库存, category_id BIGINT NOT NULL COMMENT 所属分类, cover_image VARCHAR(255) COMMENT 封面图URL, status TINYINT DEFAULT 1 COMMENT 状态1上架0下架, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_category (category_id), FOREIGN KEY (category_id) REFERENCES category(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品表; -- 订单表user_id关联用户total_amount为订单总金额 CREATE TABLE order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(50) NOT NULL UNIQUE COMMENT 订单号格式ORD20240520123456, user_id BIGINT NOT NULL COMMENT 用户ID, total_amount DECIMAL(10,2) NOT NULL COMMENT 订单总金额, status TINYINT DEFAULT 0 COMMENT 状态0待支付1已支付2已发货3已完成-1已取消, address VARCHAR(255) COMMENT 收货地址, contact_name VARCHAR(50) COMMENT 收货人姓名, contact_phone VARCHAR(20) COMMENT 收货人电话, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, paid_at DATETIME COMMENT 支付时间, FOREIGN KEY (user_id) REFERENCES user(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单主表; -- 订单项表一个订单可含多个商品price为下单时快照价 CREATE TABLE order_item ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_id BIGINT NOT NULL COMMENT 订单ID, product_id BIGINT NOT NULL COMMENT 商品ID, product_name VARCHAR(100) NOT NULL COMMENT 商品名称冗余防商品删除后订单信息丢失, price DECIMAL(10,2) NOT NULL COMMENT 下单时商品单价, quantity INT NOT NULL DEFAULT 1 COMMENT 购买数量, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (order_id) REFERENCES order(id) ON DELETE CASCADE, FOREIGN KEY (product_id) REFERENCES product(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单明细表;参数说明order_no字段不依赖数据库自增而是在Java层用ORD LocalDateTime.now().format(DateTimeFormatter.ofPattern(yyyyMMddHHmmss)) RandomUtil.randomNumbers(4)生成确保全局唯一且可读order_item.product_name必须冗余这是电商领域血泪经验——商品下架或改名后订单页仍需显示原始名称所有DATETIME字段显式声明DEFAULT CURRENT_TIMESTAMP避免MySQL 5.7严格模式报错外键约束ON DELETE CASCADE仅用于order_item → order绝不在product → category上加级联删除防止误删分类导致商品丢失。2.3 Java分层代码结构Controller→Service→Mapper的职责边界项目包结构严格按功能划分杜绝“所有代码塞进controller”的反模式com.example.mall ├── MallApplication.java // 启动类 ├── config/ │ └── JwtConfig.java // JWT密钥、过期时间配置 ├── controller/ // 接口层只做参数校验、调用Service、返回VO │ ├── UserController.java │ ├── ProductController.java │ └── OrderController.java ├── service/ // 业务层含事务控制、核心逻辑如减库存、生成订单 │ ├── UserService.java │ ├── ProductService.java │ └── OrderService.java ├── mapper/ // 持久层接口定义XML写SQL不用注解便于复杂查询调试 │ ├── UserMapper.java │ ├── ProductMapper.java │ └── OrderMapper.java ├── entity/ // 实体类与数据库表一一对应Lombok可选此处手动写getter/setter │ ├── User.java │ └── Product.java ├── dto/ // 数据传输对象接收前端参数如LoginDTO、返回给前端如OrderVO │ ├── LoginDTO.java │ └── OrderVO.java └── util/ // 工具类JwtUtil、RandomUtil、ResponseUtil统一封装success/fail以下单接口为例看三层如何协作// controller/OrderController.java PostMapping(/orders) public ResultVO createOrder(RequestBody Valid CreateOrderDTO dto, RequestHeader(Authorization) String token) { Long userId jwtUtil.getUserIdFromToken(token); // 从JWT解析用户ID Long orderId orderService.createOrder(userId, dto); // 调用Service return ResultVO.success(下单成功, Collections.singletonMap(order_id, orderId)); }// service/OrderService.java Transactional(rollbackFor Exception.class) // 关键事务必须在此层开启 public Long createOrder(Long userId, CreateOrderDTO dto) { // 1. 校验商品库存查product表 Product product productMapper.selectById(dto.getProductId()); if (product null || product.getStock() dto.getQuantity()) { throw new BusinessException(商品库存不足); } // 2. 扣减库存update product set stock stock - ? where id ? and stock ? int updated productMapper.decreaseStock(dto.getProductId(), dto.getQuantity()); if (updated 0) { // 防超卖CAS更新失败 throw new BusinessException(库存已被抢光请刷新重试); } // 3. 生成订单主表insert into order ... Order order new Order(); order.setUserId(userId); order.setOrderNo(ORD generateOrderNo()); order.setTotalAmount(product.getPrice().multiply(BigDecimal.valueOf(dto.getQuantity()))); order.setStatus(OrderStatus.WAIT_PAY.getValue()); orderMapper.insert(order); // 4. 插入订单项insert into order_item ... OrderItem item new OrderItem(); item.setOrderId(order.getId()); item.setProductId(product.getId()); item.setProductName(product.getName()); item.setPrice(product.getPrice()); item.setQuantity(dto.getQuantity()); orderItemMapper.insert(item); return order.getId(); }逻辑说明Controller只做三件事取token、校验DTO、调Service绝不写SQL或事务逻辑Service用Transactional包裹整个下单流程确保“扣库存插订单插订单项”原子性Mapper层用XML写SQLProductMapper.xml中update iddecreaseStock因为UPDATE ... WHERE stock #{quantity}这种条件更新用注解写易出错且难调试decreaseStock方法返回int值为1表示更新成功0表示库存不足被其他请求抢先扣完这是防止超卖的最简有效手段。3. 微信小程序前端从项目初始化到与Java后端联调3.1 小程序项目初始化与网络请求封装用微信开发者工具新建项目模板选择“Empty Project”空项目拒绝“云开发模板”或“TS模板”——前者绑定腾讯云后者增加编译环节偏离“纯前端对接Java后端”主线。在app.js中全局配置请求基地址开发/生产环境分离// app.js App({ globalData: { // 开发时指向本地Java后端http://localhost:8080 // 生产时需改为服务器IP或域名且必须HTTPS baseUrl: http://localhost:8080/api, // token存储key tokenKey: x-auth-token } })封装统一请求函数utils/request.js自动携带token、处理401跳转登录// utils/request.js const request (options) { const token wx.getStorageSync(x-auth-token); return new Promise((resolve, reject) { wx.request({ url: getApp().globalData.baseUrl options.url, method: options.method || GET, data: options.data || {}, header: { Content-Type: application/json, Authorization: token || // 后端JWT校验依赖此header }, success: (res) { if (res.statusCode 200) { resolve(res.data); } else if (res.statusCode 401) { // token失效跳转登录页 wx.navigateTo({ url: /pages/login/login }); reject(new Error(登录已过期)); } else { reject(new Error(res.data.message || 请求失败)); } }, fail: (err) { reject(new Error(网络错误请检查服务器是否运行)); } }) }) } module.exports request逻辑说明wx.request默认不携带cookie所以JWT必须放Authorizationheader后端RequestHeader(Authorization)才能取到401状态码由后端统一返回如登录过期、token伪造前端捕获后强制跳转登录页避免用户在购物车页卡死baseUrl硬编码在app.js而非project.config.json因后者不支持JS表达式无法动态切环境。3.2 关键页面实现商品列表页的分页与分类筛选商品列表页pages/product/list.js需同时支持① 下拉刷新重新加载第1页② 上拉触底加载下一页③ 点击分类Tab切换不刷新页面核心是维护三个状态当前分类IDcategoryId、当前页码page、是否正在加载loading// pages/product/list.js Page({ data: { products: [], // 商品列表 categoryId: 0, // 当前分类ID0表示全部 page: 1, // 当前页码 pageSize: 10, // 每页条数 hasMore: true, // 是否还有更多数据 loading: false // 加载中状态 }, onLoad() { this.loadProducts(); }, // 加载商品列表 loadProducts() { if (this.data.loading || !this.data.hasMore) return; this.setData({ loading: true }); const params { categoryId: this.data.categoryId, page: this.data.page, size: this.data.pageSize }; request({ url: /products, method: GET, data: params }).then(res { const newProducts this.data.page 1 ? res.data.list : [...this.data.products, ...res.data.list]; this.setData({ products: newProducts, page: this.data.page 1, hasMore: res.data.list.length this.data.pageSize, loading: false }); }).catch(err { wx.showToast({ title: err.message, icon: none }); this.setData({ loading: false }); }); }, // 下拉刷新 onPullDownRefresh() { this.setData({ page: 1, products: [], hasMore: true }); this.loadProducts(); wx.stopPullDownRefresh(); // 停止下拉动画 }, // 上拉触底 onReachBottom() { this.loadProducts(); }, // 切换分类 switchCategory(e) { const categoryId e.currentTarget.dataset.id; this.setData({ categoryId: categoryId, page: 1, products: [], hasMore: true }); this.loadProducts(); } })对应的Java后端ProductController分页接口// controller/ProductController.java GetMapping(/products) public ResultVO listProducts( RequestParam(defaultValue 0) Long categoryId, RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size) { PageProduct productPage productService.listByCategory(categoryId, page, size); return ResultVO.success(获取成功, productPage); }// service/ProductService.java public PageProduct listByCategory(Long categoryId, Integer page, Integer size) { PageProduct pageInfo new Page(page, size); QueryWrapperProduct wrapper new QueryWrapper(); if (categoryId ! 0) { wrapper.eq(category_id, categoryId); } wrapper.eq(status, 1); // 只查上架商品 wrapper.orderByDesc(created_at); // 新商品靠前 return productMapper.selectPage(pageInfo, wrapper); }参数说明小程序端page从1开始MyBatis-Plus的Page构造器也要求current参数从1起始无需-1转换hasMore判断逻辑若本次返回数据条数等于pageSize如10条则认为可能还有下一页若返回9条说明到底了分类切换时重置page1并清空products数组这是用户体验关键——用户点“手机”分类不该看到上一页“电脑”的商品。3.3 支付流程模拟为什么不用真实微信支付SDK真实微信支付需企业资质、ICP备案、HTTPS域名、商户号审核学生项目根本走不通。本方案采用前端生成模拟支付单 后端标记订单为“已支付”的轻量替代小程序端点击“立即支付”调用/orders/{id}/pay接口无真实支付动作后端校验订单状态为“待支付”更新status1并记录paid_atnow()小程序收到成功响应后跳转至“支付成功”页// pages/order/detail.js payOrder() { request({ url: /orders/${this.data.order.id}/pay, method: POST }).then(res { wx.showToast({ title: 支付成功, icon: success }); setTimeout(() { wx.redirectTo({ url: /pages/order/success?orderNo this.data.order.orderNo }); }, 1500); }).catch(err { wx.showToast({ title: err.message, icon: none }); }); }// controller/OrderController.java PostMapping(/orders/{id}/pay) public ResultVO payOrder(PathVariable Long id, RequestHeader(Authorization) String token) { Long userId jwtUtil.getUserIdFromToken(token); orderService.payOrder(id, userId); return ResultVO.success(支付成功); }// service/OrderService.java public void payOrder(Long orderId, Long userId) { // 1. 查询订单校验归属和状态 Order order orderMapper.selectById(orderId); if (order null || !order.getUserId().equals(userId)) { throw new BusinessException(订单不存在或无权操作); } if (order.getStatus() ! OrderStatus.WAIT_PAY.getValue()) { throw new BusinessException(订单状态异常无法支付); } // 2. 更新订单状态为已支付 order.setStatus(OrderStatus.PAID.getValue()); order.setPaidAt(LocalDateTime.now()); orderMapper.updateById(order); }逻辑说明此模拟支付完全绕过微信支付SDK但保留了完整的订单状态机WAIT_PAY → PAID → SHIPPED → FINISHED后续接入真实支付只需替换payOrder方法内部逻辑order.getUserId().equals(userId)校验是安全底线防止用户A恶意请求支付用户B的订单OrderStatus定义为枚举值用Integer而非String数据库存数字0,1,2,3查询效率更高。4. 高分毕设关键文档说明、数据库设计图与答辩话术设计4.1 文档结构清单哪些内容必须写哪些可以精简一份让导师眼前一亮的文档不是堆砌字数而是精准覆盖评审关注点。根据某高校计算机学院毕设评分标准文档应包含以下6个核心章节每章字数建议如下总字数控制在8000字内章节核心内容字数建议为什么必须写1. 系统概述用1句话定义系统如“基于微信小程序与Spring Boot的轻量级购物商城”列出3个核心功能用户管理、商品浏览、订单交易说明技术选型理由如“选用MySQL因关系模型契合订单-商品强关联”500首章定调让导师30秒内理解项目价值2. 需求分析用表格列出5个核心用例UC1用户注册、UC2商品搜索、UC3加入购物车...每例注明参与者、前置条件、主事件流、后置条件1200体现需求工程能力避免“我觉得应该有”式主观描述3. 系统设计重点包含①总体架构图小程序←→Nginx←→Spring Boot←→MySQL②E-R图用draw.io导出PNG标注主外键③核心接口列表如POST /api/orders参数productId, quantity2500设计能力是高分分水岭E-R图必须手绘风格非自动生成体现思考过程4. 数据库实现对user、product、order三张核心表用表格列出字段名、类型、长度、是否为空、键类型、注释如order_no: VARCHAR(50), PK, 订单号全局唯一1500导师必查字段设计合理性冗余字段如order_item.product_name必须解释原因5. 系统实现截图展示3个关键页面登录页、商品列表页、订单详情页每图配50字说明如“商品列表页支持分类Tab切换点击‘手机’分类后URL参数category_id2”1000证明系统真实可运行截图需带窗口标题栏显示localhost:80806. 总结与展望总结2个技术收获如“掌握了JWT无状态认证实现”提出1个可扩展点如“可接入Redis缓存热门商品”不写“由于时间有限未能实现XXX”800展望体现技术视野回避缺陷表述提示文档中所有截图必须是真实运行时截取禁止PS合成。例如登录成功后的首页必须能看到右上角用户头像来自user.avatar_url字段且头像URL是http://localhost:8080/upload/avatar_123.jpg这类真实路径。4.2 E-R图绘制规范手绘感与专业性的平衡不要用PowerPoint画“完美圆角矩形”用 draw.io 免费在线绘制设置如下字体全部用Microsoft YaHei字号14实体框填充色#f8cecc浅红边框#b85450深红圆角半径10关系菱形填充色#dae8fc浅蓝边框#6c8ebf深蓝连线实线一对一、虚线一对多、双线多对多关键标注在连线旁手写标注1、N、0..*draw.io的Text工具字号12以order与order_item关系为例order框写“订单”order_item框写“订单项”连线从order出发经菱形“包含”到order_itemorder侧标1order_item侧标N一个订单可含多个订单项在order_item框内用红色字体标注【冗余字段】product_name并在下方加注释框“防止商品删除后订单信息丢失”避坑E-R图中绝不出现“用户登录”“支付成功”等行为实体这些是用例Use Case不是数据实体。实体只包含持久化存储的数据对象user, product, order。4.3 答辩话术设计3个高频问题与满分回答模板导师提问往往围绕可行性、安全性、扩展性。准备3个问题的标准答案用“结论先行证据支撑”结构Q1为什么用户密码用BCrypt加密而不是MD5A因为BCrypt是自适应哈希算法内置盐值salt和计算轮数cost factor能有效抵御彩虹表攻击和暴力破解。而MD5是快速哈希现代GPU每秒可计算数十亿次且无盐值相同密码哈希值恒定。我们在UserServiceImpl.register()中调用BCryptPasswordEncoder.encode(rawPassword)轮数设为10new BCryptPasswordEncoder(10)实测加密耗时约300ms既保障安全又不影响用户体验。Q2订单表的order_no为什么不用数据库自增IDA因为自增ID暴露业务量如ID10000说明已成交万单且无法追溯生成时间。我们采用ORD 当前日期时间戳 4位随机数的格式例如ORD202405201234567890。这样①前缀ORD标识业务类型②时间戳便于按天统计订单③随机数保证并发场景不重复。代码在OrderService.generateOrderNo()中实现已通过JMeter 1000线程压测验证无重复。Q3如果要做促销活动如满200减20代码要改哪里A只需在OrderService.createOrder()方法中在计算total_amount前插入促销计算逻辑。具体步骤①查询用户可用优惠券couponMapper.selectUsableByUserId(userId)②遍历筛选满足门槛的券如minAmount200③取最优一张折扣最大④更新total_amount originalAmount - couponDiscount。所有改动都在Service层不影响Controller和Mapper符合单一职责原则。提示回答时务必指明代码文件名和方法名如OrderService.createOrder()这比说“在后端逻辑里”专业十倍。提前在答辩PPT的附录页放一张“核心方法索引表”列明登录校验→JwtFilter.java、库存扣减→ProductMapper.xml#decreaseStock等导师追问时秒翻。5. 避坑指南Java后端与小程序联调的5个血泪教训5.1 现象小程序调用/login返回400控制台打印Required request body is missing原因前端wx.request发送的是application/x-www-form-urlencoded格式默认但后端RequestBody LoginDTO dto要求application/json。两者Content-Type不匹配Spring MVC无法反序列化。解决前端请求时显式设置header并确保data是JSON字符串// 错误写法data传Objectheader未设 wx.request({ url: /login, method: POST, data: { username: a, password: b } }) // 正确写法data转JSON字符串header指定json wx.request({ url: /login, method: POST, data: JSON.stringify({ username: a, password: b }), header: { Content-Type: application/json } })5.2 现象MySQL插入中文显示为???日志报Incorrect string value: \xE4\xBD\xA0\xE5\xA5\xBD原因数据库、表、字段三级字符集未统一为utf8mb4或JDBC连接URL缺少useUnicodetruecharacterEncodingutf8mb4serverTimezoneGMT%2B8参数。解决① 执行SQL确认SHOW VARIABLES LIKE character_set_%;确保character_set_database和character_set_server为utf8mb4② 修改application.yml中的JDBC URLspring: datasource: url: jdbc:mysql://localhost:3306/mall?useUnicodetruecharacterEncodingutf8mb4serverTimezoneGMT%2B8allowPublicKeyRetrievaltrueuseSSLfalse5.3 现象小程序onPullDownRefresh触发后页面白屏控制台报Cannot read property setData of undefined原因this指向丢失。在setTimeout或Promise.then回调中this不再指向Page实例。解决用箭头函数保持this上下文或提前缓存that this// 错误写法普通function中this丢失 this.loadProducts().then(function() { this.setData({ loading: false }); // 此处this为undefined }); // 正确写法箭头函数继承外层this this.loadProducts().then(() { this.setData({ loading: false }); });5.4 现象后台管理页PC端能正常登录但小程序端调用/admin/login始终401原因JWT校验Filter未排除/admin/**路径导致管理员Token被当作普通用户Token校验user_id字段不存在。解决在JwtAuthenticationFilter.doFilterInternal()中添加路径白名单// JwtAuthenticationFilter.java Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String path request.getRequestURI(); // 放行管理员登录接口 if (path.startsWith(/admin/login) || path.startsWith(/admin/logout)) { filterChain.doFilter(request, response); return; } // 其余路径执行JWT校验... }5.5 现象商品图片上传后小程序显示404但浏览器直接访问http://localhost:8080/upload/xxx.jpg能打开原因Spring Boot默认静态资源路径为/static、/public等而/upload本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑