初始化

This commit is contained in:
2026-02-22 18:56:10 +08:00
commit 26677972a6
3112 changed files with 255972 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
package tech.easyflow.common.dict;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class Dict implements Serializable {
private String name;
private String code;
private String description;
private List<DictItem> items;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<DictItem> getItems() {
return items;
}
public void setItems(List<DictItem> items) {
this.items = items;
}
public void addItem(DictItem item){
if (this.items == null){
this.items = new ArrayList<>();
}
items.add(item);
}
}

View File

@@ -0,0 +1,42 @@
package tech.easyflow.common.dict;
import tech.easyflow.common.util.SpringContextUtil;
import tech.easyflow.common.annotation.DictDef;
import tech.easyflow.common.dict.loader.EnumDictLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.event.EventListener;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Component;
@Component
public class DictDefAutoConfig {
private static final Logger LOG = LoggerFactory.getLogger(DictDefAutoConfig.class);
@EventListener(ApplicationReadyEvent.class)
public <E extends Enum<E>> void onApplicationStartup() {
DictManager dictManager = SpringContextUtil.getBean(DictManager.class);
ClassPathScanningCandidateComponentProvider scanner =
new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(DictDef.class));
for (BeanDefinition bd : scanner.findCandidateComponents("tech.easyflow")) {
try {
@SuppressWarnings("unchecked")
Class<E> enumClass = (Class<E>) Class.forName(bd.getBeanClassName());
DictDef dictDef = enumClass.getAnnotation(DictDef.class);
dictManager.putLoader(new EnumDictLoader<>(dictDef.code(), enumClass, dictDef.keyField(), dictDef.labelField()));
} catch (ClassNotFoundException e) {
LOG.warn("Could not resolve class object for bean definition", e);
}
}
}
}

View File

