1. 项目概述与核心价值
这个基于Spring Boot+Vue的线上超市购物管理系统,本质上是一个典型的B2C电商平台简化版实现。我在实际开发中发现,这类系统虽然业务逻辑相对标准化,但涉及的技术栈整合和细节处理恰恰是检验开发者全栈能力的最佳试金石。
系统采用前后端分离架构,后端基于Spring Boot 2.7.x(兼容Spring Boot 4.x新特性)提供RESTful API,前端使用Vue 3组合式API开发管理界面。数据库选用MySQL 8.0,考虑到电商系统对事务一致性的要求,特别针对库存扣减、订单状态流转等核心业务场景设计了ACID事务保障机制。
提示:新手常犯的错误是直接套用开源电商模板,而忽略了根据实际业务需求调整架构设计。比如超市类系统需要特别关注生鲜商品的保质期管理和促销活动叠加计算等特色功能。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术栈选型与项目搭建
2.1 后端技术栈深度解析
Spring Boot的选择绝非偶然——其自动配置特性大幅简化了SSM框架的整合难度。我在pom.xml中精心配置了这些核心依赖:
xml复制<dependencies>
<!-- 持久层 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 安全控制 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 定时任务 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
</dependencies>
特别提醒:Quartz定时任务配置要注意避免"只执行最后一个任务"的经典陷阱。解决方案是在JobDetail定义时使用@DisallowConcurrentExecution注解,并为每个任务创建独立的Trigger实例。
2.2 前端架构设计要点
Vue 3的组合式API相比Options API更适合复杂业务场景。项目采用如下架构:
code复制src/
├── api/ # 接口定义
├── assets/ # 静态资源
├── components/ # 通用组件
├── composables/ # 组合式函数
├── router/ # 路由配置
├── stores/ # Pinia状态管理
├── utils/ # 工具函数
└── views/ # 页面组件
路由管理中使用keep-alive缓存页面状态时,要注意el-table等组件在路由切换时的滚动位置问题。实测有效的解决方案是在路由meta中设置scrollTop记录:
javascript复制router.afterEach((to, from) => {
if (from.meta.keepAlive) {
from.meta.scrollTop = document.documentElement.scrollTop
}
})
3. 核心业务模块实现
3.1 商品管理子系统
商品模块采用树形分类+标签的双维度管理体系,数据库设计遵循以下原则:
sql复制CREATE TABLE `product` (
`id` bigint NOT NULL AUTO_INCREMENT,
`category_id` int NOT NULL COMMENT '所属分类',
`name` varchar(100) NOT NULL,
`price` decimal(10,2) NOT NULL,
`stock` int NOT NULL DEFAULT '0',
`shelf_status` tinyint NOT NULL DEFAULT '0' COMMENT '上架状态',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_category` (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
库存扣减是电商系统的核心难点,我采用乐观锁+事务的方案:
java复制@Transactional
public boolean reduceStock(Long productId, int quantity) {
Product product = productRepository.findById(productId)
.orElseThrow(() -> new BusinessException("商品不存在"));
if (product.getStock() < quantity) {
throw new BusinessException("库存不足");
}
int updated = productRepository.updateStock(productId,
product.getVersion(),
product.getStock() - quantity);
return updated > 0;
}
3.2 订单处理流水线
订单状态机设计采用状态模式,包含以下核心状态:
- 待支付(WAIT_PAYMENT)
- 已支付(PAID)
- 配送中(DELIVERING)
- 已完成(COMPLETED)
- 已取消(CANCELLED)
状态转换通过Spring StateMachine实现:
java复制@Configuration
@EnableStateMachineFactory
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<String, String> {
@Override
public void configure(StateMachineStateConfigurer<String, String> states)
throws Exception {
states
.withStates()
.initial("WAIT_PAYMENT")
.states(EnumSet.allOf(OrderStatus.class));
}
@Override
public void configure(StateMachineTransitionConfigurer<String, String> transitions)
throws Exception {
transitions
.withExternal()
.source("WAIT_PAYMENT").target("PAID")
.event("PAY")
.and()
.withExternal()
.source("PAID").target("DELIVERING")
.event("SHIP");
}
}
4. 系统安全与性能优化
4.1 安全防护体系
采用JWT+Spring Security的认证方案,特别注意以下几点:
- 密码存储使用BCryptPasswordEncoder
- JWT设置合理的过期时间(建议2小时)
- 接口防刷采用Redis计数器+IP限制
- XSS防护通过Jackson的HTML转义实现
安全配置示例:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
4.2 性能调优实战
通过JMeter压力测试发现,商品列表接口在100并发下响应时间超过2秒。优化措施包括:
- 添加Redis缓存层
- 数据库查询优化:建立复合索引、避免SELECT *
- 启用Spring Boot Actuator监控端点
- 配置HikariCP连接池参数:
yaml复制spring:
datasource:
hikari:
maximum-pool-size: 20
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
重要提示:Actuator端点必须做好安全防护,否则可能泄露敏感信息。生产环境建议通过management.endpoints.web.exposure.include属性精确控制开放的端点。
5. 部署与运维方案
5.1 多环境配置管理
采用Profile区分不同环境配置:
code复制resources/
├── application.yml # 公共配置
├── application-dev.yml # 开发环境
├── application-test.yml # 测试环境
└── application-prod.yml # 生产环境
通过spring.profiles.active指定激活的Profile。MySQL生产环境配置示例:
yaml复制spring:
datasource:
url: jdbc:mysql://prod-db:3306/supermarket?useSSL=false&serverTimezone=Asia/Shanghai
username: prod_user
password: ${DB_PASSWORD}
jpa:
show-sql: false
properties:
hibernate:
format_sql: true
5.2 容器化部署实践
Docker Compose部署方案:
dockerfile复制version: '3.8'
services:
backend:
build: ./backend
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
depends_on:
- mysql
- redis
frontend:
build: ./frontend
ports:
- "80:80"
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: supermarket
volumes:
- mysql_data:/var/lib/mysql
volumes:
mysql_data:
前端Dockerfile特别注意多阶段构建优化:
dockerfile复制# 构建阶段
FROM node:16 as build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# 生产阶段
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
6. 开发经验与避坑指南
在项目开发过程中,我总结了这些宝贵经验:
-
事务失效的常见场景:
- 方法非public修饰
- 自调用问题(this.method())
- 异常类型非RuntimeException
- 数据库引擎不支持事务(如MyISAM)
-
Vue性能优化技巧:
- 使用v-if替代v-show处理初始不显示的组件
- 大数据列表采用虚拟滚动
- 复杂计算属性使用computed缓存
- 第三方组件按需引入
-
MySQL调优重点:
- 执行计划分析(EXPLAIN)
- 避免全表扫描
- 索引优化(最左前缀原则)
- 合理设置innodb_buffer_pool_size
-
跨域问题的终极解决方案:
- 开发环境配置代理
- 生产环境使用Nginx反向代理
- 避免使用@CrossOrigin注解暴露全部端点
-
接口文档自动化:
集成Swagger UI并配置权限控制:
java复制@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.supermarket"))
.paths(PathSelectors.any())
.build()
.securitySchemes(Arrays.asList(apiKey()))
.securityContexts(Arrays.asList(securityContext()));
}
private ApiKey apiKey() {
return new ApiKey("JWT", "Authorization", "header");
}
}
这个项目从技术选型到最终部署,每个环节都蕴含着架构设计的思考。比如在商品搜索功能实现时,初期采用LIKE查询导致性能瓶颈,后期通过Elasticsearch重构,QPS从50提升到2000+。这提醒我们:技术方案需要随着业务规模演进,没有银弹架构。
