打开IDEA,导入项目,把数据库脚本一跑,改了配置文件里的密码,启动后端,再npm install && npm run dev,一个完整的网上超市系统就这样跑起来了。这个过程我重复过很多次,每次帮别人调这种基于SpringBoot+Vue的管理系统时,我都会感叹一句:这套组合确实是做JavaWeb课设、毕设、还有小型商用系统最稳妥的路线之一。
如果你正打算做或者正在做类似的“网上超市管理系统”,这篇文章会把从需求分析、技术选型、数据库设计、后端接口开发、前端页面实现的完整链路讲清楚。我尽量把每一步为什么这么做、踩过哪些坑、有哪些可以抄作业的细节都写出来,而不是只给你看一堆“官方文档式”的概念。毕竟,能落地跑通的系统,才算真的完成。
1. 项目概述与需求剖析
1.1 这个系统到底是做什么的
先明确一个概念:网上超市管理系统,本质上是一个“电商系统的精简版”。和淘宝、京东那种庞然大物相比,它砍掉了秒杀、优惠券、推荐算法、分布式事务这些复杂模块,保留了电商最核心的主链路——用户浏览商品、加入购物车、下单、支付(通常用模拟支付)、管理员在后台维护商品和订单。
我见过不少同学把这个项目做复杂了,一上来就想设计十几个表、几十个接口,最后把自己绕晕。其实对于这种系统,核心就两条线:
- 前台用户线:注册登录 → 浏览商品(分类筛选、搜索) → 加入购物车 → 结算下单 → 查看订单。
- 后台管理员线:登录后台 → 商品管理(增删改查、上下架) → 分类管理 → 订单管理(发货、查看详情) → 用户管理。
搞定这两条线,系统的主功能就完整了。其他像轮播图管理、公告发布、评论功能,都是锦上添花,前期可以不做,后期有余力再加。
1.2 功能需求怎么拆解
我在接到这个项目时,第一步不是写代码,而是把功能点一条条列出来,画成一个清晰的清单。下面是我整理的常用功能清单,你可以直接拿来参考:
用户端:
- 注册与登录:用户名+密码,密码用MD5加密存储(生产环境建议BCrypt,下面会讲为什么)。
- 商品浏览:首页展示热销商品、新品推荐,商品列表页支持分类筛选和关键字搜索。
- 商品详情:展示图片、价格、库存、销量、商品描述。
- 购物车:加入商品、修改数量、删除、勾选结算。
- 订单管理:下单(生成订单号)、查看订单列表、查看订单详情、取消订单。
- 个人中心:修改个人信息、收货地址管理。
管理端:
- 商品管理:添加商品(含图片上传)、编辑、删除、上下架。
- 分类管理:商品分类的增删改。
- 订单管理:查看全部订单、按状态筛选、发货操作。
- 用户管理:查看用户列表、禁用/启用用户。
- 数据统计:简单的销售额、订单量统计(用图表展示可选)。
需求梳理清楚之后,再去做技术选型和数据库设计,就会非常顺畅。很多项目做到一半推倒重来,基本都是前期需求没想清楚。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型:为什么是这套组合
2.1 前后端分离架构的价值
SpringBoot + Vue这套组合最大的特点就是“前后端分离”。前端跑在Vue的开发服务器上(默认localhost:8080),后端跑在SpringBoot内置的Tomcat上(默认localhost:8080),两者通过HTTP接口通信。
前后端分离的好处,我用大白话讲:
- 职责清晰:前端只负责页面的展示和交互,后端只负责业务逻辑和数据存储。改前端页面不会动到后端代码,反之亦然。
- 并行开发:前后端可以同时开工。只要提前约定好接口格式(返回什么字段、什么状态码),前端用Mock数据先画页面,后端专心写接口。这在真实团队里是基本操作。
- 独立部署:前端打包成静态文件扔到Nginx,后端打成Jar包独立运行,扩展起来方便。
对于做毕设或课设的同学,还有一个隐藏好处:答辩时老师问你架构,你能把“前后端分离”这个概念讲得头头是道,这就是加分项。
2.2 后端技术栈详解
后端选型,核心就是SpringBoot + MyBatis + MySQL这套“老三样”,我再补一个Spring Security或者JWT做认证。具体每个组件的角色:
- SpringBoot:快速构建项目的脚手架。它帮你把SpringMVC、事务管理、Jackson序列化这些基础配置都收进内置约定里,不用再写一堆XML配置文件。Tomcat也内嵌了,一个
java -jar就能启动。 - MyBatis:ORM框架,负责Java对象和数据库记录之间的映射。相比JPA,MyBatis的SQL是自己控制的,对于这种多表联查比较多的电商系统,写原生SQL查订单详情、统计销量反而更顺手。
- MySQL:关系型数据库,存储用户、商品、订单这些结构化数据。8.0版本是当前主流,注意时区配置这个坑(后面细说)。
- JWT / Token:用户登录后签发一个Token,后续请求在Header里带着,后端通过拦截器校验身份。
关于密码加密,我多说一句。很早以前很多教程用MD5直接加密,但MD5已经被证明可以暴力撞库,所以现在更推荐BCrypt。Spring Security框架里自带BCryptPasswordEncoder,也可以用hutool工具包的BCrypt工具类,用起来不复杂,安全性却提升一大截。
2.3 数据库选型与MyBatis的搭配逻辑
MySQL的选型理由不用多说,开源免费、资料多、语法通用。到了MyBatis这一层,我需要强调一个使用习惯:能用注解就别写XML,但要写复杂SQL时XML反而更清晰。
简单增删改查,直接在Mapper接口方法上写@Select、@Insert这类注解就行。涉及动态SQL(比如条件不确定的查询、批量插入),XML里写<if>、<foreach>更直观。MyBatis这种“半自动化”的特性,让它特别适合业务逻辑相对固定、但查询条件灵活的管理系统。
在配置MyBatis时有个小细节:map-underscore-to-camel-case这个配置一定要打开。数据库字段user_name就能自动映射到Java实体类的userName属性,省去写一堆resultMap的麻烦。
yaml复制mybatis:
configuration:
map-underscore-to-camel-case: true
mapper-locations: classpath:mapper/*.xml
3. 数据库设计与建模
3.1 核心表结构设计
数据库设计是整个系统的地基。我见过太多人上来就写代码,后面发现字段不够用、表关系乱,回头改表改到头秃。对于网上超市系统,我设计的是下面这些核心表:
用户表(user)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键,自增 |
| username | varchar(50) | 用户名,唯一索引 |
| password | varchar(100) | 加密后的密码 |
| nickname | varchar(50) | 昵称 |
| phone | varchar(20) | 手机号 |
| avatar | varchar(255) | 头像图片URL |
| role | tinyint | 角色,0-普通用户,1-管理员 |
| status | tinyint | 状态,0-正常,1-禁用 |
| create_time | datetime | 注册时间 |
商品分类表(category)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| name | varchar(50) | 分类名称 |
| sort | int | 排序权重,越小越靠前 |
| create_time | datetime | 创建时间 |
商品表(product)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| category_id | bigint | 所属分类,外键 |
| name | varchar(100) | 商品名称 |
| subtitle | varchar(255) | 副标题/卖点描述 |
| main_image | varchar(500) | 主图URL |
| detail_image | text | 详情图片,可以是多个URL拼接 |
| price | decimal(10,2) | 当前售价 |
| stock | int | 库存 |
| sales | int | 销量 |
| status | tinyint | 状态,1-上架,0-下架 |
| create_time | datetime | 创建时间 |
| update_time | datetime | 更新时间 |
购物车表(cart)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| user_id | bigint | 用户ID |
| product_id | bigint | 商品ID |
| quantity | int | 数量 |
| checked | tinyint | 是否选中结算 |
| create_time | datetime | 添加时间 |
订单表(order)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| order_no | varchar(50) | 订单号,唯一 |
| user_id | bigint | 下单用户 |
| total_price | decimal(10,2) | 订单总价 |
| status | tinyint | 订单状态:0-待付款,1-待发货,2-待收货,3-已完成,4-已取消 |
| receiver_name | varchar(50) | 收货人姓名 |
| receiver_phone | varchar(20) | 收货人电话 |
| receiver_address | varchar(255) | 收货地址 |
| create_time | datetime | 下单时间 |
| pay_time | datetime | 支付时间 |
| deliver_time | datetime | 发货时间 |
订单明细表(order_item)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| order_id | bigint | 订单ID |
| product_id | bigint | 商品ID |
| product_name | varchar(100) | 商品名称(冗余存储) |
| product_image | varchar(500) | 商品主图(冗余存储) |
| price | decimal(10,2) | 下单时单价 |
| quantity | int | 数量 |
| total_price | decimal(10,2) | 小计金额 |
3.2 订单与商品的关系设计
订单表里我故意存了receiver_name、receiver_phone、receiver_address这些冗余字段,而不是用单独的地址表再关联。为什么?因为订单是“快照型”数据——用户下单那一刻的收货信息、商品信息必须固定下来。如果下单后用户改了地址或者商品下架了,订单里的信息不能跟着变。这在电商行业是基本常识。
order_item里冗余了product_name和product_image,道理一样。商品可能改名、换图,甚至被删除,但订单明细必须保留当时购买的信息。这种“用空间换准确性”的设计,在业务系统里非常常见。
3.3 索引与性能优化
小项目可能感觉不到索引的作用,但数据量上来之后,索引就是命根子。这个系统我建议至少建立这些索引:
user.username:唯一索引,登录查询用。product.category_id:按分类查商品。product.status:查上架商品。order.user_id:查用户订单列表。order.order_no:唯一索引,订单号查询。order_item.order_id:查订单明细。
建索引的时候注意一个原则:索引不是越多越好,每个索引都会占用磁盘空间,还会拖慢写入速度。只给“高频查询条件”建索引就够了。
sql复制ALTER TABLE `order` ADD INDEX idx_user_id (user_id);
ALTER TABLE `order` ADD UNIQUE INDEX uk_order_no (order_no);
4. 后端核心功能实现
4.1 项目结构与分层设计
后端代码结构我习惯按这种分包方式:
code复制src/main/java/com/example/supermarket/
├── common/ # 通用类:Result返回体、异常处理、常量
├── config/ # 配置类:CORS、WebMvc、拦截器注册
├── controller/ # 接口层:接收请求、返回结果
├── service/ # 业务层:核心逻辑
│ └── impl/ # 业务实现类
├── mapper/ # MyBatis持久层接口
├── entity/ # 数据库实体类
├── dto/ # 数据传输对象(接收前端参数)
└── vo/ # 视图对象(返回给前端的数据)
分层的好处是职责单一:Controller只做参数接收和结果返回,Service写业务流程,Mapper写SQL。一开始觉得多写几个包很麻烦,等要改功能的时候你会发现,定位代码的速度快得不是一点半点。
统一返回体是必须的。我通常定义一个Result类,包含code、message、data三个字段,所有接口都返回这个结构。前端就能统一处理响应,不用一个接口一种格式。
java复制@Data
public class Result<T> {
private Integer code;
private String message;
private T data;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMessage("操作成功");
result.setData(data);
return result;
}
public static <T> Result<T> error(Integer code, String message) {
Result<T> result = new Result<>();
result.setCode(code);
result.setMessage(message);
return result;
}
}
4.2 用户认证与登录状态管理
登录这块,我推荐用JWT(JSON Web Token)而不是传统的Session。Session需要服务端保存会话状态,在多实例部署时会话同步是个麻烦事;JWT是无状态的,Token本身携带用户信息,服务端只要验签即可信任。
JWT的流程很简单:
- 用户提交用户名和密码,后端校验通过后,生成一个Token返回给前端。
- 前端把Token存在
localStorage里,每次请求在Header中带上Authorization: Bearer <token>。 - 后端写一个拦截器,拦截需要登录的接口,从Token中解析出用户ID和角色,放入
ThreadLocal(或Request属性)供后续逻辑使用。
生成Token我用jjwt这个库,代码大概这样:
java复制String token = Jwts.builder()
.setSubject(userId.toString())
.claim("role", user.getRole())
.setExpiration(new Date(System.currentTimeMillis() + 7 * 24 * 3600 * 1000))
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
secretKey一定要放到配置文件里,不要写死在代码中。Token有效期我一般设7天,既保证用户体验,又不至于太长导致安全风险。
4.3 商品管理模块实现
商品模块是系统的核心,前后端都围绕它转。后端接口要覆盖:
- 分页查询商品列表,支持按分类、关键字、价格区间筛选。
- 根据ID查询商品详情。
- 管理员新增、修改、删除商品。
- 管理员上下架商品。
分页查询是高频需求。MyBatis里我用通用分页插件PageHelper,引入依赖后在Service层写一行PageHelper.startPage(pageNum, pageSize),后面的查询自动带上LIMIT,返回数据也自动封装成PageInfo对象,非常方便。
商品列表接口的SQL需要注意:当搜索关键字和分类条件都不存在时,不能用WHERE 1=1这种写法。正确做法是用MyBatis的动态SQL,让条件的拼接由框架处理:
xml复制<select id="selectProductList" resultType="com.example.supermarket.vo.ProductVO">
SELECT p.*, c.name AS category_name
FROM product p
LEFT JOIN category c ON p.category_id = c.id
<where>
<if test="keyword != null and keyword != ''">
AND p.name LIKE CONCAT('%', #{keyword}, '%')
</if>
<if test="categoryId != null">
AND p.category_id = #{categoryId}
</if>
AND p.status = 1
</where>
ORDER BY p.create_time DESC
</select>
<where>标签会自动处理首行的AND,比“逗号拼接”这种手写方式安全很多。
4.4 购物车与订单流程
购物车的数据结构很简单:每个用户对每个商品,在购物车里最多一条记录,数量可以增减。所以cart表最好加一个UNIQUE(user_id, product_id)约束,防止重复数据。
加购物车时先查一下这个用户的购物车是否有该商品,有就加数量,没有就新增记录。用一行MySQL的INSERT ... ON DUPLICATE KEY UPDATE就能原子化地处理这个逻辑,避免并发问题。
下单是整个系统最需要小心的地方。我的建议是把“创建订单”和“扣库存”放在同一个事务里:
java复制@Transactional(rollbackFor = Exception.class)
public OrderVO createOrder(Long userId, List<CartItemDTO> items) {
// 1. 校验商品库存
// 2. 计算订单总价
// 3. 创建订单主记录
// 4. 创建订单明细
// 5. 扣减库存、增加销量
// 6. 清空购物车对应商品
}
注意,事务里的每一步出错都要抛出异常,事务才会回滚。我见过有同学在Service里自己catch异常但不往外抛,结果库存扣了订单没生成,数据就乱了。所以要么不catch,要么catch之后重新抛一个RuntimeException。
库存扣减的SQL也要注意并发问题。直接用UPDATE product SET stock = stock - 1 WHERE id = ? AND stock > 0这种带条件的更新,能避免超卖:
java复制int rows = productMapper.reduceStock(productId, quantity);
if (rows == 0) {
throw new RuntimeException("库存不足");
}
4.5 后台管理功能
后台管理相比用户端,代码反而更简单,因为它就是常规的增删改查。但有几个细节值得注意:
删除商品:如果商品已经有订单关联,不能物理删除,否则订单明细就成了“孤儿数据”。我的做法是“逻辑删除”:给product表加一个deleted字段,删除时置为1,查询时默认过滤掉deleted=1的记录。或者干脆只提供“下架”操作,不提供删除。后台管理系统里,数据只做“软处理”是更稳妥的方案。
图片上传:商品图片不能存数据库里,数据库存URL即可。前端把图片文件传到一个上传接口,后端把文件保存到本地磁盘(D:/upload/之类),然后返回可访问的URL。开发环境可以配置一个静态资源映射:
java复制@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/upload/**")
.addResourceHandler("file:D:/upload/");
}
}
正式部署时再用Nginx把/upload/映射到磁盘目录,这样图片访问和后端服务就解耦了。
数据统计:如果要做首页的“今日订单量”“总销售金额”,写一个聚合查询的SQL就行。用MyBatis返回一个Map或者专门的VO对象。图表展示可以前端用ECharts,后端只提供数据。
5. 前端Vue实现要点
5.1 前端项目结构与路由设计
Vue这边我推荐用Vue CLI或者Vite创建项目。Vite启动速度快,而且Vue 3的生态已经很成熟,建议直接用Vue 3 + Vite。如果对Vue 2特别熟,用Vue 2 + Vue CLI也没问题,但新项目没必要守着旧版本。
前端项目结构:
code复制src/
├── api/ # 接口封装,一个模块一个文件
├── assets/ # 静态资源
├── components/ # 公共组件
├── router/ # 路由配置
├── store/ # 状态管理(Pinia或Vuex)
├── views/ # 页面组件
│ ├── user/ # 用户端页面
│ └── admin/ # 管理端页面
├── utils/ # 工具函数(axios封装等)
├── App.vue
└── main.js
路由设计直接反映页面结构。用户端路由我用懒加载方式:
javascript复制const routes = [
{ path: '/', component: () => import('@/views/user/Home.vue') },
{ path: '/product/:id', component: () => import('@/views/user/ProductDetail.vue') },
{ path: '/cart', component: () => import('@/views/user/Cart.vue') },
{ path: '/order', component: () => import('@/views/user/OrderList.vue') },
{ path: '/login', component: () => import('@/views/Login.vue') },
// 管理端
{
path: '/admin',
component: () => import('@/views/admin/Layout.vue'),
children: [
{ path: 'products', component: () => import('@/views/admin/ProductManage.vue') },
{ path: 'orders', component: () => import('@/views/admin/OrderManage.vue') },
{ path: 'categories', component: () => import('@/views/admin/CategoryManage.vue') }
]
}
]
懒加载(() => import())的作用是按需加载组件,首屏只下载当前页面需要的JS,整体包体积小很多。我当时第一次用Vite构建,没做懒加载,首屏要下800KB的JS,加载明显偏慢,拆分之后体验好了很多。
5.2 页面组件拆解
页面组件拆分的核心原则:一个页面只做一件事,公共的东西抽出去。比如商品卡片在首页、搜索结果页、分类页都会用到,就抽成一个ProductCard组件。
用户端核心页面:
- 首页:轮播图 + 分类导航 + 热销商品。数据从后端接口拉取,用
onMounted里调用。 - 商品列表页:接收分类ID或搜索关键字,分页展示商品。筛选条件变了就重新请求数据。
- 商品详情页:展示大图、价格、库存,数量选择器,加入购物车按钮。
- 购物车页:展示购物车列表,支持勾选、修改数量、删除,底部显示总价,点击结算跳转下单。
- 订单列表页:按状态Tab切换订单列表,每个订单可以看详情。
管理端页面我用Element Plus组件库,表格用el-table,表单用el-form,几乎不用写太多自定义样式就能出来一个像样的后台界面。Element Plus在Vue 3项目里已经是事实标准了,文档齐全,照着示例改就行。
前端封装axios是个关键步骤。统一设置baseURL、请求头带Token、拦截响应处理错误码:
javascript复制import axios from 'axios'
import { ElMessage } from 'element-plus'
import router from '@/router'
const request = axios.create({
baseURL: '/api',
timeout: 10000
})
// 请求拦截器:自动携带Token
request.interceptors.request.use(config => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
// 响应拦截器:统一处理业务错误码和登录过期
request.interceptors.response.use(
response => {
const res = response.data
if (res.code !== 200) {
ElMessage.error(res.message)
if (res.code === 401) {
router.push('/login')
}
return Promise.reject(new Error(res.message))
}
return res
},
error => {
ElMessage.error('网络异常,请稍后重试')
return Promise.reject(error)
}
)
export default request
5.3 状态管理与接口封装
Vue 3里状态管理我推荐Pinia,比Vuex更简单,类型支持也更好。网上超市这个系统需要全局状态的地方不多,主要的场景是:
- 用户登录信息(用户ID、昵称、头像、角色)。
- 购物车数量角标(顶部导航栏显示“购物车(3)”这种效果)。
购物车数量这种数据,如果每进一个页面都重新请求接口,会有点浪费;存到Pinia里,加入购物车后更新状态,页面跳转时状态还在,体验就很顺畅。
接口封装我习惯按模块拆文件:api/user.js、api/product.js、api/order.js、api/cart.js。每个文件里的函数就是一个个具体的请求方法:
javascript复制// api/product.js
import request from '@/utils/request'
export function getProductList(params) {
return request.get('/product/list', { params })
}
export function getProductDetail(id) {
return request.get(`/product/${id}`)
}
这样做的最大好处是:页面组件里不用直接写URL,接口路径集中管理,后端改了路由只需要改一个地方。
6. 联调、部署与常见问题排查
6.1 前后端联调注意点
前后端分离开发中,联调是必踩坑的环节。最典型的问题是跨域(CORS)。前端跑在http://localhost:5173,后端跑在http://localhost:8080,端口不同,浏览器的同源策略会拦截请求。
解决跨域有两个思路,推荐第二种:
- 后端开启CORS,在SpringBoot里配置
addCorsMappings。 - 前端用Vite的代理转发。在
vite.config.js里配置:
javascript复制export default {
server: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}
}
这样前端请求/api/product/list,Vite开发服务器会转发到http://localhost:8080/product/list。浏览器看到的是同源请求,不会触发跨域。生产环境部署时,再用Nginx做一层同样的代理转发即可。
6.2 高频报错与解决方案
我整理一下这个项目里出现频率最高的几个问题,都是实测中遇到的:
| 问题 | 原因 | 解决方案 |
|---|---|---|
数据库连接报Server returns invalid timezone |
MySQL 8.0时区配置问题 | 连接串加serverTimezone=Asia/Shanghai |
| 前端访问图片404 | 静态资源映射没配 | 后端配置WebMvcConfigurer的addResourceHandlers |
| Vue路由刷新后404 | history模式配合Nginx没配try_files | Nginx配置try_files $uri $uri/ /index.html; |
接口返回500,日志里Invalid bound statement |
Mapper XML路径没扫到 | 检查mapper-locations和XML的namespace |
| 修改密码后登录失败 | 加密方式不一致 | 注册和登录必须用同一个加密工具类 |
| 订单创建后库存没变 | 事务没加或异常被吞 | Service方法加@Transactional并向外抛异常 |
第一个时区问题估计每个人都遇过。MySQL 8.0之后默认时区和驱动默认时区不一致,报错信息挺迷惑的。解决方案是在application.yml里做完整配置:
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/supermarket?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
username: root
password: your_password
driver-class-name: com.mysql.cj.jdbc.Driver
字符集characterEncoding=utf8也很关键,不然商品描述里的中文会变乱码。
6.3 部署上线经验
开发完成后部署上线,有两种常见方式:
方式一:前后端分离部署
- 后端:
mvn package打成jar包,扔到服务器上nohup java -jar supermarket.jar &。 - 前端:
npm run build生成dist目录,扔到Nginx的html目录,配置好反向代理和try_files。
方式二:统一部署到Tomcat
把前端dist里的文件复制到后端src/main/resources/static目录下,重新打成一个Jar包,直接用java -jar启动,前端页面由SpringBoot内置的静态资源处理机制托管。这种方式适合课设演示和小型内部系统,省去配置Nginx的麻烦。
我用过方式二给一个朋友部署过小超市收银系统,整个过程真的很省事。但要注意:由于前端页面和后端接口同源了,前端axios的baseURL直接设成空字符串或者同路径相对路径即可。
关于生产环境的配置文件,一定要把数据库密码、JWT密钥这些敏感信息抽到环境变量或者外部配置文件中,不要打包进Jar里。万一源码泄露,至少数据库信息不用跟着一起完蛋。
7. 实操心得与扩展建议
做完这个系统,我自己最大的体会是:一个能跑通全流程的“小系统”,比一个半途而废的“大系统”有价值得多。
网上超市管理系统听起来简单,但它把电商主链路的每个环节——用户认证、商品展示、购物车、订单事务、文件上传、权限控制——都串起来了。你把这个项目吃透,SpringBoot的自动配置原理、MyBatis的动态SQL、Vue的组件通信、前端的接口封装、JWT认证这套流程,就都有了真实的实践基础。去面Java后端岗位时,这完全是一个可以拿出来讲的完整项目。
最后分享两个我在项目中后来才意识到的小技巧:
- 接口文档一定要写。哪怕是用Postman导出一份JSON集合,发给前端同学或者答辩老师看一眼,都会让人觉着专业许多。
- 种子数据提前准备。在数据库脚本里就插入好商品分类、十来个商品、一个管理员账号。否则每次重新部署环境,你都要手动录数据,非常痛苦。
如果还想继续扩展,可以在这个基础上加Redis缓存商品列表(热点数据查询性能立竿见影)、加Elasticsearch做商品搜索、加RabbitMQ处理订单超时取消,或者把静态资源迁移到OSS。这些方向每一个都是成长的好话题,但前提是先把当前这个系统做到干净、完整、可运行。先把地基打牢,后面想加几层楼,都是水到渠成的事。