@@ -0,0 +1,108 @@
package tech.easyflow.common.dict;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* 在 DictItem 的属性中key value 是同一个值,而 label 和 title 是同一个值
* 进行这么设计的原因,是为了适配不同的前段组件,不需要对数据进行字段转换
*/
public class DictItem implements Serializable {
/**
* 值
*/
private Object value;
/**
* key
*/
private Object key;
/**
* 标签
*/
private String label;
/**
* 标题
*/
private String title;
/**
* 禁用
*/
private Boolean disabled;
private Integer layerNo;
private List<DictItem> children;
public DictItem() {
}
public DictItem(Object value, String label) {
this.setValue(value);
this.setLabel(label);
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
this.key = value;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
this.title = label;
}
public List<DictItem> getChildren() {
return children;
}
public void setChildren(List<DictItem> children) {
this.children = children;
}
public Object getKey() {
return key;
}
public void setKey(Object key) {
this.key = key;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Boolean getDisabled() {
return disabled;
}
public void setDisabled(Boolean disabled) {
this.disabled = disabled;
}
public Integer getLayerNo() {
return layerNo;
}
public void setLayerNo(Integer layerNo) {
this.layerNo = layerNo;
}
public void addChild(DictItem childDictItem) {
if (children == null) {
children = new ArrayList<>();
}
children.add(childDictItem);
}
}

View File

@@ -0,0 +1,8 @@
package tech.easyflow.common.dict;
import java.util.Map;
public interface DictLoader {
String code();
Dict load(String keyword, Map<String, String[]> parameters);
}

View File

@@ -0,0 +1,48 @@
package tech.easyflow.common.dict;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
public class DictManager implements BeanPostProcessor {
private Map<String, DictLoader> loaders = new HashMap<>();
public DictManager(ObjectProvider<List<DictLoader>> listObjectProvider) {
List<DictLoader> dictLoaders = listObjectProvider.getIfAvailable();
if (dictLoaders != null) {
dictLoaders.forEach(dictLoader -> loaders.put(dictLoader.code(), dictLoader));
}
}
public Map<String, DictLoader> getLoaders() {
return loaders;
}
public void setLoaders(Map<String, DictLoader> loaders) {
this.loaders = loaders;
}
public void putLoader(DictLoader loader) {
if (loader == null){
return;
}
loaders.put(loader.code(), loader);
}
public void removeLoader(String code) {
loaders.remove(code);
}
public DictLoader getLoader(String code) {
if (loaders == null || loaders.isEmpty()) {
return null;
}
return loaders.get(code);
}
}

View File

@@ -0,0 +1,29 @@
package tech.easyflow.common.dict;
import tech.easyflow.common.annotation.DictDef;
@DictDef(name = "字典类型", code = "dictType", keyField = "value", labelField = "text")
public enum DictType {
CUSTOM(1, "自定义字典"),
TABLE(2, "数据表字典"),
ENUM(3, "枚举类字典"),
SYSTEM(4, "系统字典"),
;
private final int value;
private final String text;
DictType(int value, String text) {
this.value = value;
this.text = text;
}
public int getValue() {
return value;
}
public String getText() {
return text;
}
}

View File

@@ -0,0 +1,208 @@
package tech.easyflow.common.dict.loader;
import tech.easyflow.common.tree.Tree;
import tech.easyflow.common.util.RequestUtil;
import tech.easyflow.common.dict.Dict;
import tech.easyflow.common.dict.DictItem;
import tech.easyflow.common.dict.DictLoader;
import com.mybatisflex.core.row.Db;
import com.mybatisflex.core.row.Row;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class DatabaseDictLoader implements DictLoader {
private static final Object[] EMPTY_PARAMS = new Object[0];
private String code;
private String tableName;
private String keyColumn;
private String labelColumn;
private String parentColumn;
private String orderBy;
public DatabaseDictLoader(String code, String tableName, String keyColumn, String labelColumn) {
this.code = code;
this.tableName = tableName;
this.keyColumn = keyColumn;
this.labelColumn = labelColumn;
}
public DatabaseDictLoader(String code, String tableName, String keyColumn, String labelColumn, String parentColumn) {
this.code = code;
this.tableName = tableName;
this.keyColumn = keyColumn;
this.labelColumn = labelColumn;
this.parentColumn = parentColumn;
}
public DatabaseDictLoader(String code, String tableName, String keyColumn, String labelColumn, String parentColumn, String orderBy) {
this.code = code;
this.tableName = tableName;
this.keyColumn = keyColumn;
this.labelColumn = labelColumn;
this.parentColumn = parentColumn;
this.orderBy = orderBy;
}
@Override
public String code() {
return code;
}
@Override
public Dict load(String keyword, Map<String, String[]> parameters) {
String sql = "SELECT " + keyColumn + ", " + labelColumn;
if (StringUtils.hasText(parentColumn)) {
sql += ", " + parentColumn;
}
sql += " FROM " + tableName;
if (StringUtils.hasText(keyword)) {
sql += "WHERE " + labelColumn + " = ?";
}
if (StringUtils.hasText(orderBy)) {
sql += " ORDER BY " + orderBy;
}
List<Row> rows = Db.selectListBySql(sql, StringUtils.hasText(keyword) ? new Object[]{keyword.trim()} : EMPTY_PARAMS);
if (rows == null || rows.isEmpty()) {
return null;
}
List<DictItem> items = new ArrayList<>(rows.size());
Boolean asTree = RequestUtil.getParamAsBoolean(parameters,"asTree");
//有树形结构
if (StringUtils.hasText(parentColumn)) {
List<Row> topLayerRows = findTopLayerRows(rows);
//以树形结构输出
if (asTree != null && asTree) {
makeTree(topLayerRows, items, rows);
}
//以平级结构输出
else {
makeLayer(0, topLayerRows, items, rows);
}
}
//无树形结构数据
else {
for (Row row : rows) {
DictItem dictItem = new DictItem();
dictItem.setValue(row.get(keyColumn));
dictItem.setLabel(String.valueOf(row.get(labelColumn)));
items.add(dictItem);
}
}
Dict dict = new Dict();
dict.setCode(code);
dict.setItems(items);
return dict;
}
private void makeTree(List<Row> parentRows, List<DictItem> parentItems, List<Row> allRows) {
for (Row parentRow : parentRows) {
DictItem parentItem = row2DictItem(0, parentRow);
parentItems.add(parentItem);
List<Row> children = new ArrayList<>();
for (Row maybeChild : allRows) {
if (Objects.equals(maybeChild.get(parentColumn), parentRow.get(keyColumn))) {
children.add(maybeChild);
}
}
if (!children.isEmpty()) {
List<DictItem> childrenItems = new ArrayList<>(children.size());
parentItem.setChildren(childrenItems);
makeTree(children, childrenItems, allRows);
}
}
}
private void makeLayer(int layerNo, List<Row> parentRows, List<DictItem> parentItems, List<Row> allRows) {
for (Row parentRow : parentRows) {
parentItems.add(row2DictItem(layerNo, parentRow));
List<Row> children = new ArrayList<>();
for (Row maybeChild : allRows) {
if (Objects.equals(maybeChild.get(parentColumn), parentRow.get(keyColumn))) {
children.add(maybeChild);
}
}
if (!children.isEmpty()) {
makeLayer(layerNo + 1, children, parentItems, allRows);
}
}
}
private DictItem row2DictItem(int layerNo, Row row) {
DictItem dictItem = new DictItem();
dictItem.setValue(row.get(keyColumn));
dictItem.setLabel(Tree.getPrefix(layerNo) + row.get(labelColumn));
dictItem.setLayerNo(layerNo);
return dictItem;
}
private List<Row> findTopLayerRows(List<Row> rows) {
List<Row> topLayerRows = new ArrayList<>();
for (Row row : rows) {
boolean foundParent = false;
for (Row row1 : rows) {
if (Objects.equals(row1.get(keyColumn), row.get(parentColumn))) {
foundParent = true;
break;
}
}
if (!foundParent) {
topLayerRows.add(row);
}
}
return topLayerRows;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getKeyColumn() {
return keyColumn;
}
public void setKeyColumn(String keyColumn) {
this.keyColumn = keyColumn;
}
public String getLabelColumn() {
return labelColumn;
}
public void setLabelColumn(String labelColumn) {
this.labelColumn = labelColumn;
}
public String getOrderBy() {
return orderBy;
}
public void setOrderBy(String orderBy) {
this.orderBy = orderBy;
}
}

View File

@@ -0,0 +1,214 @@
package tech.easyflow.common.dict.loader;
import cn.hutool.core.util.StrUtil;
import tech.easyflow.common.tree.Tree;
import tech.easyflow.common.util.RequestUtil;
import tech.easyflow.common.dict.Dict;
import tech.easyflow.common.dict.DictItem;
import tech.easyflow.common.dict.DictLoader;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.mybatisflex.core.BaseMapper;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class DbDataLoader<T> implements DictLoader {
private final String code;
private final BaseMapper<T> mapper;
// 下划线命名
private final String keyColumn;
private final String labelColumn;
private final String parentColumn;
// 驼峰命名
private final String keyColumnCamelCase;
private final String labelColumnCamelCase;
private final String parentColumnCamelCase;
private final String orderBy;
private boolean queryStatus = false;
public DbDataLoader(String code,
BaseMapper<T> mapper,
String keyColumn,
String labelColumn,
String parentColumn,
String orderBy,
boolean queryStatus) {
this.code = code;
this.mapper = mapper;
this.keyColumn = keyColumn;
this.labelColumn = labelColumn;
this.parentColumn = parentColumn;
this.keyColumnCamelCase = StrUtil.toCamelCase(this.keyColumn);
this.labelColumnCamelCase = StrUtil.toCamelCase(labelColumn);
this.parentColumnCamelCase = StrUtil.toCamelCase(parentColumn);
this.orderBy = orderBy;
this.queryStatus = queryStatus;
}
@Override
public String code() {
return code;
}
@Override
public Dict load(String keyword, Map<String, String[]> parameters) {
QueryWrapper where = QueryWrapper.create();
if (StrUtil.isNotEmpty(keyword)) {
where.eq(labelColumn, keyword);
}
if (queryStatus) {
where.eq("status", 1);
}
if (StrUtil.isNotEmpty(orderBy)) {
where.orderBy(orderBy);
}
List<T> records = mapper.selectListByQuery(where);
List<JSONObject> rows = new ArrayList<>();
for (T record : records) {
rows.add(JSON.parseObject(JSON.toJSONString(record)));
}
Boolean asTree = RequestUtil.getParamAsBoolean(parameters, "asTree");
List<DictItem> items = new ArrayList<>(rows.size());
//有树形结构
if (StringUtils.hasText(parentColumn)) {
List<JSONObject> topLayerRows = findTopLayerRows(rows);
//以树形结构输出
if (asTree != null && asTree) {
makeTree(topLayerRows, items, rows);
}
//以平级结构输出
else {
makeLayer(0, topLayerRows, items, rows);
}
}
//无树形结构数据
else {
for (JSONObject row : rows) {
DictItem dictItem = new DictItem();
dictItem.setValue(row.get(keyColumnCamelCase));
dictItem.setLabel(String.valueOf(row.get(labelColumnCamelCase)));
items.add(dictItem);
}
}
Dict dict = new Dict();
dict.setCode(code);
dict.setItems(items);
return dict;
}
private void makeTree(List<JSONObject> parentRows, List<DictItem> parentItems, List<JSONObject> allRows) {
for (JSONObject parentRow : parentRows) {
DictItem parentItem = row2DictItem(0, parentRow);
parentItems.add(parentItem);
List<JSONObject> children = new ArrayList<>();
for (JSONObject maybeChild : allRows) {
if (Objects.equals(maybeChild.get(parentColumnCamelCase), parentRow.get(keyColumnCamelCase))) {
children.add(maybeChild);
}
}
if (!children.isEmpty()) {
List<DictItem> childrenItems = new ArrayList<>(children.size());
parentItem.setChildren(childrenItems);
makeTree(children, childrenItems, allRows);
}
}
}
private void makeLayer(int layerNo, List<JSONObject> parentRows, List<DictItem> parentItems, List<JSONObject> allRows) {
for (JSONObject parentRow : parentRows) {
parentItems.add(row2DictItem(layerNo, parentRow));
List<JSONObject> children = new ArrayList<>();
for (JSONObject maybeChild : allRows) {
if (Objects.equals(maybeChild.get(parentColumnCamelCase), parentRow.get(keyColumnCamelCase))) {
children.add(maybeChild);
}
}
if (!children.isEmpty()) {
makeLayer(layerNo + 1, children, parentItems, allRows);
}
}
}
private DictItem row2DictItem(int layerNo, JSONObject row) {
DictItem dictItem = new DictItem();
dictItem.setValue(row.get(keyColumnCamelCase));
dictItem.setLabel(Tree.getPrefix(layerNo) + row.get(labelColumnCamelCase));
dictItem.setLayerNo(layerNo);
return dictItem;
}
private List<JSONObject> findTopLayerRows(List<JSONObject> rows) {
List<JSONObject> topLayerRows = new ArrayList<>();
for (JSONObject row : rows) {
boolean foundParent = false;
for (JSONObject row1 : rows) {
if (Objects.equals(row1.get(keyColumnCamelCase), row.get(parentColumnCamelCase))) {
foundParent = true;
break;
}
}
if (!foundParent) {
topLayerRows.add(row);
}
}
return topLayerRows;
}
public String getCode() {
return code;
}
public BaseMapper<T> getMapper() {
return mapper;
}
public String getKeyColumn() {
return keyColumn;
}
public String getLabelColumn() {
return labelColumn;
}
public String getParentColumn() {
return parentColumn;
}
public String getKeyColumnCamelCase() {
return keyColumnCamelCase;
}
public String getLabelColumnCamelCase() {
return labelColumnCamelCase;
}
public String getParentColumnCamelCase() {
return parentColumnCamelCase;
}
public String getOrderBy() {
return orderBy;
}
public boolean isQueryStatus() {
return queryStatus;
}
}

View File

@@ -0,0 +1,87 @@
package tech.easyflow.common.dict.loader;
import tech.easyflow.common.dict.Dict;
import tech.easyflow.common.dict.DictItem;
import tech.easyflow.common.dict.DictLoader;
import com.mybatisflex.core.util.ClassUtil;
import com.mybatisflex.core.util.StringUtil;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class EnumDictLoader<E extends Enum<E>> implements DictLoader {
private final String code;
private final Dict dict;
public EnumDictLoader(String code, Class<E> enumClass, String keyField, String labelField) {
this(null, code, enumClass, keyField, labelField);
}
public EnumDictLoader(String name, String code, Class<E> enumClass, String keyField, String labelField) {
this.code = code;
E[] enums = enumClass.getEnumConstants();
this.dict = new Dict();
this.dict.setName(name);
this.dict.setCode(code);
Field keyProperty = ClassUtil.getFirstField(enumClass, field -> field.getName().equals(keyField));
String keyGetterMethodName = "get" + StringUtil.firstCharToUpperCase(keyField);
Method keyGetter = ClassUtil.getFirstMethod(enumClass, method -> {
String methodName = method.getName();
return methodName.equals(keyGetterMethodName) && Modifier.isPublic(method.getModifiers());
});
Field valueProperty = ClassUtil.getFirstField(enumClass, field -> field.getName().equals(keyField));
String valueGetterMethodName = "get" + StringUtil.firstCharToUpperCase(labelField);
Method valueGetter = ClassUtil.getFirstMethod(enumClass, method -> {
String methodName = method.getName();
return methodName.equals(valueGetterMethodName) && Modifier.isPublic(method.getModifiers());
});
List<DictItem> items = new ArrayList<>(enums.length);
for (E anEnum : enums) {
Object key = getByMethodOrField(anEnum, keyGetter, keyProperty);
Object value = getByMethodOrField(anEnum, valueGetter, valueProperty);
DictItem dictItem = new DictItem();
dictItem.setValue(key);
dictItem.setLabel(String.valueOf(value));
items.add(dictItem);
}
this.dict.setItems(items);
}
private Object getByMethodOrField(E anEnum, Method keyGetter, Field keyProperty) {
if (keyGetter != null) {
try {
return keyGetter.invoke(anEnum);
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
try {
return keyProperty.get(anEnum);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
@Override
public String code() {
return code;
}
@Override
public Dict load(String keyword, Map<String, String[]> parameters) {
return dict;
}
}