简介:基于Springboot+Vue+微信小程序的购物系统毕业设计项目,面向高校计算机相关专业学生,覆盖从系统选题、前后端开发、数据库设计到论文撰写与答辩演示的完整流程,可解决毕业设计缺乏可运行范例的常见问题。资源包共1033个文件,压缩后约22.17MB,内容包含Java后端源码、Vue前端组件、微信小程序页面、SQL数据库脚本、论文Word文档、答辩PPT、部署脚本及使用说明文档,目录结构清晰,方便按模块查阅。目前已有66人学习浏览。项目已通过导师指导认可,答辩评分达98分,在Windows10/11环境下严格调试,下载后按说明即可部署运行;配套文档详尽,覆盖需求分析、系统设计、功能实现、测试部署等环节,适合作为毕业设计高分参考或商城类项目的二次开发基础。
1. Springboot+Vue微信小程序购物系统:毕设里最稳的选择,也是最容易返工的题目
每年到毕业季,基于Springboot+Vue微信小程序的购物系统都是选题单上出现频率最高的方向之一。这个题目看起来常规,但真正交付时往往包含四块各自独立的成果:Springboot后端接口、Vue管理后台、微信小程序用户端,以及配套的数据库脚本、论文、PPT答辩材料和使用说明文档。我见过大量同学在这个题目上返工,原因不是写不出代码,而是环境起不来、表结构缺字段、订单状态乱跳这类基础问题。这篇文章按「能交付、能演示、能通过答辩」的标准,把这个项目从选型、架构到落地完整拆开讲。读完你可以直接照着我说的路径去复现,新手能一步步跟上,熟手可以跳过铺垫直奔避坑章节。
这个选题最适合两类人:一类是Java基础一般,想靠一套成熟技术栈稳住毕业设计的学生;另一类是已有项目经验,想把主要精力留给论文和答辩准备的开发者。双端分离的结构恰好能在答辩时用十分钟讲清楚「前端调后端、后端管数据」这条主线,这也是评委最容易接受和理解的项目逻辑。
2. 技术选型与系统架构:为什么这套组合在毕设场景里最不容易翻车
购物系统不算复杂业务系统,但也不是单表CRUD那么简单。它涉及多角色、多状态流转,后端框架、前端方案和数据库设计三者怎么搭,直接决定后面能否按时写完论文和答辩PPT。这一章先把选型逻辑讲透,再给出一套可以直接落地的分层结构和表结构。
2.1 后端:Springboot的边界与独特价值
在Java技术栈里,Springboot几乎是毕业设计的默认起点。它内置Tomcat,省去单独部署Web服务器;通过starter机制把依赖管理简化成一行配置;配合MyBatis-Plus,单表CRUD可以只写两行代码。这些都是购物系统这种业务场景非常需要的,它不要求你技术上炫技,但要求你快速稳定地交付一堆接口。
我的习惯是严格按 controller、service、mapper 三层组织后端代码,另外单独放 entity、dto、vo、config 四个辅助包。毕设的核心诉求是答辩时能讲清楚,三层架构加清晰的VO转换,比任何花哨的设计模式都好解释。别在毕设里引入过度设计,比如为了一个小购物系统搞微服务拆分,那是给自己挖坑。
下面是这个项目最核心的Maven依赖,可以直接用在你的pom.xml里:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>4.4.0</version> </dependency> </dependencies>上面的依赖里,spring-boot-starter-web负责接口和内置Tomcat,validation负责参数校验,MyBatis-Plus负责数据访问,lombok负责简化实体类的getter和setter,java-jwt用来生成登录令牌。给两点提醒:MyBatis-Plus的版本不要盲目追新,3.5.x与Springboot 2.x的兼容性经过大量项目验证;如果你用的是Springboot 3.x,必须换用MyBatis-Plus的对应新版本,否则启动阶段就会报一堆反射和代理相关的异常。
2.2 前端:Vue管理端与微信小程序端的边界怎么划
购物系统天然要做两个前端。管理后台在电脑浏览器上操作,核心场景是商品增删改查、订单状态处理、用户列表查看,这些用Vue配合Element生态做起来非常顺手,路由、表格、弹窗、表单验证的组件体系已经很成熟。用户端则做成微信小程序,用户在手机上的核心场景是首页浏览、分类筛选、商品详情、加购结算,原生小程序加一个Vant Weapp做UI组件就足够,没必要强行上uni-app,多一层编译链反而可能在答辩现场出岔子。
双端的边界以接口为界:小程序端只调用户侧接口,管理端只调管理侧接口。建议在Controller层用路径把两套接口分开,比如统一以 /api/user/** 和 /api/admin/** 开头。这样做的好处是拦截器可以按路径区分权限,答辩时也能对应上「用户端和管理端是两个入口、共用同一套后端」的系统架构描述。
一个路径划分的样例:
@RestController @RequestMapping("/api/user/product") public class UserProductController { @Resource private ProductService productService; @GetMapping("/list") public Result pageProducts(@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size) { return Result.ok(productService.pageProducts(page, size)); } }这个示例里,@RequestParam的defaultValue属性让小程序的页面即使不传分页参数,也能拿到默认第一页十条数据。管理端的接口单独放在AdminProductController里,用 @RequestMapping("/api/admin/product") 开头,后面通过拦截器统一校验后台登录状态,两个端面的权限边界就清晰了。
2.3 数据库设计:购物系统跑得稳不稳,全看这几张表
数据库是购物系统的地基。最标准的表结构至少包含八张表:用户表、收货地址表、商品分类表、商品表、购物车表、订单表、订单明细表、轮播图表。其中订单表和订单明细表是主从关系,一张订单对应多条明细,这是答辩时几乎必被问到的地方。
下面给出核心表的字段设计与用途,按这个结构建库,跑完整下单流程不会缺字段:
| 表名 | 关键字段 | 说明 |
|---|---|---|
| user | id、openid、nickname、avatar、phone | openid唯一,用户登录凭证 |
| address | id、user_id、receiver、phone、detail | 用户收货地址 |
| category | id、name、parent_id、sort | 商品分类,支持二级分类 |
| product | id、category_id、name、price、stock、main_image、status | stock为库存,下单时扣减 |
| cart | id、user_id、product_id、count、checked | checked标记是否勾选结算 |
| order | id、order_no、user_id、total_amount、status、create_time | 订单主表,记录金额与状态 |
| order_item | id、order_id、product_id、product_name、price、count | 订单明细,快照商品信息 |
| banner | id、image、link、sort | 首页轮播图 |
商品表有几个点需要特别注意。price字段用decimal类型,不要用double,浮点在订单金额计算时会出现0.1加0.2不等于0.3这类问题。stock字段用整数且下单时要原子扣减。status字段控制商品上下架,用户端查询只查status等于1的商品,管理端要看到全部商品则不能加这个条件。我在帮同学复查表设计时,发现最常见的返工原因就是漏了status字段导致商品没法下架。
订单表的status字段是整个系统状态流转最复杂的部分,建议用整数枚举:0待支付、1待发货、2待收货、3已完成、4已取消、5退款中。不要用字符串存状态,字符串的模糊比较很容器让判断出错。后面的订单模块,我会单独把状态机的流转条件展开讲。
3. 本地环境搭建与最小启动步骤:从空电脑到三端跑通
不少同学拿到项目第一反应是打开IDEA直接运行,然后卡在包名不对、数据库连不上、小程序请求报错。这一章先解决环境基线,再分别给出后端和小程序端的最小启动步骤。按这个顺序走,二十分钟就可以把项目跑出基础页面。
3.1 本机环境:版本匹配才是毕设的第一道坎
先给出一套我反复验证过的版本组合,照着装最省事:
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| JDK | 1.8 | 大量毕设项目的稳定基线,Springboot 2.x完美兼容 |
| Maven | 3.6及以上 | 用IDEA内置Maven也可,但建议配置国内镜像 |
| MySQL | 5.7或8.0 | 注意8.0以上驱动名与连接参数有差异 |
| Node.js | 14到16 | Vue 2项目与新版Node存在兼容问题 |
| 微信开发者工具 | 稳定版即可 | 不要用预览版,预览版问题多 |
JDK版本是血泪经验最集中地方。项目如果是Springboot 2.7.x加MyBatis-Plus 3.5.x,配合JDK 1.8没有任何问题;但如果你电脑默认装了JDK 17以上,很多老项目的反射和代理代码会直接报错。建议在IDEA的Project Structure里把项目SDK明确指向1.8,不要依赖系统默认值。
MySQL方面,5.7和8.0都行,对购物系统我建议直接用5.7。一方面资料多,另一方面5.7的默认字符集行为更可预测,不容易在导入数据时碰到排序规则引发的报错。如果必须用8.0,驱动类要写成 com.mysql.cj.jdbc.Driver,连接串里要带 serverTimezone 参数,否则启动就报时区异常。
3.2 后端启动最小步骤:改三处配置就能跑
后端启动前,先把SQL脚本导入数据库,然后修改application.yml的三处配置:数据库账号密码、Redis连接、文件上传大小。导入SQL脚本我习惯用命令行,避免图形工具版本差异带来的字符集干扰:
mysql -u root -p shopping < /path/to/init.sql命令执行成功后,打开application.yml,按下面这段配置修改:
server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/shopping?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 password: servlet: multipart: max-file-size: 10MB max-request-size: 20MB mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0连接串里的characterEncoding=utf8解决中文乱码,serverTimezone=Asia/Shanghai解决时区报错,useSSL=false避免本地连接时刷屏的告警。MyBatis-Plus的logic-delete配置表示逻辑删除,删除商品时实际执行update而不是delete。这个设计在购物系统里很实用,订单明细引用了商品名称和价格,一旦物理删除商品,历史订单的快照就残缺了。
提示:如果本机没有安装Redis,暂时可以先把Redis相关配置注掉,跑通主流程再说。但登录Token如果依赖Redis存储,这一步就不能省,建议直接用默认配置安装一个本地Redis服务。
启动后端的动作很简单:找到主类,类名通常是项目名加Application后缀,比如ShoppingApplication,右键执行main方法。控制台出现类似「Started ShoppingApplication in 5.32 seconds」的信息,就说明后端已经起来了。浏览器访问 http://localhost:8080/api/user/product/list ,能看到JSON数据代表接口已经通了。
3.3 微信小程序端:先从模拟器跑通再上真机
小程序端拿到手之后,第一步不是改代码,而是用微信开发者工具导入项目目录,然后去「详情 -> 本地设置」勾选不校验合法域名。原因在于本地开发时接口地址是http协议的非安全域名,小程序平台要求正式请求必须走HTTPS,不勾选的话所有请求都会被拦截。
接着修改小程序端的接口地址配置。大多数项目会在utils目录下放一个config.js之类的文件统一管理baseUrl:
// utils/config.js module.exports = { // 本地开发:用 127.0.0.1 或 localhost baseUrl: 'http://127.0.0.1:8080', // 如果后端接口前缀是 /api,这里可以统一拼好 apiUrl: 'http://127.0.0.1:8080/api' }模拟器里跑通之后要上真机调试,这里有个新手最容易忽略的细节:真机上不能填127.0.0.1,因为手机上的127.0.0.1指的是手机自己。应该改成电脑的局域网IP,让手机和电脑连同一个WiFi,在电脑终端执行 ipconfig 或 ifconfig 查到形如 192.168.x.x 的地址填进去。这就是「模拟器正常、真机全部报错」的最常见原因。
4. 核心模块实现路径:登录、商品、购物车与订单的串联方式
购物系统本质上是一条链路:用户登录后浏览商品,把商品加入购物车,从购物车勾选结算生成订单,管理端再处理订单发货。这一章按模块拆解实现路径,给出可以直接参考的代码逻辑。理解这条链路,也等于理解了答辩时最值得演示的主线。
4.1 微信登录与Token鉴权:小程序端身份识别的标准做法
小程序端没有传统账号密码登录,用户在手机上点一下授权,实际是前端通过wx.login拿到一个临时code,再把code交给后端换取身份信息。后端用code调用微信的jscode2session接口,拿到openid作为用户唯一标识。整个交互建议跳过自建密码体系,直接以openid关联用户表。
后端登录接口可以这样组织:
@RestController @RequestMapping("/api/user/auth") public class AuthController { @Resource private AuthService authService; @PostMapping("/login") public Result login(@RequestBody LoginRequest req) { // code 是 wx.login 返回的一次性凭证 if (StringUtils.isBlank(req.getCode())) { return Result.fail("缺少code参数"); } String token = authService.loginByWxCode(req.getCode()); return Result.ok(token); } }LoginRequest里只需要一个code字段。wx.login返回的code有效期大约五分钟,且只能使用一次,后端不要缓存它。authService.loginByWxCode内部做的事是:拿着code调用jscode2session、解析openid、按openid查用户表,查不到就新建用户,最后用jwt生成token返回给小程序端。前端拿到token后存进Storage,后续每个请求在header里带 Authorization: Bearer {token}。
有了token,需要用一个拦截器统一校验。常见做法是写一个HandlerInterceptor,在preHandle方法里解析token,解析失败直接返回401,成功就把用户信息放进ThreadLocal。这里有个关键点:购物车和下单接口频繁要取当前用户,不建议在Controller里每次手动解析token,而是通过ThreadLocal或一个UserContext工具类统一获取,代码会干净很多。
4.2 商品模块:分页、分类、搜索三个条件怎么拼
商品模块是管理端和用户端共用的模块。管理端维护上架状态、库存和价格,用户端只读取上架商品。列表需要同时支持分页、分类筛选、关键字搜索,对应到接口就是一个list方法加三个可空参数。
用MyBatis-Plus的LambdaQueryWrapper拼条件非常简洁:
@Override public PageResult<ProductVO> pageProducts(int page, int size, Integer categoryId, String keyword) { LambdaQueryWrapper<Product> wrapper = new LambdaQueryWrapper<>(); // eq 的第一个参数为 false 时,该条件自动忽略 wrapper.eq(Product::getStatus, 1) .eq(categoryId != null, Product::getCategoryId, categoryId) .like(StrUtil.isNotBlank(keyword), Product::getName, keyword) .orderByDesc(Product::getCreateTime); Page<Product> result = productMapper.selectPage(new Page<>(page, size), wrapper); return PageResult.from(result); }这段代码里eq方法的重载是关键:eq(boolean condition, column, value) 在第一个参数为false时会自动跳过条件,不用写一堆if去拼SQL。orderByDesc按创建时间倒序,保证用户端看到的是最新商品。还要提醒一句,用户端查询务必加上 eq(Product::getStatus, 1),否则下架商品会出现在列表,答辩时万一被当场演示出来,印象分损失不小。
商品详情接口则简单很多,直接按id查询返回。但要注意关联数据尽量一次性查完:详情页需要分类名称、轮播图、销量等,不要在页面拿到productId之后再连续调两三个接口。多个请求增加出错概率,答辩演示也不够利落,可以在Service层把关联数据组装成ProductDetailVO一次性返回。
4.3 购物车与订单模块:从加购到支付的状态流转
购物车的核心定位是用户与商品之间的中间关系,本身不做金额计算。用户加购时,购物车表只记录user_id、product_id、count、checked四个字段。最终订单金额必须在生成订单那一刻,用商品表当前价格重新计算,不能直接累加购物车里的价格快照,因为管理端可能随时改了价格。
创建订单是整个系统最需要谨慎的代码,涉及多表操作,必须放在同一个事务里。核心步骤是:校验库存、扣减库存、生成订单主表、生成订单明细、清空已结算的购物车项。给出事务方法的骨架:
@Transactional(rollbackFor = Exception.class) public Order createOrder(Long userId, List<CartItemVO> checkedItems, AddressVO address) { // 1. 预扣库存:一次性校验并扣减,避免超卖 for (CartItemVO item : checkedItems) { int rows = productMapper.deductStock(item.getProductId(), item.getCount()); if (rows == 0) { throw new ServiceException("商品[" + item.getProductName() + "]库存不足"); } } // 2. 生成订单主表,status 置为 0,等待支付 Order order = buildOrder(userId, checkedItems, address); orderMapper.insert(order); // 3. 批量插入订单明细 orderItemMapper.insertBatch(order.getId(), checkedItems); // 4. 删除购物车中已结算的商品 cartMapper.deleteByProductIds(userId, checkedItems); return order; }这段代码里最关键的是deductStock操作。它不能先用select查出库存、在Java里比较再update,因为两个并发请求同时查到剩余1时,各自判断完再更新就会超卖。正确做法是让数据库扣减自带条件,也就是「库存足够才扣减」的原子操作。受影响行数为0说明库存不足,事务回滚,下单失败。这个细节在答辩时如果老师问到并发,可以直接答出来,是加分项。
@Transactional(rollbackFor = Exception.class) 的含义是方法内任何异常都触发事务回滚,扣掉的库存、插入的订单、删除的购物车项全部撤销。毕设里最常见的翻车是漏了rollbackFor,默认情况下只有RuntimeException会回滚,其他异常下事务形同虚设。
支付环节在毕设项目里一律建议做模拟支付,不要尝试接入真实第三方支付平台,个人开发者没有对应商户资质。正确做法是提供一个模拟支付接口,把订单状态从0待支付改成1待发货,并在论文里如实写明采用了模拟支付流程。如果论文里写了「已接入实际支付」,答辩老师翻开代码一看便知是虚假描述。
5. 避坑指南:这套购物系统最常见的六个翻车现场
这一章写的都是真实环境中反复出现的坑,有配置问题、设计问题,也有答辩准备问题。每一条都按「现象、原因、解决」的结构写,可以直接对照排查。
5.1 小程序端所有请求失败:url not in domain list
现象:在微信开发者工具的模拟器打开项目,页面能渲染,但所有请求都报「url not in domain list」,接口返回不了数据。
原因:小程序平台出于安全策略,只允许请求已配置合法域名的HTTPS接口。本地开发用http://localhost,当然不在合法域名列表里。
解决:开发阶段在微信开发者工具右上角「详情 -> 本地设置」勾选「不校验合法域名、web-view(业务域名)、TLS版本以及HTTPS证书」。这个选项只对当前项目本地开发生效。真机调试仍然全部失败时,检查手机和电脑是否在同一局域网,以及baseUrl里填的是不是电脑的局域网IP。
5.2 数据库导入后中文全部变成问号
现象:把SQL脚本导入MySQL,打开商品表看到中文字段全是「???」;或者后端查询返回中文正常,小程序端却显示乱码。
原因:一是数据库字符集不是utf8mb4,二是连接串没指定characterEncoding=utf8,三是SQL脚本文件本身是GBK编码,导入时被错误解析。
解决:建库语句统一写成 CREATE DATABASE shopping DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci。导入前确认脚本头部有 SET NAMES utf8mb4,没有就补上。后端连接串带 characterEncoding=utf8。这三步都做到,乱码问题可以彻底消失。
5.3 商品超卖:两个用户同时买最后的库存
现象:一件商品只剩最后1个,两个账号同时下单,系统都提示成功,扣库存后库存变成负数。
原因:扣库存不是原子操作。先用select检查库存大于0再update,两个并发请求都通过了检查,最后都执行了update。
解决:把扣库存改成带条件的单条update,通过受影响行数判断是否失败。更进一步,可以给商品表加一个version乐观锁字段,更新时带上version条件,形成双重保护。
像下面这条就是带乐观锁的扣库存SQL:
UPDATE product SET stock = stock - #{count}, version = version + 1 WHERE id = #{productId} AND stock >= #{count} AND version = #{oldVersion}SQL里stock >= #{count}是防超卖前置条件,version = #{oldVersion}是乐观锁标记。执行后受影响行数为0,就说明要么库存不够、要么版本号已被其他请求修改,立刻返回失败,让用户重新获取最新状态。
5.4 订单状态乱跳:取消的订单还能去支付
现象:用户在待支付状态取消了订单,订单变成已取消,但再次点击支付,订单又变成了已支付。
原因:更新状态的代码没有校验前置状态,直接按订单号 update status = 1,无论之前是0、4还是5都会成功更新。
解决:所有状态流转的update语句都带上前置状态条件。支付操作只能在status等于0时生效,SQL写成 update order set status = 1 where order_no = ? and status = 0,受影响行数为0就说明当前状态不允许支付,直接返回「订单状态异常」。建议用一个OrderStatus常量类或枚举统一管理这些状态,散落在Service各处的魔法数字很容易出错。
5.5 论文和代码对不上:答辩老师一翻代码就露馅
现象:论文的「系统功能模块图」里画着积分商城,代码里没有积分表;论文的「数据库设计」里写了六个字段,SQL脚本建出来只有五个。被提问时支支吾吾。
原因:先把论文框架搭好,代码边写边调整,最后论文没有同步更新。
解决:论文里出现的每一个功能模块、每一张数据表、每一条数据流,都要对照最终代码做交叉检查。画完架构图后,把图里每个模块在代码里找到对应Controller和Service入口,找不到就补代码,或者删掉论文里对应描述。宁可功能少一点,也要保证论文写的每句话都能当场演示出来。我的做法是最后留一天专门做这个对应检查,花的时间不多,收益却很实在。
5.6 答辩前换电脑演示,项目起不来
现象:答辩当天用教室电脑演示,IDEA导入项目后跑不起来,数据库连接失败,最后只能对着PPT干讲。
原因:教室电脑JDK版本和本地不一样,MySQL服务没启动,或者SQL脚本根本没导入进去。
解决:答辩前至少提前一天,在答辩用的那台电脑上完整跑通一遍:导入项目、导入数据库、启动后端、小程序打开首页。不要拿自己电脑临时演示,一旦设备不适配,整个环节都会崩掉。如果确实只能用个人电脑,检查演示环境是否有稳定电源和网络,并把页面里依赖外网图片的素材提前替换成本地图片。
6. 答辩前必做的三件事:数据验证、演示脚本与代码自审
答辩演示要的不是功能多,而是一条完整业务链路从头走到尾不出错。答辩之前把下面三件事做完,基本能稳住现场节奏。
6.1 数据验证:别用空数据库去答辩
答辩前先往数据库灌一批有真实感的演示数据。商品分类至少6个,商品20条以上,每个商品配本地图片、合理价格和库存。订单表要有多个状态的数据,待支付、待发货、已完成各留几条。这样无论老师问「订单状态怎么区分」,还是现场让你演示一个已完成订单,你都能直接调出数据。同时把论文里的数据库设计章节和真实数据对应起来,截真实的数据表结构图放进论文,比手画示意图更有说服力。空数据库演示会让整个项目显得像个没做完的骨架。
6.2 演示脚本:从用户登录到订单完成的完整链路
写一个控制在十分钟内的演示脚本:打开小程序首页、滑动轮播图、搜索商品、点进详情、加入购物车、结算下单、模拟支付、切换到管理后台看到新订单、把订单状态改为已发货。这条链路覆盖了用户端和管理端的全部核心交互,是答辩时最值得走的路线。把每一步对应的操作和预期页面写下来,演示前自己走两遍,确认每个按钮点了有反应、每个页面跳转不出错。PPT里的截图也应当和这个脚本里的页面保持一致,现场演示万一出问题,指着PPT截图也能继续讲下去。
6.3 代码自审:让老师翻得了代码、还翻不出大问题
答辩老师大概率会随机打开几个代码文件。走进Controller,看它是否只负责接收参数和返回结果,有没有把大量业务逻辑写进请求方法里。打开Service,看核心方法上有没有@Transactional,条件查询时有没有手拼SQL。打开实体类,看字段命名是否规范。再顺手把使用说明文档最后检查一遍,里面提到的部署步骤要和项目实际目录结构一致,答辩时按文档操作也能顺下来。这些地方都干净,老师很难挑出明显硬伤。
代码自审不是走过场,而是给答辩现场加的一道保险。我见过论文漂亮、演示顺畅的同学,最后因为老师点开一个Service方法发现少了事务注解而扣分。别让这种小细节影响整体的交付印象。最后希望你也能把这三件事认真走完。希望帮到你。
本文还有配套的精品资源,点击获取