在电商后台管理中,SPU和SKU是两个最基础却极其重要的概念。它们是商品体系的核心骨架,直接影响着库存管理、价格策略、搜索优化和用户体验。本文将深入解析这两个概念的区别、联系以及在实际业务中的应用,帮助你彻底理解电商后台的商品管理逻辑。
一、核心概念解析:SPU与SKU的定义
1. SPU(Standard Product Unit)- 标准产品单元
SPU是商品的”类别”或”系列”概念,代表一组具有相同属性和功能的产品集合。它描述的是一个产品的”身份”,而不是具体的某一个商品实例。
SPU的特征:
- 标准化:SPU是标准化的产品概念,不包含颜色、尺寸等具体属性
- 共享性:同一个SPU下的所有SKU共享大部分属性(如品牌、型号、核心功能)
- 管理维度:SPU主要用于商品分类、品牌管理和销售数据分析
实际例子:
- 苹果iPhone 15 Pro是一个SPU,它代表了2023年苹果发布的Pro系列手机这一类产品
- 小米Redmi Note 13也是一个SPU,代表小米的中端手机系列
- 优衣库的HEATTECH保暖内衣也是一个SPU,代表一个特定系列的产品
在电商后台,SPU通常包含以下信息:
- 产品名称(如”iPhone 15 Pro”)
- 品牌(Apple)
- 核心卖点(A17 Pro芯片、钛金属边框)
- 基本参数(屏幕尺寸6.1英寸、后置三摄)
- 产品描述和营销文案
- 主图和详情页模板
2. SKU(Stock Keeping Unit)- 库存单位
SKU是实际存储和销售的最小单位,是SPU的具体实例化。每个SKU对应一个具体的商品规格,是库存管理、订单处理和价格计算的最小单元。
SKU的特征:
- 具体化:SKU必须包含所有影响库存和销售的属性组合
- 唯一性:每个SKU都有唯一的编码,用于系统识别
- 实操性:SKU直接对应仓库中的实际商品和订单中的具体商品
继续上面的例子:
- iPhone 15 Pro(SPU)下的SKU包括:
- iPhone 15 Pro 256GB 原色钛金属
- iPhone 15 Pro 512GB 蓝色钛金属
- iPhone 15 Pro 1TB 白色钛金属
- 小米Redmi Note 13(SPU)下的SKU包括:
- Redmi Note 13 8GB+128GB 子夜黑
- Redmi Note 13 12GB+256GB 星沙白
- 优衣库HEATTECH保暖内衣(SPU)下的SKU包括:
- HEATTECH圆领长袖T恤 L 黑色
- HEATTECH圆领长袖T恤 M 白色
每个SKU都有独立的:
- SKU编码(如:APL-IP15P-256G-PT)
- 库存数量
- 销售价格
- 规格属性(颜色、尺寸、容量等)
- 条形码/二维码
二、SPU与SKU的核心区别对比
为了更清晰地理解两者的区别,我们从多个维度进行详细对比:
| 对比维度 | SPU(标准产品单元) | SKU(库存单位) |
|---|---|---|
| 定义 | 一组相似产品的集合,代表产品类别 | 具体的商品规格,库存管理的最小单位 |
| 颗粒度 | 粗粒度,一个SPU包含多个SKU | 细粒度,直接对应具体商品 |
| 属性 | 包含通用属性(品牌、型号、核心功能) | 包含所有属性(颜色、尺寸、容量、版本等) |
| 价格 | 通常展示参考价或价格区间 | 有明确的销售价格 |
| 库存 | 不直接管理库存,库存是SKU级别的 | 直接管理库存,是库存的最小单位 |
| 编码 | 可选,用于产品分类 | 必须,用于系统识别和仓库管理 |
| 应用场景 | 商品展示、搜索、品牌管理、数据分析 | 订单处理、库存管理、物流配送、财务核算 |
| 数量关系 | 一个SPU可以对应多个SKU(1:N) | 一个SKU只能属于一个SPU(N:1) |
详细区别举例说明
场景:某电商平台销售运动鞋
SPU:耐克Air Jordan 1 Mid运动鞋
- 包含信息:品牌Nike、系列Air Jordan、款式Mid、材质皮革、适用场景休闲运动
- 不包含:具体颜色、尺码
SKU:
- AJ1-Mid-42-黑白红(42码,黑白红配色)
- AJ1-Mid-43-黑白红(43码,黑白红配色)
- AJ1-Mid-42-芝加哥(42码,芝加哥配色)
- AJ1-Mid-43-芝加哥(43码,芝加哥配色)
关键区别点:
- 库存管理:仓库里实际存放的是SKU级别的商品,每个SKU有独立的库存位置
- 价格策略:不同颜色可能价格相同,但不同尺码可能价格不同(如大码加价)
- 销售分析:分析”黑白红”配色的销量时,需要聚合该颜色下所有尺码的SKU数据
- 用户搜索:用户搜索”Air Jordan 1 Mid”时,系统展示SPU;用户筛选”42码”时,系统过滤SKU
三、SPU与SKU在电商后台的实际应用
1. 商品发布流程中的应用
标准流程:
1. 创建SPU(定义产品基本信息)
↓
2. 添加SKU(定义具体规格)
↓
3. 设置SKU属性(颜色、尺寸等)
↓
4. 为每个SKU设置价格和库存
↓
5. 关联SKU与SPU的图片和描述
实际案例:发布一款连衣裙
步骤1:创建SPU
{
"spu_code": "DR2024SS001",
"name": "法式碎花连衣裙",
"brand": "LilyRose",
"category": "女装/连衣裙",
"material": "雪纺",
"season": "春夏",
"style": "法式复古",
"main_image": "dr2024ss001_main.jpg",
"description": "采用优质雪纺面料,法式复古设计,适合日常和约会穿着..."
}
步骤2:创建SKU
// SKU1
{
"sku_code": "DR2024SS001-S-M",
"spu_code": "DR2024SS001",
"attributes": {
"color": "碎花蓝",
"size": "S"
},
"price": 299.00,
"stock": 50,
"images": ["dr2024ss001_blue_s_1.jpg", "dr2024ss001_blue_s_2.jpg"]
}
// SKU2
{
"sku_code": "DR2024SS001-M-M",
"spu_code": "DR2024SS001",
"attributes": {
"color": "碎花蓝",
"size": "M"
},
"price": 299.00,
"stock": 80,
"images": ["dr2024ss001_blue_m_1.jpg", "dr2024ss001_blue_m_2.jpg"]
}
// SKU3
{
"sku_code": "DR2024SS001-S-L",
"spu_code": "DR2024SS001",
"attributes": {
"color": "碎花白",
"size": "S"
},
"price": 299.00,
"stock": 30,
"images": ["dr2024ss001_white_s_1.jpg", "dr2024ss001_white_s_2.jpg"]
}
2. 库存管理中的应用
SPU级别库存(虚拟):
- 显示总库存:所有SKU库存之和 = 50+80+30 = 160件
- 用于前端展示”库存充足”提示
SKU级别库存(实际):
- 精确管理每个规格的库存
- 支持库存预警(如SKU库存低于20件时提醒补货)
- 支持多仓库管理(SKU在不同仓库的库存分布)
库存操作示例:
-- 查询SPU总库存
SELECT SUM(stock) FROM sku WHERE spu_code = 'DR2024SS001';
-- 查询特定SKU库存
SELECT stock FROM sku WHERE sku_code = 'DR2024SS001-S-M';
-- 扣减库存(订单创建时)
UPDATE sku SET stock = stock - 1 WHERE sku_code = 'DR2024SS001-S-M' AND stock >= 1;
3. 价格管理中的应用
SPU价格策略:
- 参考价:299元(展示在商品列表页)
- 价格区间:299-399元(当不同SKU价格不同时)
- 促销活动:SPU级别的满减、优惠券
SKU价格策略:
- 基础价格:每个SKU独立定价
- 动态调价:根据库存、成本、竞品调整单个SKU价格
- 促销价格:特定SKU的限时折扣
实际案例:
SPU:iPhone 15 Pro
- 参考价:7999元起
SKU价格:
- 128GB 黑色:7999元
- 256GB 黑色:8999元
- 512GB 黑色:10999元
- 1TB 黑色:12999元
- 128GB 白色:7999元
- 256GB 白色:8999元
4. 前端展示中的应用
商品列表页:
- 展示SPU信息(主图、名称、参考价)
- 用户点击进入详情页
商品详情页:
- 展示SPU的通用描述和规格
- 通过SKU选择器让用户选择具体规格
- 根据用户选择实时更新价格、库存和图片
SKU选择器实现(前端代码示例):
// SPU数据
const spu = {
code: 'DR2024SS001',
name: '法式碎花连衣裙',
basePrice: 299,
colors: ['碎花蓝', '碎花白'],
sizes: ['S', 'M', 'L']
};
// SKU数据
const skus = [
{ code: 'DR2024SS001-S-M', color: '碎花蓝', size: 'S', price: 299, stock: 50 },
{ code: 'DR2024SS001-M-M', color: '碎花蓝', size: 'M', price: 299, stock: 80 },
{ code: 'DR2024SS001-L-M', color: '碎花蓝', size: 'L', price: 329, stock: 40 },
{ code: 'DR2024SS001-S-L', color: '碎花白', size: 'S', price: 299, stock: 30 },
{ code: 'DR2024SS001-M-L', color: '碎花白', size: 'M', price: 299, stock: 60 }
];
// SKU选择器逻辑
class SKUSelector {
constructor(spu, skus) {
this.spu = spu;
this.skus = skus;
this.selectedColor = null;
this.selectedSize = null;
}
// 获取可选的颜色列表
getAvailableColors() {
return this.spu.colors.map(color => {
const hasStock = this.skus.some(sku =>
sku.color === color && sku.stock > 0
);
return { color, available: hasStock };
});
}
// 获取指定颜色下的可选尺码
getAvailableSizes(color) {
return this.spu.sizes.map(size => {
const sku = this.skus.find(s => s.color === color && s.size === size);
return {
size,
available: sku ? sku.stock > 0 : false,
price: sku ? sku.price : null
};
});
}
// 选择规格
select(color, size) {
const sku = this.skus.find(s =>
s.color === color && s.size === size
);
if (!sku) {
return { success: false, message: '该规格不存在' };
}
if (sku.stock <= 0) {
return { success: false, message: '该规格已售罄' };
}
this.selectedColor = color;
this.selectedSize = size;
return {
success: true,
sku: sku.code,
price: sku.price,
stock: sku.stock
};
}
// 获取当前选中状态
getCurrentSelection() {
if (!this.selectedColor || !this.selectedSize) {
return null;
}
const sku = this.skus.find(s =>
s.color === this.selectedColor && s.size === this.selectedSize
);
return sku ? {
color: this.selectedColor,
size: this.selectedSize,
price: sku.price,
stock: sku.stock,
skuCode: sku.code
} : null;
}
}
// 使用示例
const selector = new SKUSelector(spu, skus);
// 用户点击颜色"碎花蓝"
console.log('可选尺码:', selector.getAvailableSizes('碎花蓝'));
// 输出: [{size: 'S', available: true, price: 299}, {size: 'M', available: true, price: 299}, {size: 'L', available: true, price: 329}]
// 用户选择"碎花蓝" + "M"
const result = selector.select('碎花蓝', 'M');
console.log(result);
// 输出: {success: true, sku: 'DR2024SS001-M-M', price: 299, stock: 80}
5. 搜索与筛选中的应用
SPU在搜索中的作用:
- 用户搜索”连衣裙”时,系统返回SPU列表
- 搜索结果去重(同一个SPU只展示一次)
- 模糊匹配SPU名称、品牌、关键词
SKU在筛选中的作用:
- 用户筛选”碎花蓝”时,系统过滤SKU属性
- 筛选”价格200-300元”时,按SKU价格过滤
- 筛选”有货”时,按SKU库存过滤
数据库查询示例:
-- 搜索SPU
SELECT * FROM spu
WHERE name LIKE '%连衣裙%' OR brand LIKE '%连衣裙%';
-- 筛选SKU(带SPU信息)
SELECT DISTINCT spu.*, sku.price, sku.stock
FROM spu
JOIN sku ON spu.code = sku.spu_code
WHERE sku.color = '碎花蓝'
AND sku.price BETWEEN 200 AND 300
AND sku.stock > 0;
四、实际应用中的常见问题与解决方案
问题1:如何处理SKU爆炸问题?
问题描述:当产品属性组合过多时,SKU数量会呈指数级增长。例如:
- 10种颜色 × 10种尺码 × 5种材质 = 500个SKU
- 实际可能只有部分组合有库存或销售
解决方案:
方案A:动态SKU生成
# 只为有库存的组合生成SKU
def generate_skus(base_product, combinations):
skus = []
for combo in combinations:
# 检查该组合是否有实际库存或销售记录
if combo['stock'] > 0 or combo['sales'] > 0:
sku = {
'code': f"{base_product['code']}-{combo['color']}-{combo['size']}",
'attributes': combo,
'price': base_product['base_price'] + combo.get('price_adjust', 0),
'stock': combo['stock']
}
skus.append(sku)
return skus
# 实际组合(只有部分有库存)
combinations = [
{'color': '红色', 'size': 'M', 'stock': 50},
{'color': '红色', 'size': 'L', 'stock': 0}, # 无库存,不生成SKU
{'color': '蓝色', 'size': 'M', 'stock': 30}
]
方案B:SKU模板化
{
"spu_code": "DR2024SS001",
"name": "法式碎花连衣裙",
"sku_template": {
"colors": ["碎花蓝", "碎花白"],
"sizes": ["S", "M", "L"],
"price_by_size": {
"S": 299,
"M": 299,
"L": 329
},
"stock_by_color_size": {
"碎花蓝": {"S": 50, "M": 80, "L": 40},
"碎花白": {"S": 30, "M": 60, "L": 0}
}
}
}
问题2:SKU属性如何标准化?
问题描述:不同运营人员可能用不同方式描述同一属性,导致数据混乱。例如颜色:”红色” vs “红” vs “红色系”。
解决方案:
建立属性值字典
-- 属性值字典表
CREATE TABLE attribute_dict (
id INT PRIMARY KEY,
attribute_name VARCHAR(50), -- 'color', 'size'
standard_value VARCHAR(50), -- 标准值
alias VARCHAR(200), -- 别名,JSON格式
is_active BOOLEAN DEFAULT TRUE
);
-- 插入标准值
INSERT INTO attribute_dict VALUES
(1, 'color', '红色', '["红", "红色系", "red", "RED"]', TRUE),
(2, 'color', '蓝色', '["蓝", "蓝色系", "blue", "BLUE"]', TRUE),
(3, 'size', 'M', '["中", "中码", "Medium"]', TRUE);
-- 创建SKU时标准化
CREATE FUNCTION normalize_attribute(attr_name VARCHAR(50), attr_value VARCHAR(50))
RETURNS VARCHAR(50)
BEGIN
DECLARE standard_val VARCHAR(50);
SELECT standard_value INTO standard_val
FROM attribute_dict
WHERE attribute_name = attr_name
AND (attr_value = standard_value OR JSON_CONTAINS(alias, JSON_QUOTE(attr_value)))
LIMIT 1;
RETURN IFNULL(standard_val, attr_value);
END;
问题3:如何处理SPU与SKU的价格同步?
问题描述:当SPU级别的促销活动(如品牌日全场9折)时,如何快速更新所有SKU价格?
解决方案:
方案A:价格分离策略
-- SKU表只存储基础价格
CREATE TABLE sku (
sku_code VARCHAR(50) PRIMARY KEY,
spu_code VARCHAR(50),
base_price DECIMAL(10,2),
-- 其他字段
INDEX idx_spu (spu_code)
);
-- 促销活动表
CREATE TABLE promotion (
id INT PRIMARY KEY,
target_type ENUM('SPU', 'SKU'),
target_code VARCHAR(50),
discount_type ENUM('PERCENT', 'FIXED'),
discount_value DECIMAL(10,2),
start_time DATETIME,
end_time DATETIME
);
-- 查询最终价格(实时计算)
SELECT
sku.sku_code,
sku.base_price,
COALESCE(
CASE
WHEN p.discount_type = 'PERCENT' THEN sku.base_price * (1 - p.discount_value/100)
WHEN p.discount_type = 'FIXED' THEN sku.base_price - p.discount_value
ELSE sku.base_price
END,
sku.base_price
) as final_price
FROM sku
LEFT JOIN promotion p ON (p.target_type = 'SPU' AND p.target_code = sku.spu_code)
OR (p.target_type = 'SKU' AND p.target_code = sku.sku_code)
WHERE p.start_time <= NOW() AND p.end_time >= NOW();
方案B:价格缓存策略
class PriceManager:
def __init__(self):
self.price_cache = {}
self.promotion_cache = {}
def get_sku_price(self, sku_code, spu_code):
# 检查缓存
cache_key = f"{sku_code}:{spu_code}"
if cache_key in self.price_cache:
return self.price_cache[cache_key]
# 获取基础价格
base_price = self.get_base_price(sku_code, spu_code)
# 获取促销信息
promotions = self.get_active_promotions(spu_code, sku_code)
# 计算最终价格
final_price = base_price
for promo in promotions:
if promo['type'] == 'percent':
final_price *= (1 - promo['value']/100)
elif promo['type'] == 'fixed':
final_price -= promo['value']
# 缓存结果(5分钟)
self.price_cache[cache_key] = final_price
return final_price
def invalidate_cache(self, spu_code):
# 清除相关缓存
keys_to_delete = [k for k in self.price_cache.keys() if spu_code in k]
for key in keys_to_delete:
del self.price_cache[key]
问题4:如何处理SPU与SKU的图片管理?
问题描述:SPU有主图和详情图,SKU有特定规格的图片(如不同颜色的实物图),如何组织这些图片关系?
解决方案:
图片分层管理
{
"spu_code": "DR2024SS001",
"images": {
"common": {
"main": "dr2024ss001_main.jpg",
"detail": [
"dr2024ss001_detail_1.jpg",
"dr2024ss001_detail_2.jpg"
],
"video": "dr2024ss001_video.mp4"
},
"by_color": {
"碎花蓝": {
"color_main": "dr2024ss001_blue_main.jpg",
"color_detail": [
"dr2024ss001_blue_detail_1.jpg",
"dr2024ss001_blue_detail_2.jpg"
]
},
"碎花白": {
"color_main": "dr2024ss001_white_main.jpg",
"color_detail": [
"dr2024ss001_white_detail_1.jpg"
]
}
}
}
}
图片获取逻辑:
function getDisplayImages(spu, sku) {
const images = [];
// 1. 优先使用SKU特定图片
if (sku && spu.images.by_color[sku.color]) {
const colorImages = spu.images.by_color[sku.color];
if (colorImages.color_main) images.push(colorImages.color_main);
if (colorImages.color_detail) images.push(...colorImages.color_detail);
}
// 2. 没有SKU特定图片时使用SPU通用图片
if (images.length === 0) {
if (spu.images.common.main) images.push(spu.images.common.main);
if (spu.images.common.detail) images.push(...spu.images.common.detail);
}
// 3. 添加视频
if (spu.images.common.video) {
images.push({ type: 'video', url: spu.images.common.video });
}
return images;
}
五、SPU与SKU设计的最佳实践
1. 编码规范设计
推荐编码规则:
SPU编码:品类码 + 品牌码 + 系列码 + 序列号
示例:DR-LS-2024-001(连衣裙-LilyRose-2024系列-001)
SKU编码:SPU编码 + 属性简码 + 序列号
示例:DR-LS-2024-001-BLUE-M(SPU-颜色-尺码)
代码实现:
class ProductCodeGenerator:
def __init__(self):
self.category_map = {
'dress': 'DR',
'shirt': 'SH',
'pants': 'PT',
'shoes': 'SHO'
}
self.brand_map = {
'LilyRose': 'LS',
'Nike': 'NK',
'Apple': 'APL'
}
def generate_spu_code(self, category, brand, series, year):
cat = self.category_map.get(category, 'XX')
bra = self.brand_map.get(brand, 'XX')
return f"{cat}-{bra}-{year}-{self._get_sequence('spu')}"
def generate_sku_code(self, spu_code, attributes):
attr_str = ""
for key, value in sorted(attributes.items()):
# 取前两个字母作为简码
attr_str += f"-{value[:2].upper()}"
return f"{spu_code}{attr_str}"
def _get_sequence(self, type):
# 从数据库获取序列号
# 实际实现需要数据库操作
return "001"
2. 数据库设计最佳实践
推荐表结构:
-- SPU表
CREATE TABLE spu (
id INT PRIMARY KEY AUTO_INCREMENT,
spu_code VARCHAR(50) UNIQUE NOT NULL,
name VARCHAR(200) NOT NULL,
brand VARCHAR(100),
category_path VARCHAR(200), -- 分类路径,如"女装/连衣裙"
base_price DECIMAL(10,2),
description TEXT,
main_image VARCHAR(500),
status ENUM('draft', 'active', 'inactive') DEFAULT 'draft',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_brand (brand),
INDEX idx_category (category_path),
INDEX idx_status (status)
);
-- SKU表
CREATE TABLE sku (
id INT PRIMARY KEY AUTO_INCREMENT,
sku_code VARCHAR(50) UNIQUE NOT NULL,
spu_code VARCHAR(50) NOT NULL,
attributes JSON NOT NULL, -- {"color": "红色", "size": "M", "material": "棉"}
price DECIMAL(10,2) NOT NULL,
cost DECIMAL(10,2), -- 成本价
stock INT DEFAULT 0,
stock_warning INT DEFAULT 10, -- 库存预警阈值
images JSON, -- SKU特定图片
barcode VARCHAR(100), -- 条形码
status ENUM('active', 'inactive', 'sold_out') DEFAULT 'active',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (spu_code) REFERENCES spu(spu_code),
INDEX idx_spu (spu_code),
INDEX idx_stock (stock),
INDEX idx_price (price),
INDEX idx_attributes (attributes(64)) -- 对JSON字段前64字符建索引
);
-- 属性字典表
CREATE TABLE attribute_dict (
id INT PRIMARY KEY AUTO_INCREMENT,
attribute_name VARCHAR(50) NOT NULL,
standard_value VARCHAR(50) NOT NULL,
alias JSON, -- 别名列表
display_order INT DEFAULT 0,
is_required BOOLEAN DEFAULT TRUE,
UNIQUE KEY unique_attr (attribute_name, standard_value)
);
-- 促销价格表(分离价格策略)
CREATE TABLE promotion_price (
id INT PRIMARY KEY AUTO_INCREMENT,
target_type ENUM('SPU', 'SKU') NOT NULL,
target_code VARCHAR(50) NOT NULL,
promotion_type ENUM('percent', 'fixed', 'group') NOT NULL,
promotion_value DECIMAL(10,2) NOT NULL,
start_time DATETIME NOT NULL,
end_time DATETIME NOT NULL,
priority INT DEFAULT 0, -- 优先级,数字越大优先级越高
INDEX idx_target (target_type, target_code),
INDEX idx_time (start_time, end_time)
);
3. 前端展示优化策略
SPU列表页优化:
// 批量加载SPU和SKU数据,减少请求次数
async function loadSPUList(category, filters) {
// 1. 查询SPU列表(分页)
const spuList = await api.get('/spu/list', {
params: { category, page: 1, size: 20 }
});
// 2. 批量查询这些SPU的SKU信息(聚合查询)
const spuCodes = spuList.map(spu => spu.code);
const skuInfo = await api.post('/sku/batch', { spuCodes, filters });
// 3. 组装数据
return spuList.map(spu => {
const skus = skuInfo[spu.code] || [];
const minPrice = Math.min(...skus.map(s => s.price));
const hasStock = skus.some(s => s.stock > 0);
return {
...spu,
minPrice,
hasStock,
image: spu.main_image,
// 只返回第一个SKU的图片作为展示
previewImage: skus[0]?.images?.[0] || spu.main_image
};
});
}
详情页SKU选择器优化:
// 预加载所有SKU数据,避免多次请求
class SPUDetailManager {
constructor(spuCode) {
this.spuCode = spuCode;
this.spu = null;
this.skus = [];
this.attributeMap = {}; // 属性映射,用于快速查找
}
async load() {
// 一次性加载SPU和所有SKU
const [spu, skus] = await Promise.all([
api.get(`/spu/${this.spuCode}`),
api.get(`/sku/list?spu=${this.spuCode}`)
]);
this.spu = spu;
this.skus = skus;
this.buildAttributeMap();
}
buildAttributeMap() {
// 构建属性组合到SKU的映射
this.attributeMap = {};
this.skus.forEach(sku => {
const key = this.getAttributeKey(sku.attributes);
this.attributeMap[key] = sku;
});
}
getAttributeKey(attributes) {
// 将属性对象转换为可排序的字符串键
return Object.entries(attributes)
.sort(([a], [b]) => a.localeCompare(b))
.map(([k, v]) => `${k}:${v}`)
.join('|');
}
// 快速查找SKU
findSKU(attributes) {
const key = this.getAttributeKey(attributes);
return this.attributeMap[key];
}
}
六、总结
SPU和SKU是电商后台商品体系的两大支柱,理解它们的区别和联系是高效管理电商后台的基础:
核心要点回顾:
- SPU是”类别”,SKU是”实例”:SPU定义产品是什么,SKU定义具体是什么规格
- 管理维度不同:SPU用于展示和分类,SKU用于库存和交易
- 1:N关系:一个SPU对应多个SKU,一个SKU只属于一个SPU
- 数据分离:价格、库存、图片等数据应根据使用场景合理分布在SPU和SKU层级
实际应用建议:
- 设计阶段:先规划SPU结构,再细化SKU组合
- 编码规范:建立清晰的SPU和SKU编码规则
- 数据管理:保持SPU和SKU数据的一致性,建立数据校验机制
- 性能优化:批量查询、缓存策略、索引优化
- 用户体验:SKU选择器要清晰展示可选/不可选状态,避免用户困惑
掌握SPU和SKU的核心概念,不仅能帮助你更好地设计电商后台系统,还能在实际运营中做出更合理的商品策略和库存管理决策。
