一、功能概述
商品迭代记录功能用于跟踪和管理叮咚买菜平台上商品的变更历史,包括商品信息修改、上下架状态变化、价格调整等关键操作记录,以提升商品管理透明度和可追溯性。
二、系统架构设计
1. 数据库设计
```sql
-- 商品迭代记录表
CREATE TABLE `product_iteration_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID,
`product_id` bigint(20) NOT NULL COMMENT 商品ID,
`iteration_type` tinyint(4) NOT NULL COMMENT 迭代类型:1-基本信息变更 2-上下架状态变更 3-价格变更 4-库存变更 5-图片变更,
`change_field` varchar(50) DEFAULT NULL COMMENT 变更字段(如name,price,stock等),
`old_value` text COMMENT 旧值,
`new_value` text COMMENT 新值,
`operator_id` bigint(20) DEFAULT NULL COMMENT 操作人ID,
`operator_name` varchar(50) DEFAULT NULL COMMENT 操作人姓名,
`operation_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 操作时间,
`operation_ip` varchar(50) DEFAULT NULL COMMENT 操作IP,
`remark` varchar(255) DEFAULT NULL COMMENT 备注,
PRIMARY KEY (`id`),
KEY `idx_product_id` (`product_id`),
KEY `idx_operation_time` (`operation_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=商品迭代记录表;
```
2. 核心模块设计
1. 数据采集模块:在商品信息变更时自动捕获变更数据
2. 记录存储模块:将变更记录持久化到数据库
3. 查询展示模块:提供商品迭代历史查询界面
4. 通知预警模块:对关键变更发送通知
三、核心功能实现
1. 变更捕获实现
方式一:AOP切面实现(推荐)
```java
@Aspect
@Component
public class ProductChangeAspect {
@Autowired
private ProductIterationLogService logService;
@AfterReturning(pointcut = "execution(* com.dingdong.product.service.ProductService.update*(..)) && args(product,..)",
returning = "result")
public void afterProductUpdate(JoinPoint joinPoint, Product product, Boolean result) {
if (result) {
// 获取方法名判断具体操作类型
String methodName = joinPoint.getSignature().getName();
Product oldProduct = // 从数据库获取旧数据
ProductIterationLog log = new ProductIterationLog();
log.setProductId(product.getId());
log.setOperatorId(// 获取当前操作人ID);
log.setOperationTime(new Date());
switch (methodName) {
case "updateBasicInfo":
log.setIterationType(1);
// 比较新旧数据,设置变更字段和值
compareAndSetChange(oldProduct, product, log);
break;
case "updatePrice":
log.setIterationType(3);
log.setChangeField("price");
log.setOldValue(oldProduct.getPrice().toString());
log.setNewValue(product.getPrice().toString());
break;
// 其他方法处理...
}
logService.save(log);
}
}
private void compareAndSetChange(Product oldProduct, Product newProduct, ProductIterationLog log) {
// 实现字段比较逻辑
// 示例:
if (!Objects.equals(oldProduct.getName(), newProduct.getName())) {
log.setChangeField("name");
log.setOldValue(oldProduct.getName());
log.setNewValue(newProduct.getName());
}
// 其他字段比较...
}
}
```
方式二:事件驱动实现
```java
@Service
public class ProductService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public boolean updateProduct(Product product) {
// 业务逻辑...
// 获取旧数据
Product oldProduct = productRepository.findById(product.getId()).orElse(null);
// 发布商品变更事件
ProductChangeEvent event = new ProductChangeEvent(
this,
oldProduct,
product,
// 其他上下文信息
);
eventPublisher.publishEvent(event);
return true;
}
}
@Component
public class ProductChangeEventListener {
@Autowired
private ProductIterationLogService logService;
@EventListener
public void handleProductChange(ProductChangeEvent event) {
// 根据事件类型创建对应的迭代记录
// 实现逻辑与AOP方式类似
}
}
```
2. 查询接口实现
```java
@RestController
@RequestMapping("/api/product/iteration")
public class ProductIterationController {
@Autowired
private ProductIterationLogService logService;
@GetMapping("/history")
public ResponseEntity
> getProductIterationHistory(
@RequestParam Long productId,
@RequestParam(required = false) Integer iterationType,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by("operationTime").descending());
Page logs = logService.findByProductId(productId, iterationType, pageable);
return ResponseEntity.ok(logs.map(this::convertToDTO));
}
private ProductIterationLogDTO convertToDTO(ProductIterationLog log) {
// 转换逻辑
}
}
```
3. 前端展示实现
```javascript
// Vue组件示例
export default {
data() {
return {
productId: this.$route.params.id,
iterationHistory: [],
pagination: {
currentPage: 1,
pageSize: 10,
total: 0
},
filters: {
iterationType: null
}
}
},
created() {
this.fetchIterationHistory();
},
methods: {
async fetchIterationHistory() {
const params = {
productId: this.productId,
iterationType: this.filters.iterationType,
page: this.pagination.currentPage - 1,
size: this.pagination.pageSize
};
const response = await api.getProductIterationHistory(params);
this.iterationHistory = response.data.content;
this.pagination.total = response.data.totalElements;
}
}
}
```
四、高级功能实现
1. 变更对比视图
```javascript
// 实现前后变更的差异高亮显示
methods: {
getChangeDiff(log) {
if (!log.oldValue || !log.newValue) return null;
// 简单实现:返回包含变更的HTML片段
return `
${log.changeField}:
旧值:
${log.oldValue}
新值:
${log.newValue}
`;
}
}
```
2. 关键变更通知
```java
@Service
public class NotificationService {
@Autowired
private EmailService emailService;
@Autowired
private SmsService smsService;
public void notifyCriticalChange(ProductIterationLog log) {
// 定义关键变更类型
Set criticalTypes = Set.of(2, 3); // 上下架和价格变更
if (criticalTypes.contains(log.getIterationType())) {
String subject = "商品关键信息变更通知";
String content = String.format(
"商品ID:%d 发生关键变更:\n类型:%d\n字段:%s\n旧值:%s\n新值:%s\n操作人:%s\n时间:%s",
log.getProductId(),
log.getIterationType(),
log.getChangeField(),
log.getOldValue(),
log.getNewValue(),
log.getOperatorName(),
log.getOperationTime()
);
// 发送邮件给相关人员
emailService.sendToAdmins(subject, content);
// 发送短信给负责人(可选)
if (log.getIterationType() == 3) { // 价格变更
smsService.sendToManager(content);
}
}
}
}
```
五、性能优化考虑
1. 异步记录:使用消息队列异步处理变更记录,避免阻塞主业务流程
2. 批量插入:对于高频变更的商品,实现批量记录插入
3. 索引优化:为常用查询字段建立索引
4. 数据归档:定期将历史数据归档到冷存储
六、安全与合规
1. 操作审计:记录操作人IP和身份信息
2. 数据脱敏:对敏感信息进行脱敏处理
3. 访问控制:限制迭代记录的查询权限
4. 数据保留策略:根据法规要求设置数据保留期限
七、部署与监控
1. 灰度发布:先在测试环境验证功能
2. 监控指标:
- 记录写入成功率
- 查询响应时间
- 关键变更通知送达率
3. 告警规则:
- 记录写入失败率 > 1%
- 查询超时率 > 5%
八、测试方案
1. 单元测试:验证变更捕获逻辑
2. 集成测试:测试完整记录流程
3. 性能测试:模拟高并发变更场景
4. 用户验收测试:由产品经理验证功能符合需求
九、维护与迭代
1. 版本控制:记录功能变更历史
2. 文档更新:维护系统设计文档和API文档
3. 用户反馈:收集使用反馈持续优化
通过以上方案,叮咚买菜系统可以实现完善的商品迭代记录功能,提升商品管理的透明度和效率,同时为数据分析提供有价值的历史数据支持。