1. 项目概述:社区生鲜团购系统的全栈实现
这个基于Spring Boot和Vue3的社区生鲜团购系统,是我在实际社区运营需求中开发的一个全栈解决方案。系统采用前后端分离架构,后端使用Spring Boot提供RESTful API,前端使用Vue3构建响应式用户界面,实现了从商品管理、订单处理到配送跟踪的完整生鲜电商流程。
在社区场景下,生鲜团购有几个特殊需求:首先是时效性要求高,从下单到配送通常需要在24小时内完成;其次是库存管理需要实时精确,因为生鲜商品容易变质;最后是支付和结算流程要灵活,支持多种社区常见的支付方式。这套系统正是针对这些痛点设计的,目前已在三个社区实际运行超过半年,稳定支撑日均300+订单的处理。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术栈选型与架构设计
2.1 为什么选择Spring Boot + Vue3组合
后端选择Spring Boot主要考虑其快速开发特性和丰富的生态系统。Spring Data JPA极大简化了数据库操作,Spring Security提供了完善的权限控制,而Spring Boot Actuator则方便系统监控。特别值得一提的是,我们在商品库存管理模块使用了Spring的@Transactional注解和@Cacheable缓存注解的组合,有效解决了高并发下的库存超卖问题。
前端选择Vue3而不是React或Angular,主要看中其组合式API带来的更好代码组织方式,以及更小的运行时体积。Vue3的响应式系统重构后性能提升明显,对于需要频繁更新商品状态和订单信息的生鲜系统特别重要。我们实测下来,同样功能的页面,Vue3比Vue2的渲染速度快了约18%。
2.2 系统架构详解
系统采用典型的三层架构:
- 表现层:Vue3 + Element Plus + Axios
- 业务逻辑层:Spring Boot + Spring Security
- 数据持久层:MySQL + Redis + MyBatis
部署架构上,我们使用Nginx作为反向代理和静态资源服务器,后端服务采用Docker容器化部署,方便横向扩展。数据库主从分离,读写操作分开,Redis作为缓存层减轻数据库压力。
3. 核心功能模块实现
3.1 商品管理与库存系统
生鲜商品管理有几个特殊需求:
- 需要支持多规格(如重量、包装)
- 需要设置截单时间
- 需要实时库存预警
我们设计了这样的数据模型:
java复制@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
@OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
private List<ProductSpec> specs;
private LocalDateTime orderDeadline; // 截单时间
// 其他字段...
}
@Entity
public class ProductSpec {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String specName; // 如"500g装"
private BigDecimal price;
@ManyToOne
@JoinColumn(name = "product_id")
private Product product;
private Integer stock;
// 其他字段...
}
库存扣减是生鲜系统的关键,我们实现了分布式锁防止超卖:
java复制public boolean reduceStock(Long specId, int quantity) {
String lockKey = "product_stock_lock:" + specId;
try {
// 尝试获取分布式锁
boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
if (!locked) {
throw new BusinessException("系统繁忙,请稍后再试");
}
ProductSpec spec = productSpecRepository.findById(specId)
.orElseThrow(() -> new EntityNotFoundException("商品规格不存在"));
if (spec.getStock() < quantity) {
return false;
}
spec.setStock(spec.getStock() - quantity);
productSpecRepository.save(spec);
return true;
} finally {
// 释放锁
redisTemplate.delete(lockKey);
}
}
3.2 订单系统与支付集成
订单系统设计考虑了社区场景的特殊性:
- 支持团长代收货款
- 支持到店自提
- 支持拼团优惠
订单状态机设计如下:
mermaid复制stateDiagram-v2
[*] --> 待支付
待支付 --> 已取消: 超时未支付
待支付 --> 已支付: 支付成功
已支付 --> 已发货: 商品准备完成
已发货 --> 已完成: 用户确认收货
已发货 --> 售后中: 用户发起售后
售后中 --> 已完成: 售后处理完成
售后中 --> 已取消: 退款成功
支付系统集成了微信支付和支付宝,同时支持线下支付标记。关键代码:
java复制@PostMapping("/orders/{id}/pay")
public ResponseEntity<?> payOrder(
@PathVariable Long id,
@RequestParam String paymentType,
HttpServletRequest request) {
Order order = orderService.getOrder(id);
if (order.getStatus() != OrderStatus.PENDING_PAYMENT) {
throw new BusinessException("订单状态异常");
}
if ("wechat".equals(paymentType)) {
// 微信支付逻辑
String ip = getClientIp(request);
Map<String, String> params = wechatPayService.createPayment(order, ip);
return ResponseEntity.ok(params);
} else if ("offline".equals(paymentType)) {
// 线下支付标记
orderService.markAsPaid(id, PaymentMethod.OFFLINE);
return ResponseEntity.ok().build();
}
// 其他支付方式...
}
3.3 配送与团长管理系统
社区团购的特色是依赖团长进行商品分发。我们设计了这样的团长管理功能:
- 团长申请与审核
- 团长专属二维码
- 团长收益计算
- 团长数据看板
团长收益计算考虑了多种因素:
java复制public BigDecimal calculateCommission(Order order) {
BigDecimal commissionRate = getBaseCommissionRate(order.getGroupLeader());
// 新用户首单加成
if (userService.isFirstOrder(order.getUser())) {
commissionRate = commissionRate.add(new BigDecimal("0.02"));
}
// 商品类目加成
BigDecimal categoryBonus = productService.getCategoryCommissionBonus(
order.getItems());
// 特殊活动加成
BigDecimal activityBonus = promotionService.getCurrentCommissionBonus();
return order.getTotalAmount()
.multiply(commissionRate.add(categoryBonus).add(activityBonus));
}
4. 前端关键实现
4.1 Vue3组合式API实践
商品详情页使用Vue3的组合式API组织代码:
javascript复制<script setup>
import { ref, computed, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { getProductDetail } from '@/api/product'
import { addToCart } from '@/api/cart'
const route = useRoute()
const product = ref(null)
const selectedSpec = ref(null)
const quantity = ref(1)
const price = computed(() => {
return selectedSpec.value
? selectedSpec.value.price * quantity.value
: 0
})
const loadData = async () => {
const { data } = await getProductDetail(route.params.id)
product.value = data
if (data.specs.length > 0) {
selectedSpec.value = data.specs[0]
}
}
const handleAddToCart = async () => {
if (!selectedSpec.value) return
await addToCart({
specId: selectedSpec.value.id,
quantity: quantity.value
})
// 提示添加成功...
}
onMounted(() => {
loadData()
})
</script>
4.2 性能优化实践
针对生鲜系统商品图片多的特点,我们做了以下优化:
- 图片懒加载
- WebP格式转换
- 前端缓存策略
图片懒加载实现:
javascript复制<template>
<img
v-for="img in images"
:key="img.id"
:data-src="img.url"
class="lazy-image"
alt="商品图片"
>
</template>
<script setup>
import { onMounted } from 'vue'
const lazyLoad = () => {
const lazyImages = document.querySelectorAll('.lazy-image')
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target
img.src = img.dataset.src
observer.unobserve(img)
}
})
})
lazyImages.forEach(img => observer.observe(img))
}
onMounted(() => {
lazyLoad()
})
</script>
5. 部署与运维
5.1 生产环境部署
我们使用Docker Compose编排服务:
yaml复制version: '3.8'
services:
backend:
build: ./backend
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
depends_on:
- redis
- mysql
frontend:
build: ./frontend
ports:
- "80:80"
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: fresh
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:6
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
mysql_data:
redis_data:
5.2 监控与日志
Spring Boot应用集成Prometheus监控:
java复制@Configuration
@EnablePrometheusEndpoint
@EnableSpringBootMetricsCollector
public class PrometheusConfig implements WebMvcConfigurer {
// 配置端点权限等
}
前端使用Sentry捕获错误:
javascript复制import * as Sentry from '@sentry/vue'
app = createApp(App)
Sentry.init({
app,
dsn: 'your-dsn',
integrations: [
new Sentry.BrowserTracing({
routingInstrumentation: Sentry.vueRouterInstrumentation(router)
}),
new Sentry.Replay()
],
tracesSampleRate: 0.1,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0
})
6. 开发中的经验与教训
6.1 踩过的坑
-
Vue3响应式代理问题:
在解构从API返回的数据时,响应式会丢失。解决方案是使用toRefs或在模板中直接访问原始对象。 -
Spring Boot事务传播:
在库存扣减和订单创建需要在一个事务中完成,但默认的@Transactional传播行为可能导致意外。我们最终使用了REQUIRES_NEW确保库存操作独立提交。 -
微信支付回调:
微信支付回调需要快速响应success,但业务处理可能耗时。我们改为异步处理模式,回调接口只负责验证和接收通知。
6.2 性能优化建议
-
数据库查询优化:
- 商品列表分页查询添加了覆盖索引
- 复杂统计报表使用定时任务预计算
-
前端渲染优化:
- 长列表使用虚拟滚动
- 频繁更新的数据使用WebSocket推送
-
缓存策略:
- 商品详情使用多级缓存:Redis → 本地缓存 → 数据库
- 热点数据预加载
7. 系统扩展方向
基于现有系统,可以考虑以下扩展:
-
智能采购预测:
基于历史销售数据,使用机器学习预测各商品的采购量 -
冷链物流跟踪:
接入温度传感器数据,实时监控生鲜运输环境 -
社区拼团社交化:
增加分享助力、拼团进度提醒等社交功能 -
团长移动端:
开发专门的团长APP,方便收货管理和会员维护
这套系统从实际需求出发,经过多次迭代已经形成了一套完整的社区生鲜解决方案。在开发过程中,Spring Boot和Vue3的组合展现了极高的开发效率和良好的性能表现。特别是Vue3的组合式API,让前端复杂业务逻辑的组织变得更加清晰。
