多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

校园二手交易系统毕设全攻略:Spring Boot+安卓+智能推荐

校园二手交易系统毕设全攻略:Spring Boot+安卓+智能推荐 每年毕业季总能遇到一类提问“学长我想做校园二手交易平台用什么技术最合适”“Java写这个会不会太老”说实话校园二手交易这题目确实被写烂了但绝大多数人交上来的东西还停留在“商品发布浏览联系买家”的演示层面既没有完整的交易闭环也谈不上“智能”。如果你打算拿它当毕设又刚好想把分数往上抬一抬那这篇东西就是冲着你来的。我会从需求分析、技术选型、数据库设计、核心接口、智能推荐、安卓端实现一路讲到测试答辩里面的图和代码都是我自己在类似项目里验证过、能直接抄的近路。1. 这个经典选题背后的真实需求与常见误区1.1 校园二手市场为什么绕不开“管理”而不是“交易”很多学生一上来就把这个题目理解为“淘宝校园版”这恰恰是最容易跑偏的地方。校园二手交易的痛点从来不是“没有平台”而是人与人之间的信任成本和信息匹配效率。你的项目叫“校园二手智能交易管理系统”核心词不只是“交易”还有“管理”——管理用户身份、管理商品生命周期、管理订单状态、管理双方评价与纠纷。如果只做商品发布和留言那连课设都勉强。我建议你把这个系统拆成四条业务线用户管理学生认证、校级范围限定、信用档案、举报与封禁记录。商品管理发布、修改、上下架、审核如果有后台、类别与标签、图片。订单交易管理发起订单、双方确认、交付状态流转、完成/取消。智能辅助推荐匹配、价格参考、信用评分、热门/闲置时间提醒。答辩时最亮眼的部分是第四条。哪怕算法简单到只是“基于同学院同分类关键词相似度”名字叫“智能推荐”也比做成普通列表更有竞争力。后面第4章我会直接给出一种不需要机器学习框架也能跑的落地方案。1.2 不要一上来写“智能推荐”先把事务边界画清楚不少同学喜欢先研究协同过滤算法再把算法往半成品系统里硬塞最后代码和业务互不搭界。正确的做法是先把商品和订单的完整生命周期跑通再给某个环节加入算法或规则。整个项目分三层去思考数据层哪些表会频繁读写、哪些数据需要事务保护库存、订单状态、余额。业务层下单时同时扣减商品状态和创建订单必须在一个事务里完成。智能层只读数据、计算结果、返回推荐列表或参考价不能反向修改核心业务数据。这样做的好处是即使推荐逻辑拍脑袋写的也不会把交易搞崩。我在强调架构的同时也在教身边学弟们怎么避免那种“代码很多但别人看不懂”的自嗨型毕设。2. 技术选型安卓端与后端的一整套取舍思路2.1 后端为什么选Spring Boot而不是直接写Servlet/JSP题目里带“Java”最稳妥、也最容易答辩通过的组合是后端用Spring Boot MyBatis Plus MySQL客户端用原生AndroidJava实现。除非你面试方向明确要做Vue否则别给自己加戏。选Spring Boot的理由不是“现在流行”这种空洞的话而是它帮你解决三个毕设级别的痛点嵌入式Tomcat一个jar包直接跑部署到服务器上不用额外配Tomcat演示环境迁移成本极低。starter依赖体系Web、AOP、Validator、Redis、测试这些模块通过坐标引入不用自己拼一大份XML。与MyBatis Plus配合单表CRUD几乎不用写SQL写复杂查询时再自己补注解SQL。下面是一份可以直接用的核心依赖版本按你的环境自行调整dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency2.2 数据库选型与连接配置的注意点MySQL 8.0以上在数据库连接串里必须显式指定时区否则部署到非中国区的云服务器后时间会差8小时。这是我给一个学弟查了两天的坑先列在这里datasource: url: jdbc:mysql://localhost:3306/campus_secondhand?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue username: root password: your_passwordMyBatis Plus的驼峰映射默认开着所以你的Java实体类里写createTime数据库字段写create_time它能自动对映不用每个字段都加TableField。要注意的是逻辑删除建议在商品表和订单表加一个deleted字段并用TableLogic这样所有默认查询都会自动带上deleted 0条件。这个设计在答辩时很好讲“所有删除都是软删除为了保留完整的交易追溯链。”2.3 安卓端是原生还是跨端我的建议说句实在话如果只是为了毕设演示原生Android Java代码就够了。理由如下题目里明晃晃写着“APP”答辩时你需要真机或模拟器现场操作原生应用启动快、少一层框架转译少一个崩溃理由。RecyclerView Glide OkHttp这套组合足够完成列表、图片加载、网络请求。抗辩时你可以说“客户端使用了Material Design组件规范”比说“我用了一个封装好的低代码平台”要硬气。如果切换成Flutter或React Native除非你已经很熟否则不建议在毕设周期内冒险。很多人忽视了一个点安卓模拟器和后端在同一台电脑上时测试环境地址要写10.0.2.2而不是localhostReal机器用局域网IP时还需要在AndroidManifest里允许usesCleartextTraffic否则走不了HTTP明文请求。这个坑我在后面专门列了一条。3. 数据库模型与核心接口从一张订单到一套流程3.1 核心表结构别贪多五张表支撑闭环建议不要设计二三十张表来显示工作量那只会让答辩提问漏出马脚。下面五张表加一个中间表就足够撑起全套业务表名核心字段说明userid, student_no, nickname, avatar, credit_score, campus学生认证与信用分categoryid, name, parent_id分类树一般两级productid, user_id, category_id, title, description, price, trade_status, images, view_counttrade_status枚举0在售、1锁定、2已售、3下架trade_orderid, product_id, seller_id, buyer_id, status, type(闲置/求购), create_timestatus0待双方确认、1进行中、2已完成、3已取消、4纠纷commentid, order_id, user_id, score, content交易完成后双向评价favoriteid, user_id, product_id, create_time收藏也是推荐算法的输入为什么订单表里要保留buyer_id和seller_id各一份因为“买”和“卖”是两个角色一条订单必须能关联两本账。交易状态机必须在后端控制不能由客户端自由跳转。我用枚举常量管理状态不再用乱糟糟的魔法数字public enum TradeStatus { ON_SALE(0, 在售), LOCKED(1, 交易锁定), SOLD(2, 已售出), OFF_SHELF(3, 已下架); private final int code; private final String desc; // 构造方法、getter... }3.2 发布、浏览、下单三个接口要讲清楚“为什么这么设计”发布商品客户端上传图片走独立接口先拿到图片URL再提交商品信息。好处是就算商品信息填写失败图片不会反复传。图片服务器可以直接用本地文件存储也可以接入云OSS毕设用本地路径加上一个Nginx反向代理就够。PostMapping(/product) public ResultVoid createProduct(RequestBody ProductDTO dto, RequestAttribute(userId) Long userId) { if (!campusVerified(userId)) { return Result.error(403, 未通过校园认证); } productService.create(dto, userId); return Result.success(); }浏览商品列表不要一次性select *返回全表。分页是基本功排序规则要显式声明。我常用的是“SORT HOT / NEW / PRICE_ASC / PRICE_DESC”四选一每个都对应一个明确SQL条件。LambdaQueryWrapperProduct wrapper new LambdaQueryWrapper(); wrapper.eq(Product::getTradeStatus, TradeStatus.ON_SALE.getCode()) .eq(Product::getDeleted, 0) .orderByDesc(Product::getCreateTime); page(product, wrapper);下单这是整个系统里最需要事务保护的地方。要点是用SELECT ... FOR UPDATE锁住商品行再检查状态是否为在售是则置为锁定并创建订单。如果没锁行两个用户同一秒同时下单就会出现超卖。Transactional public boolean createOrder(Long productId, Long buyerId) { Product product productMapper.selectByIdForUpdate(productId); if (product null || product.getTradeStatus() ! TradeStatus.ON_SALE.getCode()) { throw new BizException(商品不存在或已被下单); } int updated productMapper.updateTradeStatus(productId, TradeStatus.ON_SALE.getCode(), TradeStatus.LOCKED.getCode()); if (updated 0) { throw new BizException(手慢了商品已被锁定); } orderMapper.insert(buildOrder(product, buyerId)); return true; }3.3 为什么引入Redis而不只靠MySQLRedis在这里至少干三件事首页商品缓存热门列表一次性放入Redis过期时间5分钟扛住并发浏览。简单分布式会话登录后用UUID生成token以userId为key存入Redis客户端每次请求带token。比传统HttpSession更适合多端。防重复提交下单时用“商品ID 用户ID”作为Redis key加上setIfAbsent做幂等重复点击同一商品时直接拦截。毕设答辩时这一层非常加分因为能说明你考虑了性能和并发控制。缓存与数据库一致性问题不用展开太深只要保证商品状态变更时主动删除对应缓存就能规避绝大部分脏读。4. “智能交易”到底怎么落地推荐、搜索与信用评分4.1 不带机器学习框架的推荐方案标签向量 最近邻这是整个项目最容易吹嘘也最容易翻车的地方。我的经验是别用Spark、别用PyTorch就把服务端Java写个轻量计算器为每个商品打N个标签例如“二手”“教材”“考研”“电子”“九成新”。用户的历史行为收藏、发布、成交也能收集成标签向量。计算当前用户向量与待推荐商品向量的余弦相似度取Top10。向量存在MySQL里用临时计算数据量几千条毫无压力。核心代码很短double cosineScore(ListDouble userVec, ListDouble itemVec) { double dot 0, uNorm 0, iNorm 0; for (int i 0; i userVec.size(); i) { dot userVec.get(i) * itemVec.get(i); uNorm userVec.get(i) * userVec.get(i); iNorm itemVec.get(i) * itemVec.get(i); } return dot / (Math.sqrt(uNorm) * Math.sqrt(iNorm) 1e-6); }如果时间充裕可以再加一条改进从favorite表里找“和你收藏同一件商品的人还收藏了哪些商品”做一个基于物品的协同过滤。这条逻辑编程不难但是答辩时的“智能含量”直接提升一个档次。4.2 信用评分把“勤劳用户”从“放鸽子用户”里区分出来校园二手的关键问题是交易双方不一定认识需要在系统层面建立信任。信用分建议按加权公式计算初始分100。完成一单并收到好评加2分上限150。超时未履约被投诉扣10分。累计3次未按时交付禁止发布商品14天。public void afterTradeCompleted(TradeOrder order) { int delta 2; if (order.getCommentScore() ! null order.getCommentScore() 3) { delta -10; } userService.changeCredit(order.getSellerId(), delta); }分数、徽章和权限三者联动才是评分系统的意义。答辩时很多老师会问“凭什么扣分”你只要回答“每次扣分都有对应的评价或举报记录支撑”即可。建议为每个订单维护一条trade_log流水表写清是“卖家延迟发货”还是“买家未按约定取件”申诉界面也能给证据。4.3 搜索排序不只有SQL的LIKE如果商品标题像“九成新高数教材考研英语”你用LIKE %考研%查没问题但如果用户只搜“数学”你就得考虑同义词关联。最简单的增强是建一个keyword_alias表把“数学”“高数”“数学分析”映射到同一组词条。搜索时先查别名再查标题和描述。排序权重我建议这样算排序分 关键词命中标题权重3 命中描述权重1 商品发布时间新鲜度衰减分时间衰减可以用exp(-hours/72)之类的指数也可以直接用“发布三天内的商品加固定权值”简单实用。这块和机器学习区分开你在论文里写“基于规则的多因子排序模型”导师不会觉得你在水。5. 安卓APP端的关键实现细节与踩坑记录5.1 网络层用Retrofit还是OkHttp原生Android比较合理的组合是Retrofit 2 OkHttp Gson。不要把业务代码写死在Activity里建议做成三层API接口层一个Service接口定义所有请求。Repository仓库层管理数据来源网络/缓存。ViewModel层通过LiveData通知UI刷新。Retrofit定义示例public interface SecondHandApi { POST(api/auth/login) CallResultLoginVO login(Body LoginDTO dto); GET(api/product/recommend) CallResultListProductVO recommend(Query(page) int page, Query(size) int size); }登录后token要保存到SharedPreferences同时通过OkHttp的Interceptor在每个请求的Header里追加Authorization。切记token不要放明文日志里否则打印日志时会被同学看到。5.2 图片上传与压缩一个必须处理的体积问题手机原图动不动5MB如果直接传给后端既慢又占存储联调和面试演示时还会把WiFi跑满。解决方式很常规Bitmap按最大边不超过1280像素缩放。压缩质量设为85%转成JPEG。直接在后台把byte[]上传到后端后端再用MultipartFile接收。安卓端压缩代码private File compressImage(File file) { Bitmap bmp BitmapFactory.decodeFile(file.getAbsolutePath()); int maxWidth 1280; if (bmp.getWidth() maxWidth) { float ratio maxWidth / (float) bmp.getWidth(); bmp Bitmap.createScaledBitmap(bmp, maxWidth, (int)(bmp.getHeight() * ratio), true); } File outFile new File(getCacheDir(), compressed_ file.getName()); try (FileOutputStream fos new FileOutputStream(outFile)) { bmp.compress(Bitmap.CompressFormat.JPEG, 85, fos); } bmp.recycle(); return outFile; }5.3 容易让项目当场翻车的三个坑明文HTTP被拦截Android 9开始默认禁止明文流量真机调试接口如果返回CLEARTEXT communication not permitted在AndroidManifest.xml的application节点加上android:usesCleartextTraffictrue。网络操作放在主线程跨进程访问后端必须放到子线程用enqueue回调或RxJava直接在onCreate里同步请求会抛出NetworkOnMainThreadException。RecyclerView复用导致图片闪烁Glide加载图片时一定要override固定宽高并提供placeholder否则列表滚动时会出现旧图闪一下再变新图的现象。5.4 “管理系统”的另一个形态后台管理端标题里的“管理系统”不一定非得做成Web后台但建议你为管理员做一个Web管理页面或再补一个简易Web管理模块。功能就三块用户列表、商品审核、举报处理。技术上用Thymeleaf加简单后台模板就够了服务端共用同一套Service层。这个后台不是应付差事而是证明你有“系统级”思维前端用户、后台管理、服务端API能构成一枚完整的项目架构。答辩演示时直接在后台把某个违规商品下架然后切到APP刷新看变化这套联动演示比单独介绍功能截图要生动得多。6. 项目测试、打包与毕业答辩准备6.1 后台功能测试不只测“能点通”不要只写“打开APP点按钮能显示列表”就完事。毕业设计测试要体现边界情况和数据一致性。我的建议是把核心流程写成一套测试用例表编号场景前置条件操作预期结果TC01正常登录已注册学生用户输入账号密码登录成功返回tokenTC02密码错误已注册用户输入错误密码返回401提示重新输入TC03发布商品缺图登录状态无图片直接提交返回参数校验错误TC04并发下单同一商品两个不同用户同时点击购买只有一个成功TC05交易完成订单状态进行中双方确认完成商品变为已售出双方信用分变动TC06未认证用户发布游客登录点发布按钮跳转认证页面/拒绝这六条用例足以覆盖主干链路论文里写“功能测试模块覆盖率达到XX%”时至少手里有真材实料。Spring Boot自带spring-boot-starter-test可以顺手写两个针对Service的Transactional单元测试让代码覆盖率报告更好看。6.2 打包部署的最后一公里后端建议用Maven打包成一个jar部署到云服务器时用nohup java -jar xxx.jar --spring.profiles.activeprod 启动。配置文件要区分application-dev.yml和application-prod.yml数据库密码、Redis密码不要硬编码放到环境变量或JVM启动参数里。安卓端打包正式签名APK时记得保证minifyEnabled不要随便开shrinkResources毕设项目开启混淆很容易把Retrofit的接口类搞挂。演示前检查一遍签名包能正常安装别到答辩现场才发现debug包没法覆盖安装。6.3 答辩演示脚本两分钟讲清一个核心设计演示不需要完整走一遍所有功能但要按业务流程一口气串下来。我的建议顺序是登录 → 首页推荐列表点这一点要强调“这里的商品是根据谁收藏了什么算的推荐”。发布一个商品并上传图片强调压缩和图片服务设计。换一个账号模拟买家搜索、收藏、下单强调事务锁与防重复提交。双方确认完成展示信用分变化强调事务日志。打开后台管理端下架一个违规商品并回APP验证强调软删除与状态联动。老师后续最可能问的问题是“如果用户量到一万这个系统哪里是瓶颈”你不用答得很玄就说“商品表和订单表需要加索引图片建议迁到对象存储推荐计算可以改离线预计算”。这几句话一出口你已经甩开一堆背书选手。7. 我从这个项目里总结的几条实操经验项目做完回头审视我最想强调的一点是不要为了“智能”两个字硬上高深算法。校园二手交易的数据量就几千条真正的复杂性在交易状态和信任机制里。把“商品锁定状态机”“信用分与权益联动”“基于用户行为的相似度推荐”这三件事做扎实即便技术上很朴素也远比光是套公式更打动人。另外毕设不是软件工程毕业论文没必要把架构搞得飞起。如果你能在一个月内把“发布-浏览-下单-完成-评价”的闭环跑通再花两周把推荐和后台管理补上后面基本就是查漏补缺的事了。写论文时尽量别用“使用了大数据分析技术”这种词改用“基于用户行为的轻量级推荐策略”这种准确表述评审老师反而觉得你认知清醒。最后分享一个让项目更像“作品”的小技巧给商品图片加水印、给APP启动页加一个学校名字的定制化入口、在用户协议里写明“仅限校园内部师生使用”。这些小细节不会变成论文得分点但能让你在演示时从“这可能是外包”变成“这是认真做的”。做毕设说到底是在做一件能证明你完整开发能力的作品请把它当成你未来简历里的第一份项目经验来对待。
返回列表