电商商品管理系统设计与实现
2024/1/1
# 电商商品管理系统设计与实现
# 系统概述
商品管理系统是电商平台的核心模块之一,负责商品信息的维护、分类管理、库存控制、价格策略等功能。一个完善的商品管理系统需要支持多维度的商品属性、灵活的分类体系、精准的库存管理和高效的搜索功能。
# 核心功能模块
# 1. 商品基础信息管理
# 1.1 商品实体设计
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 200)
private String name;
@Column(length = 100)
private String brand;
@Column(columnDefinition = "TEXT")
private String description;
@Column(precision = 10, scale = 2)
private BigDecimal price;
@Column(precision = 10, scale = 2)
private BigDecimal originalPrice;
@Column(nullable = false)
private Integer stock;
@Column(nullable = false)
private Integer sales = 0;
@Enumerated(EnumType.STRING)
private ProductStatus status;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id")
private Category category;
@OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
private List<ProductImage> images;
@OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
private List<ProductAttribute> attributes;
@CreationTimestamp
private LocalDateTime createdAt;
@UpdateTimestamp
private LocalDateTime updatedAt;
}
public enum ProductStatus {
DRAFT, // 草稿
ACTIVE, // 上架
INACTIVE, // 下架
DELETED // 已删除
}
# 1.2 商品服务实现
@Service
@Transactional
public class ProductService {
@Autowired
private ProductRepository productRepository;
@Autowired
private CategoryService categoryService;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 创建商品
*/
public Product createProduct(ProductCreateDTO dto) {
// 验证分类是否存在
Category category = categoryService.findById(dto.getCategoryId());
if (category == null) {
throw new BusinessException("商品分类不存在");
}
Product product = new Product();
BeanUtils.copyProperties(dto, product);
product.setCategory(category);
product.setStatus(ProductStatus.DRAFT);
// 保存商品基础信息
product = productRepository.save(product);
// 保存商品图片
if (dto.getImages() != null && !dto.getImages().isEmpty()) {
saveProductImages(product, dto.getImages());
}
// 保存商品属性
if (dto.getAttributes() != null && !dto.getAttributes().isEmpty()) {
saveProductAttributes(product, dto.getAttributes());
}
return product;
}
/**
* 商品上架
*/
public void publishProduct(Long productId) {
Product product = findById(productId);
if (product.getStatus() != ProductStatus.DRAFT) {
throw new BusinessException("只有草稿状态的商品才能上架");
}
// 验证商品信息完整性
validateProductForPublish(product);
product.setStatus(ProductStatus.ACTIVE);
productRepository.save(product);
// 同步到搜索引擎
syncToSearchEngine(product);
// 清除缓存
clearProductCache(productId);
}
/**
* 更新库存
*/
@Transactional(isolation = Isolation.READ_COMMITTED)
public boolean updateStock(Long productId, Integer quantity, StockOperation operation) {
Product product = productRepository.findByIdForUpdate(productId);
if (product == null) {
return false;
}
int newStock;
switch (operation) {
case INCREASE:
newStock = product.getStock() + quantity;
break;
case DECREASE:
newStock = product.getStock() - quantity;
if (newStock < 0) {
throw new BusinessException("库存不足");
}
break;
default:
throw new IllegalArgumentException("不支持的库存操作类型");
}
product.setStock(newStock);
productRepository.save(product);
// 更新缓存中的库存
updateStockCache(productId, newStock);
return true;
}
}
# 2. 商品分类管理
# 2.1 分类实体设计
@Entity
@Table(name = "categories")
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 100)
private String name;
@Column(length = 500)
private String description;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
private Category parent;
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<Category> children;
@Column(nullable = false)
private Integer level;
@Column(nullable = false)
private Integer sortOrder = 0;
@Column(nullable = false)
private Boolean isActive = true;
@CreationTimestamp
private LocalDateTime createdAt;
@UpdateTimestamp
private LocalDateTime updatedAt;
}
# 2.2 分类服务实现
@Service
public class CategoryService {
@Autowired
private CategoryRepository categoryRepository;
/**
* 构建分类树
*/
public List<CategoryTreeNode> buildCategoryTree() {
List<Category> allCategories = categoryRepository.findAllByIsActiveOrderBySortOrder(true);
Map<Long, CategoryTreeNode> nodeMap = new HashMap<>();
// 转换为树节点
for (Category category : allCategories) {
CategoryTreeNode node = new CategoryTreeNode();
BeanUtils.copyProperties(category, node);
nodeMap.put(category.getId(), node);
}
// 构建树结构
List<CategoryTreeNode> rootNodes = new ArrayList<>();
for (CategoryTreeNode node : nodeMap.values()) {
if (node.getParentId() == null) {
rootNodes.add(node);
} else {
CategoryTreeNode parent = nodeMap.get(node.getParentId());
if (parent != null) {
parent.getChildren().add(node);
}
}
}
return rootNodes;
}
/**
* 获取分类路径
*/
public List<Category> getCategoryPath(Long categoryId) {
List<Category> path = new ArrayList<>();
Category current = findById(categoryId);
while (current != null) {
path.add(0, current);
current = current.getParent();
}
return path;
}
}
# 3. 商品属性管理
# 3.1 属性实体设计
@Entity
@Table(name = "product_attributes")
public class ProductAttribute {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "product_id")
private Product product;
@Column(nullable = false, length = 100)
private String attributeName;
@Column(nullable = false, length = 500)
private String attributeValue;
@Enumerated(EnumType.STRING)
private AttributeType type;
@Column(nullable = false)
private Integer sortOrder = 0;
}
public enum AttributeType {
BASIC, // 基础属性
SPEC, // 规格属性
CUSTOM // 自定义属性
}
# 4. 库存管理
# 4.1 库存服务实现
@Service
public class InventoryService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ProductRepository productRepository;
private static final String STOCK_KEY_PREFIX = "product:stock:";
/**
* 预减库存(Redis)
*/
public boolean preReduceStock(Long productId, Integer quantity) {
String key = STOCK_KEY_PREFIX + productId;
// 使用Lua脚本保证原子性
String luaScript =
"local stock = redis.call('get', KEYS[1]) " +
"if stock == false then " +
" return -1 " +
"end " +
"if tonumber(stock) >= tonumber(ARGV[1]) then " +
" return redis.call('decrby', KEYS[1], ARGV[1]) " +
"else " +
" return -2 " +
"end";
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(luaScript);
script.setResultType(Long.class);
Long result = redisTemplate.execute(script,
Collections.singletonList(key), quantity);
return result != null && result >= 0;
}
/**
* 恢复库存
*/
public void restoreStock(Long productId, Integer quantity) {
String key = STOCK_KEY_PREFIX + productId;
redisTemplate.opsForValue().increment(key, quantity);
}
/**
* 同步库存到数据库
*/
@Scheduled(fixedDelay = 60000) // 每分钟同步一次
public void syncStockToDatabase() {
Set<String> keys = redisTemplate.keys(STOCK_KEY_PREFIX + "*");
for (String key : keys) {
Long productId = Long.parseLong(key.replace(STOCK_KEY_PREFIX, ""));
Integer redisStock = (Integer) redisTemplate.opsForValue().get(key);
if (redisStock != null) {
Product product = productRepository.findById(productId).orElse(null);
if (product != null && !product.getStock().equals(redisStock)) {
product.setStock(redisStock);
productRepository.save(product);
}
}
}
}
}
# 5. 商品搜索
# 5.1 搜索服务实现
@Service
public class ProductSearchService {
@Autowired
private ElasticsearchRestTemplate elasticsearchTemplate;
/**
* 商品搜索
*/
public PageResult<ProductSearchResult> searchProducts(ProductSearchQuery query) {
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 关键词搜索
if (StringUtils.hasText(query.getKeyword())) {
MultiMatchQueryBuilder multiMatchQuery = QueryBuilders
.multiMatchQuery(query.getKeyword(), "name", "description", "brand")
.type(MultiMatchQueryBuilder.Type.BEST_FIELDS)
.fuzziness(Fuzziness.AUTO);
boolQuery.must(multiMatchQuery);
}
// 分类筛选
if (query.getCategoryId() != null) {
boolQuery.filter(QueryBuilders.termQuery("categoryId", query.getCategoryId()));
}
// 价格区间筛选
if (query.getMinPrice() != null || query.getMaxPrice() != null) {
RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
if (query.getMinPrice() != null) {
rangeQuery.gte(query.getMinPrice());
}
if (query.getMaxPrice() != null) {
rangeQuery.lte(query.getMaxPrice());
}
boolQuery.filter(rangeQuery);
}
// 品牌筛选
if (query.getBrands() != null && !query.getBrands().isEmpty()) {
boolQuery.filter(QueryBuilders.termsQuery("brand", query.getBrands()));
}
// 状态筛选
boolQuery.filter(QueryBuilders.termQuery("status", "ACTIVE"));
// 排序
List<SortBuilder<?>> sorts = buildSorts(query.getSortType());
// 构建搜索请求
NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder()
.withQuery(boolQuery)
.withPageable(PageRequest.of(query.getPage(), query.getSize()))
.withSorts(sorts);
// 执行搜索
SearchHits<ProductSearchDocument> searchHits =
elasticsearchTemplate.search(searchQueryBuilder.build(), ProductSearchDocument.class);
// 转换结果
List<ProductSearchResult> results = searchHits.getSearchHits().stream()
.map(hit -> convertToSearchResult(hit.getContent()))
.collect(Collectors.toList());
return new PageResult<>(results, searchHits.getTotalHits(), query.getPage(), query.getSize());
}
}
# 性能优化策略
# 1. 缓存策略
@Component
public class ProductCacheManager {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String PRODUCT_CACHE_PREFIX = "product:";
private static final int CACHE_EXPIRE_SECONDS = 3600; // 1小时
/**
* 缓存商品信息
*/
public void cacheProduct(Product product) {
String key = PRODUCT_CACHE_PREFIX + product.getId();
redisTemplate.opsForValue().set(key, product, CACHE_EXPIRE_SECONDS, TimeUnit.SECONDS);
}
/**
* 获取缓存的商品信息
*/
public Product getCachedProduct(Long productId) {
String key = PRODUCT_CACHE_PREFIX + productId;
return (Product) redisTemplate.opsForValue().get(key);
}
/**
* 清除商品缓存
*/
public void evictProductCache(Long productId) {
String key = PRODUCT_CACHE_PREFIX + productId;
redisTemplate.delete(key);
}
}
# 2. 数据库优化
-- 商品表索引优化
CREATE INDEX idx_product_category_status ON products(category_id, status);
CREATE INDEX idx_product_brand_status ON products(brand, status);
CREATE INDEX idx_product_price_status ON products(price, status);
CREATE INDEX idx_product_created_at ON products(created_at);
CREATE INDEX idx_product_sales ON products(sales DESC);
-- 分类表索引
CREATE INDEX idx_category_parent_active ON categories(parent_id, is_active);
CREATE INDEX idx_category_level_sort ON categories(level, sort_order);
# 总结
商品管理系统是电商平台的基础模块,需要考虑以下关键点:
- 数据模型设计:合理的实体关系设计,支持灵活的商品属性扩展
- 库存管理:使用Redis预减库存,定时同步到数据库,保证高并发下的库存准确性
- 搜索功能:集成Elasticsearch,支持多维度搜索和筛选
- 缓存策略:多层缓存设计,提升系统性能
- 数据一致性:通过事务和分布式锁保证数据一致性
- 扩展性:支持商品属性的动态扩展和分类体系的灵活调整
通过以上设计和实现,可以构建一个高性能、高可用的商品管理系统,为电商平台提供稳定可靠的商品服务。