add:增加AGV看板
This commit is contained in:
@@ -1,73 +1,130 @@
|
||||
package org.nl.common.page;
|
||||
|
||||
import cn.hutool.core.lang.ParameterizedTypeImpl;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Data;
|
||||
import org.nl.common.util.CustomColumnUtils;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.nl.common.util.MapOf;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 泛型必须为数据tb对应do:由mybatis管理
|
||||
* @author ZZQ
|
||||
* @Date 2022/12/14 6:33 下午
|
||||
*/
|
||||
@Data
|
||||
public class BaseQuery<T> {
|
||||
/**
|
||||
* 模糊查询
|
||||
*/
|
||||
public abstract class BaseQuery<T> {
|
||||
private String blurry;
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
private String is_used;
|
||||
/**
|
||||
* 创建时间范围查询
|
||||
*/
|
||||
private String start_time;
|
||||
private String end_time;
|
||||
|
||||
/**
|
||||
* 字段映射配置
|
||||
*/
|
||||
@JsonIgnore
|
||||
protected Map<String, QParam> fieldMappings = new HashMap<String, QParam>() {{
|
||||
put("blurry", QParam.builder().k(new String[]{"name"}).type(QueryTEnum.LK).build());
|
||||
put("start_time", QParam.builder().k(new String[]{"create_time"}).type(QueryTEnum.LT).build());
|
||||
put("end_time", QParam.builder().k(new String[]{"create_time"}).type(QueryTEnum.LE).build());
|
||||
put("sort", QParam.builder().k(new String[]{"sort"}).type(QueryTEnum.BY).build());
|
||||
}};
|
||||
|
||||
/**
|
||||
* 字段映射Map:指定字段对应QueryWrapper的查询类型
|
||||
* 字段与数据库字段对应,不支持驼峰
|
||||
* @see org.nl.common.page.QueryTEnum
|
||||
* 通过buid构建
|
||||
* 实体类信息(由子类提供)
|
||||
*/
|
||||
public Map<String, QParam> doP = MapOf.of("blurry", QParam.builder().k(new String[]{"name"}).type(QueryTEnum.LK).build()
|
||||
,"startTime", QParam.builder().k(new String[]{"create_time"}).type(QueryTEnum.LT).build()
|
||||
,"endTime", QParam.builder().k(new String[]{"create_time"}).type(QueryTEnum.LE).build()
|
||||
,"sort", QParam.builder().k(new String[]{"sort"}).type(QueryTEnum.BY).build()
|
||||
);
|
||||
@JsonIgnore
|
||||
protected abstract Class<T> getEntityClass();
|
||||
|
||||
public QueryWrapper<T> build(){
|
||||
/**
|
||||
* 列名映射(子类可以重写此方法提供自定义映射)
|
||||
*/
|
||||
@JsonIgnore
|
||||
protected Map<String, String> getColumnMappings() {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
public QueryWrapper<T> build() {
|
||||
this.paramMapping();
|
||||
QueryWrapper<T> wrapper = new QueryWrapper<>();
|
||||
JSONObject json = (JSONObject)JSONObject.toJSON(this);
|
||||
Type[] types = ((ParameterizedTypeImpl) this.getClass().getGenericSuperclass()).getActualTypeArguments();
|
||||
Map<String, ColumnCache> columnMap = LambdaUtils.getColumnMap((Class<?>) types[0]);
|
||||
|
||||
String dopStr = "doP";
|
||||
json.forEach((key, vel) -> {
|
||||
if (vel != null && !key.equals(dopStr)){
|
||||
QParam qParam = doP.get(key);
|
||||
if (qParam != null){
|
||||
QueryTEnum.build(qParam.type,wrapper,qParam.k,vel);
|
||||
}else {
|
||||
ColumnCache columnCache = columnMap.get(LambdaUtils.formatKey(key));
|
||||
if (columnCache!=null){
|
||||
wrapper.eq(columnCache.getColumn(),vel);
|
||||
Class<T> entityClass = getEntityClass();
|
||||
Map<String, String> columnMappings = getColumnMappings();
|
||||
|
||||
// 获取MyBatis-Plus的列缓存
|
||||
Map<String, ColumnCache> columnCacheMap = CustomColumnUtils.getColumnMap(entityClass);
|
||||
|
||||
// 获取所有字段
|
||||
Field[] fields = this.getClass().getDeclaredFields();
|
||||
|
||||
for (Field field : fields) {
|
||||
field.setAccessible(true);
|
||||
|
||||
try {
|
||||
Object value = field.get(this);
|
||||
String fieldName = field.getName();
|
||||
|
||||
if (value == null || fieldName.equals("fieldMappings")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查是否有自定义映射
|
||||
QParam qParam = fieldMappings.get(fieldName);
|
||||
if (qParam != null) {
|
||||
QueryTEnum.build(qParam.type, wrapper, qParam.k, value);
|
||||
} else {
|
||||
// 查找对应的数据库列名
|
||||
String columnName = findColumnName(fieldName, columnMappings, columnCacheMap);
|
||||
if (columnName != null) {
|
||||
wrapper.eq(columnName, value);
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
// 忽略无法访问的字段
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
public void paramMapping(){};
|
||||
/**
|
||||
* 查找字段对应的数据库列名
|
||||
*/
|
||||
private String findColumnName(String fieldName,
|
||||
Map<String, String> customMappings,
|
||||
Map<String, ColumnCache> columnCache) {
|
||||
// 1. 首先检查自定义映射
|
||||
if (customMappings.containsKey(fieldName)) {
|
||||
return customMappings.get(fieldName);
|
||||
}
|
||||
|
||||
// 2. 检查列缓存(支持驼峰转下划线)
|
||||
ColumnCache cache = columnCache.get(fieldName);
|
||||
if (cache == null) {
|
||||
// 尝试蛇形命名
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (int i = 0; i < fieldName.length(); i++) {
|
||||
char c = fieldName.charAt(i);
|
||||
if (Character.isUpperCase(c)) {
|
||||
result.append('_').append(Character.toLowerCase(c));
|
||||
} else {
|
||||
result.append(c);
|
||||
}
|
||||
}
|
||||
cache = columnCache.get(result.toString());
|
||||
}
|
||||
return cache != null ? cache.getColumn() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@Override
|
||||
public void paramMapping() {
|
||||
super.doP.put("pid_is_null", QParam.builder().k(new String[]{"pid"}).type(QueryTEnum.NO).build());
|
||||
super.doP.put("deptIds", QParam.builder().k(new String[]{"dept_id"}).type(QueryTEnum.IN).build());
|
||||
}
|
||||
*/
|
||||
public void paramMapping() {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
package org.nl.common.util;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 自定义列映射工具类,避免使用 MyBatis-Plus 的内部 API
|
||||
*/
|
||||
public class CustomColumnUtils {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CustomColumnUtils.class);
|
||||
|
||||
// 缓存实体类的列映射
|
||||
private static final Map<Class<?>, Map<String, ColumnCache>> COLUMN_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 获取实体类的列映射(替代 LambdaUtils.getColumnMap)
|
||||
*/
|
||||
public static Map<String, ColumnCache> getColumnMap(Class<?> clazz) {
|
||||
return COLUMN_CACHE.computeIfAbsent(clazz, CustomColumnUtils::buildColumnMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建实体类的列映射
|
||||
*/
|
||||
private static Map<String, ColumnCache> buildColumnMap(Class<?> clazz) {
|
||||
Map<String, ColumnCache> columnMap = new HashMap<>();
|
||||
|
||||
try {
|
||||
// 获取所有字段(包括父类)
|
||||
Field[] fields = getAllFields(clazz);
|
||||
|
||||
for (Field field : fields) {
|
||||
String fieldName = field.getName();
|
||||
String columnName = getColumnName(field);
|
||||
|
||||
if (columnName != null) {
|
||||
// 创建 ColumnCache 对象
|
||||
ColumnCache columnCache = new ColumnCache(columnName, fieldName);
|
||||
|
||||
// 添加多种格式的键
|
||||
// 1. 原始字段名
|
||||
columnMap.put(fieldName, columnCache);
|
||||
|
||||
// 2. 驼峰格式(如果需要)
|
||||
String camelCase = toCamelCase(columnName);
|
||||
if (!columnMap.containsKey(camelCase)) {
|
||||
columnMap.put(camelCase, columnCache);
|
||||
}
|
||||
|
||||
// 3. 首字母小写的字段名
|
||||
String firstLower = Character.toLowerCase(fieldName.charAt(0)) + fieldName.substring(1);
|
||||
if (!firstLower.equals(fieldName) && !columnMap.containsKey(firstLower)) {
|
||||
columnMap.put(firstLower, columnCache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Built column map for class: {}, size: {}", clazz.getName(), columnMap.size());
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to build column map for class: {}", clazz.getName(), e);
|
||||
throw new RuntimeException("Failed to build column map", e);
|
||||
}
|
||||
|
||||
return columnMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有字段(包括父类)
|
||||
*/
|
||||
private static Field[] getAllFields(Class<?> clazz) {
|
||||
Map<String, Field> fieldMap = new HashMap<>();
|
||||
Class<?> currentClass = clazz;
|
||||
|
||||
while (currentClass != null && currentClass != Object.class) {
|
||||
Field[] declaredFields = currentClass.getDeclaredFields();
|
||||
for (Field field : declaredFields) {
|
||||
// 避免覆盖子类字段
|
||||
if (!fieldMap.containsKey(field.getName())) {
|
||||
fieldMap.put(field.getName(), field);
|
||||
}
|
||||
}
|
||||
currentClass = currentClass.getSuperclass();
|
||||
}
|
||||
|
||||
return fieldMap.values().toArray(new Field[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段对应的数据库列名
|
||||
*/
|
||||
private static String getColumnName(Field field) {
|
||||
// 1. 检查 @TableField 注解
|
||||
TableField tableField = field.getAnnotation(TableField.class);
|
||||
if (tableField != null) {
|
||||
// 如果字段不存在于数据库,跳过
|
||||
if (!tableField.exist()) {
|
||||
return null;
|
||||
}
|
||||
// 如果有指定的列名,使用它
|
||||
if (!tableField.value().isEmpty()) {
|
||||
return tableField.value();
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 检查 @TableId 注解
|
||||
TableId tableId = field.getAnnotation(TableId.class);
|
||||
if (tableId != null && !tableId.value().isEmpty()) {
|
||||
return tableId.value();
|
||||
}
|
||||
// 3. 默认:驼峰转下划线
|
||||
return camelToUnderscore(field.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰转下划线
|
||||
*/
|
||||
private static String camelToUnderscore(String camelCase) {
|
||||
if (camelCase == null || camelCase.isEmpty()) {
|
||||
return camelCase;
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append(Character.toLowerCase(camelCase.charAt(0)));
|
||||
|
||||
for (int i = 1; i < camelCase.length(); i++) {
|
||||
char c = camelCase.charAt(i);
|
||||
if (Character.isUpperCase(c)) {
|
||||
result.append('_').append(Character.toLowerCase(c));
|
||||
} else {
|
||||
result.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 下划线转驼峰
|
||||
*/
|
||||
private static String toCamelCase(String underscore) {
|
||||
if (underscore == null || underscore.isEmpty()) {
|
||||
return underscore;
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
boolean nextUpper = false;
|
||||
|
||||
for (int i = 0; i < underscore.length(); i++) {
|
||||
char c = underscore.charAt(i);
|
||||
if (c == '_') {
|
||||
nextUpper = true;
|
||||
} else {
|
||||
if (nextUpper) {
|
||||
result.append(Character.toUpperCase(c));
|
||||
nextUpper = false;
|
||||
} else {
|
||||
result.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user