每年毕业季,图书馆和宿舍楼下总能看到一堆堆被遗弃的教材、台灯、自行车。其实这些东西里很多都还有使用价值,但缺乏一个靠谱的流转渠道。我前两年带学生做课设时,恰好有小组选了这个题目——Java实现的校园旧物交易系统,当时跟着他们把整个项目从需求分析、数据库设计到接口联调、论文撰写完整走了一遍。这篇就结合当时的实践经验,从技术选型、核心模块、关键代码到论文写法,系统拆解一下这类校园旧物交易系统到底该怎么做。
这套系统最核心的价值在于解决校园内闲置物品信息不对称的问题。和闲鱼这类通用平台相比,校园场景的核心用户是学生,范围明确、身份可信、交易半径小,所以系统在功能设计上应当围绕“实名可信、当面交易、轻量发布”三个关键词展开。技术栈上,Java + Spring Boot + MySQL 依然是这类课设和论文项目最稳妥的组合,生态成熟、资料多、出问题也容易查。这篇文章适合准备做Java课程设计、毕业设计的在校学生,也适合刚入行想练手的初级开发者,我会把项目从零到一的完整链路讲清楚,包括代码层面的踩坑记录。
1. 项目定位与整体设计思路
1.1 校园旧物交易和普通二手电商的本质区别
先说需求分析。普通二手平台(比如闲鱼)解决的是陌生人之间的信任问题,所以需要芝麻信用、担保交易、物流跟踪、售后纠纷处理一大堆机制。校园场景不一样,交易双方大概率都在同一个校区,甚至可能是同一栋宿舍楼的,线下见面交易成本极低,快递费都省了。这意味着系统设计上可以砍掉物流模块、支付模块,把重心放在信息发布、检索、沟通约见这些环节。
我见过很多学生做这个题目时习惯性照搬电商系统的功能清单,什么购物车、库存管理、支付回调全往上堆,结果论文写得痛苦,代码bug满天飞,答辩时老师一问“你这个支付接口用的什么、资金流向怎么监管”就哑火。正确的做法是清醒地做减法:系统核心只有三个动作——发布闲置、发现商品、联系成交。围绕这三个动作展开,后续所有模块设计都会自然很多。
具体到角色和权限,系统至少要有三类角色:普通学生用户、管理员、游客。游客可以浏览商品列表和详情,但想要发布商品、收藏、留言,必须注册并完成学生身份认证(简单做法是学号+姓名匹配,或者上传校园卡照片由管理员审核)。管理员负责用户管理、商品审核(防止违规物品上架,比如违禁电器、假冒证件)、分类管理、公告发布。这个权限模型足够撑起论文的“系统管理”章节,又不至于复杂到难以实现。
1.2 技术选型:为什么是Spring Boot而不是其他
技术选型上,Java体系内当前最优解就是Spring Boot 2.x + MyBatis-Plus + MySQL 5.7/8.0,前端可以用Thymeleaf模板引擎,也可以前后端分离用Vue。我做课设指导时默认推荐单体架构 + Thymeleaf,理由很实在:这类项目时间紧、评审重点在业务逻辑和数据设计,前后端分离意味着要额外处理跨域、Token鉴权、接口文档,对新手是负担不是加分项。
Spring Boot最大的好处是自动装配和Starter机制,以前SSM时代要手写一大堆XML配置,现在几行依赖就搞定。这里提醒一个版本坑:创建项目时Spring Boot版本不要选最新版,建议用2.5.x或2.7.x,因为这些版本对应的MyBatis-Plus和Thymeleaf整合资料最多,遇到问题搜得到。3.x版本虽然新,但部分旧教程方案不兼容,容易让新手卡死在环境配置阶段。
数据库方面,MySQL就够了。Redis如果只是用来做缓存,没必要加——加了Redis论文确实能多写一章,但带来的问题(缓存一致性、序列化、部署复杂度)会占用大量调试时间。如果真的想体现技术深度,更建议在数据一致性和并发控制上下功夫(后面第四章详聊),这比堆一个Redis更有说服力。
1.3 模块拆分:六张表撑起一个完整闭环
整个系统的后端模块可以拆成六大块,对应数据库里六张核心表:用户表、商品表、分类表、留言/咨询表、收藏表、管理员操作日志表。用户表存账号密码、学号、昵称、头像、联系电话;商品表是核心业务表,包含标题、描述、图片、原价、期望售价、成色、交易地点、发布状态、浏览量;分类表做简单的一级分类,比如“教材教辅”“数码电子”“生活用品”“运动户外”“其他”;留言表用于买家在商品详情页提问或约看,收藏表记录用户关注;日志表则记录管理员的审核操作,论文里可以体现“可追溯性”这个安全设计点。
这样的模块划分,对应到后期的论文章节安排也很顺:第一章绪论、第二章需求分析、第三章概要设计、第四章详细设计、第五章系统实现、第六章系统测试,正好是本科毕业论文最经典的五章式框架。功能模块和章节一一对应后,写论文时几乎不用“编内容”,每一章都有真实的设计过程和代码可以写。
2. 核心业务细节与Java实现要点
2.1 用户认证与权限控制:从Session到拦截器
用户认证是我每次都要重点强调的部分。课设阶段没必要引入Spring Security(配置复杂、概念多,写进论文容易被追问),用Session + 拦截器(HandlerInterceptor)就能实现一套够用的认证授权机制。
具体实现思路:用户登录成功后,把user对象存入session;自定义一个拦截器,在preHandle方法中检查当前请求的session里有没有登录用户,没有就重定向到登录页;如果需要区分管理员权限,可以在session里再存一个role字段,在拦截器里判断role是否匹配。实际代码核心就二十行左右:
public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); User user = (User) session.getAttribute("loginUser"); if (user == null) { // 未登录,跳转登录页 或者返回JSON错误(接口场景) response.sendRedirect("/login"); return false; } if ("admin".equals(request.getParameter("role"))) { // 这里是简化写法,实际建议用路径前缀区分管理端 } return true; // 放行 } }拦截器在配置类里注册,注意要排除静态资源(css、js、图片)和登录、注册、首页等公开接口,否则会连登录页都进不去,这种低级错误在验收时很常见。另外,对于发布商品、删除商品这类写操作,后端controller里必须再次校验登录状态,不能只靠前端隐藏按钮——接口是可以被直接调用的。
2.2 商品发布流程与图片处理:对象拷贝和文件上传的坑
商品发布是整个系统里交互最复杂的模块,涉及表单数据绑定、图片上传、数据入库三个步骤。前端页面里,用户填写标题、描述、分类、价格、成色、交易地点,选择一个或多个图片文件。这里有个用户体检细节:图片要做压缩限制,常见做法是限制单张不超过2MB,总图片数不超过5张,格式只允许jpg/png/webp。
后端处理图片,稳定方案是存储到服务器本地磁盘指定目录,然后把相对路径存进数据库的goods表字段里,页面用Thymeleaf渲染时拼上绝对路径访问。实际开发时要注意,IDEA内置的Tomcat虚拟路径和部署环境不一致,最好在配置文件中单独定义上传路径,如file.upload-dir=/var/www/upload,然后用ResourceHandler对外暴露。否则本地调试好好的,打包部署到服务器后图片全部404。
上传代码里有个用得上Java语法细节的地方——MultipartFile转File时必须做对象拷贝。我当时指导的学生在写这部分时卡了很久,明明multipartFile.getOriginalFilename()能拿到文件名,转成File后内容却是空的,其实就是没有调用multipartFile.transferTo(destFile)方法,或者没有检查目标目录是否存在。这个步骤用一句话说清楚:
// 目录不存在则创建 File dir = new File(uploadDir + "/" + goodsId); if (!dir.exists()) { dir.mkdirs(); } // MultipartFile需要“转移”到本地文件 String fileName = UUID.randomUUID().toString().replaceAll("-", "") + ".jpg"; File dest = new File(dir, fileName); multipartFile.transferTo(dest);文件命名一定要用UUID,不要用原始文件名,否则两个用户同时上传名为“微信图片.jpg”的文件会相互覆盖,而且中文文件名在不同编码环境下容易乱码。
2.3 交易状态机:用Java枚举替代散乱的魔法数字
订单或商品状态的管理,是体现设计能力的关键点。很多初学者喜欢在代码里直接用int变量,值为1代表“在售”、2代表“已预约”、3代表“已售出”,然后散落在各个if判断里。这种做法短期能跑,但状态一旦多了就乱——比如一个商品可能有:审核中、在售、被预约、已下架、已售出、违规删除,六种状态。你永远不知道自己是不是漏了某个分支。
规范做法是用枚举类封装状态机和状态流转逻辑。这里也正好呼应了热词里的“java设计模式”和“面向对象编程java”——这个场景就是用一个状态枚举把业务规则内聚起来:
public enum GoodsStatus { PENDING(0, "审核中"), ON_SALE(1, "在售"), RESERVED(2, "被预约"), SOLD(3, "已售出"), OFF_SHELF(4, "已下架"), BANNED(5, "违规下架"); private final int code; private final String desc; // 构造方法、getter略 // 状态流转合法性判断 public boolean canTransitionTo(GoodsStatus target) { if (this == PENDING) return target == ON_SALE || target == BANNED; if (this == ON_SALE) return target == RESERVED || target == OFF_SHELF || target == SOLD; if (this == RESERVED) return target == SOLD || target == ON_SALE; if (this == SOLD) return false; // 终态 return target == ON_SALE; // OFF_SHELF和BANNED可重新上架(需审核) } }使用枚举后,商品表里状态字段可以直接存枚举的名字(varchar),也可以在转换器中映射成int。更重要的是,论文里可以直接画一张状态流转图,这是评委会觉得眼前一亮的技术亮点,代码里也可以声明性的表达业务规则。与此配套,前端页面的操作按钮(立即购买、取消预约、重新上架)可以基于当前状态动态渲染,避免用户提交非法操作。
2.4 数据一致性与并发:交易场景下的乐观锁实战
校园旧物交易系统有一个高频业务场景:一件商品被多人同时看到,同时发起下单。如果用最简单的“先查询状态再更新”写法,极大概率出现超卖——两个人同时看到商品在售,同时执行“update goods set status='已售出' where id=1”,结果两个订单都创建成功。
这个问题在答辩时被问到的概率非常高,因为它是典型的并发一致性问题,和热词中的“java怎么保证数据一致性”直接相关。解决方案有很多:悲观锁(select for update)、乐观锁(版本字段)、Redis分布式锁。我建议在论文和实现里用乐观锁,原因很实际:校园系统的并发量级根本到不了需要分布式锁的程度,乐观锁实现简单、不需要额外引入中间件,而且能自然引出“ABA问题”“SQL更新行数判断”这些可讲的点。
具体实现方案:在goods表加一个version字段,查询时取出,更新时带上version条件:
UPDATE goods SET status = 3, version = version + 1 WHERE id = #{goodsId} AND status = 1 AND version = #{oldVersion}在MyBatis-Plus里可以这样写:
@Override @Transactional public boolean buyGoods(Long goodsId, Long buyerId) { Goods goods = goodsMapper.selectById(goodsId); if (goods == null || goods.getStatus() != GoodsStatus.ON_SALE.getCode()) { throw new BusinessException("商品不存在或不在售状态"); } // 乐观锁更新,影响行数为0说明期间状态已经被改动 int rows = goodsMapper.updateStatusByVersion(goodsId, GoodsStatus.SOLD.getCode(), goods.getVersion()); if (rows == 0) { throw new BusinessException("手慢了,商品已被抢购"); } // 创建订单记录 Order order = new Order(); order.setGoodsId(goodsId); order.setBuyerId(buyerId); order.setSellerId(goods.getSellerId()); order.setStatus(1); orderMapper.insert(order); return true; }这里涉及的@Transactional要提两个注意点:事务要放在public方法上,且不能是同类内部调用,否则注解会失效;事务只能保证数据库操作的原子性,对于“先查后改”这种逻辑,必须配合乐观锁或悲观锁才能防超卖,单靠事务是不够的。
3. 实操过程与核心环节实现
3.1 数据库表设计:字段命名、索引与ER图写法
数据库设计是Java后端项目的骨架,也是论文里占篇幅最多的部分。我直接把核心表的设计思路拉出来说。
先看用户表(user),字段包括:id(自增主键,不去用学号做主键,因为学号是业务数据,可能变更)、username(登录名)、password(使用BCrypt加密存储,不要明文;课设阶段强行用MD5也能跑,但论文里被追问就露怯)、student_no、nickname、phone、avatar、role(0普通用户 1管理员)、status(0正常 1禁用)、create_time。注意所有时间字段统一用datetime类型,Java侧对应LocalDateTime,避免用java.util.Date,后者在格式化时容易出现时区问题。
商品表(goods)是重中之重,字段包括:goods_name、description(text类型)、price(decimal(10,2),不要用float/double,否则会出现0.1+0.2不等于0.3的精度问题)、original_price、category_id、images(用varchar存多个图片路径,以逗号分隔)、quality(成色描述)、location、contact_way、seller_id、status、view_count、version、create_time、update_time。
索引设计是新手最容易忽略的点。主键id自然有索引,除此之外建议给seller_id(用于展示“我发布的”)和status + create_time(用于首页商品列表排序)建索引。索引不要建多了,每个索引都是写入负担,对于课设级别,三五个索引足够。在论文中可以写清楚“根据主要的查询场景设计索引”,这句话比全文堆概念有价值得多。
3.2 实体类与Mapper层:MyBatis-Plus的CRUD与条件构造器
MyBatis-Plus在校园系统里的核心价值是内置了BaseMapper,提供selectById、insert、updateById等基本CRUD,不用像原生MyBatis那样为每个小操作写XML映射。实体类上直接用@TableName("goods")、@TableId(type = IdType.AUTO)、@TableField("create_time")这些注解,字段驼峰命名和数据库下划线自动映射。
最常用的是条件构造器QueryWrapper和LambdaQueryWrapper。举一个首页按分类和关键词搜索的例子:
LambdaQueryWrapper<Goods> wrapper = Wrappers.lambdaQuery(); wrapper.eq(Goods::getStatus, GoodsStatus.ON_SALE.getCode()) .eq(StringUtils.hasText(categoryId), Goods::getCategoryId, categoryId) .like(StringUtils.hasText(keyword), Goods::getGoodsName, keyword) .orderByDesc(Goods::getCreateTime); Page<Goods> page = goodsMapper.selectPage(new Page<>(pageNum, pageSize), wrapper);这里用到的LambdaQueryWrapper能避免硬编码字段名,重构时Java编译器会帮忙检查错误。顺带说一下分页:MyBatis-Plus分页需要配置PaginationInnerInterceptor插件,否则selectPage虽然不报错但实际是不分页的,这是必踩的坑。检查方法是看控制台SQL有没有“LIMIT”,没有就说明插件没配置成功。
对于多表连接查询(比如商品列表要显示卖家昵称),不建议在Mapper里写复杂join,最简单的方案是查出List后,用Java代码循环补全卖家信息。性能在校园系统量级下完全不是问题,还能简化SQL,让论文里的SQL不至于复杂到不好解释。更高级一点,可以用MyBatis-Plus的自定义SQL注解@Select写在Mapper接口里,适合单个查询的场景。
3.3 核心接口实现:发布、列表、下单三大链路
系统里三个最核心的业务接口值得结合代码串一遍。
第一个是商品发布接口。接收参数包括Goods对象以及上传的图片文件数组。处理流程:校验登录权限 → 校验参数完整性 → 保存goods记录 → 保存图片文件 → 更新goods的images字段 → 返回成功与商品id。这里有个实现顺序的讲究:先insert拿到goodsId,再用goodsId拼图片目录,避免图片文件夹和商品对应错乱。
第二个是商品列表分页接口。涉及两个重难点:图片路径要转换成完整可访问的URL(存储的是相对路径,比如/upload/12/xxx.jpg,展示时拼接域名);浏览量要按规则累加(简单的做法是详情接口里view_count = view_count + 1,不要试图精确统计,校园项目里显示个大概的数字就够了,这个取舍要写进论文的展望部分,反而加分)。
第三个是下单接口。我在2.4节已经给了核心代码,这里补充一个业务逻辑细节:下单成功后,除了订单表插记录、商品状态置为已售出,还要给卖家生成一条消息通知(可以简单存在message表里,也可以直接复用留言表加个type字段标识是“系统通知”)。这个设计既提升了用户体验,又能让代码里体现出“模块间协作”的能力。
我在实际指导中发现,很多学生的主力时间花在了猛写CRUD上,把登录、注册、增删改查做完就以为大功告成,结果后来答辩演示时一操作才发现:买家拍下后,卖家完全不知道自己的东西已经卖了。这种功能闭环上的疏漏比代码bug更致命,因为它暴露的是系统设计阶段没有走查业务流程。
3.4 定时任务:用@Scheduled实现订单超时关闭
校园交易有大量线下交易的成分,和线上电商不同,这里不存在“支付成功后发货”的概念,而是“买家发起约看/下单,卖家确认,双方线下见面交易”。那就会存在一个情况:买家预约了商品,但卖家迟迟没有确认,或者双方都没下文了,商品状态一直卡在“被预约”,影响商品流转效率。
解决方案是引入定时任务,每天凌晨把所有超过24小时仍处于“被预约”状态的商品自动释放回“在售”。Spring Boot的@Scheduled注解就能实现,核心代码:
@Component public class OrderTimeoutTask { @Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行 public void releaseExpiredReservedGoods() { // 找到所有被预约超过24小时的商品 LocalDateTime deadline = LocalDateTime.now().minusHours(24); List<Goods> goodsList = goodsMapper.selectExpiredReserved(deadline); for (Goods goods : goodsList) { // 需要加上业务校验,防止释放期间商品已被手动处理 goodsMapper.updateStatus(goods.getId(), GoodsStatus.ON_SALE.getCode(), GoodsStatus.RESERVED.getCode()); } } }注意@Scheduled默认是单线程串行执行,如果之后为了展示加了多个定时任务(比如每天清理过期公告、定期统计浏览量),别忘了配置ThreadPoolTaskScheduler,否则多个任务会互相阻塞。定时任务这块很值得写进论文的第五章“系统实现”,它是系统设计“考虑业务状态自流转”的最佳佐证。
4. 常见问题与排查技巧实录
4.1 环境配置篇:JDK安装与Maven依赖的经典报错
不少同学的第一个坎是JDK环境配置。标题的热词里也出现“java环境变量配置详细教程”“win11系统java环境配置”“java安装教程详细”,看来这个问题确实困惑了很多人。只说关键点:JDK8和JDK17都是当前课设的合理选择(Spring Boot 2.7支持到17,用Java 8也完全没问题),但两者都要求配置JAVA_HOME和PATH环境变量。Win11系统上配完之后,一定要在完全新开的命令行窗口里执行java -version验证,因为旧窗口不刷新环境变量,经常出现“明明配好了却提示不是内部或外部命令”。
Maven依赖拉不下来是另一个高频问题。原因绝大多数是网络问题,尤其是从Maven中央仓库下载时总超时。解决方法是换阿里云镜像地址,在maven安装目录的conf/settings.xml里配置<mirror>节点。这里补充一条冷门的排查技巧:IDEA里用的Maven不一定是本机装的哪个版本,在IDEA的Settings里检查Maven home path和settings文件路径,确保用的是自己配置过镜像的那个。
4.2 代码运行篇:数组越界、空指针和时区三座大山
日常编码中碰到的bug基本就是三类。数组越界通常是循环边界笔误或者前端传参为空,比如“java中数组越界异常”这个热词对应的场景。空指针则更喜欢在关联查询里出现,典型的例子是:用户没传头像,数据库里avatar是null,页面直接渲染user.getAvatar().startsWith("/upload")就是NullPointerException。养成好习惯:所有从数据库取出的对象都假设可能为null,用ObjectUtils.isEmpty或Optional做防护,这也是体现“经验”的代码细节。
时区问题隐蔽性更高。服务器部署在Linux上,默认时区是UTC,本地开发是东八区,同样一个时间字段,本地显示正常,服务器上差了8小时。通用解法是JDBC连接串里加serverTimezone=Asia/Shanghai,同时在统一的配置类里指定Jackson反序列化时区,或者更粗暴的做法是实体类统一用String接收时间字段,展示层再解析——后者虽然不够优雅,却几乎不会出错。
4.3 攻防安全篇:防止爬虫和恶意刷接口
如果系统要长期挂着让别人参观,防爬虫和接口刷量就值得做。热词里正好有“java controller层 如何防护 防止爬虫”,说明这也是同行关注的话题。对这个系统来说,最简单有效的防护是给接口加访问频率限制,用Java自带的ConcurrentHashMap就能实现一个小型滑动窗口计数器:记录每个userId在最近1分钟内的请求次数,超过比如30次就暂时拒绝。
更实际的做法是登录验证码:登录、注册接口接入图形验证码(用Google开源的Kaptcha组件就能生成,代码量很小)。管理员后台的敏感操作(审核、删除、封禁)一律加二次确认和操作日志记录,这样即便有爬虫拿到了接口地址,也无法直接提交非法操作。这部分功能可以在论文的非功能需求章节里写上一个subsection,名字就叫“系统安全性和防恶意访问设计”,答辩时有话可说。
4.4 部署上线篇:从IDEA到Linux服务器的完整姿势
系统开发完成后要写部署文档(论文最后的附录部分也会用到)。最轻量的部署方案是:打包成Spring Boot的可执行jar包,在服务器上安装JDK和MySQL,然后直接用java -jar启动。这里有几个容易踩的坑值得提前摆出来:
- 数据库连接地址不能是localhost,要从云数据库服务的公网/内网地址复制,注意放通安全组端口
- 图片上传路径在服务器上必须设置为一个持久化目录,不能写死成项目的相对路径,否则重新部署时图片全部丢失
- 如果使用HTTPS,WebSocket和图片地址都要跟着换协议,否则浏览器会无情地拦截
服务器上也可以用systemd将Java进程注册为系统服务,实现开机自启和崩溃自动重启。基础配置内容很短,但价值很高:使用systemd后,进程管理不再依赖你“挂着SSH窗口”,这基本上可以成为所有“能持续访问系统的毕设”的分水岭。
5. 论文写作与答辩要点
5.1 论文框架怎么排:让论文配得上系统实现
很多人编程能力强,但论文写成了“代码粘贴流水账”,这是非常吃亏的。这里的核心认知是:论文是在讲“你为什么这么设计”,不是在讲“你写了哪些代码”。我建议的模拟目录结构,是本文开头提过的五章式框架,关键在第二章需求分析里,把业务用例描述清楚,例如“发布闲置”这个用例的参与者、前置条件(已登录)、主流程、异常流(图片太大、价格非法),这就是一张让老师觉得很认真的业务用例说明表。
论文里的“核心代码”不能整个类贴上去,应当只贴关键方法,并紧跟着一段文字解释这段代码在解决什么问题、为什么用这种方式。比如乐观锁更新的那8行SQL,配上“通过version字段检测更新冲突,影响行数为0时说明数据已被修改,此时抛出友好提示让用户重试”的解释,远胜贴50行CRUD代码。
5.2 图表怎么画:用例图、ER图、时序图说话
论文配图的质量直接决定答辩时的第一印象。我强烈建议把以下图按顺序放在对应章节:需求分析阶段用用例图(Use Case Diagram),画清楚三个角色的操作权限;概要设计阶段画系统架构图(简单的分层图:表现层/业务层/数据访问层/数据库);详细设计阶段画ER图(实体关系图,只需要六张核心表);每个典型业务(发布、下单、审核)配一张时序图(Sequence Diagram)。
画图工具有很多选择,我用过ProcessOn和draw.io,前者上手快有现成模板,后者免费。如果担心在答辩演示时深挖某个细节,图和代码里提到的名词必须能一一对上,比如图中写“商品服务”对应的就是“GoodsService”类,这样老师会觉得你的文档和代码是真实匹配的。
5.3 答辩前准备:十连问自检清单
搜狗了一下“java面试题”“java基础”“java八股文”这些热词,看得出来你们在担心同类问题——答辩本质上也是一场技术面试,只是面试官是你的毕设老师。要提前准备好被问概率极高的十个问题:
- 为什么选Spring Boot?它相比传统SSM解决了什么问题?
- 说一下你们系统的核心表结构和表间关系
- 商品浏览量大时,你如何优化查询性能?(答:分页+索引+必要时候加Redis缓存,并诚实说明当前系统的量级不需要)
- 怎么防止表单重复提交?(答:提交后按钮置灰+后端判断幂等键)
- 什么是事务?你项目中哪里用到了事务?
- 乐观锁和悲观锁的区别?
- 你的密码是怎么加密存储的?
- 如果并发下单同一个人同一件商品,怎么保证不会一卖多?
- 数据库中隔离级别了解吗?
- 你怎么做系统安全防护?
这些问题不用全部“标准八股”,关键是要每个都能结合自己项目说出一实例,比如乐观锁的问题就回答“我在下单接口里用version字段做的,更新行数为0就提示手慢了”,这比背概念评分高出很多。
最后的经验之谈
这类校园旧物交易系统,我在不同时期带过好几轮,每一轮的实现都在变化:最初自己读书时用JSP+Servlet,后来带课设时流行SSM,现在几乎清一色Spring Boot。但内核其实一直没变——CRUD谁都会写,拉开差距的是业务逻辑的完整度、异常情况的考虑、以及设计文档里能不能把“为什么”讲明白。你如果正在做这个题目,我的建议是不要在一开始就钻进代码里,先把用例图、ER图画出来,把状态流转表列出来,画好这些,后面的代码只是体力活。
系统做完之后,可以做的扩展方向其实很多:比如把学生认证换成微信扫码登录,增加站内信通知的WebSocket实时推送,或者把图片上传接入对象存储。我会在后续的博文里,逐个拆解这些扩展怎么落地。如果你正在摸索这条路,不妨先从今天这篇里的某一个模块入手——发布流程也好,乐观锁也好,把它真正吃透,你的收获会超出预期。