package com.dsh.guns.modular.system.audit;
|
|
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONObject;
|
import io.swagger.annotations.ApiModelProperty;
|
import cn.mb.cloud.common.core.util.SpringContextHolder;
|
|
import java.lang.reflect.Field;
|
import java.util.LinkedHashMap;
|
import java.util.Objects;
|
import java.util.Map;
|
|
/** 审计数据统一脱敏并将常用字段名转换为中文,供日志页面直接展示。 */
|
public final class AuditDataFormatter {
|
private static final Map<String, String> FIELD_NAMES = new LinkedHashMap<String, String>();
|
static {
|
FIELD_NAMES.put("id", "ID"); FIELD_NAMES.put("ids", "ID列表");
|
FIELD_NAMES.put("name", "名称"); FIELD_NAMES.put("username", "用户名"); FIELD_NAMES.put("userName", "用户名");
|
FIELD_NAMES.put("account", "账号"); FIELD_NAMES.put("phone", "手机号"); FIELD_NAMES.put("mobile", "手机号");
|
FIELD_NAMES.put("password", "密码"); FIELD_NAMES.put("oldPwd", "旧密码"); FIELD_NAMES.put("newPwd", "新密码");
|
FIELD_NAMES.put("idCard", "身份证号"); FIELD_NAMES.put("identity", "身份信息");
|
FIELD_NAMES.put("status", "状态"); FIELD_NAMES.put("state", "状态"); FIELD_NAMES.put("type", "类型");
|
FIELD_NAMES.put("birthday", "生日"); FIELD_NAMES.put("sex", "性别"); FIELD_NAMES.put("roleid", "角色ID");
|
FIELD_NAMES.put("deptid", "部门ID"); FIELD_NAMES.put("avatar", "头像"); FIELD_NAMES.put("jstime", "入职时间");
|
FIELD_NAMES.put("email", "邮箱"); FIELD_NAMES.put("objectType", "归属类型"); FIELD_NAMES.put("objectId", "归属对象ID");
|
FIELD_NAMES.put("remark", "备注"); FIELD_NAMES.put("reason", "原因"); FIELD_NAMES.put("content", "内容");
|
FIELD_NAMES.put("operatorId", "运营商ID"); FIELD_NAMES.put("storeId", "门店ID");
|
FIELD_NAMES.put("userId", "用户ID"); FIELD_NAMES.put("roleId", "角色ID"); FIELD_NAMES.put("roleIds", "角色ID列表");
|
FIELD_NAMES.put("cityId", "城市ID"); FIELD_NAMES.put("courseId", "课程ID");
|
FIELD_NAMES.put("createdAt", "创建时间"); FIELD_NAMES.put("updateTime", "更新时间");
|
}
|
|
private AuditDataFormatter() { }
|
|
public static String format(Object value) {
|
return format(value, null);
|
}
|
|
/** entityType 非空时,字段中文名完全由实体注解决定。 */
|
public static String format(Object value, Class<?> entityType) {
|
if (value == null) return null;
|
Object json = value instanceof String ? parse((String) value) : JSON.toJSON(value);
|
return mask(JSON.toJSONString(localize(json, entityType)));
|
}
|
|
/** 返回前后快照中实际发生改变的字段;未变化字段不会写入审计记录。 */
|
public static ChangeSet changes(String before, String after) {
|
JSONObject beforeObject = object(before);
|
JSONObject afterObject = object(after);
|
JSONObject changedBefore = new JSONObject(true);
|
JSONObject changedAfter = new JSONObject(true);
|
Map<String, Object> all = new LinkedHashMap<String, Object>();
|
all.putAll(beforeObject);
|
all.putAll(afterObject);
|
for (String field : all.keySet()) {
|
Object oldValue = beforeObject.get(field);
|
Object newValue = afterObject.get(field);
|
if (!Objects.equals(JSON.toJSONString(oldValue), JSON.toJSONString(newValue))) {
|
changedBefore.put(field, oldValue);
|
changedAfter.put(field, newValue);
|
}
|
}
|
return new ChangeSet(changedBefore.isEmpty() ? null : mask(changedBefore.toJSONString()),
|
changedAfter.isEmpty() ? null : mask(changedAfter.toJSONString()));
|
}
|
|
private static JSONObject object(String value) {
|
if (value == null || value.trim().isEmpty()) return new JSONObject(true);
|
try {
|
JSONObject object = JSON.parseObject(value);
|
return object == null ? new JSONObject(true) : object;
|
} catch (Exception ignored) {
|
return new JSONObject(true);
|
}
|
}
|
|
public static final class ChangeSet {
|
private final String before;
|
private final String after;
|
private ChangeSet(String before, String after) { this.before = before; this.after = after; }
|
public String getBefore() { return before; }
|
public String getAfter() { return after; }
|
}
|
|
private static Object parse(String value) {
|
try { return JSON.parse(value); } catch (Exception ignored) { return value; }
|
}
|
|
private static Object localize(Object value, Class<?> entityType) {
|
if (value instanceof JSONObject) {
|
JSONObject source = (JSONObject) value;
|
JSONObject target = new JSONObject(true);
|
for (Map.Entry<String, Object> entry : source.entrySet()) {
|
target.put(displayName(entry.getKey(), entityType), localize(entry.getValue(), null));
|
}
|
return target;
|
}
|
if (value instanceof Map) {
|
JSONObject target = new JSONObject(true);
|
for (Object item : ((Map<?, ?>) value).entrySet()) {
|
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) item;
|
target.put(displayName(String.valueOf(entry.getKey()), entityType), localize(entry.getValue(), null));
|
}
|
return target;
|
}
|
if (value instanceof JSONArray) {
|
JSONArray target = new JSONArray();
|
for (Object item : (JSONArray) value) target.add(localize(item, null));
|
return target;
|
}
|
return value;
|
}
|
|
private static String displayName(String field, Class<?> entityType) {
|
String annotated = annotationName(entityType, field);
|
if (annotated != null) return annotated;
|
String name = FIELD_NAMES.get(field);
|
return name == null ? field : name;
|
}
|
|
private static String annotationName(Class<?> entityType, String fieldName) {
|
if (entityType == null) return null;
|
for (Class<?> type = entityType; type != null && type != Object.class; type = type.getSuperclass()) {
|
try {
|
Field field = type.getDeclaredField(fieldName);
|
AuditField audit = field.getAnnotation(AuditField.class);
|
if (audit != null) return audit.value();
|
ApiModelProperty api = field.getAnnotation(ApiModelProperty.class);
|
if (api != null && api.value() != null && !api.value().trim().isEmpty()) return api.value();
|
} catch (NoSuchFieldException ignored) { }
|
}
|
try {
|
String comment = SpringContextHolder.getBean(AuditColumnCommentResolver.class).resolve(entityType).get(fieldName);
|
if (comment != null && !comment.trim().isEmpty()) return comment;
|
} catch (Exception ignored) {
|
// 审计展示名解析失败时回退字段名,不能影响业务。
|
}
|
return null;
|
}
|
|
private static String mask(String text) {
|
return text.replaceAll("(?i)(\\\"?(password|oldpwd|newpwd|token|authorization|secret|idcard|identity|bank(card)?|mobile|phone|密码|手机号|身份证号)\\\"?\\s*[:=]\\s*)\\\"[^\\\"]*\\\"", "$1\\\"***\\\"");
|
}
|
}
|