一、功能概述 商品迭代记录是美团买菜系统中重要的商品管理功能,用于跟踪商品从上架到下架的全生命周期变化,包括价格调整、规格变更、描述修改、库存变化等所有关键操作记录。 二、系统架构设计 1.数据库设计 ```sql CREATETABLEproduct_versio
一、功能概述
商品迭代记录是美团买菜系统中重要的商品管理功能,用于跟踪商品从上架到下架的全生命周期变化,包括价格调整、规格变更、描述修改、库存变化等所有关键操作记录。
二、系统架构设计
1. 数据库设计
```sql
CREATE TABLE product_version_history (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 版本记录ID,
product_id BIGINT NOT NULL COMMENT 商品ID,
version_number INT NOT NULL COMMENT 版本号,
change_type VARCHAR(50) NOT NULL COMMENT 变更类型(PRICE/STOCK/SPEC/DESC/STATUS),
old_value TEXT COMMENT 旧值(JSON格式),
new_value TEXT COMMENT 新值(JSON格式),
change_reason VARCHAR(255) COMMENT 变更原因,
operator_id BIGINT NOT NULL COMMENT 操作人ID,
operator_name VARCHAR(100) NOT NULL COMMENT 操作人姓名,
change_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 变更时间,
ip_address VARCHAR(50) COMMENT 操作IP,
device_info VARCHAR(255) COMMENT 操作设备信息,
INDEX idx_product_id (product_id),
INDEX idx_change_time (change_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=商品版本历史记录表;
```
2. 核心模块设计
1. 变更监听模块
- 使用AOP切面或事件驱动机制监听商品相关操作
- 拦截商品价格修改、库存变更、上下架等操作
2. 数据转换模块
- 将变更前后的数据转换为标准JSON格式
- 对敏感信息进行脱敏处理
3. 存储模块
- 批量写入数据库减少IO操作
- 考虑使用消息队列异步处理高并发写入
4. 查询模块
- 支持按商品ID、时间范围、变更类型等条件查询
- 实现版本回滚功能
三、关键实现代码
1. 变更监听实现(Spring AOP示例)
```java
@Aspect
@Component
public class ProductChangeAspect {
@Autowired
private ProductVersionHistoryService historyService;
@AfterReturning(pointcut = "execution(* com.meituan.maicai.service.ProductService.updatePrice(..)) " +
"|| execution(* com.meituan.maicai.service.ProductService.updateStock(..)) " +
"|| execution(* com.meituan.maicai.service.ProductService.updateStatus(..))",
returning = "result")
public void afterProductChange(JoinPoint joinPoint, Object result) {
// 获取方法参数
Object[] args = joinPoint.getArgs();
Long productId = null;
// 根据不同方法解析参数
if (joinPoint.getSignature().getName().equals("updatePrice")) {
productId = (Long)args[0];
// 获取新旧价格...
} else if (joinPoint.getSignature().getName().equals("updateStock")) {
productId = (Long)args[0];
// 获取新旧库存...
}
// 记录变更历史
if (productId != null) {
historyService.recordChange(productId, getChangeType(joinPoint),
getOldValue(), getNewValue(),
getOperatorInfo());
}
}
private String getChangeType(JoinPoint joinPoint) {
// 根据方法名返回变更类型
switch (joinPoint.getSignature().getName()) {
case "updatePrice": return "PRICE";
case "updateStock": return "STOCK";
case "updateStatus": return "STATUS";
default: return "OTHER";
}
}
}
```
2. 历史记录服务实现
```java
@Service
public class ProductVersionHistoryServiceImpl implements ProductVersionHistoryService {
@Autowired
private ProductVersionHistoryMapper historyMapper;
@Autowired
private OperatorInfoProvider operatorInfoProvider;
@Override
@Transactional
public void recordChange(Long productId, String changeType,
String oldValue, String newValue,
OperatorInfo operatorInfo) {
// 获取当前最大版本号
Integer maxVersion = historyMapper.findMaxVersionByProductId(productId);
Integer newVersion = maxVersion == null ? 1 : maxVersion + 1;
// 构建历史记录对象
ProductVersionHistory history = new ProductVersionHistory();
history.setProductId(productId);
history.setVersionNumber(newVersion);
history.setChangeType(changeType);
history.setOldValue(oldValue);
history.setNewValue(newValue);
history.setOperatorId(operatorInfo.getOperatorId());
history.setOperatorName(operatorInfo.getOperatorName());
history.setIpAddress(operatorInfo.getIpAddress());
history.setDeviceInfo(operatorInfo.getDeviceInfo());
// 保存记录
historyMapper.insert(history);
}
@Override
public PageInfo
queryHistory(Long productId,
Date startTime,
Date endTime,
String changeType,
Integer pageNum,
Integer pageSize) {
PageHelper.startPage(pageNum, pageSize);
List list = historyMapper.selectByConditions(
productId, startTime, endTime, changeType);
return new PageInfo<>(list);
}
@Override
public boolean rollbackToVersion(Long productId, Integer targetVersion) {
// 1. 查询目标版本数据
ProductVersionHistory targetHistory = historyMapper.findByProductAndVersion(
productId, targetVersion);
if (targetHistory == null) {
return false;
}
// 2. 根据变更类型执行回滚操作
switch (targetHistory.getChangeType()) {
case "PRICE":
// 回滚价格
break;
case "STOCK":
// 回滚库存
break;
// 其他类型处理...
}
// 3. 记录回滚操作
recordChange(productId, "ROLLBACK",
JSON.toJSONString(getCurrentState(productId)),
JSON.toJSONString(targetHistory.getOldValue()),
operatorInfoProvider.getCurrentOperator());
return true;
}
}
```
四、高级功能实现
1. 版本差异对比
```java
public class VersionDiffUtil {
public static String generateDiff(String oldJson, String newJson) {
JSONObject oldObj = JSON.parseObject(oldJson);
JSONObject newObj = JSON.parseObject(newJson);
StringBuilder diff = new StringBuilder();
// 比较价格
if (!Objects.equals(oldObj.get("price"), newObj.get("price"))) {
diff.append("价格变更: ")
.append(oldObj.get("price"))
.append(" → ")
.append(newObj.get("price"))
.append("\n");
}
// 比较库存
if (!Objects.equals(oldObj.get("stock"), newObj.get("stock"))) {
diff.append("库存变更: ")
.append(oldObj.get("stock"))
.append(" → ")
.append(newObj.get("stock"))
.append("\n");
}
// 其他字段比较...
return diff.length() > 0 ? diff.toString() : "无变更";
}
}
```
2. 批量导入导出
```java
@Service
public class HistoryExportService {
@Autowired
private ProductVersionHistoryService historyService;
public void exportToExcel(Long productId, HttpServletResponse response) throws IOException {
// 查询历史记录
List histories = historyService.queryAllByProductId(productId);
// 创建Excel工作簿
try (XSSFWorkbook workbook = new XSSFWorkbook()) {
XSSFSheet sheet = workbook.createSheet("商品变更历史");
// 创建表头
Row headerRow = sheet.createRow(0);
String[] headers = {"版本号", "变更类型", "变更前", "变更后", "操作人", "操作时间"};
for (int i = 0; i < headers.length; i++) {
headerRow.createCell(i).setCellValue(headers[i]);
}
// 填充数据
for (int i = 0; i < histories.size(); i++) {
ProductVersionHistory history = histories.get(i);
Row row = sheet.createRow(i + 1);
row.createCell(0).setCellValue(history.getVersionNumber());
row.createCell(1).setCellValue(history.getChangeType());
row.createCell(2).setCellValue(history.getOldValue());
row.createCell(3).setCellValue(history.getNewValue());
row.createCell(4).setCellValue(history.getOperatorName());
row.createCell(5).setCellValue(DateUtil.format(history.getChangeTime(), "yyyy-MM-dd HH:mm:ss"));
}
// 设置响应头
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment;filename=product_history_" + productId + ".xlsx");
// 写入响应流
workbook.write(response.getOutputStream());
}
}
}
```
五、性能优化方案
1. 异步写入:使用消息队列(Kafka/RocketMQ)实现历史记录的异步写入
2. 批量插入:对于高频变更的商品,采用批量插入方式减少数据库IO
3. 读写分离:将历史记录查询操作路由到读库
4. 缓存热点数据:对最近30天的变更记录进行缓存
5. 分区表设计:按商品ID或时间范围对历史记录表进行分区
六、安全考虑
1. 操作审计:记录所有操作人的IP、设备信息
2. 数据脱敏:对价格等敏感信息进行脱敏处理
3. 权限控制:只有特定角色可以查看或导出历史记录
4. 操作日志:记录所有历史记录查询操作
七、部署与监控
1. 监控指标:
- 历史记录写入延迟
- 批量处理队列积压量
- 查询响应时间
2. 告警规则:
- 写入延迟超过500ms
- 队列积压超过1000条
- 查询错误率超过1%
3. 日志收集:
- 收集所有历史记录操作日志
- 记录关键操作耗时
八、扩展功能建议
1. 变更预测:基于历史变更数据预测未来价格/库存变化趋势
2. 自动回滚:当检测到异常变更时自动触发回滚
3. 变更影响分析:分析某个变更对销售的影响
4. 多版本对比:支持同时对比多个版本差异
通过以上设计和实现,美团买菜系统可以构建一个健壮的商品迭代记录系统,满足商品全生命周期管理的需求,为运营决策提供数据支持,同时满足合规审计要求。