一、系统设计目标 1.实现商品全生命周期管理 2.记录商品每次变更的详细信息 3.支持版本回滚和差异对比 4.提供审计追踪功能 5.确保数据一致性和可追溯性 二、核心功能模块 1.商品基础信息管理 -商品ID、名称、分类、条码等基础属性 -商品状态管理(上架/下
一、系统设计目标
1. 实现商品全生命周期管理
2. 记录商品每次变更的详细信息
3. 支持版本回滚和差异对比
4. 提供审计追踪功能
5. 确保数据一致性和可追溯性
二、核心功能模块
1. 商品基础信息管理
- 商品ID、名称、分类、条码等基础属性
- 商品状态管理(上架/下架/预售/缺货等)
- 商品生命周期管理
2. 商品迭代记录模块
- 版本控制:为每个商品维护版本号(如V1.0, V1.1)
- 变更类型:新增、修改、删除、状态变更等
- 变更字段:记录具体修改的字段和旧值/新值
- 变更时间:精确到秒的变更时间戳
- 操作人:记录执行变更的操作员/系统账号
- 变更原因:可选的变更说明字段
3. 变更审批流程(可选)
- 变更申请
- 审批流程配置
- 审批记录留存
三、数据库设计
商品主表 (product_main)
```sql
CREATE TABLE product_main (
product_id VARCHAR(32) PRIMARY KEY,
current_version VARCHAR(16) NOT NULL,
name VARCHAR(100) NOT NULL,
category_id VARCHAR(32) NOT NULL,
barcode VARCHAR(50),
status TINYINT NOT NULL COMMENT 1-上架 2-下架 3-预售 4-缺货,
create_time DATETIME NOT NULL,
update_time DATETIME NOT NULL
);
```
商品版本历史表 (product_version_history)
```sql
CREATE TABLE product_version_history (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
product_id VARCHAR(32) NOT NULL,
version VARCHAR(16) NOT NULL,
change_type TINYINT NOT NULL COMMENT 1-新增 2-修改 3-删除 4-状态变更,
change_fields JSON NOT NULL COMMENT 记录变更的字段和值,
operator VARCHAR(50) NOT NULL,
change_reason VARCHAR(500),
change_time DATETIME NOT NULL,
INDEX idx_product_id (product_id),
INDEX idx_change_time (change_time)
);
```
商品字段变更详情表 (product_field_changes)
```sql
CREATE TABLE product_field_changes (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
history_id BIGINT NOT NULL COMMENT 关联version_history表ID,
field_name VARCHAR(50) NOT NULL,
old_value TEXT,
new_value TEXT,
FOREIGN KEY (history_id) REFERENCES product_version_history(id)
);
```
四、核心实现逻辑
1. 商品更新流程
```java
public class ProductService {
@Transactional
public void updateProduct(ProductUpdateDTO dto, String operator) {
// 1. 获取当前商品信息
Product current = productRepository.findById(dto.getProductId())
.orElseThrow(() -> new BusinessException("商品不存在"));
// 2. 创建新版本号
String newVersion = generateNextVersion(current.getCurrentVersion());
// 3. 记录变更历史
ProductVersionHistory history = new ProductVersionHistory();
history.setProductId(dto.getProductId());
history.setVersion(newVersion);
history.setChangeType(ChangeType.MODIFY);
history.setOperator(operator);
history.setChangeTime(LocalDateTime.now());
// 4. 构建变更字段列表
List
changes = new ArrayList<>();
if (!Objects.equals(current.getName(), dto.getName())) {
changes.add(new ProductFieldChange("name", current.getName(), dto.getName()));
}
// 其他字段比较...
// 5. 保存变更记录
history.setChangeFields(changes); // 实际可用JSON存储或关联表
productVersionHistoryRepository.save(history);
// 6. 更新商品主表
current.setCurrentVersion(newVersion);
current.setName(dto.getName());
// 其他字段更新...
productRepository.save(current);
}
private String generateNextVersion(String currentVersion) {
// 版本号生成逻辑,如V1.0 -> V1.1
// 可根据业务需求实现
}
}
```
2. 版本回滚实现
```java
public void rollbackToVersion(String productId, String targetVersion, String operator) {
// 1. 获取目标版本信息
ProductVersionHistory targetHistory = productVersionHistoryRepository
.findByProductIdAndVersion(productId, targetVersion)
.orElseThrow(() -> new BusinessException("目标版本不存在"));
// 2. 获取当前商品信息
Product current = productRepository.findById(productId)
.orElseThrow(() -> new BusinessException("商品不存在"));
// 3. 创建回滚版本号
String newVersion = generateNextVersion(targetVersion);
// 4. 记录回滚操作
ProductVersionHistory rollbackHistory = new ProductVersionHistory();
rollbackHistory.setProductId(productId);
rollbackHistory.setVersion(newVersion);
rollbackHistory.setChangeType(ChangeType.ROLLBACK);
rollbackHistory.setOperator(operator);
rollbackHistory.setChangeTime(LocalDateTime.now());
rollbackHistory.setChangeReason("回滚到版本 " + targetVersion);
// 5. 解析目标版本字段并更新商品
// 这里需要根据实际存储方式解析targetHistory中的变更字段
// 示例伪代码:
Map targetFields = parseHistoryFields(targetHistory);
current.setCurrentVersion(newVersion);
current.setName((String)targetFields.get("name"));
// 其他字段更新...
// 6. 保存记录
productRepository.save(current);
productVersionHistoryRepository.save(rollbackHistory);
}
```
五、高级功能实现
1. 变更差异对比
```java
public List compareVersions(String productId, String version1, String version2) {
// 获取两个版本的历史记录
ProductVersionHistory history1 = productVersionHistoryRepository
.findByProductIdAndVersion(productId, version1)
.orElseThrow(() -> new BusinessException("版本1不存在"));
ProductVersionHistory history2 = productVersionHistoryRepository
.findByProductIdAndVersion(productId, version2)
.orElseThrow(() -> new BusinessException("版本2不存在"));
// 解析字段变更
Map fields1 = parseHistoryFields(history1);
Map fields2 = parseHistoryFields(history2);
// 比较差异
List diffs = new ArrayList<>();
// 获取所有字段名(可根据业务需要调整)
Set allFields = new HashSet<>();
allFields.addAll(fields1.keySet());
allFields.addAll(fields2.keySet());
for (String field : allFields) {
Object val1 = fields1.get(field);
Object val2 = fields2.get(field);
if (!Objects.equals(val1, val2)) {
diffs.add(new FieldDiff(field, val1, val2));
}
}
return diffs;
}
```
2. 变更审计日志
```java
@Aspect
@Component
public class ProductChangeAuditAspect {
@Autowired
private ProductVersionHistoryRepository historyRepository;
@AfterReturning(pointcut = "execution(* com.dingdong.product.service.*.update*(..)) || " +
"execution(* com.dingdong.product.service.*.delete*(..)) || " +
"execution(* com.dingdong.product.service.*.changeStatus*(..))",
returning = "result")
public void afterProductChange(JoinPoint joinPoint, Object result) {
// 获取方法参数
Object[] args = joinPoint.getArgs();
String productId = null;
String operator = "system"; // 默认值,实际应从安全上下文获取
// 解析参数获取productId和operator
// 这里需要根据实际方法签名实现参数解析
// 获取方法名
String methodName = joinPoint.getSignature().getName();
// 确定变更类型
ChangeType changeType;
if (methodName.startsWith("update")) {
changeType = ChangeType.MODIFY;
} else if (methodName.startsWith("delete")) {
changeType = ChangeType.DELETE;
} else if (methodName.startsWith("changeStatus")) {
changeType = ChangeType.STATUS_CHANGE;
} else {
changeType = ChangeType.OTHER;
}
// 创建并保存变更记录
ProductVersionHistory history = new ProductVersionHistory();
history.setProductId(productId);
history.setVersion("AUTO-" + System.currentTimeMillis()); // 临时版本号,实际更新流程中会设置正确版本
history.setChangeType(changeType);
history.setOperator(operator);
history.setChangeTime(LocalDateTime.now());
// 对于更新操作,需要记录具体变更字段
if (changeType == ChangeType.MODIFY) {
// 这里需要实现字段差异检测逻辑
// 示例:记录所有非null参数作为变更字段
Map changes = new HashMap<>();
// 解析args填充changes...
history.setChangeFields(changes);
}
historyRepository.save(history);
}
}
```
六、前端展示建议
1. 商品详情页:
- 显示当前版本号
- 提供"查看变更历史"按钮
2. 变更历史页面:
- 时间线形式展示所有变更
- 每条记录显示:
* 变更时间
* 操作人
* 变更类型
* 变更摘要(如"修改了商品名称从苹果到红富士苹果")
- 提供"查看详情"按钮显示完整变更字段
3. 版本对比页面:
- 选择两个版本进行对比
- 表格形式展示字段差异
* 字段名 | 版本1值 | 版本2值
七、性能优化考虑
1. 变更记录存储:
- 频繁变更的字段可考虑单独建表
- 不常查询的历史记录可归档到冷存储
2. 查询优化:
- 为常用查询字段建立索引(product_id, change_time等)
- 对变更历史表进行分表(按时间或商品ID分片)
3. 缓存策略:
- 缓存当前商品信息和最新版本号
- 变更后及时更新缓存
八、扩展功能
1. 变更订阅通知:
- 当特定商品变更时通知相关人员
- 支持邮件/短信/站内信通知
2. 变更影响分析:
- 分析商品变更对其他系统(如库存、促销、订单)的影响
3. 批量变更管理:
- 支持批量商品变更的版本控制
4. 变更模板:
- 预定义常见变更模板(如季节性价格调整)
此方案提供了完整的商品迭代记录实现框架,可根据叮咚买菜实际业务需求和技术栈进行调整优化。