ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

微信小程序+SSM校园二手交易系统:从数据库设计到接口联调

2026/9/20 3:34:00 拓冰建站 浏览量
微信小程序+SSM校园二手交易系统:从数据库设计到接口联调 简介一份完整的本科毕业设计论文文档主题为基于微信小程序的校园二手交易平台系统设计与实现适合软件工程、计算机等相关专业学生作为毕业设计参考或同类项目开发蓝本。文档围绕微信小程序端、Java后台服务与MySQL数据库三大技术栈展开明确划分管理员与学生两类角色系统阐述商品分类、商品发布与购买、收藏、交流论坛、后台管理等核心功能模块并讲解了后台如何接收和处理微信小程序传入的JSON数据、借助MySQL保障数据存储安全以及界面友好性和后续可扩展性设计。压缩包内仅含1个doc文件大小1.06MB即论文全文内容涵盖摘要、Abstract、目录及完整章节结构清晰便于对照模仿。目前已有80人学习下载读者可从中获取系统选题思路、技术选型论证和论文组织方法能有效缩短毕业设计前期准备时间。1. 一个毕设题为什么从 PC 端挪到微信小程序里校园二手交易这个题目过去大多是做成 SpringMVC JSP 的 Web 应用演示的时候要在电脑浏览器里开一个后台再模拟用户操作评委看的时候总觉得有距离感。把同一个业务搬到微信小程序里之后交互链路变成了「学生手机端点开小程序 → 发布/购买闲置 → 后端 Java 接口处理 JSON → MySQL 落库」整个过程在手机上就能跑通也更贴近现在学生实际的交易习惯。这个选题最大的价值不是功能多复杂而是把微信小程序的端侧开发、SSM 后端的接口开发、MySQL 的表设计这三块毕业设计里最常见的考察点串在了一起。对要做类似课题的人来说这套系统的角色划分和数据结构可以直接复用踩坑点也集中在两端联调的部分而不是业务本身。2. 角色权限与数据表设计先想清楚谁在卖谁在买2.1 两个角色的权限边界决定了接口数量系统里只有管理员和学生两个角色这是典型的轻量级后台设计。管理员端的功能落在「个人中心、学生管理、商品分类管理、商品信息管理、购买信息管理、出售信息管理、交流论坛、系统管理」这八块本质上就是一个针对学生、商品、交易记录、帖子的 CRUD 后台。学生在小程序端能做的事是注册登录、浏览商品、发布出售信息、购买商品、收藏商品、在论坛发帖。这个权限边界直接影响了后端接口的设计。管理员侧的接口基本都可以做成「表名 增删改查」的通用接口比如/student/list、/goods/update而学生侧的接口则需要带上用户身份校验例如发布出售信息时要写入xueshengzhanghao购买商品时要校验商品是否还在售。我在做这类毕业设计时一般会先把角色的功能列表整理成一张矩阵哪张表由哪个角色写、哪个角色只能读确定了再开始建表否则很容易出现学生能直接改商品审核状态的逻辑漏洞。2.2 商品信息表和出售信息表为什么分开从论文里的表结构可以看出系统有「商品信息表」和「出售信息表」两张看起来很像的表。后台管理员管理的是商品信息表学生发布的是出售信息表。这两张表的字段高度相似都包含商品名称、分类、封面、数量、规格、价格、发布时间等但语义完全不同出售信息表是学生在平台上发起的出售请求需要经过管理员审核sfsh字段表示是否审核shhf字段表示审核回复商品信息表则可以理解为审核通过后、正式在首页展示的商品。也就是说学生发布 → 生成出售信息记录 → 管理员审核 → 通过后写入商品信息表 → 小程序首页展示。这个流程在演示时一定要讲清楚否则评委问「为什么学生发布了但首页没看到」就会卡住。2.3 核心表字段清单根据论文里的数据库设计把最核心的几张表整理如下。字段名保留原设计方便直接对着建库。表名关键字段说明学生表xueshengzhanghao, mima, xueshengxingming, xingbie, lianxifangshi, touxiang账号默认唯一密码建议 MD5 后存储商品信息表shangpinbianhao, shangpinmingcheng, shangpinfenlei, shangpinfengmian, shuliang, guige, jiage, fabushijianshangpinbianhao可做唯一索引出售信息表chushoubianhao, shangpinmingcheng, chushoushuliang, chushoujiage, xueshengzhanghao, sfsh, shhf新增学生账号、审核字段购买信息表dingdanbianhao, shangpinmingcheng, 购买数量、购买价格、买家账号订单号建议用时间戳 随机数交流论坛表title, content, parentid, userid, usernameparentid为 0 表示主帖非 0 表示回复收藏表userid, refid, tablename, name, typerefid存商品 idtablename存商品表名这里有一个容易被忽略的点购买信息表里没有直接存外键关联商品 id而是冗余了商品名称和价格。这种设计在毕业设计里很常见好处是查询订单列表时不需要 join 商品表坏处是如果商品价格改了历史订单会跟着变或者需要额外存一份下单时的快照字段比如jiage和实际支付价格分开。如果你的业务有价格变动场景建议在购买信息表里增加一个pay_price字段固定下单时的价格。2.4 表间 id 关联与设计原则数据库设计主要遵循三范式但在这个系统里像收藏表通过refid tablename来指向任意表记录就属于反范式的设计。它的好处是收藏功能不需要为每个业务表单独建收藏关系表只靠一张表就能支持收藏商品、收藏帖子甚至点赞。代价是查询时无法用数据库外键保证引用完整性只能在业务层判断删除。实际开发中很多后台管理系统的通用收藏组件就是这么做的毕设采用这个方案完全够用。接口在查询商品列表时要注意分页。微信小程序端的滚动列表一般用page和limit两个参数后端对应PageHelper.startPage(page, limit)返回的 JSON 结构建议统一为{code, msg, data, count}。这样小程序端做触底加载时只需要判断data.length limit就知道是否还有下一页了。3. 后端 SSM 接收小程序 JSON接口设计才是联调重点3.1 小程序请求与后端 Controller 的映射微信小程序的wx.request默认提交的是 JSON 格式数据所以在 SSM 后端要用RequestBody来接收对象而不是传统的表单RequestParam。Spring 的MappingJackson2HttpMessageConverter会把 JSON 自动反序列化成 Java 对象前提是前端字段名和后端实体属性名一致。这一步是两端联调时最容易出问题的地方小程序端习惯用驼峰后端实体如果用下划线就会出现字段全部为 null 的情况。一个规范的做法是后端实体类属性名统一用驼峰数据库字段用下划线通过 MyBatis 的mapUnderscoreToCamelCase配置自动映射。配置如下mybatis: configuration: map-underscore-to-camel-case: true然后在application.yml里设置。这样shangpin_bianhao就能自动映射到shangpinBianhao。如果坚持手写 SQL也可以直接在resultMap里指定column和property的对应关系但字段多了会很啰嗦。我一般倾向直接开启驼峰映射代码量最少也不容易出错。3.2 登录与注册接口的 JSON 参数处理学生注册登录的接口是最基础的。前端把xueshengzhanghao和mima用 JSON 格式传过来后端StudentController提供一个/student/login接口RestController RequestMapping(/student) public class StudentController { Autowired private StudentService studentService; PostMapping(/login) public MapString, Object login(RequestBody Student student) { MapString, Object result new HashMap(); Student exist studentService.selectByAccount(student.getXueshengzhanghao()); if (exist null) { result.put(code, 500); result.put(msg, 账号不存在); return result; } if (!exist.getMima().equals(student.getMima())) { result.put(code, 500); result.put(msg, 密码错误); return result; } result.put(code, 200); result.put(data, exist); return result; } }这段逻辑很简单但有几个参数层面的细节需要说明。第一RequestBody Student会把前端传的{xueshengzhanghao:2021001, mima:123456}直接映射到Student对象所以字段名必须严格一致。第二密码比较用的是明文equals毕设可以这么做但正式项目里必须用加盐哈希至少也要用 MD5 后再比较否则数据库泄露就是灾难。第三返回的Map会由 Spring 自动转成 JSON前端拿到后判断code是不是 200 就能决定是否跳转页面。注册接口类似只是要增加一个唯一性校验PostMapping(/register) public MapString, Object register(RequestBody Student student) { MapString, Object result new HashMap(); if (studentService.selectByAccount(student.getXueshengzhanghao()) ! null) { result.put(code, 500); result.put(msg, 账号已存在); return result; } studentService.insert(student); result.put(code, 200); result.put(msg, 注册成功); return result; }这里的参数说明是insert方法写入数据库时addtime字段可以用数据库的DEFAULT CURRENT_TIMESTAMP自动填充不需要 Java 代码设置。注册成功后建议直接调用登录接口返回用户信息省得前端再跳一次登录页。3.3 发布商品接口出售信息与商品信息双写学生发布商品的流程前面讲过先写出售信息表管理员审核通过后再写商品信息表。所以发布接口只需要操作出售信息表PostMapping(/sale/add) public MapString, Object addSale(RequestBody ChushouInfo sale) { MapString, Object result new HashMap(); sale.setSfsh(待审核); sale.setShhf(); saleService.insert(sale); result.put(code, 200); result.put(msg, 发布成功等待管理员审核); return result; }代码逻辑很直接但要注意chushoubianhao这个编号字段。如果前端不传后端在插入前要自己生成常见的做法是System.currentTimeMillis()拼接随机数sale.setChushoubianhao(CS System.currentTimeMillis() (int)((Math.random() * 9 1) * 1000));这样生成的编号不会重复也能作为购买信息表里的关联依据。管理员审核的接口就是修改sfsh为通过然后将这条记录复制插入到商品信息表并把shangpinbianhao也生成好。这一段逻辑建议放在事务里Transactional保证两个写操作要么都成功要么都失败避免出现出售信息显示已通过但商品列表里查不到的情况。3.4 购买信息记录与商品下架学生点击购买时前端传商品 id 和学生账号后端做两件事插入购买信息表更新商品信息的库存数量。Transactional PostMapping(/buy) public MapString, Object buy(RequestBody BuyInfo buy) { MapString, Object result new HashMap(); Goods goods goodsService.selectById(buy.getRefid()); if (goods null || goods.getShuliang() 0) { result.put(code, 500); result.put(msg, 商品已下架或库存不足); return result; } buy.setDingdanbianhao(DD System.currentTimeMillis()); buyInfoService.insert(buy); goods.setShuliang(goods.getShuliang() - 1); goodsService.update(goods); result.put(code, 200); result.put(msg, 购买成功); return result; }这里参数buy.getRefid()是商品在收藏表或者说前端传过来的商品信息表主键。要注意控制超卖毕设项目可以不加分布式锁但至少要在 SQL 里加条件比如UPDATE goods SET shuliang shuliang - 1 WHERE id #{id} AND shuliang 0如果受影响行数为 0说明库存已经被扣完了直接返回失败。用Transactional保证购买记录和库存更新一致避免出现订单有了但库存负数的情况。4. 微信小程序端页面与请求封装4.1 页面结构首页、分类、发布、我的小程序端的典型 tabBar 是四个页面首页、分类、发布、我的。首页展示商品列表分类页按shangpinfenlei筛选发布页就是表单提交我的页面包含个人信息、我发布的、我购买的、我的收藏和论坛入口。每个页面由.wxml、.wxss、.js、.json四个文件组成页面跳转用wx.navigateTotab 切换用wx.switchTab。在app.json里注册页面路径和 tabBar 配置要注意tabBar的pagePath必须在pages数组里声明否则编译直接报错。一个容易忽略的细节是window.navigationBarTitleText会显示在标题栏每个页面需要单独在对应页面的.json里配置标题否则会统一用全局的。4.2 request 请求封装与身份标识小程序端的wx.request每次都要写 url、method、header、success、fail非常繁琐一般会封装成一个公共方法。我在项目里通常放在utils/request.jsfunction request(url, method, data) { return new Promise((resolve, reject) { wx.request({ url: getApp().globalData.baseUrl url, method: method, data: data, header: { content-type: application/json, token: wx.getStorageSync(token) }, success(res) { if (res.statusCode 200) { resolve(res.data) } else { reject(res.data) } }, fail(err) { reject(err) } }) }) } module.exports request封装后的调用方式很简单const request require(../../utils/request.js) Page({ onLoad() { request(/goods/list, GET, { page: 1, limit: 10 }) .then(res { this.setData({ goodsList: res.data }) }) } })参数说明baseUrl在小程序开发时填本机局域网 IP 加后端端口比如http://192.168.31.100:8080。content-type必须写成application/json这样data里的对象才会被序列化成 JSON 字符串发出。token可以在登录成功后存入wx.setStorageSync后端如果有拦截器每次都从 header 里取这个字段校验 session。4.3 发布商品页的 form 数据组装发布页面用表单收集商品名称、分类、数量、价格、规格、详情等信息。图片上传用wx.chooseMedia选择图片后需要先通过wx.uploadFile传给后端的上传接口拿到返回的图片 URL再和表单数据一起提交。这里要注意wx.uploadFile的name参数要和后端RequestParam(file) MultipartFile file对应后端保存图片后返回{url: /upload/xxx.png}。组装数据时不要直接提交一个包含 null 字段的对象。通常我会先在 JS 里做一次轻校验submitForm() { const { name, price, quantity, category, detail } this.data if (!name || !price || !quantity) { wx.showToast({ title: 请填写必填项, icon: none }) return } const saleData { shangpinmingcheng: name, shangpinfenlei: category, shangpinfengmian: this.data.coverImage, shuliang: parseInt(quantity), jiage: parseFloat(price), shangpinxiangqing: detail, xueshengzhanghao: wx.getStorageSync(account), xueshengxingming: wx.getStorageSync(name), lianxifangshi: wx.getStorageSync(phone) } request(/sale/add, POST, saleData).then(res { wx.showToast({ title: res.msg || 发布成功 }) }) }这段代码里的字段名需要和后端实体完全一致否则 Spring 反序列化后是 null。价格和数量在前端就要转成 number 类型因为后端float和Integer不能接收字符串。分类字段建议用 picker 组件从后端分类接口动态加载不要在前端写死这样管理员在后台增删分类时小程序端不需要发版。4.4 交流论坛的列表与发帖论坛页通常做成两个层级主帖列表和帖子详情。列表接口/forum/list返回parentid 0的主帖详情页根据refid或parentid关联查询回复列表。发帖时需要注意parentid的取值主帖为 0回复帖为主帖的 id。一个简化版的发帖实现submitPost() { const content this.data.postContent request(/forum/add, POST, { title: this.data.postTitle, content: content, parentid: this.data.currentPostId || 0, userid: wx.getStorageSync(userId), username: wx.getStorageSync(name) }).then(res { if (res.code 200) { wx.navigateBack() } }) }参数说明userid是学生表的主键 id不是账号。有些后端会忽略前端传的userid而直接从 token 里解析但毕设项目直接传也方便演示。如果帖子有「状态」字段isdone在发帖时前端可以不传后端默认给一个正常值即可。这里最容易踩的坑是字段名论坛表里是parentid不是parentIdMyBatis 如果没开驼峰映射就会查不到数据。5. 联调部署与高频踩坑从毕设到能演示的细节以我自己的经历这个系统最容易卡住的地方不在代码逻辑而在微信开发者工具和后端之间的联通上。第一件事是打开微信开发者工具的「详情 → 本地设置 → 不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书」否则开发环境下请求http://localhost:8080会被拦截。第二件事是后端服务的 IP 不能用127.0.0.1因为手机模拟器访问的是电脑的局域网 IP启动后端时让它监听0.0.0.0java -jar campus-trade.jar --server.address0.0.0.0在真机调试时手机和电脑必须连同一个 Wi-Fi然后baseUrl改成电脑的局域网 IP。如果发现请求能到后端但响应很慢检查一下 Windows 防火墙有没有放开 8080 端口。JSON 字段映射是第二个高频问题。我曾经遇到过前端传了shangpinfengmian后端实体是shangpinFengmian结果图片字段一直是 null。排查方法很简单在 Controller 入口处打日志或者用 postman 模拟请求看RequestBody接收到的对象到底哪些字段有值。如果 Postman 正常但小程序端不通多半是content-type被设成了application/x-www-form-urlencoded导致后端把 JSON 字符串当一个参数解析。另外MySQL 的时区问题也会在演示当天突然出现。如果数据库连接串里没有配serverTimezoneAsia/ShanghaiJava 8 以上会报The server time zone value Öйú±ê׼ʱ¼ä的错或者时间字段少 8 小时。我一般直接在配置里加spring: datasource: url: jdbc:mysql://localhost:3306/campus_trade?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai演示前一定要把这几条固定检查一遍学生账号能否登录、发布商品后管理员在后台能看到待审核记录、审核通过后小程序首页出现该商品、点击购买后库存减一、论坛发帖后详情页能刷出帖子。把这条链路走通这个毕设的核心演示就稳了。对于想在这个项目上加分的人可以在收藏表上做一个「我的收藏」列表前端用refid关联商品详情页跳转工作量不大但能明显提升完成度的观感。本文还有配套的精品资源点击获取