简介:谷粒商城是一套典型的电商全栈项目,这份压缩包面向正在学习后端开发或需要完整项目参考的开发者,集中收录了商城后端源代码、数据库脚本以及前端页面,可直接用于本地部署、二次开发或者课程设计。包体约127.7MB,主要内容以代码文件、SQL脚本和HTML页面为主,其中SQL部分覆盖了数据库表结构设计、初始化数据、业务查询和存储过程;HTML配合样式表与脚本实现商品展示、购物车等交互界面;后端代码则包含接口、业务逻辑与数据库操作,整体较完整地呈现出商城系统从数据层到表现层的实现链路。目前已有417人学习下载,适合用来分析电商项目结构、练习数据库建模,也可以在此基础上扩展功能、优化性能。若希望快速跑通整个谷粒商城并理解前后端如何协同,这份压缩包能提供一套可对照的完整参考。
1. 谷粒商城压缩包到底装了什么:代码、SQL、HTML 三件套的定位
谷粒商城这套资源我在本地跑通花了不到一下午,印象最深的是它把电商项目的后端骨架、数据库脚本和前端页面捆在了一起,非常适合拿来当课程设计参考或者快速理解传统电商的闭环。你拿到手的是一个压缩包,里面按代码、SQL、HTML 分了区域:代码解决业务逻辑怎么组织,SQL 解决数据怎么落库,HTML 解决页面长什么样。它适合两种人:一种是做商城类课程设计或毕业设计的在读学生,另一种是刚入行想搞懂商品、购物车、订单、库存这几张核心表怎么联动的初级开发者。这篇笔记从解压开始讲,按导入工程、初始化数据库、对接页面、排查问题的顺序走,最后聊一个能拿来应付演示的小技巧。
2. 环境准备与导入工程:JDK、MySQL、IDEA 组合怎么选
2.1 环境版本选型:为什么 JDK 8 + MySQL 5.7 是稳妥组合
拿到代码先别急着解压,先确认机器上的环境跟项目匹配。这类商城教学资源最常见的组合是 JDK 1.8、MySQL 5.7、Maven 3.6 左右、IDEA 自带启动。JDK 8 和 MySQL 5.7 属于“教学项目安全区”,因为这套组合下的驱动类名、日期时间处理、默认字符集都被大量项目验证过,坑最少。
如果机器上装的是 JDK 11 甚至 JDK 17,常见问题是编译阶段报javax.xml.bind相关缺失,这是因为高版本 JDK 移除了 Java EE 模块。解决办法不是换 JDK,而是在 pom.xml 里补 JAXB 依赖,但我更建议直接装一个 JDK 8,省得跟 Spring Boot 2.x 的兼容性纠缠。
MySQL 8.0 也能用,但要注意驱动类名变了:老代码里写的是com.mysql.jdbc.Driver,8.0 之后要改成com.mysql.cj.jdbc.Driver,同时连接串里得多加serverTimezone=Asia/Shanghai,否则会报时区错误。这个细节在解压后的 application.yml 里通常已经配好,但如果你是拿老版本代码自己拼环境,十有八九会栽在这。
2.2 导入工程的三个步骤:解压、改配置、等 Maven 拉完
解压之后你会看到标准的 Maven 工程结构:src/main/java下面按 controller、service、dao、entity 分包,src/main/resources里放着application.yml、mapper目录和静态资源。导入 IDEA 时选Import Project,指定 pom.xml 作为导入文件,Maven 会自动读依赖。
# 解压并检查顶层目录结构 unzip gulimall.zip -d gulimall-src cd gulimall-src find . -maxdepth 2 -type d | sort这段命令把压缩包解开,然后列出两层目录结构,目的是先确认代码、SQL、HTML 三块的落点。一般代码在src下,SQL 文件会独立放在sql或doc目录,HTML 在resources/static或resources/templates下。跑完这步,你就知道该把数据库脚本拿到哪里去执行。
导入工程后 Maven 会自动下载依赖,在国内网络环境下这一步可能很慢。我一般会先检查settings.xml里有没有配阿里云镜像,没配的话在 IDEA 的 Maven 设置里加上镜像地址,然后再触发reimport。常见做法是在.m2/settings.xml的 mirrors 节点里加一个mirrorOf为central的镜像配置,指向仓库地址,这样依赖拉取速度能从几分钟降到几十秒。
2.3 application.yml 里的关键参数逐个看
工程能启动的前提是配置文件里的数据库连接是对的。打开src/main/resources/application.yml,你大概率会看到下面的结构:
server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/gulimall?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver redis: host: localhost port: 6379这里的server.port是后端服务的 HTTP 端口,前端页面请求接口时就是打到这个端口上。如果你本机 8080 被占用,改成 8081 后,HTML 里所有请求路径都得跟着改,这一点后面会展开讲。url里的characterEncoding=utf8保证了中文能正常读写,serverTimezone=Asia/Shanghai是避免 MySQL 连接时的时区报错。username和password务必改成你本机数据库的账号。
Redis 配置不是每台机器都有,不少教学项目的登录验证码和购物车临时数据会用到 Redis。如果你没有安装 Redis,可以先把相关配置注释掉,或者把spring.redis整段删掉,然后去代码里找到 Redis 工具类。如果发现不依赖也能跑就继续,如果登录接口强依赖它,那就得用 Docker 拉一个 Redis 5 的容器,命令是docker run -d -p 6379:6379 redis:5,这个成本很低,建议直接装上。
3. SQL 脚本分析:从建库到数据关联,先看懂才敢改
3.1 一份 SQL 文件里的表结构演变:从 spu 到 sku 到库存
SQL 文件通常不止一个,常见做法是gulimall.sql负责建库建表,data.sql负责灌基础数据。拿到 SQL 不要急着双击导入,先用文本编辑器打开看前几十行,确认库名、默认字符集和排序规则。
-- 创建数据库,指定字符集避免中文乱码 CREATE DATABASE IF NOT EXISTS gulimall DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE gulimall; -- 商品 spu 表:描述一个抽象商品 CREATE TABLE `pms_spu` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `spu_name` varchar(100) DEFAULT NULL, `brand_id` bigint(20) DEFAULT NULL, `category_id` bigint(20) DEFAULT NULL, `publish_status` tinyint(4) DEFAULT NULL, `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;这段建表脚本表达的是电商里最常见的设计思路:SPU 是商品抽象层,比如“某品牌手机”是一个 SPU,而具体某个颜色、某个内存版本的手机才是一个 SKU。表结构上pms_spu存抽象信息,pms_sku_info存具体规格和价格库存,两张表通过spu_id关联。看懂这一层,你才知道为什么库存扣减不能直接操作 spu 表,只能锁 sku 行。
执行 SQL 的推荐方式是命令行,避免图形客户端在某些版本上字符集不兼容:
mysql -uroot -p --default-character-set=utf8mb4 < gulimall.sql--default-character-set=utf8mb4这个参数能保证 SQL 文件里的中文注释和初始数据导入后不乱码。如果你用的是 Navicat 之类客户端,把连接编码也改成 utf8mb4 再执行脚本,效果一样。
3.2 订单、购物车、库存三张表的勾稽关系
订单相关表是这套资源里最值得细读的部分。典型的表有oms_order、oms_order_item、cart_item、wms_ware_sku。它们之间的关系是:用户把商品加进购物车,购物车表记录的是未确认状态的临时选择,提交订单时生成oms_order主表和oms_order_item子表,然后扣减wms_ware_sku里的库存。
-- 查看订单主表与订单明细表的关联字段 SELECT o.id, o.order_sn, o.total_amount, i.sku_id, i.sku_name, i.sku_price FROM oms_order o LEFT JOIN oms_order_item i ON o.id = i.order_id WHERE o.member_id = 12;这里order_id是子表指向主表的外键,查询时用LEFT JOIN把订单头和明细拼一起。重点看total_amount和sku_price的汇总关系,如果订单总额不等于明细价格之和,十有八九是优惠分摊逻辑出问题。教学项目里这块一般做得比较简单,很多直接在代码里算总额后再插入订单表,你读 SQL 时能发现total_amount和pay_amount两个字段,前者是商品原价合计,后者是实付金额,中间差的是优惠和运费,这个逻辑面试常问。
3.3 表字段里的时间字段更新:ON UPDATE 要不要加
不少教学项目的建表脚本里,update_time字段只写了DEFAULT CURRENT_TIMESTAMP,没写ON UPDATE CURRENT_TIMESTAMP。这意味着数据修改时,这个时间字段不会自动刷新。如果你要做“最近更新时间”的展示,会发现数据改了但时间纹丝不动。
ALTER TABLE `pms_spu` MODIFY COLUMN `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;执行这段可以将时间字段改为更新时自动刷新。业务代码里就不用再手动setUpdateTime(new Date()),减少一处容易漏改的地方。但要注意,如果你的项目里已经有代码显式更新这个字段,加了ON UPDATE后两者并不冲突,显式赋值优先级更高。
3.4 初始数据里的密码字段:看到密文不要慌
教学项目一般不会在 SQL 里明文存密码,而是存 MD5 或 BCrypt 密文。如果你在执行完 data.sql 后拿admin和123456去登录发现失败,先去数据库查一下ums_member表里 password 列存的是什么格式。如果看到一串$2a$10$开头的密文,说明是 BCrypt,前端登录时后端会用BCryptPasswordEncoder做校验,你在数据库里把密文改成123456的 MD5 也登录不进去,因为比对算法不在数据库层。
这种情况下调试登录接口最简单的方式是注册一个新账号,走一遍正常注册流程,数据库里自然会多一条对应密文的记录,拿它登录准没错。查账号状态时要留意status字段,有的初始化数据把默认用户状态设成了 0,也就是禁用态,这种坑很容易被误判成密码问题。
4. 前端 HTML 与后端接口的对接:看清页面请求怎么打
4.1 静态页面的目录结构与 Controller 路由映射
HTML 资源一般放在src/main/resources/static下,按index.html、login.html、product.html、cart.html这类名称平铺或者分目录存放。Spring Boot 对静态资源的默认映射路径是/static/**,也就是说访问http://localhost:8080/index.html时,框架会去static目录下找同名文件。
页面和后端的对接方式有两种:一种是表单直接提交到 Controller 的 URL,另一种是页面里的 AJAX 请求调用接口。教学项目里两种都有,登录页面通常用表单提交,商品列表、购物车数量加减则用 AJAX。
$.ajax({ url: '/product/list', type: 'GET', data: { categoryId: 12, page: 1, limit: 10 }, dataType: 'json', success: function (res) { if (res.code === 0) { renderProductList(res.data.list); } else { alert(res.message); } } });这里的/product/list就是后端 Controller 里的@RequestMapping("/product/list"),方法的入参categoryId、page、limit对应 SQL 里的查询条件。看到res.code === 0这个判断就能推测后端统一返回格式是code、message、data三件套,这是很多教学项目沿用的规范,和真实项目的驼峰命名无关,你只需要知道这个code含义是全局定义的即可。
4.2 页面里的静态资源路径:为什么单独开 HTML 会样式丢失
直接用浏览器双击打开login.html,页面多半是样式全丢的,因为 HTML 里引用的 CSS、JS、图片用的是相对路径或者根路径/css/login.css,根路径在没有后端服务的file://协议下解析不了。正确做法是把工程跑起来后访问http://localhost:8080/login.html,让 Spring Boot 接管静态资源映射。
如果样式还是丢,检查 HTML 里引用的前缀和工程的static目录结构是否对上。有的页面写的是/static/css/login.css,而实际文件在static/css/login.css,访问根路径时会变成/static/static/css/login.css,这就是重复路径问题。解决办法是把 HTML 里的引用统一去掉/static前缀,让 Spring Boot 通过默认映射找。
<!-- 错误写法:访问路径变成 /static/static/css/main.css --> <link rel="stylesheet" href="/static/css/main.css"> <!-- 正确写法:去掉 static 前缀 --> <link rel="stylesheet" href="/css/main.css">改完后刷新浏览器,按 F12 打开开发者工具,在 Network 面板看 CSS 文件的响应状态码。如果 200 说明路径已通,如果 404 就继续调整相对路径。
4.3 表单提交与接口调试的常见做法
登录页和注册页多数是同步表单提交,后端处理完后返回redirect:或者直接返回一个视图名。看代码时注意 Controller 方法上的注解:
@Controller public class LoginController { @PostMapping("/doLogin") public String doLogin(String username, String password, HttpSession session) { // 校验账号密码成功后,把用户信息放进 session session.setAttribute("loginUser", username); return "redirect:/index.html"; } }这段代码表达了一个同步登录闭环:表单 POST 到/doLogin,成功后跳转主页,失败则返回到登录页。要调整登录跳转逻辑,核心是改return的路径。需要注意@Controller返回字符串时会被视图解析器处理,如果你不想走视图解析而直接返回一个外部 URL,要加redirect:前缀。
调试这类接口时,我一般直接在浏览器地址栏输入 URL 加参数测试 GET 接口,POST 接口则用开发者工具的 Console 里执行fetch辅助,这样比反复改 Java 代码重启服务快。另一种常见做法是安装 Postman 类工具,把项目所有接口按 Controller 里的路径整理成一个集合,导出后随时可以回归测试。这个方法尤其在 SQL 数据被改乱后非常有用。
5. 启动与运行避坑:五个常见故障与排查顺序
5.1 现象:数据库连接报错,提示 Access denied 或 Unknown database
启动工程时控制台直接抛Access denied for user 'root'@'localhost',或者Unknown database 'gulimall'。前者原因是 application.yml 里密码和本机不一致,后者原因是 SQL 脚本没执行成功,或者执行时选错了库。
解决步骤是先确认 MySQL 服务和库存在:命令行执行mysql -uroot -p登录后show databases;。如果库不存在就重新执行建库脚本;如果是账号密码问题,把 yml 里改成实际密码。改完配置后重启前,先在 IDEA 里执行 Maven 的clean再compile,避免旧配置被缓存。
5.2 现象:端口被占用,服务启动失败
启动日志里出现Port 8080 was already in use是常见现象。原因是上一次服务没关干净,或者机器上其他程序占了 8080。
# 查看 8080 端口被哪个进程占用(Linux/Mac 用 lsof) netstat -ano | findstr 8080Windows 下用上面命令查出 PID,然后taskkill /PID 进程号 /F杀掉;Mac 和 Linux 下用lsof -i :8080查 PID,再kill。想避免这个坑,可以在 IDEA 的 Run Configuration 里改Server port,但记得同步改 HTML 里的请求 URL,否则页面依然访问的是旧端口。
5.3 现象:页面能打开但看不到样式和图片
页面 HTML 返回了,但 Network 面板里 CSS 全是红字 404。原因是 HTML 中引用的静态资源路径与实际目录不一致,或者浏览器缓存了旧的失败记录。
解决时先按 F12 看具体 404 的 URL,和static目录下的实际路径比对,修好引用路径后强刷浏览器(Ctrl+Shift+R)。如果路径核对没问题但仍加载失败,看static目录下有没有大小写不一致的文件名,Windows 下不敏感,部署到 Linux 服务器时就会出问题,所以文件命名从一开始就别用驼峰加大小写混排。
5.4 现象:登录成功却跳转到 404 页
表单提交后 URL 变成了404页面,原因是redirect路径指向了不存在的页面。教学项目里经常出现return "redirect:/index";,但实际的首页文件名是index.html,这时候要改成redirect:/index.html。
还有一种隐蔽原因是登录成功后在 Controller 里存了 session 数据,但跳转目标使用了模板引擎的路径写法,比如return "index",而项目没配 Thymeleaf 相关依赖,就会视图解析失败。这种问题在控制台里会有一行Error resolving template的日志,看到它基本就是视图路径写法不对。
5.5 现象:数据库中文乱码,SQL 注释和页面数据全是问号
执行完 SQL 后,页面上显示的商品名称、用户昵称全部变成???。原因是 SQL 脚本的字符集和数据库连接字符集不一致,或者建表时没指定 utf8mb4。
解决三步走:第一步确认 SQL 文件头部建库语句带CHARACTER SET utf8mb4;第二步执行时加上--default-character-set=utf8mb4;第三步检查 yml 里的url是否带characterEncoding=utf8。这三处只要有一处不对,中文就会在某个环节变成问号。
5.6 完整排查顺序与验证清单
遇到启动问题不要东一榔头西一棒子,我按效率排了一个顺序:先看数据库连接,再查端口占用,然后看静态资源路径,接着验证登录跳转,最后查字符集。每一类问题都有对应的日志关键词:数据库问题看Access denied、Unknown database,端口问题看Port already in use,路径问题看 HTTP 404,视图问题看Error resolving template。
按这个顺序走完,绝大多数资源包都能在半小时内跑起来。跑起来后做一个冒烟验证:打开首页、登录、浏览一个商品详情、添加购物车、走一遍下单流程。这套动作能确认最重要的表关联和接口链路是通的,之后再改代码心里才有底。
6. 进阶:把教学资源改装成自己的演示项目
拿到手能跑只是第一步,怎么把它变成“你的东西”才是差异化。最直接的做法是全局替换项目名称:把 Java 包名、前端标题、数据库库名三处统一改掉。改包名时不要用 IDEA 的全局替换硬改,正确做法是右键java目录下的根包 →Refactor→Rename,让 IDE 同步更新所有package声明和 import 引用。库名则先建新库再执行 SQL,把 yml 里的url同步改,三处一致后就算是一个包装完整的新项目。
如果要在答辩或演示时更出效果,我建议给它加一个支付结果回调的 Mock 接口。常见做法是写一个简单的 Controller,模拟支付平台回调订单状态:
@RestController public class MockPayCallbackController { @PostMapping("/mockPay/callback") public String callback(@RequestParam String orderSn, @RequestParam String status) { // 这里把订单状态更新为已支付,模拟真实支付回调 return "success"; } }这段代码添加了模拟支付回调接口。参数里的orderSn是订单号,前端在演示时可以把订单号手动填进测试工具或页面输入框,status传paid就把订单状态置为已支付。这样在演示“支付完成后跳转订单详情”的流程时,就不需要真的接入支付平台。你可以写一个极简测试页面,放一个输入框和一个按钮,演示时填入订单号就能现场改状态。
注意一件事:改订单状态的 SQL 不能只 UPDATE 主表,还要把订单明细表和库存流水一起更新,否则会出现订单已支付但库存逻辑对不上的尴尬。这也是工程里最容易露馅的地方,演示前一定要在一个全新测试账号上完整走一遍:注册、登录、下单、支付回调、查看订单详情。
在我第一次拿类似资源做二次开发时,没改库名直接全局搜索替换,结果把 SQL 脚本里的gulimall全换成了新名字,创建的新库叫gulimall的地方变成了xxxmall,但代码里另一处写死的库名没改,启动三分钟后数据库连接报错才发现。从那以后我每次拿到新项目都强制走一遍“按模块替换 + 完整冒烟”的流程,替换一个模块验证一次,不把所有修改攒到最后一起看。这套习惯帮我省掉了不少返工时间,希望帮到你。
本文还有配套的精品资源,点击获取