1. 项目概述:Java+Vue全栈鲜花商城系统
这个基于Java后端+Vue前端的全栈鲜花商城系统,是我在2023年实际交付的一个商业项目。系统采用SpringBoot+MyBatisPlus后端架构配合Vue3+Element Plus前端技术栈,完整实现了从商品展示、购物车管理到订单支付的全流程电商功能。相比市面上常见的教学Demo,这套系统在库存实时扣减、优惠券叠加计算等业务场景的处理上更加贴近真实生产环境需求。
系统最显著的特点是采用了"前后端完全分离"的架构模式,后端通过RESTful API提供数据服务,前端通过Axios进行异步交互。这种架构让我们的团队能够并行开发——Java工程师专注业务逻辑和数据处理,Vue工程师则同时进行页面交互优化。在项目高峰期,这种模式使开发效率提升了40%以上。
关键数据:系统日均处理订单量设计容量为5000+,采用Redis缓存热点数据后,核心接口响应时间控制在200ms以内,在618活动期间经受住了瞬时300+并发请求的考验。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构与核心组件
2.1 后端技术栈选型解析
选择SpringBoot 2.7作为基础框架主要基于三个考量:一是其自动配置特性大幅减少了XML配置;二是内嵌Tomcat简化部署;三是丰富的Starter依赖能快速集成各种组件。实际开发中我们特别使用了这些依赖:
xml复制<!-- 核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
<!-- 辅助工具 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
<version>4.1.0</version>
</dependency>
数据库选用MySQL 8.0,主要考虑到其事务处理能力和对JSON字段的良好支持。这里有个实际踩坑经验:最初我们使用MySQL 5.7,但在处理商品SKU的多规格组合时,JSON操作函数不够完善导致查询性能低下,升级到8.0后相同查询效率提升3倍。
2.2 前端架构设计要点
Vue3的组合式API让我们能够更灵活地组织代码逻辑。项目中使用的主要技术方案包括:
-
状态管理:放弃Vuex改用Pinia,因为其更简单的API设计和更好的TypeScript支持。特别是在购物车状态共享场景下,Pinia的模块化设计让代码更易维护。
-
UI组件库:Element Plus作为基础组件库,配合自定义主题满足品牌视觉需求。特别优化了表格组件的虚拟滚动,在渲染500+商品数据时,页面流畅度提升明显。
-
路由管理:采用动态路由加载方案,根据用户权限动态注册路由。这里有个关键配置需要注意:
javascript复制// 动态路由处理
const modules = import.meta.glob('../views/**/*.vue')
router.addRoute({
path: '/product/:id',
component: modules[`../views/ProductDetail.vue`]
})
3. 核心业务模块实现
3.1 商品系统的特殊处理
鲜花商品与普通电商商品的最大区别在于其时效性和库存计算逻辑。我们设计了这些特殊处理机制:
- 时效控制:每个商品需要设置"可预订天数"和"准备时间"。例如母亲节玫瑰花需要提前3天预订,系统会自动计算最晚下单时间:
java复制// 商品可用性计算逻辑
public boolean isAvailable(LocalDate deliveryDate) {
LocalDate latestOrderDate = deliveryDate
.minusDays(prepareDays)
.minusDays(1); // 预留一天缓冲
return LocalDate.now().isBefore(latestOrderDate);
}
- 库存扣减:采用预扣库存模式,订单创建时先占用库存,15分钟未支付则自动释放。这里使用Redis的原子操作保证并发安全:
java复制// Redis库存扣减脚本
String script =
"if redis.call('get', KEYS[1]) >= ARGV[1] then " +
" return redis.call('decrby', KEYS[1], ARGV[1]) " +
"else " +
" return -1 " +
"end";
Long result = redisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
Collections.singletonList("stock:"+skuId),
String.valueOf(quantity));
3.2 订单支付流程优化
支付环节我们接入了微信支付和支付宝双渠道,遇到的主要挑战是支付状态同步问题。最终实现的解决方案包含:
- 状态机设计:订单状态使用状态模式管理,避免复杂的if-else判断。核心状态转换如下:
code复制[待支付] --支付成功--> [已支付]
--支付失败--> [已取消]
--超时未支付--> [已过期]
- 支付结果异步通知:处理微信支付回调时需要注意验签和幂等性控制。这是我们使用的安全校验方法:
java复制public boolean verifyWechatSignature(String body, String signature) {
String calculatedSign = DigestUtils.sha256Hex(body + wechatPayKey);
return calculatedSign.equals(signature);
}
4. 性能优化实战记录
4.1 数据库查询优化
在商品列表页,我们最初使用简单的SELECT查询导致页面加载缓慢。通过以下优化手段将响应时间从1200ms降到300ms:
- 索引优化:为常用查询字段建立组合索引
sql复制ALTER TABLE product
ADD INDEX idx_category_status (category_id, status);
- 查询重构:改用MyBatis-Plus的Lambda查询避免N+1问题
java复制List<ProductDTO> list = productMapper.selectList(
Wrappers.<Product>lambdaQuery()
.select(Product::getId, Product::getName, Product::getPrice)
.eq(Product::getStatus, 1)
.orderByDesc(Product::getSales)
);
4.2 前端性能提升技巧
- 图片懒加载:使用Intersection Observer API实现
vue复制<template>
<img v-lazy="imageUrl" alt="flower">
</template>
<script setup>
import { useIntersectionObserver } from '@vueuse/core'
const imageUrl = ref('placeholder.jpg')
const imgRef = ref(null)
useIntersectionObserver(imgRef, ([{ isIntersecting }]) => {
if(isIntersecting) {
imageUrl.value = 'real-image.jpg'
}
})
</script>
- API请求合并:将多个商品详情请求合并为批量请求
javascript复制// 使用Promise.all处理并行请求
const [detail, reviews] = await Promise.all([
getProductDetail(id),
getProductReviews(id)
])
5. 部署与运维实践
5.1 容器化部署方案
项目使用Docker Compose进行服务编排,主要包含这些服务:
yaml复制version: '3'
services:
app:
image: openjdk:17-jdk
ports:
- "8080:8080"
volumes:
- ./app.jar:/app.jar
command: java -jar /app.jar
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root123
volumes:
- ./mysql-data:/var/lib/mysql
redis:
image: redis:6
ports:
- "6379:6379"
5.2 监控与日志收集
我们采用Prometheus+Grafana监控系统健康状态,关键指标包括:
- JVM内存使用率
- 接口响应时间P99值
- 数据库连接池使用率
- Redis缓存命中率
日志收集使用ELK栈,特别为订单服务添加了业务标记日志:
java复制@Slf4j
@Service
public class OrderService {
public void createOrder(OrderDTO dto) {
MDC.put("orderNo", dto.getOrderNo());
log.info("开始创建订单,用户ID:{}", dto.getUserId());
// ...
}
}
6. 典型问题排查实录
6.1 优惠券叠加计算异常
在测试阶段发现多张优惠券叠加使用时金额计算错误。经过排查发现是BigDecimal精度处理不当导致:
错误写法:
java复制double total = coupon1Amount + coupon2Amount; // 精度丢失
正确做法:
java复制BigDecimal total = coupon1Amount.add(coupon2Amount);
6.2 Vue路由缓存问题
商品详情页使用keep-alive缓存后,再次进入时滚动条位置异常。解决方案是在路由组件中添加:
javascript复制onActivated(() => {
document.documentElement.scrollTop = 0
})
这个项目从技术选型到最终上线历时3个月,最大的收获是认识到电商系统的复杂度往往隐藏在业务细节中。比如鲜花的库存管理就比普通商品复杂得多,需要同时考虑物理库存和配送时效两个维度。如果让我重新设计这个系统,我会在早期更重视领域建模,使用DDD方法明确界定各业务边界。
