需求:API接口--【SaaS后台】增加数据操作日志记录
目前做的
实现过程
1.service中调用
- 异步处理数据及存储
2.1数据处理(展示替换)
2.2数据处理(数据对比)
/**
* 对比节点数据
*
* @param oldNode 旧节点数据
* @param newNode 新节点数据
* @param operationDetails 操作详情列表
*/
private void compareJsonNodes(JsonNode oldNode, JsonNode newNode, List<OperationDetails> operationDetails) {
// 检查两个节点是否都是数组
if (oldNode.isArray() && newNode.isArray()) {
int minSize = Math.min(oldNode.size(), newNode.size());
for (int i = 0; i < minSize; i++) {
compareJsonNodes(oldNode.get(i), newNode.get(i), operationDetails);
}
for (int i = minSize; i < oldNode.size(); i++) {
compareJsonNodes(oldNode.get(i), createEmptyNode(oldNode.get(i)), operationDetails);
}
for (int i = minSize; i < newNode.size(); i++) {
compareJsonNodes(createEmptyNode(newNode.get(i)), newNode.get(i), operationDetails);
}
return;
}
List<SysOperationLogTrackEnum> childNodes = getChildNodes();
Iterator<Map.Entry<String, JsonNode>> newFields = newNode.fields();
// 处理新数据中的字段
while (newFields.hasNext()) {
Map.Entry<String, JsonNode> entry = newFields.next();
String fieldName = entry.getKey();
Optional<SysOperationLogTrackEnum> operationLogTrackEnum = childNodes.stream()
.filter(r -> r.getColumnName().equals(fieldName))
.findFirst();
if (operationLogTrackEnum.isPresent()) {
SysOperationLogTrackEnum sysOperationLogTrackEnum = operationLogTrackEnum.get();
JsonNode newValue = entry.getValue();
JsonNode oldValue = oldNode.get(fieldName);
if ((newValue == null || newValue.isNull()) && (oldValue != null && !oldValue.isNull())) {
OperationDetails operationDetail = sysOperationLogTrackEnum.replaceValues(
fieldName, handleJsonNode(oldValue), null
);
operationDetails.add(operationDetail);
} else if (oldValue == null || oldValue.isNull() && (newValue != null && !newValue.isNull())) {
OperationDetails operationDetail = sysOperationLogTrackEnum.replaceValues(
fieldName, null, handleJsonNode(newValue)
);
operationDetails.add(operationDetail);
} else if (oldValue.equals(newValue)) {
} else if (!sysOperationLogTrackEnum.getChildNodes().isEmpty()) {
sysOperationLogTrackEnum.compareJsonNodes(oldValue, newValue, operationDetails);
} else {
OperationDetails operationDetail = sysOperationLogTrackEnum.replaceValues(
fieldName, handleJsonNode(oldValue), handleJsonNode(newValue)
);
operationDetails.add(operationDetail);
}
}
}
}
存在的疑问
1.不可避免的需要数据对比,这种方式是否可行
2.谁来定义哪些字段需要对比
3.需要默认展示方式如何展示,使用参数占位符如何展示
待优化
删除原始数据的存储
异步线程池进行存储
参数占位符入参展示
空间换时间把子节点数据做成hashMap存储减少时间复杂度
可以添加一个操作对象字段
可以把展示的方式改成一句话
展示时可以进行对比标记新增或删除的内容
假设
在只有两层递归结构的情况下,如果第一层包含数组,且每层大约有 10 个字段,我们可以更具体地计算时间复杂度。
- 第一层有一个数组,数组长度为
n
。 - 每个数组元素是一个 JSON 对象,包含大约 10 个字段。
- 第二层递归处理这些 JSON 对象中的字段。
分析
1. 第一层数组处理
- 数组长度为
n
。 - 对数组中的每个元素,调用
compareJsonNodes
处理其内部的 JSON 对象。由于数组长度为n
,这一层的时间复杂度为O(n)
。
2. 第二层字段处理
- 假设每个 JSON 对象包含
k
个字段(在你的例子中k ≈ 10
)。 - 处理每个字段时,可能需要对
childNodes.stream().filter(...).findFirst()
进行线性搜索。假设childNodes
的大小为m
,则对每个字段的处理时间为O(m)
。 - 第二层递归的复杂度为
O(k * m)
。由于k ≈ 10
,可以视为常数,复杂度为O(m)
。
综合计算
- 第一层的时间复杂度是
O(n)
。 - 每个数组元素在第二层的时间复杂度为
O(m)
,由于有n
个元素,总体时间复杂度为O(n * m)
。
总时间复杂度
在这种情况下,代码的总时间复杂度为 O(n * m)
,其中:
-
n
是第一层数组的长度。 -
m
是childNodes
的大小。
由于 k ≈ 10
,这个部分基本可以视为常数,不影响整体的时间复杂度计算。
a. 测试不同 n
值的性能表现,验证 O(n * m)
的实际影响。
b. 考虑优化 childNodes
搜索部分,减少 m
的影响。