ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

校园网上店铺全栈开发:SpringBoot+Vue+MyBatis实战详解

校园网上店铺全栈开发:SpringBoot+Vue+MyBatis实战详解 1. 项目整体设计与技术选型思路校园网上店铺这类项目基本上是每年毕业设计选题表里的常客。它不挑学校、不挑导师偏好Java方向的学生大概率会撞上类似的题目。说句实在话这个题目的含金量不在于“校园网上店铺”这几个字的业务有多新鲜而在于它把Web开发的一条完整链路都串起来了前端页面渲染、后端接口编写、数据库建模、订单状态流转以及项目打包部署。SpringBoot负责后端服务的搭建Vue负责用户界面的交互MySQL承担数据存储MyBatis负责数据库访问四个技术点组合起来就是一个很典型的全栈实践项目。这篇文章我会从设计和实现两个角度把这个项目拆开讲清楚。从表结构怎么设计、接口怎么划分到前端Vue路由怎么配置、Axios怎么封装再到最后的本地部署和答辩高频问题基本覆盖你从拿到题目到交付项目的全过程。不管你手里是不是已经有一份源码这篇文章都能帮你把每一个模块的运行逻辑、开发时的坑点全部理顺。1.1 这个系统到底在解决什么问题校园网上店铺本质上是一个封闭环境内的小型电商平台。跟淘宝、京东这些公开电商不一样它的用户群体是校园内的学生和少量商家交易范围相对固定商品类别也多以二手教材、学习资料、手工制品、零食日用品为主。这种环境决定了系统功能不能做得太复杂但该有的核心链路一个都不能少用户注册登录、店铺管理、商品管理、购物车、下单支付校内场景多模拟支付、订单状态跟踪、个人中心。功能上可以划分为三类角色来理解。学生买家端浏览商品、按分类或关键词搜索、加入购物车、提交订单、查看订单状态、确认收货。普通注册用户天然就是买家不需要额外申请。卖家店主端申请开店、发布商品、编辑商品信息、下架商品、查看订单、发货处理订单。管理员端用户管理、店铺审核、商品审核、订单监管、数据统计以及一些运营类的配置。这个角色划分逻辑也回答了答辩时容易碰到的第一个问题权限是怎么设计的。用一张用户表加一个用户角色字段就可以支撑不需要单独建角色权限表。校园场景的特点是并发量不大、业务规则简单越直接的设计越不容易出问题。1.2 技术栈选型的三点考量这个选题在技术栈上几乎是“标准答案”级别的组合但我还是想聊聊每个选择背后的理由这样你在答辩或者写论文的时候能说出自己的思考而不是被问到时只说“这个比较流行”。第一后端用SpringBoot而不是SSH或者SpringMVC。SpringBoot的自动配置让开发者不用再写一堆XML配置内嵌Tomcat也让启动部署变得非常简单。对于这种课程设计、毕业设计级别的项目使用SpringBoot能让你把更多精力放在业务逻辑的实现上而不是消耗在环境配置的泥潭里。同时SpringBoot也是目前企业里Java后端的主流方案学了这个将来工作能直接上手。第二前端选择Vue。Vue的中文文档完善、学习曲线平缓组件化的开发方式非常适合这种“页面多但类型相似”的管理系统。Element UI组件库提供了现成的表格、表单、弹窗、消息提示能省掉大量写样式的时间。Vue Router负责前端路由Vuex或Pinia负责跨页面共享状态比如购物车数据的同步问题。第三数据访问层选择MyBatis。既然标题里明确写了MyBatis那就按原生的方式讲。MyBatis的优势在于SQL由开发者直接控制对于多表联查、复杂统计这类需求写起来很直观。配合动态SQL标签可以灵活处理商品多条件检索这类场景。比JPA更容易定位SQL性能问题也比MyBatis-Plus更贴近“手写完整SQL”的考试需求。另外MySQL作为存储层就不用多说了免费开源、跨平台学校里做项目基本都用它。数据库客户端可以用Navicat也可以直接用命令行看个人习惯。1.3 项目目录结构建议拿到一个项目先别急着敲代码把包结构定好后面开发会顺很多。后端 Java 目录建议这样组织com.example.campusmall ├── controller // 接口层接收前端请求 ├── service // 业务层处理业务逻辑 │ └── impl ├── mapper // MyBatis数据访问接口 ├── entity // 数据库实体类 ├── dto // 数据传输对象接收前端参数 ├── vo // 视图对象返回给前端的数据 ├── config // 配置类跨域、拦截器等 ├── interceptor // 登录拦截器 ├── utils // 工具类JWT、MD5等 └── common // 通用类统一返回结果、状态码、异常前端 Vue 目录按照常规的 src/views、src/router、src/api、src/store、src/components 来划分就行。前后端分离的结构只要你把接口路径规范好了前端开发时甚至不需要等后端写完Mock数据可以先顶着用。2. 数据库设计是项目的定海神针说句大实话校园网上店铺这种项目的业务逻辑其实不算特别复杂真正的技术和业务难点都沉淀在数据库设计上。一个字段设计不合理后面写代码时就会被迫在Java层做各种过滤和弥补表关系理不清楚联表查询时SQL写起来就会很难受。数据库是地基地基没打好后面全是返工的活。2.1 核心表结构与关系梳理这个项目建议从六张核心表开始设计后面根据功能再补。用户表sys_user主键id、用户名username、密码password、昵称nickname、头像avatar、手机号phone、角色role0-管理员1-买家2-卖家、状态status、创建时间create_time。密码必须加密存储推荐用MD5加盐或者BCrypt。店铺表shop主键id、所属用户id user_id、店铺名称shop_name、店铺简介description、店铺头像shop_logo、审核状态status0-待审核1-通过2-拒绝、创建时间。一个用户只能有一个店铺所以user_id上加唯一索引。商品表product主键id、所属店铺id shop_id、商品名称name、商品描述description、商品主图image、价格price、库存stock、所属分类category_id、销量sales、上下架状态status、创建时间update_time。订单表orders主键id、订单编号order_no、用户id user_id、店铺id shop_id、订单总金额total_amount、收货人姓名receiver_name、收货人电话receiver_phone、收货地址receiver_address、订单状态status、创建时间pay_time、发货时间delivery_time、完成时间finish_time。订单明细表order_item:主键id、订单id order_id、商品id product_id、商品名称product_name、商品图片product_image、商品单价price、购买数量quantity、小计金额subtotal。这里冗余商品名称和图片是为了防止商品被删除后订单数据变得不可读。购物车表cart主键id、用户id user_id、商品id product_id、数量quantity、加入时间create_time。加一个唯一联合索引user_id, product_id防止同一商品重复加购。这几张表的关系也比较直白user1——Nshopshop1——Nproductuser1——Nordersorders1——Norder_itemproduct1——Norder_itemuser1——Ncartproduct1——Ncart。2.2 订单表设计里的几个坑订单表是这类项目里细节最多的表很多新手会在这里踩坑。第一个坑是订单金额精度问题。价格字段不要用float和double用decimal(10,2)最稳。Java对应的是BigDecimal。浮点数在二进制里无法精确表示运算会出现0.1 0.2不等于0.3的问题做电商相关的金额计算这是基础常识级别的错误。第二个坑是订单编号不要用自增id要单独生成一个业务订单号。最简单的生成方式是利用时间戳加随机数String orderNo System.currentTimeMillis() String.valueOf(new Random().nextInt(1000))在并发不高的情况下够用了。更规范一点可以用“日期年月日时分秒用户id后四位随机数”生成规则不太容易撞单。第三个坑是订单状态字段。用一个tinyint存状态码0表示待付款1表示待发货2表示待收货3表示已完成4表示已取消。理由很直白整型状态码在代码里比较好判断可读性问题通过状态枚举类去解决。不要用字符串状态描述比如“已付款待发货”这种设计会在统计时让人抓狂。2.3 逻辑外键和索引策略MySQL里物理外键会让数据一致性比较强但在高并发插入和删除场景下性能损耗明显。这类课程级别项目我的建议是不加物理外键而是在Java代码的service层去控制数据关联的有效性。买商品时先判断商品是否存在查订单明细时根据订单id去关联查询商品表。逻辑外键的灵活度更高也不会让你在删除某些测试数据时被外键约束卡住。索引方面给几个常用查询条件加上普通索引就行商品表的category_id联合status上下架状态、订单表的user_id、订单表的shop_id、购物车的user_id。对了别忘了给用户表的username加唯一索引注册的时候可以直接利用数据库做用户名唯一性校验省得每次先查再插。3. SpringBoot后端实现要点解析后端这块是整个项目的重头戏也是答辩时老师关注最多的部分。从分层结构的理解到接口实现我会挑几个核心功能模块来拆解分析。不问业务怎么跑通的只看代码结构是什么样、关键逻辑是怎么写的。3.1 统一返回体与全局异常处理前后端分离的项目接口返回数据必须有一套统一的格式约定。我的习惯是定义一个Result类所有接口都返回这个结构public class ResultT { private Integer code; // 状态码200成功500失败 private String message; // 提示信息 private T data; // 返回数据 }成功时返回 Result.success(data)失败时返回 Result.error(商品库存不足)。前端axios响应拦截器里统一判断code不用每个页面单独处理错误分支。建议不要图省事直接用后端的HttpStatus映射到前端状态码只表达HTTP层面的语义业务层面的错误提示还是交给code message更清晰。全局异常处理用RestControllerAdvice注解实现统一捕获参数校验异常、业务异常自定义BusinessException、以及兜底的Exception。这样controller里不需要写一堆try-catch代码会干净很多。最核心的是把兜底异常也处理掉不然一旦出现空指针返回给前端的是SpringBoot的默认错误页前端axios根本没法解析排查起来特别费劲。3.2 登录认证与拦截器配置校园网上店铺这种系统前端登录后访问接口后端需要知道“你是谁”。这里我推荐用JWT的方式不搞复杂的Session集群方案。实现逻辑是用户登录成功后用id、用户名、角色三个字段生成一个token过期时间设置为24小时然后返回给前端。前端每次请求带上 Authorization 的请求头后端写个拦截器校验。JWT的引入不需要额外插件用jjwt这个依赖就能搞定。核心代码就三块生成token的工具类、负责校验token的拦截器、以及放行白名单配置。public class JwtUtils { private static final String SECRET your-secret-key; public static String generateToken(Integer userId, String username, Integer role) { return Jwts.builder() .setSubject(username) .claim(userId, userId) .claim(role, role) .setExpiration(new Date(System.currentTimeMillis() 24 * 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS256, SECRET) .compact(); } }拦截器里解析token从claim中取出userId和role存到ThreadLocal或者HttpServletRequest的attribute里后续controller方法里直接取就行。需要注意的是注册、登录、首页商品列表这些接口要放行否则前端第一次请求就会被403拦住。拦截器里校验通过后别忘了执行chain.doFilter(request, response)放行很多人第一次写拦截器会漏掉这一步。3.3 商品检索与分页查询商品列表是访问量最大的接口需要考虑条件组合查询和分页两个问题。分页不建议自己用LIMIT手算偏移量容易出错直接集成PageHelper这个插件三行配置就能用上dependency groupIdcom.github.pagehelper/groupId artifactIdpagehelper-spring-boot-starter/artifactId version1.4.7/version /dependency查询商品列表时第一行写上 PageHelper.startPage(pageNum, pageSize)紧接着的MyBatis查询就会自动拼接分页SQL返回的PageInfo里带上total、pages这些分页参数前端拿到后直接渲染分页组件。商品多条件检索的逻辑放在MyBatis的XML文件里实现动态SQL的where条件是这套方案的精华select idselectProductList resultTypecom.example.campusmall.vo.ProductVO SELECT p.*, s.shop_name FROM product p LEFT JOIN shop s ON p.shop_id s.id where if testkeyword ! null and keyword ! AND (p.name LIKE CONCAT(%, #{keyword}, %) OR p.description LIKE CONCAT(%, #{keyword}, %)) /if if testcategoryId ! null AND p.category_id #{categoryId} /if AND p.status 1 /where ORDER BY p.create_time DESC /select注意动态SQL里where标签会自动去掉第一项多出来的AND并且status 1这个条件放在where标签外面最后的写法是错误的应该放到 标签内部。不然一旦其他条件都为空SQL就是空where。这个细节很多人容易踩。3.4 订单提交的事务控制下单操作是典型的需要保证原子性的场景先校验库存是否充足扣减库存生成订单主表记录生成订单明细记录同时清空购物车里对应的商品。这四步任何一步失败都不能留下部分数据。所以这个service方法必须加上Transactional注解让四步操作处于同一个事务里。订单状态流转在后端对应的是一个状态机待付款可以变成待发货或者已取消只有待发货才能变成待收货待收货才能变成已完成。处理状态变更时先查当前状态再判断目标状态是否合法不合法直接抛业务异常。最粗暴的写法是用后端if判断加上状态枚举不用引入复杂的状态机框架。不过可以把每个状态定义成枚举类代码可读性会好很多答辩的时候也是一个加分项。扣减库存也要注意一个细节更新库存的SQL是 UPDATE product SET stock stock - #{quantity} WHERE id #{productId} AND stock #{quantity}通过受影响行数判断库存是否足够。不要在Java层查出stock再比较再更新这种读改写的方式在高并发场景下会出超卖问题。虽然校园店铺的并发量不大但养成这个好习惯以后面试被问到“如何防止超卖”就有真实经验可以聊了。4. Vue前端集成与页面实现前端这块很多同学会觉得比后端简单但实际开发中路由守卫怎么配、Axios拦截器怎么写、购物车状态怎么跨页面共享都是能把你卡住半天的细节问题。4.1 前端工程结构与路由组织用Vue CLI或者Vite创建一个Vue 2或Vue 3项目都行这里以Vue 2 Element UI为例讲。页面文件建议按角色划分目录不要把所有vue文件都堆在views下面不然找文件会找到崩溃。我的目录划分是这样views/ ├── home/ // 首页、商品列表、商品详情 ├── user/ // 登录、注册、个人中心 ├── cart/ // 购物车页面 ├── order/ // 确认订单、订单列表、订单详情 ├── shop/ // 店铺管理商品发布、订单处理、店铺信息 └── admin/ // 后台管理用户管理、商品审核、数据面板路由配置里最重要的部分是路由守卫。后端JWT的token是放在localStorage里的访问需要登录的页面之前前端路由守卫要先检查本地有没有token没有就跳转登录页。角色权限的控制可以写一个meta字段标记当前路由需要的角色在beforeEach里判断。比如店铺管理页的meta里标记 requiresRole: 2卖家管理员页面标记 requiresRole: 0管理员其他角色访问时直接跳到首页。4.2 Axios封装与统一响应处理前端请求后端建议不要直接在每个页面上用axios.get这种散装写法封装一个request.js文件统一管理。基本套路是先创建axios实例设置baseURL为 /api然后在请求拦截器里把本地存储的token加到请求头response拦截器里统一处理业务状态码。// request.js import axios from axios import { Message } from element-ui import router from /router const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config }) service.interceptors.response.use(res { const { code, message, data } res.data if (code 200) { return data } if (code 401) { localStorage.removeItem(token) router.push(/login) } Message.error(message) return Promise.reject(new Error(message)) }, err { Message.error(网络异常请稍后重试) return Promise.reject(err) })这里有一个容易被忽略的问题如果后端返回的code统一为200那么response拦截器里直接判断res.data.code就行。但因为HTTP状态码和业务状态码是两套东西有些人习惯把业务失败也返回为500这种情况下res.status就不是2xx会走到下面的错误分支逻辑就会乱。统一约定HTTP永远返回200业务状态码放body里前端只判断body。关于baseURL和跨域开发环境下前端跑在8080端口后端跑在8081端口浏览器直接发请求会被CORS拦截。两个常见解决办法方案一是后端写一个CorsConfig配置类放行跨域请求方案二是前端在vue.config.js里配置devServer的proxy代理把/api请求代理到后端地址。生产环境下直接把前端打包产物放到SpringBoot的static目录里同源部署就不会有跨域问题这也是为什么后面要讲Vue打包集成。4.3 商品列表和购物车的实现思路商品列表页面是典型的“条件查询分页”场景。搜索框绑定keyword分类菜单绑定categoryId点击查询时调用后端接口把返回的数据渲染到卡片布局中用Element UI的el-card加el-pagination组件。分页参数改变时重新请求接口这里注意pageNum重置问题用户改变页码到第5页后如果再去搜索新关键词页码应该重置为第1页而不是继续停留在第5页。购物车的核心难点在于多个页面需要共享数据。用户把商品加入购物车后导航栏右上角显示购物车数量这个数量在首页、商品详情页、购物车页面都应该同步一致。最简单的做法是登录后调用一次获取购物车列表的接口然后把购物车数据存到Vuex里加入购物车成功后dispatch一个action更新Vuex状态。页面刷新时Vuex数据会丢失解决办法是在App.vue的created生命周期里重新请求一次购物车数据或者用vuex-persistedstate插件把数据持久化到localStorage。购物车接口设计上要注意幂等性。加入购物车时后端先判断该用户购物车里是否已经有这个商品有就数量加一没有就插入新记录。根据之前表设计里的联合唯一索引来判断就行。4.4 Vue打包后怎么放进SpringBoot这是项目交付阶段的关键一步。开发环境前后端分离跑着没问题但最终交付时需要前端打包然后放进SpringBoot的静态资源目录里让后端Tomcat直接托管前端页面。操作流程不长。前端项目里修改vue.config.js把publicPath设置成相对路径的./不能是默认的/否则文件部署到非根路径时资源全部404。然后执行npm run build生成的dist目录就是打包产物。把dist目录里的所有文件复制到后端项目src/main/resources/static目录下。前端页面通过 /api 开头的路径请求后端接口部署后SpringBoot默认的静态资源访问和Controller映射可能会抢路径。解决办法是配置一个WebMvcConfigurer让Controller优先处理接口静态资源走默认路径即可Configuration public class WebConfig implements WebMvcConfigurer { Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController(/).setViewName(forward:/index.html); } }需要注意一点如果当前路径没有对应接口SpringBoot会尝试找前端路由对应的页面。Vue Router的history模式在服务端不配置重写规则时直接刷新 /shop/management 这种二级路由页面会变成404。解决这个问题最省事的办法是改用hash模式即createWebHashHistory或者Vue 2的mode: hashURL里带#刷新不会出问题。想要好看一点的history模式就得在后端写一个errorPage转发的配置比较麻烦我自己做项目时直接选hash模式省心答辩时也不会被这个细节绊住。5. 本地部署运行与常见坑位实录拿到源码、把项目跑起来这一步我见过太多同学卡在这里。有些是MySQL版本问题有些是JDK版本不一致还有些是Maven依赖没拉下来。下面把我总结的一套步骤和问题排查方式完整过一遍每一步都是实测可用的。5.1 从零搭建开发环境的完整步骤环境准备环节必需组件是JDK 8、Maven 3.6以上、MySQL 5.7或8.0、Node.js 14以上、Vue CLI 4或5。如果是从头搭环境我建议的安装顺序是先装MySQL再装JDK然后装Maven最后装Node。MySQL装好之后命令行里执行mysql -u root -p能正常进入数据库的交互界面再继续下一步。这个顺序的好处是后面每一步都能立刻验证不会最后所有问题挤在一起排查。数据库初始化打开Navicat或者命令行新建一个数据库建议字符集选utf8mb4排序规则utf8mb4_general_ci。然后执行项目里的sql文件。执行完检查一下表结构是否正常重点确认是否有空库的情况发生——很多同学执行脚本时报错“表已经存在”这种情况先drop掉再重新执行即可。后端启动用IDEA打开maven项目等依赖下载完成。注意IDEA右下角如果有提示“Maven projects need to be imported”一定要点导入。然后配置application.yml里的数据源信息数据库地址、用户名、密码是否对得上。启动主类前先确认MySQL服务是开着的。SpringBoot默认端口一般是8080如果被占用就在application.yml里改成8081。启动成功后浏览器访问 http://localhost:8081/api/product/list 看接口是否正常返回JSON。前端运行前端项目在命令行里执行npm install安装依赖如果网络不好拉取慢配置一下npm的淘宝镜像源。npm install成功后执行npm run serveVue启动在8080端口。这时候前后端端口不一样请求会跨域所以要么在vue.config.js里配置devServer的proxy指向后端地址要么给后端加跨域配置。强烈推荐第一种方案因为部署到生产环境时前端就同源了开发环境的代理配置不用改。5.2 答辩高频问题与思路整理这部分内容建议答辩前反复看。老师问的问题通常不会特别深入但会围绕“你自己的思考”来展开回答的时候要有层次感先说结论再解释。第一个高频问题为什么用JWT而不用Session回答思路校园项目虽然是单体应用但前后端分离的场景下前端可能是多端入口如果以后要做小程序端或者App端Session的写法就不太合适。JWT的状态不存储在服务端扩展性好跨端方便。注意JWT的缺点是token无法主动失效所以过期时间要合理本项目设置为24小时。第二个高频问题MyBatis和MyBatis-Plus有什么区别为什么不用Plus回答思路Plus是在MyBatis基础上的增强工具提供了通用的CRUD方法开发效率更高。但MyBatis本身更接近原生SQL的实现方式在写复杂查询和多表联查时能让你完全控制SQL语句也更容易理解数据库操作的本质。项目用MyBatis是为了更直观地呈现SQL执行过程适合学习和理解底层的场景。第三个高频问题订单防超卖怎么实现的回答思路核心是条件更新语句UPDATE product SET stock stock - #{quantity} WHERE id #{id} AND stock #{quantity}用数据库的行锁机制保证同一时刻只有一个事务能成功扣减库存。更新受影响行数为0说明库存不足此时抛异常回滚事务。第四个高频问题如果一个学生用户同时是卖家和买家登录的时候角色怎么区分回答思路角色是用户在注册时选择的但卖家需要用户申请开店并通过审核后才有卖家角色。登录时后端根据用户表的role字段下发token中的角色信息前端根据不同角色展示不同菜单入口。这里可以补充说一个账号在校园场景下可以既是买家也是卖家所以在设计时没有做成互斥角色而是通过店铺申请流程来开通卖家能力。5.3 常见异常排查速查表启动报错 Access denied for user rootlocalhost检查数据库用户名密码是否和application.yml一致注意root密码在MySQL 8和5.7下的加密规则不同必要时重置密码或使用mysql_native_password插件。启动报错 Unknown database xxx数据库没有创建成功或者sql没有执行成功。确认数据库名是否和配置文件一致。前端npm install报错 ERESOLVE unable to resolve dependency tree通常是Node版本和依赖版本冲突。可以尝试先删除node_modules和package-lock.json再重新安装或者使用npm install --legacy-peer-deps跳过依赖冲突检查。页面加载出来但接口报404大概率是后端Controller的路径和前端request.js的baseURL没有对齐。检查后端接口的RequestMapping路径是否以/api开头前端baseURL是否也是/api。接口报500且控制台是SQLException优先检查表名和字段名是否在SQL里写错然后检查数据库连接url上是否加了useSSLfalse参数MySQL 8的SSL连接有时会出错。页面刷新后404Vue路由模式的问题。使用hash模式能规避绝大多数这类问题把前端打包放到static目录时尤其要注意。6. 项目扩展方向与个人实操体会校园网上店铺做到底其实还有很多值得打磨的空间。如果你时间充裕想拿个更好的成绩或者在简历上写得更出彩下面这几个方向可以重点考虑一下。6.1 系统还能怎么升级第一个可以升级的点是引入Redis。商品浏览量、首页缓存、验证码存储、接口限流都可以用Redis来优化。答辩时可以提出这样的思路把热点商品的详情数据放到Redis缓存配合过期时间减少数据库压力。第二个升级点是推荐算法。校园店铺的用户群相对固定可以根据用户的历史购买偏好做一个简单的推荐不用上复杂的协同过滤基于商品分类的简单统计推荐也能看出效果。这个方向在论文里写起来也容易出亮点。第三个升级点是支付环节。目前校内场景一般是模拟支付如果要做得更真实可以接入微信支付或者支付宝沙箱环境把支付回调、订单状态自动更新的链路补上。第四个升级点是后台数据面板。目前统计功能一般做得比较简单可以补充一个基于ECharts的销量趋势图、商品类别分布图展示本月订单总额对管理员来说更有实用意义视觉效果也好答辩时展示更生动。6.2 我写下这些代码时的几点实在感受最后聊一点非技术层面的感想。这类项目拿到手之后不要急着上来就写代码。先把需求文档里的功能清单列出来按模块画一画前后端调用的链路再考虑表设计。我一个比较深的体会是数据库的表定了后面的开发速度完全取决于表设计的合理程度。如果表结构推倒重来一次那几乎等于项目重做这样的代价真的很大。另一个体会是关于代码规范的。命名上尽量保持统一Controller的方法用驼峰命名Service接口的方法名要有尽量明确的语义例如getProductById、createOrder这种。注释可以适当写但不要每一行都注释那样反而影响阅读。你自己回头看的时候也会更轻松。对于还在做课程设计或者毕业设计的同学我的建议是把这个项目当成一个真实的交付项目来对待每一步多想一层为什么。比如为什么订单明细里要冗余商品名称和图片为什么扣减库存要用条件更新语句这些细节是你答辩时能自信讲出来的来源。代码能跑通只是及格线能讲清楚才算真正吃透了。这个项目之后再遇到类似的管理系统比如校园二手交易平台、校内跑腿系统、社团招新管理系统你会发现核心逻辑都是相似的。底子打好了换个业务壳子开发起来会快很多。如果你在部署过程中碰到了其他奇怪的问题回头看这章排查速查表大多数情况下都能找到答案。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进