刘帅
2026-09-30 a9a6d242b50a7c51ed04d612d9950608919bf2be
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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\\\"***\\\"");
    }
}