每年到了毕设季,“SpringBoot + 商城”这个组合都会精准命中一大波计算机专业的学生。而礼物商城这个题目,又是其中相当讨巧的一个:它看起来和普通电商系统差不多,但“礼物”这个关键词天然带出了场景化选品、个性化推荐、订单流程、库存管理这些听起来就有分量的模块,工作量可控,演示效果又很直观。说白了,这是一个下限很低、上限很高的题目——用最低成本做完一个能跑的CRUD系统不难,但想拿个像样的分数,就得把里面的业务逻辑和技术细节真正串起来。
我前前后后指导过不少做这个题目的学生,也亲手写过类似的完整项目。这篇就把我从需求拆分、技术选型、功能实现到部署答辩的完整思路捋一遍,重点讲那些论文里不会写、视频教程里也未必会提的坑和细节。不管你目前是刚学完Java基础、正对着SpringBoot一脸懵,还是已经有项目经验、想在这个题目上做出差异化,这篇都值得你花十几分钟看完。
1. 先把题目读透:这个毕设到底在考什么
很多同学拿到题目第一反应是“网上找个商城源码改改”,这恰恰是把这个题目想浅了。先把三个标题并在一起看:
- 计算机毕业设计springboot礼物商城的设计与实践
- 基于SpringBoot的个性化礼品电商平台的设计与实现
- 基于Java Web的创意礼物在线销售系统的设计与开发
三个说法虽然有差异,但拆开之后核心就四件事:SpringBoot、电商、礼物场景、设计与实现全流程。前两个决定了技术路线,第三个决定了业务上限,第四个决定了你要在论文和系统里“自圆其说”。尤其是“个性化”和“创意”这两个词,是你和普通商城拉开差距的关键——后面我会专门讲怎么用低成本方案把这两个词落地。
1.1 这个题目适合什么水平的人选
先说结论:Java基础扎实、懂一点MySQL和前端、但还没接触过微服务这类重型框架的人,选它最合适。
它的技术栈是标准的SpringBoot单体应用,但你又能通过合理的模块设计把它包装成“具备良好扩展性的分层架构”。SpringBoot本身帮你屏蔽掉了SSM时代大量繁琐的XML配置,让新手也能在一两天内把项目跑起来;而投影到论文里,你可以围绕自动装配、starter机制、MVC分层好好聊上几千字。这一点后面答辩环节很关键。
反过来,如果是一个连Maven依赖都还不太熟、Java集合都写不利索的同学,我不建议硬选这个题。它从头到尾的CRUD虽然是套路活,但订单流程、购物车状态、库存扣减这些业务逻辑需要你具备一定的建模能力。基础太弱的话,后期基本就是在抄代码和改bug之间反复横跳。
1.2 为什么是SpringBoot,而不是SSM也不是Spring Cloud
这几乎是每次答辩必被问到的问题,得提前想明白。
SSM(Spring + SpringMVC + MyBatis)是上一代经典组合,但它的问题恰恰体现在“配置地狱”上。四个配置文件起步,环境一换就到处报错。SpringBoot的核心思想是“约定优于配置”,它通过自动配置机制,让你引入一个starter就获得一套默认行为。你自己不需要写那么多配置了,写点必要的配置就行。
Spring Cloud又是另一个层面的东西。它是微服务框架,内部是多个服务拆分的架构,还会涉及到注册中心、网关、配置中心等一堆中间件。你一个毕业设计,又要展示礼物浏览,又要演示下单流程,单体应用完全够了。硬上微服务只会让自己陷入部署和联调的泥潭,而且评审老师看到你用微服务的理由只是“想把架构做大”,反而会追问出一堆问题来。
所以SpringBoot是这个题目的最佳中间值:它比SSM更现代、更贴近企业实际,又比Spring Cloud更可控、更能在毕设周期内完成。这本身就是方案选型能力的体现,写论文的时候这句话可以直接用。
1.3 功能清单别拍脑袋,按电商主线来
不少同学一上来就列了一堆功能,看起来很多,其实很乱。我建议你按“电商主线 + 礼物场景线 + 个性化线”三条线来梳理:
| 模块 | 核心功能 | 对应业务意义 |
|---|---|---|
| 用户模块 | 注册、登录、JWT鉴权、收货地址管理 | 所有交易的基础 |
| 商品模块 | 礼物SPU/SKU、分类、场景标签(生日/节日/情侣)、多图展示、上下架 | 礼物和普通商品的区别在这里体现 |
| 搜索模块 | 关键词搜索、场景标签筛选、排序 | 用户找礼物的入口 |
| 购物车模块 | 添加、修改数量、删除、选中结算 | 交易的中间态 |
| 订单模块 | 提交订单、订单状态流转、取消订单 | 核心业务闭环 |
| 库存与支付 | 扣减库存、模拟支付(或对接沙箱) | 体现并发和一致性意识 |
| 个性化推荐 | 猜你喜欢、热门礼物、同类推荐 | 对应题干里的“个性化” |
| 后台管理 | 商品管理、订单管理、用户管理、数据统计 | 完整系统的必需闭环 |
清单里每个模块都不是孤立的。写论文和做系统时,一定要能说出“用户下单之后,库存为什么在这一步扣、订单状态为什么这样流转”——这比功能多寡更能体现你的设计水平。
2. 项目初始化与技术栈:第一天就把地基打稳
确定功能清单之后,别急着写业务代码。花一个晚上把项目骨架搭好、依赖选对、分层合理,后面能省出两周的改bug时间。
2.1 用IDEA快速创建一个SpringBoot项目
我默认你用的是IDEA。打开IDEA后按这个路径走:File -> New -> Project -> Spring Initializr。这里稍微说明一下,Spring Initializr本质上是去start.spring.io拉一份项目模板,如果你的网络访问比较慢,可以在Setting里把Server URL换成国内镜像,这一步很多新手会卡住。
创建时关键的是这几项:
- Group填你的包名,比如
com.gift,Artifact填gift-mall,Type选Maven。 - Java版本选8或11,这里强烈建议你直接选8,原因在2.2说。
- Dependencies勾选:Spring Web、Validation、MyBatis或MyBatis-Plus Framework(视你习惯而定)、MySQL Driver、Redis、Lombok。
JWT和Swagger这类第三方依赖Spring Initializr里没有,需要后续在pom.xml里手动引入。我个人会在项目里加一个统一工具包hutool,格式化、加密、Bean拷贝之类的小操作能省很多体力活。
2.2 SpringBoot版本怎么选:别追新,踩坑摔过才懂
这是很多新手会踩的第一个大坑。SpringBoot 3.x现在是主流新版本,但它有几个对毕设不友好的地方:
- SpringBoot 3.x要求JDK 17起步。很多学校机房或者你自己电脑上装的可能还是JDK 8,升版本不只是点两下对话框的事,老项目里的依赖可能全部冲突。
- 3.x把原来的
javax.*包全部换成了jakarta.*。网上大量教程、博客、毕设源码还是基于旧包名写的,你复制一行import javax.servlet过来,直接就编译错误。 - 3.x对部分第三方starter的兼容性更严格,比如一些老版的MyBatis-Plus、OSS SDK,在新版本下会出各种奇怪问题。
所以我一直建议毕业设计老老实实用SpringBoot 2.7.x。它成熟、稳定、教程多、跟JDK 8完美搭配,到答辩的时候也不会有任何版本层面的硬伤。你要想在论文里提一句“评估过3.x,但考虑生态兼容性和稳定性,最终选用2.7”,这反而是加分项。
2.3 项目分层结构:包结构不是随便起的
包结构直接反映了你的设计能力,也是老师翻你代码时最先看到的东西。建议按“controller -> service -> mapper -> entity + common/config/vo/dto”来组织。
com.gift.mall ├── common // 统一返回结果、异常处理、常量、工具类 ├── config // 配置类:Redis、拦截器、CORS、Swagger ├── controller // 接口层,只做参数接收和结果返回 ├── service // 业务层,接口 + 实现类,放核心业务逻辑 ├── mapper // 数据访问层,MyBatis-Plus的Mapper接口 ├── entity // 数据库实体 ├── vo // 视图对象,用于接口返回数据 └── dto // 数据传输对象,用于接收前端参数这里有两个细节容易被忽略。一是vo和dto很多人混用,其实职责不同:dto负责入参,vo负责出参。在礼物商品返回时,你可能想把价格、图片URL、标签名拼在一起返回给前端,但对前端传过来的参数,还需要校验字段非空。分开之后,参数和响应的结构都更清晰。
二是业务层接口和实现类分离的问题。有人觉得这是多此一举,但这是SpringBoot项目约定俗成的分层习惯,也方便你后续做事务时更清晰地圈定边界。既然是毕设,按标准套路来,别给自己加戏。
3. 核心功能模块:把礼物的业务逻辑落到实处
骨架搭好之后,真正的活儿来了。这里我不按“点哪个按钮写什么代码”这种教程讲,而是把每个模块里最核心的设计决策、最容易错的地方、以及如何体现“个性化”逐一拆开说。
3.1 用户模块:用JWT做登录,不用Session
礼物商城面向的是散客,用户登录之后逛、加购、下单,服 务器不应该保存状态。这里用JWT + 拦截器的方式做无状态认证,是非常标准,也非常适合写进论文的方案。
流程大致是:用户登录成功后,服务端用密钥生成一个token串返回给前端。前端把它存在localStorage或pinia/vuex里,之后每次请求在请求头带上Authorization: Bearer token。后端写一个拦截器,校验token是否有效,顺便把userId解析出来放进ThreadLocal,后续业务代码里随时取。
实操上有三个点必须注意:
- JWT密钥不要硬编码散落在各个类里,应该统一配置在application.yml中,同时设置一个合理的过期时间。商城类应用token过期时间我一般设24小时,太短会让用户反复登录,太长有安全风险。
- 拦截器要排除登录、注册、商品列表这些公开接口。如果你用了Swagger,还得把swagger相关的路径一并在白名单里放行,否则接口文档都打不开。
- 密码存储一定要用加密算法,不要明文入库。日常毕设推荐用
BCryptPasswordEncoder或hutool里的BCrypt,加盐对安全性提升明显,论文数据安全部分能多写两段。
3.2 商品模块:SPU/SKU和场景标签是“礼物感”的体现
普通商城可以在商品表里直接放几个字段,但礼物商城如果没有SPU/SKU的概念,一看就很业余。
简单梳理:SPU是“礼品”本身,比如“永生花礼盒”;SKU是具体的规格,比如“粉色款+贺卡A”“红色款+贺卡B”。数据库里用gift_spu和gift_sku两张表拆开。sku里存价格、库存、规格编码,spu里存主图、详情、标题、场景标签。
“个性化”这个词,在商品模块里可以用“场景标签”来承接。在spu表里加一个scene_tags字段,或者干脆建一个标签表做多对多关联。标签像“送女友”“送闺蜜”“生日礼物”“节日礼物”“创意手工”等。这样一来,用户可以按“节日 -> 送女友 -> 预算300以内”这样的维度去筛选礼物,这个筛选路径比普通电商的“分类 -> 品牌”有意思得多。写论文的时候,可以把这个说成“基于用户场景的礼品筛选方案”,听起来就高级了不少。
商品列表返回给前端时,我建议用GiftVO封装,里面聚合spu信息、主图、首屏几个标签名、最低价格。最低价格的取法用SQL子查询或MyBatis-Plus的min()就可以,没必要在业务代码里循环取。
3.3 购物车与订单:状态机和事务边界
购物车推荐用Redis缓存实现。读取快、过期自然清理,也比Session方案更接近企业实践。以userId作为key,商品skuId + 数量作为value份数。这里要留意一个场景:用户购物车里有商品,结果商品被后台下架了。结算的时候后端一定要重新校验一遍商品状态和价格,不能直接信任前端传过来的金额。
订单模块是整张系统里最容易出bug的地方。先把订单状态机想清楚:
- 待付款 -> 已付款 -> 已发货 -> 已完成
- 待付款可以取消,取消后回滚库存
- 已发货可以申请售后(简化起见,毕设做到退货退款这一层就行)
下单这个动作,要做好三件事:校验库存、扣减库存、创建订单。这三个动作之间不能有间隙。用@Transactional把整个下单方法包起来,出了异常就整体回滚,避免出现“订单建了但库存没扣”或者反过来“库存扣了但订单失败”的脏数据。
“扣库存”本身还有一个并发问题。两个用户同时买同一个礼物,如果只是先查再改,会超卖。最简单的方案是SQL层面的条件更新:update gift_sku set stock = stock - #{count} where id = #{skuId} and stock >= #{count},通过受影响行数判断扣减是否成功。这是乐观锁思路的精简版,代码写起来不复杂,但能在答辩现场高效应对“并发安全”的提问。
3.4 个性化推荐:用低成本方案做出“猜你喜欢”
听到“个性化推荐”很多同学会被吓住,以为必须上机器学习模型。这恰恰是很多毕设把自己做到坑里去的点。需求文档说是“个性化礼品电商平台”,不代表你一定要训练模型,基于规则和统计的推荐完全够用。
我推荐做三层推荐,全部基于业务数据实现,不需要额外依赖:
- 基于内容的推荐:根据用户历史支付或点赞的礼物,提取它们的场景标签和分类,找到同标签、同分类的其他高分礼物推荐给用户。
- 基于物品协同过滤的简化版:找到跟当前用户买过同一件礼物的其他用户,他们买过的别的礼物,你还没买过的,也可以推荐给你。这个用SQL做交集的关联查询就能搞定,不用计算矩阵。
- 基于时间的兜底策略:用户行为数据太少的时候,直接推热销榜、新品榜。这保证了任何状态下推荐位都有内容。
把这套逻辑封装到一个RecommendService里,在首页和礼物详情页各放一个推荐位。论文里你可以讲清楚的逻辑是:先基于行为标签做召回,再用协同过滤做排序,最后用热度做兜底。虽然算法朴素,但它是完整的、可自洽的方案,比口嗨“我用了一个神经网络”强一百倍。
4. 数据存储与接口设计:把底层磨顺
4.1 数据库表结构:一张表也别多想
表设计是这个项目的地基,通常可以按下面的思路建表。记住,毕业设计表别过度设计,每张表字段够用就好,但关系要理顺。
| 表名 | 主要字段 | 说明 |
|---|---|---|
| user | id, username, password, nickname, avatar, phone, created_at | 注册、登录基础信息 |
| gift_spu | id, title, sub_title, main_image, detail_images, scene_tags, description, status | 礼物商品主信息 |
| gift_sku | id, spu_id, spec_name, price, stock, sku_code, status | 规格、价格、库存 |
| cart_item | id, user_id, sku_id, count, checked, created_at | 购物车数据落库(可配合Redis) |
| order | id, order_no, user_id, total_amount, status, address_snapshot, created_at | 订单主表,地址做快照 |
| order_item | id, order_id, sku_id, spu_title, sku_spec, price, count | 订单详情,价格做快照 |
| address | id, user_id, receiver, phone, province, city, detail, is_default | 收货地址 |
| tag | id, name | 场景标签,可扩展成独立表 |
| spu_tag_relation | spu_id, tag_id | 多对多关联 |
地址、价格快照这个问题,很多新手会忽略。你的订单表里不能只存一个地址id,因为用户事后改了地址,也不应该影响历史订单的收货信息。下单的那一瞬间要把地址内容、价格、商品标题全部复制到订单相关表里。这属于“记账式”设计思路,写论文时在数据库设计章节体现出这个细节,评委会觉得你确实在做系统而不是做增删改查。
4.2 Redis在项目里用在哪里:别为了用而用
Redis不是摆设,你的项目里需要能够明确说出三个以上的使用场景:
- 商品详情缓存:礼物详情页访问量大,把一个spu的详情、SKU列表、标签名组装好缓存到Redis里,设置10分钟过期。变更时主动删除缓存。这里注意,缓存的是组装好的VO,不是原始实体,能省下大量查询时间。
- 购物车存储:以Hash结构存购物车,field是skuId,value是数量,实现轻量读写。
- 分布式Session或临时码存储:如果做了短信验证码模拟,可以把它缓存到Redis里并设置5分钟过期。
缓存能真正发挥作用的前提是有一致性保障。最简单可用的方案是Cache-Aside模式:读的时候先查缓存,未命中则查库并回填;写的时候先更新数据库,再删除缓存。在毕设这个量级下,这样处理已经够了。如果你能在答辩时把这个模式讲清楚,Redis这块基本就稳了。
4.3 前后端分离的接口规范
现在做毕设,几乎都是SpringBoot + Vue前后端分离。接口侧的规范定了,联调才会省力。你需要做三件事。
第一,搞一个统一的返回结构Result<T>:
{ code: 200, message: "操作成功", data: { ... } }业务失败时code一改,前端统 一处理提示信息,不需要为每个接口单独写错误分支。
第二,统一异常处理。写一个@RestControllerAdvice类,集中捕获参数校验异常、业务异常和未知异常。这个类能把所有异常转换为上面统一格式的返回结果,避免前端拿到一堆让人摸不着头脑的报错堆栈。
第三,接口路径语义化。按/api/user、/api/gift、/api/cart、/api/order这种风格来组织,动词都不要加。这是RESTful风格的基本要求,也是答辩时可以说出口的设计原则。
5. 常见问题和踩坑实录:这些坑我都替你们踩过
这一部分是我最想写的。很多同学项目跑不起来,根本不是代码逻辑问题,而是被各种环境坑、配置坑、版本坑折磨到怀疑人生。下面这些都是真实高频问题,按排查思路整理成速查表。
5.1 SpringBoot版本引发的疑难杂症
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
导入依赖后启动失败,提示ClassNotFoundException: javax.servlet.* | SpringBoot 3.x被使用了旧的javax坐标依赖 | 换回2.7.x,或把第三方依赖升级到兼容jakarta的版本 |
Maven报Failed to read artifact descriptor | 镜像源不稳定或依赖冲突 | 检查settings.xml,换成阿里云镜像,mvn clean后reimport |
| 启动成功后访问接口404 | 包扫描路径不对,controller不在主类所在包的子包下 | 主类移动到最外层包,确保@SpringBootApplication能扫描到 |
| 中文乱码 | 控制台编码或响应编码未配置 | 在application.yml里配置UTF-8,IDEA设置File Encoding为UTF-8 |
版本问题排查有个通用思路:先看控制台第一行SpringBoot的版本号和你pom里声明的版本号是否一致。很多老教程会用spring-boot-starter-parent版本锁定,你自己又额外引入了一个高版本依赖,两边版本不匹配,各种诡异现象都会出来。
5.2 LocalDateTime序列化格式问题
这是接口联调时高频出现的第一坑。使用Jackson时,默认输出的LocalDateTime格式是一长串时间戳数组,前端拿到的没法直接显示。解决办法很简单,在application.yml里统一配置:
spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8设置之后,当前端看到“2025-06-01 15:30:00”的时间格式,才发现生活可以这么美好。别问我当时是怎么一步步排查出来的。
5.3 事务不生效:考试最爱的陷阱
@Transactional注释不生效是一个经典问题,原因通常就三个:
- 同类内部方法调用。比如
save()方法里调了同一类的doSave()方法,而@Transactional标在doSave()上,Spring代理到第一个方法就停了,事务起不到作用。解决方式有两种:拆成不同的类互相调用,或者把事务方法在外部类入口调用。 @Transactional被try-catch吞了异常。你在方法内部自己捕获了异常并且没有往外抛,Spring看不到异常,自然不触发回滚。正确姿势是捕获后throw new RuntimeException()抛给事务管理器。- 应用了非RuntimeException异常。默认情况下
@Transactional只回滚RuntimeException和Error。如果你手动抛了个IOException之类的受检异常,事务不会回滚,得在注解上指定rollbackFor = Exception.class。
这些细节在经典SSH阶段就是高频面试题,放到SpringBoot毕设里依然成立。答辩时如果老师问“你的下单操作怎么保证一致性”,你把事务边界和回滚条件讲清楚,这一问就稳了。
5.4 Docker部署SpringBoot项目:本地能跑,服务器跑不起来?
如果你的毕设要求部署到云服务器上,Docker是最快的方式。这里我不想贴一大段Dockerfile,而是重点说说最容易踩的地方。
一个常见的坑是端口和MySQL地址。容器里的SpringBoot连数据库时,localhost指向容器内部而不是宿主机,所以database url里的host要填服务器的局域网IP,或通过docker-compose里服务名访问。如果部署后一启动就报连接超时,先怀疑这个,而不是怀疑代码漏洞。
第二个坑是时区问题。容器默认时区不是东八区,导致你接口返回的时间差8小时。在启动容器时加上-e TZ=Asia/Shanghai能解决,或者在application.yml里把JDBC连接的serverTimezone=Asia/Shanghai显式写出来。
第三个坑是日志。用Docker以后日志默认打到容器里,出问题排查很费劲。建议启动命令加上-v /home/app/logs:/app/logs把日志目录挂载出来,这样直接用tail -f看日志,效率高太多。
5.5 JWT过期和拦截器放行问题
有同学做完了功能,却发现前端有些接口一会能访问一会不能访问。查下来发现是两个原因:一是前端请求头没带token,拦截器直接拦了。二是白名单里漏掉了静态资源路径,导致前端加载logo图片都被拦下来。排查思路很简单:后端拦截器过滤前加一行日志,打印请求路径和是否放行,看一遍就全明白了。
6. 论文撰写与答辩准备:不要只管代码不管文档
很多同学代码写得好的,却在论文和答辩环节拉了胯。其实这个题目的论文是最好写的,因为它对应一个完整的电商业务链条,每一层都有东西可以写。
6.1 论文框架:顺着项目主线走
你完全可以用这个框架来写:
- 绪论:表达研究背景和意义。为什么做礼物电商?现在线上送礼需求大,传统电商缺乏场景化引导,所以要做个性化礼品平台。
- 相关技术介绍:SpringBoot、MyBatis-Plus、Redis、JWT、Vue。这里不要纯抄百度百科,要写“为什么选择它”。
- 需求分析:从功能性需求(用户、商品、购物车、订单、推荐、后台)和非功能性需求(性能、安全、可用性)两个角度展开。
- 总体设计:给出架构图、功能结构图、数据库E-R图。这章节可以大量放图,是拼篇幅的地方。
- 详细设计与实现:按模块逐个写流程描述、关键类设计、核心代码。不要只贴一大段代码,要写清楚“这个模块解决了什么问题”。
- 系统测试:功能测试用例表 + 典型性能测试数据。重点测试下单并发、搜索响应时间等,这些数据能让论文看起来真实。
写论文的时候要记住一个原则:每个章节之间要能互相呼应。需求分析里提到的功能,在详细设计里必须出现;测试章节里必须覆盖这些功能。好多学生论文被老师打回,就是因为需求分析写了一堆功能,后面设计实现根本没体现。
6.2 答辩高频问题:提前准备好答案
答辩老师不一定会深抠代码,但他们特别喜欢从技术选型、业务边界、安全设计入手提问。下面是高频问题清单:
- 为什么用JWT而不用Session?——无状态、跨域友好、适合前后端分离;答完还可以补一句“session在集群环境要共享存储,比较麻烦”。
- 自动装配的原理是什么?——重点讲
@SpringBootApplication组合注解、@EnableAutoConfiguration通过spring.factories或AutoConfiguration.imports加载配置类、@Conditional系列注解按条件生效。这个问题几乎必问,建议背熟。 - 缓存和数据库的一致性怎么保证?——讲Cache-Aside模式的更新策略,先改库再删缓存。这个前面说过,把逻辑理顺即可。
- 订单超时未支付怎么办?——最简单的方式是定时任务扫描超时订单并取消,也可以答延迟消息。毕设里用
@Scheduled定时扫描是够用的,但要说明两次扫描之间的时间窗口,承认利弊。 - 项目遇到了什么难点?怎么解决的?——重点讲并发扣库存超卖问题和缓存一致性。这个问题一定要准备,而且要有真实细节,比如“一开始用先查再改导致超卖,后来改成SQL条件更新,用受影响行数判断是否成功”,这种实话式回答非常加分。
6.3 扩展方向:适当留出亮点
如果时间有余,或者你想冲一下优秀毕设,可以考虑往下扩展:
- 支付模块:接入支付宝沙箱环境,完成后端异步通知处理和支付状态回调。哪怕不做真实支付,只用沙箱,这也是很好的亮点。
- 秒杀与限流:在礼物详情页做定时上架的限量款,用Redis预扣库存 + 简单限流实现。虽然核心逻辑简单,但“高并发场景下的库存控制”听起来就有力。
- 搜索能力升级:用Elasticsearch替换MySQL模糊查询,这和你列表里的热门搜索词也能对得上。但要注意,Elasticsearch本身部署要资源,做之前评估一下你服务器的内存。
扩展功能不要全面铺开,选一个做深做透就好。毕设成绩靠的不是功能数量,而是每一个功能的完成质量和设计思路。
最后再分享一个我做这类项目的经验:一个礼物商城项目想做得像样,最核心的其实是“业务逻辑闭环”。从浏览礼物、加入购物车、下单、扣库存、模拟支付、后台发货,到最终的订单完成,中间每一步的异常和边界情况都要处理到位。代码可以简单,但逻辑必须完整。把这条主链路打磨顺了,这个毕设你就已经赢了一大半。做项目的时候,多想想“如果我是用户,哪些地方会让我觉得这是个假系统”,多问自己几句,比照着网上的源码复制粘贴有意义得多。