1. 项目概述与核心需求
这个基于Java SSM316框架的网上水果生鲜销售系统,是我去年为本地一家连锁超市开发的电商平台。系统采用SpringBoot作为基础框架,整合了SSM(Spring+SpringMVC+MyBatis)技术栈,主要解决传统生鲜行业线下销售渠道单一、库存管理混乱的问题。
系统最核心的业务需求包括:
- 实现水果生鲜商品的在线展示与分类浏览
- 完整的购物车与订单流程(含库存实时校验)
- 会员体系与积分奖励机制
- 支持生鲜特有的配送时效选择(如次日达、定时达)
- 后台的商品进销存管理与销售数据分析
提示:生鲜电商系统与普通电商的最大区别在于对库存时效性的强依赖——香蕉不能像手机壳那样长期备货,这直接影响了整个系统的业务逻辑设计。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与架构设计
2.1 为什么选择SpringBoot+SSM组合
在技术选型阶段,我对比了三种主流方案:
- 纯SSM传统架构:配置繁琐,依赖管理复杂
- SpringBoot+JPA:JPA对复杂查询的支持较弱
- 最终方案:SpringBoot+SSM(MyBatis)
选择理由:
- SpringBoot的自动配置大幅减少了XML配置(相比传统SSM减少约70%的配置代码)
- MyBatis的灵活SQL更适合生鲜业务中的复杂统计查询
- 内嵌Tomcat方便快速部署调试
java复制// 典型的主配置类示例
@SpringBootApplication
@MapperScan("com.fresh.mapper") // MyBatis接口扫描
public class FreshApplication {
public static void main(String[] args) {
SpringApplication.run(FreshApplication.class, args);
}
}
2.2 分层架构设计
系统采用标准MVC分层:
code复制com.fresh
├── config # 自定义配置
├── controller
├── service # 业务逻辑
│ └── impl # 实现类
├── mapper # MyBatis接口
├── entity # 实体类
└── util # 工具包
特别之处在于增加了fresh子包处理生鲜特有逻辑:
service/fresh/InventoryService.java- 包含库存冻结/释放逻辑entity/FreshItem.java- 扩展了保质期字段
3. 核心业务模块实现
3.1 商品模块的特殊处理
生鲜商品需要额外字段:
java复制public class Product {
private Long id;
private String name;
private BigDecimal price;
// 生鲜特有字段
private Date harvestDate; // 采摘日期
private Integer shelfLife; // 保质期(天)
private String storageMethod; // 存储要求
}
对应的MySQL建表语句包含特殊索引:
sql复制CREATE TABLE `product` (
`id` bigint NOT NULL AUTO_INCREMENT,
`harvest_date` date NOT NULL COMMENT '采摘日期',
`shelf_life` int DEFAULT '3' COMMENT '默认3天保质期',
KEY `idx_freshness` (`harvest_date`,`shelf_life`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.2 库存管理的并发控制
生鲜商品的库存需要特殊处理——不仅要考虑数量,还要考虑时效性。我们采用乐观锁+库存预占模式:
java复制@Transactional
public boolean reduceInventory(Long productId, int quantity) {
// 1. 查询当前可用库存(排除已过期的)
Product product = productMapper.selectFresh(productId);
// 2. 乐观锁更新
int rows = productMapper.updateInventory(
productId,
quantity,
product.getVersion());
if(rows == 0){
throw new FreshException("库存不足或已被修改");
}
// 3. 记录预占(30分钟未支付自动释放)
reserveMapper.insert(new Reserve(productId, quantity));
}
注意:生鲜库存必须与普通商品区分处理,我们的方案是在库存表中增加
is_fresh标志位,后台定时任务每天凌晨会清理过期库存。
4. 典型问题与解决方案
4.1 高并发下的库存超卖
在618大促期间,我们遇到了芒果秒杀时的超卖问题。通过以下方案解决:
- Redis分布式锁:对热门商品采用分段锁
java复制public boolean seckill(Long productId, Long userId) {
String lockKey = "lock:product:" + productId % 10; // 分10段
try {
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
if(locked != null && locked) {
// 执行库存扣减
}
} finally {
redisTemplate.delete(lockKey);
}
}
- 库存预加载:活动前将库存数量加载到Redis
code复制> SET inventory:product_123 100
4.2 生鲜商品搜索优化
用户常按"新鲜度"搜索,我们在Elasticsearch中建立了特殊分析器:
json复制{
"settings": {
"analysis": {
"analyzer": {
"freshness_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": ["freshness_filter"]
}
},
"filter": {
"freshness_filter": {
"type": "synonym",
"synonyms": [
"新鲜 => 1",
"刚摘 => 1",
"现采 => 1",
"隔夜 => 0"
]
}
}
}
}
}
5. 部署与性能调优
5.1 生产环境配置
采用Docker Compose部署方案:
yaml复制version: '3'
services:
app:
image: fresh:1.0
ports:
- "8080:8080"
depends_on:
- redis
- mysql
redis:
image: redis:6
volumes:
- redis_data:/data
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: fresh123
volumes:
- mysql_data:/var/lib/mysql
关键JVM参数:
code复制-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-Xmx2g
-XX:MetaspaceSize=256m
5.2 性能优化记录
通过Arthas工具发现并解决了三个典型问题:
- MyBatis查询N+1问题:
bash复制watch com.fresh.mapper.ProductMapper getById '{params,returnObj}' -x 3
→ 添加了@BatchSize注解优化关联查询
- Jackson序列化瓶颈:
配置了全局序列化过滤:
java复制@Bean
public MappingJackson2HttpMessageConverter jacksonConverter() {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return new MappingJackson2HttpMessageConverter(mapper);
}
- Tomcat连接池优化:
properties复制spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.connection-timeout=30000
6. 扩展功能实现
6.1 智能推荐模块
基于用户购买历史实现推荐逻辑:
java复制public List<Product> recommend(Long userId) {
// 1. 获取用户最近购买的生鲜类别
List<Category> favCategories = orderMapper.selectUserCategories(userId);
// 2. 结合时令因素(夏季推荐西瓜)
Season season = SeasonUtil.getCurrentSeason();
// 3. 排除已过期商品
return productMapper.selectRecommend(
favCategories,
season,
LocalDate.now());
}
6.2 微信小程序对接
采用WxJava SDK处理支付回调:
java复制@PostMapping("/pay/callback")
public String wxCallback(HttpServletRequest request) {
WxPayOrderNotifyResult result = wxPayService.parseOrderNotifyResult(request);
if ("SUCCESS".equals(result.getResultCode())) {
orderService.paySuccess(result.getOutTradeNo());
return WxPayNotifyResponse.success("OK");
}
return WxPayNotifyResponse.fail("支付失败");
}
7. 项目总结与改进方向
经过三个月的开发和优化,系统目前日均处理订单2000+,峰值QPS达到150。几个关键收获:
-
生鲜业务的特殊性:必须把"时间"作为第一维度考虑,这影响了从数据库设计到前端展示的所有环节
-
技术选型的平衡:SpringBoot确实大幅提升了开发效率,但对于复杂事务仍需结合MyBatis的细粒度控制
-
后续优化方向:
- 引入RabbitMQ实现异步库存处理
- 增加区块链溯源功能验证商品来源
- 开发APP端原生应用提升用户体验
这个项目让我深刻体会到,生鲜电商系统不是简单的"电商+生鲜",而是一套完全不同的业务体系。如果你也在开发类似系统,建议从第一天就把"时效性"作为核心设计原则。
