1. 项目概述:在线家具商城的技术架构与核心价值
这个基于SpringBoot+Vue3+MyBatis的在线家具商城系统,采用了当前主流的前后端分离架构。前端使用Vue3组合式API开发,后端基于SpringBoot框架构建,数据持久层采用MyBatis实现与MySQL的交互。整套系统源码完整,可直接用于商业项目开发或学习参考。
提示:选择前后端分离架构时,建议开发团队至少具备3个月以上的Vue和SpringBoot实战经验,否则跨域问题、接口联调等环节容易成为开发瓶颈。
我在实际开发中发现,家具类电商系统有三个特殊需求点:商品SKU组合复杂(如沙发有面料、尺寸、颜色等多维度属性)、大尺寸图片加载性能要求高、需要3D展示等富媒体支持。本系统针对这些痛点做了专门优化:
- 采用Vue3的异步组件实现图片懒加载
- 使用MyBatis动态SQL处理多条件SKU查询
- 通过SpringBoot的线程池隔离处理高并发商品详情请求
2. 技术栈深度解析与选型依据
2.1 后端技术组合:SpringBoot+MyBatis
SpringBoot 2.7.x版本提供了开箱即用的特性:
- 内嵌Tomcat服务器(默认端口8080)
- 自动配置的MyBatis-Spring绑定
- Actuator端点监控
- 以下是核心依赖示例:
xml复制<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.6</version>
</dependency>
选择MyBatis而非JPA的考虑:
- 家具商城的动态查询条件复杂(价格区间、材质筛选、风格组合等)
- 需要精细控制SQL性能,特别是商品列表页的分页查询
- 历史订单的连表查询场景多
2.2 前端技术栈:Vue3组合式API
Vue3相比Vue2的主要优势:
- Composition API使代码组织更灵活
- 更好的TypeScript支持
- 更小的打包体积(Tree-shaking优化)
典型页面结构示例:
javascript复制// 商品详情页组件
import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'
export default {
setup() {
const product = ref(null)
const route = useRoute()
onMounted(async () => {
const res = await axios.get(`/api/products/${route.params.id}`)
product.value = res.data
})
return { product }
}
}
2.3 数据库设计:MySQL优化要点
家具商城的关键表结构设计:
sql复制CREATE TABLE `product` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL COMMENT '商品名称',
`category_id` int NOT NULL COMMENT '分类ID',
`price` decimal(10,2) NOT NULL COMMENT '基准价',
`main_image` varchar(255) DEFAULT NULL COMMENT '主图URL',
`detail_html` text COMMENT '详情HTML',
`spec_group_json` json DEFAULT NULL COMMENT '规格组JSON',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- SKU库存表
CREATE TABLE `product_sku` (
`id` bigint NOT NULL AUTO_INCREMENT,
`product_id` bigint NOT NULL,
`spec_json` json NOT NULL COMMENT '规格值JSON',
`stock` int NOT NULL DEFAULT '0',
`price_adjust` decimal(10,2) DEFAULT '0.00' COMMENT '价格调整',
PRIMARY KEY (`id`),
KEY `idx_product` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
注意:JSON字段的使用需要MySQL 5.7+版本,如需兼容旧版可改用TEXT字段存储序列化数据
3. 核心功能模块实现细节
3.1 商品系统设计与难点突破
3.1.1 多维度SKU实现方案
家具商品通常有多个规格维度(如沙发包含:尺寸、面料、颜色)。系统采用以下数据结构处理:
java复制// 规格组定义
public class SpecGroup {
private Long id;
private String name; // 如"尺寸"
private List<SpecItem> items; // 如["1.5m","1.8m"]
}
// 前端规格选择数据结构
public class SkuSelector {
private Long productId;
private List<SpecSelection> selections;
}
// MyBatis动态SQL示例
@Select("<script>" +
"SELECT * FROM product_sku WHERE product_id=#{productId} " +
"<foreach item='spec' collection='specConditions'>" +
" AND JSON_CONTAINS(spec_json, #{spec.value})" +
"</foreach>" +
"</script>")
List<ProductSku> findMatchSkus(@Param("productId") Long productId,
@Param("specConditions") List<SpecCondition> conditions);
3.1.2 商品搜索与筛选
Elasticsearch集成方案:
- 使用Spring Data Elasticsearch建立商品索引
- 双写机制保证MySQL与ES数据一致
- 关键字段配置:
java复制@Document(indexName = "furniture_products")
public class ESProduct {
@Id
private Long id;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String name;
@Field(type = FieldType.Double)
private Double price;
@Field(type = FieldType.Keyword)
private String categoryPath;
@Field(type = FieldType.Nested)
private List<Spec> specs;
}
3.2 订单系统关键技术实现
3.2.1 高并发库存扣减
采用Redis+Lua脚本保证原子性:
lua复制-- KEYS[1]: 库存key
-- ARGV[1]: 扣减数量
local stock = tonumber(redis.call('GET', KEYS[1]))
if stock >= tonumber(ARGV[1]) then
return redis.call('DECRBY', KEYS[1], ARGV[1])
else
return -1
end
SpringBoot中调用示例:
java复制@Resource
private StringRedisTemplate redisTemplate;
public boolean reduceStock(Long skuId, int num) {
String script = "上述Lua脚本内容";
RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
Long result = redisTemplate.execute(redisScript,
Collections.singletonList("stock:" + skuId),
String.valueOf(num));
return result != null && result >= 0;
}
3.2.2 分布式事务处理
使用Seata处理跨服务事务:
- 配置Seata Server(1.5.0+版本)
- 添加依赖:
xml复制<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.5.2</version>
</dependency>
关键注解使用:
java复制@GlobalTransactional
public OrderDTO createOrder(OrderRequest request) {
// 1. 扣减库存
inventoryService.reduceStock(request.getItems());
// 2. 创建订单
Order order = orderMapper.create(request);
// 3. 生成支付记录
paymentService.createPayment(order);
return convertToDTO(order);
}
4. 安全防护与性能优化
4.1 安全防护措施
4.1.1 SQL注入防护
MyBatis使用#{}防止注入:
xml复制<!-- 安全写法 -->
<select id="findByCondition" resultType="Product">
SELECT * FROM product
WHERE name LIKE CONCAT('%',#{keyword},'%')
</select>
<!-- 危险写法(绝对避免) -->
<select id="findByCondition" resultType="Product">
SELECT * FROM product
WHERE name LIKE '%${keyword}%'
</select>
4.1.2 XSS防护方案
前端使用vue-dompurify-html插件:
javascript复制import dompurify from 'dompurify'
import VueDOMPurifyHTML from 'vue-dompurify-html'
app.use(VueDOMPurifyHTML, {
default: {
ALLOWED_TAGS: ['a', 'strong'],
ALLOWED_ATTR: ['href', 'class']
}
})
后端统一过滤:
java复制public class XssFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) {
HttpServletRequest req = (HttpServletRequest) request;
XssHttpServletRequestWrapper wrappedRequest =
new XssHttpServletRequestWrapper(req);
chain.doFilter(wrappedRequest, response);
}
}
4.2 性能优化实践
4.2.1 前端性能优化
- 图片懒加载:
vue复制<template>
<img v-lazy="imageUrl" alt="product image">
</template>
<script>
import { Lazyload } from 'vant'
app.use(Lazyload, {
loading: '/loading.gif',
error: '/error.png'
})
</script>
- 路由懒加载:
javascript复制const ProductDetail = () => import('./views/ProductDetail.vue')
4.2.2 后端缓存策略
多级缓存配置:
java复制@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
.disableCachingNullValues();
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.withInitialCacheConfigurations(Collections.singletonMap(
"products", config.entryTtl(Duration.ofHours(1))
))
.transactionAware()
.build();
}
}
5. 部署与监控方案
5.1 容器化部署
Docker Compose编排示例:
yaml复制version: '3'
services:
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: furniture
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"
5.2 监控系统搭建
SpringBoot Actuator配置:
yaml复制management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
metrics:
enabled: true
Prometheus监控配置:
yaml复制scrape_configs:
- job_name: 'spring'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['backend:8080']
6. 开发中的典型问题与解决方案
6.1 跨域问题处理
SpringBoot配置类:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:8081")
.allowedMethods("*")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
Vue3代理配置(vite.config.js):
javascript复制export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}
})
6.2 文件上传大小限制
SpringBoot配置:
yaml复制spring:
servlet:
multipart:
max-file-size: 20MB
max-request-size: 30MB
前端分片上传示例:
javascript复制async function chunkUpload(file, chunkSize = 5 * 1024 * 1024) {
const chunks = Math.ceil(file.size / chunkSize)
for (let i = 0; i < chunks; i++) {
const start = i * chunkSize
const end = Math.min(file.size, start + chunkSize)
const chunk = file.slice(start, end)
const formData = new FormData()
formData.append('file', chunk)
formData.append('chunkIndex', i)
formData.append('totalChunks', chunks)
formData.append('fileId', file.name + '-' + file.lastModified)
await axios.post('/api/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
})
}
}
7. 项目扩展方向建议
- 接入第三方登录(微信、支付宝)
java复制// Spring Social配置示例
@Configuration
@EnableSocial
public class SocialConfig extends SocialConfigurerAdapter {
@Override
public void addConnectionFactories(
ConnectionFactoryConfigurer configurer,
Environment environment) {
configurer.addConnectionFactory(
new WeChatConnectionFactory(
env.getProperty("wechat.appId"),
env.getProperty("wechat.appSecret")));
}
}
- 实现商品3D展示
- 使用Three.js集成3D模型查看器
- 模型文件建议使用glTF格式(体积小、加载快)
- 接入推荐系统
- 基于用户行为的协同过滤
- 商品特征向量化推荐
python复制# Python推荐服务示例
from surprise import Dataset, KNNBasic
data = Dataset.load_builtin('ml-100k')
trainset = data.build_full_trainset()
algo = KNNBasic()
algo.fit(trainset)
# 为用户123推荐5个商品
algo.get_neighbors(123, k=5)
- 移动端适配方案
- 使用Vant或NutUI组件库
- 响应式布局配置示例:
css复制@media (max-width: 768px) {
.product-list {
grid-template-columns: repeat(2, 1fr);
}
}
在项目开发过程中,特别要注意家具类商品的特殊性:大尺寸图片优化、复杂SKU管理、高价值订单的安全控制等。建议在正式上线前进行完整的压力测试,模拟秒杀场景下的系统表现。对于初期技术选型,这套SpringBoot+Vue3+MyBatis的组合已经过多个电商项目验证,能很好平衡开发效率与系统性能。
