diff --git a/build.gradle b/build.gradle index 96c3e3b..ee64e85 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ buildscript { ext { - springBootVersion = '2.1.2.RELEASE' + springBootVersion = '2.1.3.RELEASE' } repositories { maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'} @@ -9,30 +9,30 @@ buildscript { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } - +// 全局配置 allprojects { group 'com.diboot' version '2.0-alpha' - apply plugin: 'idea' } - -repositories { - maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'} -} - +// 子项目的配置 subprojects { apply plugin: 'java' + + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + + [compileJava,compileTestJava,javadoc]*.options*.encoding = 'UTF-8' repositories { - jcenter() + maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'} } ext {//依赖版本 - springBootVersion = "2.1.2.RELEASE" - mysqlConnectorVersion = "8.0.13" - mybatisStarterVersion = "1.3.2" - mybatisPlusVersion = "3.0.7.1" - fastjsonVersion = "1.2.54" - lombokVersion = "1.18.4" + springBootVersion = "2.1.4.RELEASE" + mysqlConnectorVersion = "8.0.15" + mybatisStarterVersion = "2.0.1" + mybatisPlusVersion = "3.1.0" + fastjsonVersion = "1.2.57" + lombokVersion = "1.18.6" } dependencies { compileOnly("org.projectlombok:lombok:$lombokVersion") @@ -50,7 +50,7 @@ subprojects { compile("com.alibaba:fastjson:$fastjsonVersion") // Apache Commons compile("org.apache.commons:commons-lang3:3.8.1") - + // 测试实现 testCompile("org.springframework.boot:spring-boot-starter-test:$springBootVersion") testCompile("junit:junit:4.12") diff --git a/diboot-core/build.gradle b/diboot-core/build.gradle index f10238f..42b86de 100644 --- a/diboot-core/build.gradle +++ b/diboot-core/build.gradle @@ -1,13 +1,3 @@ -plugins { - id 'java' -} - -sourceCompatibility = 1.8 - -repositories { - maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'} -} - dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' } diff --git a/diboot-core/src/main/java/com/diboot/core/binding/BaseBinder.java b/diboot-core/src/main/java/com/diboot/core/binding/BaseBinder.java new file mode 100644 index 0000000..f117190 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/BaseBinder.java @@ -0,0 +1,144 @@ +package com.diboot.core.binding; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.diboot.core.binding.parser.MiddleTable; +import com.diboot.core.service.BaseService; +import com.diboot.core.util.BeanUtils; +import com.diboot.core.util.IGetter; +import com.diboot.core.util.S; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.List; + +/** + * 关系绑定Binder父类 + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/19 + */ +public abstract class BaseBinder { + private static final Logger log = LoggerFactory.getLogger(BaseBinder.class); + /*** + * 需要绑定到的VO注解对象List + */ + protected List annoObjectList; + /*** + * VO注解对象中的外键属性 + */ + protected String annoObjectForeignKey; + /** + * 被关联对象的Service实例 + */ + protected BaseService referencedService; + /*** + * DO对象中的主键属性名 + */ + protected String referencedEntityPrimaryKey; + /** + * 初始化QueryWrapper + */ + protected QueryWrapper queryWrapper; + + /** + * 多对多关联的桥接表,如 user_role
+ * 多对多注解示例: id=user_role.user_id AND user_role.role_id=id + */ + protected MiddleTable middleTable; + + /** + * join连接条件,指定当前VO的取值方法和关联entity的取值方法 + * @param annoObjectFkGetter 当前VO的取值方法 + * @param referencedEntityPkGetter 关联entity的取值方法 + * @param 当前VO的对象类型 + * @param 关联对象entity类型 + * @return + */ + public BaseBinder joinOn(IGetter annoObjectFkGetter, IGetter referencedEntityPkGetter){ + return joinOn(BeanUtils.convertToFieldName(annoObjectFkGetter), BeanUtils.convertToFieldName(referencedEntityPkGetter)); + } + + /** + * join连接条件,指定当前VO的取值方法和关联entity的取值方法 + * @param annoObjectForeignKey 当前VO的取值属性名 + * @param referencedEntityPrimaryKey 关联entity的属性 + * @return + */ + public BaseBinder joinOn(String annoObjectForeignKey, String referencedEntityPrimaryKey){ + this.annoObjectForeignKey = S.toLowerCaseCamel(annoObjectForeignKey); + this.referencedEntityPrimaryKey = S.toLowerCaseCamel(referencedEntityPrimaryKey); + return this; + } + + public BaseBinder andEQ(String fieldName, Object value){ + queryWrapper.eq(S.toSnakeCase(fieldName), value); + return this; + } + public BaseBinder andNE(String fieldName, Object value){ + queryWrapper.ne(S.toSnakeCase(fieldName), value); + return this; + } + public BaseBinder andGT(String fieldName, Object value){ + queryWrapper.gt(S.toSnakeCase(fieldName), value); + return this; + } + public BaseBinder andGE(String fieldName, Object value){ + queryWrapper.ge(S.toSnakeCase(fieldName), value); + return this; + } + public BaseBinder andLT(String fieldName, Object value){ + queryWrapper.lt(S.toSnakeCase(fieldName), value); + return this; + } + public BaseBinder andLE(String fieldName, Object value){ + queryWrapper.le(S.toSnakeCase(fieldName), value); + return this; + } + public BaseBinder andIsNotNull(String fieldName){ + queryWrapper.isNotNull(S.toSnakeCase(fieldName)); + return this; + } + public BaseBinder andIsNull(String fieldName){ + queryWrapper.isNull(S.toSnakeCase(fieldName)); + return this; + } + public BaseBinder andBetween(String fieldName, Object begin, Object end){ + queryWrapper.between(S.toSnakeCase(fieldName), begin, end); + return this; + } + public BaseBinder andLike(String fieldName, String value){ + queryWrapper.like(S.toSnakeCase(fieldName), value); + return this; + } + public BaseBinder andIn(String fieldName, Collection valueList){ + queryWrapper.in(S.toSnakeCase(fieldName), valueList); + return this; + } + public BaseBinder andNotIn(String fieldName, Collection valueList){ + queryWrapper.notIn(S.toSnakeCase(fieldName), valueList); + return this; + } + public BaseBinder andNotBetween(String fieldName, Object begin, Object end){ + queryWrapper.notBetween(S.toSnakeCase(fieldName), begin, end); + return this; + } + public BaseBinder andNotLike(String fieldName, String value){ + queryWrapper.notLike(S.toSnakeCase(fieldName), value); + return this; + } + public BaseBinder andApply(String applySql){ + queryWrapper.apply(applySql); + return this; + } + public BaseBinder withMiddleTable(MiddleTable middleTable){ + this.middleTable = middleTable; + return this; + } + + /*** + * 执行绑定, 交由子类实现 + */ + public abstract void bind(); + +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/binding/EntityBinder.java b/diboot-core/src/main/java/com/diboot/core/binding/EntityBinder.java new file mode 100644 index 0000000..4badb54 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/EntityBinder.java @@ -0,0 +1,99 @@ +package com.diboot.core.binding; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.diboot.core.service.BaseService; +import com.diboot.core.util.BeanUtils; +import com.diboot.core.util.ISetter; +import com.diboot.core.util.S; +import com.diboot.core.util.V; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Entity实体绑定Binder,用于绑定当前一个entity到目标对象的属性 + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/19 + */ +public class EntityBinder extends BaseBinder { + private static final Logger log = LoggerFactory.getLogger(EntityBinder.class); + + /*** + * 给待绑定list中VO对象赋值的setter属性名 + */ + protected String annoObjectField; + + public EntityBinder(){} + /*** + * 构造方法 + * @param referencedService + * @param voList + */ + public EntityBinder(BaseService referencedService, List voList){ + this.referencedService = referencedService; + this.annoObjectList = voList; + this.queryWrapper = new QueryWrapper(); + } + + /*** + * 指定VO绑定属性赋值的setter方法 + * @param voSetter VO中调用赋值的setter方法 + * @param VO类型 + * @param set方法参数类型 + * @return + */ + public BaseBinder set(ISetter voSetter){ + return set(BeanUtils.convertToFieldName(voSetter)); + } + + /*** + * 指定VO绑定属性赋值的set属性 + * @param annoObjectField VO中调用赋值的setter属性 + * @return + */ + public BaseBinder set(String annoObjectField){ + this.annoObjectField = annoObjectField; + return this; + } + + @Override + public void bind() { + if(V.isEmpty(annoObjectList)){ + return; + } + if(referencedEntityPrimaryKey == null){ + log.warn("调用错误:必须调用joinOn()方法连接两个字段."); + } + // 提取主键pk列表 + List pkList = BeanUtils.collectToList(annoObjectList, S.toLowerCaseCamel(annoObjectForeignKey)); + if(V.isEmpty(pkList)){ + return; + } + // 构建查询条件 + queryWrapper.in(S.toSnakeCase(referencedEntityPrimaryKey), pkList); + // 查询entity列表 + List list = referencedService.getEntityList(queryWrapper); + // 绑定结果 + bindingResult(S.toLowerCaseCamel(referencedEntityPrimaryKey), list); + } + + /*** + * 绑定结果 + * @param doPkPropName + * @param list + */ + protected void bindingResult(String doPkPropName, List list) { + Map valueEntityMap = new HashMap<>(list.size()); + for(T entity : list){ + Object pkValue = BeanUtils.getProperty(entity, doPkPropName); + valueEntityMap.put(pkValue, entity); + } + // 绑定 + BeanUtils.bindPropValueOfList(annoObjectField, annoObjectList, annoObjectForeignKey, valueEntityMap); + } + +} diff --git a/diboot-core/src/main/java/com/diboot/core/binding/EntityListBinder.java b/diboot-core/src/main/java/com/diboot/core/binding/EntityListBinder.java new file mode 100644 index 0000000..dea649b --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/EntityListBinder.java @@ -0,0 +1,49 @@ +package com.diboot.core.binding; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.diboot.core.service.BaseService; +import com.diboot.core.util.BeanUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/19 + */ +public class EntityListBinder extends EntityBinder { + private static final Logger log = LoggerFactory.getLogger(EntityListBinder.class); + + /*** + * 构造方法 + * @param serviceInstance + * @param voList + */ + public EntityListBinder(BaseService serviceInstance, List voList){ + this.referencedService = serviceInstance; + this.annoObjectList = voList; + this.queryWrapper = new QueryWrapper(); + } + + @Override + protected void bindingResult(String fkName, List list) { + Map> valueEntityListMap = new HashMap<>(list.size()); + for(T entity : list){ + Object keyValue = BeanUtils.getProperty(entity, fkName); + List entityList = valueEntityListMap.get(keyValue); + if(entityList == null){ + entityList = new ArrayList<>(); + valueEntityListMap.put(keyValue, entityList); + } + entityList.add(entity); + } + // 绑定 + BeanUtils.bindPropValueOfList(annoObjectField, annoObjectList, annoObjectForeignKey, valueEntityListMap); + } + +} diff --git a/diboot-core/src/main/java/com/diboot/core/binding/FieldBinder.java b/diboot-core/src/main/java/com/diboot/core/binding/FieldBinder.java new file mode 100644 index 0000000..33d78ae --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/FieldBinder.java @@ -0,0 +1,130 @@ +package com.diboot.core.binding; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.diboot.core.service.BaseService; +import com.diboot.core.util.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 关联字段绑定 + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/19 + */ +public class FieldBinder extends BaseBinder { + private static final Logger log = LoggerFactory.getLogger(FieldBinder.class); + + /** + * VO对象绑定赋值的属性名列表 + */ + private List annoObjectSetterPropNameList; + /** + * DO/Entity对象对应的getter取值属性名列表 + */ + private List referencedGetterColumnNameList; + + /*** + * 构造方法 + * @param serviceInstance + * @param voList + */ + public FieldBinder(BaseService serviceInstance, List voList){ + this.referencedService = serviceInstance; + this.annoObjectList = voList; + this.queryWrapper = new QueryWrapper(); + } + + /*** + * 指定VO绑定属性赋值的setter和DO/Entity取值的getter方法 + * @param toVoSetter VO中调用赋值的setter方法 + * @param VO类型 + * @param DO类型 + * @param set方法参数类型 + * @return + */ + public FieldBinder link(IGetter fromDoGetter, ISetter toVoSetter){ + return link(BeanUtils.convertToFieldName(fromDoGetter), BeanUtils.convertToFieldName(toVoSetter)); + } + + /*** + * 指定VO绑定赋值的setter属性名和DO/Entity取值的getter属性名 + * @param toVoField VO中调用赋值的setter属性名 + * @return + */ + public FieldBinder link(String fromDoField, String toVoField){ + if(annoObjectSetterPropNameList == null){ + annoObjectSetterPropNameList = new ArrayList<>(); + } + annoObjectSetterPropNameList.add(toVoField); + if(referencedGetterColumnNameList == null){ + referencedGetterColumnNameList = new ArrayList<>(); + } + referencedGetterColumnNameList.add(S.toSnakeCase(fromDoField)); + return this; + } + + @Override + public void bind() { + if(V.isEmpty(annoObjectList)){ + return; + } + if(referencedGetterColumnNameList == null){ + log.error("调用错误:字段绑定必须调用link()指定字段赋值和取值的setter/getter方法!"); + return; + } + // 解析默认主键字段名 + String referencedEntityPkName = S.toSnakeCase(referencedEntityPrimaryKey); + String annoObjectFkFieldName = S.toLowerCaseCamel(annoObjectForeignKey); + // 提取主键pk列表 + List pkList = BeanUtils.collectToList(annoObjectList, annoObjectFkFieldName); + // 构建查询条件 + List selectColumns = new ArrayList<>(referencedGetterColumnNameList.size()+1); + selectColumns.add(referencedEntityPkName); + selectColumns.addAll(referencedGetterColumnNameList); + queryWrapper.select(S.toStringArray(selectColumns)).in(referencedEntityPkName, pkList); + // 获取匹配结果的mapList + List> mapList = referencedService.getMapList(queryWrapper); + if(V.isEmpty(mapList)){ + return; + } + // 将结果list转换成map + Map> referencedEntityPk2DataMap = new HashMap<>(mapList.size()); + for(Map map : mapList){ + if(map.get(referencedEntityPkName) != null){ + Object pkVal = map.get(referencedEntityPkName); + // 将数字类型转换成字符串,以便解决类型不一致的问题 + Object formatPkVal = getFormatKey(pkVal); + referencedEntityPk2DataMap.put(formatPkVal, map); + } + } + // 遍历list并赋值 + for(Object annoObject : annoObjectList){ + Object annoObjectId = BeanUtils.getProperty(annoObject, annoObjectFkFieldName); + // 将数字类型转换成字符串,以便解决类型不一致的问题 + Object formatAnnoObjectId = getFormatKey(annoObjectId); + Map relationMap = referencedEntityPk2DataMap.get(formatAnnoObjectId); + if(relationMap != null){ + for(int i = 0; i< annoObjectSetterPropNameList.size(); i++){ + BeanUtils.setProperty(annoObject, annoObjectSetterPropNameList.get(i), relationMap.get(referencedGetterColumnNameList.get(i))); + } + } + } + } + + /** + * 获取统一定义的key(避免Mybatis自动转换BigInteger和Long的差异问题) + */ + private Object getFormatKey(Object annoObjectId){ + if(annoObjectId instanceof BigInteger || annoObjectId instanceof Long || annoObjectId instanceof Integer) { + return String.valueOf(annoObjectId); + } + return annoObjectId; + } +} diff --git a/diboot-core/src/main/java/com/diboot/core/binding/annotation/BindEntity.java b/diboot-core/src/main/java/com/diboot/core/binding/annotation/BindEntity.java new file mode 100644 index 0000000..f6d12a6 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/annotation/BindEntity.java @@ -0,0 +1,27 @@ +package com.diboot.core.binding.annotation; + +import java.lang.annotation.*; + +/** + * 绑定Entity 注解定义 + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/21 + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface BindEntity { + /*** + * 对应的service类 + * @return + */ + Class entity(); + + /*** + * JOIN连接条件 + * @return + */ + String condition(); +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/binding/annotation/BindEntityList.java b/diboot-core/src/main/java/com/diboot/core/binding/annotation/BindEntityList.java new file mode 100644 index 0000000..3d0febb --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/annotation/BindEntityList.java @@ -0,0 +1,26 @@ +package com.diboot.core.binding.annotation; + +import java.lang.annotation.*; + +/** + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/21 + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface BindEntityList { + /*** + * 对应的entity类 + * @return + */ + Class entity(); + + /*** + * JOIN连接条件 + * @return + */ + String condition(); +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/binding/annotation/BindField.java b/diboot-core/src/main/java/com/diboot/core/binding/annotation/BindField.java new file mode 100644 index 0000000..4b13ca3 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/annotation/BindField.java @@ -0,0 +1,32 @@ +package com.diboot.core.binding.annotation; + +import java.lang.annotation.*; + +/** + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/21 + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface BindField { + /*** + * 绑定的Entity类 + * @return + */ + Class entity(); + + /*** + * 绑定字段 + * @return + */ + String field(); + + /*** + * JOIN连接条件 + * @return + */ + String condition(); +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/binding/annotation/BindMetadata.java b/diboot-core/src/main/java/com/diboot/core/binding/annotation/BindMetadata.java new file mode 100644 index 0000000..3ab995a --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/annotation/BindMetadata.java @@ -0,0 +1,27 @@ +package com.diboot.core.binding.annotation; + +import java.lang.annotation.*; + +/** + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/21 + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface BindMetadata { + + /*** + * 绑定元数据类型 + * @return + */ + String type(); + + /*** + * 元数据项取值字段 + * @return + */ + String field(); +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/binding/manager/AnnotationBindingManager.java b/diboot-core/src/main/java/com/diboot/core/binding/manager/AnnotationBindingManager.java new file mode 100644 index 0000000..a697728 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/manager/AnnotationBindingManager.java @@ -0,0 +1,209 @@ +package com.diboot.core.binding.manager; + +import com.diboot.core.binding.BaseBinder; +import com.diboot.core.binding.FieldBinder; +import com.diboot.core.binding.annotation.BindEntity; +import com.diboot.core.binding.annotation.BindEntityList; +import com.diboot.core.binding.annotation.BindField; +import com.diboot.core.binding.annotation.BindMetadata; +import com.diboot.core.binding.parser.BindAnnotationGroup; +import com.diboot.core.binding.parser.ConditionManager; +import com.diboot.core.binding.parser.FieldAnnotation; +import com.diboot.core.entity.Metadata; +import com.diboot.core.service.BaseService; +import com.diboot.core.service.MetadataService; +import com.diboot.core.util.ContextHelper; +import com.diboot.core.util.V; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.List; + +/** + * 绑定管理器 + * @author Mazhicheng + * @version v2.0 + * @date 2019/3/30 + */ +public class AnnotationBindingManager { + private static final Logger log = LoggerFactory.getLogger(AnnotationBindingManager.class); + + /** + * 自动绑定关联对象 + * @return + * @throws Exception + */ + public static void autoBind(List voList){ + if(V.isEmpty(voList)){ + return; + } + // 获取VO类 + Class voClass = voList.get(0).getClass(); + BindAnnotationGroup bindAnnotationGroup = BindAnnotationCacheManager.getBindAnnotationGroup(voClass); + if(bindAnnotationGroup.isNotEmpty()){ + // 绑定元数据 + List metadataAnnoList = bindAnnotationGroup.getBindMetadataAnnotations(); + if(metadataAnnoList != null){ + for(FieldAnnotation annotation : metadataAnnoList){ + doBindingMetadata(voList, annotation); + } + } + // 绑定Field字段名 + List fieldAnnoList = bindAnnotationGroup.getBindFieldAnnotations(); + if(fieldAnnoList != null){ + doBindingField(voList, fieldAnnoList); + } + // 绑定Entity实体 + List entityAnnoList = bindAnnotationGroup.getBindEntityAnnotations(); + if(entityAnnoList != null){ + for(FieldAnnotation anno : entityAnnoList){ + doBindingEntity(voList, anno); + } + } + // 绑定Entity实体List + List entitiesAnnoList = bindAnnotationGroup.getBindEntityListAnnotations(); + if(entitiesAnnoList != null){ + for(FieldAnnotation anno : entitiesAnnoList){ + doBindingEntityList(voList, anno); + } + } + } + } + + /*** + * 绑定元数据 + * @param voList + * @param fieldAnno + * @param + */ + private static void doBindingMetadata(List voList, FieldAnnotation fieldAnno) { + MetadataService metadataService = (MetadataService) ContextHelper.getBean(MetadataService.class); + if(metadataService != null){ + BindMetadata annotation = (BindMetadata) fieldAnno.getAnnotation(); + metadataService.bindItemLabel(voList, fieldAnno.getFieldName(), annotation.field(), annotation.type()); + } + } + + /*** + * 绑定字段 + * @param voList + * @param fieldAnnoList + * @param + */ + private static void doBindingField(List voList, List fieldAnnoList) { + //多个字段,合并查询,以减少SQL数 + Map> clazzToListMap = new HashMap<>(); + for(FieldAnnotation anno : fieldAnnoList){ + BindField bindField = (BindField) anno.getAnnotation(); + String key = bindField.entity().getName() + ":" + bindField.condition(); + List list = clazzToListMap.computeIfAbsent(key, k -> new ArrayList<>()); + list.add(anno); + } + // 解析条件并且执行绑定 + for(Map.Entry> entry : clazzToListMap.entrySet()){ + List list = entry.getValue(); + BindField bindAnnotation = (BindField) list.get(0).getAnnotation(); + BaseService service = getService(bindAnnotation); + FieldBinder binder = service.bindingFieldTo(voList); + for(FieldAnnotation anno : list){ + BindField bindField = (BindField) anno.getAnnotation(); + binder.link(bindField.field(), anno.getFieldName()); + } + parseConditionsAndBinding(binder, bindAnnotation.condition()); + } + } + + /*** + * 绑定Entity + * @param voList + * @param fieldAnnotation + * @param + */ + private static void doBindingEntity(List voList, FieldAnnotation fieldAnnotation) { + BindEntity annotation = (BindEntity) fieldAnnotation.getAnnotation(); + // 绑定关联对象entity + BaseService service = getService(annotation); + if(service != null){ + // 字段名 + String voFieldName = fieldAnnotation.getFieldName(); + // 构建binder + BaseBinder binder = service.bindingEntityTo(voList).set(voFieldName); + // 解析条件并且执行绑定 + parseConditionsAndBinding(binder, annotation.condition()); + } + } + + /*** + * 绑定Entity + * @param voList + * @param fieldAnnotation + * @param + */ + private static void doBindingEntityList(List voList, FieldAnnotation fieldAnnotation) { + BindEntityList bindAnnotation = (BindEntityList) fieldAnnotation.getAnnotation(); + // 绑定关联对象entity + BaseService service = getService(bindAnnotation); + if(service != null){ + // 字段名 + String voFieldName = fieldAnnotation.getFieldName(); + // 构建binder + BaseBinder binder = service.bindingEntityListTo(voList).set(voFieldName); + // 解析条件并且执行绑定 + parseConditionsAndBinding(binder, bindAnnotation.condition()); + } + } + + /*** + * 解析条件并且执行绑定 + * @param condition + * @param binder + */ + private static void parseConditionsAndBinding(BaseBinder binder, String condition){ + try{ + ConditionManager.parseConditions(condition, binder); + binder.bind(); + } + catch (Exception e){ + log.error("解析注解条件与绑定执行异常", e); + } + } + + /** + * 通过Entity获取对应的Service实现类 + * @param annotation + * @return + */ + private static BaseService getService(Annotation annotation){ + Class entityClass = null; + if(annotation instanceof BindMetadata){ + entityClass = Metadata.class; + } + else if(annotation instanceof BindField){ + BindField bindAnnotation = (BindField)annotation; + entityClass = bindAnnotation.entity(); + } + else if(annotation instanceof BindEntity){ + BindEntity bindAnnotation = (BindEntity)annotation; + entityClass = bindAnnotation.entity(); + } + else if(annotation instanceof BindEntityList){ + BindEntityList bindAnnotation = (BindEntityList)annotation; + entityClass = bindAnnotation.entity(); + } + else{ + log.warn("非预期的注解: "+ annotation.getClass().getSimpleName()); + return null; + } + // 根据entity获取Service + BaseService service = ContextHelper.getServiceByEntity(entityClass); + if(service == null){ + log.error("未能识别到Entity: "+entityClass.getName()+" 的Service实现!"); + } + return service; + } + +} diff --git a/diboot-core/src/main/java/com/diboot/core/binding/manager/BindAnnotationCacheManager.java b/diboot-core/src/main/java/com/diboot/core/binding/manager/BindAnnotationCacheManager.java new file mode 100644 index 0000000..8adff95 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/manager/BindAnnotationCacheManager.java @@ -0,0 +1,53 @@ +package com.diboot.core.binding.manager; + +import com.diboot.core.binding.parser.BindAnnotationGroup; +import com.diboot.core.util.V; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * VO对象中的绑定注解 缓存管理类 + * @author Mazhicheng
+ * @version 1.0
+ * @date 2019/04/03
+ */ +public class BindAnnotationCacheManager { + /** + * VO类-绑定注解缓存 + */ + private static Map allVoBindAnnotationCacheMap = new ConcurrentHashMap<>(); + + /** + * 获取指定class对应的Bind相关注解 + * @param voClass + * @return + */ + public static BindAnnotationGroup getBindAnnotationGroup(Class voClass){ + BindAnnotationGroup group = allVoBindAnnotationCacheMap.get(voClass); + if(group == null){ + // 获取注解并缓存 + group = new BindAnnotationGroup(); + // 获取当前VO的注解 + Field[] fields = voClass.getDeclaredFields(); + if(fields != null){ + for (Field field : fields) { + //遍历属性 + Annotation[] annotations = field.getDeclaredAnnotations(); + if (V.isEmpty(annotations)) { + continue; + } + for (Annotation annotation : annotations) { + group.addBindAnnotation(field.getName(), annotation); + } + } + } + allVoBindAnnotationCacheMap.put(voClass, group); + } + // 返回归类后的注解对象 + return group; + } + +} diff --git a/diboot-core/src/main/java/com/diboot/core/binding/manager/EntityToTableCacheManager.java b/diboot-core/src/main/java/com/diboot/core/binding/manager/EntityToTableCacheManager.java new file mode 100644 index 0000000..a741479 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/manager/EntityToTableCacheManager.java @@ -0,0 +1,49 @@ +package com.diboot.core.binding.manager; + +/** + * 实体类与表名之间的关联关系 + * @author Mazhicheng
+ * @version 1.0
+ * @date 2019/04/03
+ */ + +import com.baomidou.mybatisplus.annotation.TableName; +import com.diboot.core.util.S; +import com.diboot.core.util.V; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 实体类与表名之间的关联关系 + * @author Mazhicheng
+ * @version 1.0
+ * @date 2019/04/03
+ */ +public class EntityToTableCacheManager { + /** + * entity类-table表名的映射关系 + */ + private static Map CLASSNAME_TABLENAME_MAP = new ConcurrentHashMap<>(); + + /** + * 获取表名 + * @param entityClass + * @return + */ + public static String getTableName(Class entityClass){ + String tableName = CLASSNAME_TABLENAME_MAP.get(entityClass.getName()); + if(V.isEmpty(tableName)){ + // 获取当前VO的注解 + TableName annotation = (TableName) entityClass.getAnnotation(TableName.class); + if(annotation != null){ + tableName = annotation.value(); + } + else{ + tableName = S.toSnakeCase(entityClass.getSimpleName()); + } + CLASSNAME_TABLENAME_MAP.put(entityClass.getName(), tableName); + } + return tableName; + } +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/binding/parser/BindAnnotationGroup.java b/diboot-core/src/main/java/com/diboot/core/binding/parser/BindAnnotationGroup.java new file mode 100644 index 0000000..b14dfb3 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/parser/BindAnnotationGroup.java @@ -0,0 +1,87 @@ +package com.diboot.core.binding.parser; + +import com.diboot.core.binding.annotation.BindEntity; +import com.diboot.core.binding.annotation.BindEntityList; +import com.diboot.core.binding.annotation.BindField; +import com.diboot.core.binding.annotation.BindMetadata; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.List; + +/** + * VO绑定注解的归类分组,用于缓存解析后的结果 + * @author Mazhicheng
+ * @version 1.0
+ * @date 2019/04/03
+ */ +public class BindAnnotationGroup { + /** + * Metadata注解 + */ + private List bindMetadataAnnotations; + /** + * 字段关联注解 + */ + private List bindFieldAnnotations; + /** + * 实体关联注解 + */ + private List bindEntityAnnotations; + /** + * 实体集合关联注解 + */ + private List bindEntityListAnnotations; + + /** + * 添加注解 + * @param fieldName + * @param annotation + */ + public void addBindAnnotation(String fieldName, Annotation annotation){ + if(annotation instanceof BindMetadata){ + if(bindMetadataAnnotations == null){ + bindMetadataAnnotations = new ArrayList<>(); + } + bindMetadataAnnotations.add(new FieldAnnotation(fieldName, annotation)); + } + else if(annotation instanceof BindField){ + if(bindFieldAnnotations == null){ + bindFieldAnnotations = new ArrayList<>(); + } + bindFieldAnnotations.add(new FieldAnnotation(fieldName, annotation)); + } + else if(annotation instanceof BindEntity){ + if(bindEntityAnnotations == null){ + bindEntityAnnotations = new ArrayList<>(); + } + bindEntityAnnotations.add(new FieldAnnotation(fieldName, annotation)); + } + else if(annotation instanceof BindEntityList){ + if(bindEntityListAnnotations == null){ + bindEntityListAnnotations = new ArrayList<>(); + } + bindEntityListAnnotations.add(new FieldAnnotation(fieldName, annotation)); + } + } + + public List getBindMetadataAnnotations() { + return bindMetadataAnnotations; + } + + public List getBindFieldAnnotations() { + return bindFieldAnnotations; + } + + public List getBindEntityAnnotations() { + return bindEntityAnnotations; + } + + public List getBindEntityListAnnotations() { + return bindEntityListAnnotations; + } + + public boolean isNotEmpty() { + return bindMetadataAnnotations != null || bindFieldAnnotations != null || bindEntityAnnotations != null || bindEntityListAnnotations != null; + } +} diff --git a/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionManager.java b/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionManager.java new file mode 100644 index 0000000..21a18db --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionManager.java @@ -0,0 +1,318 @@ +package com.diboot.core.binding.parser; + +import com.diboot.core.binding.BaseBinder; +import com.diboot.core.util.S; +import com.diboot.core.util.V; +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.operators.relational.*; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.schema.Column; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 条件表达式的管理器 + * @author Mazhicheng + * @version v2.0 + * @date 2019/4/1 + */ +public class ConditionManager { + private static final Logger log = LoggerFactory.getLogger(ConditionManager.class); + + /** + * 表达式缓存Map + */ + private static Map> expressionParseResultMap = new ConcurrentHashMap<>(); + + /** + * 获取解析后的Expression列表 + * @param condition + * @return + */ + private static List getExpressionList(String condition){ + if(V.isEmpty(condition)){ + return null; + } + List expressionList = expressionParseResultMap.get(condition); + if(expressionList == null){ + ConditionParser visitor = new ConditionParser(); + try{ + Expression expression = CCJSqlParserUtil.parseCondExpression(condition); + expression.accept(visitor); + expressionList = visitor.getExpressList(); + expressionParseResultMap.put(condition, expressionList); + } + catch (Exception e){ + log.error("关联条件解析异常", e); + } + } + return expressionList; + } + + /** + * 附加条件到binder + * @param binder + * @throws Exception + */ + public static void parseConditions(String condition, BaseBinder binder) throws Exception{ + List expressionList = getExpressionList(condition); + if(V.isEmpty(expressionList)){ + log.warn("无法解析注解条件: {} ", condition); + return; + } + // 解析中间表关联 + MiddleTable middleTable = parseMiddleTable(expressionList); + if(middleTable != null){ + binder.withMiddleTable(middleTable); + } + // 解析直接关联 + for(Expression operator : expressionList){ + if(operator instanceof EqualsTo){ + EqualsTo express = (EqualsTo)operator; + String annoColumn = getColumnName(express.getLeftExpression().toString()); + if(express.getRightExpression() instanceof Column){ + String entityColumn = getColumnName(express.getRightExpression().toString()); + binder.joinOn(annoColumn, entityColumn); + } + else{ + binder.andEQ(annoColumn, express.getRightExpression().toString()); + } + } + else if(operator instanceof NotEqualsTo){ + NotEqualsTo express = (NotEqualsTo)operator; + String annoColumn = getColumnName(express.getLeftExpression().toString()); + if(express.getRightExpression() instanceof Column){ + binder.andApply(S.toSnakeCase(annoColumn) + " != " + S.toSnakeCase(express.getRightExpression().toString())); + } + else{ + binder.andNE(annoColumn, express.getRightExpression().toString()); + } + } + else if(operator instanceof GreaterThan){ + GreaterThan express = (GreaterThan)operator; + String annoColumn = getColumnName(express.getLeftExpression().toString()); + if(express.getRightExpression() instanceof Column){ + binder.andApply(S.toSnakeCase(annoColumn) + " > "+ S.toSnakeCase(express.getRightExpression().toString())); + } + else{ + binder.andGT(annoColumn, express.getRightExpression().toString()); + } + } + else if(operator instanceof GreaterThanEquals){ + GreaterThanEquals express = (GreaterThanEquals)operator; + String annoColumn = getColumnName(express.getLeftExpression().toString()); + if(express.getRightExpression() instanceof Column){ + binder.andApply(S.toSnakeCase(annoColumn) + " >= "+ express.getRightExpression().toString()); + } + else{ + binder.andGE(annoColumn, express.getRightExpression().toString()); + } + } + else if(operator instanceof MinorThan){ + MinorThan express = (MinorThan)operator; + String annoColumn = getColumnName(express.getLeftExpression().toString()); + if(express.getRightExpression() instanceof Column){ + binder.andApply(S.toSnakeCase(annoColumn) + " < "+ express.getRightExpression().toString()); + } + else{ + binder.andLT(annoColumn, express.getRightExpression().toString()); + } + } + else if(operator instanceof MinorThanEquals){ + MinorThanEquals express = (MinorThanEquals)operator; + String annoColumn = getColumnName(express.getLeftExpression().toString()); + if(express.getRightExpression() instanceof Column){ + binder.andApply(S.toSnakeCase(annoColumn) + " <= "+ express.getRightExpression().toString()); + } + else{ + binder.andLE(annoColumn, express.getRightExpression().toString()); + } + } + else if(operator instanceof IsNullExpression){ + IsNullExpression express = (IsNullExpression)operator; + String annoColumn = getColumnName(express.getLeftExpression().toString()); + if(express.isNot() == false){ + binder.andIsNull(annoColumn); + } + else{ + binder.andIsNotNull(annoColumn); + } + } + else if(operator instanceof InExpression){ + InExpression express = (InExpression)operator; + String annoColumn = getColumnName(express.getLeftExpression().toString()); + if(express.isNot() == false){ + binder.andApply(S.toSnakeCase(annoColumn) + " IN " + express.getRightItemsList().toString()); + } + else{ + binder.andApply(S.toSnakeCase(annoColumn) + " NOT IN " + express.getRightItemsList().toString()); + } + } + else if(operator instanceof Between){ + Between express = (Between)operator; + String annoColumn = getColumnName(express.getLeftExpression().toString()); + if(express.isNot() == false){ + binder.andBetween(annoColumn, express.getBetweenExpressionStart().toString(), express.getBetweenExpressionEnd().toString()); + } + else{ + binder.andNotBetween(annoColumn, express.getBetweenExpressionStart().toString(), express.getBetweenExpressionEnd().toString()); + } + } + else if(operator instanceof LikeExpression){ + LikeExpression express = (LikeExpression)operator; + String annoColumn = getColumnName(express.getLeftExpression().toString()); + if(express.isNot() == false){ + binder.andLike(annoColumn, express.getStringExpression()); + } + else{ + binder.andNotLike(annoColumn, express.getStringExpression()); + } + } + else{ + log.warn("不支持的条件: "+operator.toString()); + } + } + } + + /** + * 解析中间表 + * @param expressionList + * @return + */ + private static MiddleTable parseMiddleTable(List expressionList) { + // 单一条件不是中间表条件 + if(expressionList.size() <= 1){ + return null; + } + // 统计出现次数 + Map tableNameCountMap = new HashMap<>(); + for(Expression operator : expressionList){ + if(operator instanceof EqualsTo){ + EqualsTo express = (EqualsTo)operator; + // 均为列 + if(express.getLeftExpression() instanceof Column && express.getRightExpression() instanceof Column){ + // 统计左侧列中出现的表名 + String leftColumn = express.getLeftExpression().toString(); + countTableName(tableNameCountMap, leftColumn); + // 统计右侧列中出现的表名 + String rightColumn = express.getRightExpression().toString(); + countTableName(tableNameCountMap, rightColumn); + } + } + } + if(tableNameCountMap.isEmpty()){ + return null; + } + String tableName = null; + int count = 0; + for(Map.Entry entry : tableNameCountMap.entrySet()){ + if(entry.getValue() > count){ + count = entry.getValue(); + tableName = entry.getKey(); + } + } + // 提取到表 + MiddleTable middleTable = new MiddleTable(tableName); + String leftHandColumn = null, rightHandColumn = null; + for(Expression operator : expressionList){ + if(operator instanceof EqualsTo){ + EqualsTo express = (EqualsTo)operator; + // 均为列 + if(express.getLeftExpression() instanceof Column && express.getRightExpression() instanceof Column){ + // 如果左侧为中间表字段 + String leftColumn = express.getLeftExpression().toString(); + if(leftColumn.startsWith(tableName+".")){ + // 绑定右手边连接列 + rightHandColumn = S.substringAfter(leftColumn, "."); + //TODO entityPkField = + } + // 如果右侧为中间表字段 + String rightColumn = express.getRightExpression().toString(); + if(rightColumn.startsWith(tableName+".")){ + // 绑定左手边连接列 + leftHandColumn = S.substringAfter(leftColumn, "."); + // TODO annoObjectFkField = + } + } + } + else{ + String leftExpression = null; + if(operator instanceof NotEqualsTo){ + NotEqualsTo express = (NotEqualsTo)operator; + leftExpression = express.getLeftExpression().toString(); + } + else if(operator instanceof GreaterThan){ + GreaterThan express = (GreaterThan)operator; + leftExpression = express.getLeftExpression().toString(); + } + else if(operator instanceof GreaterThanEquals){ + GreaterThanEquals express = (GreaterThanEquals)operator; + leftExpression = express.getLeftExpression().toString(); + } + else if(operator instanceof MinorThan){ + MinorThan express = (MinorThan)operator; + leftExpression = express.getLeftExpression().toString(); + } + else if(operator instanceof MinorThanEquals){ + MinorThanEquals express = (MinorThanEquals)operator; + leftExpression = express.getLeftExpression().toString(); + } + else if(operator instanceof IsNullExpression){ + IsNullExpression express = (IsNullExpression)operator; + leftExpression = express.getLeftExpression().toString(); + } + else if(operator instanceof InExpression){ + InExpression express = (InExpression)operator; + leftExpression = express.getLeftExpression().toString(); + } + else if(operator instanceof Between){ + Between express = (Between)operator; + leftExpression = express.getLeftExpression().toString(); + } + else if(operator instanceof LikeExpression){ + LikeExpression express = (LikeExpression)operator; + leftExpression = express.getLeftExpression().toString(); + } + if(leftExpression != null && leftExpression.startsWith(tableName+".")){ + middleTable.addAdditionalCondition(operator.toString()); + // TODO 移除 + } + } + + } + //TODO + return null; + } + + private static void countTableName(Map tableNameCountMap, String columnStr) { + if(columnStr.contains(".")){ + String tempTableName = S.substringBefore(columnStr, "."); + // 如果是中间表(非this,self标识的当前表) + if(!"this".equals(tempTableName) && !"self".equals(tempTableName)){ + Integer count = tableNameCountMap.get(tempTableName); + if(count == null){ + count = 0; + } + tableNameCountMap.put(tempTableName, count++); + } + } + } + + /** + * 注解列 + * @return + */ + private static String getColumnName(String annoColumn){ + if(annoColumn.contains(".")){ + annoColumn = S.substringAfter(annoColumn, "."); + } + return annoColumn; + } + +} diff --git a/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionParser.java b/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionParser.java new file mode 100644 index 0000000..7d87fd1 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionParser.java @@ -0,0 +1,305 @@ +package com.diboot.core.binding.parser; + +import com.diboot.core.util.S; +import com.diboot.core.util.V; +import net.sf.jsqlparser.expression.*; +import net.sf.jsqlparser.expression.operators.arithmetic.*; +import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +import net.sf.jsqlparser.expression.operators.conditional.OrExpression; +import net.sf.jsqlparser.expression.operators.relational.*; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.statement.select.SubSelect; + +import java.util.ArrayList; +import java.util.List; + +/** + * 关联注解条件解析器 + * @author Mazhicheng + * @version v2.0 + * @date 2019/3/30 + */ +public class ConditionParser implements ExpressionVisitor,ItemsListVisitor { + + public ConditionParser() { + } + + private List errorMsgList = null; + private List expressList = new ArrayList<>(); + + /** + * 添加错误信息 + * @param errorMsg + */ + private void addError(String errorMsg){ + if(errorMsgList == null){ + errorMsgList = new ArrayList<>(); + } + if(!errorMsgList.contains(errorMsg)){ + errorMsgList.add(errorMsg); + } + } + + /** + * 获取解析后的结果 + * @return + * @throws Exception + */ + public List getExpressList() throws Exception{ + if(V.notEmpty(errorMsgList)){ + throw new Exception(S.join(errorMsgList, "; ")); + } + return expressList; + } + + @Override + public void visit(AndExpression andExpression) { + andExpression.getLeftExpression().accept(this); + andExpression.getRightExpression().accept(this); + } + + // ----- 支持的条件 + @Override + public void visit(EqualsTo equalsTo) { + if(!(equalsTo.getLeftExpression() instanceof Column)){ + addError("=条件左侧必须为字段/列名"); + } + expressList.add(equalsTo); + } + @Override + public void visit(NotEqualsTo notEqualsTo) { + if(!(notEqualsTo.getLeftExpression() instanceof Column)){ + addError("!=条件左侧必须为字段/列名"); + } + expressList.add(notEqualsTo); + } + @Override + public void visit(GreaterThan greaterThan) { + if(!(greaterThan.getLeftExpression() instanceof Column)){ + addError(">条件左侧必须为字段/列名"); + } + expressList.add(greaterThan); + } + @Override + public void visit(GreaterThanEquals greaterThanEquals) { + if(!(greaterThanEquals.getLeftExpression() instanceof Column)){ + addError(">=条件左侧必须为字段/列名"); + } + expressList.add(greaterThanEquals); + } + @Override + public void visit(MinorThan minorThan) { + if(!(minorThan.getLeftExpression() instanceof Column)){ + addError("<条件左侧必须为字段/列名"); + } + expressList.add(minorThan); + } + @Override + public void visit(MinorThanEquals minorThanEquals) { + if(!(minorThanEquals.getLeftExpression() instanceof Column)){ + addError("<=条件左侧必须为字段/列名"); + } + expressList.add(minorThanEquals); + } + @Override + public void visit(IsNullExpression isNullExpression) { + if(!(isNullExpression.getLeftExpression() instanceof Column)){ + addError("IsNull条件左侧必须为字段/列名"); + } + expressList.add(isNullExpression); + } + @Override + public void visit(InExpression inExpression) { + if(!(inExpression.getLeftExpression() instanceof Column)){ + addError("IN条件左侧必须为字段/列名"); + } + expressList.add(inExpression); + } + @Override + public void visit(Between between) { + if(!(between.getLeftExpression() instanceof Column)){ + addError("Between条件左侧必须为字段/列名"); + } + expressList.add(between); + } + @Override + public void visit(LikeExpression likeExpression) { + if(!(likeExpression.getLeftExpression() instanceof Column)){ + addError("Like条件左侧必须为字段/列名"); + } + expressList.add(likeExpression); + } + + //------- 暂不支持的条件 + @Override + public void visit(OrExpression orExpression) { + addError("暂不支持 OR 关联条件"); + } + // ------ 忽略的条件 + @Override + public void visit(Column tableColumn) { + } + @Override + public void visit(SubSelect subSelect) { + } + + @Override + public void visit(ExpressionList expressionList) { + + } + + @Override + public void visit(NamedExpressionList namedExpressionList) { + + } + @Override + public void visit(MultiExpressionList multiExprList) { + } + @Override + public void visit(CaseExpression caseExpression) { + } + @Override + public void visit(WhenClause whenClause) { + } + @Override + public void visit(ExistsExpression existsExpression) { + } + @Override + public void visit(AllComparisonExpression allComparisonExpression) { + } + @Override + public void visit(AnyComparisonExpression anyComparisonExpression) { + } + @Override + public void visit(Concat concat) { + } + @Override + public void visit(Matches matches) { + } + @Override + public void visit(BitwiseAnd bitwiseAnd) { + } + @Override + public void visit(BitwiseOr bitwiseOr) { + } + @Override + public void visit(BitwiseXor bitwiseXor) { + } + @Override + public void visit(CastExpression cast) { + } + @Override + public void visit(Modulo modulo) { + } + @Override + public void visit(AnalyticExpression aexpr) { + } + @Override + public void visit(ExtractExpression eexpr) { + } + @Override + public void visit(IntervalExpression iexpr) { + } + @Override + public void visit(OracleHierarchicalExpression oexpr) { + } + @Override + public void visit(RegExpMatchOperator rexpr) { + } + @Override + public void visit(JsonExpression jsonExpr) { + } + @Override + public void visit(JsonOperator jsonExpr) { + } + @Override + public void visit(RegExpMySQLOperator regExpMySQLOperator) { + } + @Override + public void visit(UserVariable var) { + } + @Override + public void visit(NumericBind bind) { + } + @Override + public void visit(KeepExpression aexpr) { + } + @Override + public void visit(MySQLGroupConcat groupConcat) { + } + @Override + public void visit(ValueListExpression valueList) { + } + @Override + public void visit(RowConstructor rowConstructor) { + } + @Override + public void visit(OracleHint hint) { + } + @Override + public void visit(TimeKeyExpression timeKeyExpression) { + } + @Override + public void visit(DateTimeLiteralExpression literal) { + } + @Override + public void visit(NotExpression aThis) { + } + @Override + public void visit(BitwiseRightShift aThis) { + } + @Override + public void visit(BitwiseLeftShift aThis) { + } + @Override + public void visit(NullValue nullValue) { + } + @Override + public void visit(Function function) { + } + @Override + public void visit(SignedExpression signedExpression) { + } + @Override + public void visit(JdbcParameter jdbcParameter) { + } + @Override + public void visit(JdbcNamedParameter jdbcNamedParameter) { + } + @Override + public void visit(DoubleValue doubleValue) { + } + @Override + public void visit(LongValue longValue) { + } + @Override + public void visit(HexValue hexValue) { + } + @Override + public void visit(DateValue dateValue) { + } + @Override + public void visit(TimeValue timeValue) { + } + @Override + public void visit(TimestampValue timestampValue) { + } + @Override + public void visit(Parenthesis parenthesis) { + } + @Override + public void visit(StringValue stringValue) { + } + @Override + public void visit(Addition addition) { + } + @Override + public void visit(Division division) { + } + @Override + public void visit(Multiplication multiplication) { + } + @Override + public void visit(Subtraction subtraction) { + } +} diff --git a/diboot-core/src/main/java/com/diboot/core/binding/parser/FieldAnnotation.java b/diboot-core/src/main/java/com/diboot/core/binding/parser/FieldAnnotation.java new file mode 100644 index 0000000..280331d --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/parser/FieldAnnotation.java @@ -0,0 +1,34 @@ +package com.diboot.core.binding.parser; + +import java.lang.annotation.Annotation; + +/** + * 字段名与注解的包装对象关系
+ * + * @author Mazhicheng
+ * @version 1.0
+ * @date 2019/04/04
+ */ +public class FieldAnnotation{ + /** + * 字段名 + */ + private String fieldName; + /** + * 注解 + */ + private Annotation annotation; + + public FieldAnnotation(String fieldName, Annotation annotation){ + this.fieldName = fieldName; + this.annotation = annotation; + } + + public String getFieldName() { + return fieldName; + } + + public Annotation getAnnotation() { + return annotation; + } +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/binding/parser/MiddleTable.java b/diboot-core/src/main/java/com/diboot/core/binding/parser/MiddleTable.java new file mode 100644 index 0000000..5619195 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/parser/MiddleTable.java @@ -0,0 +1,85 @@ +package com.diboot.core.binding.parser; + +import com.diboot.core.config.Cons; + +import java.util.ArrayList; +import java.util.List; + +/** + *
+ * + * @author Mazhicheng
+ * @version 1.0
+ * @date 2019/04/01
+ */ +public class MiddleTable { + /** + * 中间表 + */ + private String table; + /** + * 与注解VO的外键关联的连接字段 + */ + private String leftHandColumn; + /** + * 与被引用Entity属性主键的连接字段 + */ + private String rightHandColumn; + /** + * 附加条件 + */ + private List additionalConditions; + + public MiddleTable(String table){ + this.table = table; + } + + public String getTable() { + return table; + } + + /** + * 连接(左手连接VO的外键,右手连接Entity属性的主键) + * @param leftHandColumn + * @param rightHandColumn + * @return + */ + public MiddleTable connect(String leftHandColumn, String rightHandColumn) { + this.leftHandColumn = leftHandColumn; + this.rightHandColumn = rightHandColumn; + return this; + } + + /** + * 添加中间表查询所需的附加条件 + * @param additionalCondition + */ + public MiddleTable addAdditionalCondition(String additionalCondition) { + if(this.additionalConditions == null){ + this.additionalConditions = new ArrayList<>(); + } + this.additionalConditions.add(additionalCondition); + return this; + } + + /** + * 转换查询SQL + * @param annoForeignKeyList 注解外键值的列表,用于拼接SQL查询 + * @return + */ + public String toSQL(List annoForeignKeyList){ + StringBuilder sb = new StringBuilder(); + sb.append("SELECT ").append(this.leftHandColumn).append( Cons.SEPARATOR_COMMA) + .append(this.rightHandColumn).append(" FROM ").append(this.table) + .append(" WHERE ").append(this.leftHandColumn).append(" IN(") + //TODO 生成IN + .append(")"); + if(this.additionalConditions != null){ + for(String condition : this.additionalConditions){ + sb.append(" AND ").append(condition); + } + } + return sb.toString(); + } + +} diff --git a/diboot-core/src/main/java/com/diboot/core/config/BaseConfig.java b/diboot-core/src/main/java/com/diboot/core/config/BaseConfig.java new file mode 100644 index 0000000..d615ef6 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/config/BaseConfig.java @@ -0,0 +1,76 @@ +package com.diboot.core.config; + +import com.diboot.core.util.PropertiesUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/*** + * 系统默认配置 + * @author Mazhicheng + * @version 2.0 + * @date 2019/01/01 + */ +public class BaseConfig { + private static final Logger log = LoggerFactory.getLogger(BaseConfig.class); + + /** + * 从默认的/指定的 Properties文件获取配置 + * @param key + * @return + */ + public static String getProperty(String key, String... propertiesFileName){ + return PropertiesUtils.get(key, propertiesFileName); + } + + /*** + * 从默认的/指定的 Properties文件获取boolean值 + * @param key + * @param propertiesFileName + * @return + */ + public static boolean isTrue(String key, String... propertiesFileName){ + return PropertiesUtils.getBoolean(key, propertiesFileName); + } + + /*** + * 获取int类型 + * @param key + * @param propertiesFileName + * @return + */ + public static int getInteger(String key, String... propertiesFileName){ + return PropertiesUtils.getInteger(key, propertiesFileName); + } + + /*** + * 获取截取长度 + * @return + */ + public static int getCutLength(){ + Integer length = PropertiesUtils.getInteger("system.default.cutLength"); + if(length != null){ + return length; + } + return 20; + } + + /*** + * 默认页数 + * @return + */ + public static int getPageSize() { + Integer length = PropertiesUtils.getInteger("pagination.default.pageSize"); + if(length != null){ + return length; + } + return 20; + } + + /*** + * 获取批量插入的每批次数量 + * @return + */ + public static int getBatchSize() { + return 1000; + } +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/config/Cons.java b/diboot-core/src/main/java/com/diboot/core/config/Cons.java new file mode 100644 index 0000000..bc371f6 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/config/Cons.java @@ -0,0 +1,38 @@ +package com.diboot.core.config; + +/** + * 基础常量定义 + * @author Mazhicheng + * @version 2.0 + * @date 2019/01/01 + */ +public class Cons { + /** + * 默认字符集UTF-8 + */ + public static final String CHARSET_UTF8 = "UTF-8"; + /** + * 分隔符 , + */ + public static final String SEPARATOR_COMMA = ","; + + public static final String SEPARATOR_UNDERSCORE = "_"; + /*** + * 默认字段名定义 + */ + public enum FieldName{ + /** + * 主键属性名 + */ + id, + /** + * 默认的上级ID属性名 + */ + parentId, + /** + * 子节点属性名 + */ + children + } + +} diff --git a/diboot-core/src/main/java/com/diboot/core/controller/BaseController.java b/diboot-core/src/main/java/com/diboot/core/controller/BaseController.java new file mode 100644 index 0000000..8b5dfcb --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/controller/BaseController.java @@ -0,0 +1,435 @@ +package com.diboot.core.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.diboot.core.config.Cons; +import com.diboot.core.entity.BaseEntity; +import com.diboot.core.util.BeanUtils; +import com.diboot.core.util.JSON; +import com.diboot.core.util.S; +import com.diboot.core.util.V; +import com.diboot.core.vo.JsonResult; +import com.diboot.core.vo.Pagination; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.validation.BindingResult; +import org.springframework.validation.ObjectError; +import org.springframework.web.bind.annotation.RequestMethod; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.*; + +/*** + * Controller的父类 + * @author Mazhicheng + * @version 2.0 + * @date 2019/01/01 + */ +@Controller +public class BaseController { + private static final Logger log = LoggerFactory.getLogger(BaseController.class); + + /*** + * 分页参数列表 + */ + protected static final List PARAM_PAGES = Arrays.asList("_pageIndex", "_pageSize", "_totalCount", "_orderBy"); + /*** + * 字段 + */ + protected static final String PARAM_FIELDS = "_fields"; + + /** + * ID参数名 + */ + protected static final String PARAM_ID = Cons.FieldName.id.name(); + + /** + * 错误关键字 + */ + protected static final String ERROR = "error"; + + /** + * 解析所有的验证错误信息,转换为JSON + * @param result + * @return + */ + protected String getBindingError(BindingResult result){ + if(result == null || !result.hasErrors()){ + return null; + } + List errors = result.getAllErrors(); + List allErrors = new ArrayList<>(errors.size()); + for(ObjectError error : errors){ + allErrors.add(error.getDefaultMessage().replaceAll("\"", "'")); + } + return S.join(allErrors); + } + + /*** + * 构建查询wrapper + * @param request + * @param + * @return + */ + public QueryWrapper buildQuery(HttpServletRequest request) throws Exception{ + if(!RequestMethod.GET.name().equalsIgnoreCase(request.getMethod())){ + log.warn("调用错误: 非GET请求,无需构建查询条件!"); + return null; + } + //TODO 是否需要先拿到Entity定义的属性列表,只映射该列表中的属性? + QueryWrapper query = new QueryWrapper(); + Map requestMap = getParamsMap(request); + if(V.notEmpty(requestMap)){ + if(requestMap.containsKey(PARAM_FIELDS) && V.notEmpty(requestMap.get(PARAM_FIELDS))){ + if(requestMap.get(PARAM_FIELDS) instanceof String){ + String fields = (String) requestMap.get(PARAM_FIELDS); + query.select(fields); + } + } + for(Map.Entry entry : requestMap.entrySet()){ + Object value = entry.getValue(); + if(!entry.getKey().startsWith("_") && value != null){ + if(value instanceof Set || value instanceof List || value.getClass().isArray()){ + query.in(S.toSnakeCase(entry.getKey()), value); + } + else if(value instanceof String){ + query.eq(S.toSnakeCase(entry.getKey()), value); + } + } + } + } + return query; + } + + /*** + * 构建分页对象 + * @param request + * @return + */ + protected Pagination buildPagination(HttpServletRequest request) throws Exception{ + return buildPagination(request, true); + } + + /*** + * 构建分页对象 + * @param request + * @return + */ + protected Pagination buildPagination(HttpServletRequest request, boolean newInstanceIfNull) throws Exception{ + Pagination page = newInstanceIfNull? new Pagination() : null; + Map pageParamMap = getParamsMap(request, PARAM_PAGES); + if(V.notEmpty(pageParamMap)){ + if(page == null){ + page = new Pagination(); + } + BeanUtils.bindProperties(page, pageParamMap); + } + return page; + } + + /*** + * 获取请求参数Map + * @param request + * @return + */ + public Map getParamsMap(HttpServletRequest request) throws Exception{ + return getParamsMap(request, null); + } + + /*** + * 获取请求参数Map + * @param request + * @return + */ + private Map getParamsMap(HttpServletRequest request, List paramList) throws Exception{ + Map result = new HashMap<>(8); + Enumeration paramNames = request.getParameterNames(); + while (paramNames.hasMoreElements()){ + String paramName = (String) paramNames.nextElement(); + // 如果非要找的参数,则跳过 + if(V.notEmpty(paramList) && !paramList.contains(paramName)){ + continue; + } + String[] values = request.getParameterValues(paramName); + if(V.notEmpty(values)){ + if(values.length == 1){ + if(V.notEmpty(values[0])){ + String paramValue = java.net.URLDecoder.decode(values[0], Cons.CHARSET_UTF8); + result.put(paramName, paramValue); + } + } + else{ + String[] valueArray = new String[values.length]; + for(int i=0; i convertParams2Map(HttpServletRequest request){ + Map result = new HashMap<>(8); + if(request == null){ + return result; + } + Enumeration paramNames = request.getParameterNames(); + while (paramNames.hasMoreElements()){ + String paramName = (String) paramNames.nextElement(); + String[] values = request.getParameterValues(paramName); + if(V.notEmpty(values)){ + if(values.length == 1){ + result.put(paramName, values[0]); + } + else{ + // 多个值需传递到后台SQL的in语句 + result.put(paramName, values); + } + } + } + return result; + } + + /*** + * 将请求参数值绑定成Model + * @param request + */ + public static void buildEntity(BaseEntity entity, HttpServletRequest request){ + Map propMap = convertParams2Map(request); + BeanUtils.bindProperties(entity, propMap); + } + + /*** + * 打印所有参数信息 + * @param request + */ + protected static void dumpParams(HttpServletRequest request){ + Map params = request.getParameterMap(); + if(params != null && !params.isEmpty()){ + StringBuilder sb = new StringBuilder(); + for(Map.Entry entry : params.entrySet()){ + String[] values = entry.getValue(); + if(values != null && values.length > 0){ + sb.append(entry.getKey() + "=" + S.join(values)+"; "); + } + } + log.debug(sb.toString()); + } + } + + /** + * 从request获取Long参数 + * @param request + * @param param + * @return + */ + public Long getLong(HttpServletRequest request, String param){ + return S.toLong(request.getParameter(param)); + } + + /** + * 从request获取Long参数 + * @param request + * @param param + * @param defaultValue + * @return + */ + public long getLong(HttpServletRequest request, String param, Long defaultValue){ + return S.toLong(request.getParameter(param), defaultValue); + } + + /** + * 从request获取Int参数 + * @param request + * @param param + * @return + */ + public Integer getInteger(HttpServletRequest request, String param){ + return S.toInt(request.getParameter(param)); + } + + /** + * 从request获取Int参数 + * @param request + * @param param + * @param defaultValue + * @return + */ + public int getInt(HttpServletRequest request, String param, Integer defaultValue){ + return S.toInt(request.getParameter(param), defaultValue); + } + + /*** + * 从request中获取boolean值 + * @param request + * @param param + * @return + */ + public boolean getBoolean(HttpServletRequest request, String param){ + return S.toBoolean(request.getParameter(param)); + } + + /*** + * 从request中获取boolean值 + * @param request + * @param param + * @param defaultBoolean + * @return + */ + public boolean getBoolean(HttpServletRequest request, String param, boolean defaultBoolean){ + return S.toBoolean(request.getParameter(param), defaultBoolean); + } + + /** + * 从request获取Double参数 + * @param request + * @param param + * @return + */ + public Double getDouble(HttpServletRequest request, String param){ + if(V.notEmpty(request.getParameter(param))){ + return Double.parseDouble(request.getParameter(param)); + } + return null; + } + + /** + * 从request获取Double参数 + * @param request + * @param param + * @param defaultValue + * @return + */ + public Double getDouble(HttpServletRequest request, String param, Double defaultValue){ + if(V.notEmpty(request.getParameter(param))){ + return Double.parseDouble(request.getParameter(param)); + } + return defaultValue; + } + + /** + * 从request获取String参数 + * @param request + * @param param + * @return + */ + public String getString(HttpServletRequest request, String param){ + if(V.notEmpty(request.getParameter(param))){ + return request.getParameter(param); + } + return null; + } + + /** + * 从request获取String参数 + * @param request + * @param param + * @param defaultValue + * @return + */ + public String getString(HttpServletRequest request, String param, String defaultValue){ + if(V.notEmpty(request.getParameter(param))){ + return request.getParameter(param); + } + return defaultValue; + } + + /** + * 从request获取String[]参数 + * @param request + * @param param + * @return + */ + public String[] getStringArray(HttpServletRequest request, String param){ + if(request.getParameterValues(param) != null){ + return request.getParameterValues(param); + } + return null; + } + + /*** + * 从request里获取String列表 + * @param request + * @param param + * @return + */ + public List getStringList(HttpServletRequest request, String param){ + String[] strArray = getStringArray(request, param); + if(V.isEmpty(strArray)){ + return null; + } + return Arrays.asList(strArray); + } + + /*** + * 从request里获取Long列表 + * @param request + * @param param + * @return + */ + public List getLongList(HttpServletRequest request, String param){ + String[] strArray = getStringArray(request, param); + if(V.isEmpty(strArray)){ + return null; + } + List longList = new ArrayList<>(); + for(String str : strArray){ + if(V.notEmpty(str)){ + longList.add(Long.parseLong(str)); + } + } + return longList; + } +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/controller/BaseCrudRestController.java b/diboot-core/src/main/java/com/diboot/core/controller/BaseCrudRestController.java new file mode 100644 index 0000000..6078cbb --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/controller/BaseCrudRestController.java @@ -0,0 +1,190 @@ +package com.diboot.core.controller; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.diboot.core.entity.BaseEntity; +import com.diboot.core.service.BaseService; +import com.diboot.core.util.BeanUtils; +import com.diboot.core.util.JSON; +import com.diboot.core.vo.JsonResult; +import com.diboot.core.vo.Status; +import com.diboot.core.vo.Pagination; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/*** + * 增删改查通用管理功能-父类 + * @author Mazhicheng + * @version 2.0 + * @date 2019/01/01 + */ +@RestController +public abstract class BaseCrudRestController extends BaseController { + private static final Logger log = LoggerFactory.getLogger(BaseCrudRestController.class); + + /** + * 获取service实例 + * @return + */ + protected abstract BaseService getService(); + + /*** + * 获取某资源的集合 + *

+ * url参数示例: /metadata/list?_pageSize=20&_pageIndex=1&_orderBy=itemValue&type=GENDAR + *

+ * @param request + * @return JsonResult + * @throws Exception + */ + protected JsonResult getEntityList(HttpServletRequest request, Wrapper queryWrapper) throws Exception { + // 查询当前页的数据 + List entityList = getService().getEntityList(queryWrapper); + // 返回结果 + return new JsonResult(Status.OK, entityList); + } + + /*** + * 获取某资源的集合 + *

+ * url参数示例: /metadata/list?_pageSize=20&_pageIndex=1&_orderBy=itemValue&type=GENDAR + *

+ * @param request + * @return JsonResult + * @throws Exception + */ + protected JsonResult getEntityListWithPaging(HttpServletRequest request, Wrapper queryWrapper, Class clazz) throws Exception { + // 构建分页 + Pagination pagination = buildPagination(request); + log.debug(JSON.stringify(pagination)); + // 查询当前页的数据 + List entityList = getService().getEntityList(queryWrapper, pagination); + // 转换为VO + entityList = BeanUtils.convertList(entityList, clazz); + // 返回结果 + return new JsonResult(Status.OK, entityList).bindPagination(pagination); + } + + /*** + * 根据id获取某资源对象 + * @param id + * @return JsonResult + * @throws Exception + */ + protected JsonResult getEntity(Long id) throws Exception { + Object entity = getService().getEntity(id); + return new JsonResult(Status.OK, entity); + } + + /*** + * 创建资源对象 + * @param entity + * @param result + * @return JsonResult + * @throws Exception + */ + protected JsonResult createEntity(BaseEntity entity, BindingResult result, ModelMap modelMap) throws Exception { + // Model属性值验证结果 + if(result != null && result.hasErrors()) { + return new JsonResult(Status.FAIL_INVALID_PARAM, super.getBindingError(result)); + } + if(modelMap.get(ERROR) != null){ + return new JsonResult(Status.FAIL_VALIDATION, (String) modelMap.get(ERROR)); + } + // 执行保存操作 + boolean success = getService().createEntity(entity); + if(success){ + // 组装返回结果 + Map data = new HashMap<>(2); + data.put(PARAM_ID, entity.getId()); + return new JsonResult(Status.OK, data); + } + else{ + log.warn("创建操作未成功,model="+entity.getClass().getSimpleName()); + // 组装返回结果 + return new JsonResult(Status.FAIL_OPERATION); + } + } + + /*** + * 根据ID更新资源对象 + * @param entity + * @param result + * @return JsonResult + * @throws Exception + */ + protected JsonResult updateEntity(BaseEntity entity, BindingResult result, ModelMap modelMap) throws Exception{ + // Model属性值验证结果 + if(result.hasErrors()) { + return new JsonResult(Status.FAIL_INVALID_PARAM, super.getBindingError(result)); + } + if(modelMap.get(ERROR) != null){ + return new JsonResult(Status.FAIL_VALIDATION, (String) modelMap.get(ERROR)); + } + // 执行保存操作 + boolean success = getService().updateEntity(entity); + if(success){ + // 组装返回结果 + Map data = new HashMap<>(2); + data.put(PARAM_ID, entity.getId()); + return new JsonResult(Status.OK, data); + } + else{ + log.warn("更新操作失败,model="+entity.getClass().getSimpleName()+", id="+entity.getId()); + // 返回操作结果 + return new JsonResult(Status.FAIL_OPERATION); + } + } + + /*** + * 根据id删除资源对象 + * @param id + * @return + * @throws Exception + */ + protected JsonResult deleteEntity(Serializable id) throws Exception{ + if(id == null) { + return new JsonResult(Status.FAIL_INVALID_PARAM, "请选择需要删除的条目!"); + } + // 是否有权限删除 + BaseEntity model = (BaseEntity) getService().getEntity(id); + // 执行删除操作 + String error = beforeDelete(model); + if(error != null){ + // 返回json + return new JsonResult(Status.FAIL_OPERATION, error); + } + // 执行删除操作 + boolean success = getService().deleteEntity(id); + if(success){ + log.info("删除操作成功,model="+model.getClass().getSimpleName()+", id="+id); + // 组装返回结果 + Map data = new HashMap<>(2); + data.put(PARAM_ID, model.getId()); + return new JsonResult(Status.OK, data); + } + else{ + log.warn("删除操作未成功,model="+model.getClass().getSimpleName()+", id="+id); + return new JsonResult(Status.FAIL_OPERATION); + } + } + + //============= 供子类继承重写的方法 ================= + /*** + * 是否有删除权限,如不可删除返回错误提示信息,如 Status.FAIL_NO_PERMISSION.label() + * @param entity + * @return + */ + protected String beforeDelete(BaseEntity entity){ + return null; + } + +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/controller/ExceptionController.java b/diboot-core/src/main/java/com/diboot/core/controller/ExceptionController.java new file mode 100644 index 0000000..7f3f4cb --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/controller/ExceptionController.java @@ -0,0 +1,39 @@ +package com.diboot.core.controller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.ExceptionHandler; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 异常处理类 + * @author Mazhicheng + * @version 2.0 + * @date 2019/01/01 + */ +public class ExceptionController extends BaseController{ + private static final Logger log = LoggerFactory.getLogger(ExceptionController.class); + + /*** + * 默认异常处理 + * @param request + * @param ex + * @return + */ + @ExceptionHandler(Exception.class) + public void handleException(HttpServletRequest request, HttpServletResponse response, Exception ex) { + // 记录日志 + log.error("发生异常:", ex); + + String requestUrl = (String) request.getAttribute("javax.servlet.error.request_uri"); + Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code"); + Object exception = request.getAttribute("javax.servlet.error.exception"); + + StringBuilder sb = new StringBuilder(); + sb.append("request_uri: [").append(requestUrl).append("] Error occured : ").append("status_code=").append(statusCode) + .append(";message=").append(request.getAttribute("javax.servlet.error.message")).append(";exception=").append(exception); + log.warn(sb.toString()); + } +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/entity/BaseEntity.java b/diboot-core/src/main/java/com/diboot/core/entity/BaseEntity.java new file mode 100644 index 0000000..61047c1 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/entity/BaseEntity.java @@ -0,0 +1,92 @@ +package com.diboot.core.entity; + +import com.alibaba.fastjson.annotation.JSONField; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.diboot.core.util.JSON; + +import java.io.Serializable; +import java.util.Date; +import java.util.Map; + +/** + * Entity基础父类 + * @author Mazhicheng + * @version v2.0 + * @date 2018/12/27 + */ +public abstract class BaseEntity implements Serializable { + private static final long serialVersionUID = 10203L; + + /*** + * 默认主键字段id,类型为Long型自增,转json时转换为String + */ + @TableId(type = IdType.AUTO) + private Long id; + + /*** + * 默认逻辑删除标记,deleted=0有效 + */ + @TableLogic + @JSONField(serialize = false) + private boolean deleted = false; + + /*** + * 默认记录创建时间字段,新建时由数据库赋值 + */ + @TableField(update="now()") + private Date createTime; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Date getCreateTime() { + return this.createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public boolean isDeleted() { + return deleted; + } + + public void setDeleted(boolean deleted) { + this.deleted = deleted; + } + + /*** + * 是否为新建 + * @return + */ + @JSONField(serialize = false) + public boolean isNew(){ + return getId() != null; + } + + /*** + * model对象转为map + * @return + */ + public Map toMap(){ + String jsonStr = JSON.stringify(this); + return JSON.toMap(jsonStr); + } + + /** + * model对象转为String + * @return + */ + @Override + public String toString(){ + return this.getClass().getName()+ ":"+this.getId(); + } +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/entity/BaseExtEntity.java b/diboot-core/src/main/java/com/diboot/core/entity/BaseExtEntity.java new file mode 100644 index 0000000..98e35e8 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/entity/BaseExtEntity.java @@ -0,0 +1,65 @@ +package com.diboot.core.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.diboot.core.util.JSON; +import com.diboot.core.util.V; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * 附带extdata扩展字段的Entity父类 + * @author Mazhicheng + * @version v2.0 + * @date 2018/12/27 + */ +public abstract class BaseExtEntity extends BaseEntity { + private static final long serialVersionUID = 10204L; + + @TableField + private String extdata; //扩展数据 + + @TableField(exist = false) + private Map extdataMap; + + public String getExtdata() { + if(V.isEmpty(this.extdataMap)){ + return null; + } + return JSON.toJSONString(this.extdataMap); + } + + public void setExtdata(String extdata) { + if(V.notEmpty(extdata)){ + this.extdataMap = JSON.toLinkedHashMap(extdata); + } + } + + /*** + * 从extdata JSON中提取扩展属性值 + * @param extAttrName + * @return + */ + public Object getFromExt(String extAttrName){ + if(this.extdataMap == null){ + return null; + } + return this.extdataMap.get(extAttrName); + } + + /*** + * 添加扩展属性和值到extdata JSON中 + * @param extAttrName + * @param extAttrValue + */ + public void addIntoExt(String extAttrName, Object extAttrValue){ + if(extAttrName == null && extAttrValue == null){ + return; + } + if(this.extdataMap == null){ + this.extdataMap = new LinkedHashMap<>(); + } + this.extdataMap.put(extAttrName, extAttrValue); + } + +} diff --git a/diboot-core/src/main/java/com/diboot/core/entity/EntityList.java b/diboot-core/src/main/java/com/diboot/core/entity/EntityList.java new file mode 100644 index 0000000..f95ccab --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/entity/EntityList.java @@ -0,0 +1,30 @@ +package com.diboot.core.entity; + +import javax.validation.Valid; +import java.util.Arrays; +import java.util.List; + +/** + * Model的List包装类,用于接收List并绑定校验的情况 + * @author Mazhicheng + * @version 2.0 + * @date 2018/11/8 + */ +public class EntityList { + + @Valid + private final List entityList; + + public EntityList(final T... entities){ + this.entityList = Arrays.asList(entities); + } + + public EntityList(final List entityList){ + this.entityList = entityList; + } + + public List getModelList(){ + return entityList; + } + +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/entity/Metadata.java b/diboot-core/src/main/java/com/diboot/core/entity/Metadata.java new file mode 100644 index 0000000..c4466ab --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/entity/Metadata.java @@ -0,0 +1,137 @@ +package com.diboot.core.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotNull; + +/** + * 元数据实体 + * @author Mazhicheng + * @version v2.0 + * @date 2018/12/27 + */ +public class Metadata extends BaseExtEntity { + private static final long serialVersionUID = 11301L; + + /*** + * 上级ID + */ + @NotNull(message = "上级ID不能为空,如无请设为0") + @TableField + private Long parentId = 0L; + + /*** + * 元数据类型 + */ + @NotNull(message = "元数据类型不能为空!") + @Length(max = 50, message = "元数据类型长度超长!") + @TableField + private String type; + + /*** + * 元数据项的显示名称 + */ + @NotNull(message = "元数据项名称不能为空!") + @Length(max = 100, message = "元数据项名称长度超长!") + @TableField + private String itemName; + + /*** + * 元数据项的存储值(编码) + */ + @Length(max = 100, message = "元数据项编码长度超长!") + @TableField + private String itemValue; + + /*** + * 备注信息 + */ + @Length(max = 200, message = "元数据备注长度超长!") + @TableField + private String comment; + + /*** + * 排序号 + */ + @TableField + private int sortId = 99; + + /*** + * 是否为系统预置(预置不可删除) + */ + @TableField + private boolean system = true; + + /*** + * 是否可编辑 + */ + @TableField + private boolean editable = false; + + public Long getParentId() { + return parentId; + } + + public void setParentId(Long parentId) { + this.parentId = parentId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getItemName() { + return itemName; + } + + public void setItemName(String itemName) { + this.itemName = itemName; + } + + public String getItemValue() { + return itemValue; + } + + public void setItemValue(String itemValue) { + this.itemValue = itemValue; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public int getSortId() { + return sortId; + } + + public void setSortId(int sortId) { + this.sortId = sortId; + } + + public boolean isSystem() { + return system; + } + + public void setSystem(boolean system) { + this.system = system; + } + + public boolean isEditable() { + return editable; + } + + public void setEditable(boolean editable) { + this.editable = editable; + } + +} diff --git a/diboot-core/src/main/java/com/diboot/core/mapper/BaseCrudMapper.java b/diboot-core/src/main/java/com/diboot/core/mapper/BaseCrudMapper.java new file mode 100644 index 0000000..6f66a21 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/mapper/BaseCrudMapper.java @@ -0,0 +1,13 @@ +package com.diboot.core.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 基础CRUD的父类Mapper + * @author Mazhicheng + * @version 2018/12/22 + * Copyright © www.dibo.ltd + */ +public interface BaseCrudMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/mapper/MetadataMapper.java b/diboot-core/src/main/java/com/diboot/core/mapper/MetadataMapper.java new file mode 100644 index 0000000..41bca4d --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/mapper/MetadataMapper.java @@ -0,0 +1,14 @@ +package com.diboot.core.mapper; + +import com.diboot.core.entity.Metadata; + +/** + * 元数据Mapper + * @author Mazhicheng + * @version 2018/12/22 + * Copyright © www.dibo.ltd + */ +public interface MetadataMapper extends BaseCrudMapper { + +} + diff --git a/diboot-core/src/main/java/com/diboot/core/plugin/PluginManager.java b/diboot-core/src/main/java/com/diboot/core/plugin/PluginManager.java new file mode 100644 index 0000000..726c593 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/plugin/PluginManager.java @@ -0,0 +1,11 @@ +package com.diboot.core.plugin; + +/** + * 插件管理器 + * @author Mazhicheng + * @version v2.0 + * @date 2018/10/23 + */ +public interface PluginManager { + +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/service/BaseService.java b/diboot-core/src/main/java/com/diboot/core/service/BaseService.java new file mode 100644 index 0000000..3607e3c --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/service/BaseService.java @@ -0,0 +1,190 @@ +package com.diboot.core.service; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.diboot.core.binding.EntityBinder; +import com.diboot.core.binding.EntityListBinder; +import com.diboot.core.binding.FieldBinder; +import com.diboot.core.vo.KeyValue; +import com.diboot.core.vo.Pagination; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 基础服务Service + * @author Mazhicheng + * @version 2.0 + * @date 2019/01/01 + */ +public interface BaseService{ + + /** + * 获取Entity实体 + * @param id 主键 + * @return entity + */ + T getEntity(Serializable id); + + /** + * 创建Entity实体 + * @param entity + * @return true:成功, false:失败 + */ + boolean createEntity(T entity); + + /*** + * 批量创建Entity + * @param entityList 实体对象列表 + * @return true:成功, false: 失败 + */ + boolean createEntities(Collection entityList); + + /** + * 更新Entity实体 + * @param entity + * @return + */ + boolean updateEntity(T entity); + + /** + * 更新Entity实体(更新符合条件的所有非空字段) + * @param entity + * @param updateCriteria + * @return + */ + boolean updateEntity(T entity, Wrapper updateCriteria); + + /** + * 更新Entity实体(仅更新updateWrapper.set指定的字段) + * @param updateWrapper + * @return + */ + boolean updateEntity(Wrapper updateWrapper); + + /*** + * 创建或更新entity(entity.id存在则新建,否则更新) + * @param entity + * @return + */ + boolean createOrUpdateEntity(T entity); + + /** + * 根据主键删除实体 + * @param id 主键 + * @return true:成功, false:失败 + */ + boolean deleteEntity(Serializable id); + + /** + * 按条件删除实体 + * @param queryWrapper + * @return + */ + boolean deleteEntities(Wrapper queryWrapper) throws Exception; + + /** + * 获取符合条件的entity记录总数 + * @return + * @throws Exception + */ + int getEntityListCount(Wrapper queryWrapper); + + /** + * 获取model列表 + * @param queryWrapper + * @return + * @throws Exception + */ + List getEntityList(Wrapper queryWrapper); + + /** + * 获取model列表 + * @param queryWrapper + * @param pagination + * @return + * @throws Exception + */ + List getEntityList(Wrapper queryWrapper, Pagination pagination); + + /** + * 获取指定数量的entity记录 + * @param queryWrapper + * @param limitCount + * @return + * @throws Exception + */ + List getEntityListLimit(Wrapper queryWrapper, int limitCount); + + /** + * 获取指定属性的Map列表 + * @param queryWrapper + * @return + */ + List> getMapList(Wrapper queryWrapper); + + /** + * 获取指定属性的Map列表 + * @param queryWrapper + * @param pagination + * @return + */ + List> getMapList(Wrapper queryWrapper, Pagination pagination); + + /*** + * 获取键值对的列表,用于构建select下拉选项等 + * + * @param queryWrapper + * @return + */ + List getKeyValueList(Wrapper queryWrapper); + + /** + * 获取View Object对象 + * @param id 主键 + * @param voClass vo类 + * @return entity + */ + VO getViewObject(Serializable id, Class voClass); + + /** + * 获取View Object对象列表 + * @param entityList + * @param voClass vo类 + * @return + * @throws Exception + */ + List getViewObjectList(List entityList, Class voClass); + + /** + * 根据查询条件获取vo列表 + * @param queryWrapper + * @param pagination + * @return + * @throws Exception + */ + List getViewObjectList(Wrapper queryWrapper, Pagination pagination, Class voClass); + + /*** + * 绑定字段值到VO列表的元素中 + * @param voList + * @return + */ + FieldBinder bindingFieldTo(List voList); + + /*** + * 绑定entity对象到VO列表元素中 + * @param voList + * @return + */ + EntityBinder bindingEntityTo(List voList); + + /*** + * 绑定entity对象列表到VO列表元素中(适用于VO-Entity一对多的关联) + * @param voList vo列表 + * @return + */ + EntityListBinder bindingEntityListTo(List voList); + +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/service/MetadataService.java b/diboot-core/src/main/java/com/diboot/core/service/MetadataService.java new file mode 100644 index 0000000..b142bf6 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/service/MetadataService.java @@ -0,0 +1,44 @@ +package com.diboot.core.service; + +import com.diboot.core.entity.Metadata; +import com.diboot.core.util.IGetter; +import com.diboot.core.util.ISetter; +import com.diboot.core.vo.KeyValue; + +import java.util.List; + +/** + * 元数据Service + * @author Mazhicheng + * @version 2.0 + * @date 2019/01/01 + */ +public interface MetadataService extends BaseService{ + + /*** + * 获取对应类型的键值对 + * @param type + * @return + */ + List getKeyValueList(String type); + + /*** + * 绑定itemName字段到VoList + * @param voList + * @param setFieldLabelFn + * @param getFieldIdFn + * @param + * @param + * @param + */ + void bindItemLabel(List voList, ISetter setFieldLabelFn, + IGetter getFieldIdFn, String type); + + /*** + * 绑定itemName字段到VoList + * @param voList + * @param setFieldName + * @param getFieldName + */ + void bindItemLabel(List voList, String setFieldName, String getFieldName, String type); +} diff --git a/diboot-core/src/main/java/com/diboot/core/service/impl/BaseServiceImpl.java b/diboot-core/src/main/java/com/diboot/core/service/impl/BaseServiceImpl.java new file mode 100644 index 0000000..0afd563 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/service/impl/BaseServiceImpl.java @@ -0,0 +1,292 @@ +package com.diboot.core.service.impl; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.diboot.core.binding.manager.AnnotationBindingManager; +import com.diboot.core.binding.EntityBinder; +import com.diboot.core.binding.EntityListBinder; +import com.diboot.core.binding.FieldBinder; +import com.diboot.core.config.BaseConfig; +import com.diboot.core.config.Cons; +import com.diboot.core.mapper.BaseCrudMapper; +import com.diboot.core.service.BaseService; +import com.diboot.core.util.BeanUtils; +import com.diboot.core.util.S; +import com.diboot.core.util.V; +import com.diboot.core.vo.KeyValue; +import com.diboot.core.vo.Pagination; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.transaction.annotation.Transactional; + +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/*** + * CRUD通用接口实现类 + * @author Mazc + * @param mapper类 + * @param entity类 + * @version 2.0 + * @date 2019/01/01 + */ +public class BaseServiceImpl, T> extends ServiceImpl implements BaseService { + private static final Logger log = LoggerFactory.getLogger(BaseServiceImpl.class); + + /*** + * VO类与注解的缓存 + */ + private static Map> CLASS_ANNOTATION_MAP = new ConcurrentHashMap<>(); + + /*** + * 获取当前的Mapper对象 + * @return + */ + protected M getMapper(){ + return baseMapper; + } + + @Override + public T getEntity(Serializable id){ + return super.getById(id); + } + + @Override + public boolean createEntity(T entity) { + if(entity == null){ + warning("createModel", "参数entity为null"); + return false; + } + return super.save(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean createEntities(Collection entityList){ + if(V.isEmpty(entityList)){ + warning("createEntities", "参数entityList为空!"); + return false; + } + // 批量插入 + return super.saveBatch(entityList, BaseConfig.getBatchSize()); + } + + @Override + public boolean updateEntity(T entity) { + boolean success = super.updateById(entity); + return success; + } + + @Override + public boolean updateEntity(T entity, Wrapper updateWrapper) { + boolean success = super.update(entity, updateWrapper); + return success; + } + + @Override + public boolean updateEntity(Wrapper updateWrapper) { + boolean success = super.update(null, updateWrapper); + return success; + } + + @Override + public boolean createOrUpdateEntity(T entity) { + boolean success = super.saveOrUpdate(entity); + return success; + } + + @Override + public boolean deleteEntity(Serializable id) { + boolean success = super.removeById(id); + return success; + } + + @Override + public boolean deleteEntities(Wrapper queryWrapper) throws Exception{ + // 执行 + boolean success = super.remove(queryWrapper); + return success; + } + + @Override + public int getEntityListCount(Wrapper queryWrapper) { + return super.count(queryWrapper); + } + + @Override + public List getEntityList(Wrapper queryWrapper) { + return getEntityList(queryWrapper, null); + } + + @Override + public List getEntityList(Wrapper queryWrapper, Pagination pagination) { + if(pagination != null){ + IPage page = convertToIPage(pagination); + page = super.page(page, queryWrapper); + // 如果重新执行了count进行查询,则更新pagination中的总数 + if(page.isSearchCount()){ + pagination.set_totalCount(page.getTotal()); + } + return page.getRecords(); + } + else{ + List list = super.list(queryWrapper); + if(list == null){ + list = Collections.emptyList(); + } + else if(list.size() > BaseConfig.getBatchSize()){ + log.warn("单次查询记录数量过大,返回结果数={}", list.size()); + } + return list; + } + } + + @Override + public List getEntityListLimit(Wrapper queryWrapper, int limitCount) { + IPage page = new Page<>(1, limitCount); + page = super.page(page, queryWrapper); + return page.getRecords(); + } + + @Override + public List> getMapList(Wrapper queryWrapper) { + return getMapList(queryWrapper, null); + } + + @Override + public List> getMapList(Wrapper queryWrapper, Pagination pagination) { + if(pagination != null){ + IPage page = convertToIPage(pagination); + IPage> resultPage = super.pageMaps(page, queryWrapper); + // 如果重新执行了count进行查询,则更新pagination中的总数 + if(page.isSearchCount()){ + pagination.set_totalCount(page.getTotal()); + } + return resultPage.getRecords(); + } + else{ + List> list = super.listMaps(queryWrapper); + if(list == null){ + list = Collections.emptyList(); + } + else if(list.size() > BaseConfig.getBatchSize()){ + log.warn("单次查询记录数量过大,返回结果数={}", list.size()); + } + return list; + } + } + + @Override + public List getKeyValueList(Wrapper queryWrapper) { + String sqlSelect = queryWrapper.getSqlSelect(); + if(V.isEmpty(sqlSelect) || S.countMatches(sqlSelect, Cons.SEPARATOR_COMMA) != 1){ + log.error("调用错误: getKeyValueList必须用select依次指定返回的键值字段,如: new QueryWrapper().lambda().select(Metadata::getItemName, Metadata::getItemValue)"); + return Collections.emptyList(); + } + // 获取mapList + List> mapList = super.listMaps(queryWrapper); + if(mapList == null){ + return Collections.emptyList(); + } + // 转换为Key-Value键值对 + String[] keyValueArray = sqlSelect.split(Cons.SEPARATOR_COMMA); + List keyValueList = new ArrayList<>(mapList.size()); + for(Map map : mapList){ + if(map.get(keyValueArray[0]) != null){ + KeyValue kv = new KeyValue((String)map.get(keyValueArray[0]), map.get(keyValueArray[1])); + keyValueList.add(kv); + } + } + return keyValueList; + } + + @Override + public FieldBinder bindingFieldTo(List voList){ + return new FieldBinder<>(this, voList); + } + + @Override + public EntityBinder bindingEntityTo(List voList){ + return new EntityBinder<>(this, voList); + } + + @Override + public EntityListBinder bindingEntityListTo(List voList){ + return new EntityListBinder<>(this, voList); + } + + /** + * 获取View Object对象 + * @param id 主键 + * @return entity + */ + @Override + public VO getViewObject(Serializable id, Class voClass){ + T entity = getEntity(id); + if(entity == null){ + return null; + } + List enityList = new ArrayList<>(); + enityList.add(entity); + // 绑定 + List voList = getViewObjectList(enityList, voClass); + return voList.get(0); + } + + /** + * 获取View Object对象列表 + * @param entityList + * @return + * @throws Exception + */ + @Override + public List getViewObjectList(List entityList, Class voClass){ + // 转换为VO列表 + List voList = BeanUtils.convertList(entityList, voClass); + // 自动绑定关联对象 + AnnotationBindingManager.autoBind(voList); + return voList; + } + + @Override + public List getViewObjectList(Wrapper queryWrapper, Pagination pagination, Class voClass) { + List entityList = getEntityList(queryWrapper, null); + // 转换为VO列表 + List voList = BeanUtils.convertList(entityList, voClass); + // 自动绑定关联对象 + AnnotationBindingManager.autoBind(voList); + return voList; + } + + /*** + * 转换为IPage + * @param pagination + * @return + */ + protected IPage convertToIPage(Pagination pagination){ + if(pagination == null){ + return null; + } + IPage page = new Page() + .setCurrent(pagination.get_pageIndex()) + .setSize(pagination.get_pageSize()) + // 如果前端传递过来了缓存的总数,则本次不再count统计 + .setTotal(pagination.get_totalCount() > 0? -1 : pagination.get_totalCount()) + .setAscs(S.toSnakeCase(pagination.getAscList())) + .setDescs(S.toSnakeCase(pagination.getDescList())); + return page; + } + + /*** + * 打印警告信息 + * @param method + * @param message + */ + private void warning(String method, String message){ + log.warn(this.getClass().getName() + "."+ method +" 调用错误: "+message+", 请检查!"); + } +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/service/impl/MetadataServiceImpl.java b/diboot-core/src/main/java/com/diboot/core/service/impl/MetadataServiceImpl.java new file mode 100644 index 0000000..dbbf6ed --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/service/impl/MetadataServiceImpl.java @@ -0,0 +1,69 @@ +package com.diboot.core.service.impl; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.diboot.core.entity.Metadata; +import com.diboot.core.mapper.MetadataMapper; +import com.diboot.core.service.MetadataService; +import com.diboot.core.util.*; +import com.diboot.core.vo.KeyValue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 元数据相关service实现 + * @author Mazhicheng + * @version 2.0 + * @date 2019/01/01 + */ +@Service +public class MetadataServiceImpl extends BaseServiceImpl implements MetadataService{ + private static final Logger log = LoggerFactory.getLogger(MetadataServiceImpl.class); + + private static final String FIELD_NAME_ITEM_NAME = BeanUtils.convertToFieldName(Metadata::getItemName); + private static final String FIELD_NAME_ITEM_VALUE = BeanUtils.convertToFieldName(Metadata::getItemValue); + private static final String FIELD_NAME_TYPE = BeanUtils.convertToFieldName(Metadata::getType); + private static final String FIELD_NAME_PARENT_ID = BeanUtils.convertToFieldName(Metadata::getParentId); + + @Override + public List getKeyValueList(String type) { + // 构建查询条件 + Wrapper queryMetadata = new QueryWrapper().lambda() + .select(Metadata::getItemName, Metadata::getItemValue) + .eq(Metadata::getType, type) + .gt(Metadata::getParentId, 0); + // 返回构建条件 + return getKeyValueList(queryMetadata); + } + + @Override + public void bindItemLabel(List voList, ISetter setFieldLabelFn, + IGetter getFieldIdFn, String type){ + if(V.isEmpty(voList)){ + return; + } + bindingFieldTo(voList) + .link(Metadata::getItemName, setFieldLabelFn) + .joinOn(getFieldIdFn, Metadata::getItemValue) + .andEQ(FIELD_NAME_TYPE, type) + .andGT(FIELD_NAME_PARENT_ID, 0) + .bind(); + } + + @Override + public void bindItemLabel(List voList, String setFieldName, String getFieldName, String type){ + if(V.isEmpty(voList)){ + return; + } + getFieldName = S.toLowerCaseCamel(getFieldName); + bindingFieldTo(voList) + .link(FIELD_NAME_ITEM_NAME, setFieldName) + .joinOn(getFieldName, FIELD_NAME_ITEM_VALUE) + .andEQ(FIELD_NAME_TYPE, type) + .andGT(FIELD_NAME_PARENT_ID, 0) + .bind(); + } +} diff --git a/diboot-core/src/main/java/com/diboot/core/util/BatchIterator.java b/diboot-core/src/main/java/com/diboot/core/util/BatchIterator.java new file mode 100644 index 0000000..90b9e27 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/util/BatchIterator.java @@ -0,0 +1,54 @@ +package com.diboot.core.util; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * 集合批次迭代 + * @author Mazhicheng + * @version v2.0 + * @date 2019/01/01 + */ +public class BatchIterator implements Iterator> { + /** + * 每批次集合数量 + */ + private int batchSize; + /*** + * 原始list + */ + private List originalList; + private int index = 0; + private List result; + private int total = 0; + + public BatchIterator(List originalList, int batchSize) { + if (batchSize <= 0) { + return; + } + this.batchSize = batchSize; + this.originalList = originalList; + this.total = V.notEmpty(originalList)? originalList.size() : 0; + result = new ArrayList<>(batchSize); + } + + @Override + public boolean hasNext() { + return index < total; + } + + @Override + public List next() { + result.clear(); + for (int i = 0; i < batchSize && index < total; i++) { + result.add(originalList.get(index++)); + } + return result; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("BatchIterator.remove未实现!"); + } +} diff --git a/diboot-core/src/main/java/com/diboot/core/util/BeanUtils.java b/diboot-core/src/main/java/com/diboot/core/util/BeanUtils.java new file mode 100644 index 0000000..71740ca --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/util/BeanUtils.java @@ -0,0 +1,525 @@ +package com.diboot.core.util; + + +import com.diboot.core.config.Cons; +import com.diboot.core.entity.BaseEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeanWrapper; +import org.springframework.beans.PropertyAccessorFactory; +import org.springframework.cglib.beans.BeanCopier; + +import java.beans.BeanInfo; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.io.Serializable; +import java.lang.invoke.SerializedLambda; +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Bean相关处理工具类 + * @author Mazhicheng + * @version v2.0 + * @date 2019/01/01 + */ +public class BeanUtils { + private static final Logger log = LoggerFactory.getLogger(BeanUtils.class); + + /** + * 连接符号 + */ + private static final String CHANGE_FLAG = "->"; + + /** + * 忽略对比的字段 + */ + private static final Set IGNORE_FIELDS = new HashSet(){{ + add("createTime"); + }}; + + /*** + * 缓存BeanCopier + */ + private static Map BEAN_COPIER_INST_MAP = new ConcurrentHashMap<>(); + /*** + * 缓存类-Lambda的映射关系 + */ + private static Map CLASS_LAMDBA_CACHE = new ConcurrentHashMap<>(); + + /*** + * 获取实例 + * @param source + * @param target + * @return + */ + private static BeanCopier getBeanCopierInstance(Object source, Object target){ + //build key + String beanCopierKey = source.getClass().toString() +"_"+ target.getClass().toString(); + BeanCopier copierInst = BEAN_COPIER_INST_MAP.get(beanCopierKey); + if(copierInst == null){ + copierInst = BeanCopier.create(source.getClass(), target.getClass(), false); + BEAN_COPIER_INST_MAP.put(beanCopierKey, copierInst); + } + return copierInst; + } + + /** + * Copy属性到另一个对象 + * @param source + * @param target + */ + public static Object copyProperties(Object source, Object target){ + BeanCopier copierInst = getBeanCopierInstance(source, target); + copierInst.copy(source, target, null); + return target; + } + + /*** + * 将对象转换为另外的对象实例 + * @param source + * @param clazz + * @param + * @return + */ + public static T convert(Object source, Class clazz){ + if(source == null){ + return null; + } + T target = null; + try{ + target = clazz.getConstructor().newInstance(); + copyProperties(source, target); + } + catch (Exception e){ + log.warn("对象转换异常, class="+clazz.getName()); + } + return target; + } + + /*** + * 将对象转换为另外的对象实例 + * @param sourceList + * @param clazz + * @param + * @return + */ + public static List convertList(List sourceList, Class clazz){ + if(V.isEmpty(sourceList)){ + return Collections.emptyList(); + } + List resultList = new ArrayList<>(); + try{ + for(Object source : sourceList){ + T target = clazz.getConstructor().newInstance(); + copyProperties(source, target); + resultList.add(target); + } + } + catch (Exception e){ + log.warn("对象转换异常, class="+clazz.getName()); + } + return resultList; + } + + /*** + * 附加Map中的属性值到Model + * @param model + * @param propMap + */ + public static void bindProperties(Object model, Map propMap){ + try{// 获取类属性 + BeanInfo beanInfo = Introspector.getBeanInfo(model.getClass()); + // 给 JavaBean 对象的属性赋值 + PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); + for (PropertyDescriptor descriptor : propertyDescriptors) { + String propertyName = descriptor.getName(); + if (propMap.containsKey(propertyName)){ + Object value = propMap.get(propertyName); + Class type = descriptor.getWriteMethod().getParameterTypes()[0]; + Object[] args = new Object[1]; + String fieldType = type.getSimpleName(); + // 类型不一致,需转型 + if(!value.getClass().getTypeName().equals(fieldType)){ + if(value instanceof String){ + // String to Date + if(fieldType.equalsIgnoreCase(Date.class.getSimpleName())){ + args[0] = D.fuzzyConvert((String)value); + } + // Map中的String型转换为其他型 + else if(fieldType.equalsIgnoreCase(Boolean.class.getSimpleName())){ + args[0] = V.isTrue((String)value); + } + else if (fieldType.equalsIgnoreCase(Integer.class.getSimpleName()) || "int".equals(fieldType)) { + args[0] = Integer.parseInt((String)value); + } + else if (fieldType.equalsIgnoreCase(Long.class.getSimpleName())) { + args[0] = Long.parseLong((String)value); + } + else if (fieldType.equalsIgnoreCase(Double.class.getSimpleName())) { + args[0] = Double.parseDouble((String)value); + } + else if (fieldType.equalsIgnoreCase(Float.class.getSimpleName())) { + args[0] = Float.parseFloat((String)value); + } + else{ + args[0] = value; + log.warn("类型不一致,暂无法自动绑定,请手动转型一致后调用!字段类型="+fieldType); + } + } + else{ + args[0] = value; + log.warn("类型不一致,且Map中的value非String类型,暂无法自动绑定,请手动转型一致后调用!value="+value); + } + } + else{ + args[0] = value; + } + descriptor.getWriteMethod().invoke(model, args); + } + } + } + catch (Exception e){ + log.warn("复制Map属性到Model异常: " + e.getMessage(), e); + } + } + + /*** + * 获取对象的属性值 + * @param obj + * @param field + * @return + */ + public static Object getProperty(Object obj, String field){ + BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(obj); + return wrapper.getPropertyValue(field); + } + + /*** + * 设置属性值 + * @param obj + * @param field + * @param value + */ + public static void setProperty(Object obj, String field, Object value) { + BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(obj); + wrapper.setPropertyValue(field, value); + } + + /*** + * Key-Object对象Map + * @param allLists + * @return + */ + public static Map convert2KeyObjectMap(List allLists, String... fields){ + if(allLists == null || allLists.isEmpty()){ + return null; + } + Map allListMap = new LinkedHashMap<>(allLists.size()); + // 转换为map + try{ + for(T model : allLists){ + Object key = null; + if(V.isEmpty(fields)){ + //未指定字段,以id为key + key = getProperty(model, Cons.FieldName.parentId.name());; + } + // 指定了一个字段,以该字段为key,类型同该字段 + else if(fields.length == 1){ + key = getProperty(model, fields[0]); + } + else{ // 指定了多个字段,以字段S.join的结果为key,类型为String + List list = new ArrayList(); + for(String fld : fields){ + list.add(getProperty(model, fld)); + } + key = S.join(list); + } + if(key != null){ + allListMap.put(key, model); + } + else{ + log.warn(model.getClass().getName() + " 的属性 "+fields[0]+" 值存在 null,BeanUtils.convert2KeyModelMap转换结果需要确认!"); + } + } + } + catch(Exception e){ + log.warn("转换key-model异常", e); + } + return allListMap; + } + + /*** + * 构建上下级关联的树形结构的model + * @param allModels + * @param + * @return + */ + public static List buildTree(List allModels){ + if(V.isEmpty(allModels)){ + return null; + } + // 提取所有的top level对象 + List topLevelModels = new ArrayList(); + for(T model : allModels){ + Object parentId = getProperty(model, Cons.FieldName.parentId.name()); + if(parentId == null || V.fuzzyEqual(parentId, 0)){ + topLevelModels.add(model); + } + } + if(V.isEmpty(topLevelModels)){ + return topLevelModels; + } + // 提取向下一层的对象 + buildDeeperLevelTree(topLevelModels, allModels); + // 返回第一层级节点(二级及以上子级通过children属性获取) + return topLevelModels; + } + + /*** + * 构建下一层级树形结构 + * @param parentModels + * @param allModels + * @param + */ + private static void buildDeeperLevelTree(List parentModels, List allModels){ + List deeperLevelModels = new ArrayList(); + Map parentLevelModelMap = convert2KeyObjectMap(parentModels); + for(T model : allModels){ + Object parentId = getProperty(model, Cons.FieldName.parentId.name()); + if(parentLevelModelMap.keySet().contains(parentId) && !parentId.equals(model.getId())){ + deeperLevelModels.add(model); + } + } + if(V.isEmpty(deeperLevelModels)){ + return; + } + for(T model : deeperLevelModels){ + Object parentId = getProperty(model, Cons.FieldName.parentId.name()); + T parentModel = parentLevelModelMap.get(parentId); + if(parentModel!=null){ + List children = (List) getProperty(parentModel, Cons.FieldName.children.name()); + if(children == null){ + children = new ArrayList(); + setProperty(parentModel, Cons.FieldName.children.name(), children); + } + children.add(model); + } + } + // 递归进入下一层级 + buildDeeperLevelTree(deeperLevelModels, allModels); + } + + /*** + * 提取两个model的差异值 + * @param oldModel + * @param newModel + * @return + */ + public static String extractDiff(BaseEntity oldModel, BaseEntity newModel){ + return extractDiff(oldModel, newModel, null); + } + + /*** + * 提取两个model的差异值,只对比指定字段 + * @param oldModel + * @param newModel + * @return + */ + public static String extractDiff(BaseEntity oldModel, BaseEntity newModel, Set fields){ + if(newModel == null || oldModel == null){ + log.warn("调用错误,Model不能为空!"); + return null; + } + Map oldMap = oldModel.toMap(); + Map newMap = newModel.toMap(); + Map result = new HashMap<>(oldMap.size()+newMap.size()); + for(Map.Entry entry : oldMap.entrySet()){ + if(IGNORE_FIELDS.contains(entry.getKey())){ + continue; + } + String oldValue = entry.getValue()!=null ? String.valueOf(entry.getValue()) : ""; + Object newValueObj = newMap.get(entry.getKey()); + String newValue = newValueObj!=null? String.valueOf(newValueObj) : ""; + // 设置变更的值 + boolean checkThisField = fields == null || fields.contains(entry.getKey()); + if(checkThisField && !oldValue.equals(newValue)){ + result.put(entry.getKey(), S.join(oldValue, CHANGE_FLAG, newValue)); + } + // 从新的map中移除该key + if(newValueObj!=null){ + newMap.remove(entry.getKey()); + } + } + if(!newMap.isEmpty()){ + for(Map.Entry entry : newMap.entrySet()){ + if(IGNORE_FIELDS.contains(entry.getKey())){ + continue; + } + Object newValueObj = entry.getValue(); + String newValue = newValueObj!=null? String.valueOf(newValueObj) : ""; + // 设置变更的值 + if(fields==null || fields.contains(entry.getKey())){ + result.put(entry.getKey(), S.join("", CHANGE_FLAG, newValue)); + } + } + } + oldMap = null; + newMap = null; + // 转换结果为String + return JSON.toJSONString(result); + } + + /** + * 从list对象列表中提取指定属性值到新的List + * @param objectList 对象list + * @param getterFn get方法 + * @param + * @return + */ + public static List collectToList(List objectList, IGetter getterFn){ + if(V.isEmpty(objectList)){ + return Collections.emptyList(); + } + String getterPropName = convertToFieldName(getterFn); + return collectToList(objectList, getterPropName); + } + + /** + * 从list对象列表中提取Id主键值到新的List + * @param objectList 对象list + * @param + * @return + */ + public static List collectIdToList(List objectList){ + if(V.isEmpty(objectList)){ + return Collections.emptyList(); + } + return collectToList(objectList, Cons.FieldName.id.name()); + } + + /*** + * 从list对象列表中提取指定属性值到新的List + * @param objectList + * @param getterPropName + * @param + * @return + */ + public static List collectToList(List objectList, String getterPropName){ + List fieldValueList = new ArrayList(); + try{ + for(E object : objectList){ + Object fieldValue = getProperty(object, getterPropName); + if(!fieldValueList.contains(fieldValue)){ + fieldValueList.add(fieldValue); + } + } + } + catch (Exception e){ + log.warn("提取属性值异常, getterPropName="+getterPropName, e); + } + return fieldValueList; + } + + /** + * 绑定map中的属性值到list + * @param setFieldFn + * @param getFieldFun + * @param fromList + * @param valueMatchMap + * @param + */ + public static void bindPropValueOfList(ISetter setFieldFn, List fromList, IGetter getFieldFun, Map valueMatchMap){ + if(V.isEmpty(fromList)){ + return; + } + // function转换为字段名 + String setterFieldName = convertToFieldName(setFieldFn), getterFieldName = convertToFieldName(getFieldFun); + bindPropValueOfList(setterFieldName, fromList, getterFieldName, valueMatchMap); + } + + /*** + * 从对象集合提取某个属性值到list中 + * @param setterFieldName + * @param fromList + * @param getterFieldName + * @param valueMatchMap + * @param + */ + public static void bindPropValueOfList(String setterFieldName, List fromList, String getterFieldName, Map valueMatchMap){ + if(V.isEmpty(fromList)){ + return; + } + try{ + for(E object : fromList){ + // 获取到当前的属性值 + Object fieldValue = getProperty(object, getterFieldName); + // 获取到当前的value + Object value = valueMatchMap.get(fieldValue); + // 赋值 + setProperty(object, setterFieldName, value); + } + } + catch (Exception e){ + log.warn("设置属性值异常, setterFieldName="+setterFieldName, e); + } + } + + /*** + * 转换方法引用为属性名 + * @param fn + * @return + */ + public static String convertToFieldName(IGetter fn) { + SerializedLambda lambda = getSerializedLambda(fn); + String methodName = lambda.getImplMethodName(); + String prefix = null; + if(methodName.startsWith("get")){ + prefix = "get"; + } + else if(methodName.startsWith("is")){ + prefix = "is"; + } + if(prefix == null){ + log.warn("无效的getter方法: "+methodName); + } + return S.uncapFirst(S.substringAfter(methodName, prefix)); + } + + /*** + * 转换方法引用为属性名 + * @param fn + * @return + */ + public static String convertToFieldName(ISetter fn) { + SerializedLambda lambda = getSerializedLambda(fn); + String methodName = lambda.getImplMethodName(); + if(!methodName.startsWith("set")){ + log.warn("无效的setter方法: "+methodName); + } + return S.uncapFirst(S.substringAfter(methodName, "set")); + } + + /*** + * 获取类对应的Lambda + * @param fn + * @return + */ + private static SerializedLambda getSerializedLambda(Serializable fn){ + SerializedLambda lambda = CLASS_LAMDBA_CACHE.get(fn.getClass()); + if(lambda == null){ + try{ + Method method = fn.getClass().getDeclaredMethod("writeReplace"); + method.setAccessible(Boolean.TRUE); + lambda = (SerializedLambda) method.invoke(fn); + CLASS_LAMDBA_CACHE.put(fn.getClass(), lambda); + } + catch (Exception e){ + log.error("获取SerializedLambda异常, class="+fn.getClass().getSimpleName(), e); + } + } + return lambda; + } + +} diff --git a/diboot-core/src/main/java/com/diboot/core/util/ContextHelper.java b/diboot-core/src/main/java/com/diboot/core/util/ContextHelper.java new file mode 100644 index 0000000..89909fb --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/util/ContextHelper.java @@ -0,0 +1,141 @@ +package com.diboot.core.util; + +import com.diboot.core.service.BaseService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Lazy; +import org.springframework.core.ResolvableType; +import org.springframework.stereotype.Component; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Spring上下文帮助类 + * @author Mazhicheng + * @version 2.0 + * @date 2019/01/01 + */ +@Component +@Lazy(false) +public class ContextHelper implements ApplicationContextAware { + private static final Logger log = LoggerFactory.getLogger(ContextHelper.class); + + /*** + * ApplicationContext上下文 + */ + private static ApplicationContext APPLICATION_CONTEXT = null; + + /** + * Entity-对应的Mapper缓存 + */ + private static Map entityToMapperCacheMap = new ConcurrentHashMap<>(); + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + if(APPLICATION_CONTEXT == null){ + APPLICATION_CONTEXT = applicationContext; + } + } + + /*** + * 获取ApplicationContext上下文 + */ + public static ApplicationContext getApplicationContext() { + return APPLICATION_CONTEXT; + } + + /*** + * 根据beanId获取Bean实例 + * @param beanId + * @return + */ + public static Object getBean(String beanId){ + return getApplicationContext().getBean(beanId); + } + + /*** + * 获取指定类型的单个Bean实例 + * @param type + * @return + */ + public static Object getBean(Class type){ + return getApplicationContext().getBean(type); + } + + /*** + * 获取指定类型的全部实例 + * @param type + * @param + * @return + */ + public static List getBeans(Class type){ + // 获取所有的定时任务实现类 + Map map = getApplicationContext().getBeansOfType(type); + if(V.isEmpty(map)){ + return null; + } + List beanList = new ArrayList<>(); + beanList.addAll(map.values()); + return beanList; + } + + /*** + * 根据注解获取beans + * @param annotationType + * @return + */ + public static List getBeansByAnnotation(Class annotationType){ + Map map = getApplicationContext().getBeansWithAnnotation(annotationType); + if(V.isEmpty(map)){ + return null; + } + List beanList = new ArrayList<>(); + beanList.addAll(map.values()); + return beanList; + } + + /** + * 根据Entity获取对应的Service + * @param entity + * @return + */ + public static BaseService getServiceByEntity(Class entity){ + if(entityToMapperCacheMap.isEmpty()){ + Map serviceMap = getApplicationContext().getBeansOfType(BaseService.class); + if(V.notEmpty(serviceMap)){ + for(Map.Entry entry : serviceMap.entrySet()){ + String entityClassName = getEntityClassByServiceImpl(entry.getValue().getClass()); + if(V.notEmpty(entityClassName)){ + entityToMapperCacheMap.put(entityClassName, entry.getValue()); + } + } + } + } + return entityToMapperCacheMap.get(entity.getName()); + } + + /** + * 根据Service实现类的bean解析出Entity类名 + * @param currentClass + * @return + */ + private static String getEntityClassByServiceImpl(Class currentClass){ + ResolvableType superType = ResolvableType.forClass(currentClass).getSuperType(); + ResolvableType[] genericsTypes = superType.getSuperType().getGenerics(); + if(V.notEmpty(genericsTypes) && genericsTypes.length >= 2){ + log.debug("Entity-Service: {} -> {}", genericsTypes[1].toString(), currentClass.getName()); + return genericsTypes[1].toString(); + } + else{ + return null; + } + } + +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/util/D.java b/diboot-core/src/main/java/com/diboot/core/util/D.java new file mode 100644 index 0000000..9577a47 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/util/D.java @@ -0,0 +1,323 @@ +package com.diboot.core.util; + + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +/** + * 提供常用的日期操作的工具类 + * @author MaZhicheng + * @version 2.0 + * @date 2019/01/01 + */ +public class D extends DateUtils{ + private static final Logger log = LoggerFactory.getLogger(DateUtils.class); + + /*** + * 日期时间格式 + */ + public static final String FORMAT_DATE_y2M = "yyMM"; + public static final String FORMAT_DATE_y2Md = "yyMMdd"; + public static final String FORMAT_DATE_y4 = "yyyy"; + public static final String FORMAT_DATE_y4Md = "yyyyMMdd"; + public static final String FORMAT_DATE_Y4MD = "yyyy-MM-dd"; + public static final String FORMAT_TIMESTAMP = "yyMMddhhmmss"; + public static final String FORMAT_TIME_HHmm = "HH:mm"; + public static final String FORMAT_TIME_HHmmss = "HH:mm:ss"; + public static final String FORMAT_DATETIME_Y4MDHM = "yyyy-MM-dd HH:mm"; + public static final String FORMAT_DATETIME_Y4MDHMS = "yyyy-MM-dd HH:mm:ss"; + /*** + * 星期 + */ + protected static final String[] WEEK = new String[]{"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"}; + + /*** + * 当前的日期时间 + * @return format指定格式的日期时间 + */ + public static String now(String format){ + Calendar cal = Calendar.getInstance(); + SimpleDateFormat sdf = new SimpleDateFormat(format); + return sdf.format(cal.getTime()); + } + + /** + * 当前日期时间串 + * @return yyMMddhhmmss + */ + public static String toTimestamp(Date date){ + SimpleDateFormat sdf = new SimpleDateFormat(FORMAT_TIMESTAMP); + return sdf.format(date.getTime()); + } + + /** + * 获取月份 + * @return + */ + public static String getMonth(){ + return now(FORMAT_DATE_y2M); + } + + /*** + * 获取今天的日期 + * @return yyyyMMdd + */ + public static String today(){ + return now(FORMAT_DATE_y4Md); + } + + /*** + * 转换字符串为日期date + * @param datetime + * @param fmt + * @return + */ + public static Date convert2FormatDate(String datetime, String fmt){ + if (StringUtils.isBlank(datetime)){ + return null; + } + SimpleDateFormat format = new SimpleDateFormat(fmt); + try { + Date date = format.parse(datetime); + return date; + } + catch (ParseException e) { + log.warn("日期格式转换异常"); + } + return null; + } + + /*** + * 转换date为格式化字符串 + * @param date + * @param fmt + * @return + */ + public static String convert2FormatString(Date date, String fmt) { + if (date == null) { + return null; + } else { + SimpleDateFormat format = new SimpleDateFormat(fmt); + return format.format(date); + } + } + + /** + * 获取格式化的日期 + * @param date 基准日期 + * @param daysOffset 偏移量 + * @return yyyy-MM-dd + */ + public static String getDate(Date date, int... daysOffset){ + if(date == null){ + date = new Date(); + } + if(daysOffset != null && daysOffset.length > 0){ + date = addDays(date, daysOffset[0]); + } + SimpleDateFormat sdf = new SimpleDateFormat(FORMAT_DATE_Y4MD); + return sdf.format(date); + } + + /*** + * 获取格式化的日期时间 + * @param date + * @return yyyy-MM-dd HH:mm + */ + public static String getDateTime(Date date, int... daysOffset){ + if(date == null){ + date = new Date(); + } + if(daysOffset != null && daysOffset.length > 0){ + date = addDays(date, daysOffset[0]); + } + SimpleDateFormat sdf = new SimpleDateFormat(FORMAT_DATETIME_Y4MDHM); + return sdf.format(date); + } + + /** + * 是否是工作时间段,用于后台程序等 + * @return + */ + public static boolean isWorkingTime(){ + Calendar cal = Calendar.getInstance(); + int hour = cal.get(Calendar.HOUR_OF_DAY); + return (hour >= 8 && hour < 20); + } + + /*** + * 获取上午/下午 + * @return + */ + public static String getAmPm() { + Calendar c = Calendar.getInstance(); + int hours = c.get(Calendar.HOUR_OF_DAY); + if (hours <= 9){ + return "早上"; + } + else if (9 < hours && hours <= 12){ + return "上午"; + } + else if (12 < hours && hours <= 13){ + return "中午"; + } + else if (13 < hours && hours <= 18){ + return "下午"; + } + else{ + return "晚上"; + } + } + + /** + * 得到当前的年月YYMM,用于生成文件夹名称 + * @return + */ + public static String getYearMonth(){ + Calendar cal = Calendar.getInstance(); + SimpleDateFormat sdf = new SimpleDateFormat(FORMAT_DATE_y2M); + return sdf.format(cal.getTime()); + } + + /** + * 得到当前的年月YYMM,用于生成文件夹 + * @return + */ + public static String getYearMonthDay(){ + Calendar cal = Calendar.getInstance(); + SimpleDateFormat sdf = new SimpleDateFormat(FORMAT_DATE_y2Md); + return sdf.format(cal.getTime()); + } + + /** + * 得到当前的年月YYMM,用于生成文件夹 + * @return + */ + public static int getDay(){ + Calendar cal = Calendar.getInstance(); + return cal.get(Calendar.DAY_OF_MONTH); + } + + /*** + * 获取日期对应的星期 + * @param date + * @return + */ + public static String getWeek(Date date){ + return WEEK[Calendar.getInstance().get(Calendar.DAY_OF_WEEK)]; + } + + /** + * 毫秒数转date + * @param timeMillis + * @return + */ + public static Date timeMillis2Date(Long timeMillis){ + return new Date(timeMillis); + } + + /** + * 字符串时间戳转日期 + * @param value + * @return + * @throws ParseException + */ + public static Date datetimeString2Date(String value){ + return convert2DateTime(value, FORMAT_DATETIME_Y4MDHMS); + } + + /** + * 字符串时间戳转日期 + * @return + * @throws ParseException + */ + public static Date convert2Date(String date){ + return convert2FormatDate(date, FORMAT_DATE_Y4MD); + } + + /** + * 字符串时间戳转日期 + * @param dateTime + * @return + * @throws ParseException + */ + public static Date convert2DateTime(String dateTime, String... dateFormat){ + String f = FORMAT_DATETIME_Y4MDHM; + if(dateFormat != null && dateFormat.length > 0){ + f = dateFormat[0]; + } + return convert2FormatDate(dateTime, f); + } + + /*** + * 模糊转换日期 + * @param dateString + * @return + */ + public static Date fuzzyConvert(String dateString){ + if(V.isEmpty(dateString)){ + return null; + } + // 清洗 + if(dateString.contains("-")){ + } + else if(dateString.contains("月")){ + dateString = dateString.replaceAll("年", "-").replaceAll("月", "-").replaceAll("日", "").replaceAll("号", ""); + } + else{ + dateString = dateString.replaceAll("\\/", "-").replaceAll("\\.", "-"); + } + String[] parts = dateString.split(" "); + String[] ymd = parts[0].split("-"); + if(ymd.length >= 3){ + if(ymd[0].length() == 2){ + ymd[0] = String.valueOf(Calendar.getInstance().get(Calendar.YEAR)).substring(0, 2) + ymd[0]; + } + if(ymd[1].length() == 1){ + ymd[1] = "0" + ymd[1]; + } + if(ymd[2].length() == 1){ + ymd[2] = "0" + ymd[2]; + } + } + parts[0] = S.join(ymd, "-"); + if(parts.length == 1){ + return D.convert2FormatDate(parts[0], D.FORMAT_DATE_Y4MD); + } + // 18:20:30:103 + String[] hmsArray = new String[3]; + String[] hms = parts[1].split(":"); + if(hms[0].length() == 1){ + hms[0] = "0" + hms[0]; + } + hmsArray[0] = hms[0]; + if(hms.length >= 2){ + if(hms[1].length() == 1){ + hms[1] = "0" + hms[1]; + } + hmsArray[1] = hms[1]; + } + else{ + hmsArray[1] = "00"; + } + if(hms.length >= 3){ + if(hms[2].length() == 1){ + hms[2] = "0" + hms[2]; + } + hmsArray[2] = hms[2]; + } + else{ + hmsArray[2] = "00"; + } + parts[1] = S.join(hmsArray, ":"); + return D.convert2FormatDate(S.join(parts, " "), D.FORMAT_DATETIME_Y4MDHMS); + } + +} diff --git a/diboot-core/src/main/java/com/diboot/core/util/DateConverter.java b/diboot-core/src/main/java/com/diboot/core/util/DateConverter.java new file mode 100644 index 0000000..89dcbba --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/util/DateConverter.java @@ -0,0 +1,22 @@ +package com.diboot.core.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.convert.converter.Converter; + +import java.util.Date; + +/** + * Spring表单自动绑定到Java属性时的日期格式转换 + * @author Mazhicheng + * @version v2.0 + * @date 2019/01/01 + */ +public class DateConverter implements Converter { + private static final Logger log = LoggerFactory.getLogger(DateConverter.class); + + @Override + public Date convert(String dateString) { + return D.fuzzyConvert(dateString); + } +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/util/Encryptor.java b/diboot-core/src/main/java/com/diboot/core/util/Encryptor.java new file mode 100644 index 0000000..000daf3 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/util/Encryptor.java @@ -0,0 +1,130 @@ +package com.diboot.core.util; + +import com.diboot.core.config.BaseConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.util.Base64; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 加解密工具类 (提供AES加解密,MD5多次哈希...) + * @author Mazhicheng + * @version v2.0 + * @date 2019/01/01 + */ +public class Encryptor { + private static final Logger log = LoggerFactory.getLogger(Encryptor.class); + + private static final String KEY_ALGORITHM = "AES"; + private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS5PADDING"; + private static final String KEY_DEFAULT = V.notEmpty(BaseConfig.getProperty("diboot.encryptor.seed"))? BaseConfig.getProperty("diboot.encryptor.seed") : "Dibo2017M"; + private static final String KEY_FILL = "abcdefghijklmnop"; + // 加密Cipher缓存 + private static Map encryptorMap = new ConcurrentHashMap<>(); + // 解密Cipher缓存 + private static Map decryptorMap = new ConcurrentHashMap<>(); + + /** + * 加密字符串(可指定加密密钥) + * @param input 待加密文本 + * @param key 密钥(可选) + * @return + * @throws Exception + */ + public static String encrypt(String input, String... key){ + String seedKey = V.notEmpty(key)? key[0] : KEY_DEFAULT; + try{ + Cipher cipher = getEncryptor(seedKey); + byte[] enBytes = cipher.doFinal(input.getBytes()); + return Base64.getEncoder().encodeToString(enBytes); + } + catch(Exception e){ + log.error("加密出错:"+input, e); + return input; + } + } + + /** + * 解密字符串 + * @param input 待解密文本 + * @param key 加密key(可选) + * @return + * @throws Exception + */ + public static String decrypt(String input, String... key){ + if(V.isEmpty(input)){ + return input; + } + String seedKey = V.notEmpty(key)? key[0] : KEY_DEFAULT; + try{ + Cipher cipher = getDecryptor(seedKey); + byte[] deBytes = Base64.getDecoder().decode(input.getBytes()); + return new String(cipher.doFinal(deBytes)); + } + catch(Exception e){ + log.error("解密出错:"+input, e); + return input; + } + } + + /*** + * 获取指定key的加密器 + * @param key 加密密钥 + * @return + * @throws Exception + */ + private static Cipher getEncryptor(String key) throws Exception{ + byte[] keyBytes = getKey(key); + Cipher encryptor = encryptorMap.get(new String(keyBytes)); + if(encryptor == null){ + SecretKeySpec skeyspec = new SecretKeySpec(keyBytes, KEY_ALGORITHM); + encryptor = Cipher.getInstance(CIPHER_ALGORITHM); + encryptor.init(Cipher.ENCRYPT_MODE, skeyspec); + // 放入缓存 + encryptorMap.put(key, encryptor); + } + return encryptor; + } + + /*** + * 获取指定key的解密器 + * @param key 解密密钥 + * @return + * @throws Exception + */ + private static Cipher getDecryptor(String key) throws Exception{ + byte[] keyBytes = getKey(key); + Cipher decryptor = encryptorMap.get(new String(keyBytes)); + if(decryptor == null){ + SecretKeySpec skeyspec = new SecretKeySpec(keyBytes, KEY_ALGORITHM); + decryptor = Cipher.getInstance(CIPHER_ALGORITHM); + decryptor.init(Cipher.DECRYPT_MODE, skeyspec); + // 放入缓存 + decryptorMap.put(key, decryptor); + } + return decryptor; + } + + /*** + * 获取key,如非16位则调整为16位 + * @param seed + * @return + */ + private static byte[] getKey(String seed){ + if(V.isEmpty(seed)){ + seed = KEY_DEFAULT; + } + if(seed.length() < 16){ + seed = seed + S.cut(KEY_FILL, 16-seed.length()); + } + else if(seed.length() > 16){ + seed = S.cut(KEY_FILL, 16); + } + return seed.getBytes(); + } + +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/util/IGetter.java b/diboot-core/src/main/java/com/diboot/core/util/IGetter.java new file mode 100644 index 0000000..9dd2355 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/util/IGetter.java @@ -0,0 +1,14 @@ +package com.diboot.core.util; + +import java.io.Serializable; + +/** + * getter方法接口定义 + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/15 + */ +@FunctionalInterface +public interface IGetter extends Serializable { + Object apply(T source); +} diff --git a/diboot-core/src/main/java/com/diboot/core/util/ISetter.java b/diboot-core/src/main/java/com/diboot/core/util/ISetter.java new file mode 100644 index 0000000..a966ca1 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/util/ISetter.java @@ -0,0 +1,14 @@ +package com.diboot.core.util; + +import java.io.Serializable; + +/** + * setter方法接口定义 + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/17 + */ +@FunctionalInterface +public interface ISetter extends Serializable { + void accept(T t, U u); +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/util/JSON.java b/diboot-core/src/main/java/com/diboot/core/util/JSON.java new file mode 100644 index 0000000..d312e34 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/util/JSON.java @@ -0,0 +1,62 @@ +package com.diboot.core.util; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.serializer.SerializeConfig; +import com.alibaba.fastjson.serializer.SimpleDateFormatSerializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; + +/*** + * JSON操作辅助类 + * @author Mazhicheng + * @version v2.0 + * @date 2019/01/01 + */ +public class JSON extends JSONObject{ + private static final Logger log = LoggerFactory.getLogger(JSON.class); + + private static SerializeConfig serializeConfig = new SerializeConfig(); + static { + serializeConfig.put(Date.class, new SimpleDateFormatSerializer(D.FORMAT_DATETIME_Y4MDHM)); + } + + public static String stringify(Object object){ + return toJSONString(object, serializeConfig); + } + + /*** + * 将JSON字符串转换为java对象 + * @param jsonStr + * @return + */ + public static Map toMap(String jsonStr){ + return parseObject(jsonStr); + } + + /*** + * 将JSON字符串转换为java对象 + * @param jsonStr + * @return + */ + public static LinkedHashMap toLinkedHashMap(String jsonStr){ + if(V.isEmpty(jsonStr)){ + return null; + } + return toJavaObject(jsonStr, LinkedHashMap.class); + } + + /*** + * 将JSON字符串转换为java对象 + * @param jsonStr + * @param clazz + * @return + */ + public static T toJavaObject(String jsonStr, Class clazz){ + return JSONObject.parseObject(jsonStr, clazz); + } + +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/util/PropertiesUtils.java b/diboot-core/src/main/java/com/diboot/core/util/PropertiesUtils.java new file mode 100644 index 0000000..9a4b5af --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/util/PropertiesUtils.java @@ -0,0 +1,176 @@ +package com.diboot.core.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.PropertiesFactoryBean; +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.core.io.ClassPathResource; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +/** + * 配置文件工具类 + * @author Mazhicheng + * @version v2.0 + * @date 2019/01/01 + */ +public class PropertiesUtils { + private static final Logger log = LoggerFactory.getLogger(PropertiesUtils.class); + + /*** + * 缓存多个资源文件 + */ + private static Map allPropertiesMap = new HashMap<>(); + + /** + * 默认资源文件名称 + */ + private static final String DEFAULT_PROPERTIES_NAME = "application.properties"; + + /*** + * Properties配置工厂类 + */ + private static PropertiesFactoryBean pfb = new PropertiesFactoryBean(); + + /*** + * 获取指定的配置文件 + * @return + */ + private static Properties getProperties(String... propertiesFileNameArgs){ + // 获取文件名 + String propFileName = getPropertiesFileName(propertiesFileNameArgs); + // 从缓存读取 + Properties props = allPropertiesMap.get(propFileName); + if(props == null){ + if(propFileName.endsWith(".properties")){ + pfb.setLocation(new ClassPathResource(propFileName)); + try{ + pfb.afterPropertiesSet(); + props = pfb.getObject(); + } + catch (Exception e){ + log.warn("无法找到配置文件: " + propFileName, e); + } + } + else if(propFileName.endsWith(".yaml")){ + props = yamlToProperties(propFileName); + } + if(props != null){ + allPropertiesMap.put(propFileName, props); + } + } + return props; + } + + /*** + * 读取配置项的值 + * @param key + * @return + */ + public static String get(String key, String... propertiesFileName){ + // 获取文件名 + String propFileName = getPropertiesFileName(propertiesFileName); + // 获取配置值 + String value = getConfigValueFromPropFile(key, propFileName); + if(value == null && DEFAULT_PROPERTIES_NAME.equals(propFileName)){ + // 如果是默认配置,读取不到再尝试从当前profile配置中读取 + String profile = getConfigValueFromPropFile("spring.profiles.active", DEFAULT_PROPERTIES_NAME); + if(V.notEmpty(profile)){ + value = getConfigValueFromPropFile(key, S.substringBeforeLast(DEFAULT_PROPERTIES_NAME, ".") + "-"+profile+".properties"); + } + } + if(value == null){ + log.trace("配置文件 {} 中未找到配置项: {}", propertiesFileName, key); + } + return value; + } + + /*** + * 读取int型的配置项 + * @param key + * @return + */ + public static Integer getInteger(String key, String... propertiesFileName){ + // 获取文件名 + String propFileName = getPropertiesFileName(propertiesFileName); + // 获取配置值 + String value = get(key, propFileName); + if(V.notEmpty(value)){ + return Integer.parseInt(value); + } + log.trace("配置文件 {} 中未找到配置项: {}", propertiesFileName, key); + return null; + } + + /*** + * 读取boolean值的配置项 + */ + public static boolean getBoolean(String key, String... propertiesFileName) { + // 获取文件名 + String propFileName = getPropertiesFileName(propertiesFileName); + // 获取配置值 + String value = get(key, propFileName); + if(V.notEmpty(value)){ + return V.isTrue(value); + } + log.trace("配置文件 "+(V.notEmpty(propertiesFileName)? propertiesFileName[0] : "")+" 中未找到配置项: "+key + ", 启用默认值 false."); + return false; + } + + /*** + * 获取配置文件名称 + * @param propertiesFileName + * @return + */ + private static String getPropertiesFileName(String... propertiesFileName){ + // 获取文件名 + if(propertiesFileName != null && propertiesFileName.length > 0){ + return propertiesFileName[0]; + } + else{ + return DEFAULT_PROPERTIES_NAME; + } + } + + /*** + * 从配置文件中读取配置值 + * @param key + * @param propertiesFileName + * @return + */ + private static String getConfigValueFromPropFile(String key, String propertiesFileName){ + // 获取配置值 + Properties properties = getProperties(propertiesFileName); + if(properties != null){ + if(properties.containsKey(key)){ + String value = properties.getProperty(key); + // 任何password相关的参数需解密 + boolean isSensitiveConfig = key.contains(".password") || key.contains(".secret"); + if(value != null && isSensitiveConfig){ + value = Encryptor.decrypt(value); + } + return value; + } + } + return null; + } + + /** + * 读取yaml并转换为Properties + * @param yamlSource + * @return + */ + private static Properties yamlToProperties(String yamlSource) { + try { + YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean(); + yaml.setResources(new ClassPathResource(yamlSource)); + return yaml.getObject(); + } + catch (Exception e) { + log.error("Cannot read yaml "+yamlSource, e); + return null; + } + } +} diff --git a/diboot-core/src/main/java/com/diboot/core/util/S.java b/diboot-core/src/main/java/com/diboot/core/util/S.java new file mode 100644 index 0000000..842b654 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/util/S.java @@ -0,0 +1,312 @@ +package com.diboot.core.util; + +import com.diboot.core.config.BaseConfig; +import com.diboot.core.config.Cons; +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.UUID; + +/*** + * String 操作类 + * @author Mazhicheng + * @version v2.0 + * @date 2019/01/01 + */ +public class S extends StringUtils{ + /*** + * 默认分隔符 , + */ + public static final String SEPARATOR = ","; + + /*** + * 裁剪字符串,显示前部分+... + * @param input + * @return + */ + public static String cut(String input){ + return cut(input, BaseConfig.getCutLength()); + } + + /*** + * 裁剪字符串,显示前部分+... + * @param input + * @return + */ + public static String cut(String input, int cutLength){ + return substring(input, 0, cutLength); + } + + /*** + * 将list拼接成string,默认分隔符:, + * @param stringList + * @return + */ + public static String join(List stringList){ + return StringUtils.join(stringList, SEPARATOR); + } + + /*** + * 将list拼接成string,默认分隔符:, + * @param stringArray + * @return + */ + public static String join(String[] stringArray){ + return StringUtils.join(stringArray, SEPARATOR); + } + + /*** + * 按,拆分字符串 + * @param joinedStr + * @return + */ + public static String[] split(String joinedStr){ + if(joinedStr == null){ + return null; + } + return joinedStr.split(SEPARATOR); + } + + /*** + * 转换为String数组(避免转型异常) + * @param stringList + * @return + */ + public static String[] toStringArray(List stringList){ + String[] array = new String[stringList.size()]; + for(int i=0; i toSnakeCase(List camelCaseStrArray){ + if(camelCaseStrArray == null){ + return null; + } + List snakeCaseList = new ArrayList<>(camelCaseStrArray.size()); + for(String camelCaseStr : camelCaseStrArray){ + snakeCaseList.add(toSnakeCase(camelCaseStr)); + } + return snakeCaseList; + } + + /*** + * 转换成蛇形命名(用于Java属性转换为数据库列名) + * @param camelCaseStr + * @return + */ + public static String toSnakeCase(String camelCaseStr){ + if(V.isEmpty(camelCaseStr)){ + return null; + } + char[] chars = camelCaseStr.toCharArray(); + StringBuilder sb = new StringBuilder(); + for (char c : chars){ + if(Character.isUpperCase(c)){ + if(sb.length() > 0){ + sb.append(Cons.SEPARATOR_UNDERSCORE); + } + sb.append(Character.toLowerCase(c)); + } + else{ + sb.append(c); + } + } + return sb.toString(); + } + + /*** + * 转换成首字母小写的驼峰命名(用于数据库列名转换为Java属性) + * @param snakeCaseStr + * @return + */ + public static String toLowerCaseCamel(String snakeCaseStr){ + if(V.isEmpty(snakeCaseStr)){ + return null; + } + // 不包含_,直接return + if(!snakeCaseStr.contains(Cons.SEPARATOR_UNDERSCORE)){ + return snakeCaseStr; + } + char[] chars = snakeCaseStr.toCharArray(); + StringBuilder sb = new StringBuilder(); + boolean upperCase = false; + for (char c : chars){ + if(Cons.SEPARATOR_UNDERSCORE.equals(Character.toString(c))){ + upperCase = true; + continue; + } + if(upperCase){ + sb.append(Character.toUpperCase(c)); + upperCase = false; + } + else{ + sb.append(c); + } + } + return sb.toString(); + } + + /*** + * 转换为Long类型(判空,避免NPE) + * @param strValue + * @return + */ + public static Long toLong(String strValue){ + return toLong(strValue, null); + } + + /*** + * 转换为Long类型(判空,避免NPE) + * @param strValue 字符类型值 + * @param defaultLong 默认值 + * @return + */ + public static Long toLong(String strValue, Long defaultLong){ + if(V.isEmpty(strValue)){ + return defaultLong; + } + return Long.parseLong(strValue); + } + + /*** + * 转换为Integer类型(判空,避免NPE) + * @param strValue + * @return + */ + public static Integer toInt(String strValue){ + return toInt(strValue, null); + } + + /*** + * 转换为Integer类型(判空,避免NPE) + * @param strValue + * @param defaultInt 默认值 + * @return + */ + public static Integer toInt(String strValue, Integer defaultInt){ + if(V.isEmpty(strValue)){ + return defaultInt; + } + return Integer.parseInt(strValue); + } + + /*** + * 字符串转换为boolean + * @param strValue + * @return + */ + public static boolean toBoolean(String strValue){ + return toBoolean(strValue, false); + } + + /*** + * 字符串转换为boolean + * @param strValue + * @param defaultBoolean + * @return + */ + public static boolean toBoolean(String strValue, boolean defaultBoolean){ + if(V.notEmpty(strValue)){ + return V.isTrue(strValue); + } + return defaultBoolean; + } + + /*** + * 将多个空格替换为一个 + * @param input + * @return + */ + public static String removeDuplicateBlank(String input){ + if(V.isEmpty(input)){ + return input; + } + return input.trim().replaceAll(" +", " "); + } + + /** + * 获得随机串 + * @return + */ + public static String newUuid() { + return UUID.randomUUID().toString().replaceAll("-", ""); + } + + /*** + * 生成指定位数的数字/验证码 + */ + private static final String NUMBER_SET = "12345678901"; + private static Random random = new Random(); + public static String newRandomNum(int length){ + StringBuilder sb = new StringBuilder(); + sb.append(NUMBER_SET.charAt(random.nextInt(9))); + for(int i=1; i searchList, List replacementList){ + if(V.isEmpty(searchList) || V.isEmpty(replacementList)){ + return text; + } + String[] searchArray = searchList.toArray(new String[searchList.size()]); + String[] replacementArray = replacementList.toArray(new String[replacementList.size()]); + return replaceEach(text, searchArray, replacementArray); + } + +} diff --git a/diboot-core/src/main/java/com/diboot/core/util/V.java b/diboot-core/src/main/java/com/diboot/core/util/V.java new file mode 100644 index 0000000..43be64a --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/util/V.java @@ -0,0 +1,377 @@ +package com.diboot.core.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Timestamp; +import java.util.*; + +/*** + * Validator校验类 + * @author Mazhicheng + * @version v2.0 + * @date 2019/01/01 + * + */ +public class V { + private static final Logger log = LoggerFactory.getLogger(V.class); + + /*** + * 对象是否为空 + * @param obj + * @return + */ + public static boolean isEmpty(Object obj){ + return obj == null; + } + + /*** + * 字符串是否为空 + * @param value + * @return + */ + public static boolean isEmpty(String value){ + return S.isBlank(value); + } + + /*** + * 字符串数组是否不为空 + * @param values + * @return + */ + public static boolean isEmpty(String[] values){ + return values == null || values.length == 0; + } + + /*** + * 集合为空 + * @param list + * @return + */ + public static boolean isEmpty(Collection list) { + return list == null || list.isEmpty(); + } + + /*** + * Map为空 + * @param obj + * @return + */ + public static boolean isEmpty(Map obj){ + return obj == null || obj.isEmpty(); + } + + /*** + * 对象是否为空 + * @param obj + * @return + */ + public static boolean notEmpty(Object obj){ + return obj != null; + } + + /*** + * 字符串是否不为空 + * @param value + * @return + */ + public static boolean notEmpty(String value){ + return S.isNotBlank(value); + } + + /*** + * 字符串数组是否不为空 + * @param values + * @return + */ + public static boolean notEmpty(String[] values){ + return values != null && values.length > 0; + } + + /*** + * 集合不为空 + * @param list + * @return + */ + public static boolean notEmpty(Collection list) { + return list != null && !list.isEmpty(); + } + + /*** + * 对象不为空且不为0 + * @param longObj + * @return + */ + public static boolean notEmptyOrZero(Long longObj){ + return longObj != null && longObj.longValue() != 0; + } + + /*** + * 对象不为空且不为0 + * @param intObj + * @return + */ + public static boolean notEmptyOrZero(Integer intObj){ + return intObj != null && intObj.intValue() != 0; + } + + /*** + * Map为空 + * @param obj + * @return + */ + public static boolean notEmpty(Map obj){ + return obj != null && !obj.isEmpty(); + } + + /** + * 判断是否为数字(允许小数点) + * @param str + * @return true Or false + */ + public static boolean isNumeric(String str){ + return S.isNumeric(str); + } + + /** + * 判断是否为正确的邮件格式 + * @param str + * @return boolean + */ + public static boolean isEmail(String str){ + if(isEmpty(str)) { + return false; + } + return str.matches("^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$"); + } + + /** + * 判断字符串是否为电话号码 + * @param str + * @return boolean + */ + public static boolean isPhone(String str){ + if(isEmpty(str)){ + return false; + } + boolean valid = str.matches("^1\\d{10}$"); + if(!valid){ + valid = str.matches("^0\\d{2,3}-?\\d{7,8}$"); + } + return valid; + } + + /** + * 判断是否为整型数字 + * @param str + * @return + */ + public static boolean isNumber(String str) { + try{ + S.isNumeric(str); + Integer.parseInt(str); + return true; + } + catch(Exception ex){ + return false; + } + } + + /** + * 是否boolean值范围 + */ + private static final Set TRUE_SET = new HashSet(){{ + add("true"); add("是"); add("y"); add("yes"); add("1"); + }}; + private static final Set FALSE_SET = new HashSet(){{ + add("false"); add("否"); add("n"); add("no"); add("0"); + }}; + + /*** + * 是否为boolean类型 + * @param value + * @return + */ + public static boolean isValidBoolean(String value){ + if(value == null){ + return false; + } + value = S.trim(value).toLowerCase(); + return TRUE_SET.contains(value) || FALSE_SET.contains(value); + } + + /*** + * 转换为boolean类型, 并判定是否为true + * @param value + * @return + */ + public static boolean isTrue(String value){ + if(value == null){ + return false; + } + value = S.trim(value).toLowerCase(); + return TRUE_SET.contains(value); + } + + /*** + * 根据指定规则校验字符串的值是否合法 + * @param value + * @param validation + * @return + */ + public static String validate(String value, String validation){ + if(isEmpty(validation)){ + return null; + } + List errorMsgList = new ArrayList<>(); + String[] rules = validation.split(","); + for(String rule : rules){ + if ("NotNull".equalsIgnoreCase(rule)){ + if (isEmpty(value)) { + errorMsgList.add("不能为空"); + } + } + else if ("Number".equalsIgnoreCase(rule)){ + if (!isNumber(value)) { + errorMsgList.add("非数字格式"); + } + } + else if ("Boolean".equalsIgnoreCase(rule)){ + if (!isValidBoolean(value)) { + errorMsgList.add("非Boolean格式"); + } + } + else if ("Date".equalsIgnoreCase(rule)){ + if (D.fuzzyConvert(value) == null) { + errorMsgList.add("非日期格式"); + } + } + else if (rule.toLowerCase().startsWith("length")) { + String range = rule.substring(rule.indexOf("(") + 1, rule.lastIndexOf(")")); + if (range.contains("-")) { + String[] arr = range.split("-"); + if (notEmpty(arr[0])) { + if ((value.length() < Integer.parseInt(arr[0]))) { + errorMsgList.add("长度少于最小限制数: " + arr[0]); + } + } + if (notEmpty(arr[1])) { + if (value.length() > Integer.parseInt(arr[1])) { + errorMsgList.add("长度超出最大限制数: " + arr[1]); + } + } + } + else { + if (!(value.length() == Integer.parseInt(range))) { + errorMsgList.add("长度限制: " + range + "位"); + } + } + } + else if ("Email".equalsIgnoreCase(rule)) { + if (!isEmail(value)) { + errorMsgList.add("非Email格式"); + } + } + else if ("Phone".equalsIgnoreCase(rule)) { + if (!isPhone(value)) { + errorMsgList.add("非电话号码格式"); + } + } + else{ + //TODO 无法识别的格式 + } + } + // 返回校验不通过的结果 + if(errorMsgList.isEmpty()){ + return null; + } + else{ + return S.join(errorMsgList); + } + } + + /*** + * 判定两个对象是否不同类型或不同值 + * @param source + * @param target + * @return + */ + public static boolean notEqual(Object source, Object target){ + return !equal(source, target); + } + + /*** + * 判定两个对象是否类型相同值相等 + * @param source + * @param target + * @return + */ + public static boolean equal(Object source, Object target){ + // 都为空 + if(source == null && target == null){ + return true; + }// 一个为空一个非空 + else if(source == null || target == null){ + return false; + } + else if((source instanceof String && target instanceof String) + || (source instanceof Long && target instanceof Long) + || (source instanceof Integer && target instanceof Integer) + || (source instanceof Float && target instanceof Float) + || (source instanceof Double && target instanceof Double) + ){ + return (source).equals(target); + } + else if(source instanceof List && target instanceof List){ + List sourceList = (List)source, targetList = (List)target; + // size不等 + if(sourceList.size() != targetList.size()){ + return false; + } + for(Object obj : sourceList){ + if(!targetList.contains(obj)){ + return false; + } + } + return true; + } + // 其他类型不一致 + else if(!source.getClass().getName().equals(target.getClass().getName())){ + return false; + } + else{ + log.warn("暂未实现类型 "+ source.getClass().getSimpleName() + "-"+ target.getClass().getSimpleName() + " 的比对!"); + return false; + } + } + + /*** + * 模糊对比是否相等(类型不同的转成String对比) + * @param source + * @param target + * @return + */ + public static boolean fuzzyEqual(Object source, Object target){ + if(equal(source, target)){ + return true; + } + // Boolean-String类型 + if(source instanceof Boolean && target instanceof String){ + return (boolean) source == V.isTrue((String)target); + } + if(target instanceof Boolean && source instanceof String){ + return (boolean) target == V.isTrue((String)source); + } + // Date-String类型 + else if((source instanceof Timestamp || source instanceof Date) && target instanceof String){ + return D.getDateTime((Date)source).equals(target) || D.getDate((Date)source).equals(target); + } + else if((target instanceof Timestamp || target instanceof Date) && source instanceof String){ + return D.getDateTime((Date)target).equals(source) || D.getDate((Date)target).equals(source); + } + else{ + return String.valueOf(source).equals(String.valueOf(target)); + } + } + +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/vo/JsonResult.java b/diboot-core/src/main/java/com/diboot/core/vo/JsonResult.java new file mode 100644 index 0000000..1ec0c6b --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/vo/JsonResult.java @@ -0,0 +1,105 @@ +package com.diboot.core.vo; + +import com.diboot.core.util.V; + +import java.io.Serializable; + +/** + * JSON返回结果 + * @author Mazhicheng + * @version v2.0 + * @date 2019/01/01 + */ +public class JsonResult implements Serializable { + private static final long serialVersionUID = 1001L; + + /*** + * 状态码 + */ + private int code; + /*** + * 消息内容 + */ + private String msg; + /*** + * 返回结果数据 + */ + private Object data; + + /** + * 默认成功,无返回数据 + */ + public JsonResult(){ + this.code = Status.OK.code(); + this.msg = Status.OK.label(); + this.data = null; + } + + /** + * 默认成功,有返回数据(及附加提示信息) + */ + public JsonResult(Object data, String... additionalMsg){ + this.code = Status.OK.code(); + this.msg = Status.OK.label(); + this.data = data; + if(V.notEmpty(additionalMsg)){ + this.msg += ": " + additionalMsg[0]; + } + } + + /*** + * 非成功,指定状态(及附加提示信息) + * @param status + * @param additionalMsg + */ + public JsonResult(Status status, String... additionalMsg){ + this.code = status.code(); + this.msg = status.label(); + if(V.notEmpty(additionalMsg)){ + this.msg += ": " + additionalMsg[0]; + } + this.data = null; + } + + /** + * 非成功,指定状态、返回数据(及附加提示信息) + */ + public JsonResult(Status status, Object data, String... additionalMsg){ + this.code = status.code(); + this.msg = status.label(); + if(V.notEmpty(additionalMsg)){ + this.msg += ": " + additionalMsg[0]; + } + this.data = data; + } + + /*** + * 自定义JsonResult + * @param code + * @param label + * @param data + */ + public JsonResult(int code, String label, Object data){ + this.code = code; + this.msg = label; + this.data = data; + } + + /*** + * 绑定分页信息 + * @param pagination + */ + public JsonResult bindPagination(Pagination pagination){ + return new PagingJsonResult(this, pagination); + } + + public int getCode() { + return code; + } + public String getMsg() { + return msg; + } + public Object getData() { + return data; + } +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/vo/KeyValue.java b/diboot-core/src/main/java/com/diboot/core/vo/KeyValue.java new file mode 100644 index 0000000..09a9f67 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/vo/KeyValue.java @@ -0,0 +1,46 @@ +package com.diboot.core.vo; + +import java.io.Serializable; + +/** + * KeyValue键值对形式的VO + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/4 + */ +public class KeyValue implements Serializable { + private static final long serialVersionUID = -2358161241655186720L; + + public KeyValue(){} + + public KeyValue(String key, Object value){ + this.k = key; + this.v = value; + } + + /*** + * key: 显示值 + */ + private String k; + + /*** + * value: 存储值 + */ + private Object v; + + public String getK() { + return k; + } + + public void setK(String k) { + this.k = k; + } + + public Object getV() { + return v; + } + + public void setV(Object v) { + this.v = v; + } +} diff --git a/diboot-core/src/main/java/com/diboot/core/vo/Pagination.java b/diboot-core/src/main/java/com/diboot/core/vo/Pagination.java new file mode 100644 index 0000000..07330da --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/vo/Pagination.java @@ -0,0 +1,153 @@ +package com.diboot.core.vo; + +import com.diboot.core.config.BaseConfig; +import com.diboot.core.config.Cons; +import com.diboot.core.util.S; +import com.diboot.core.util.V; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 分页 + * @author Mazhicheng + * @version v2.0 + * @date 2019/01/01 + */ +public class Pagination implements Serializable { + private static final Logger log = LoggerFactory.getLogger(Pagination.class); + + private static final long serialVersionUID = -4083929594112114522L; + + /*** + * 当前页 + */ + private int _pageIndex = 1; + /*** + * 默认每页数量10 + */ + private int _pageSize = BaseConfig.getPageSize(); + /*** + * count总数 + */ + private long _totalCount = 0; + /*** + * 排序-升序排列的字段 + */ + private List ascList = null; + /*** + * 降序排列的字段(默认以ID降序排列,当指定了其他排列方式时以用户指定为准) + */ + private List descList = new ArrayList<>(Arrays.asList(Cons.FieldName.id.name())); + + public Pagination(){ + } + + /*** + * 指定当前页数 + */ + public Pagination(int pageIndex){ + set_pageIndex(pageIndex); + } + + public int get_pageIndex() { + return _pageIndex; + } + + public void set_pageIndex(int _pageIndex) { + this._pageIndex = _pageIndex; + } + + public int get_pageSize() { + return _pageSize; + } + + public void set_pageSize(int _pageSize) { + if(_pageSize > 1000){ + log.warn("分页pageSize过大,将被调整为默认限值,请检查调用是否合理!pageSize="+_pageSize); + _pageSize = 1000; + } + this._pageSize = _pageSize; + } + + public long get_totalCount() { + return _totalCount; + } + + public void set_totalCount(long _totalCount) { + this._totalCount = _totalCount; + } + + public void set_orderBy(String orderBy){ + if(V.notEmpty(orderBy)){ + // 先清空默认排序规则 + clearDefaultOrder(); + // 指定新的排序规则 + String[] orderByFields = S.split(orderBy); + for(String field : orderByFields){ + // orderBy=name:DESC,age:ASC,birthdate + if(field.contains(":")){ + String[] fieldAndOrder = S.split(field, ":"); + if("DESC".equalsIgnoreCase(fieldAndOrder[1])){ + if(descList == null){ + descList = new ArrayList<>(); + } + descList.add(fieldAndOrder[0]); + } + else{ + if(ascList == null){ + ascList = new ArrayList<>(); + } + ascList.add(fieldAndOrder[0]); + } + } + else{ + if(ascList == null){ + ascList = new ArrayList<>(); + } + ascList.add(field); + } + } + } + } + + /*** + * 清除默认排序 + */ + public void clearDefaultOrder(){ + ascList = null; + descList = null; + } + + /*** + * 获取总的页数 + * @return + */ + public int get_totalPage() { + if(_totalCount <= 0){ + return 0; + } + return (int)Math.ceil((float)_totalCount/_pageSize); + } + + /*** + * 获取数据库字段的列排序,用于service层调用 + * @return + */ + public List getAscList() { + return ascList; + } + + /*** + * 获取数据库字段的列排序,,用于service层调用 + * @return + */ + public List getDescList() { + return descList; + } + +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/vo/PagingJsonResult.java b/diboot-core/src/main/java/com/diboot/core/vo/PagingJsonResult.java new file mode 100644 index 0000000..a55ee69 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/vo/PagingJsonResult.java @@ -0,0 +1,28 @@ +package com.diboot.core.vo; + +/** + * JSON返回结果 + * @author Mazhicheng + * @version v2.0 + * @date 2019/01/01 + */ +public class PagingJsonResult extends JsonResult{ + private static final long serialVersionUID = 1001L; + + /*** + * 分页相关信息 + */ + private Pagination page; + + /** + * 默认成功,无返回数据 + */ + public PagingJsonResult(JsonResult jsonResult, Pagination pagination){ + super(jsonResult.getCode(), jsonResult.getMsg(), jsonResult.getData()); + this.page = pagination; + } + + public Pagination getPage() { + return page; + } +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/vo/Status.java b/diboot-core/src/main/java/com/diboot/core/vo/Status.java new file mode 100644 index 0000000..37fe36d --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/vo/Status.java @@ -0,0 +1,89 @@ +package com.diboot.core.vo; + +/** + * 状态码定义 + * @author Mazhicheng + * @version v2.0 + * @date 2019/01/01 + */ +public enum Status { + /*** + * 请求处理成功 + */ + OK(0, "操作成功"), + + /*** + * 部分成功(一般用于批量处理场景,只处理筛选后的合法数据) + */ + WARN_PARTIAL_SUCCESS(1001, "部分成功"), + + /*** + * 有潜在的性能问题 + */ + WARN_PERFORMANCE_ISSUE(1002, "潜在的性能问题"), + + /*** + * 传入参数不对 + */ + FAIL_INVALID_PARAM(4000, "请求参数不匹配"), + + /*** + * Token无效或已过期 + */ + FAIL_INVALID_TOKEN(4001, "Token无效或已过期"), + + /*** + * 没有权限执行该操作 + */ + FAIL_NO_PERMISSION(4003, "无权执行该操作"), + + /*** + * 请求资源不存在 + */ + FAIL_NOT_FOUND(4004, "请求资源不存在"), + + /*** + * 数据校验不通过 + */ + FAIL_VALIDATION(4005, "数据校验不通过"), + + /*** + * 操作执行失败 + */ + FAIL_OPERATION(4006, "操作执行失败"), + + /*** + * 系统异常 + */ + FAIL_EXCEPTION(5000, "系统异常"); + + private int code; + private String label; + Status(int code, String label){ + this.code = code; + this.label = label; + } + public int code(){ + return this.code; + } + public String label(){ + return this.label; + } + public static int getCode(String value){ + for(Status eu : Status.values()){ + if(eu.name().equals(value)){ + return eu.code(); + } + } + return 0; + } + public static String getLabel(String value){ + for(Status eu : Status.values()){ + if(eu.name().equals(value)){ + return eu.label(); + } + } + return null; + } + +} \ No newline at end of file diff --git a/diboot-core/src/main/resources/META-INF/sql/install.sql b/diboot-core/src/main/resources/META-INF/sql/install.sql new file mode 100644 index 0000000..aeeb14a --- /dev/null +++ b/diboot-core/src/main/resources/META-INF/sql/install.sql @@ -0,0 +1,19 @@ +SET FOREIGN_KEY_CHECKS=0; +-- 元数据表 +-- DROP TABLE IF EXISTS `metadata`; +CREATE TABLE `metadata` ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', + `parent_id` int unsigned NOT NULL COMMENT '父ID', + `type` varchar(50) NOT NULL COMMENT '元数据类型', + `item_name` varchar(100) NOT NULL COMMENT '元数据项显示名', + `item_value` varchar(100) DEFAULT NULL COMMENT '元数据项存储值', + `comment` varchar(100) DEFAULT NULL COMMENT '备注', + `extdata` varchar(200) DEFAULT NULL COMMENT '扩展属性', + `sort_id` smallint NOT NULL DEFAULT '99' COMMENT '排序号', + `system` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否是系统预置', + `editable` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否可编辑', + `active` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否有效', + `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + KEY `idx_metadata` (`type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/diboot-example/build.gradle b/diboot-example/build.gradle index 8e36688..ef9a964 100644 --- a/diboot-example/build.gradle +++ b/diboot-example/build.gradle @@ -1,13 +1,4 @@ -plugins { - id 'java' -} - -sourceCompatibility = 1.8 - -repositories { - mavenCentral() -} - dependencies { + compile project(":diboot-core") testCompile group: 'junit', name: 'junit', version: '4.12' -} +} \ No newline at end of file diff --git a/diboot-example/src/main/java/com/diboot/example/Application.java b/diboot-example/src/main/java/com/diboot/example/Application.java new file mode 100644 index 0000000..d812ce1 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/Application.java @@ -0,0 +1,17 @@ +package com.diboot.example; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * @author Administrator + */ +@SpringBootApplication +public class Application extends SpringBootServletInitializer { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} \ No newline at end of file diff --git a/diboot-example/src/main/java/com/diboot/example/config/AppConfig.java b/diboot-example/src/main/java/com/diboot/example/config/AppConfig.java new file mode 100644 index 0000000..e346d46 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/config/AppConfig.java @@ -0,0 +1,13 @@ +package com.diboot.example.config; + +import com.diboot.core.config.BaseConfig; + +/** + * 应用配置 + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/19 + */ +public class AppConfig extends BaseConfig { + +} diff --git a/diboot-example/src/main/java/com/diboot/example/config/MybatisPlusConfig.java b/diboot-example/src/main/java/com/diboot/example/config/MybatisPlusConfig.java new file mode 100644 index 0000000..15ed657 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/config/MybatisPlusConfig.java @@ -0,0 +1,38 @@ +package com.diboot.example.config; + +import com.baomidou.mybatisplus.core.injector.ISqlInjector; +import com.baomidou.mybatisplus.extension.injector.LogicSqlInjector; +import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Mybatis-Plus配置 + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/19 + */ +@Configuration +@MapperScan(basePackages={"com.diboot.*.mapper*"}) +public class MybatisPlusConfig { + + /** + * Mybatis-plus分页插件 + */ + @Bean + public PaginationInterceptor paginationInterceptor() { + PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); + return paginationInterceptor; + } + + /*** + * 逻辑删除注入 + * @return + */ + @Bean + public ISqlInjector sqlInjector() { + return new LogicSqlInjector(); + } + +} diff --git a/diboot-example/src/main/java/com/diboot/example/config/SpringMvcConfig.java b/diboot-example/src/main/java/com/diboot/example/config/SpringMvcConfig.java new file mode 100644 index 0000000..6f4e9fe --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/config/SpringMvcConfig.java @@ -0,0 +1,64 @@ +package com.diboot.example.config; + +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson.support.config.FastJsonConfig; +import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; +import com.diboot.core.util.D; +import com.diboot.core.util.DateConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.http.HttpMessageConverters; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.format.FormatterRegistry; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.ArrayList; +import java.util.List; + +/*** + * Spring配置文件 + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/19 + */ +@Configuration +@EnableAutoConfiguration +@EnableTransactionManagement(proxyTargetClass=true) +@ComponentScan(basePackages={"com.diboot"}) +public class SpringMvcConfig implements WebMvcConfigurer{ + private static final Logger log = LoggerFactory.getLogger(SpringMvcConfig.class); + + /** + * JSON转换组件替换为fastJson + */ + @Bean + public HttpMessageConverters fastJsonHttpMessageConverters() { + FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); + //处理中文乱码问题 + List fastMediaTypes = new ArrayList<>(); + fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8); + converter.setSupportedMediaTypes(fastMediaTypes); + // 配置转换格式 + FastJsonConfig fastJsonConfig = new FastJsonConfig(); + // 设置fastjson的序列化参数:禁用循环依赖检测,数据兼容浏览器端(避免JS端Long精度丢失问题) + fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect, + SerializerFeature.BrowserCompatible); + fastJsonConfig.setDateFormat(D.FORMAT_DATETIME_Y4MDHM); + converter.setFastJsonConfig(fastJsonConfig); + + HttpMessageConverter httpMsgConverter = converter; + return new HttpMessageConverters(httpMsgConverter); + } + + @Override + public void addFormatters(FormatterRegistry registry) { + registry.addConverter(new DateConverter()); + } + +} \ No newline at end of file diff --git a/diboot-example/src/main/java/com/diboot/example/controller/DepartmentController.java b/diboot-example/src/main/java/com/diboot/example/controller/DepartmentController.java new file mode 100644 index 0000000..b80b41c --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/controller/DepartmentController.java @@ -0,0 +1,131 @@ +package com.diboot.example.controller; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.diboot.core.controller.BaseCrudRestController; +import com.diboot.core.service.BaseService; +import com.diboot.core.util.BeanUtils; +import com.diboot.core.vo.JsonResult; +import com.diboot.core.vo.KeyValue; +import com.diboot.core.vo.Status; +import com.diboot.example.entity.Department; +import com.diboot.example.entity.Organization; +import com.diboot.example.service.DepartmentService; +import com.diboot.example.vo.DepartmentVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +/** + * Organization相关Controller + * @author Mazhicheng + * @version 2018/12/23 + * Copyright © www.dibo.ltd + */ +@RestController +@RequestMapping("/department") +public class DepartmentController extends BaseCrudRestController { + + @Autowired + private DepartmentService departmentService; + + /*** + * 默认的分页实现 + *

+ * url参数示例: /list?_pageSize=20&_pageIndex=1&_orderBy=itemValue&type=GENDAR + *

+ * @return + * @throws Exception + */ + @GetMapping("/list") + public JsonResult getDefaultVOList(HttpServletRequest request) throws Exception{ + QueryWrapper queryWrapper = buildQuery(request); + return super.getEntityListWithPaging(request, queryWrapper, DepartmentVO.class); + } + + /*** + * 默认的分页实现 + *

+ * url参数示例: /listVo?page.size=20&page.index=1&page.orderBy=itemValue&type=GENDAR + *

+ * @return + * @throws Exception + */ + @GetMapping("/listVo") + public JsonResult getCustomVOList(HttpServletRequest request) throws Exception{ + QueryWrapper queryWrapper = buildQuery(request); + // 查询当前页的数据 + List entityList = departmentService.getEntityList(queryWrapper); + List voList = departmentService.getViewObjectList(entityList, DepartmentVO.class); + // 返回结果 + return new JsonResult(Status.OK, voList); + } + + @GetMapping("/kv") + public JsonResult getKVPairList(HttpServletRequest request){ + Wrapper wrapper = new QueryWrapper().lambda() + .select(Department::getName, Department::getId); + List list = departmentService.getKeyValueList(wrapper); + return new JsonResult(list); + } + + /*** + * 创建Entity + * @return + * @throws Exception + */ + @PostMapping("/") + public JsonResult createEntity(@ModelAttribute DepartmentVO viewObject, BindingResult result, HttpServletRequest request, ModelMap modelMap) + throws Exception{ + // 转换 + Department entity = BeanUtils.convert(viewObject, Department.class); + // 创建 + return super.createEntity(entity, result, modelMap); + } + + /*** + * 查询Entity + * @param id ID + * @return + * @throws Exception + */ + @GetMapping("/{id}") + public JsonResult getModel(@PathVariable("id")Long id, HttpServletRequest request, ModelMap modelMap) + throws Exception{ + DepartmentVO vo = departmentService.getViewObject(id, DepartmentVO.class); + return new JsonResult(vo); + } + + /*** + * 更新Entity + * @param id ID + * @return + * @throws Exception + */ + @PutMapping("/{id}") + public JsonResult updateModel(@PathVariable("id")Long id, @ModelAttribute Organization entity, BindingResult result, + HttpServletRequest request, ModelMap modelMap) throws Exception{ + return super.updateEntity(entity, result, modelMap); + } + + /*** + * 删除用户 + * @param id 用户ID + * @return + * @throws Exception + */ + @DeleteMapping("/{id}") + public JsonResult deleteModel(@PathVariable("id")Long id, HttpServletRequest request) throws Exception{ + return super.deleteEntity(id); + } + + @Override + protected BaseService getService() { + return departmentService; + } + +} diff --git a/diboot-example/src/main/java/com/diboot/example/controller/EmployeeController.java b/diboot-example/src/main/java/com/diboot/example/controller/EmployeeController.java new file mode 100644 index 0000000..1ac4209 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/controller/EmployeeController.java @@ -0,0 +1,161 @@ +package com.diboot.example.controller; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.diboot.core.controller.BaseCrudRestController; +import com.diboot.core.service.BaseService; +import com.diboot.core.util.BeanUtils; +import com.diboot.core.util.JSON; +import com.diboot.core.vo.JsonResult; +import com.diboot.core.vo.KeyValue; +import com.diboot.core.vo.Status; +import com.diboot.example.entity.Department; +import com.diboot.example.entity.Employee; +import com.diboot.example.entity.Organization; +import com.diboot.example.service.EmployeeService; +import com.diboot.example.vo.DepartmentVO; +import com.diboot.example.vo.EmployeeVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.List; + +/** + * Organization相关Controller + * @author Mazhicheng + * @version 2018/12/23 + * Copyright © www.dibo.ltd + */ +@RestController +@RequestMapping("/employee") +public class EmployeeController extends BaseCrudRestController { + + @Autowired + private EmployeeService employeeService; + + @GetMapping("/test") + public JsonResult test(HttpServletRequest request) throws Exception{ +// List userIdList = new ArrayList<>(); +// userIdList.add(1001L); +// userIdList.add(1003L); +// String sql = "SELECT user_id, role_id FROM user_role WHERE user_id IN (?)"; +// List> list = SqlExecutor.executeQuery(sql, userIdList); + + + List userIdList = new ArrayList<>(); + userIdList.add(1L); + QueryWrapper queryWrapper = new QueryWrapper(); + + //IN条件一定不能为空,否则就会删除整张表数据 + queryWrapper.in("id", userIdList); + System.out.println(JSON.stringify(queryWrapper)); + employeeService.deleteEntities(queryWrapper) + ; + userIdList.clear(); + queryWrapper = new QueryWrapper(); + queryWrapper.in("id", userIdList); + employeeService.deleteEntities(queryWrapper); + + return new JsonResult(); + } + + /*** + * 默认的分页实现 + *

+ * url参数示例: /list?_pageSize=20&_pageIndex=1&_orderBy=itemValue&type=GENDAR + *

+ * @return + * @throws Exception + */ + @GetMapping("/list") + public JsonResult getDefaultVOList(HttpServletRequest request) throws Exception{ + QueryWrapper queryWrapper = buildQuery(request); + return super.getEntityListWithPaging(request, queryWrapper, DepartmentVO.class); + } + + /*** + * 默认的分页实现 + *

+ * url参数示例: /listVo?page.size=20&page.index=1&page.orderBy=itemValue&type=GENDAR + *

+ * @return + * @throws Exception + */ + @GetMapping("/listVo") + public JsonResult getCustomVOList(HttpServletRequest request) throws Exception{ + QueryWrapper queryWrapper = buildQuery(request); + // 查询当前页的数据 + List entityList = employeeService.getEntityList(queryWrapper); + List voList = employeeService.getViewObjectList(entityList, EmployeeVO.class); + // 返回结果 + return new JsonResult(Status.OK, voList); + } + + @GetMapping("/kv") + public JsonResult getKVPairList(HttpServletRequest request){ + Wrapper wrapper = new QueryWrapper().lambda() + .select(Employee::getRealname, Employee::getId); + List list = employeeService.getKeyValueList(wrapper); + return new JsonResult(list); + } + + /*** + * 创建Entity + * @return + * @throws Exception + */ + @PostMapping("/") + public JsonResult createEntity(@ModelAttribute EmployeeVO viewObject, BindingResult result, HttpServletRequest request, ModelMap modelMap) + throws Exception{ + // 转换 + Employee entity = BeanUtils.convert(viewObject, Employee.class); + // 创建 + return super.createEntity(entity, result, modelMap); + } + + /*** + * 查询Entity + * @param id ID + * @return + * @throws Exception + */ + @GetMapping("/{id}") + public JsonResult getModel(@PathVariable("id")Long id, HttpServletRequest request, ModelMap modelMap) + throws Exception{ + EmployeeVO vo = employeeService.getViewObject(id, EmployeeVO.class); + return new JsonResult(vo); + } + + /*** + * 更新Entity + * @param id ID + * @return + * @throws Exception + */ + @PutMapping("/{id}") + public JsonResult updateModel(@PathVariable("id")Long id, @ModelAttribute Organization entity, BindingResult result, + HttpServletRequest request, ModelMap modelMap) throws Exception{ + return super.updateEntity(entity, result, modelMap); + } + + /*** + * 删除用户 + * @param id 用户ID + * @return + * @throws Exception + */ + @DeleteMapping("/{id}") + public JsonResult deleteModel(@PathVariable("id")Long id, HttpServletRequest request) throws Exception{ + return super.deleteEntity(id); + } + + @Override + protected BaseService getService() { + return employeeService; + } + +} diff --git a/diboot-example/src/main/java/com/diboot/example/controller/OrganizationController.java b/diboot-example/src/main/java/com/diboot/example/controller/OrganizationController.java new file mode 100644 index 0000000..0d09653 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/controller/OrganizationController.java @@ -0,0 +1,130 @@ +package com.diboot.example.controller; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.diboot.core.controller.BaseCrudRestController; +import com.diboot.core.service.BaseService; +import com.diboot.core.util.BeanUtils; +import com.diboot.core.vo.JsonResult; +import com.diboot.core.vo.KeyValue; +import com.diboot.core.vo.Status; +import com.diboot.example.entity.Organization; +import com.diboot.example.service.OrganizationService; +import com.diboot.example.vo.OrganizationVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.ui.ModelMap; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +/** + * Organization相关Controller + * @author Mazhicheng + * @version 2018/12/23 + * Copyright © www.dibo.ltd + */ +@RestController +@RequestMapping("/organization") +public class OrganizationController extends BaseCrudRestController { + + @Autowired + private OrganizationService organizationService; + + /*** + * 默认的分页实现 + *

+ * url参数示例: /list?_pageSize=20&_pageIndex=1&_orderBy=itemValue&type=GENDAR + *

+ * @return + * @throws Exception + */ + @GetMapping("/list") + public JsonResult getDefaultVOList(HttpServletRequest request) throws Exception{ + QueryWrapper queryWrapper = buildQuery(request); + return super.getEntityListWithPaging(request, queryWrapper, OrganizationVO.class); + } + + /*** + * 默认的分页实现 + *

+ * url参数示例: /listVo?page.size=20&page.index=1&page.orderBy=itemValue&type=GENDAR + *

+ * @return + * @throws Exception + */ + @GetMapping("/listVo") + public JsonResult getCustomVOList(HttpServletRequest request) throws Exception{ + QueryWrapper queryWrapper = buildQuery(request); + // 查询当前页的数据 + List entityList = organizationService.getEntityList(queryWrapper); + List voList = organizationService.getViewObjectList(entityList, OrganizationVO.class); + // 返回结果 + return new JsonResult(Status.OK, voList); + } + + @GetMapping("/kv") + public JsonResult getKVPairList(HttpServletRequest request){ + Wrapper wrapper = new QueryWrapper().lambda() + .select(Organization::getName, Organization::getId); + List list = organizationService.getKeyValueList(wrapper); + return new JsonResult(list); + } + + /*** + * 创建Entity + * @return + * @throws Exception + */ + @PostMapping("/") + public JsonResult createEntity(@ModelAttribute OrganizationVO viewObject, BindingResult result, HttpServletRequest request, ModelMap modelMap) + throws Exception{ + // 转换 + Organization entity = BeanUtils.convert(viewObject, Organization.class); + // 创建 + return super.createEntity(entity, result, modelMap); + } + + /*** + * 查询Entity + * @param id ID + * @return + * @throws Exception + */ + @GetMapping("/{id}") + public JsonResult getModel(@PathVariable("id")Long id, HttpServletRequest request, ModelMap modelMap) + throws Exception{ + OrganizationVO vo = organizationService.getViewObject(id, OrganizationVO.class); + return new JsonResult(vo); + } + + /*** + * 更新Entity + * @param id ID + * @return + * @throws Exception + */ + @PutMapping("/{id}") + public JsonResult updateModel(@PathVariable("id")Long id, @ModelAttribute Organization entity, BindingResult result, + HttpServletRequest request, ModelMap modelMap) throws Exception{ + return super.updateEntity(entity, result, modelMap); + } + + /*** + * 删除用户 + * @param id 用户ID + * @return + * @throws Exception + */ + @DeleteMapping("/{id}") + public JsonResult deleteModel(@PathVariable("id")Long id, HttpServletRequest request) throws Exception{ + return super.deleteEntity(id); + } + + @Override + protected BaseService getService() { + return organizationService; + } + +} diff --git a/diboot-example/src/main/java/com/diboot/example/entity/Department.java b/diboot-example/src/main/java/com/diboot/example/entity/Department.java new file mode 100644 index 0000000..cf586af --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/entity/Department.java @@ -0,0 +1,29 @@ +package com.diboot.example.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.diboot.core.entity.BaseExtEntity; +import lombok.Data; + +/** + * 定时任务 + * @author Mazhicheng + * @version v2.0 + * @date 2018/12/27 + */ +@Data +public class Department extends BaseExtEntity { + private static final long serialVersionUID = -4849732665419794547L; + + @TableField + private Long parentId; + + @TableField + private Long orgId; + + @TableField + private String name; + + @TableField + private String code; + +} \ No newline at end of file diff --git a/diboot-example/src/main/java/com/diboot/example/entity/Employee.java b/diboot-example/src/main/java/com/diboot/example/entity/Employee.java new file mode 100644 index 0000000..4291ca6 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/entity/Employee.java @@ -0,0 +1,34 @@ +package com.diboot.example.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.diboot.core.entity.BaseEntity; +import lombok.Data; + +import java.util.Date; + +/** + * 员工Entity + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/30 + */ +@Data +public class Employee extends BaseEntity { + private static final long serialVersionUID = 8980226078305249367L; + + @TableField + private Long departmentId; + + @TableField + private String realname; + + @TableField + private Date birthdate; + + @TableField + private String gender; + + @TableField + private String status; + +} diff --git a/diboot-example/src/main/java/com/diboot/example/entity/Organization.java b/diboot-example/src/main/java/com/diboot/example/entity/Organization.java new file mode 100644 index 0000000..c01feb6 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/entity/Organization.java @@ -0,0 +1,29 @@ +package com.diboot.example.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.diboot.core.entity.BaseEntity; +import lombok.Data; + +/** + * 单位Entity + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/5 + */ +@Data +public class Organization extends BaseEntity { + private static final long serialVersionUID = -5889309041570465909L; + + @TableField + private Long parentId; + + @TableField + private String name; + + @TableField + private String telphone; + + @TableField + private String address; + +} diff --git a/diboot-example/src/main/java/com/diboot/example/entity/Role.java b/diboot-example/src/main/java/com/diboot/example/entity/Role.java new file mode 100644 index 0000000..7d5d8b6 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/entity/Role.java @@ -0,0 +1,17 @@ +package com.diboot.example.entity; + +import com.diboot.core.entity.BaseEntity; + +/** + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/30 + */ +public class Role extends BaseEntity { + private static final long serialVersionUID = 3701095453152116088L; + + private String name; + + private String code; + +} diff --git a/diboot-example/src/main/java/com/diboot/example/entity/User.java b/diboot-example/src/main/java/com/diboot/example/entity/User.java new file mode 100644 index 0000000..9fc385a --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/entity/User.java @@ -0,0 +1,22 @@ +package com.diboot.example.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.diboot.core.entity.BaseEntity; +import lombok.Data; + +/** + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/30 + */ +@Data +public class User extends BaseEntity { + private static final long serialVersionUID = 3050761344045195972L; + + @TableField + private String username; + + @TableField + private String gender; + +} diff --git a/diboot-example/src/main/java/com/diboot/example/mapper/DepartmentMapper.java b/diboot-example/src/main/java/com/diboot/example/mapper/DepartmentMapper.java new file mode 100644 index 0000000..b755090 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/mapper/DepartmentMapper.java @@ -0,0 +1,14 @@ +package com.diboot.example.mapper; + +import com.diboot.core.mapper.BaseCrudMapper; +import com.diboot.example.entity.Department; + +/** + * 部门Mapper + * @author Mazhicheng + * @version 2018/12/22 + * Copyright © www.dibo.ltd + */ +public interface DepartmentMapper extends BaseCrudMapper { + +} \ No newline at end of file diff --git a/diboot-example/src/main/java/com/diboot/example/mapper/DepartmentMapper.xml b/diboot-example/src/main/java/com/diboot/example/mapper/DepartmentMapper.xml new file mode 100644 index 0000000..6157bb0 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/mapper/DepartmentMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/diboot-example/src/main/java/com/diboot/example/mapper/EmployeeMapper.java b/diboot-example/src/main/java/com/diboot/example/mapper/EmployeeMapper.java new file mode 100644 index 0000000..43496cd --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/mapper/EmployeeMapper.java @@ -0,0 +1,15 @@ +package com.diboot.example.mapper; + +import com.diboot.core.mapper.BaseCrudMapper; +import com.diboot.example.entity.Employee; + +/** + * 员工Mapper + * @author Mazhicheng + * @version 2018/12/22 + * Copyright © www.dibo.ltd + */ +public interface EmployeeMapper extends BaseCrudMapper { + +} + diff --git a/diboot-example/src/main/java/com/diboot/example/mapper/EmployeeMapper.xml b/diboot-example/src/main/java/com/diboot/example/mapper/EmployeeMapper.xml new file mode 100644 index 0000000..9bde515 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/mapper/EmployeeMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/diboot-example/src/main/java/com/diboot/example/mapper/OrganizationMapper.java b/diboot-example/src/main/java/com/diboot/example/mapper/OrganizationMapper.java new file mode 100644 index 0000000..5365ec7 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/mapper/OrganizationMapper.java @@ -0,0 +1,15 @@ +package com.diboot.example.mapper; + +import com.diboot.core.mapper.BaseCrudMapper; +import com.diboot.example.entity.Organization; + +/** + * 单位Mapper + * @author Mazhicheng + * @version 2018/12/22 + * Copyright © www.dibo.ltd + */ +public interface OrganizationMapper extends BaseCrudMapper { + +} + diff --git a/diboot-example/src/main/java/com/diboot/example/mapper/OrganizationMapper.xml b/diboot-example/src/main/java/com/diboot/example/mapper/OrganizationMapper.xml new file mode 100644 index 0000000..507ebdd --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/mapper/OrganizationMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/diboot-example/src/main/java/com/diboot/example/mapper/RoleMapper.java b/diboot-example/src/main/java/com/diboot/example/mapper/RoleMapper.java new file mode 100644 index 0000000..4ed3983 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/mapper/RoleMapper.java @@ -0,0 +1,15 @@ +package com.diboot.example.mapper; + +import com.diboot.core.mapper.BaseCrudMapper; +import com.diboot.example.entity.Role; + +/** + * 员工Mapper + * @author Mazhicheng + * @version 2018/12/22 + * Copyright © www.dibo.ltd + */ +public interface RoleMapper extends BaseCrudMapper { + +} + diff --git a/diboot-example/src/main/java/com/diboot/example/mapper/UserMapper.java b/diboot-example/src/main/java/com/diboot/example/mapper/UserMapper.java new file mode 100644 index 0000000..9497b5e --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/mapper/UserMapper.java @@ -0,0 +1,15 @@ +package com.diboot.example.mapper; + +import com.diboot.core.mapper.BaseCrudMapper; +import com.diboot.example.entity.User; + +/** + * 员工Mapper + * @author Mazhicheng + * @version 2018/12/22 + * Copyright © www.dibo.ltd + */ +public interface UserMapper extends BaseCrudMapper { + +} + diff --git a/diboot-example/src/main/java/com/diboot/example/mapper/mybatis-3-mapper.dtd b/diboot-example/src/main/java/com/diboot/example/mapper/mybatis-3-mapper.dtd new file mode 100644 index 0000000..dc7f99d --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/mapper/mybatis-3-mapper.dtd @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/diboot-example/src/main/java/com/diboot/example/service/DepartmentService.java b/diboot-example/src/main/java/com/diboot/example/service/DepartmentService.java new file mode 100644 index 0000000..d495e1f --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/service/DepartmentService.java @@ -0,0 +1,14 @@ +package com.diboot.example.service; + +import com.diboot.core.service.BaseService; +import com.diboot.example.entity.Department; + +/** + * 部门相关Service + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/30 + */ +public interface DepartmentService extends BaseService { + +} diff --git a/diboot-example/src/main/java/com/diboot/example/service/EmployeeService.java b/diboot-example/src/main/java/com/diboot/example/service/EmployeeService.java new file mode 100644 index 0000000..37d5a08 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/service/EmployeeService.java @@ -0,0 +1,20 @@ +package com.diboot.example.service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.diboot.core.service.BaseService; +import com.diboot.example.entity.Employee; + +import java.util.List; +import java.util.Map; + +/** + * 员工相关Service + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/5 + */ +public interface EmployeeService extends BaseService { + + List> testCustomQueryWrapper(QueryWrapper wrapper); + +} diff --git a/diboot-example/src/main/java/com/diboot/example/service/OrganizationService.java b/diboot-example/src/main/java/com/diboot/example/service/OrganizationService.java new file mode 100644 index 0000000..b89cfbb --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/service/OrganizationService.java @@ -0,0 +1,14 @@ +package com.diboot.example.service; + +import com.diboot.core.service.BaseService; +import com.diboot.example.entity.Organization; + +/** + * 单位相关Service + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/5 + */ +public interface OrganizationService extends BaseService { + +} diff --git a/diboot-example/src/main/java/com/diboot/example/service/RoleService.java b/diboot-example/src/main/java/com/diboot/example/service/RoleService.java new file mode 100644 index 0000000..95e7c0f --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/service/RoleService.java @@ -0,0 +1,14 @@ +package com.diboot.example.service; + +import com.diboot.core.service.BaseService; +import com.diboot.example.entity.Role; + +/** + * 员工相关Service + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/5 + */ +public interface RoleService extends BaseService { + +} diff --git a/diboot-example/src/main/java/com/diboot/example/service/UserService.java b/diboot-example/src/main/java/com/diboot/example/service/UserService.java new file mode 100644 index 0000000..bf613fe --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/service/UserService.java @@ -0,0 +1,14 @@ +package com.diboot.example.service; + +import com.diboot.core.service.BaseService; +import com.diboot.example.entity.User; + +/** + * 员工相关Service + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/5 + */ +public interface UserService extends BaseService { + +} diff --git a/diboot-example/src/main/java/com/diboot/example/service/impl/DepartmentServiceImpl.java b/diboot-example/src/main/java/com/diboot/example/service/impl/DepartmentServiceImpl.java new file mode 100644 index 0000000..468e7f1 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/service/impl/DepartmentServiceImpl.java @@ -0,0 +1,20 @@ +package com.diboot.example.service.impl; + +import com.diboot.core.service.impl.BaseServiceImpl; +import com.diboot.example.entity.Department; +import com.diboot.example.mapper.DepartmentMapper; +import com.diboot.example.service.DepartmentService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * 部门相关Service实现 + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/30 + */ +@Service +@Slf4j +public class DepartmentServiceImpl extends BaseServiceImpl implements DepartmentService { + +} diff --git a/diboot-example/src/main/java/com/diboot/example/service/impl/EmployeeServiceImpl.java b/diboot-example/src/main/java/com/diboot/example/service/impl/EmployeeServiceImpl.java new file mode 100644 index 0000000..2ef15ba --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/service/impl/EmployeeServiceImpl.java @@ -0,0 +1,28 @@ +package com.diboot.example.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.diboot.core.service.impl.BaseServiceImpl; +import com.diboot.example.entity.Employee; +import com.diboot.example.mapper.EmployeeMapper; +import com.diboot.example.service.EmployeeService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 员工相关Service + * @author Mazhicheng + * @version 2018/12/23 + * Copyright © www.dibo.ltd + */ +@Service +@Slf4j +public class EmployeeServiceImpl extends BaseServiceImpl implements EmployeeService { + + @Override + public List> testCustomQueryWrapper(QueryWrapper wrapper){ + return getBaseMapper().selectMaps(wrapper); + } +} diff --git a/diboot-example/src/main/java/com/diboot/example/service/impl/OrganizationServiceImpl.java b/diboot-example/src/main/java/com/diboot/example/service/impl/OrganizationServiceImpl.java new file mode 100644 index 0000000..a36cb55 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/service/impl/OrganizationServiceImpl.java @@ -0,0 +1,20 @@ +package com.diboot.example.service.impl; + +import com.diboot.core.service.impl.BaseServiceImpl; +import com.diboot.example.entity.Organization; +import com.diboot.example.mapper.OrganizationMapper; +import com.diboot.example.service.OrganizationService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * 单位相关Service实现 + * @author Mazhicheng + * @version 2018/12/23 + * Copyright © www.dibo.ltd + */ +@Service +@Slf4j +public class OrganizationServiceImpl extends BaseServiceImpl implements OrganizationService { + +} diff --git a/diboot-example/src/main/java/com/diboot/example/service/impl/RoleServiceImpl.java b/diboot-example/src/main/java/com/diboot/example/service/impl/RoleServiceImpl.java new file mode 100644 index 0000000..9f999ae --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/service/impl/RoleServiceImpl.java @@ -0,0 +1,20 @@ +package com.diboot.example.service.impl; + +import com.diboot.core.service.impl.BaseServiceImpl; +import com.diboot.example.entity.Role; +import com.diboot.example.mapper.RoleMapper; +import com.diboot.example.service.RoleService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * 员工相关Service + * @author Mazhicheng + * @version 2018/12/23 + * Copyright © www.dibo.ltd + */ +@Service +@Slf4j +public class RoleServiceImpl extends BaseServiceImpl implements RoleService { + +} diff --git a/diboot-example/src/main/java/com/diboot/example/service/impl/UserServiceImpl.java b/diboot-example/src/main/java/com/diboot/example/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..8f20906 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/service/impl/UserServiceImpl.java @@ -0,0 +1,20 @@ +package com.diboot.example.service.impl; + +import com.diboot.core.service.impl.BaseServiceImpl; +import com.diboot.example.entity.User; +import com.diboot.example.mapper.UserMapper; +import com.diboot.example.service.UserService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * 员工相关Service + * @author Mazhicheng + * @version 2018/12/23 + * Copyright © www.dibo.ltd + */ +@Service +@Slf4j +public class UserServiceImpl extends BaseServiceImpl implements UserService { + +} diff --git a/diboot-example/src/main/java/com/diboot/example/vo/DepartmentVO.java b/diboot-example/src/main/java/com/diboot/example/vo/DepartmentVO.java new file mode 100644 index 0000000..200f4c6 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/vo/DepartmentVO.java @@ -0,0 +1,29 @@ +package com.diboot.example.vo; + +import com.diboot.core.binding.annotation.BindEntityList; +import com.diboot.core.binding.annotation.BindField; +import com.diboot.example.entity.Department; +import com.diboot.example.entity.Organization; +import lombok.Data; + +import java.util.List; + +/** + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/5 + */ +@Data +public class DepartmentVO extends Department { + private static final long serialVersionUID = -362116388664907913L; + + @BindField(entity = Organization.class, field = "name", condition = "org_id=id") + private String orgName; + + @BindField(entity = Department.class, field = "name", condition = "parent_id=id") + private String parentName; + + @BindEntityList(entity = Department.class, condition = "id=parent_id") + private List children; + +} \ No newline at end of file diff --git a/diboot-example/src/main/java/com/diboot/example/vo/EmployeeVO.java b/diboot-example/src/main/java/com/diboot/example/vo/EmployeeVO.java new file mode 100644 index 0000000..0b8d6b8 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/vo/EmployeeVO.java @@ -0,0 +1,27 @@ +package com.diboot.example.vo; + +import com.diboot.core.binding.annotation.BindEntity; +import com.diboot.core.binding.annotation.BindField; +import com.diboot.core.binding.annotation.BindMetadata; +import com.diboot.example.entity.Department; +import com.diboot.example.entity.Employee; +import lombok.Data; + +/** + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/5 + */ +@Data +public class EmployeeVO extends Employee { + private static final long serialVersionUID = 2956966168209358800L; + + @BindEntity(entity = Department.class, condition="department_id=id") + private Department department; + + @BindMetadata(type="GENDER", field="gender") + private String genderLabel; + + @BindField(entity=Department.class, field="name", condition="this.department_id=id AND code IS NOT NULL") + private String deptName; +} \ No newline at end of file diff --git a/diboot-example/src/main/java/com/diboot/example/vo/OrganizationVO.java b/diboot-example/src/main/java/com/diboot/example/vo/OrganizationVO.java new file mode 100644 index 0000000..6ebce98 --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/vo/OrganizationVO.java @@ -0,0 +1,25 @@ +package com.diboot.example.vo; + +import com.diboot.core.binding.annotation.BindEntityList; +import com.diboot.core.binding.annotation.BindField; +import com.diboot.example.entity.Organization; +import lombok.Data; + +import java.util.List; + +/** + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/5 + */ +@Data +public class OrganizationVO extends Organization { + private static final long serialVersionUID = 9056449207962546696L; + + @BindField(entity = Organization.class, field = "name", condition = "this.parentId=id") + private String parentName; + + @BindEntityList(entity = Organization.class, condition = "this.id=parentId") + private List children; + +} diff --git a/diboot-example/src/main/java/com/diboot/example/vo/UserVO.java b/diboot-example/src/main/java/com/diboot/example/vo/UserVO.java new file mode 100644 index 0000000..81d7ddd --- /dev/null +++ b/diboot-example/src/main/java/com/diboot/example/vo/UserVO.java @@ -0,0 +1,33 @@ +package com.diboot.example.vo; + +import com.diboot.core.binding.annotation.BindEntityList; +import com.diboot.core.binding.annotation.BindField; +import com.diboot.core.binding.annotation.BindMetadata; +import com.diboot.example.entity.Organization; +import com.diboot.example.entity.Role; +import com.diboot.example.entity.User; +import lombok.Data; + +import java.util.List; + +/** + * @author Mazhicheng + * @version v2.0 + * @date 2019/1/30 + */ +@Data +public class UserVO extends User { + private static final long serialVersionUID = 3526115343377985725L; + + @BindMetadata(type="GENDER", field = "gender") + private String genderLabel; + + // 支持级联字段关联 + @BindField(entity = Organization.class, field="name", condition="this.departmentId=Department.id AND Department.orgId=id") + private String orgName; + + // 支持多-多Entity实体关联 + @BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id") + private List roleList; + +} \ No newline at end of file diff --git a/diboot-example/src/main/resources/application.properties b/diboot-example/src/main/resources/application.properties new file mode 100644 index 0000000..27dccc5 --- /dev/null +++ b/diboot-example/src/main/resources/application.properties @@ -0,0 +1,45 @@ +server.port=8080 +server.servlet.context-path=/example +#10秒超时 +spring.server.connectionTimeout=10000 +spring.server.protocol=org.apache.coyote.http11.Http11Nio2Protocol +spring.server.redirectPort=443 +spring.server.compression=on + +# spring config +spring.devtools.restart.enabled=true + +#datasource config +spring.datasource.url=jdbc:mysql://localhost:3306/diboot_example?characterEncoding=utf8 +spring.datasource.username=diboot +spring.datasource.password=123456 +spring.datasource.hikari.maximum-pool-size=5 +spring.datasource.hikari.data-source-properties.useInformationSchema=true +spring.datasource.hikari.data-source-properties.nullCatalogMeansCurrent=true +# 数据库驱动 +spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver + +#字符集utf-8 +spring.http.encoding.charset=UTF-8 +spring.http.encoding.enabled=true +spring.http.encoding.force=true + +# mybatis配置 +#mybatis.configuration.cache-enabled=false +#mybatis.configuration.lazy-loading-enabled=true +#mybatis.configuration.map-underscore-to-camel-case=true +#mybatis.configuration.multiple-result-sets-enabled=false +#mybatis.configuration.use-generated-keys=true +#mybatis.configuration.auto-mapping-behavior=full +#mybatis.configuration.default-statement-timeout=60 +#mybatis.configuration.log-impl=org.apache.ibatis.logging.log4j2.Log4j2Impl + +# logging config +logging.pattern.console=%clr{%d{MM-dd HH:mm:ss.SSS}}{faint} %clr{%5p} %clr{${PID}}{faint} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n%xwEx +logging.level.root=info +logging.level.org.apache=info +logging.level.org.hibernate.validator=info +logging.level.org.springframework=info +logging.level.com.zaxxer.hikari=info +logging.level.com.diboot=debug +logging.level.org.mybatis=debug \ No newline at end of file diff --git a/diboot-example/src/main/resources/banner.txt b/diboot-example/src/main/resources/banner.txt new file mode 100644 index 0000000..0bad421 --- /dev/null +++ b/diboot-example/src/main/resources/banner.txt @@ -0,0 +1,5 @@ + __ _ __ __ + ____/ / (_) / /_ ____ ____ / /_ + / __ / / / / __ \ / __ \ / __ \ / __/ +/ /_/ / / / / /_/ / / /_/ / / /_/ / / /_ +\__,_/ /_/ /_.___/ \____/ \____/ \__/