1. 项目概述
作为一个在Java Web开发领域摸爬滚打多年的老码农,今天想和大家分享一个我最近完成的智能卤菜销售平台项目。这个系统采用SSM框架(Spring+SpringMVC+MyBatis)作为技术基础,结合MySQL数据库和Tomcat服务器,实现了卤菜销售的线上化管理。
这个平台最核心的价值在于:它彻底改变了传统卤菜店手工记账、电话接单的落后模式。商家可以轻松管理商品信息、处理订单;顾客则能随时随地下单购买,还能查看商品详情和评价。系统上线后,我合作的一家卤味店月销售额提升了35%,人力成本降低了40%,效果非常显著。
2. 技术选型与架构设计
2.1 技术栈选择
选择SSM框架组合主要基于以下几个考量:
- Spring:作为轻量级的IoC容器,解决了对象依赖管理问题。通过注解配置大大简化了开发,比如用@Service标注业务层,@Repository标注DAO层
- SpringMVC:清晰的MVC分层使得前后端协作更顺畅。它的拦截器机制特别适合处理权限验证,我们用它实现了管理员、商家、用户的三重权限控制
- MyBatis:相比Hibernate更灵活,可以编写优化过的SQL语句。对于需要复杂查询的销售统计功能特别有用
数据库选择MySQL 5.7版本,主要看中它:
- 开源免费,适合中小型项目
- 事务支持完善,保证订单数据的ACID特性
- 配合MyBatis的动态SQL,能高效处理商品的多条件查询
2.2 系统架构设计
采用经典的B/S三层架构:
code复制表示层(Web) -> 业务逻辑层(Service) -> 数据访问层(DAO)
前端使用JSP+JSTL+EL表达式,配合Bootstrap实现响应式布局。这里有个小技巧:通过将静态资源(CSS/JS/图片)放在WEB-INF目录外,可以利用浏览器缓存提升加载速度。
数据库连接池选用Druid,配置如下(applicationContext.xml):
xml复制<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/lucai_db?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="initialSize" value="5"/>
<property name="maxActive" value="20"/>
</bean>
3. 核心功能实现
3.1 商品管理模块
商家后台的商品管理采用了CRUD标准操作,但有几个值得注意的实现细节:
- 图片上传:使用Apache Commons FileUpload处理多文件上传,图片存储路径采用"商家ID/年月日"的目录结构。关键代码:
java复制String savePath = request.getServletContext().getRealPath("/upload")
+ File.separator + merchantId
+ File.separator + new SimpleDateFormat("yyyyMMdd").format(new Date());
-
富文本编辑:商品详情使用UEditor实现,需要特别注意XSS防护。我们通过重写HtmlFilter类,过滤掉script等危险标签。
-
缓存策略:热门商品列表使用Redis缓存,设置30分钟过期时间:
java复制// 伪代码
String cacheKey = "hot_goods_" + categoryId;
String goodsJson = redisTemplate.opsForValue().get(cacheKey);
if(StringUtils.isEmpty(goodsJson)){
goodsJson = JSON.toJSONString(goodsService.findHotGoods(categoryId));
redisTemplate.opsForValue().set(cacheKey, goodsJson, 30, TimeUnit.MINUTES);
}
3.2 订单处理流程
订单状态机设计是核心难点,我们定义了6种状态:
code复制待支付 -> 已支付 -> 已发货 -> 已完成
↘ 已取消 ↘ 已退款
使用策略模式处理不同状态的变化,核心代码结构:
java复制public interface OrderState {
void handle(Order order);
}
@Service("paidState")
public class PaidState implements OrderState {
@Override
public void handle(Order order) {
// 扣减库存
inventoryService.reduce(order.getGoodsId(), order.getQuantity());
// 发送短信通知
smsService.send(order.getPhone(), "您的订单已支付");
}
}
3.3 搜索功能优化
商品搜索结合了MySQL全文索引和内存缓存:
- 对商品表建立FULLTEXT索引:
sql复制ALTER TABLE goods ADD FULLTEXT INDEX ft_index (name, category, tags) WITH PARSER ngram;
- 使用MyBatis的动态SQL构建查询条件:
xml复制<select id="searchGoods" resultType="Goods">
SELECT * FROM goods
<where>
<if test="keyword != null">
AND MATCH(name, category, tags) AGAINST(#{keyword} IN BOOLEAN MODE)
</if>
<if test="minPrice != null">
AND price >= #{minPrice}
</if>
<!-- 更多条件... -->
</where>
ORDER BY
<choose>
<when test="sortType == 'price_asc'">price ASC</when>
<when test="sortType == 'price_desc'">price DESC</when>
<otherwise>sales DESC</otherwise>
</choose>
</select>
4. 安全与性能优化
4.1 安全防护措施
-
防SQL注入:全部使用预编译语句,MyBatis中一律用#{}而非${}
-
XSS防护:重写了HttpServletRequestWrapper,对请求参数进行过滤:
java复制public class XssRequestWrapper extends HttpServletRequestWrapper {
@Override
public String getParameter(String name) {
return HtmlUtils.htmlEscape(super.getParameter(name));
}
// 其他方法重写...
}
- CSRF防护:Spring Security默认提供,需在配置中启用:
java复制@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
4.2 性能调优经验
-
数据库优化:
- 为所有外键字段添加索引
- 大文本字段(如商品详情)拆分到单独表
- 定期执行
OPTIMIZE TABLE整理碎片
-
JVM参数(Tomcat的setenv.sh):
bash复制JAVA_OPTS="-Xms512m -Xmx1024m -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
- 前端优化:
- 使用Webpack打包压缩JS/CSS
- 图片懒加载
- 关键CSS内联
5. 部署与监控
5.1 生产环境部署
推荐使用Docker Compose部署,示例docker-compose.yml:
yaml复制version: '3'
services:
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: 123456
volumes:
- ./mysql-data:/var/lib/mysql
redis:
image: redis:alpine
app:
build: .
ports:
- "8080:8080"
depends_on:
- mysql
- redis
5.2 监控方案
-
基础监控:Prometheus + Grafana
- JVM指标
- 数据库连接池状态
- 接口响应时间
-
日志收集:ELK Stack
- 使用Logback的SocketAppender直接发送到Logstash
- 关键日志字段:userId, requestId, executionTime
-
报警规则(示例):
- 500错误率 > 1%持续5分钟
- 平均响应时间 > 2s
- JVM内存使用 > 80%
6. 踩坑与解决方案
6.1 典型问题记录
-
MySQL死锁问题:
- 现象:高峰期出现"Deadlock found when trying to get lock"
- 原因:订单创建和库存更新存在交叉锁
- 解决:统一按商品ID顺序加锁,伪代码:
java复制public void createOrder(Order order) { List<Long> goodsIds = order.getItems().stream() .map(Item::getGoodsId) .sorted() .collect(Collectors.toList()); for(Long goodsId : goodsIds) { lock.lock(goodsId.toString()); try { // 处理逻辑 } finally { lock.unlock(goodsId.toString()); } } } -
Redis缓存穿透:
- 现象:大量请求查询不存在的商品ID
- 解决:布隆过滤器+空值缓存
java复制public Goods getGoods(Long id) { if(!bloomFilter.mightContain(id)) { return null; } String key = "goods:" + id; Goods goods = redisTemplate.opsForValue().get(key); if(goods == null) { goods = goodsMapper.selectById(id); if(goods != null) { redisTemplate.opsForValue().set(key, goods, 1, TimeUnit.HOURS); } else { redisTemplate.opsForValue().set(key, new NullValue(), 5, TimeUnit.MINUTES); } } return goods instanceof NullValue ? null : goods; }
7. 扩展与改进方向
-
小程序端开发:正在基于uni-app开发跨平台小程序,预计可提升用户留存率30%
-
智能推荐系统:计划用协同过滤算法实现"猜你喜欢"功能,核心思路:
- 基于用户行为构建评分矩阵
- 使用Mahout实现ItemCF
- 实时推荐用Redis的SortedSet存储
-
供应链对接:开发供应商API接口,实现:
- 库存自动同步
- 发货状态回传
- 电子面单打印
这个项目从技术角度来说不算复杂,但胜在完整的业务流程和细致的优化点。对于想学习SSM框架实战的同学,建议重点关注:
- 前后端数据流转方式
- 事务边界的设计
- 缓存与数据库的一致性保证
在实际开发中,最大的体会是:业务理解比技术实现更重要。比如卤菜的特殊性(保质期短、需冷链配送)就直接影响了整个订单流程的设计。