
校园里的闲置交易做过的人都知道有多麻烦。去年我在某高校做毕设调研时发现几乎所有学生群都有人在卖旧书、台灯、自行车但信息几分钟就被聊天刷没了想找同款基本靠缘分。当时就决定做一个专门的校园二手交易平台技术栈选了 Spring Boot 加微信小程序也就是标题里那个“springboot校园二手交易平台小程序”。这篇内容不打算只贴源码和截图而是把整个项目的需求拆解、数据库设计、核心接口逻辑、小程序端实现以及我最想分享的踩坑记录都写出来。哪怕你不想关注拿源码单按这篇文章里的架构思路和数据表去复刻也能把一个可上线运行的二手交易系统搭起来。这个项目适合谁正在做毕业设计的学生、想系统走一遍前后端分离开发的初学者、或者单纯想了解校园交易类小程序业务闭环的产品和技术新人。阅读过程中你会发现它没有悬乎的高并发和分布式核心价值在于把“登录、发帖、交易、状态流转”这几件小事做扎实而这恰恰是很多二手平台项目翻车的地方。1. 项目从需求到架构先搞清楚二手交易到底在交易什么1.1 校园闲置场景的真实痛点校园二手交易和闲鱼同城交易不太一样。闲鱼的买家卖家是完全陌生关系靠的是芝麻信用、平台担保和事后评价体系来建立信任而校园场景里买卖双方大概率都在同一个校区、同一栋宿舍楼甚至可能是同一个学院的信任基础天然存在但信息和撮合机制却很原始。我在前期调研里梳理出几个典型问题第一信息承载分散社团群、表白墙、宿舍群各有各的帖子没有统一的浏览和搜索入口第二商品的成色、价格、交易方式完全靠卖家自己描述买家需要反复私聊确认沟通成本高第三缺少订单状态概念说好面交却放鸽子的情况非常多第四没有“已售出”的标记过期很久的商品还在被点赞和询问体验很差。因此这个项目的核心需求不是做一个“能登录、能发帖”的玩具而是要完成一条相对完整的交易闭环用户登录 - 发布商品 - 买家浏览/搜索/联系 - 表达购买意向 - 双方确认 - 订单状态流转 - 线下交易完成。再加上收藏、留言、个人中心等辅助功能就是一个能自洽的业务模型。1.2 为什么是 Spring Boot 加微信小程序技术选型上后端我最终定了 Spring Boot前端定了微信原生小程序没有引入太重的微服务或跨端框架。理由很直接。Spring Boot 的优势在于快速搭建、生态成熟。这个系统需要的能力无非是 REST API、文件存储、鉴权、数据库访问Spring Boot 都能用很简洁的方式集成而且网上资料多遇到问题能找到大量参考。相比 Go、Node 之类的方案对刚接触全栈开发的人来说Spring Boot 的思维方式更规整分层也更清晰。微信小程序作为前端载体则是因为它天然贴合校园场景。学生不需要额外下载 App微信扫一下就进去分享到群和朋友圈也方便。小程序的原生框架对后端的请求封装、登录态管理、图片上传都有现成接口开发成本比 React Native 或 Flutter 低不少。而且小程序的发布审核并不会卡常规分类的二手交易类目只要避免出现明显的平台化交易嫌疑比如不要内置支付通道审核就不会有太大问题。整套系统采用前后端分离小程序端只负责界面交互和请求发送后端只负责业务逻辑、数据校验和接口响应。这样做的直接好处是后续如果要加一个管理后台或者把小程序端换成 H5完全不用动后端核心逻辑。1.3 模块拆分与数据流方向在画 ER 图之前我先按业务角色把系统拆成了几个模块用户模块、商品模块、订单模块、收藏与留言模块、图片存储模块。每个模块的职责尽量单一模块之间只通过接口或者数据表外键产生关联。数据流的方向是这样的小程序端向 Spring Boot 后端发起 HTTP 请求后端通过拦截器统一处理 Token 鉴权Controller 层接收参数并做基础校验Service 层处理业务逻辑Mapper 层通过 MyBatis 与 MySQL 交互。图片上传走独立的接口先落盘到服务器存储目录再返回 URL。所有接口统一返回 JSON格式是{ code, message, data }小程序端对这个结构做统一解析和处理。这个数据流听起来简单但实际操作中如果不提前把统一返回结构定死前后端联调时会有无穷无尽的体力消耗。我强烈建议在项目一开始就定好Result包装类和错误码规范避免一个接口返回true、另一个接口返回success联调时全靠猜。2. 后端核心实现表结构、登录态与交易状态机2.1 数据库设计要把业务演变想清楚数据库是这种业务系统的地基表结构设计得不好后面写接口会到处别扭。我最终设计的核心表如下user用户表字段包括id、openid、nickname、avatar_url、phone、campus、create_time。product商品表id、user_id、title、description、price、original_price、condition_level、category_id、status、view_count、create_time、update_time。product_image商品图片表id、product_id、image_url、sort_order。category分类表id、name、icon。order订单表id、order_no、product_id、buyer_id、seller_id、amount、status、create_time、update_time。favorite收藏表id、user_id、product_id、create_time。message留言表id、product_id、from_user_id、to_user_id、content、create_time。这里有一个关键点product表里的status字段我用了0-在售、1-已售出、2-下架而不是直接删除商品。二手平台最忌讳真正删除商品记录因为订单和留言都外链到商品一旦删了历史记录就全断了管理后台也无法追溯。用户“删除”自己的商品时实际执行的是软删除也就是只改状态不删行。价格字段要特别注意。单价用DECIMAL(10, 2)而不是FLOAT因为浮点数在计算时会有精度误差订单金额、广告位价格这类数据一旦出错就是事故。我见过有人用浮点存价格导致对账少了几块钱的例子校园项目虽然量小但该严谨的地方不能松。索引方面product表的status和create_time要建联合索引因为首页列表、分类商品列表基本都按“在售并且时间倒序”来查。order表的buyer_id和seller_id分别建索引用于个人中心的“我买到的/我卖出的”列表查询。如果数据量到几十万级别这些索引会极大缓解慢查询不过现阶段规模下没必要做分库分表。2.2 微信登录与 Token 鉴权的完整链路微信小程序的登录是典型的 code2session 换 openid 流程很多新手在这里被绕晕。我整理一下实际代码里的顺序小程序端调用wx.login()拿到临时凭证code。小程序把code发给后端/api/auth/login。后端拿着code调用微信接口code2session换取openid和session_key。后端根据openid查询用户如果没有则自动注册一个新用户并生成默认昵称和头像。后端为用户签发一个自定义的 Token返回给小程序小程序存入wx.setStorageSync后续每个请求的头里带上Authorization。Token 方案我选择的是自定义 Token 存 Redis而不是 JWT。原因很朴素JWT 是无状态的用户被封禁或改头像之后已经签发的 Token 仍然有效想踢人下线只能等过期。校园平台虽然不需要太强管控但如果以后要做管理员封号功能存 Redis 的 Token 能实现即时失效灵活得多。后端用拦截器统一处理 Token 解析。拦截器里从请求头拿出Authorization从 Redis 查用户信息查到了就往ThreadLocal里塞当前用户 IDController 层就可以直接用注解拿到当前登录用户。查不到就返回401。注意code2session的接口有每天调用上限虽然日常项目不会触发但一定不要把code写在日志里。这个临时凭证和session_key一旦泄露存在被伪造请求的风险。2.3 商品发布、图片上传与列表查询商品发布接口是核心中的核心逻辑上分两部分先处理图片再写入商品信息。图片上传我用的方式是小程序端wx.chooseMedia选择图片通过wx.uploadFile以multipart/form-data形式传到后端/api/upload。后端接收文件后先校验文件大小、扩展名只允许 jpg/png/webp单张不超过 5MB然后按日期存储到本地目录生成带时间戳的随机文件名避免重名覆盖。文件名我习惯用yyyyMMddHHmmss 随机数的格式比如20250605153021_8291a.jpg这样即使用户用一个叫“1.jpg”的文件反复上传也不会撞车。上传成功后后端返回 URL 列表小程序端再把 URL 和商品表单内容一起提交到发布接口。这里有一个常见的坑有些开发者会在上传接口里顺便创建商品记录结果用户传了图但没填标题就离开了数据库里就会多出一堆脏数据。正确做法是上传仅返回地址发布接口才真正落库商品表里维护一个product_image子表来关联多个图片 URL。商品列表查询要考虑过滤条件按分类、按价格区间、按关键词搜索。关键词搜索我用的是LIKE %keyword%匹配标题和描述数据量小时完全够用。如果以后商品量暴增再升级到全文检索也不迟。2.4 订单状态机让买卖双方都有明确预期二手交易系统的订单状态不能像电商那样复杂但也不能完全没有。我设计了五个状态待付款、待发货、待收货、已完成、已取消。当然校园二手很大程度是线下当面交易所以“待发货”和“待收货”在实际场景里可以解释成“约定时间地点”和“线下交易完成”。订单的生成要防重复。同一商品同一时间只能有一个买家发起交易后端在生成订单前必须查一次商品状态并且用数据库的乐观锁或UPDATE ... WHERE status 在售的原子更新来抢占商品。我最终选了UPDATE product SET status 1 WHERE id ? AND status 0这种方式如果更新影响行数为 0说明商品已经被别人抢占了直接返回“商品已售出”。这个设计在并发量不高时完全够用而且避免了先查再更新的时间差问题。我实测过用 Postman 并发打 20 个同样的购买请求只有 1 个成功其余全部正确拦截。如果将来并发量变大可以换成 Redis 分布式锁但目前没必要过度设计。确认收货之后买卖双方都可以看到订单的完成状态。如果有一方超过约定时间没有操作可以在列表里提供“提醒对方”的功能通过站内信或模板消息触发。注意这里我没有接入真实支付和自动发货逻辑因为校园二手以面交为主接入支付反而会把项目变得复杂且审核风险升高。2.5 留言与收藏增强购买意向的表达留言功能本质是提供一个轻量的商品沟通渠道。它的字段只有product_id、from_user_id、to_user_id、content、create_time。查询时按商品ID倒序取最新 20 条不需要做网络通信因为校内当面沟通更高效。这里要注意防止 XSS用户输入的留言内容在存储和展示前都要做、、、、等字符的转义处理。收藏功能则是给买家一个“先存下来再慢慢考虑”的入口。收藏表用user_id product_id做联合唯一索引确保同一个人不能重复收藏同一商品。收藏数量不用实时统计到商品表可以在需要时COUNT(*)查出来展示。这也是性能和简洁之间的平衡。3. 小程序端页面设计、请求封装与表单校验3.1 整体页面结构与导航设计小程序端我采用了五个 Tab 的结构首页、分类、发布、消息、我的。这个结构对二手交易类应用来说比较成熟用户进入后第一眼看到的是商品流发布按钮固定在显眼位置消息中心承载留言和订单通知我的页面展示个人数据和功能入口。首页商品流用双列瀑布流展示在售商品每张卡片包含主图、标题、价格、成色标签。瀑布流要注意图片比例问题不同卖家上传的图片比例可能不同我在后端返回图片URL时额外给每个URL附加了宽高比字段小程序端用modeaspectFill统一裁切视觉上整齐很多。很多新手忽略这个细节结果首页有的卡片高有的卡片矮显得很凌乱。分类页则按预设的分类展示商品左侧分类栏、右侧商品列表点击分类后请求对应接口并置顶刷新。分类数据来自数据库不写死在前端这样以后加分类不用发新版小程序。3.2 发布功能的交互与校验发布页是体现项目完成度的试金石。我参考闲鱼的发布交互顶部选择商品分类中间是标题输入、描述输入、价格输入、成色选择下面是九宫格图片上传。图片上传区域在用户选择图片后立即上传到服务器但商品本身要等用户点击“发布”才创建。表单校验要前置到前端。标题必填且限制 20 字以内价格必须为正数且最多两位小数成色必须选择一种。前端校验过了后端接口还会再校验一遍双重校验可以避免绕过客户端直接调接口产生的脏数据。价格输入的实现上我用typedigit让键盘直接弹出数字键盘再配合maxlength6限制输入长度。发布成功的提示我用了一个轻量 toast然后跳转到“我的发布”列表用户能立刻看到自己刚发布的商品。如果跳回首页因为列表页没有刷新的概念用户会以为发布失败了这是产品交互上的细节影响体验非常大。3.3 请求统一封装与登录态管理小程序端最需要规划的就是网络请求。我在utils/request.js里统一封装了wx.request每次请求前自动从本地存储读取 Token放进Authorization请求头每次响应先判断 HTTP 状态码再判断业务码code如果业务码是401就自动跳转登录页。这个封装让业务代码里完全不需要写wx.request的细节页面里只要写request({ url: /api/product/list, method: GET })就能拿到数据。回调地狱的问题也通过 Promise 解决了异步逻辑用async/await写起来非常顺。注意小程序真机调试时一定要在微信公众平台配置 request 合法域名并且域名必须备案且支持 HTTPS。开发阶段可以在开发者工具里勾选“不校验合法域名”但上线前必须改回来否则正式版会白屏。这个坑我踩过一次排查了很久才意识到是域名没有加进白名单。3.4 商品详情、订单流程与消息提醒商品详情页除了展示图片和价格还要展示卖家的信息比如昵称、头像、校区以及“联系买家”和“立即购买”两个操作按钮。联系买家走的是留言或引导复制微信号考虑到项目审核我没有做内置聊天而是用留言板加复制联系方式的组合。订单流程在小程序端的表现是买家点击“立即购买”后弹窗确认价格确认后调用后端下单接口卖家在“我卖出的”列表里看到新订单可以点击“约定的线下交易已完成”订单进入待收货状态买家收到货后确认订单完成。每一步操作后端都会更新状态并校验当前用户是否是订单的买方或卖方防止越权操作。消息中心展示两类内容最新留言和订单状态变更。数据来自后端聚合查询将留言记录和订单状态变更记录合并按时间排序。这里要注意分页我用了简单的“下拉加载下一页”模式每页 10 条避免一次性拉取太多数据导致页面卡顿。4. 部署上线与问题排查把项目真正跑起来4.1 从本地到服务器的部署流程这个项目是标准的单体 Spring Boot 应用部署不复杂但步骤要踩对。我用的方案是Maven 打包成可执行的 jar通过 scp 上传到服务器再用nohup java -jar启动配合 Nginx 做反向代理和 HTTPS 终结。打包时要注意 profile 的切换。开发环境连本地 MySQL生产环境要连服务器上的 MySQL数据库地址和密码不能写死。我的做法是 Spring Boot 的多环境配置application-dev.yml和application-prod.yml打包时通过--spring.profiles.activeprod指定环境。不然每次部署都要改配置文件改漏一次就等着联调翻车。Nginx 部分要配置两个转发一是把/api/开头的请求转发到 Spring Boot 的8080端口二是把静态图片目录映射到一个对外可访问的 URL比如http://你的域名/upload/指向服务器上的上传目录。HTTPS 证书用免费的证书服务配置好 SSL 后微信小程序端才能正常发起请求。4.2 常见问题与排查速查表这一部分是我实际开发中遇到的典型问题整理成表格方便快速定位现象可能原因解决方案小程序请求全部失败真机上白屏request 合法域名未配置或域名未备案在微信公众平台配置合法域名使用 HTTPS登录后用户信息不显示Token 解析失败或 Redis 中 Key 过期检查拦截器逻辑确认 Redis 是否有数据Token 有效期设为 7 天图片上传成功但商品页图片裂开图片 URL 用了相对路径小程序无法访问返回绝对 URL或在前端拼接完整域名同一个商品被两个人同时下单成功缺少商品状态原子更新使用UPDATE product SET status 1 WHERE id ? AND status 0发布商品列表偶发重复数据页面下拉刷新与上拉加载事件冲突用 loading 锁防止并发请求价格出现 0.30000000000000004数据库或 JSON 用了浮点数数据库用 DECIMALJava 用 BigDecimal4.3 安全和性能的几个硬性原则安全方面有几条我始终坚持的底线。第一是越权防护查询订单和操作商品前必须校验当前登录用户与资源所有者是否为同一人不能只依赖前端隐藏按钮接口层面必须做拦截。第二是参数校验不仅依赖前端后端 Controller 层要用Valid或手动校验所有关键参数包括分页的page、size不能为负排序字段用白名单。第三是文件上传的安全图片类型要校验真实文件头而不是只看扩展名恶意脚本文件不能进服务器。性能方面现阶段业务量不大最值得关注的是 SQL 查询效率和图片体积。列表查询不要SELECT *只需要返回卡片需要的字段图片上传前在小程序端先压缩一遍可以极大减少存储空间和加载带宽。我在项目里用wx.compressImage把图片统一压到最大宽度 800 像素保证清晰度的同时体积控制在 200KB 以内首页加载速度明显更快。5. 做完整个项目的体会与后续扩展思路5.1 几个亲测有效的开发心得这个项目完整走下来我最大的体会是业务流程的完整度比技术花活重要得多。很多二手交易平台的项目案例只做了商品 CRUD根本没有订单状态、没有交易冲突处理、没有越权校验看起来功能挺多但本质是管理系统而不是交易系统。做这类项目先画状态图先定义好订单的生命周期再动代码能省掉后期大量返工。第二个体会是日志和联调工具要早用早熟。我在开发阶段就养成了每次接口调用都打印请求参数和响应结果的习惯而且用 Postman 或者 Apifox 管理接口文档联调时双方都对着同一份文档争论少很多。如果你自己同时负责前后端也要注意别因为“自己心里有数”就不写接口文档两周后再看自己都容易遗忘字段含义。第三小程序端的错误提示要友好。500 错误时不能直接弹“服务器内部错误”这种摸不着头脑的话。我封装请求时对code做了映射例如“商品已售出”对应提示“手慢了一步商品已经被买走了”“401”对应“登录已过期请重新进入”。好的错误提示能把用户从困惑里拉回来不怎么增加开发成本但体验提升非常明显。5.2 这个项目还能往哪些方向扩展如果这个项目要继续迭代我最建议加一个管理后台因为运营校园二手平台总得有人处理违规商品和用户纠纷。管理后台可以复用后端的接口单独做一套 Web 前端用 Vue 或 React 都行核心功能包括商品审核、用户管理、订单数据看板。第二个优先级是对消息通知的完善。现在消息中心的实现是被动拉取用户离开小程序后就不知道自己的商品有没有被留言。接入微信的订阅消息后可以在被留言、订单状态变更时推送模板消息用户留存率会高很多。这里注意订阅消息的模板要提前申请而且推送是一次性的需要在用户触发操作时向后端传递授权结果。再往后可以考虑做价格参考和信用体系。价格参考可以从历史成交记录里算同品类均价帮买卖双方定一个公平价格信用体系可以来自用户完成订单数量和实名认证不用太复杂一个信誉分字段加上最近 30 天成交记录就够了。这些扩展不会改变核心交易链路但能把系统的可用性往上拉一个台阶。5.3 最后分享一点我踩过的坑最后说一个我印象最深的坑开发初期我直接用了懒加载的图片组件wx.lazy-load结果首页瀑布流的图片在快速滑动时经常出现空白占位用户以为页面出 bug 了。调试了很久发现是图片 URL 不带宽高信息组件无法预知尺寸。我最终的处理是后端返回图片时顺带返回宽高比前端拿到比例后立即布局再配合懒加载这个问题就消失了。类似这样的细节还有很多比如订单号不能只靠时间戳、发布按钮在 Tab 中间不能是普通页面跳转、退出登录必须清掉本地存储和缓存。这些都是文档里不会写、但真要做产品时躲不过去的细节。希望这篇内容能帮你少走几步弯路把精力花在真正有意义的功能和体验上。