字典注解
类注解
import lombok.NonNull;
import java.lang.annotation.*;
/**
* 数据字典类注解
*
* @author huxiang
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DictClass {
/**
* js字典文件名称 ,不填生成公用js 字典文件
*/
String dictJsonFile() default "dict";
/**
* 字典key
*/
@NonNull
String dictKey();
}
属性注解
import lombok.NonNull;
import java.lang.annotation.*;
/**
* 数据字典属性注解
*
* @author huxiang
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DictField {
/**
* 字典值样式 生成js用
*/
String cssClass() default "";
/**
* 字典值对应描述-中文
*/
@NonNull
String dictLabelZH();
/**
* 字典值对应描述-英文
*/
@NonNull
String dictLabelEN();
}
属性拓展注解
import lombok.NonNull;
import java.lang.annotation.*;
/**
* 数据字典拓展属性注解---用于标准数据字典外需要额外生成key-value
*
* @author huxiang
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DictFieldEx {
/**
* 拓展属性名
*/
@NonNull
String[] propKey();
/**
* 拓展属性值
*/
@NonNull
String[] propValue();
}
工具类
import com.paratera.console.dict.annotation.DictClass;
import com.paratera.console.dict.annotation.DictField;
import com.paratera.console.dict.annotation.DictFieldEx;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import .Resource;
import .support.PathMatchingResourcePatternResolver;
import org.springframework.util.StringUtils;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 数据字典工具类
*
* @author huxiang
*/
@Slf4j
public class DictUtils {
/**
* 缓存后台数据字典数据
*/
private static ConcurrentHashMap<String, String> dictMap = new ConcurrentHashMap<String, String>();
/**
* 空格常量
*/
private static String SPACE1 = " ";
private static String SPACE2 = " ";
private static String SPACE3 = " ";
/**
* 数据字典常量类路径
*/
private static String constansFilePath = "classpath:com/paratera/console/dict/constants/*.class";
/**
* 数据字典 全限定类名前缀
*/
private static String constansClassPrefix = "com.paratera.console.dict.constants.";
/**
* 生成前端json 字典文件路径
*/
private static String jsonDirUrl = DictUtils.class.getResource("/").getPath() + "dict/";
/**
* 初始化数据字典数据到缓存(可以在引入项目启动时加载)
*
* @throws IOException
* @throws ClassNotFoundException
*/
public static void initDictMapCache() throws IOException, ClassNotFoundException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException, InstantiationException {
PathMatchingResourcePatternResolver util = new PathMatchingResourcePatternResolver();
Resource[] files = util.getResources(constansFilePath);
for (Resource resource : files) {
String className = constansClassPrefix
+ resource.getFilename().substring(0, resource.getFilename().lastIndexOf("."));
Class<?> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(DictClass.class)) {
DictClass dictClass = clazz.getAnnotation(DictClass.class);
String dictKey = dictClass.dictKey();
Field[] fields = clazz.getDeclaredFields();
Object instance = clazz.getDeclaredConstructor().newInstance();
for (Field field : fields) {
if (field.isAnnotationPresent(DictField.class)) {
DictField dictField = field.getAnnotation(DictField.class);
dictMap.put(dictKey + ":" + field.get(instance) + ":zh", dictField.dictLabelZH());
dictMap.put(dictKey + ":" + field.get(instance) + ":en", dictField.dictLabelEN());
}
}
}
}
}
/**
* 从缓存中获取数据字典值对应的text
*
* @param dictKey
* @param dictValue
* @param language
* @return
*/
public static String getDictText(String dictKey, String dictValue, String language) {
return dictMap.get(dictKey + ":" + dictValue + ":" + language);
}
/**
* 生成json字典文件,可用于前端使用,统一前后端字典值
*
* @throws IOException
* @throws ClassNotFoundException
*/
public static void genJsonFile() throws IOException, ClassNotFoundException, InstantiationException,
IllegalAccessException, InvocationTargetException, NoSuchMethodException {
PathMatchingResourcePatternResolver util = new PathMatchingResourcePatternResolver();
Resource[] files = util.getResources(constansFilePath);
StringBuffer sb = new StringBuffer();
sb.append("{ \n");
for (int j = 0; j < files.length; j++) {
Resource resource = files[j];
String className = constansClassPrefix
+ resource.getFilename().substring(0, resource.getFilename().lastIndexOf("."));
Class<?> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(DictClass.class)) {
DictClass dictClass = clazz.getAnnotation(DictClass.class);
String jsonFile = dictClass.dictJsonFile();
// 私有json输出流
if (jsonFile != null && !jsonFile.equals("dict")) {
StringBuffer ss = new StringBuffer();
ss.append("{ \n");
// 这个地方第二个参数设置为0,主要是区别公用字典的逻辑
settingjsonInfo(ss, 0, clazz, dictClass, jsonFile);
ss.append("\n}");
OutputStreamWriter osw = new OutputStreamWriter(
new FileOutputStream(new File(jsonDirUrl + jsonFile + ".json")), "utf-8");
osw.append(sb.toString());
osw.close();
} else {
settingjsonInfo(sb, j, clazz, dictClass, jsonFile);
if (j == files.length - 1) {
sb.append("\n");
}
}
}
}
sb.append("\n}");
// 公用dict.json输出流
OutputStreamWriter outStreamWriter = new OutputStreamWriter(
new FileOutputStream(new File(jsonDirUrl + "dict.json")), "utf-8");
outStreamWriter.append(sb.toString());
outStreamWriter.close();
log.debug("json文件已生成,路径为:" + jsonDirUrl + "dict.json");
}
/**
* 设置json内容
*
* @param sb
* @param j
* @param clazz
* @param dictClass
* @param jsonFile
* @throws IOException
*/
private static void settingjsonInfo(StringBuffer sb, int j, Class<?> clazz, DictClass dictClass, String jsonFile)
throws IOException, InstantiationException, IllegalAccessException, NoSuchMethodException,
InvocationTargetException {
File file = new File(jsonDirUrl + jsonFile + ".json");
if (file.exists()) {
log.debug("json文件:" + jsonFile + ".json存在,开始覆盖!");
} else {
log.debug("json文件:" + jsonFile + ".json不存在,开始创建!");
File fileParent = new File(jsonDirUrl);
if (!fileParent.exists()) {
fileParent.mkdir();
}
file.createNewFile();
}
if (j != 0) {
sb.append(",\n");
}
// 添加常量配置
Field[] fields = clazz.getDeclaredFields();
Object instance = clazz.getDeclaredConstructor().newInstance();
boolean dictFieldGenerated = false;
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (field.isAnnotationPresent(DictField.class)) {
if (dictFieldGenerated) {
sb.append(",");
} else {
sb.append(SPACE1 + "\"" + dictClass.dictKey() + "\": [");
}
sb.append("{\n");
if (field.getGenericType().toString().equals("class java.lang.Integer")) {
sb.append(SPACE2 + "\"dictValue\":" + field.get(instance) + ",\n");
} else {
sb.append(SPACE2 + "\"dictValue\":\"" + field.get(instance) + "\",\n");
}
DictField dictField = field.getAnnotation(DictField.class);
sb.append(SPACE2 + "\"dictClass\":\"" + dictField.cssClass() + "\",\n");
sb.append(SPACE2 + "\"dictLabel\": {\n");
sb.append(SPACE3 + "\"zh\": \"" + dictField.dictLabelZH() + "\",\n");
sb.append(SPACE3 + "\"en\": \"" + dictField.dictLabelEN() + "\"\n");
//拼接拓展属性
settingDictFieldEx(sb, field);
if (!dictFieldGenerated) {
dictFieldGenerated = true;
}
}
}
if (dictFieldGenerated) {
sb.append("]");
}
}
/**
* 根据className生成数据字典json数据
*
* @param className
* @return
*/
public static String getJsonDictByClass(String className) {
//如果缓存有,从缓存取,没有,解析后缓存
if (dictMap.containsKey(className)) {
return dictMap.get(className);
}
Class<?> clazz = null;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
log.error("反射获取Class失败:" + e.getMessage());
}
StringBuffer sb = new StringBuffer();
if (clazz.isAnnotationPresent(DictClass.class)) {
DictClass dictClass = clazz.getAnnotation(DictClass.class);
// 添加常量配置
Field[] fields = clazz.getDeclaredFields();
Object instance = null;
try {
instance = clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
log.error("反射获取对象实例失败:" + e.getMessage());
}
boolean dictFieldGenerated = false;
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (field.isAnnotationPresent(DictField.class)) {
if (dictFieldGenerated) {
sb.append(",");
} else {
sb.append("[");
}
sb.append("{\n");
try {
if (field.getGenericType().toString().equals("class java.lang.Integer")) {
sb.append(SPACE2 + "\"dictValue\":" + field.get(instance) + ",\n");
} else {
sb.append(SPACE2 + "\"dictValue\":\"" + field.get(instance) + "\",\n");
}
} catch (IllegalAccessException e) {
log.error("反射获取对象实例字段值失败:" + e.getMessage());
}
sb.append(SPACE2 + "\"dictLabel\": {\n");
DictField dictField = field.getAnnotation(DictField.class);
sb.append(SPACE3 + "\"zh\": \"" + dictField.dictLabelZH() + "\",\n");
sb.append(SPACE3 + "\"en\": \"" + dictField.dictLabelEN() + "\"\n");
//拼接拓展属性
settingDictFieldEx(sb, field);
if (!dictFieldGenerated) {
dictFieldGenerated = true;
}
}
}
sb.append("]");
}
//缓存
dictMap.put(className, sb.toString());
return sb.toString();
}
/**
* 拓展注解处理@DictFieldEx
*
* @param sb
* @param field
*/
private static void settingDictFieldEx(StringBuffer sb, Field field) {
DictFieldEx dictFieldEx = field.getAnnotation(DictFieldEx.class);
if (dictFieldEx != null) {
sb.append(SPACE2 + "},\n");
String[] keys = dictFieldEx.propKey();
String[] values = dictFieldEx.propValue();
for (int k = 0; k < keys.length; k++) {
String label = null;
//如果value是json字符串,则不再加双引号包裹
boolean flag = values[k].trim().startsWith("{") && values[k].trim().endsWith("}");
if (flag) {
label = SPACE2 + "\"" + keys[k] + "\":" + values[k];
} else {
label = SPACE2 + "\"" + keys[k] + "\":" + "\"" + values[k];
label = label + "\"";
}
if (k == keys.length - 1) {
sb.append(label);
continue;
}
sb.append(label).append(",\n");
}
} else {
sb.append(SPACE2 + "}\n");
}
sb.append(SPACE1 + "}");
}
}
字典常量类配置示例
import com.paratera.console.dict.annotation.DictClass;
import com.paratera.console.dict.annotation.DictField;
import com.paratera.console.dict.annotation.DictFieldEx;
/**
* 消息类型字典常量类
* @date 2021-09-29 10:18:45
*/
@DictClass(dictKey = AlarmMessageTypeConstants.DICT_KEY)
public class AlarmMessageTypeConstants {
public static final String DICT_KEY = "alarmMessageType";
@DictField(dictLabelZH = "存储使用量预警通知", dictLabelEN = "Storage")
@DictFieldEx(propKey = {"labelType", "thresholdType", "onOff", "thresholdProps"},
propValue = {AlarmMessageCategoriesConstants.STORAGE, "1", "1", """
{
"unit":"int",
"unitName":"百分比",
"descZH":"存储使用量预警阈值(最小70%, 最大100%)",
"descEN":"Storage usage alarm threshold(min 70%, max 100%)",
"defaultValue":80,
"minValue":70,
"maxValue":100,
"step":1
}
"""})
public static final Integer STORAGE = 1;
@DictField(dictLabelZH = "作业长时间运行通知", dictLabelEN = "Job running overtime")
@DictFieldEx(propKey = {"labelType", "thresholdType", "onOff", "thresholdProps"},
propValue = {AlarmMessageCategoriesConstants.JOB, "1", "1", """
{
"unit":"float",
"unitName":"天",
"descZH":"作业长时间运行预警阈值(天)",
"descEN":"Warning threshold for long-time operation of job (days)",
"defaultValue":1,
"minValue":0.5,
"maxValue":14,
"step":0.5
}
"""})
public static final Integer LONG_JOB = 2;
@DictField(dictLabelZH = "账户余额预警通知", dictLabelEN = "Balance not enough")
@DictFieldEx(propKey = {"labelType", "thresholdType", "onOff", "thresholdProps"},
propValue = {AlarmMessageCategoriesConstants.FEE, "1", "1", """
{
"unit":"int",
"unitName":"元",
"descZH":"账户余额预警阈值(元)",
"descEN":"Insufficient balance alarm threshold (元)",
"defaultValue":200,
"minValue":1,
"maxValue":50000,
"step":1
}
"""})
public static final Integer AMOUNT_AVAILABLE = 3;
}
JSON文件格式
{
"alarmMessageType": [{
"dictValue":1,
"dictClass":"",
"dictLabel": {
"zh": "存储使用量预警通知",
"en": "Storage"
},
"labelType":"storage",
"thresholdType":"1",
"onOff":"1",
"thresholdProps":{
"unit":"int",
"unitName":"百分比",
"descZH":"存储使用量预警阈值(最小70%, 最大100%)",
"descEN":"Storage usage alarm threshold(min 70%, max 100%)",
"defaultValue":80,
"minValue":70,
"maxValue":100,
"step":1
}
},{
"dictValue":2,
"dictClass":"",
"dictLabel": {
"zh": "作业长时间运行通知",
"en": "Job running overtime"
},
"labelType":"job",
"thresholdType":"1",
"onOff":"1",
"thresholdProps":{
"unit":"float",
"unitName":"天",
"descZH":"作业长时间运行预警阈值(天)",
"descEN":"Warning threshold for long-time operation of job (days)",
"defaultValue":1,
"minValue":0.5,
"maxValue":14,
"step":0.5
}
},{
"dictValue":3,
"dictClass":"",
"dictLabel": {
"zh": "账户余额预警通知",
"en": "Balance not enough"
},
"labelType":"fee",
"thresholdType":"1",
"onOff":"1",
"thresholdProps":{
"unit":"int",
"unitName":"元",
"descZH":"账户余额预警阈值(元)",
"descEN":"Insufficient balance alarm threshold (元)",
"defaultValue":200,
"minValue":1,
"maxValue":50000,
"step":1
}
}]
}