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.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))); } 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\\\"***\\\""); } }