一、功能概述 商品迭代记录是美团买菜系统中重要的功能模块,用于跟踪商品从上架到下架的全生命周期变化,包括价格调整、库存变更、规格修改、图片更新等所有变更历史。 二、核心需求 1.完整记录:记录所有商品属性的变更历史 2.版本对比:支持不同版本商品信息的对比查看 3.回滚
一、功能概述
商品迭代记录是美团买菜系统中重要的功能模块,用于跟踪商品从上架到下架的全生命周期变化,包括价格调整、库存变更、规格修改、图片更新等所有变更历史。
二、核心需求
1. 完整记录:记录所有商品属性的变更历史
2. 版本对比:支持不同版本商品信息的对比查看
3. 回滚机制:支持恢复到历史版本
4. 审计追踪:满足合规性要求,记录操作者信息
5. 高效查询:支持按时间、操作类型、操作人等条件筛选
三、数据库设计
商品主表(product)
```sql
CREATE TABLE product (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
category_id BIGINT NOT NULL,
current_version_id BIGINT NOT NULL,
status TINYINT DEFAULT 1 COMMENT 1-上架 0-下架,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL
);
```
商品版本表(product_version)
```sql
CREATE TABLE product_version (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
product_id BIGINT NOT NULL,
version_number INT NOT NULL,
price DECIMAL(10,2) NOT NULL,
original_price DECIMAL(10,2),
stock INT NOT NULL,
specs JSON COMMENT 商品规格JSON,
images JSON COMMENT 图片URL数组,
description TEXT,
operator_id BIGINT NOT NULL COMMENT 操作人ID,
operator_name VARCHAR(50) NOT NULL,
change_type TINYINT NOT NULL COMMENT 1-新增 2-修改 3-删除,
change_reason VARCHAR(255),
created_at DATETIME NOT NULL,
FOREIGN KEY (product_id) REFERENCES product(id)
);
```
版本变更详情表(product_version_detail)
```sql
CREATE TABLE product_version_detail (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
version_id BIGINT NOT NULL,
field_name VARCHAR(50) NOT NULL COMMENT 变更字段名,
old_value TEXT,
new_value TEXT,
FOREIGN KEY (version_id) REFERENCES product_version(id)
);
```
四、核心实现逻辑
1. 商品变更拦截器
```java
@Aspect
@Component
public class ProductChangeAspect {
@Autowired
private ProductVersionService versionService;
@AfterReturning(pointcut = "execution(* com.meituan.maicai.service.ProductService.update*(..)) || " +
"execution(* com.meituan.maicai.service.ProductService.delete*(..))",
returning = "result")
public void afterProductChange(JoinPoint joinPoint, Object result) {
// 获取操作类型
String methodName = joinPoint.getSignature().getName();
ChangeType changeType = determineChangeType(methodName);
// 获取商品ID
Object[] args = joinPoint.getArgs();
Long productId = extractProductId(args);
// 获取当前用户信息
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
// 创建新版本
versionService.createNewVersion(productId, changeType, userDetails.getUsername(),
getChangeReason(joinPoint));
}
private ChangeType determineChangeType(String methodName) {
if (methodName.startsWith("update")) return ChangeType.UPDATE;
if (methodName.startsWith("delete")) return ChangeType.DELETE;
return ChangeType.CREATE; // 新增通常通过save方法
}
}
```
2. 版本创建服务
```java
@Service
public class ProductVersionServiceImpl implements ProductVersionService {
@Autowired
private ProductVersionRepository versionRepository;
@Autowired
private ProductRepository productRepository;
@Autowired
private ProductVersionDetailRepository detailRepository;
@Transactional
@Override
public void createNewVersion(Long productId, ChangeType changeType, String operator, String reason) {
// 1. 获取当前商品最新信息
Product product = productRepository.findById(productId)
.orElseThrow(() -> new RuntimeException("商品不存在"));
// 2. 创建新版本记录
ProductVersion version = new ProductVersion();
version.setProductId(productId);
version.setVersionNumber(getNextVersionNumber(productId));
version.setPrice(product.getPrice());
version.setStock(product.getStock());
// 设置其他字段...
version.setChangeType(changeType.getValue());
version.setOperatorId(getCurrentUserId()); // 从安全上下文获取
version.setOperatorName(operator);
version.setChangeReason(reason);
version.setCreatedAt(LocalDateTime.now());
versionRepository.save(version);
// 3. 记录变更详情(如果是修改操作)
if (changeType == ChangeType.UPDATE) {
recordChangeDetails(productId, version.getId());
}
// 4. 更新商品当前版本号
product.setCurrentVersionId(version.getId());
productRepository.save(product);
}
private void recordChangeDetails(Long productId, Long versionId) {
// 获取上一个版本
ProductVersion lastVersion = versionRepository.findTopByProductIdOrderByVersionNumberDesc(productId)
.filter(v -> v.getId() != versionId)
.orElse(null);
if (lastVersion == null) return; // 第一个版本无需比较
// 比较字段差异(简化示例)
compareAndRecordField("price", lastVersion.getPrice(), versionRepository.getById(versionId).getPrice());
compareAndRecordField("stock", lastVersion.getStock(), versionRepository.getById(versionId).getStock());
// 其他字段...
}
}
```
3. 版本对比功能
```java
@Service
public class ProductVersionCompareService {
@Autowired
private ProductVersionRepository versionRepository;
@Autowired
private ProductVersionDetailRepository detailRepository;
public Map
compareVersions(Long productId, Long versionId1, Long versionId2) { ProductVersion v1 = versionRepository.findById(versionId1) .orElseThrow(() -> new RuntimeException("版本1不存在")); ProductVersion v2 = versionRepository.findById(versionId2) .orElseThrow(() -> new RuntimeException("版本2不存在")); Map result = new HashMap<>(); // 基本字段比较 compareField(result, "价格", v1.getPrice(), v2.getPrice()); compareField(result, "库存", v1.getStock(), v2.getStock()); // 其他字段... // 规格比较(JSON字段) compareSpecs(result, v1.getSpecs(), v2.getSpecs()); // 从变更详情表获取更详细的变更记录 List details = detailRepository.findByVersionIdIn(Arrays.asList(versionId1, versionId2)); // 处理详情数据... return result; } private void compareField(Map result, String fieldName, Object value1, Object value2) { if (!Objects.equals(value1, value2)) { result.put(fieldName, Map.of( "version1", value1, "version2", value2 )); } } } ``` 五、前端展示方案 1. 版本列表组件 ```javascript function VersionList({ productId }) { const [versions, setVersions] = useState([]); useEffect(() => { fetch(`/api/products/${productId}/versions`) .then(res => res.json()) .then(data => setVersions(data)); }, [productId]); return ( 商品版本历史 版本号 操作类型 操作人 操作时间 操作 {versions.map(version => ( {version.versionNumber} {version.changeTypeText} {version.operatorName} {formatDate(version.createdAt)} compareWithCurrent(version.id)}>对比 rollbackToVersion(version.id)}>回滚 ))}
); } ``` 2. 版本对比视图 ```javascript function VersionCompare({ version1, version2 }) { const differences = compareVersions(version1, version2); return ( 版本对比: v{version1.versionNumber} vs v{version2.versionNumber} {Object.entries(differences).map(([field, { v1, v2 }]) => (
{field}: 旧值: {v1} 新值: {v2}
))}
); } ``` 六、性能优化方案 1. 异步处理:对于非关键的变更记录操作,采用消息队列异步处理 2. 增量存储:对于大字段如商品描述,采用增量存储策略 3. 定期归档:对超过一定时间的历史版本进行归档存储 4. 缓存策略:对频繁访问的最新版本信息进行缓存 5. 分表策略:按商品ID或时间范围对版本表进行分表 七、安全考虑 1. 操作审计:记录所有版本操作日志 2. 权限控制:只有特定角色可以查看历史版本和执行回滚 3. 数据加密:对敏感信息进行加密存储 4. 防篡改:使用数字签名或区块链技术确保版本数据不可篡改 八、扩展功能 1. 自动版本标记:对重要变更(如价格大幅调整)自动标记 2. 版本预览:在回滚前预览版本效果 3. 批量操作:支持批量回滚多个商品的特定版本 4. 数据分析:分析商品变更频率与销售数据的关系 通过以上设计和实现,美团买菜系统可以构建一个完善的商品迭代记录体系,既满足业务运营需求,又符合合规性要求,同时为数据分析提供有价值的历史数据支持。