最近把“校园网上店铺”这套系统从头到尾做完了,技术栈就是标题里那套:SpringBoot + Vue + MySQL + MyBatis。做之前我以为也就是个普通的增删改查,真正动手后才发现,校园店铺这类业务很有代表性,既要做商品展示、购物车、订单这些电商核心流程,又要处理学生、店家、管理员三种角色的权限区分,还要考虑前后端分离后怎么打包部署、怎么解决跨域和登录态这些问题。如果你的目标是课程设计、毕业设计,或者想系统练一下 Java 全栈开发,这个项目确实值得花时间自己敲一遍,而不是直接拿去交差。
代码量看起来不少,但整体思路其实很好理清:数据库设计定业务边界,后端用 SpringBoot 把接口暴露出来,MyBatis 负责 Java 对象和 MySQL 表之间的映射,前端用 Vue 把页面和数据绑到一起。这篇文章不打算贴全套源码,重点把设计思路、核心实现、配置细节和常见的坑讲清楚,顺手把当年我自己踩过的几个雷也列出来,给大家做个参考。
1. 项目概述与需求拆解
1.1 这个系统解决什么问题
校园网店的本质是给校园内的买家、开店的学生店主、还有后台管理员提供一个交易管理平台。跟淘宝京东那种大电商平台相比,它的核心不是高并发、千人千面,而是流程完整、角色清晰、可维护性高。买家要能逛商品、加购、下单;店家要能管理自己的商品和订单;管理员要能审核店家、管理用户、统计数据。一套功能做完,电商系统的常用业务模型基本都能覆盖到。
具体到实际使用场景,最常见的是两类:一类是二手交易,学生把自己的教材、台灯、自行车挂上去卖;另一类是校园小铺,比如宿舍零食、手工艺品、打印服务这种。既然是校园场景,用户注册就需要有学号或校园工号这类标识,店家入驻需要提交申请由管理员审核,这些通用电商平台不会做的逻辑,恰恰是校园店铺系统里最有价值的部分。
1.2 用户角色与典型业务场景
整个系统我分了三个角色,每个角色的入口和权限不同:
- 普通用户(学生/教职工):浏览商品、按分类筛选、搜索关键词、加入购物车、下单、查看个人订单。
- 店家:可以申请入驻、发布商品、修改商品信息、上下架商品、处理订单状态。
- 管理员:用户管理、店家审核、商品审核、订单总体查看、数据统计。
在设计接口的时候,我的做法是所有请求都走统一鉴权,不同角色通过拦截器校验权限。之所以不把页面简单分成用户端和管理端两个独立系统,是为了让前后端代码结构保持一致,后端只需要用一套接口,通过 JWT 里的角色信息来决定某个接口能不能访问。
典型的业务流程有一条主线:店家发布商品 -> 管理员审核通过 -> 买家浏览搜索 -> 加入购物车 -> 提交订单 -> 店家更新订单状态 -> 买家确认收货。这条线就是我们数据库表设计和接口设计的主心骨,只要它走通了,系统的主体价值就实现了八成。
1.3 功能模块清单
我把系统功能整理成了几个模块,方便后续对应到数据库表和接口:
| 模块 | 功能点 | 涉及的实体 |
|---|---|---|
| 用户与权限 | 注册、登录、角色区分、个人信息维护 | user, role |
| 商品管理 | 商品发布、编辑、上下架、分类浏览、搜索 | product, category |
| 购物车 | 添加、修改数量、删除、勾选结算 | cart_item |
| 订单管理 | 创建订单、订单明细、状态流转、取消订单 | order, order_item |
| 店铺管理 | 开店申请、店铺信息维护、审核 | shop, shop_apply |
| 管理后台 | 用户管理、审核、统计报表 | user, shop, product, order |
功能模块定下来之后,数据库设计就有了明确目标。很多同学拿到这种项目就急着写代码,结果表结构改来改去,代码也跟着返工。我的经验是:先用一张纸把这几个模块的业务关系画清楚,把字段列出来,再动手建表,后面会顺非常多。
2. 技术选型:为什么是 SpringBoot、Vue、MySQL、MyBatis 这套组合
2.1 后端框架选型的取舍逻辑
能用 SpringBoot 解决的问题就不需要自己造轮子。这句话在做项目初期感受不深,越到后期越明显。SpringBoot 内嵌了 Tomcat,不用单独配置服务器;Spring MVC 负责请求路由;Spring Boot Starter 系列帮我把 MyBatis、MySQL 驱动、JWT、校验框架全都整合在一起。对比传统的 SSM(Spring + SpringMVC + MyBatis)手动配置一堆 XML 的方式,SpringBoot 的最大优势不是代码更少,而是让配置变得可维护、可解释。
有人会问,为什么不选 Spring Cloud 或者更重的微服务?因为校园网店根本不需要分布式、服务注册、配置中心那套东西。单机单体就能承载的业务,引入微服务只会把部署复杂度拉满。我始终觉得,技术选型看的不是技术本身有多新,而是它跟业务规模匹不匹配。一个单体 SpringBoot 应用,搭配足够的索引和合理的 SQL,足够应对校园级用户量。
2.2 MyBatis 在持久层的作用
ORM 框架里 JPA/Hibernate 也很常用,但我在这个项目里选了 MyBatis。理由很简单:MyBatis 让我能精确控制 SQL。校园网店的查询场景并不复杂,但有些统计报表和订单查询需要多表联查,用 MyBatis 的 XML 写动态 SQL 非常直观,map 参数的写法也能灵活应对不同的查询条件组合。
Hibernate 的自动映射确实方便,但一旦查询复杂,生成的 SQL 不理想,排查起来很痛苦。MyBatis 把 SQL 写在自己的 XML 文件里,你清楚知道每一条语句长什么样。还有一个现实因素:招聘市场和面试题里 MyBatis 的出镜率极高,用这个项目练一遍,MyBatis 缓存、动态 SQL、ParameterType、ResultMap 这些考点基本都能覆盖到,一举两得。
2.3 前端为什么用 Vue
Vue 的核心优势是渐进式、上手快、组件化。这个项目里,页面之间有不少共用逻辑,比如用户登录态判断、商品卡片展示、订单状态标签,用 Vue 组件封装之后,一个组件可以在多个页面复用,改一处全站生效。配合 Vue Router 做路由控制,Vuex 或 Pinia 做全局状态管理,前端工程化体验非常完整。
可能有读者纠结 Vue 2 和 Vue 3 怎么选。我当时用的是 Vue 3 + Vite,因为这是目前新项目的主流方向,而且组合式 API(Composition API)写业务逻辑确实比 Options API 更灵活,代码组织更清晰。如果你的开发环境是旧的 Vue CLI 项目,Vue 2 也能实现同样的效果,只是语法上有些差异。核心重点是组件划分和数据流设计,框架版本反而不必过度纠结。
2.4 数据库选择与整体架构
MySQL 在这个项目里是最稳妥的数据库选择,开源、资料多、面试常问、环境好装。表结构设计上,我控制了单表数据量,没有引入复杂的分库分表,只做了合理的索引和连表查询优化。配合 Navicat 或 MySQL Workbench 看数据、调 SQL,效率很高。
整体架构是前后端分离:前端 Vue 开发服务器和后端 SpringBoot 接口服务器在开发环境是两台“逻辑服务器”通过 HTTP 通信,生产环境则需要把前端打包成静态资源,要么部署到 Nginx,要么直接放到 SpringBoot 的 static 目录。从实际经验来说,我推荐用 Nginx 部署,但如果是课程作业、毕设演示,把打包后的 dist 目录放进 SpringBoot 资源目录,启动一个 Java 进程就能跑完整个系统,省事不少,这点在后面部署章节会细说。
3. 数据库设计与核心表结构
3.1 表结构总览与设计原则
数据库是整个系统的地基,表结构如果设计不合理,后面写再多的代码都别扭。我的做法是自顶向下拆:先确定核心实体,再确定实体之间的关系。核心实体有用户、店铺、商品、分类、购物车、订单、订单项、系统管理员操作记录。表结构概览如下:
| 表名 | 用途 | 关键字段 |
|---|---|---|
| user | 用户表,包含买家、店家、管理员 | id, username, password, role, school_id |
| shop | 店铺表 | id, user_id, shop_name, status, description |
| category | 商品分类表 | id, parent_id, name, sort |
| product | 商品表 | id, shop_id, category_id, title, price, stock, cover, status |
| cart_item | 购物车表 | id, user_id, product_id, quantity |
| orders | 订单主表 | id, order_no, user_id, shop_id, total_amount, status, create_time |
| order_item | 订单明细表 | id, order_id, product_id, product_name, price, quantity |
设计原则有三条。第一,每个表必须有一个逻辑主键 id,统一用自增整型,简单可靠;第二,金额字段用 decimal,不要用 float 或 double,别问为什么,问就是精度丢失的坑我已经踩过了;第三,状态字段用 tinyint 加数字枚举,而不是直接存字符串,这样数据库更紧凑,代码里再定义枚举常量做映射,维护起来也很清晰。
3.2 用户表和角色的权限模型
用户表会扩展出三种角色,我没有单独建 role 表,而是用 user 表里的 role 字段区分,配合 SpringBoot 拦截器实现权限控制。这是小型系统最务实的做法,如果后续角色权限变多,再演进成 RBAC 表模型也不迟。user 表的核心字段设计如下:
CREATE TABLE `user` ( `id` int NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL, `password` varchar(100) NOT NULL COMMENT 'BCrypt加密', `real_name` varchar(50) DEFAULT NULL, `role` tinyint NOT NULL DEFAULT 1 COMMENT '1买家 2店家 3管理员', `school_id` varchar(30) DEFAULT NULL, `phone` varchar(20) DEFAULT NULL, `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;需要提醒的是,密码不能明文存。这个项目里我用的是 BCrypt 加密,Spring Security 里自带 BCryptPasswordEncoder,也可以引入 jBCrypt 包。很多毕设项目把密码明文存库,面试官一问加密方案就露馅,这个细节一定要处理好。
3.3 商品表与分类表的设计细节
商品表的 status 字段很关键,我定义了三种状态:0 待审核、1 上架中、2 下架。店家新增一个商品后,先进入待审核,管理员审核通过后才会变成上架状态,这样的好处是把“内容安全”和“商品质量”的审核前置,避免店家乱发垃圾商品。
商品表和分类表是多对一关系。分类表我做了两级分类,通过 parent_id 关联父级,比如一级分类“数码电器”,二级分类“手机配件”“耳机音箱”。前端展示时,先加载所有一级分类,点进去再加载对应的二级分类。SQL 查询时用 parent_id 过滤即可,性能压力小,逻辑清晰。
商品表里还要注意stock和sales两个字段。stock表示库存,每次下单都要判断并扣减;sales表示销量,可以做一些人气排序。这两个字段和订单流程强相关,必须在数据库层面做好约束,否则会出现超卖这种尴尬问题。
3.4 购物车表与订单表的设计
购物车表其实就是一个关联表,记录了用户、商品、数量三个信息。设计简单但有几个细节需要注意:同一个用户同一个商品只能有一条购物车记录,所以要有唯一性约束;用户添加商品时,如果记录已经存在就应该更新数量,而不是插入新记录,这个逻辑在 Service 层处理。
订单表我拆成了 orders 主表和 order_item 明细表,原因是订单主表记录整体状态和金额,明细表保存每个商品的快照信息。之前有人把商品名称、价格直接存主表,一个订单多个商品时就会出问题。订单状态我用数字枚举:0 待付款、1 待发货、2 待收货、3 已完成、4 已取消。状态流转有方向性,比如待付款可以取消,待发货不能直接到已完成,这些约束在 Service 层做判断,也就是常说的状态机。
4. 后端核心实现:从搭建到接口落地
4.1 工程结构与 Maven 依赖配置
后端工程我按照常见的分层结构组织,包名清晰一点,后面查问题会省很多时间:
com.campus.shop ├── Controller ├── Service │ └── impl ├── Mapper ├── Entity ├── DTO ├── Common │ ├── Result │ ├── JwtUtil │ └── Interceptor └── configMaven 依赖是项目的起点,我用的是 SpringBoot 2.7.x 版本。为什么不直接用 SpringBoot 3?因为 SpringBoot 3 基于 Jakarta EE,很多老版本的 MyBatis Starter、JWT 工具包会出现坐标变更和兼容性问题。如果你不是非追新不可,SpringBoot 2.7 是最稳的区间。
核心依赖如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>4.2 MyBatis 的 Mapper 接口与 XML 编写细节
我采用的是 Mapper 接口加 XML 文件的方式。接口定义方法,XML 里写 SQL。还有另一种全注解方式,在接口方法上用 @Select、@Insert 注解写 SQL,但一旦遇到动态 SQL,注解方式会变得非常难读。XML 的优势是可以写<where>、<if>、<foreach>这些动态标签,代码整洁且不会出现字符串拼接问题。
商品查询是典型的多条件动态查询,比如按关键字搜索、按分类筛选、按价格排序:
<select id="selectProductList" resultType="com.campus.shop.Entity.Product"> SELECT * FROM product <where> status = 1 <if test="categoryId != null"> AND category_id = #{categoryId} </if> <if test="keyword != null and keyword != ''"> AND title LIKE CONCAT('%', #{keyword}, '%') </if> </where> ORDER BY <choose> <when test="sort == 'price_asc'">price ASC</when> <when test="sort == 'sales'">sales DESC</when> <otherwise>create_time DESC</otherwise> </choose> </select>一个关键点:LIKE查询不要直接在 Java 代码里拼%和关键字,而是用CONCAT('%', #{keyword}, '%'),这样能避免 SQL 注入的风险。ORDER BY字段不能直接用#{}参数占位,因为 MyBatis 会把占位符转成预编译参数,字段名会被加引号导致 SQL 报错,所以我用<choose>在白名单里做了排序字段的映射。
4.3 Service 层与事务控制
Service 层是业务逻辑的核心。商品发布要校验用户是不是店家,订单创建要扣库存、算价格、生成订单号,这些操作涉及多张表的修改,必须加事务。我直接在方法上使用@Transactional,并设置了回滚策略:
@Transactional(rollbackFor = Exception.class) public Order createOrder(Long userId, List<CartItemDTO> items) { // 1. 校验购物车数据 // 2. 计算总金额 // 3. 扣减库存并判断库存是否足够 // 4. 生成订单主表和订单明细 // 5. 清空对应购物车项 }为什么rollbackFor指定成Exception.class而不是默认的 RuntimeException?因为默认情况下,Spring 的事务只对运行时异常回滚,如果 Service 方法抛出 IOException 或者其他受检异常,事务不会自动回滚,数据就会出现不一致。这个细节面试常问,实际项目里也确实是很多人都会踩的坑。
订单号的生成也不能随便。我用的方案是yyyyMMddHHmmss加用户ID的后四位再加一个随机数,比如202506211530120024123,这样既保证了可读性,也降低了并发重复的概率。如果真遇到极端并发重复,再在数据库里给 order_no 建唯一索引兜底。
4.4 Controller 层与统一返回结构
Controller 层千万不要把实体类直接往外抛。我定义了一个通用的 Result 类,包含 code、message、data 三个字段:
public class Result<T> { private Integer code; private String message; private T data; // 提供 success() 和 error() 静态方法 }所有接口返回 Result,成功时 code 为 200,失败时返回对应的业务错误码,比如 401 表示未登录、403 表示无权限。前端拿到 code 之后再做统一拦截处理,而不是每个页面单独判断。这种统一返回结构不仅看着舒服,前端调试也非常方便,浏览器控制台里刷接口的时候一眼就能定位问题。
4.5 登录鉴权:JWT + 拦截器
校园网店系统的接口不能裸奔,用户下单和管理员审核都必须校验身份。我采用的是 JWT(JSON Web Token)方案,流程是:用户登录成功后,后端生成一个包含用户 ID、用户名、角色的 token,返回给前端;前端把 token 存到 localStorage 或内存中,之后每次请求在请求头加上Authorization: Bearer <token>;后端通过拦截器解析 token,校验签名且判断用户是否存在。
JWT 的好处是无状态,后端不用存 session,方便扩展。但要注意三个坑:第一,token 里不要放敏感信息,比如密码,因为 JWT 的 payload 只是 Base64 编码,不是加密;第二,token 要设置过期时间,一般 2 小时到 24 小时,过期后前端需要跳回登录页;第三,拦截器要放行登录、注册、商品列表这些公开接口,否则用户没登录就什么都看不到了。
写一个简单的拦截器核心逻辑:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String header = request.getHeader("Authorization"); if (header == null || !header.startsWith("Bearer ")) { response.setStatus(401); return false; } String token = header.replace("Bearer ", ""); Claims claims = JwtUtil.parseToken(token); if (claims == null) { response.setStatus(401); return false; } request.setAttribute("userId", claims.get("userId")); request.setAttribute("role", claims.get("role")); return true; }更完整的做法是引入 Spring Security + JWT,但对单体项目来说,直接用拦截器手动实现就足够了,代码量少,逻辑透明。面试官问起来,你也能够非常清晰地讲出每一步在做什么。
5. 前端 Vue 实现:页面、路由与接口联动
5.1 工程初始化与路由规划
前端我使用的是 Vue 3 + Vite。创建一个项目只需一条命令:
npm create vite@latest campus-shop-web -- --template vue装完基础依赖后,需要额外安装 vue-router 和 pinia(或者 Vuex):
npm install vue-router@4 pinia axios然后规划路由。我把路由分成两类:公开路由和需要登录的路由。公开路由包括首页、商品列表、商品详情、登录注册;需要登录的路由包括购物车、订单列表、个人中心、店铺管理后台。在 Vue Router 的全局前置守卫里,通过检查 localStorage 里有没有 token 来决定是否放行,并顺便校验路由的 meta 角色信息:
router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.meta.requiresAuth && !token) { next('/login') } else { next() } })5.2 页面组件与 API 封装
前端所有的 HTTP 请求统一放在一个api目录里,通过 axios 做封装。好处显而易见:一个地方配置 baseURL、请求头、超时时间,所有页面都能复用,代码不冗余。核心封装思路:
import axios from 'axios' const request = axios.create({ baseURL: '/api', timeout: 10000 }) request.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = 'Bearer ' + token } return config }) request.interceptors.response.use( response => response.data, error => { if (error.response && error.response.status === 401) { localStorage.removeItem('token') window.location.href = '/login' } return Promise.reject(error) } )BaseURL 设置为/api而不是直接写后端地址,是为了开发环境走 Vite 代理,生产环境把/api转发给后端处理器,真正做到零改动切换环境。
页面组件按模块拆分,完整的商品列表组件包含三个层级:商品卡片组件、商品列表页面、以及购物车状态组件。商品卡片负责展示封面图、标题、价格和销量,点击进入详情;商品列表页面负责接收查询参数并请求接口;购物车状态组件负责把“加入购物车”的操作统一处理。这种组件化划分方式,让你的前端代码可以被维护而不是一坨连自己都看不懂的 HTML。
5.3 商品浏览、购物车和下单的核心联动
商品浏览到下单这个流程是最核心的前端业务链路,我把它拆成三个动作:查商品、加购物车、提交订单。
查商品用商品列表页的搜索和筛选,触发后请求/product/list接口,后端返回商品集合,前端用computed或者 watch 监听查询条件的变化,重新拉取数据。购物车部分,商品详情页点击加入购物车时,先判断是否登录,未登录跳登录页,已登录直接调/cart/add接口。提交订单时,购物车页面把选中的商品 ID 列表传给后端,后端创建订单并清空对应购物车数据。
前端还要处理几个需要细心的地方:金额计算不要用浮点数直接相加,建议用分作为单位(整数),或者先乘以 100 再运算再除以 100,否则可能出现 0.1 + 0.2 不等于 0.3 的尴尬情况。
5.4 打包与部署:把 Vue 放进 SpringBoot
开发时前端用 Vite 开发服务器,生产环境不能这么干。打包后的 dist 目录是纯静态资源,两种常见部署方式:
第一种,用 Nginx 部署前端,Nginx 配置一个 location 把/api反向代理到 SpringBoot 的某个端口。这是企业里最常用的方式,前后端完全分离,独立扩容互不影响。
第二种,把 dist 目录里的静态文件拷贝到 SpringBoot 项目的src/main/resources/static下,然后重新打包 Java 工程,启动一个进程就能从头跑到尾,直接访问http://localhost:8080就能看到前端页面。
第二种方式更适合课程设计和毕设演示,因为不需要另外配置 Nginx,但有个关键设置:如果前端路由使用了 HTML5 History 模式,而不是 Hash 模式,刷新页面时可能触发 404。解决方式要么用 Hash 模式(路由上带#符号),要么在后端加一个 Controller 把非接口路径转发到 index.html。我为了省事选了 Hash 模式,演示效果一样,部署零额外配置。
6. 常见问题与排查实录
6.1 高频报错排查速查表
我把自己这段时间碰到的问题整理了一下,这个表对做同类项目的朋友应该很实用:
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
启动报Failed to configure a DataSource | application.yml 数据源没配好 | 检查 url、username、password 是否齐全,驱动依赖是否引入 |
MyBatis 报Invalid bound statement (not found) | Mapper 接口方法和 XML 的 namespace 或 id 对不上 | 检查 XML 的 namespace 是否为接口全限定名,每个方法的 id 是否与接口方法名一致 |
| 前端请求接口 404 | 后端地址或 baseURL 配置错误 | 用浏览器 Developer Tools 查看请求路径,确认/api前缀与后端 @RequestMapping 是否一致 |
| 前端请求接口 跨域 | 后端没有开启 CORS | SpringBoot 配置类实现 WebMvcConfigurer 添加 addCorsMappings,或使用 @CrossOrigin |
| 打包后页面刷新 404 | History 路由模式缺少回退配置 | 使用 Hash 模式或配置路径转发 |
SQL 中#{}不生效 | 参数传入的是 null 或使用了${}拼接 | 检查参数是否传入,非动态字段一律使用#{} |
| 金额小数位出现误差 | 使用 float/double 存储金额 | 数据库字段改用 decimal,Java 类型用 BigDecimal |
| 服务器时间晚 8 小时 | 时区配置不对 | JDBC URL 加serverTimezone=Asia/Shanghai |
6.2 数据库连接和账号密码的细节坑
数据库连接串绝对是每个新手必踩的雷。MySQL 8.0 版本的 JDBC URL 有一点细微差别,驱动类从com.mysql.jdbc.Driver变成了com.mysql.cj.jdbc.Driver,URL 里还要带上时区参数,如果写成旧版很可能启动就报错。推荐配置如下:
spring: datasource: url: jdbc:mysql://localhost:3306/campus_shop?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false username: root password: 你自己的密码 driver-class-name: com.mysql.cj.jdbc.DriveruseSSL=false是因为本地开发不需要 SSL 加密,不加这一项容易在连接时触发 SSL 相关的警告甚至报错。字符集用的是 utf8mb4,而不只是 utf8,这样才能兼容 emoji 表情和一些特殊符号,商品描述里有人放表情字符时不会入库报错。
6.3 MyBatis 配置文件放置的常见错误
不要把 Mapper XML 文件放到src/main/java目录下就不管了。Maven 项目在打包时默认只把src/main/resources里的东西复制到 classpath,放在 Java 包下的 XML 会被直接忽略掉,然后你就会看到经典的Invalid bound statement (not found)错误。
推荐两种做法:第一种是把 XML 放在src/main/resources/mapper目录下,然后在application.yml里指定 mapper 位置:
mybatis: mapper-locations: classpath:mapper/*.xml第二种是在 Maven 的 pom.xml 里配置build-resources把 xml 也打包进去。我强烈建议用第一种,因为更符合规范,也更容易排查问题。
6.4 关于“完整源码”的使用建议
看到“完整源码”这四个字,我希望读者朋友能保持一个正确的心态:拿源码来学,不要拿源码来逃。前端项目和后端项目如何启动、数据库脚本在哪里、默认账号密码是什么,这些第一件事就要确认清楚。如果作者提供了建表脚本,建议自己先执行一遍,不要直接运行别人的整个打包文件,这样可以理解表结构是怎么创建的。
拿到源码后的正确打开方式是:先把项目跑通,再在关键位置打断点,比如登录、下单、权限拦截,通过这些断点观察数据的流转。然后尝试加一个小功能,比如在商品列表加一个销量排行榜,或者增加一个备注字段,做完这些你才算真正掌握了这套系统。直接复制粘贴交给老师或面试官,最后受损的只能是你自己。很多基础薄弱的人问为什么面试过不去,就是因为项目不是自己做出来的,里面的细节一问三不知。
最后分享一点项目扩展的方向
如果你有余力,这套系统后续有几个扩展点我很推荐。第一个是引入 Redis 做商品热门缓存和购物车临时数据,顺便练一下缓存一致性的处理思路;第二个是增加支付模拟接口,用支付宝沙箱或微信支付沙箱把订单状态从待付款切换到待发货;第三个是把管理端的统计数据做成图表,接入 ECharts,展示每日订单量、销售额走势。这几个方向正好是简历上比较好看的技术亮点,也是面试官愿意展开问的内容。我在做这个项目的过程中最大的体会是:不要被“完整系统”四个字吓到,把用户、商品、订单这三条线捋清楚,再往上面加功能和优化,每一步都有迹可循。希望这篇文章能帮你少踩几个坑,把校园网店系统真正做成自己手里的东西。