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 FIELD_NAMES = new LinkedHashMap(); 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 all = new LinkedHashMap(); 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 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\\\"***\\\""); } }