ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

微信小程序二手车交易平台毕设全栈详解与避坑指南

微信小程序二手车交易平台毕设全栈详解与避坑指南 简介基于微信小程序的二手车交易平台毕业设计源码面向计算机相关专业学生毕业设计或课程设计场景提供从前端交互到后端管理的完整实现。项目采用Java/PHP作为服务端语言搭配MySQL数据库使用小程序框架开发前端功能涉及管理员首页、个人中心、汽车分类与品牌管理、汽车信息管理、用户管理、留言板管理、系统管理、订单管理等模块。压缩包共1241个文件大小47.39MB主要文件类型包括png/jpg界面截图、vue/wxml/wxss页面文件、js逻辑文件、java后端代码、sql数据库脚本及md/doc说明文档并附有install/run批处理脚本辅助部署。目前已有91人学习浏览适合需要对照完整项目快速搭建环境、梳理模块关系或进行二次开发的学习者。整套资料在毕业设计答辩或课程汇报中具有较强的演示价值能够清晰展示二手车交易平台的后台数据管理与用户订单流程。1. 这个毕业设计到底在做什么一个能跑通全链路的小程序全栈项目毕业设计拿到“基于微信小程序的二手车交易平台”这个题目的同学多半是冲着小程序这个壳去的——不用管安卓和 iOS 适配页面改完即刷即看还能把“前后端分离”四个字真正写进简历。但这题目要交付的东西本质上是三条链路卖家发布车辆、买家浏览咨询、平台撮合成交外加一份 MySQL 建表脚本和一本能应付答辩的说明文档也就是压缩包里常写的 LW。踩过的毕业生少说也有几百人最常见的翻车点不是需求看不懂而是把顺序搞反了一上来就跑小程序后端没起来、数据库没导入白屏一下午。这篇文章按我自己的落地顺序给你拆一遍顺便把那些搜索引擎救不了的坑直接标出来。2. 源码结构拆解三块代码各自管什么先看懂再动手拿到压缩包别急着解压运行。毕设源码基本是三件套小程序前端、后端服务、SQL 脚本外加一份说明文档。带毕业设计的这几年我见过太多同学第一步就翻车——双击后端代码、跑起来发现连不上 MySQL然后开始玄学改配置。先花十分钟把目录看明白能省后续一整天。2.1 前后端分离的目录长什么样一个标准的微信小程序 Spring Boot 前后端分离项目解压后大概是这样一棵目录树used-car-miniapp/ ├── miniprogram/ # 微信小程序端 │ ├── pages/ │ │ ├── index/ # 车辆列表首页 │ │ ├── detail/ # 车辆详情 │ │ ├── publish/ # 发布车辆 │ │ ├── order/ # 订单管理 │ │ └── mine/ # 个人中心 │ ├── utils/ │ │ └── request.js # 请求封装统一带 token │ └── app.js ├── server/ # 后端服务Spring Boot │ ├── src/main/java/ │ │ ├── controller/ # 接口层只管参数和返回值 │ │ ├── service/ # 业务层做逻辑判断 │ │ └── mapper/ # 数据访问层跟 MySQL 打交道 │ └── src/main/resources/ │ └── application.yml # 数据库连接、端口配置都在这里 └── sql/ └── used_car.sql # 建库建表脚本用 Navicat 或命令行导入这套目录的阅读顺序有讲究。先看sql/下的脚本搞清楚数据库里有哪些表和字段再看server/的 controller 包知道后端提供了哪些接口最后才轮到miniprogram/里的页面。小程序页面只是皮真正的业务约束都在后端和数据库里。新手容易犯的一个错误是拿着application.yml就开改却不知道改的是什么。文件里server.port控制后端端口spring.datasource.url指定数据库地址改错了连错信息都看不懂。先分清这三层后续所有排错都能按层定位。2.2 核心接口一览小程序要调的 API 就这九个把后端 controller 里的映射梳理一遍二手车平台的核心接口通常不会超过九张表、十来个接口。下面是做过多个类似项目后归纳出的最小接口集接口路径方法作用关键参数/api/user/loginPOST小程序 code 换 tokencodewx.login 获取/api/vehicle/listGET车辆分页列表page、size、brandId、minPrice/api/vehicle/detailGET车辆详情id/api/vehicle/publishPOST发布车辆title、price、mileage、images/api/vehicle/updatePOST改价格/下架id、status/api/favorite/addPOST收藏车辆vehicleId/api/order/createPOST创建订单买家发起vehicleId/api/order/statusPOST更新订单状态id、status/api/order/listGET我的订单买/卖type、userId注意/api/vehicle/list的page参数多数教科书代码默认从 1 开始但有些学生自己写的后端从 0 开始。前后端联调时这是第一个对不上的地方。建议以数据库分页的常见写法为准page从 1 开始size默认 10、上限 20接口返回里同时带total和pages方便小程序端渲染分页。2.3 数据库设计二手车表里除了价格还必须存什么二手车和普通商品最大的区别在于“一车一况”所以车辆表不能只存标题和价格。跟车况强相关的字段必须单独建列不能塞进描述里否则后期筛选没法做。下面是一份参考建表 SQLCREATE TABLE vehicle ( id int(11) NOT NULL AUTO_INCREMENT, user_id int(11) NOT NULL COMMENT 发布者ID, brand varchar(30) NOT NULL COMMENT 品牌如大众/丰田, title varchar(100) NOT NULL COMMENT 标题如 19年速腾 1.4T, price decimal(10,2) NOT NULL COMMENT 价格单位万, mileage int(11) DEFAULT NULL COMMENT 表显里程单位公里, year int(11) DEFAULT NULL COMMENT 上牌年份如 2019, gearbox tinyint(1) DEFAULT NULL COMMENT 0-手动 1-自动, images text COMMENT 图片URL逗号分隔, status tinyint(1) NOT NULL DEFAULT 0 COMMENT 0-在售 1-已售 2-下架, created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_brand (brand) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;字段说明里最容易被忽略的是mileage用int存公里数而非varchar以及price用decimal(10,2)而不是float。用float存价格会在计算时出现精度漂移答辩时被评委问一嘴很难解释。status用数字存状态而不是直接存中文是为了后续加枚举时不用改表结构。买家表、订单表同样有几条经验用户表里openid必须加唯一索引因为微信登录靠它识别用户重复了会导致登录串号订单表要单独存一个order_no字符串订单号不要直接用自增id当订单号给别人看能少挨不少骂。这些细节在说明文档里写清楚就是答辩的加分项。3. 从 MySQL 建库到登录接口先把后端跑通再碰页面后端和后端之间是纯接口调用只要端口通、数据对小程序页面是否美观完全不重要。反过来如果先写页面后端迟迟不通最后两天才来调接口心态会崩。正确的顺序是导入 SQL → 配置数据源 → 启动后端 → 跑通登录和列表接口 → 再写页面。3.1 建库建表SQL 脚本导入的两种方式与字符集坑拿到used_car.sql后用 Navicat 或命令行导入都行。命令行导入最稳避免 Navicat 版本差异带来的执行错误mysql -u root -p CREATE DATABASE IF NOT EXISTS used_car DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci; USE used_car; SOURCE /path/to/used_car.sql;前两行建库、指定字符集第三行切库第四行执行脚本。这里有个血泪经验如果建库时不指定utf8mb4导入带 emoji 的车辆备注时会报错或者静默丢失字符。utf8mb4 是 MySQL 5.7 之后支持四字节字符的字符集小程序端用户昵称里经常带 emoji所以建库必须用它。导入完成后用SHOW TABLES;确认表数量再执行DESC vehicle;看字段有没有缺。如果表结构和上面给出的不一致多半是 SQL 脚本版本不对优先以脚本为准而不是自己手动改表。接下来改后端配置。打开server/src/main/resources/application.yml核心就三行server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/used_car?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的密码这里的serverTimezoneAsia/Shanghai必须加。不加的话MySQL 的日期时间在 JDBC 8.0 驱动下会默认按 UTC 解析导致所有时间字段差 8 小时。这个坑在下文第 5 章还会单独讲。3.2 小程序登录接口code 换 openid再用 JWT 发 token登录是所有业务接口的前提。小程序不能直接拿到用户身份必须用wx.login()返回的code去后端换取openid。后端拿到openid后查用户表不存在就自动注册然后生成 token 返回。核心逻辑如下PostMapping(/login) public Result login(RequestBody LoginVO vo) { // 1. 拿到小程序端 wx.login 产生的 code有效期五分钟 String code vo.getCode(); // 2. 调用微信接口用 code 换 openid String openid wxService.code2Session(code); // 3. 根据 openid 查用户查不到就插入新用户 User user userMapper.selectByOpenid(openid); if (user null) { user new User(); user.setOpenid(openid); user.setNickname(微信用户); userMapper.insert(user); } // 4. 生成 JWT token里面只放 userId过期时间 7 天 String token JwtUtil.createToken(user.getId(), 7 * 24 * 3600L); return Result.success(token); }这段代码的关键在两步。第 2 步code2Session需要你在application.yml里配置小程序的appid和secret这两个值从微信公众平台获取别硬编码在代码里。第 4 步的JwtUtil.createToken建议用jjwt这个库密钥写在配置文件里过期时间设 7 天比较合理——设太短用户要频繁登录设太长又有安全风险作为毕设项目 7 天够用。写完登录接口后先用 Postman 或 Apifox 测一下不带code调接口应该返回参数校验错误带一个随便写的code应该返回微信侧的报错信息。不要一上来就打开小程序调那样报错了你分不清是前端问题还是后端问题。3.3 车辆列表分页接口page、size、brandId 是必调参数列表接口是首页的数据来源也是前后端联调最容易出问题的地方。一个合格的分页列表接口长这样GetMapping(/list) public Result list(RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size, RequestParam(required false) Integer brandId, RequestParam(required false) Integer minPrice) { // page 从 1 开始size 超过 50 直接截断 if (size null || size 50) size 10; int offset (page - 1) * size; ListVehicleVO list vehicleMapper.selectPage(offset, size, brandId, minPrice); Long total vehicleMapper.countByCondition(brandId, minPrice); return Result.success(new PageResult(list, total, page, size)); }PageResult里带total和pages两个字段小程序端渲染“共 XX 辆”和“上拉加载更多”都要靠它们。offset的计算方式是(page - 1) * size这个公式是分页的唯一真相前后端都按它算就不会错。参数上有一个细节minPrice用Integer接收单位是“万”。因为表里price字段是decimal(10,2)查询时写WHERE price #{minPrice}直接用数值比较就行避免把“万”和“元”搞混。接口写好后浏览器直接访问http://localhost:8080/api/vehicle/list?page1size10看到 JSON 返回就说明后端通了。4. 小程序端联调request 封装、列表渲染与发布车辆后端通了的标志是接口用浏览器或 Postman 能调通。但小程序和 Postman 不一样它有请求域名限制、有登录态管理、有setData的异步渲染机制。这一章是真正把“前端调用后端”这件事落地的地方。4.1 request 封装把 token 和错误提示统一处理不要在每个页面里直接写wx.request那是新手毕业设计的通病。项目里建一个utils/request.js把所有网络请求收口到一个函数里const request (url, method GET, data {}) { return new Promise((resolve, reject) { wx.request({ url: getApp().globalData.baseUrl url, method, data, header: { Content-Type: application/json, Authorization: wx.getStorageSync(token) }, success: (res) { if (res.statusCode 401) { // token 过期或无效清掉并跳登录页 wx.removeStorageSync(token); wx.navigateTo({ url: /pages/login/login }); return; } if (res.statusCode 200 res.data.code 0) { resolve(res.data.data); } else { wx.showToast({ title: res.data.message || 请求失败, icon: none }); reject(res); } }, fail: (err) { wx.showToast({ title: 网络异常请检查后端是否启动, icon: none }); reject(err); } }); }); };封装之后页面里只需要request(/api/vehicle/list, GET, { page: 1 })就能拿数据。这里两个参数值得说Authorization头带的是登录时存的 token后端从请求头里解析用户身份code 0是约定好的业务成功码和后端Result类的设计保持一致后端如果返回 500 或别的状态码前端统一弹提示不用每个页面重复写showToast。4.2 车辆列表页下拉刷新和触底加载的写法列表页是首页承担第一印象。小程序里做列表页有固定的套路data里维护list、page、hasMore三个变量下拉刷新重置page为 1触底加载让page加 1Page({ data: { list: [], // 车辆列表 page: 1, // 当前页码 size: 10, // 每页条数 hasMore: true, // 是否还有下一页 loading: false }, async loadVehicles(isRefresh false) { if (this.data.loading) return; if (isRefresh) { this.setData({ page: 1, hasMore: true }); } if (!this.data.hasMore) return; this.setData({ loading: true }); try { const data await request(/api/vehicle/list, GET, { page: this.data.page, size: this.data.size }); const list isRefresh ? data.list : this.data.list.concat(data.list); this.setData({ list, page: this.data.page 1, hasMore: this.data.page data.pages, loading: false }); } catch (e) { this.setData({ loading: false }); } }, onPullDownRefresh() { this.loadVehicles(true).finally(() wx.stopPullDownRefresh()); }, onReachBottom() { this.loadVehicles(false); } });这段代码里最值得留意的不是request调用而是setData的用法。小程序里改data必须走setData直接this.data.list list页面不会刷新。另外hasMore的判断用的是后端返回的pages而不是简单判断返回列表是否为空——如果最后一页恰好有数据但已经到底了空列表判断会触发多次重复请求。4.3 发布车辆图片上传和表单提交要分两步发布车辆和普通表单不一样图片得先传到服务器拿到 URL 后再连同表单数据一起提交。用wx.uploadFile上传成功后表单里存的是图片地址wx.chooseMedia({ count: 6, mediaType: [image], success: (res) { const filePath res.tempFiles[0].tempFilePath; wx.uploadFile({ url: getApp().globalData.baseUrl /api/upload, filePath, name: file, header: { Authorization: wx.getStorageSync(token) }, success: (upRes) { const url JSON.parse(upRes.data).data; // 返回可访问的图片 URL this.setData({ imageUrls: this.data.imageUrls.concat(url) }); } }); } });注意wx.chooseMedia拿到的是本地临时路径必须传给wx.uploadFile才会生成服务器端 URL。临时路径在跨页面使用时经常失效所以发布页里做“预览图”可以用它真正提交订单时必须用上传接口返回的 URL。后端接收上传文件后一般存到本地static目录或对象存储返回一个http://域名/文件路径的完整地址。这个地址在小程序真机上必须走 HTTPS否则图片直接裂开。5. 联调避坑五个让我熬夜到两点的细节前后端联调和单侧测试完全是两回事。后端用 Postman 测得好好的一接小程序就翻车这类问题基本集中在这五个地方。每一条都是真实踩过、后来反复在别处见到的坑。5.1 现象开发者工具预览正常手机一打开就白屏开发者工具里页面、接口一切正常但用真机预览时网络请求全部报错errMsg: request:fail页面自然渲染不出来。原因有两层首先真机环境下微信小程序要求所有请求域名必须在公众平台后台配置合法域名且必须为 HTTPS其次开发工具默认勾选了“不校验合法域名”掩盖了这个问题。解决方法是开发阶段在详情里勾选不校验域名调试流畅要正式上线必须把后端部署到带 HTTPS 的服务器上然后在微信公众平台的“开发管理 → 服务器域名”里把接口域名加进白名单。5.2 现象登录成功但接下来所有业务接口都返回 401登录接口明明拿到了 token列表接口却一直报未认证。原因通常是 token 没被正确传送到后端要么登录后存 token 时用错了 API用了setStorage异步方法页面跳转后还没写完要么请求封装里的 header 字段名写错了。解决方式很简单登录后用wx.setStorageSync(token, token)同步写入封装层统一从 sync 接口读取header 字段名和后端拦截器校验的名字保持一致。如果你改代码时发现字段名时而后端叫token时而后端叫authorization就属于典型的命名不统一建议全项目统一为Authorization。5.3 现象列表里显示的时间比实际慢了 8 个小时数据库存的是下午两点接口返回的 JSON 却变成了早上六点。这是 JDBC 连接串没带时区参数导致的MySQL 驱动 8.0 版本默认按 UTC 处理时间而本机系统时区是东八区。解决办法是改application.yml里的数据源连接串在jdbc:mysql://后面追加serverTimezoneAsia/Shanghai改完重启后端。这属于配置问题和代码逻辑无关排查时会觉得很“玄学”因为它不会报任何异常只是数据静默错位。5.4 现象后端启动报Access denied for user rootlocalhost数据库密码不对或者 root 账号只允许本机登录。很多学生下载源码后忘了把application.yml里的密码改成自己 MySQL 的密码。解决起来分两步第一步确认 MySQL 能连上命令行执行mysql -u root -p看密码是否正确第二步确认后端配的是同一个密码。还有一种少见情况是配了密码但 MySQL 用的是caching_sha2_password认证插件而 JDBC 驱动版本太老不认识把驱动升级到mysql-connector-j8.0.20 以上即可。5.5 现象setData改了数据页面却纹丝不动查了很多遍逻辑都没问题最后发现是直接写了this.data.list newList而不是this.setData({ list: newList })。小程序的this.data是数据快照直接赋值不会触发视图更新。这个坑新手几乎必踩老手偶尔也会写顺手。还有一个变体是修改数组某个下标时用了this.setData({ list[0].price: 10 })key 必须写成字符串路径写成list[0]这种对象形式是无效的。建议把setData当成唯一改数据通道统一习惯能少掉一大半渲染问题。6. 把毕设做成答辩亮点演示顺序、预置数据与代码自检后端和页面都通了之后紧接着要做三件事灌入好看的数据、设计演示路径、清理代码里的硬编码。我见过不少项目明明功能完整却因为演示时数据空空如也、一上来就点“发布车辆”结果页面空白评委全程皱眉。数据是演示的第一印象。在 SQL 脚本里预置 10 到 15 辆不同品牌的车辆覆盖不同价格区间、不同上牌年份顺便让状态字段有“在售”“已售”两种方便演示筛选。演示路径建议按“买家视角 → 卖家视角”走先打开首页看车辆列表点进详情页点收藏再模拟创建一个订单然后切换身份发布一辆车上传图片回到列表页看到新车出现在第一页。这条路径能把列表、详情、收藏、订单、发布五个核心功能全部串起来全程不冷场。代码自检的重点是硬编码。全局搜一遍localhost、127.0.0.1、appid、secret把这些值挪到配置文件或globalData里。不要小看这一步——真机演示时如果填的是开发者的局域网 IP评委的手机连不上你的网一切白搭。推荐在miniprogram/app.js的globalData中放一个baseUrl开关演示时一键切换环境地址。说明文档里也建议补一张环境要求清单写清楚 JDK 版本、MySQL 版本和 Node 环境照着项目根目录的 README 能一键复现。最后给一条个人习惯每次改完数据库字段顺手把sql/used_car.sql同步更新而不是留着旧脚本。毕业设计验收时老师大概率会拿干净环境跑一遍能不能当场跑通取决于脚本和代码是否一致。这个习惯我养了三年帮我在验收时少说了一百句解释。希望这篇笔记能让你在同样的路上少踩几个坑一次把项目跑顺。本文还有配套的精品资源点击获取
返回列表