1. 先把“菜匣子优选”这题目拆开看
我第一次看到“基于Java的菜匣子优选系统设计与实现”这个题目的时候,脑子里冒出来的第一个念头不是技术栈,而是“这题目到底想让我做什么”。后来接触的毕业设计选题多了才明白,这类题目名字起得温柔,实际是个混合体:它既要做一套完整的业务系统,又要在“优选”两个字上做出点算法上的花样,最后还得落成一篇结构规整的论文。三件事任何一件掉链子,答辩现场都不好过。
菜匣子优选系统,说白了就是一个围绕蔬菜、食材包、菜谱这三样东西转的在线平台。用户进来能看到当下哪些食材包值得买、某道菜需要哪些材料、怎么搭配更划算;后台管理员负责维护商品、处理订单、看数据;“优选”两个字则体现在系统的排序和推荐逻辑上——不是简单按上架时间倒序,而是综合新鲜度、性价比、口碑、销量这些维度给出一个分数,把真正合适的东西推到前面。它解决的问题很具体:把分散的食材信息和菜谱信息聚合起来,降低普通人“今天吃什么、怎么买、买多少”的决策成本。
适合谁参考?我把读者分成三档。第一档是正在找毕设题目、手里只有一个标题还没动手的同学,你看完能直接拿到功能清单、表结构和论文提纲;第二档是Java基础还行但没正经做过完整Web项目的人,这里面的接口写法、并发处理、部署流程可以直接抄作业;第三档是已经做完系统但论文写不动的人,第五章专门讲论文那部分,能帮你把代码翻译成老师爱看的文字。
我自己带过几轮类似的项目,也帮人改过不少这类系统。踩下来最深的体会是:这类“业务系统+一点算法”的题目,翻车点几乎从不在代码有多难,而在开头两三天没把需求边界想清楚,导致后面表结构反复改、推荐模块写成了摆设、论文写到第四章发现系统功能对不上。所以这篇我按真实的推进顺序来写,从选题拆解到选型、实现、落地、论文、踩坑,一步一步来,中间该给参数的地方给参数,该给代码的地方给代码。
2. 需求边界怎么划:决定后面返工量的关键一步
2.1 标题背后其实有三个层次的需求
很多人拿到“优选系统”就急着建表,结果建到一半发现“优选”没地方放。我的建议是先按三个层次把需求摊开。
最外面一层是基础交易层,就是电商平台都有的那套:注册登录、商品浏览、搜索、购物车、下单、支付模拟、订单状态流转、收货地址管理。这一层是系统的骨架,缺了它系统跑不起来,但它不体现任何特色,写论文的时候只能算“系统实现了基本功能”。
中间一层是菜谱与食材的关联层,这是这个题目的特色所在。一道菜关联多种食材,一个食材包又对应一道或几道菜,用户既可以“从菜找料”也可以“从料找菜”。这层的数据模型如果设计成简单的两张表加个中间表,后面推荐模块基本没戏;设计得稍微讲究一点,加上标签、口味、烹饪方式、适用人数这些属性,推荐和优选才有原料可用。
最里面一层才是优选与推荐层。这层不必做得多么高深,但一定要有明确的计算过程,能说清楚“为什么这个食材包排在第一”。我的做法是做一个可配置权重的加权评分模型,后台能调权重,前台按分数排序,论文里能画出公式、能贴出对比实验。这比硬套一个协同过滤要稳得多,也更符合本科/硕士阶段的实际能力边界。
三层需求对应的工作量大概是 4:3:3,但很多人把 90% 的时间砸在第一层,最后两层草草收尾,论文里“系统特色”一章写不满两页,这是很典型的结构性失误。
2.2 三类角色的功能边界要提前钉死
角色划分不清是后期接口混乱的主要原因。我一般划三类:普通用户、运营管理员、系统管理员。普通用户管自己的事——浏览、下单、评价、收藏、看推荐;运营管理员管内容——食材包上下架、菜谱维护、订单处理、评价审核、优选权重配置;系统管理员管人——账号、角色、权限、日志。
这里有个坑要提醒:不要把“权限管理”做成一张 role 字段打天下的简单设计。我见过一个项目,用户表里就一个role字段,值是 0/1/2,结果运营管理员要临时拥有“查看日志”的权限,只能改代码重新发版。稍微规范一点的做法是用 用户表、角色表、权限表 加两张关联表,权限点用ingredient:add、order:ship这种字符串标识,登录时把权限列表塞进 JWT 或者缓存,接口上加注解校验。这套东西写起来不到一天,但在论文的“系统设计”章节里是很实在的一节内容,答辩老师问“权限怎么做的”你能答得上来。
还有一点,用户和订单的关系要想清楚。订单归属于用户,但运营管理员也要能看全部订单,这里不要图省事直接在订单表加个admin_id,而是通过角色判断走不同查询路径。否则后期统计报表会很难写。
2.3 为什么这个题目和 Java 技术栈天然契合
题目里明确写了“基于Java”,这不是随便加的。这类系统有几个特点:实体关系多、事务要求明确、后台管理功能重、需要长期维护迭代。Java 生态在这几块积累最深,Spring 的事务管理、MyBatis 的复杂查询映射、成熟的权限框架,都是现成的。换成其他语言当然也能做,但在答辩场合,“为什么选 Java”这个问题你得有话说,我一般建议这么回答:业务对象模型复杂且关联紧密,需要强类型的实体定义和成熟的事务支持;系统包含大量后台管理表单和报表,Java 生态的模板与工具链更完整;同时这也符合作者在课程体系中积累的技术基础。这个回答既实在又不空。
3. 技术选型与库表设计:返工量最大的两个地方
3.1 框架组合:别为了显得高级而堆技术
技术选型我见过两种极端。一种是全上最新最潮的:Spring Boot 3 + Spring Cloud 微服务 + 消息队列 + 分库分表,一个毕设搞出七八个服务,最后连本地跑起来都费劲。另一种是死守 JSP + Servlet,代码里到处out.println,论文写出来像十年前的教材。这两种都不合适。
我的推荐是一套“够用且好讲”的组合,下面这张表是我实际用下来最稳的一套:
| 层次 | 选型 | 选择理由 |
|---|---|---|
| 后端框架 | Spring Boot 2.7.x | 版本成熟、社区资料多、和 JDK 8/11 兼容好,出问题好查 |
| 持久层 | MyBatis-Plus | 单表 CRUD 零 SQL,复杂查询仍可手写 XML,兼顾效率与可控性 |
| 数据库 | MySQL 8.0 | 事务、索引、窗口函数都支持,本地部署零成本 |
| 缓存 | Redis 6+ | 存会话、存热点食材包、做库存预扣,一举三得 |
| 前端 | Vue 3 + Element Plus | 组件齐全,后台管理界面几乎不用手写样式 |
| 鉴权 | JWT + 拦截器 | 无状态,前后端分离友好,代码量小 |
| 部署 | Docker Compose | 一条命令起全套环境,答辩演示不怕现场翻车 |
要注意版本匹配这件事。Spring Boot 2.7 配 JDK 8 或 11 都行,但别配 JDK 17 加老版本 MyBatis,我见过有人这么搭,启动报InaccessibleObjectException,查了两天才发现是模块化反射限制。另外 MySQL 8 的驱动类名是com.mysql.cj.jdbc.Driver,连接串要带serverTimezone=Asia/Shanghai,这两个细节每年都有人栽。
3.2 核心表结构:字段取舍决定后面能做什么
表设计这块我列一下最关键的几张,重点说“为什么这么设计”,而不是把建表语句全贴出来。
用户表(t_user)除了常规的手机号、密码哈希、昵称、头像,我建议加三个字段:member_level(会员等级,用于差异化优惠)、taste_tags(口味偏好,逗号分隔或 JSON,用于冷启动推荐)、status(启用/禁用)。第三个字段很多人不加,结果后台想封禁一个刷单账号都做不到。密码一定要存 BCrypt 哈希,别存明文也别用 MD5,答辩时被问到“密码安全怎么保证”,答 BCrypt 加盐哈希是标准答案。
食材包表(t_food_package)是这个系统的核心商品表,字段设计要兼顾展示和计算:name、cover_img、price、origin_price、stock、freshness_score(新鲜度分,0-100,入库时按品类给基准分,之后按天衰减)、sales_count、rating_avg、rating_count、tags(如“低脂”“快手菜”“两人份”)、status。这里的关键是把评分相关的字段冗余到商品表上,而不是每次排序都去关联评价表做聚合。评价表数据量一大,AVG()聚合会拖垮列表页。
菜谱表(t_recipe)和食材包-菜谱关联表(t_recipe_package)负责“从菜找料”。菜谱表存步骤(可以拆成t_recipe_step单独一张表,按step_no排序)、口味、烹饪时长、难度。关联表就是两个外键加一个gram_amount(对应克数),这样用户看菜谱时能直接算出“这道菜要买多少”。
订单表(t_order)和订单明细表(t_order_item)要分开。订单主表存总金额、优惠、状态、收货信息快照,明细表存每个食材包的下单时价格和数量。这里价格一定要在下单时快照,不能只存商品 ID 然后实时查价格,否则商品调价后历史订单金额就变了,这是新手最常犯的错。
优选权重配置表(t_weight_config)专门放那个加权模型的参数:维度标识、权重值、是否启用、生效时间。这张表的存在本身就是论文里的一个亮点,说明系统具备可配置性而非硬编码。
3.3 前后端分离的取舍:别被“分离”两个字带跑
前后端分离现在几乎是默认选项,但我还是要说句实话:如果你 Java 后端经验不多、前端也不熟,硬上分离会额外吃掉你三分之一的时间在跨域、Token 刷新、路由守卫这些和业务无关的事情上。我见过有人为了“看起来规范”坚持分离,最后接口文档都没写全,前端靠猜字段名。
折中的做法是分离但简化:后端统一返回{ code, msg, data }结构,前端用 axios 封装一层拦截器统一处理 401 和业务错误码,Token 只做单令牌不做刷新令牌,路由守卫只校验有没有 Token。这套简化方案代码量大概两百行,够用,出问题也好定位。真要追求严谨,等系统跑通了再补刷新机制也不迟。
4. 优选与推荐:这个系统真正能拿分的地方
4.1 加权评分模型:把“优选”变成可解释的公式
“优选”这个词如果只体现在界面上一个“优选”标签,那就白瞎了。我的做法是给每个食材包算一个综合分,公式写成这样:
Score = w1 * F + w2 * P + w3 * R + w4 * S其中 F 是新鲜度分,P 是性价比分,R 是口碑分,S 是热度分,四个权重加起来等于 1,从配置表读,默认给 0.30 / 0.25 / 0.25 / 0.20。四个分量的归一化方式不一样,这是要讲清楚的地方:
- 新鲜度分 F:入库时按品类给基准分(叶菜 80、根茎类 90、菌菇 85),之后按天数衰减,
F = base * exp(-0.15 * days)。用指数衰减而不是线性减分,是因为生鲜的品质下降前期慢后期快,指数曲线更贴合实际。 - 性价比分 P:用 min-max 归一化,
P = (maxPrice - price) / (maxPrice - minPrice) * 100。注意这里要在同品类内归一化,拿一颗白菜和一份牛排比价格没有意义。实现上按category_id分组查最大最小价,再套公式。 - 口碑分 R:直接取
rating_avg * 20映射到 0-100,同时做个置信度修正,评价数少于 5 条的往品类均值上拉,避免“1 条五星好评排第一”。 - 热度分 S:用近 7 天销量做对数平滑,
S = log(1 + sales7d) / log(1 + maxSales7d) * 100,防止爆款把其他商品全压死。
这套东西的好处是每一步都能在论文里展开:公式、归一化方法、权重依据、参数敏感度分析,全都有东西写。而且后台能改权重,你可以做一组对比实验——把口碑权重从 0.25 调到 0.40,看排序结果变化,截图放进论文的“实验与分析”章节,比干巴巴说“系统实现了推荐功能”强太多。
注意:评分计算不要放在列表查询的循环里逐条算。正确做法是定时任务(比如每小时)批量算好写回
t_food_package.score字段,列表页直接按这个字段排序加索引。否则一页 20 条数据要查 20 次数据库,接口响应能从 50ms 涨到 800ms。
4.2 推荐逻辑:冷启动阶段用标签兜底更实际
协同过滤听着高级,但对一个数据量只有几百条评价的毕设系统来说,用户-物品矩阵稀疏得可怜,算出来的相似度基本是噪声。我试过在类似规模的数据上跑 UserCF,推荐结果有一半是随机商品。所以我的建议是分阶段:
冷启动阶段走标签匹配。用户注册时选口味偏好(清淡、重口、嗜辣、忌口),系统按食材包标签做交集匹配,匹配数相同再按综合分排序。这套逻辑简单、可解释、效果稳定,代码不到一百行,用 SQL 的LIKE或者标签表关联都能实现。
有数据之后再叠加一层基于物品的协同过滤(ItemCF)。计算逻辑是:统计“同时购买过 A 和 B 的用户数”,除以购买过 A 的用户数,得到 B 对 A 的相似度。用一张t_item_similarity表定时存下 Top20 相似商品,推荐时直接查表。这个计算放在每天凌晨的定时任务里,用 SQL 就能算:
INSERT INTO t_item_similarity (item_id, similar_id, score) SELECT a.package_id, b.package_id, COUNT(DISTINCT b.user_id) / COUNT(DISTINCT a.user_id) AS sim FROM t_order_item a JOIN t_order_item b ON a.user_id = b.user_id AND a.package_id != b.package_id GROUP BY a.package_id, b.package_id HAVING sim > 0.1 ORDER BY a.package_id, sim DESC;论文里写推荐模块,重点不是算法多先进,而是你解释了为什么在这个数据规模下选这套方案。这个“为什么”比算法本身值钱,答辩老师也更愿意听。
4.3 库存与订单:并发问题不解决,演示当场翻车
库存扣减这块必须单独说。最朴素的写法是“先查库存、判断够不够、再更新”,这在并发下必然超卖。我见过不止一个项目在答辩演示时,老师随手点了两次下单,库存变成负数。
正确的做法有两条路,选一条就行:
乐观锁方案,适合并发量不大的场景,一条 SQL 解决:
UPDATE t_food_package SET stock = stock - #{num}, version = version + 1 WHERE id = #{id} AND stock >= #{num} AND version = #{version}影响行数为 0 就说明库存不足或版本冲突,抛异常回滚整个事务。实现简单,不需要额外组件,缺点是冲突多的时候重试率上升。
Redis 预扣方案,适合做秒杀类演示。商品上架时把库存写进 Redis,下单走 Lua 脚本原子扣减,扣成功再异步落库。这个方案代码量稍多,但演示效果好,论文里能单独成一节讲“高并发下的库存一致性”。
不管选哪个,订单创建和库存扣减必须在同一个事务里,并且要注意事务的传播行为和异常类型——@Transactional默认只回滚RuntimeException,如果你抛的是受检异常,记得加rollbackFor = Exception.class,这个细节答辩时很加分。
5. 从空目录到可运行系统:完整的落地节奏
5.1 环境搭建与工程骨架
环境这块我建议直接用 Docker Compose 把 MySQL 和 Redis 起起来,省得在自己机器上装一堆东西还版本冲突。一份能用的docker-compose.yml大概长这样:
version: "3.8" services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: caixiazi ports: - "3306:3306" command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci redis: image: redis:6-alpine ports: - "6379:6379"字符集那行别省,否则中文商品名存进去变问号,后期迁移很麻烦。
工程骨架按 Maven 多模块或者单模块分层都行,我倾向单模块分目录,结构清晰又不啰嗦:controller(接口层)、service+service.impl(业务层)、mapper(持久层)、entity(数据库实体)、dto/vo(传输对象)、config(配置类)、common(统一返回、异常、工具)、task(定时任务)。DTO 和 VO 分开这件事,很多人嫌麻烦合成一个,结果接口返回的字段里带着密码哈希和内部状态,前端一抓包全看见了。分开写多花半小时,安全性和可维护性差一个档次。
5.2 关键接口的编码示例
拿“优选列表”这个接口举例,它串起了分页、排序、评分、缓存几件事,写好了其余接口都是同一个套路:
@GetMapping("/list") public Result<PageResult<PackageVO>> list(@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) Long categoryId) { String cacheKey = "pkg:list:" + categoryId + ":" + page + ":" + size; PageResult<PackageVO> cached = redisTemplate.opsForValue().get(cacheKey); if (cached != null) { return Result.ok(cached); } Page<FoodPackage> p = new Page<>(page, size); LambdaQueryWrapper<FoodPackage> qw = new LambdaQueryWrapper<FoodPackage>() .eq(FoodPackage::getStatus, 1) .eq(categoryId != null, FoodPackage::getCategoryId, categoryId) .orderByDesc(FoodPackage::getScore) .orderByDesc(FoodPackage::getSalesCount); Page<FoodPackage> result = packageMapper.selectPage(p, qw); PageResult<PackageVO> vo = PageResult.of(result, this::toVO); redisTemplate.opsForValue().set(cacheKey, vo, 10, TimeUnit.MINUTES); return Result.ok(vo); }这段代码里有三个值得在论文里说的点:排序走score字段而不是实时计算、结果做十分钟缓存、分页参数做了默认值兜底。缓存失效策略也要想清楚——商品上下架、调价、评分重算时,要按前缀删掉相关缓存,否则用户看到的是过期数据。我一般是维护一个 key 前缀常量,用keys pkg:list:*批量删(数据量小的时候可行,大了就用scan)。
至于登录接口,重点在密码校验和 Token 签发。Token 里放userId、username、roleCode三个信息就够了,别把权限列表塞进去,不然权限一变就得重新登录。权限每次请求从 Redis 里取,性能没影响,灵活性高很多。
5.3 数据初始化:演示效果的一半靠数据
这点很少有人提,但极其重要。系统做完,数据库里就三条测试数据,演示时列表页空荡荡、推荐模块没结果、统计图表全零,观感极差。我的做法是准备一个data.sql,塞进去大概这些内容:8 个食材分类、60 个食材包(每个带封面图、价格、库存、标签)、20 个菜谱(每个关联 3-5 个食材包)、15 个用户、120 条订单(分布在近 30 天,用于统计图表)、300 条评价(评分分布要有高有低,别全是五星)。
封面图别用外链,找几十张图压缩后放本地static目录,不然演示时网络一卡,页面全是裂图。这些准备工作花不了半天,但对最终呈现的影响比多写两个接口大得多。
6. 论文那部分:代码之外的第二个战场
6.1 章节结构与字数分配
系统和论文是两件事,很多人系统做得不错,论文写成流水账。这类题目比较稳妥的章节结构是这样:
| 章节 | 内容要点 | 建议占比 |
|---|---|---|
| 摘要 | 背景一句、做了什么、用了什么技术、达到什么效果、关键词 4-5 个 | 3% |
| 绪论 | 研究背景与意义、国内外现状、研究内容、论文组织结构 | 12% |
| 相关技术 | Spring Boot、MyBatis、Vue、MySQL、推荐算法简述 | 10% |
| 需求分析 | 角色分析、功能需求(用例图)、非功能需求 | 15% |
| 系统设计 | 架构设计、功能模块设计、数据库设计(ER 图 + 表结构)、算法设计 | 25% |
| 系统实现 | 按模块贴关键代码 + 界面截图 + 流程说明 | 25% |
| 系统测试 | 功能测试用例表、性能测试、推荐效果对比 | 7% |
| 总结与展望 | 完成的工作、不足、改进方向 | 3% |
“系统设计”和“系统实现”两章加起来要占一半,这是这类论文的重心。我见过有人绪论写了八千字,实现章节三千字,评审一看就知道重心错了。
6.2 图表清单与绘制规范
图表是论文的“视觉说服力”,该有的不能少。必备清单:系统功能结构图(树状)、用例图(按角色分)、系统架构图(分层)、核心业务流程图(下单、优选计算)、ER 图(实体关系)、时序图(登录、下单)、数据库表结构表(每张表一个三线表)、界面截图(关键页面)、测试用例表、推荐效果对比表。
规范上有几个硬要求:图要有图题且居中放在图下方,表要有表题放在表上方,图表都要在正文里被引用到(“如图 4-1 所示”),不能出现没有引用的孤立图表。界面截图要统一分辨率,别一张 1920 一张 800,排版出来很难看。另外流程图和架构图不要画成截图拼贴,用统一的绘图工具画,线条粗细、字号、配色保持一致,这一条对评审观感的提升比想象中大。
6.3 测试章节与查重规避
测试章节最容易写成“点击登录按钮,登录成功”这种废话。有说服力的写法是设计测试用例表,包含用例编号、测试项、前置条件、输入、预期输出、实际输出、是否通过。功能测试覆盖主要流程,性能测试用 JMeter 压一下列表接口,给出并发 50/100/200 下的响应时间曲线,推荐效果用准确率或者人工评分对比两组权重配置的排序差异。
查重这块,重复率高的地方集中在技术介绍和需求分析。规避方法不是改同义词,而是换成自己的话描述并结合本系统。比如介绍 Spring Boot 时不要抄“Spring Boot 是由 Pivotal 团队提供的全新框架”,而是写“本系统采用 Spring Boot 作为基础框架,主要考虑到其自动装配特性可以减少配置文件编写量,内置 Tomcat 便于打包部署”。同理,需求分析里的功能描述要贴着菜匣子系统的具体业务写,通用描述越少,重复率越低。
7. 踩坑记录与常见问题速查
7.1 环境与依赖类问题
下面这张表是我这几年被问得最多的问题,基本覆盖了 80% 的“跑不起来”:
| 现象 | 根因 | 处理方式 |
|---|---|---|
| 启动报数据源找不到 | 配置文件没被加载或密码错 | 检查application.yml缩进,密码特殊字符要引号包裹 |
| 中文乱码 | 数据库或连接串字符集不对 | 建库用 utf8mb4,连接串加characterEncoding=utf8 |
| 端口被占用 | 上次进程没退干净 | lsof -i:8080找到 PID 后 kill,或改端口 |
| 前端请求 403/401 | 拦截器路径没放行或 Token 没带 | 检查拦截器excludePathPatterns和 axios 请求头 |
| 打包后访问 404 | 静态资源路径配置不对 | 确认static目录位置和index.html路由模式 |
| 时间差 8 小时 | 时区不一致 | 连接串serverTimezone=Asia/Shanghai,实体用LocalDateTime |
还有一个高频问题:JDK 版本和 Spring Boot 版本不匹配。Spring Boot 3.x 强制要求 JDK 17,如果电脑上装的是 JDK 8,启动直接报Unsupported class file major version。这种时候不要硬改,老老实实降到 2.7.x,或者升 JDK,二选一,别折腾中间状态。
7.2 数据与并发类问题
库存超卖、订单金额对不上、推荐结果每次刷新都变,这三个问题我都亲手处理过。超卖的解法前面说了,补一句:演示之前一定要用 JMeter 或者简单的多线程脚本压一下下单接口,确认库存不会被扣成负数,这个验证过程本身就可以写进论文的测试章节。
订单金额对不上,通常是优惠计算和明细汇总走了两条不同的路径。我的经验是把金额计算收敛到一个OrderCalculator类里,所有涉及金额的地方都调它,别在 Controller 里临时算。推荐结果不稳定,多半是排序字段里带了随机因素或者缓存没生效,检查ORDER BY里有没有RAND(),以及缓存 key 有没有区分用户。
还有个小细节:分页插件PageHelper和 MyBatis-Plus 的分页插件不要同时引入,会冲突导致总数永远是 0。这个坑我踩过一次,查了一下午。
7.3 答辩现场的高频追问
答辩老师的问题有套路,提前准备好答案能省很多紧张。常见的有这么几类:
“你的优选算法凭什么这么设计权重?”——回答要点是:权重可配置、有默认值依据、做过对比实验,把权重从 A 调到 B 排序变化截图拿出来。
“并发下单怎么保证不超卖?”——答乐观锁或者 Redis 预扣,说出具体 SQL 或 Lua 脚本的关键语句。
“推荐冷启动怎么解决?”——答标签匹配兜底,等数据积累后再叠加协同过滤。
“数据库为什么这么设计,有没有冗余?”——主动承认评分字段是冗余设计,说明理由是为了避免列表页聚合查询,并说明更新时机是定时任务。
“系统有什么不足?”——这个问题最忌讳说“没有不足”。坦诚说数据规模小、推荐准确率有待提升、没做分布式部署,反而显得清醒。
最后分享一个我自己一直用的做法:答辩前把系统完整走一遍全流程,并且故意制造几个异常场景(库存不足下单、未登录访问、参数传空),看系统的错误提示是否友好。很多系统正常流程很漂亮,一遇异常就白屏或者抛堆栈,而老师最喜欢点的恰恰是这些边界情况。这一遍走下来,能补的坑基本都补上了。
我在实际做这类项目的时候发现,真正拉开差距的从来不是用了多新的框架,而是有没有把“优选”这两个字吃透、有没有把边界情况想到、有没有把系统和论文对上号。一个权重模型写清楚、库存问题处理干净、论文图表规范的系统,比一个堆了五个中间件但说不清为什么用的系统,分数高得多。