功能概述
商品更新记录功能用于跟踪和记录系统中商品信息的变更历史,包括价格调整、库存变动、商品上下架等操作。这有助于:
- 审计追踪
- 问题排查
- 数据恢复
- 业务分析
数据库设计
商品更新记录表(product_update_log)
```sql
CREATE TABLE product_update_log (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 主键ID,
product_id BIGINT NOT NULL COMMENT 商品ID,
update_type VARCHAR(20) NOT NULL COMMENT 更新类型(price/stock/status/info),
old_value TEXT COMMENT 旧值(JSON格式),
new_value TEXT COMMENT 新值(JSON格式),
update_by BIGINT COMMENT 操作人ID,
update_by_name VARCHAR(50) COMMENT 操作人姓名,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 更新时间,
ip_address VARCHAR(50) COMMENT 操作IP,
remark VARCHAR(255) COMMENT 备注
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=商品更新记录表;
```
后端实现
1. 使用AOP实现自动记录
```java
@Aspect
@Component
public class ProductUpdateLogAspect {
@Autowired
private ProductUpdateLogService logService;
// 拦截商品服务中的更新方法
@AfterReturning(pointcut = "execution(* com.xiaoxiang.service.ProductService.update*(..))",
returning = "result")
public void afterProductUpdate(JoinPoint joinPoint, Object result) {
// 获取方法参数
Object[] args = joinPoint.getArgs();
if (args == null || args.length == 0) return;
// 确定是哪个更新方法
String methodName = joinPoint.getSignature().getName();
String updateType = determineUpdateType(methodName);
// 获取商品ID(假设第一个参数是商品ID或商品对象)
Long productId = extractProductId(args[0]);
// 获取旧值(需要从数据库查询)
Product oldProduct = productService.getProductById(productId);
// 获取新值(从参数中获取或从结果中获取)
Product newProduct = extractNewProduct(args, result);
// 构建日志记录
ProductUpdateLog log = new ProductUpdateLog();
log.setProductId(productId);
log.setUpdateType(updateType);
log.setOldValue(convertToJson(oldProduct));
log.setNewValue(convertToJson(newProduct));
log.setUpdateBy(getCurrentUserId());
log.setUpdateByName(getCurrentUserName());
log.setIpAddress(getRequestIp());
// 保存日志
logService.save(log);
}
private String determineUpdateType(String methodName) {
if (methodName.contains("Price")) return "price";
if (methodName.contains("Stock")) return "stock";
if (methodName.contains("Status")) return "status";
return "info";
}
// 其他辅助方法...
}
```
2. 手动记录方式(适用于无法用AOP拦截的场景)
```java
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductUpdateLogService logService;
@Override
public boolean updateProductPrice(Long productId, BigDecimal newPrice) {
// 1. 获取旧价格
Product product = productMapper.selectById(productId);
BigDecimal oldPrice = product.getPrice();
// 2. 更新价格
boolean success = productMapper.updatePrice(productId, newPrice) > 0;
// 3. 记录日志
if (success) {
ProductUpdateLog log = new ProductUpdateLog();
log.setProductId(productId);
log.setUpdateType("price");
log.setOldValue("{price:" + oldPrice + "}");
log.setNewValue("{price:" + newPrice + "}");
log.setUpdateBy(getCurrentUserId());
// 设置其他字段...
logService.save(log);
}
return success;
}
}
```
前端实现
商品更新记录列表页面
```javascript
// Vue组件示例
export default {
data() {
return {
productId: this.$route.params.id,
updateLogs: [],
pagination: {
currentPage: 1,
pageSize: 10,
total: 0
}
}
},
created() {
this.fetchUpdateLogs();
},
methods: {
async fetchUpdateLogs() {
try {
const params = {
productId: this.productId,
pageNum: this.pagination.currentPage,
pageSize: this.pagination.pageSize
};
const response = await api.getProductUpdateLogs(params);
this.updateLogs = response.data.list;
this.pagination.total = response.data.total;
} catch (error) {
console.error(获取商品更新记录失败:, error);
}
},
handlePageChange(page) {
this.pagination.currentPage = page;
this.fetchUpdateLogs();
},
formatUpdateType(type) {
const typeMap = {
price: 价格变更,
stock: 库存变更,
status: 上下架状态变更,
info: 商品信息变更
};
return typeMap[type] || type;
},
showDiff(oldValue, newValue) {
// 显示变更前后的差异
const oldObj = JSON.parse(oldValue);
const newObj = JSON.parse(newValue);
// 实现差异对比逻辑...
}
}
}
```
商品更新记录列表模板
```html
商品更新记录
查看变更
@current-change="handlePageChange"
:current-page="pagination.currentPage"
:page-size="pagination.pageSize"
layout="prev, pager, next"
:total="pagination.total">
```
高级功能实现
1. 变更差异对比
```javascript
// 在showDiff方法中实现差异对比
showDiff(oldValue, newValue) {
const oldObj = JSON.parse(oldValue);
const newObj = JSON.parse(newValue);
const differences = [];
// 比较价格
if (oldObj.price !== newObj.price) {
differences.push({
field: 价格,
old: oldObj.price,
new: newObj.price
});
}
// 比较库存
if (oldObj.stock !== newObj.stock) {
differences.push({
field: 库存,
old: oldObj.stock,
new: newObj.stock
});
}
// 其他字段比较...
this.$alert(this.generateDiffHtml(differences), 变更详情, {
dangerouslyUseHTMLString: true
});
},
generateDiffHtml(diffs) {
let html =
;
diffs.forEach(diff => {
html += `
${diff.field}:
${diff.old}
→
${diff.new}
`;
});
html +=
;
return html;
}
```
2. 按变更类型筛选
```javascript
// 在组件中添加
data() {
return {
filter: {
updateType: // 空表示不筛选
},
updateTypes: [
{value: , label: 全部类型},
{value: price, label: 价格变更},
{value: stock, label: 库存变更},
{value: status, label: 上下架状态变更},
{value: info, label: 商品信息变更}
]
}
},
methods: {
async fetchUpdateLogs() {
try {
const params = {
productId: this.productId,
updateType: this.filter.updateType,
pageNum: this.pagination.currentPage,
pageSize: this.pagination.pageSize
};
// 其余逻辑...
}
}
}
```
性能优化建议
1. 异步记录日志:对于高频更新的场景,可以考虑使用消息队列异步记录日志
2. 批量插入:如果短时间内有大量更新,可以实现批量插入日志
3. 归档策略:对历史日志进行定期归档,避免主表过大
4. 缓存旧值:对于频繁更新的字段,可以考虑在内存中缓存旧值,减少数据库查询
安全考虑
1. 对日志记录进行权限控制,只有管理员可以查看
2. 敏感信息(如成本价)在记录前进行脱敏处理
3. 记录操作IP,便于安全审计
扩展功能
1. 变更通知:当特定字段变更时发送通知(如价格变动通知运营)
2. 自动回滚:基于日志实现简单的数据回滚功能
3. 数据分析:对变更频率、变更类型等进行分析,辅助运营决策
以上实现方案可以根据实际业务需求和技术栈进行调整和优化。