1. 项目背景与技术选型
水果蔬菜商城是一个典型的B2C电商平台,采用SSM(Spring+SpringMVC+MyBatis)作为后端框架,Vue.js作为前端框架。这种技术组合在Java Web开发领域非常流行,特别适合中小型电商系统的快速开发。
SSM框架的优势在于:
- Spring的IoC容器管理对象生命周期和依赖注入
- SpringMVC提供清晰的MVC分层和灵活的请求处理机制
- MyBatis通过XML配置实现灵活的SQL管理
Vue.js的响应式数据绑定和组件化开发模式,使得前端开发更加高效。特别是Vue的虚拟DOM技术,能有效提升页面渲染性能。
2. 系统架构设计
2.1 整体架构分层
系统采用典型的三层架构:
- 表现层:Vue前端 + Element UI组件库
- 业务逻辑层:Spring MVC控制器 + Service服务
- 数据访问层:MyBatis + MySQL
2.2 数据库设计要点
核心表包括:
- 用户表(user):存储用户基本信息
- 商品表(product):记录商品详情和库存
- 订单表(order):主订单信息
- 订单明细表(order_item):订单商品明细
- 购物车表(cart):用户购物车数据
sql复制CREATE TABLE `product` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`price` decimal(10,2) NOT NULL,
`stock` int(11) NOT NULL,
`category_id` int(11) DEFAULT NULL,
`image_url` varchar(255) DEFAULT NULL,
`description` text,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 核心功能实现
3.1 商品展示模块
前端采用Vue组件化开发商品列表:
vue复制<template>
<div class="product-list">
<el-row :gutter="20">
<el-col
v-for="product in products"
:key="product.id"
:span="6"
>
<product-card :product="product"/>
</el-col>
</el-row>
</div>
</template>
<script>
import ProductCard from './ProductCard.vue'
export default {
components: { ProductCard },
data() {
return {
products: []
}
},
async created() {
const res = await this.$http.get('/api/products')
this.products = res.data
}
}
</script>
后端Spring MVC控制器:
java复制@RestController
@RequestMapping("/api/products")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping
public ResponseEntity<List<Product>> getProducts(
@RequestParam(required = false) Integer categoryId,
@RequestParam(required = false) String keyword) {
List<Product> products = productService.getProducts(categoryId, keyword);
return ResponseEntity.ok(products);
}
}
3.2 购物车功能实现
购物车数据结构设计考虑:
- 未登录用户使用浏览器localStorage临时存储
- 已登录用户数据持久化到数据库
- 采用Redis缓存热门商品信息减轻数据库压力
购物车服务关键代码:
java复制@Service
public class CartServiceImpl implements CartService {
@Autowired
private CartMapper cartMapper;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public void addToCart(Integer userId, Integer productId, Integer quantity) {
// 检查库存
Product product = productService.getProductById(productId);
if(product.getStock() < quantity) {
throw new BusinessException("库存不足");
}
// 查询是否已存在购物车记录
Cart cart = cartMapper.selectByUserAndProduct(userId, productId);
if(cart != null) {
cart.setQuantity(cart.getQuantity() + quantity);
cartMapper.updateById(cart);
} else {
cart = new Cart();
cart.setUserId(userId);
cart.setProductId(productId);
cart.setQuantity(quantity);
cartMapper.insert(cart);
}
// 更新Redis缓存
String key = "user_cart:" + userId;
redisTemplate.opsForHash().put(key, productId.toString(), cart);
}
}
4. 关键技术难点与解决方案
4.1 高并发库存扣减
采用Redis分布式锁+数据库乐观锁方案:
java复制public boolean decreaseStock(Integer productId, Integer quantity) {
String lockKey = "product_stock_lock:" + productId;
String requestId = UUID.randomUUID().toString();
try {
// 获取分布式锁
boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, requestId, 10, TimeUnit.SECONDS);
if(!locked) {
return false;
}
// 乐观锁更新库存
int affected = productMapper.decreaseStock(productId, quantity);
return affected > 0;
} finally {
// 释放锁
if(requestId.equals(redisTemplate.opsForValue().get(lockKey))) {
redisTemplate.delete(lockKey);
}
}
}
对应的MyBatis XML配置:
xml复制<update id="decreaseStock">
UPDATE product
SET stock = stock - #{quantity},
version = version + 1
WHERE id = #{productId}
AND stock >= #{quantity}
AND version = #{version}
</update>
4.2 订单超时取消
使用Spring的@Scheduled定时任务扫描超时订单:
java复制@Component
public class OrderTimeoutTask {
@Autowired
private OrderService orderService;
// 每5分钟执行一次
@Scheduled(cron = "0 */5 * * * ?")
public void cancelTimeoutOrders() {
List<Order> timeoutOrders = orderService.getTimeoutOrders(30); // 30分钟未支付
timeoutOrders.forEach(order -> {
try {
orderService.cancelOrder(order.getId(), "超时未支付");
// 恢复库存
order.getItems().forEach(item -> {
productService.increaseStock(
item.getProductId(),
item.getQuantity()
);
});
} catch (Exception e) {
log.error("取消订单失败: {}", order.getId(), e);
}
});
}
}
5. 系统优化实践
5.1 前端性能优化
- 图片懒加载:
vue复制<template>
<img v-lazy="product.imageUrl" alt="商品图片">
</template>
<script>
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload, {
preLoad: 1.3,
loading: '/static/loading.gif',
attempt: 1
})
</script>
- 路由懒加载:
javascript复制const ProductDetail = () => import('./views/ProductDetail.vue')
- Webpack分包优化:
javascript复制// vue.config.js
module.exports = {
configureWebpack: {
optimization: {
splitChunks: {
chunks: 'all',
maxSize: 244 * 1024 // 244KB
}
}
}
}
5.2 后端缓存策略
多级缓存设计:
- 本地缓存(Caffeine) - 高频访问的基础数据
- Redis集群 - 热点数据和分布式锁
- MySQL - 持久化存储
缓存更新策略:
java复制@Cacheable(value = "products", key = "#productId")
public Product getProductById(Integer productId) {
return productMapper.selectById(productId);
}
@CacheEvict(value = "products", key = "#product.id")
public void updateProduct(Product product) {
productMapper.updateById(product);
}
@Caching(
evict = {
@CacheEvict(value = "products", key = "#productId"),
@CacheEvict(value = "product_list", allEntries = true)
}
)
public void deleteProduct(Integer productId) {
productMapper.deleteById(productId);
}
6. 安全防护措施
6.1 常见Web安全防护
- XSS防护:
java复制@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers()
.xssProtection()
.and()
.contentSecurityPolicy("script-src 'self'");
}
}
- CSRF防护:
javascript复制// axios全局配置
axios.defaults.xsrfCookieName = 'XSRF-TOKEN'
axios.defaults.xsrfHeaderName = 'X-XSRF-TOKEN'
- SQL注入防护:
- 使用MyBatis参数绑定而非字符串拼接
- 定期使用SQL注入检测工具扫描
6.2 支付安全
- 敏感信息加密:
java复制public class PaymentService {
@Value("${aes.key}")
private String aesKey;
public String encryptCardNo(String cardNo) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// 初始化加密器...
return Base64.encodeToString(cipher.doFinal(cardNo.getBytes()));
} catch (Exception e) {
throw new RuntimeException("加密失败", e);
}
}
}
- 支付结果异步通知验证:
java复制@PostMapping("/pay/notify")
public String payNotify(@RequestBody String notifyData,
HttpServletRequest request) {
// 验证签名
if(!verifySign(notifyData, request.getHeader("Sign"))) {
return "fail";
}
// 处理业务逻辑
paymentService.processNotify(parseNotify(notifyData));
return "success";
}
7. 部署与监控
7.1 容器化部署
Docker Compose配置示例:
yaml复制version: '3'
services:
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: fruit_mall
ports:
- "3306:3306"
volumes:
- ./mysql/data:/var/lib/mysql
redis:
image: redis:6
ports:
- "6379:6379"
volumes:
- ./redis/data:/data
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
- redis
frontend:
build: ./frontend
ports:
- "80:80"
7.2 监控方案
- Spring Boot Actuator健康检查:
properties复制# application.properties
management.endpoints.web.exposure.include=health,info,metrics
management.endpoint.health.show-details=always
- Prometheus + Grafana监控:
java复制@Configuration
@EnablePrometheusEndpoint
public class PrometheusConfig implements MeterRegistryCustomizer<PrometheusMeterRegistry> {
@Override
public void customize(PrometheusMeterRegistry registry) {
registry.config().commonTags("application", "fruit-mall");
}
}
- ELK日志收集:
xml复制<!-- logback-spring.xml -->
<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>logstash:5044</destination>
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>
8. 项目扩展方向
- 小程序端开发:基于uni-app实现微信小程序版本
- 推荐系统:基于用户行为数据实现个性化推荐
- 供应链管理:扩展供应商管理模块
- 大数据分析:用户行为分析和销售预测
实际开发中,商品详情页的静态化处理能显著提升性能。我们使用Thymeleaf模板引擎生成静态HTML,配合Nginx直接返回,QPS从原来的200提升到了2000+。关键是要处理好商品变动的实时更新,我们采用Redis的Pub/Sub机制通知各个节点更新缓存
