diff --git a/README.md b/README.md index a110940cd5627e22db3d533c15681bcbeb9b504e..72231eac8a03663c8ec2a9aa02c8b19c9b05e5a4 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,22 @@ GeneratorConfig generatorConfig = GeneratorConfig.builder() JointBlockGenerator.generator(generatorConfig); ``` -DataFill填充框架使用必须要引入aop +DataFill填充框架使用demo -```xml - - org.springframework.boot - spring-boot-starter-aop - +```java +// 数据填充执行器 +DataFillActuator actuator = new DataFillActuator<>(billDtos); +// 中心填充处理过程 +actuator.addProcess(DataFillProcessBuilder.builder() + .keyGetFun(billDto -> billDto.getCenter().getLmnId()) + .fillDataGetFun(lmnIds -> baseDataOrgFeignClient.queryOrgPageList(DataFillProcessBuilder.request(() -> { + OrgPageQry qry = new OrgPageQry(); + qry.setLmnIdIn(lmnIds.stream().map(String::valueOf).collect(Collectors.toList())); + qry.setPageSize((long)lmnIds.size()); + return qry; + })).getRows()) + .fillKeyGetFun(OrgDto::getLmnId) + .fillFun((billDto, orgDto) -> BeanUtil.copyProperties(orgDto, billDto.getCenter())).build()); +// 执行填充 +actuator.execute(); ``` \ No newline at end of file diff --git a/changelog.md b/changelog.md index ade0bd5bfbafb9d524d9a826c9ce396eb6281008..15407da3b9f74d2b2667dbd259918429d0e63881 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,12 @@ +# 0.30.0 +feature: +- flexible查询增加,特殊情况下动态查询`totalChapter[NotIn]=(6|7|8)` +refactor: +- 重构DataFill,不在使用注解,使用编码的方式增强代码可读性。 +- 大幅度精简框架,去除大量无用代码。 +fix: +- 修复代码生成器对审计字段的支持。 + # 0.29.0 feature: - batch mapper功能支持,支持批量插入,批量replace。 diff --git a/jointblock-spring-boot-autoconfigure/src/main/java/fun/easycode/joinblock/autoconfigure/JointBlockAutoConfiguration.java b/jointblock-spring-boot-autoconfigure/src/main/java/fun/easycode/joinblock/autoconfigure/JointBlockAutoConfiguration.java index 6253a7794dde5aad0d15b699f391f1989880bedd..32bab01cfa5ab2c1dde84ee67984bb748d36649e 100644 --- a/jointblock-spring-boot-autoconfigure/src/main/java/fun/easycode/joinblock/autoconfigure/JointBlockAutoConfiguration.java +++ b/jointblock-spring-boot-autoconfigure/src/main/java/fun/easycode/joinblock/autoconfigure/JointBlockAutoConfiguration.java @@ -3,22 +3,17 @@ package fun.easycode.joinblock.autoconfigure; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import fun.easycode.jointblock.core.AuditMetaObjectHandler; import fun.easycode.jointblock.core.ExecutorContext; -import fun.easycode.jointblock.core.ExecutorDataFillInterceptor; -import fun.easycode.jointblock.core.ExecutorDataFillPointcut; -import fun.easycode.jointblock.datafill.*; -import fun.easycode.jointblock.mybatisplus.JointBlockSqlInjector; +import fun.easycode.jointblock.core.JointBlockSqlInjector; +import fun.easycode.jointblock.core.UserHolder; +import fun.easycode.jointblock.util.BatchUtil; import lombok.extern.slf4j.Slf4j; -import org.springframework.aop.Advisor; -import org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; +import org.apache.ibatis.session.SqlSessionFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -32,9 +27,23 @@ import org.springframework.context.annotation.Configuration; @EnableConfigurationProperties(JointBlockProperties.class) @Configuration @Slf4j -public class JointBlockAutoConfiguration implements BeanFactoryAware{ +public class JointBlockAutoConfiguration { - private BeanFactory beanFactory; + @Bean + @ConditionalOnBean(SqlSessionFactory.class) + public BatchUtil batchUtil(SqlSessionFactory factory){ + return new BatchUtil(factory); + } + + /** + * 配置用户信息获取器,如果用户自己配置则默认配置失效 + * @return UserHolder + */ + @Bean + @ConditionalOnMissingBean(UserHolder.class) + public UserHolder userHolder(){ + return new UserHolder.DefaultUserHolder(); + } /** * 配置mybatis plus插件, 如果用户自己配置则默认配置失效 @@ -65,11 +74,6 @@ public class JointBlockAutoConfiguration implements BeanFactoryAware{ return new JointBlockSqlInjector(); } - @Bean - public DataCopy dataCopy(){ - return new DataCopy(); - } - /** * mybatis plus审计字段处理 * @return AuditMetaObjectHandler @@ -80,41 +84,6 @@ public class JointBlockAutoConfiguration implements BeanFactoryAware{ return new AuditMetaObjectHandler(); } - /** - * 代码填充织入执行器 - * @return DefaultBeanFactoryPointcutAdvisor - */ - @ConditionalOnClass(Advisor.class) - @Bean - public DefaultBeanFactoryPointcutAdvisor executorDataFillAdvisor() { - // 自动填充织入到Executor.execute方法 - DefaultBeanFactoryPointcutAdvisor advisor = new DefaultBeanFactoryPointcutAdvisor(); - advisor.setPointcut(executorDataFillPointcut()); - advisor.setAdvice(executorDataFillInterceptor()); - advisor.setBeanFactory(beanFactory); - return advisor; - } - - /** - * 执行器识别定义的切点 - * @return ExecutorDataFillPointcut - */ - @Bean - @ConditionalOnClass(Advisor.class) - public ExecutorDataFillPointcut executorDataFillPointcut(){ - return new ExecutorDataFillPointcut(); - } - - /** - * 执行器数据填充具体的切入逻辑 - * @return ExecutorDataFillInterceptor - */ - @Bean - @ConditionalOnClass(Advisor.class) - public ExecutorDataFillInterceptor executorDataFillInterceptor(){ - return new ExecutorDataFillInterceptor(); - } - /** * 执行器上下文,用于直接获取执行器进行调用 * @return ExecutorContext @@ -123,58 +92,4 @@ public class JointBlockAutoConfiguration implements BeanFactoryAware{ public ExecutorContext executorContext(){ return new ExecutorContext(); } - - // 下方是DataFill的配置 - - /** - * mybatis plus填充策略支持 - * @return MyBatisPlusDataFillStrategy - */ - @Bean - @ConditionalOnClass({BaseMapper.class}) - public MyBatisPlusDataFillStrategy myBatisPlusDataFillStrategy(){ - return new MyBatisPlusDataFillStrategy(); - } - - /** - * feign远程调用策略支持 - * @return FeignDataFillStrategy - */ - @Bean - @ConditionalOnClass(name = "org.springframework.cloud.openfeign.FeignContext") - public FeignDataFillStrategy feignDataFillStrategy(){ - return new FeignDataFillStrategy(); - } - - /** - * 数据填充具体执行器, 这里与业务执行器没有联系 - * @return DataFillExecutor - */ - @Bean - public DataFillExecutor dataFillExecutor(){ - return new DataFillExecutor(); - } - - /** - * 数据填充策略Context, 用于获取填充策略 - * @return DataFillStrategyContext - */ - @Bean - public DataFillStrategyContext dataFillStrategyContext(){ - return new DataFillStrategyContext(); - } - - /** - * 数据填充线程池管理 - * @return DataFillThreadPoolManager - */ - @Bean - public DataFillThreadPoolManager dataFillThreadPoolManager(){ - return new DataFillThreadPoolManager(); - } - - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = beanFactory; - } } diff --git a/jointblock-spring-boot-autoconfigure/src/main/java/fun/easycode/joinblock/autoconfigure/JointBlockWebAutoConfiguration.java b/jointblock-spring-boot-autoconfigure/src/main/java/fun/easycode/joinblock/autoconfigure/JointBlockWebAutoConfiguration.java index 9194a76d6a1db90561c8e322d35ab3234f68a02c..8c8aae1321e4cbab40ca06969368f9e40fee5673 100644 --- a/jointblock-spring-boot-autoconfigure/src/main/java/fun/easycode/joinblock/autoconfigure/JointBlockWebAutoConfiguration.java +++ b/jointblock-spring-boot-autoconfigure/src/main/java/fun/easycode/joinblock/autoconfigure/JointBlockWebAutoConfiguration.java @@ -2,17 +2,17 @@ package fun.easycode.joinblock.autoconfigure; import fun.easycode.jointblock.core.*; import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.time.LocalDateTime; +import java.util.List; /** * web应用才生效 @@ -23,6 +23,11 @@ import java.time.LocalDateTime; @Slf4j public class JointBlockWebAutoConfiguration implements WebMvcConfigurer, Jackson2ObjectMapperBuilderCustomizer{ + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(new QueryHandlerMethodArgumentResolver()); + } + /** * mvc枚举转换器 * @return EnumeratorConvertFactory @@ -57,15 +62,4 @@ public class JointBlockWebAutoConfiguration implements WebMvcConfigurer, Jackson jacksonObjectMapperBuilder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer()); jacksonObjectMapperBuilder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer()); } - - /** - * Cloud UserHolder 实例 - * @return UserHolder - */ - @ConditionalOnMissingClass({"org.springframework.security.core.Authentication"}) - @ConditionalOnMissingBean(UserHolder.class) - @Bean - public UserHolder cloudUserHolder(){ - return new CloudUserHolder(); - } } diff --git a/jointblock-spring-boot-parent/pom.xml b/jointblock-spring-boot-parent/pom.xml index 7c6ee2be14b9ea4855f28f2d59e058dc65622a86..c22bfecf98412d591e13e2e1e87638e42449b6eb 100644 --- a/jointblock-spring-boot-parent/pom.xml +++ b/jointblock-spring-boot-parent/pom.xml @@ -27,7 +27,7 @@ 10.10.1 2.1.5.RELEASE 20.0 - 0.29.0 + 0.30.0 2.14.2 @@ -64,6 +64,11 @@ mybatis-plus-boot-starter ${mybatis-plus.version} + + com.baomidou + mybatis-plus + ${mybatis-plus.version} + com.baomidou mybatis-plus-generator @@ -74,6 +79,11 @@ jointblock-spring-boot-starter ${jointblock.version} + + fun.easycode + jointblock-spring-boot + ${jointblock.version} + com.github.xiaoymin knife4j-spring-boot-starter @@ -137,6 +147,11 @@ lombok-mapstruct-binding 0.2.0 + + org.springframework.boot + spring-boot-configuration-processor + ${spring-boot.version} + diff --git a/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/adpter/api/ArticleRestController.java b/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/adpter/api/ArticleRestController.java index 8b8a717b36548553ed13bc38db3593527f91e663..869bfaf15d7f023cf8186e12d2fad975b7b9e54b 100644 --- a/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/adpter/api/ArticleRestController.java +++ b/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/adpter/api/ArticleRestController.java @@ -1,10 +1,9 @@ package fun.easycode.jointblock.adpter.api; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; -import fun.easycode.jointblock.core.Constant; import fun.easycode.jointblock.core.ExecutorContext; import fun.easycode.jointblock.core.PageDto; -import fun.easycode.jointblock.service.dto.ArticlePagingQry; +import fun.easycode.jointblock.service.dto.ArticlePageQry; import fun.easycode.jointblock.service.dto.data.ArticlePagingDto; import fun.easycode.jointblock.service.executor.ArticlePagingQryExe; import io.swagger.annotations.ApiImplicitParam; @@ -22,10 +21,10 @@ public class ArticleRestController { + "查看别人的能看机审通过、到达发布时间、并且不是草稿、不是驳回的阅读文章") @GetMapping("/articles") @ApiImplicitParams({ - @ApiImplicitParam(name = Constant.CURRENT_USER_ID_HEADER, paramType = "header"), +// @ApiImplicitParam(name = Constant.CURRENT_USER_ID_HEADER, paramType = "header"), @ApiImplicitParam(value = "用户ID", name = "userId") }) - public ResponseEntity> myReadList(ArticlePagingQry qry) { + public ResponseEntity> myReadList(ArticlePageQry qry) { return ResponseEntity.ok(ExecutorContext.get(ArticlePagingQryExe.class).execute(qry)); } } diff --git a/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/manager/UserBaseDto.java b/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/manager/UserBaseDto.java index 7ed34362ff021c4fc935283a5177f0537a1b8676..56716019a3c842e5a12fe4aee7c35184b235a8d5 100644 --- a/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/manager/UserBaseDto.java +++ b/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/manager/UserBaseDto.java @@ -1,7 +1,6 @@ package fun.easycode.jointblock.manager; -import fun.easycode.jointblock.datafill.FeignId; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -15,7 +14,6 @@ import java.io.Serializable; @NoArgsConstructor public class UserBaseDto implements Serializable { - @FeignId private String userId; private String nickname; private String avatar; diff --git a/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/service/dto/ArticlePagingQry.java b/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/service/dto/ArticlePageQry.java similarity index 59% rename from jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/service/dto/ArticlePagingQry.java rename to jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/service/dto/ArticlePageQry.java index 8792403d02667cf725bb9fa26a3f00201a6aa970..c1cc688bd307f81421b29795e043213e1ac0a0ee 100644 --- a/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/service/dto/ArticlePagingQry.java +++ b/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/service/dto/ArticlePageQry.java @@ -1,11 +1,11 @@ package fun.easycode.jointblock.service.dto; -import fun.easycode.jointblock.core.PagingQry; +import fun.easycode.jointblock.core.PageQry; import fun.easycode.jointblock.validator.IValidate; import lombok.Data; @Data -public class ArticlePagingQry extends PagingQry implements IValidate { +public class ArticlePageQry extends PageQry implements IValidate { // @NotBlank private String userId; } diff --git a/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/service/dto/data/ArticlePagingDto.java b/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/service/dto/data/ArticlePagingDto.java index 40b4155a23c867202b7318d6ec0217a12882bcb1..e4d8f464cc54480b57df82773a4b34abd93378f5 100644 --- a/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/service/dto/data/ArticlePagingDto.java +++ b/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/service/dto/data/ArticlePagingDto.java @@ -1,15 +1,11 @@ package fun.easycode.jointblock.service.dto.data; -import fun.easycode.jointblock.datafill.DataFill; -import fun.easycode.jointblock.datafill.DataParam; -import fun.easycode.jointblock.datafill.EnableDataFill; import fun.easycode.jointblock.manager.*; import lombok.Data; import java.util.List; @Data -@EnableDataFill public class ArticlePagingDto { private String id; private String title; @@ -19,45 +15,14 @@ public class ArticlePagingDto { private List img; private List coverImgSmall; // , methodName = "getUserByIds" - @DataFill(source = UserClient.class, value = "userId") private UserBaseDto user; - @DataFill(source = CollegeClient.class) private CollegeDto college; private Long createTime; - @DataFill(source = PraiseCountClient.class) private int praiseCount; - @DataFill(source = CommentCountClient.class, params = { - @DataParam( - name = "topicIds", - value = "#ids" - ) - }) private int commentCount; - @DataFill(source = BroweCountClient.class - , spEl = "#source.playAmount + #source.virtualPlayAmount") private int browseCount; - @DataFill(source = PraiseClient.class, params = { - @DataParam( - name = "userId", - value = "T(fun.easycode.jointblock.common.util.SecurityUtil).getCurrentUserId()" - ), - @DataParam( - name = "topicIds", - value = "#ids" - ) - }) private Boolean isPraise = false; private Boolean isAttention = false; - @DataFill(source = FavoriteClient.class, params = { - @DataParam( - name = "userId", - value = "T(fun.easycode.jointblock.common.util.SecurityUtil).getCurrentUserId()" - ), - @DataParam( - name = "topicIds", - value = "#ids" - ) - }) private Boolean isCollection = false; private String topicType = TopicType.ARTICLE.getValue(); private String type = WorkType.TEXT.getValue(); diff --git a/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/service/executor/ArticlePagingQryExe.java b/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/service/executor/ArticlePagingQryExe.java index b040375e2b79f1c05838af9b20003d7c0ff76bdc..89402be80c1ac56a09b306539a76dea1cdcdb10d 100644 --- a/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/service/executor/ArticlePagingQryExe.java +++ b/jointblock-spring-boot-test/src/main/java/fun/easycode/jointblock/service/executor/ArticlePagingQryExe.java @@ -6,9 +6,8 @@ import fun.easycode.jointblock.core.PageDto; import fun.easycode.jointblock.manager.ArticleClientDto; import fun.easycode.jointblock.manager.ArticleClientQuery; import fun.easycode.jointblock.manager.ArticleServiceClient; -import fun.easycode.jointblock.service.dto.ArticlePagingQry; +import fun.easycode.jointblock.service.dto.ArticlePageQry; import fun.easycode.jointblock.service.dto.data.ArticlePagingDto; -import fun.easycode.jointblock.service.dto.data.ArticlePagingDtoAssembler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; @@ -20,7 +19,7 @@ import java.util.Objects; * @author xuzhe */ @Service -public class ArticlePagingQryExe implements Executor> { +public class ArticlePagingQryExe implements Executor> { private final ArticleServiceClient articleServiceClient; @@ -30,7 +29,7 @@ public class ArticlePagingQryExe implements Executor execute(ArticlePagingQry qry) { + public PageDto execute(ArticlePageQry qry) { qry.validate(); @@ -49,7 +48,8 @@ public class ArticlePagingQryExe implements Executor pageDto = ExecutorContext.get(ArticlePagingQryExe.class).execute(qry); System.out.println(pageDto.getTotalSize()); } + + public static void main(String[] args) { + Pattern pattern = Pattern.compile("(\\w+?)(\\[.+?\\]=)([a-zA-Z0-9\\\\u4e00-\\\\u9fa5()|]+?),"); + Matcher matcher = pattern.matcher("lmnid[Eq]=164044345999532823,status[Eq]=1,"); + + Pattern operatorPattern = Pattern.compile("(?<=\\[).*(?=\\])"); +// Pattern operatorPattern = Pattern.compile("(?<=\\[).*(?=\\])"); + + + while (matcher.find()) { + System.out.print(matcher.group(1) + " "); + System.out.print(matcher.group(2)+ " "); + + Matcher operatorMatcher = operatorPattern.matcher(matcher.group(2)); + + System.err.print(operatorMatcher.find() ? operatorMatcher.group() : ""); + System.out.println(matcher.group(3) + " "); + } + + } } diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/AuditMetaObjectHandler.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/AuditMetaObjectHandler.java index c6a462c33e9bd5d7e5c72ef566d72bd4ebeaa61a..29f4d1e70c406b6248698f8ff4cbbd946300b6c8 100644 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/AuditMetaObjectHandler.java +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/AuditMetaObjectHandler.java @@ -24,9 +24,9 @@ public class AuditMetaObjectHandler implements MetaObjectHandler { public void insertFill(MetaObject metaObject) { LocalDateTime now = LocalDateTime.now(); this.strictInsertFill(metaObject, "createTime", ()-> now, LocalDateTime.class); - this.strictInsertFill(metaObject, "createBy", UserHolder.getInstance()::getUserId, String.class); + this.strictInsertFill(metaObject, "createBy",UserHolder.getInstance().getLoginUser()::getId, String.class); this.strictInsertFill(metaObject, "updateTime", ()-> now, LocalDateTime.class); - this.strictInsertFill(metaObject, "updateBy", UserHolder.getInstance()::getUserId, String.class); + this.strictInsertFill(metaObject, "updateBy", UserHolder.getInstance().getLoginUser()::getId, String.class); // 兼容date Date nowDate = new Date(); @@ -38,7 +38,7 @@ public class AuditMetaObjectHandler implements MetaObjectHandler { public void updateFill(MetaObject metaObject) { LocalDateTime now = LocalDateTime.now(); this.strictUpdateFill(metaObject, "updateTime", () -> now, LocalDateTime.class); - this.strictUpdateFill(metaObject, "updateBy", UserHolder.getInstance()::getUserId, String.class); + this.strictUpdateFill(metaObject, "updateBy", UserHolder.getInstance().getLoginUser()::getId, String.class); // 兼容date Date nowDate = new Date(); diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/BatchMapper.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/BatchMapper.java similarity index 90% rename from jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/BatchMapper.java rename to jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/BatchMapper.java index 36eb6f19f4e4892446fe077bb2d471e98cd33346..9b454695d7d9f00408e1f1111617001d383ab1d6 100644 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/BatchMapper.java +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/BatchMapper.java @@ -1,4 +1,4 @@ -package fun.easycode.jointblock.mybatisplus; +package fun.easycode.jointblock.core; import java.util.List; diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/exception/CheckException.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/CheckException.java similarity index 35% rename from jointblock-spring-boot/src/main/java/fun/easycode/jointblock/exception/CheckException.java rename to jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/CheckException.java index 9e4ad40dc35ec23beb05337cd7efaa912b259ed3..fe677db562fd2635306303346a944c953f5dd09f 100644 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/exception/CheckException.java +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/CheckException.java @@ -1,14 +1,11 @@ -package fun.easycode.jointblock.exception; +package fun.easycode.jointblock.core; -import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.Getter; /** * 通用逻辑异常 - * @Author: xuzhen97 - * @Date: 2023/03/22 + * @author xuzhen97 */ @EqualsAndHashCode(callSuper = true) @Data @@ -27,46 +24,53 @@ public class CheckException extends RuntimeException{ * @Date: 2023/03/22 */ private final Integer code; + private final String message; + /** - * 错误信息 - * @Author: xuzhen97 - * @Date: 2023/03/22 + * 通用逻辑异常 + * @param message 错误信息 */ - private final String msg; - - public CheckException(String msg){ - super(msg); + public CheckException(String message){ + super(message); this.code = DEFAULT_CODE; - this.msg = msg; + this.message = message; } - public CheckException(Integer code, String msg){ - super(msg); + /** + * 通用逻辑异常 + * @param code 错误码 + * @param message 错误信息 + */ + public CheckException(Integer code, String message){ + super(message); this.code = code; - this.msg = msg; + this.message = message; } /** - * 返回结果 - * @Author: xuzhen97 - * @Date: 2023/03/22 + * 返回错误信息 + * @return 错误信息 */ - public Result result(){ - return new Result(code, msg); - } - - public static Result toResult(Integer code, String msg){ - return new Result(code, msg); + public ErrorDto error(){ + return new ErrorDto(code, message); } - public static Result toResult(String msg){ - return new Result(DEFAULT_CODE, msg); + /** + * 返回错误信息 + * @param code 错误码 + * @param message 错误信息 + * @return 错误信息 + */ + public static ErrorDto error(Integer code, String message){ + return new ErrorDto(code, message); } - @AllArgsConstructor - @Getter - public static class Result{ - private final Integer code; - private final String msg; + /** + * 返回错误信息 + * @param msg 错误信息 + * @return 错误信息 + */ + public static ErrorDto error(String msg){ + return new ErrorDto(DEFAULT_CODE, msg); } } diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/CloudUserHolder.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/CloudUserHolder.java deleted file mode 100644 index 3e3361bda0b83ab8b97052cd17c6914e4817717a..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/CloudUserHolder.java +++ /dev/null @@ -1,19 +0,0 @@ -package fun.easycode.jointblock.core; - -import fun.easycode.jointblock.exception.CheckException; -import org.springframework.util.StringUtils; - -/** - * 微服务UserHolder实例 - * @author xuzhe - */ -public class CloudUserHolder extends UserHolder{ - @Override - public String getUserId() { - String currUserId = RequestHolder.getRequest().getHeader("x-currUserId"); - if(StringUtils.isEmpty(currUserId)){ - throw new CheckException("x-currUserId header未传入!"); - } - return currUserId; - } -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/Condition.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/Condition.java deleted file mode 100644 index ad50f41029738d7677b491a54d979d0ebe2fb204..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/Condition.java +++ /dev/null @@ -1,14 +0,0 @@ -package fun.easycode.jointblock.core; - -import java.lang.annotation.*; - -/** - * 条件注解,在修改的时候标识这是条件,不是set - * @author xuzhe - */ -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD}) -public @interface Condition { - -} \ No newline at end of file diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/Constant.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/Constant.java deleted file mode 100644 index 04b0f877c9e516b9c0e34c3773e038835e9ca59d..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/Constant.java +++ /dev/null @@ -1,40 +0,0 @@ -package fun.easycode.jointblock.core; - -/** - * 框架常量 - * @author xuzhe - */ -public interface Constant { - - /** - * 当前页 - */ - int CURRENT = 1; - /** - * 页大小 - */ - int SIZE = 10; - - /** - * 总条数 header key - */ - String TOTAL_SIZE_HEADER = "x-pagination-count"; - - /** - * 总页数 header key - */ - String TOTAL_PAGE_HEADER = "x-pagination-pages"; - /** - * 页大小 header key - */ - String SIZE_HEADER = "x-pagination-size"; - /** - * 当前页 header key - */ - String CURRENT_HEADER = "x-pagination-number"; - - /** - * 当前操作人 header key - */ - String CURRENT_USER_ID_HEADER = "x-currUserId"; -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/DeleteResultConvert.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/DeleteResultConvert.java deleted file mode 100644 index 7b94781d6aaf6d7b8a8919e6e5b901e7519dcebc..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/DeleteResultConvert.java +++ /dev/null @@ -1,15 +0,0 @@ -package fun.easycode.jointblock.core; - -/** - * 删除返回结果转换 - * @author xuzhe - */ -@FunctionalInterface -public interface DeleteResultConvert { - /** - * 数据库实体T转换为返回结果R - * @param entity 数据库实体 - * @return R - */ - R to(T entity); -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/DynamicOperate.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/DynamicOperate.java index 73c4804f7e4405258be60b6b1164704069f41afc..3e1ff19e6e3c40c60ef2f3b757f705125101a7fa 100644 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/DynamicOperate.java +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/DynamicOperate.java @@ -1,21 +1,20 @@ package fun.easycode.jointblock.core; import cn.hutool.core.annotation.AnnotationUtil; -import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ReflectUtil; import com.baomidou.mybatisplus.core.conditions.AbstractWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import fun.easycode.jointblock.exception.CheckException; -import fun.easycode.jointblock.util.LogUtil; import lombok.extern.slf4j.Slf4j; +import static fun.easycode.jointblock.util.CamelUnderUtil.*; import java.lang.reflect.Field; -import java.time.LocalDateTime; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; import java.util.stream.Collectors; /** @@ -24,59 +23,6 @@ import java.util.stream.Collectors; */ @Slf4j public final class DynamicOperate { - - /** - * 等于 - */ - public static final String EQ = "Eq"; - /** - * 模糊查询 - */ - public static final String LIKE = "Like"; - /** - * 模糊查询左 - */ - public static final String LIKE_LEFT = "LikeLeft"; - /** - * 模糊查询右 - */ - public static final String LIKE_RIGHT = "LikeRight"; - /** - * 大于 - */ - public static final String GT = "Gt"; - /** - * 小于 - */ - public static final String LT = "Lt"; - /** - * 大于等于 - */ - public static final String GE = "Ge"; - /** - * 小于等于 - */ - public static final String LE = "Le"; - - /** - * in 集合查询 - */ - public static final String IN = "In"; - - /** - * not in 集合查询 - */ - public static final String NOT_IN = "NotIn"; - - /** - * is null 查询 - */ - public static final String IS_NULL = "IsNull"; - - /** - * 排序 - */ - public static final String SORT = "Sort"; /** * 跳过字段, queryWrapper中跳过 */ @@ -110,7 +56,7 @@ public final class DynamicOperate { * @param 查询指令 * @param 转换的返回结果 */ - public static PageDto page(K qry + public static PageDto page(K qry , BaseMapper mapper , QueryResultConvert resultConvert){ QueryWrapper queryWrapper = queryWrapper(qry); @@ -118,230 +64,6 @@ public final class DynamicOperate { return PageDto.toPageDto(page, resultConvert::to); } - /** - * 修改工具方法,注意此方法cmd中的值必须传入,如果未传,数据库会被设置为Null - * @param cmd 修改指令实体 - * @param mapper mybatis mapper - * @param callback 修改回调,用于自定义修改逻辑 - * @param resultConvert 修改实体返回转换 - * @param skipFields 跳过字段,用于那些需要自定义逻辑的字段配合修改回调 - * @return 返回修改后的实体 - * @param 数据库实体 - * @param 修改指令 - * @param 修改后的实体 - */ - public static Optional update(K cmd, BaseMapper mapper - , UpdateCallback callback - , UpdateResultConvert resultConvert - , String... skipFields) { - return update(cmd, mapper, callback, resultConvert, false, false, skipFields); - } - - /** - * 修改工具方法,注意此方法cmd中的值必须传入,如果未传,数据库会被设置为Null - * 此方法进行审计信息的填充update_by、update_time - * 单个对象修改,根据条件必须唯一锁定一条数据 - * - * @param cmd 修改指令实体 - * @param mapper mybatis mapper - * @param callback 修改回调,用于自定义修改逻辑 - * @param resultConvert 修改实体返回转换 - * @param skipFields 跳过字段,用于那些需要自定义逻辑的字段配合修改回调 - * @param 数据库实体 - * @param 修改指令 - * @param 修改后的实体 - * @return 返回修改后的实体 - */ - public static Optional updateNotAudit(K cmd, BaseMapper mapper - , UpdateCallback callback - , UpdateResultConvert resultConvert - , String... skipFields) { - return update(cmd, mapper, callback, resultConvert, false, true, skipFields); - } - - /** - * 修改工具方法 - * - * @param cmd 修改指令实体 - * @param mapper mybatis mapper - * @param callback 修改回调,用于自定义修改逻辑 - * @param resultConvert 修改实体返回转换 - * @param skipEmpty 是否跳过cmd的empty字段,如果要跳过,那么不传入的字段,或者是""这种都不进行修改 - * @param skipAudit 是否跳过审计字段update_by和update_time就不自动传参 - * @param skipFields 跳过字段,用于那些需要自定义逻辑的字段配合修改回调 - * @param 数据库实体 - * @param 修改指令 - * @param 修改后的实体 - * @return 返回修改后的实体 - */ - private static Optional update(K cmd - , BaseMapper mapper - , UpdateCallback callback - , UpdateResultConvert resultConvert - , boolean skipEmpty - , boolean skipAudit - , String... skipFields) { - - Map fieldMap = ReflectUtil.getFieldMap(cmd.getClass()); - List skipFieldList = Arrays.asList(skipFields); - // 生成修改id, 用来记录修改的具体情况,用于打印日志 - String updateId = IdUtil.getSnowflakeNextIdStr(); - UpdateWrapper wrapper = new LogUpdateWrapper<>(updateId); - Queue updateQueue = new LinkedList<>(); - - for (Map.Entry entry : fieldMap.entrySet()) { - // 如果字段要跳过则方法对这个字段不进行任何处理 - if (skipFieldList.stream().anyMatch(skipField -> ObjectUtil.equal(skipField, entry.getKey()))) { - continue; - } - // 在修改的过程中如果被标记条件则称为修改条件 - if (AnnotationUtil.hasAnnotation(entry.getValue(), Condition.class)) { - wrapper(cmd, entry.getKey(), entry.getValue(), wrapper); - } else { - // 如果没有被标记为条件则进行修改 - String fieldName = getDatabaseFieldName(-1, entry.getKey(), entry.getValue()); - Object fieldValue = ReflectUtil.getFieldValue(cmd, entry.getValue()); - if (ObjectUtil.isNotEmpty(fieldValue)) { - updateQueue.offer(fieldName); - updateQueue.offer(fieldValue); - } else if (!skipEmpty) { - // 如果设置跳过empty则为empty的时候不进行修改 - updateQueue.offer(fieldName); - updateQueue.offer(null); - } - } - } - T entity = mapper.selectOne(wrapper); - if (entity != null) { - if (callback != null) { - callback.callback(cmd, wrapper); - } - while (!updateQueue.isEmpty()) { - String updateFieldName = (String) updateQueue.remove(); - Object updateFieldValue = updateQueue.remove(); - wrapper.set(updateFieldName, updateFieldValue); - } - - // 当skipAudit == true, 跳过审计填充 - if (!skipAudit) { - // 审计信息支持 - wrapper.set(camel2under("lastModifiedDate"), LocalDateTime.now()); - wrapper.set(camel2under("lastModifiedBy"), UserHolder.getInstance().getUserId()); - } - mapper.update(null, wrapper); - log.info("update {}: op {} info {}." - , entity.getClass().getSimpleName(), UserHolder.getInstance().getUserId() - , LogUtil.getFieldUpdateLog(updateId, entity)); - if (resultConvert == null) { - return Optional.empty(); - } else { - return Optional.of(resultConvert.to(entity)); - } - } - throw new CheckException("要修改的实体不存在!"); - } - - /** - * 批量修改,自动填充审计lastModifiedDate lastModifiedBy - * 如果值为null会被设置成null - * - * @param cmd - * @param mapper - * @param skipFields - * @param - * @param - */ - public static void updateBatch(K cmd - , BaseMapper mapper - , UpdateCallback callback - , String... skipFields) { - UpdateWrapper updateWrapper = updateWrapper(cmd, false, false, false, skipFields); - updateWrapper = callback.callback(cmd, updateWrapper); - String whereSqlSegment = getWhereSqlSegment(updateWrapper); - String setSql = getSetSqlSegment(updateWrapper); - mapper.update(null, updateWrapper); - log.info("update {} : condition {}, set {}, op {}.", mapper.getClass().getSimpleName() - , whereSqlSegment - , setSql - , UserHolder.getInstance().getUserId()); - } - - /** - * 批量修改,自动填充审计lastModifiedDate lastModifiedBy - * 跳过空值,null不会往数据库设置 - * - * @param cmd - * @param mapper - * @param skipFields - * @param - * @param - */ - public static void updateBatchSkipEmpty(K cmd - , BaseMapper mapper - , UpdateCallback callback - , String... skipFields) { - UpdateWrapper updateWrapper = updateWrapper(cmd, false, false, false, skipFields); - updateWrapper = callback.callback(cmd, updateWrapper); - String whereSqlSegment = getWhereSqlSegment(updateWrapper); - String setSql = getSetSqlSegment(updateWrapper); - mapper.update(null, updateWrapper); - log.info("update {} : condition {}, set {}, op {}.", mapper.getClass().getSimpleName() - , whereSqlSegment - , setSql - , UserHolder.getInstance().getUserId()); - } - - /** - * 批量修改跳过审计,但是不跳过空值,值为null数据库为null - * lastModifiedDate lastModifiedBy - * - * @param cmd - * @param mapper - * @param skipFields - * @param - * @param - */ - public static void updateBatchNotAudit(K cmd - , BaseMapper mapper - , UpdateCallback callback - , String... skipFields) { - UpdateWrapper updateWrapper = updateWrapper(cmd, false, false, true, skipFields); - updateWrapper = callback.callback(cmd, updateWrapper); - String whereSqlSegment = getWhereSqlSegment(updateWrapper); - String setSql = getSetSqlSegment(updateWrapper); - mapper.update(null, updateWrapper); - log.info("update {} : condition {}, set {}, op {}.", mapper.getClass().getSimpleName() - , whereSqlSegment - , setSql - , UserHolder.getInstance().getUserId()); - } - - /** - * 批量修改跳过审计 - * lastModifiedDate lastModifiedBy - * 跳过空值null set - * - * @param cmd - * @param mapper - * @param skipFields - * @param - * @param - */ - public static void updateBatchNotAuditSkipEmpty(K cmd - , BaseMapper mapper - , UpdateCallback callback - , String... skipFields) { - UpdateWrapper updateWrapper = updateWrapper(cmd, false, true, true, skipFields); - updateWrapper = callback.callback(cmd, updateWrapper); - String whereSqlSegment = getWhereSqlSegment(updateWrapper); - String setSql = getSetSqlSegment(updateWrapper); - mapper.update(null, updateWrapper); - log.info("update {} : condition {}, set {}, op {}.", mapper.getClass().getSimpleName() - , whereSqlSegment - , setSql - , UserHolder.getInstance().getUserId()); - } - /** * 根据实体命令生成QueryWrapper条件 * @@ -397,94 +119,6 @@ public final class DynamicOperate { return queryWrapper; } - /** - * 根据指令生成UpdateWrapper - * 如果skipSet=true, 则skipEmpty设置无效 - * - * @param cmd 对象指令 - * @param skipSet 是否跳过set, 跳过set仅仅生成用于查询的wrapper - * @param skipEmpty 是否跳过empty - * , 如果跳过当值为Null或者size=0 以及""这种的时候不进行处理,反之设置为null - * @param skipFields 要跳过的字段,跳过的字段方法就不会处理这种数据 - * @param 实体类型 - * @return UpdateWrapper - */ - public static UpdateWrapper updateWrapper(Object cmd - , boolean skipSet - , boolean skipEmpty - , boolean skipAudit - , String... skipFields) { - - Map fieldMap = ReflectUtil.getFieldMap(cmd.getClass()); - List skipFieldList = Arrays.asList(skipFields); - - UpdateWrapper updateWrapper = new UpdateWrapper<>(); - boolean isCondition = false; - for (Map.Entry entry : fieldMap.entrySet()) { - // 如果字段要跳过则方法对这个字段不进行任何处理 - if (skipFieldList.stream().anyMatch(skipField -> ObjectUtil.equal(skipField, entry.getKey()))) { - continue; - } - // 在修改的过程中如果被标记条件则称为修改条件 - if (AnnotationUtil.hasAnnotation(entry.getValue(), Condition.class)) { - wrapper(cmd, entry.getKey(), entry.getValue(), updateWrapper); - isCondition = true; - } else if (!skipSet) { - // 如果没有被标记为条件则进行修改 - String fieldName = getDatabaseFieldName(-1, entry.getKey(), entry.getValue()); - Object fieldValue = ReflectUtil.getFieldValue(cmd, entry.getValue()); - if (ObjectUtil.isNotEmpty(fieldValue)) { - updateWrapper.set(fieldName, fieldValue); - } else if (!skipEmpty) { - updateWrapper.set(fieldName, null); - } - // 是否跳过审计信息支持 - if (!skipAudit) { - // 审计信息支持 - updateWrapper.set(camel2under("lastModifiedDate"), LocalDateTime.now()); - updateWrapper.set(camel2under("lastModifiedBy"), UserHolder.getInstance().getUserId()); - } - } - } - if (!isCondition) { - throw new CheckException("updateWrapper方法调用必须存在条件@Condition!"); - } - return updateWrapper; - } - - /** - * 获取where sql段用于打印 - * @param wrapper UpdateWrapper - * @return sql - * @param T - */ - private static String getWhereSqlSegment(UpdateWrapper wrapper){ - String sqlSegment = wrapper.getSqlSegment(); - - for(Map.Entry entry : wrapper.getParamNameValuePairs().entrySet()){ - String repKey = "#{ew.paramNameValuePairs."+entry.getKey()+"}"; - sqlSegment = sqlSegment.replace(repKey, String.valueOf(entry.getValue())); - } - return sqlSegment; - } - - /** - * 获取where set sql段用于打印 - * - * @param wrapper UpdateWrapper - * @param T - * @return sql - */ - private static String getSetSqlSegment(UpdateWrapper wrapper) { - String sqlSet = wrapper.getSqlSet(); - - for (Map.Entry entry : wrapper.getParamNameValuePairs().entrySet()) { - String repKey = "#{ew.paramNameValuePairs." + entry.getKey() + "}"; - sqlSet = sqlSet.replace(repKey, String.valueOf(entry.getValue())); - } - return sqlSet; - } - /** * 给wrapper增加条件 * @param o 对象例如qry cmd @@ -494,64 +128,64 @@ public final class DynamicOperate { */ private static void wrapper(Object o, String key, Field field, AbstractWrapper wrapper) { - if (isConditional(key, EQ)) { - int index = lastIndexOf(key, EQ); + if (isConditional(key, OperateSymbol.EQ.getValue())) { + int index = lastIndexOf(key, OperateSymbol.EQ.getValue()); String fieldName = getDatabaseFieldName(index, key, field); Object value = ReflectUtil.getFieldValue(o, field); if (ObjectUtil.isNotEmpty(value)) { wrapper.eq(fieldName, transformValue(value)); } - } else if (isConditional(key, LIKE)) { - int index = lastIndexOf(key, LIKE); + } else if (isConditional(key, OperateSymbol.LIKE.getValue())) { + int index = lastIndexOf(key, OperateSymbol.LIKE.getValue()); String fieldName = getDatabaseFieldName(index, key, field); Object value = ReflectUtil.getFieldValue(o, key); if (ObjectUtil.isNotEmpty(value)) { wrapper.like(fieldName, transformValue(value)); } - } else if (isConditional(key, LIKE_LEFT)) { - int index = lastIndexOf(key, LIKE_LEFT); + } else if (isConditional(key, OperateSymbol.LIKE_LEFT.getValue())) { + int index = lastIndexOf(key, OperateSymbol.LIKE_LEFT.getValue()); String fieldName = getDatabaseFieldName(index, key, field); Object value = ReflectUtil.getFieldValue(o, field); if (ObjectUtil.isNotEmpty(value)) { wrapper.likeLeft(fieldName, transformValue(value)); } - } else if (isConditional(key, LIKE_RIGHT)) { - int index = lastIndexOf(key, LIKE_RIGHT); + } else if (isConditional(key, OperateSymbol.LIKE_RIGHT.getValue())) { + int index = lastIndexOf(key, OperateSymbol.LIKE_RIGHT.getValue()); String fieldName = getDatabaseFieldName(index, key, field); Object value = ReflectUtil.getFieldValue(o, field); if (ObjectUtil.isNotEmpty(value)) { wrapper.likeRight(fieldName, transformValue(value)); } - } else if (isConditional(key, GT)) { - int index = lastIndexOf(key, GT); + } else if (isConditional(key, OperateSymbol.GT.getValue())) { + int index = lastIndexOf(key, OperateSymbol.GT.getValue()); String fieldName = getDatabaseFieldName(index, key, field); Object value = ReflectUtil.getFieldValue(o, field); if (ObjectUtil.isNotEmpty(value)) { wrapper.gt(fieldName, transformValue(value)); } - } else if (isConditional(key, LT)) { - int index = lastIndexOf(key, LT); + } else if (isConditional(key, OperateSymbol.LT.getValue())) { + int index = lastIndexOf(key, OperateSymbol.LT.getValue()); String fieldName = getDatabaseFieldName(index, key, field); Object value = ReflectUtil.getFieldValue(o, field); if (ObjectUtil.isNotEmpty(value)) { wrapper.lt(fieldName, transformValue(value)); } - } else if (isConditional(key, GE)) { - int index = lastIndexOf(key, GE); + } else if (isConditional(key, OperateSymbol.GE.getValue())) { + int index = lastIndexOf(key, OperateSymbol.GE.getValue()); String fieldName = getDatabaseFieldName(index, key, field); Object value = ReflectUtil.getFieldValue(o, field); if (ObjectUtil.isNotEmpty(value)) { wrapper.ge(fieldName, transformValue(value)); } - } else if (isConditional(key, LE)) { - int index = lastIndexOf(key, LE); + } else if (isConditional(key, OperateSymbol.LE.getValue())) { + int index = lastIndexOf(key, OperateSymbol.LE.getValue()); String fieldName = getDatabaseFieldName(index, key, field); Object value = ReflectUtil.getFieldValue(o, field); if (ObjectUtil.isNotEmpty(value)) { wrapper.le(fieldName, transformValue(value)); } - } else if (isConditional(key, IS_NULL)) { - int index = lastIndexOf(key, IS_NULL); + } else if (isConditional(key, OperateSymbol.IS_NULL.getValue())) { + int index = lastIndexOf(key, OperateSymbol.IS_NULL.getValue()); String fieldName = getDatabaseFieldName(index, key, field); Object value = ReflectUtil.getFieldValue(o, field); if (value != null && !(value instanceof Boolean)) { @@ -564,22 +198,22 @@ public final class DynamicOperate { wrapper.isNotNull(fieldName); } } - } else if (isConditional(key, NOT_IN)) { - int index = lastIndexOf(key, NOT_IN); + } else if (isConditional(key, OperateSymbol.NOT_IN.getValue())) { + int index = lastIndexOf(key, OperateSymbol.NOT_IN.getValue()); String fieldName = getDatabaseFieldName(index, key, field); Object value = ReflectUtil.getFieldValue(o, field); if (ObjectUtil.isNotEmpty(value)) { wrapper.notIn(fieldName, (Collection) value); } - } else if (isConditional(key, IN)) { - int index = lastIndexOf(key, IN); + } else if (isConditional(key, OperateSymbol.IN.getValue())) { + int index = lastIndexOf(key, OperateSymbol.IN.getValue()); String fieldName = getDatabaseFieldName(index, key, field); Object value = ReflectUtil.getFieldValue(o, field); if (ObjectUtil.isNotEmpty(value)) { wrapper.in(fieldName, (Collection) value); } - } else if (isConditional(key, SORT)) { - int index = lastIndexOf(key, SORT); + } else if (isConditional(key, OperateSymbol.SORT.getValue())) { + int index = lastIndexOf(key, OperateSymbol.SORT.getValue()); String fieldName = getDatabaseFieldName(index, key, field); Object value = ReflectUtil.getFieldValue(o, field); // 必须传入值,不是DESC其他值均为ASC @@ -651,35 +285,4 @@ public final class DynamicOperate { return camel2under(original); } - /** - * 功能:驼峰命名转下划线命名 - * 小写和大写紧挨一起的地方,加上分隔符,然后全部转小写 - */ - public static String camel2under(String c) - { - String separator = "_"; - c = c.replaceAll("([a-z])([A-Z])", "$1"+separator+"$2").toLowerCase(); - return c; - } - - /** - * 功能:下划线命名转驼峰命名 - * 将下划线替换为空格,将字符串根据空格分割成数组,再将每个单词首字母大写 - * @param s - * @return - */ - public static String under2camel(String s) - { - String separator = "_"; - String under=""; - s = s.toLowerCase().replace(separator, " "); - String sarr[]=s.split(" "); - for(int i=0;i> { @Override public void serialize(Enumerator enumerator, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { - jsonGenerator.writeStartObject(); - jsonGenerator.writeObjectField("value", enumerator.getValue()); - jsonGenerator.writeStringField("desc", enumerator.getDesc()); - jsonGenerator.writeEndObject(); +// jsonGenerator.writeStartObject(); +// jsonGenerator.writeObjectField("value", enumerator.getValue()); +// jsonGenerator.writeStringField("desc", enumerator.getDesc()); +// jsonGenerator.writeEndObject(); + jsonGenerator.writeObject(enumerator.getValue()); } } diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/ExecutorDataFillInterceptor.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/ExecutorDataFillInterceptor.java deleted file mode 100644 index 2fb1208e4cccd9438a2c1d798885d244381f6b5e..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/ExecutorDataFillInterceptor.java +++ /dev/null @@ -1,24 +0,0 @@ -package fun.easycode.jointblock.core; - -import fun.easycode.jointblock.datafill.DataFillExecutor; -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; - -import javax.annotation.Resource; - -/** - * interceptor 用于数据填充 - * @author xuzhe - */ -public class ExecutorDataFillInterceptor implements MethodInterceptor { - - @Resource - private DataFillExecutor dataFillExecutor; - - @Override - public Object invoke(MethodInvocation methodInvocation) throws Throwable { - Object result = methodInvocation.proceed(); - dataFillExecutor.execute(result); - return result; - } -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/ExecutorDataFillPointcut.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/ExecutorDataFillPointcut.java deleted file mode 100644 index b46e1fc53db47f54421527348140e41768122791..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/ExecutorDataFillPointcut.java +++ /dev/null @@ -1,47 +0,0 @@ -package fun.easycode.jointblock.core; - -import cn.hutool.core.util.ClassUtil; -import org.springframework.aop.ClassFilter; -import org.springframework.aop.MethodMatcher; -import org.springframework.aop.Pointcut; - -import java.lang.reflect.Method; -import java.util.Objects; - -/** - * 自定义aop切点 - * 当是Executor时切入,execute时执行逻辑 - * @author xuzhe - */ -public class ExecutorDataFillPointcut implements Pointcut { - @Override - public ClassFilter getClassFilter() { - return new ClassFilter() { - @Override - public boolean matches(Class aClass) { - - return ClassUtil.isAssignable(Executor.class, aClass); - } - }; - } - - @Override - public MethodMatcher getMethodMatcher() { - return new MethodMatcher() { - @Override - public boolean matches(Method method, Class aClass) { - return Objects.equals("execute", method.getName()); - } - - @Override - public boolean isRuntime() { - return false; - } - - @Override - public boolean matches(Method method, Class aClass, Object... objects) { - return false; - } - }; - } -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/InsertCallback.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/InsertCallback.java deleted file mode 100644 index b7cb3cb360556258575bea18d626255bc76f2c68..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/InsertCallback.java +++ /dev/null @@ -1,16 +0,0 @@ -package fun.easycode.jointblock.core; - -/** - * 插入自定义逻辑回调 - * @author xuzhe - */ -@FunctionalInterface -public interface InsertCallback { - /** - * 用户拿指令K和数据库实体T可以进行自己自定义的逻辑 - * @param cmd 指令 - * @param entity 数据库实体 - * @return 数据库实体 - */ - T callback(K cmd, T entity); -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/InsertOrUpdateBatchMethod.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/InsertOrUpdateBatchMethod.java new file mode 100644 index 0000000000000000000000000000000000000000..5daafb15113903d6005ca9c03daaa7f6692a1cf6 --- /dev/null +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/InsertOrUpdateBatchMethod.java @@ -0,0 +1,78 @@ +package fun.easycode.jointblock.core; + +import com.baomidou.mybatisplus.core.injector.AbstractMethod; +import com.baomidou.mybatisplus.core.metadata.TableInfo; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import org.apache.ibatis.executor.keygen.NoKeyGenerator; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlSource; + +/** + * 批量插入或更新 + * insert into table (id,name) values (1,'xuzhen'),(2,'xuzhen2') ON DUPLICATE KEY UPDATE name=VALUES(name) + * @author xuzhen97 + */ +public class InsertOrUpdateBatchMethod extends AbstractMethod { + @Override + public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { + final String sql = ""; + final String tableName = tableInfo.getTableName(); + final String filedSql = prepareFieldSql(tableInfo); + final String modelValuesSql = prepareModelValuesSql(tableInfo); + final String duplicateKeySql =prepareDuplicateKeySql(tableInfo); + final String sqlResult = String.format(sql, tableName, filedSql, modelValuesSql,duplicateKeySql); + //System.out.println("savaorupdatesqlsql="+sqlResult); + SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass); + return this.addInsertMappedStatement(mapperClass, modelClass, "insertOrUpdateBatch", sqlSource, new NoKeyGenerator(), null, null); + } + + /** + * 准备ON DUPLICATE KEY UPDATE sql + * @param tableInfo + * @return + */ + private String prepareDuplicateKeySql(TableInfo tableInfo) { + final StringBuilder duplicateKeySql = new StringBuilder(); + if(!StringUtils.isEmpty(tableInfo.getKeyColumn())) { + duplicateKeySql.append(tableInfo.getKeyColumn()).append("=values(").append(tableInfo.getKeyColumn()).append("),"); + } + + tableInfo.getFieldList().forEach(x -> { + duplicateKeySql.append(x.getColumn()) + .append("=values(") + .append(x.getColumn()) + .append("),"); + }); + duplicateKeySql.delete(duplicateKeySql.length() - 1, duplicateKeySql.length()); + return duplicateKeySql.toString(); + } + + /** + * 准备属性名 + * @param tableInfo + * @return + */ + private String prepareFieldSql(TableInfo tableInfo) { + StringBuilder fieldSql = new StringBuilder(); + fieldSql.append(tableInfo.getKeyColumn()).append(","); + tableInfo.getFieldList().forEach(x -> { + fieldSql.append(x.getColumn()).append(","); + }); + fieldSql.delete(fieldSql.length() - 1, fieldSql.length()); + fieldSql.insert(0, "("); + fieldSql.append(")"); + return fieldSql.toString(); + } + + private String prepareModelValuesSql(TableInfo tableInfo){ + final StringBuilder valueSql = new StringBuilder(); + valueSql.append(""); + if(!StringUtils.isEmpty(tableInfo.getKeyProperty())) { + valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},"); + } + tableInfo.getFieldList().forEach(x -> valueSql.append("#{item.").append(x.getProperty()).append("},")); + valueSql.delete(valueSql.length() - 1, valueSql.length()); + valueSql.append(""); + return valueSql.toString(); + } +} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/InsertResultConvert.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/InsertResultConvert.java deleted file mode 100644 index c71fcede4e9dc25056972dfa0af01bd5b94bbbdf..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/InsertResultConvert.java +++ /dev/null @@ -1,15 +0,0 @@ -package fun.easycode.jointblock.core; - -/** - * 插入成功返回结果转换 - * @author xuzhe - */ -@FunctionalInterface -public interface InsertResultConvert { - /** - * 数据库实体T转换成自定义返回结果R - * @param entity 数据库实体 - * @return R - */ - R to(T entity); -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/JointBlockMapper.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/JointBlockMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..614d49de1a17972b54312b397ad35ed7a9b91f35 --- /dev/null +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/JointBlockMapper.java @@ -0,0 +1,22 @@ +package fun.easycode.jointblock.core; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 通用mapper + * 在原版的mapper上增加了批量操作和流式查询 + * @param + */ +public interface JointBlockMapper extends BaseMapper, StreamMapper, BatchMapper{ + + /** + * 批量插入或更新 + * insert into table (id, name) values (1, 'a'), (2, 'b') on duplicate key update name = values(name) + * @param list 数据集 + * @return 影响行数 + */ + int insertOrUpdateBatch(@Param("list") List list); +} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/JointBlockSqlInjector.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/JointBlockSqlInjector.java similarity index 90% rename from jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/JointBlockSqlInjector.java rename to jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/JointBlockSqlInjector.java index fc4648248f9d1ca2a1cf60c7e42a684c70f2fb09..2dee3c5cb70e1c50eadccf169d056bf570314cec 100644 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/JointBlockSqlInjector.java +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/JointBlockSqlInjector.java @@ -1,4 +1,4 @@ -package fun.easycode.jointblock.mybatisplus; +package fun.easycode.jointblock.core; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector; @@ -19,6 +19,7 @@ public class JointBlockSqlInjector extends DefaultSqlInjector { methodList.add(new ReplaceBatchSomeColumn()); methodList.add(new StreamQuerySqlAbstractMethod()); methodList.add(new StreamQueryAbstractMethod()); + methodList.add(new InsertOrUpdateBatchMethod()); return methodList; } } diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/LogUpdateWrapper.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/LogUpdateWrapper.java deleted file mode 100644 index d9e6af5ec171ebbc3ad40db01f3b79e7cedceb2b..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/LogUpdateWrapper.java +++ /dev/null @@ -1,23 +0,0 @@ -package fun.easycode.jointblock.core; - -import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; -import fun.easycode.jointblock.util.LogUtil; - -/** - * 扩展UpdateWrapper用于收集插入的字段修改详情 - * @author xuzhe - */ -public class LogUpdateWrapper extends UpdateWrapper { - - private final String updateId; - - public LogUpdateWrapper(String updateId){ - this.updateId = updateId; - } - - @Override - public UpdateWrapper set(String column, Object val) { - LogUtil.setUpdateField(updateId, column, val); - return super.set(column, val); - } -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/OperateSymbol.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/OperateSymbol.java new file mode 100644 index 0000000000000000000000000000000000000000..4c373b43bae5088983a1d1284b13deb8be3b2e57 --- /dev/null +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/OperateSymbol.java @@ -0,0 +1,74 @@ +package fun.easycode.jointblock.core; + +/** + * 操作符枚举 + * @author xuzhen97 + */ +public enum OperateSymbol implements Enumerator{ + /** + * 等于 + */ + EQ("Eq", "等于"), + /** + * 模糊查询 + */ + LIKE("Like", "模糊查询"), + /** + * 模糊查询左 + */ + LIKE_LEFT("LikeLeft", "模糊查询左"), + /** + * 模糊查询右 + */ + LIKE_RIGHT("LikeRight", "模糊查询右"), + /** + * 大于 + */ + GT("Gt", "大于"), + /** + * 小于 + */ + LT("Lt", "小于"), + /** + * 大于等于 + */ + GE("Ge", "大于等于"), + /** + * 小于等于 + */ + LE("Le", "小于等于"), + /** + * in 集合查询 + */ + IN("In", "in 集合查询"), + /** + * not in 集合查询 + */ + NOT_IN("NotIn", "not in 集合查询"), + /** + * is null 查询 + */ + IS_NULL("IsNull", "is null 查询"), + /** + * 排序 + */ + SORT("Sort", "排序"); + private final String value; + private final String desc; + + OperateSymbol(String value, String desc) { + this.value = value; + this.desc = desc; + } + + @Override + public String getValue() { + return value; + } + + @Override + public String getDesc() { + return desc; + } + +} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/PageDto.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/PageDto.java index 0ba210b86e5de8e439515803e574d9f1ac106759..8134221488c723eab871ec68dba8162af935ad66 100644 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/PageDto.java +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/PageDto.java @@ -3,12 +3,8 @@ package fun.easycode.jointblock.core; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.Builder; import lombok.Data; -import org.springframework.http.ResponseEntity; -import java.util.Collection; import java.util.List; -import java.util.Objects; -import java.util.Optional; import java.util.stream.Collectors; /** @@ -32,9 +28,9 @@ public class PageDto { * @param 实体类型 * @return PageDto */ - public static PageDto toPageDto(Page page, Convert convert){ + public static PageDto toPageDto(Page page, QueryResultConvert convert){ - List data = page.getRecords().stream().map(convert::convert) + List data = page.getRecords().stream().map(convert::to) .collect(Collectors.toList()); return PageDto.builder() .current(page.getCurrent()) @@ -45,69 +41,6 @@ public class PageDto { .build(); } - /** - * 根据ResponseEntity 返回值以及qry生成PageDto - * @param qry - * @param entity - * @param convert - * @return - * @param - * @param - * @param - */ - public static > PageDto toPageDto(PagingQry qry, ResponseEntity entity, Convert convert){ - - List data = Objects.requireNonNull(entity.getBody()) - .stream().map(convert::convert).collect(Collectors.toList()); - - return PageDto.builder() - .current(Long.valueOf(qry.getCurrent())) - .totalPage(getHeaderValue(Constant.TOTAL_PAGE_HEADER, entity, 0L, Long.class)) - .totalSize(getHeaderValue(Constant.TOTAL_SIZE_HEADER, entity, 0L, Long.class)) - .size(Long.valueOf(qry.getSize())) - .data(data) - .build(); - } - - /** - * 根据ResponseEntity 返回值生成PageDto - * @param entity - * @param convert - * @return - * @param - * @param - * @param - */ - public static > PageDto toPageDto(ResponseEntity entity, Convert convert){ - - List data = Objects.requireNonNull(entity.getBody()) - .stream().map(convert::convert).collect(Collectors.toList()); - - return PageDto.builder() - .current(getHeaderValue(Constant.CURRENT_HEADER, entity, 1L, Long.class)) - .totalPage(getHeaderValue(Constant.TOTAL_PAGE_HEADER, entity, 0L, Long.class)) - .totalSize(getHeaderValue(Constant.TOTAL_SIZE_HEADER, entity, 0L, Long.class)) - .size(getHeaderValue(Constant.SIZE_HEADER, entity, 10L, Long.class)) - .data(data) - .build(); - } - - /** - * 获取header通用封装方法 - * @param header - * @param responseEntity - * @param defaultValue - * @param type - * @return - * @param - */ - private static T getHeaderValue(String header, ResponseEntity responseEntity, T defaultValue, Class type){ - - return Optional.ofNullable(responseEntity.getHeaders().get(header)) - .map(headers-> cn.hutool.core.convert.Convert.convert(type, headers.get(0))) - .orElse(defaultValue); - } - /** * 转换,这是mybatis plus page类型和dto类型转换的声明 * @param diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/PagingQry.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/PageQry.java similarity index 90% rename from jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/PagingQry.java rename to jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/PageQry.java index 66df9d46e7ef9f7990d1ce3dfdb6e49a91aeaa87..b2865ff765aefa4cf0784ba0db65768b27e70210 100644 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/PagingQry.java +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/PageQry.java @@ -9,7 +9,7 @@ import javax.validation.constraints.NotNull; * @author xuzhe */ @Data -public class PagingQry { +public class PageQry { @NotNull private Integer size = 10; @NotNull diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/QueryDslParser.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/QueryDslParser.java new file mode 100644 index 0000000000000000000000000000000000000000..f4a7436eb46d9229291dd98760df5c9e0b67610a --- /dev/null +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/QueryDslParser.java @@ -0,0 +1,47 @@ +package fun.easycode.jointblock.core; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 查询表达式解析器 + * @author xuzhen97 + */ +public class QueryDslParser { + + private static final Pattern QUERY_PATTERN = Pattern.compile("(\\w+?)(\\[.+?\\]=)([a-zA-Z0-9\\u4e00-\\u9fa5()|\\s\\-:]+?),"); + private static final Pattern OPERATOR_PATTERN = Pattern.compile("(?<=\\[).*(?=\\])"); + /** + * 解析查询表达式 + * @param value 查询表达式 + * @param tClass 实体类 + * @param 实体类 + * @return 查询构造器 + */ + public static QueryWrapperBuilder parser(String value, Class tClass) { + QueryWrapperBuilder builder; + if(tClass == null){ + builder = new QueryWrapperBuilder<>(); + }else{ + builder = new QueryWrapperBuilder<>(tClass); + } + // 整体表达式正则 + Matcher matcher = QUERY_PATTERN.matcher(value + ","); + // 操作符匹配正则 + while (matcher.find()) { + Matcher operatorMatcher = OPERATOR_PATTERN.matcher( matcher.group(2)); + builder.with(matcher.group(1), operatorMatcher.find() ? operatorMatcher.group() : "", matcher.group(3)); + } + return builder; + } + + /** + * 解析查询表达式 + * @param value 查询表达式 + * @param 实体类 + * @return 查询构造器 + */ + public static QueryWrapperBuilder parser(String value) { + return parser(value, null); + } +} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/QueryHandlerMethodArgumentResolver.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/QueryHandlerMethodArgumentResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..8d72c753e560036867ae227f75c6f53a7b531bfc --- /dev/null +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/QueryHandlerMethodArgumentResolver.java @@ -0,0 +1,37 @@ +package fun.easycode.jointblock.core; + +import org.springframework.core.MethodParameter; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +/** + * 查询参数解析器 + * @author xuzhen97 + */ +public class QueryHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { + @Override + public boolean supportsParameter(MethodParameter methodParameter) { + return methodParameter.hasParameterAnnotation(QueryParam.class); + } + + @Override + public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception { + QueryParam queryParam = methodParameter.getParameterAnnotation(QueryParam.class); + + String name = "".equalsIgnoreCase(queryParam.name()) ? queryParam.value() : queryParam.name(); + + if ("".equalsIgnoreCase(name)) { + name = methodParameter.getParameter().getName(); + } + + String value = nativeWebRequest.getParameter(name); + + if (value == null) { + return null; + } + + return QueryDslParser.parser(value, queryParam.root()).build(); + } +} \ No newline at end of file diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/QueryParam.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/QueryParam.java new file mode 100644 index 0000000000000000000000000000000000000000..fe9c0372cfaceca9a2f3a29b6e68e62099e371e7 --- /dev/null +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/QueryParam.java @@ -0,0 +1,22 @@ +package fun.easycode.jointblock.core; + +import org.springframework.core.annotation.AliasFor; + +import java.lang.annotation.*; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface QueryParam { + /** + * Alias for {@link #name}. + */ + @AliasFor("name") String value() default ""; + /** + * The name of the request parameter to bind to. + * + * @since 4.2 + */ + @AliasFor("value") String name() default ""; + Class root(); +} \ No newline at end of file diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/QueryWrapperBuilder.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/QueryWrapperBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..8e43ecbb42d5bc3e799c8ca08156f6c9826bc477 --- /dev/null +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/QueryWrapperBuilder.java @@ -0,0 +1,120 @@ +package fun.easycode.jointblock.core; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.LambdaUtils; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class QueryWrapperBuilder { + + private final List params; + private final Class tClass; + private final Map columnMap; + + public QueryWrapperBuilder(Class tClass){ + params = new ArrayList<>(); + this.tClass = tClass; + columnMap = LambdaUtils.getColumnMap(tClass); + } + + public QueryWrapperBuilder(){ + params = new ArrayList<>(); + this.tClass = null; + this.columnMap = null; + } + + public QueryWrapperBuilder with(String key, String operation, Object value){ + params.add(new SearchCriteria(key, operation, value)); + return this; + } + + public QueryWrapper build(){ + if(params.size() == 0){ + return null; + } + QueryWrapper queryWrapper = Wrappers.query(); + + for(SearchCriteria criteria : params){ + String tableField = getTableField(criteria.getKey()); + // 解析查询条件 + analysis(tableField, criteria.getOperation(), criteria.getValue(), queryWrapper); + } + return queryWrapper; + } + + private String getTableField(String key){ + if(tClass != null) { + String columnKey = LambdaUtils.formatKey(key); + return columnMap.get(columnKey).getColumnSelect(); + }else{ + return key; + } + } + + /** + * 解析查询条件 + * @param tableField 字段名 + * @param operation 操作符 + * @param value 值 + * @param wrapper 条件构造器 + * @param 实体类 + */ + private static void analysis(String tableField, String operation, Object value, QueryWrapper wrapper) { + + // 如果是空值,不处理 + if (!ObjectUtil.isNotEmpty(value)) { + return; + } + + if (Objects.equals(OperateSymbol.EQ.getValue(), operation)) { + wrapper.eq(tableField, value); + } else if (Objects.equals(OperateSymbol.LIKE.getValue(), operation)) { + wrapper.like(tableField, value); + } else if (Objects.equals(OperateSymbol.LIKE_LEFT.getValue(), operation)) { + wrapper.likeLeft(tableField, value); + } else if (Objects.equals(OperateSymbol.LIKE_RIGHT.getValue(), operation)) { + wrapper.likeRight(tableField, value); + } else if (Objects.equals(OperateSymbol.GT.getValue(), operation)) { + wrapper.gt(tableField, value); + } else if (Objects.equals(OperateSymbol.LT.getValue(), operation)) { + wrapper.lt(tableField, value); + } else if (Objects.equals(OperateSymbol.GE.getValue(), operation)) { + wrapper.ge(tableField, value); + } else if (Objects.equals(OperateSymbol.LE.getValue(), operation)) { + wrapper.le(tableField, value); + } else if (Objects.equals(OperateSymbol.IS_NULL.getValue(), operation)) { + if ((Boolean) value) { + wrapper.isNull(tableField); + } else { + wrapper.isNotNull(tableField); + } + } else if (Objects.equals(OperateSymbol.NOT_IN.getValue(), operation)) { + wrapper.notIn(tableField, getValueCollection(value)); + } else if (Objects.equals(OperateSymbol.IN.getValue(), operation)) { + wrapper.in(tableField, getValueCollection(value)); + } else if (Objects.equals(OperateSymbol.SORT.getValue(), operation)) { + if (ObjectUtil.equal(String.valueOf(value).toUpperCase(), "DESC")) { + wrapper.orderByDesc(tableField); + } else { + wrapper.orderByAsc(tableField); + } + } else { + throw new CheckException("不支持的查询条件!"); + } + } + + private static List getValueCollection(Object value) { + Pattern valuePattern = Pattern.compile("(?<=\\().*(?=\\))"); + Matcher matcher = valuePattern.matcher(value.toString()); + if (!matcher.find()) { + throw new CheckException("集合条件value表达式不正确!"); + } + String[] values = matcher.group().split("\\|"); + return Arrays.asList(values); + } +} \ No newline at end of file diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/ReplaceBatchSomeColumn.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/ReplaceBatchSomeColumn.java similarity index 98% rename from jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/ReplaceBatchSomeColumn.java rename to jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/ReplaceBatchSomeColumn.java index 96b19f909ea70531c54afcfc01c07e2c2c3c1c61..fe1255e7150ac937ac79823e6b939d7810a99ffd 100644 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/ReplaceBatchSomeColumn.java +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/ReplaceBatchSomeColumn.java @@ -1,4 +1,4 @@ -package fun.easycode.jointblock.mybatisplus; +package fun.easycode.jointblock.core; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.core.injector.AbstractMethod; diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/SearchCriteria.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/SearchCriteria.java new file mode 100644 index 0000000000000000000000000000000000000000..12e60ddc55217e4aa8e416cbce9e02f81caf259e --- /dev/null +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/SearchCriteria.java @@ -0,0 +1,25 @@ +package fun.easycode.jointblock.core; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * 查询条件 + * @author xuzhen97 + */ +@Data +@AllArgsConstructor +public class SearchCriteria { + /** + * 查询字段 + */ + private String key; + /** + * 查询条件 + */ + private String operation; + /** + * 查询值 + */ + private Object value; +} \ No newline at end of file diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/StreamMapper.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/StreamMapper.java similarity index 94% rename from jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/StreamMapper.java rename to jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/StreamMapper.java index 0c5ecc7365b4f3700773573cdc74d7f1108f30f0..204bb41bae44e76a5044520e357c529500f89db4 100644 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/StreamMapper.java +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/StreamMapper.java @@ -1,4 +1,4 @@ -package fun.easycode.jointblock.mybatisplus; +package fun.easycode.jointblock.core; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.toolkit.Constants; diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/StreamQueryAbstractMethod.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/StreamQueryAbstractMethod.java similarity index 91% rename from jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/StreamQueryAbstractMethod.java rename to jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/StreamQueryAbstractMethod.java index 367374cef59ba863b792220a5fae2669fb19d666..b5c96a42609dcad9b3f2ff9a5f764645c3135c0b 100644 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/StreamQueryAbstractMethod.java +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/StreamQueryAbstractMethod.java @@ -1,4 +1,4 @@ -package fun.easycode.jointblock.mybatisplus; +package fun.easycode.jointblock.core; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableInfo; @@ -20,7 +20,7 @@ public class StreamQueryAbstractMethod extends AbstractMethod { } /* 缓存逻辑处理 */ return builderAssistant.addMappedStatement(METHOD, sqlSource, StatementType.PREPARED, SqlCommandType.SELECT, - Integer.MIN_VALUE, null, null, null, null, modelClass, + Integer.MIN_VALUE, null, null, null, tableInfo.getResultMap(), modelClass, ResultSetType.FORWARD_ONLY, true, true, false, null, null, null, configuration.getDatabaseId(), languageDriver, null); } diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/StreamQuerySqlAbstractMethod.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/StreamQuerySqlAbstractMethod.java similarity index 88% rename from jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/StreamQuerySqlAbstractMethod.java rename to jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/StreamQuerySqlAbstractMethod.java index 5b922c025601c140b70ed7c0725c27183736a1b5..d6d6baf365ee64e8aaa5c8eaa41078655c1af092 100644 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/StreamQuerySqlAbstractMethod.java +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/StreamQuerySqlAbstractMethod.java @@ -1,4 +1,4 @@ -package fun.easycode.jointblock.mybatisplus; +package fun.easycode.jointblock.core; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableInfo; @@ -10,8 +10,6 @@ public class StreamQuerySqlAbstractMethod extends AbstractMethod { public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { String sqlFormat = ""; - System.err.println(tableInfo.getAllSqlSelect()); - String sql = String.format(sqlFormat, tableInfo.getAllSqlSelect(), tableInfo.getTableName(), "${customSql}"); @@ -23,7 +21,7 @@ public class StreamQuerySqlAbstractMethod extends AbstractMethod { } /* 缓存逻辑处理 */ return builderAssistant.addMappedStatement(METHOD, sqlSource, StatementType.PREPARED, SqlCommandType.SELECT, - Integer.MIN_VALUE, null, null, null, null, modelClass, + Integer.MIN_VALUE, null, null, null, tableInfo.getResultMap() , modelClass, ResultSetType.FORWARD_ONLY, true, true, false, null, null, null, configuration.getDatabaseId(), languageDriver, null); } diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/UpdateCallback.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/UpdateCallback.java deleted file mode 100644 index 62773e9c29e912f139d2cc08c6c8e7fc425660ee..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/UpdateCallback.java +++ /dev/null @@ -1,19 +0,0 @@ -package fun.easycode.jointblock.core; - -import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; - -/** - * 修改回调,用于自定义自己的修改逻辑 - * @author xuzhe - */ -@FunctionalInterface -public interface UpdateCallback { - - /** - * 修改自定义逻辑 - * @param cmd 指令 - * @param wrapper UpdateWrapper - * @return UpdateWrapper - */ - UpdateWrapper callback(K cmd, UpdateWrapper wrapper); -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/UpdateResultConvert.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/UpdateResultConvert.java deleted file mode 100644 index 27d2458c45716e28aecc5df2ac0df9403e1e23a0..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/UpdateResultConvert.java +++ /dev/null @@ -1,15 +0,0 @@ -package fun.easycode.jointblock.core; - -/** - * 修改结果转换 - * @author xuzhe - */ -@FunctionalInterface -public interface UpdateResultConvert { - /** - * 实体T转换成返回结果R - * @param entity 数据库实体 - * @return 要转换的实体 - */ - R to(T entity); -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/UserHolder.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/UserHolder.java index dce23341cd79e337f53aa58412a89be33382024f..15446baa1b27fa0b0d3d79a5c90f741411a22e8b 100644 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/UserHolder.java +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/UserHolder.java @@ -1,6 +1,5 @@ package fun.easycode.jointblock.core; -import fun.easycode.jointblock.exception.CheckException; import org.jetbrains.annotations.NotNull; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; @@ -11,7 +10,7 @@ import org.springframework.context.ApplicationContextAware; * 用户持有者,用来操作当前用户信息 * @author xuzhe */ -public abstract class UserHolder implements ApplicationContextAware { +public abstract class UserHolder implements ApplicationContextAware { private static ApplicationContext context; @@ -23,38 +22,14 @@ public abstract class UserHolder implements ApplicationContextAware { return context.getBean(UserHolder.class); } - /** - * 获取当前用户id - * @return String - */ - public abstract String getUserId(); - /** * 获取当前登录用户 * @return 登录用户 - * @param 登录用户泛型 */ - public T getUser(){ + public T getLoginUser(){ throw new CheckException("先重写getUser方法!"); } - /** - * 判断是否登录 - * @return 是否 - */ - public boolean isLogin(){ - throw new CheckException("先重写isLogin方法!"); - } - - /** - * 根据用户名强制下线用户 - * @param username 用户名 - */ - public void offline(String username){ - throw new CheckException("先重写offline方法!"); - } - - @Override public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException { context = applicationContext; @@ -67,4 +42,14 @@ public abstract class UserHolder implements ApplicationContextAware { protected static ApplicationContext getContext(){ return context; } + + /** + * 默认的UserHolder + */ + public static class DefaultUserHolder extends UserHolder{ + @Override + public UserInfo getLoginUser() { + return () -> "jointblock"; + } + } } diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataAlias.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataAlias.java deleted file mode 100644 index d023b0b6542c2f0d84bfbd826e0e035e61eb4af7..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataAlias.java +++ /dev/null @@ -1,16 +0,0 @@ -package fun.easycode.jointblock.datafill; - -import java.lang.annotation.*; - -/** - * @author xuzhe - */ -@Target({ElementType.FIELD,ElementType.ANNOTATION_TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface DataAlias { - /** - * 实体的主键名, 默认id - */ - String value(); -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataCopy.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataCopy.java deleted file mode 100644 index ef73b9ff66ebae376307d135c174a0607431c2f6..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataCopy.java +++ /dev/null @@ -1,244 +0,0 @@ -package fun.easycode.jointblock.datafill; - -import cn.hutool.core.util.ReflectUtil; -import fun.easycode.jointblock.core.PageDto; -import fun.easycode.jointblock.exception.CheckException; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.Expression; -import org.springframework.expression.ExpressionParser; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; -import org.springframework.util.StringUtils; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.util.*; - -/** - * @author xuzhe - */ -public class DataCopy implements ApplicationContextAware { - - private static ApplicationContext context; - private ExpressionParser expressionParser; - - /** - * 解析spEl表达式, 传输两个对象source 和 target 到上下文 - * @param source - * @param target - * @param spEl - * @param tClass - * @return - * @param - */ - public Optional parserSpEl(Object source, Object target, String spEl, Class tClass){ - if(StringUtils.isEmpty(spEl)){ - return Optional.empty(); - } - EvaluationContext evaluationContext = new StandardEvaluationContext(context); - evaluationContext.setVariable("source", source); - evaluationContext.setVariable("target", target); - Expression expression = expressionParser.parseExpression(spEl); - return Optional.ofNullable(expression.getValue(evaluationContext, tClass)); - } - - /** - * 解析spEl表达式 - * @param spEl - * @param tClass - * @return - * @param - */ - public Optional parserSpEl(String spEl, Class tClass){ - if(StringUtils.isEmpty(spEl)){ - return Optional.empty(); - } - EvaluationContext evaluationContext = new StandardEvaluationContext(context); - Expression expression = expressionParser.parseExpression(spEl); - return Optional.ofNullable(expression.getValue(evaluationContext, tClass)); - } - - /** - * 解析spEl表达式,允许往上下文传输contextProps变量列表,供用户使用 - * @param spEl - * @param tClass - * @param contextProps - * @return - * @param - */ - public Optional parserSpEl(String spEl, Class tClass,final Map contextProps){ - if(StringUtils.isEmpty(spEl)){ - return Optional.empty(); - } - EvaluationContext evaluationContext = new StandardEvaluationContext(context); - if(contextProps != null){ - contextProps.forEach((key,value)->{ - evaluationContext.setVariable(key, value); - }); - } - Expression expression = expressionParser.parseExpression(spEl); - return Optional.ofNullable(expression.getValue(evaluationContext, tClass)); - } - - /** - * 对象属性copy - * 方法支持使用@DataAlias注解进行转义 - * 例如目标中叫做id ,但是在source中叫做userId - * 那么我们可以在目标中增加注解@DataAlias("userId")来进行对接 - * - * @param source 元对象 - * @param target 目标对象 - */ - @SuppressWarnings("all") - public void copy(Object source, Object target, String spEl) { - - Map sourceFieldMap = ReflectUtil.getFieldMap(source.getClass()); - Map targetFieldMap = ReflectUtil.getFieldMap(target.getClass()); - - for (Map.Entry targetEntry : targetFieldMap.entrySet()) { - Field targetField = targetEntry.getValue(); - String targetFieldName; - if (targetField.isAnnotationPresent(DataAlias.class)) { - DataAlias dataAlias = targetField.getAnnotation(DataAlias.class); - targetFieldName = dataAlias.value(); - } else { - targetFieldName = targetEntry.getKey(); - } - - Field sourceField = sourceFieldMap.get(targetFieldName); - - // 如果source和target对象,属性值能直接匹配的话,就直接copy - if (sourceField != null) { - Object value = ReflectUtil.getFieldValue(source, sourceField); - ReflectUtil.setFieldValue(target, targetField, value); - } - // 如果target对象中的属性是一个完整的对象,或者list, 就需要增加DataGenerate处理 - // 要知道如果我们对象很简单数据填充的时候只需要mapstruct将id转换成空对象即可 - // 但是超过两层结构,我们的数据其实还没有加载,那么就需要DataGenerate来进行解决了 - else if (targetField.isAnnotationPresent(DataGenerate.class)) { - DataGenerate dataGenerate = targetField.getAnnotation(DataGenerate.class); - - sourceField = sourceFieldMap.get(dataGenerate.value()); - Object sourceFieldValue = ReflectUtil.getFieldValue(source, sourceField); - - // 如果是id生成的默认形式,会根据我们声明的目标对象类型,生成空对象 - // 然后去source对象中去取指定的属性值填充到空对象内 - // 通俗的讲 就是id属性转换成一个对象,对象中仅仅id属性有值 - if (dataGenerate.mode() == IDGenerateMode.DEFAULT) { - // 根据要生成空对象的Field生成实例 - Object targetFieldValue = ReflectUtil.newInstance(targetField.getType()); - - // 生成实例后将主键信息填写进去 - Field subTargetField = ReflectUtil.getField(targetFieldValue.getClass() - , dataGenerate.targetField()); - - if (targetField != null) { - ReflectUtil.setFieldValue(targetFieldValue, subTargetField, sourceFieldValue); - } - ReflectUtil.setFieldValue(target, targetField, targetFieldValue); - - } else if (dataGenerate.mode() == IDGenerateMode.MULTIPLE - && dataGenerate.listClass() != void.class) { - // 这里主要是处理数据库中一个字段存了多个id类似 aa,bb,cc这种 - // 而我们需要将多个id生成为list集合, 方便填充框架下一步工作 - String idsStr = (String) sourceFieldValue; - // 数据库的主键分割 - List ids = Arrays.asList(idsStr.split(dataGenerate.listSeparator())); - List list = new ArrayList(); - for (String id : ids) { - Object targetFieldValue = ReflectUtil.newInstance(dataGenerate.listClass()); - // 生成实例后将主键信息填写进去 - Field subTargetField = ReflectUtil.getField(targetFieldValue.getClass() - , dataGenerate.targetField()); - - if (subTargetField != null) { - ReflectUtil.setFieldValue(targetFieldValue, targetField, id); - } - list.add(targetFieldValue); - } - ReflectUtil.setFieldValue(target, targetField, list); - } - } - } - // 如果非基本类型定义了el表达式,不会取返回参数,会执行用户自定义的逻辑 - if(!StringUtils.isEmpty(spEl)){ - parserSpEl(source, target, spEl, Void.class); - } - } - - public Object getMapObjById(Object id, Map map){ - return map.get(id); - } - - public Object getPageDtoById(Object id , PageDto result - , Class idAnnotation){ - return getCollectionObjById(id, result.getData(), idAnnotation); - } - - public Object getObjById(Object id, Object container, Class idAnnotation){ - if(container instanceof Map){ - return getMapObjById(id, (Map) container); - }else if (container instanceof Collection){ - return getCollectionObjById(id, (Collection) container, idAnnotation); - }else if(container instanceof PageDto){ - return getPageDtoById(id, (PageDto) container, idAnnotation); - }else{ - throw new CheckException("不支持的容器!"); - } - } - - /** - * 根据id获取List中符合的元素 - * 默认取id的列对比,如果没有叫做id的列则取@TableId的列对比 - * @param id - * @param collection - * @param idAnnotation 如果id不是主键默认名称,应该以什么注解进行判断 - * @return - */ - @SuppressWarnings("all") - public Object getCollectionObjById(Object id, Collection collection, Class idAnnotation){ - - if(collection == null || collection.size() == 0){ - return null; - } - - Class elementClass = collection.stream().findFirst().get().getClass(); - Map fieldMap = ReflectUtil.getFieldMap(elementClass); - - Field field = fieldMap.get("id"); - - if(field == null){ - - Optional first = fieldMap.values() - .stream() - .filter(f -> f.isAnnotationPresent(idAnnotation)) - .findFirst(); - - field = first.isPresent()?first.get():null; - - if(field == null){ - throw new CheckException(elementClass.getName() + "请增加注解" + idAnnotation.getName()+"标记主键!"); - } - } - - final Field conditionField = field; - - Optional first = collection.stream().filter(obj -> Objects.equals(id, ReflectUtil.getFieldValue(obj, conditionField))) - .findFirst(); - - return first.isPresent()?first.get():null; - } - - public static DataCopy getInstance(){ - return context.getBean(DataCopy.class); - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - context = applicationContext; - expressionParser = new SpelExpressionParser(); - } -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFill.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFill.java deleted file mode 100644 index f11ff5a1a54b2827b89f422767d0865386546268..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFill.java +++ /dev/null @@ -1,56 +0,0 @@ -package fun.easycode.jointblock.datafill; - -import java.lang.annotation.*; - -/** - * @author xuzhe - */ -@Target({ElementType.FIELD,ElementType.ANNOTATION_TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface DataFill { - /** - * 实体的主键名, 默认id - * @return id字段名 - */ - String value() default "id"; - - /** - * 注解对应处理器 - * @return 对应的处理器 - */ - Class source(); - - /** - * spring el表达式 - * 如果我们自定义表达式 - * 当DataFill所在基本数据类型的时候spEl表达式的值是最终填充的值 - * 所在属性是对象,或者是list的时候,表达式不取返回值 - * 我们可以在Mapstruct转换器中自定义处理逻辑 - * 固定会传入target对象和source对象,target对象就是当前属性的值 - * List的话就是其中的对象,source是id查出的对应实体 - * @return 我们自定义的逻辑 - */ - String spEl() default ""; - - /** - * 自定义传参 - * DataParam value支持el表达式 - * @return 参数列表 - */ - DataParam[] params() default {}; - - /** - * 自定义调用方法名,目前仅对feign client生效 - * 如果不指定方法名,那么在feign client的指定中,client只能存在一个方法 - * @return 方法名 - */ - String methodName() default ""; - - /** - * 是否自定义mybatis plus id注解 - * @Author: xuzhen97 - * @Date: 2023/03/17 - */ - boolean isCustomMyBatisPlusAnno() default false; -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillActuator.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillActuator.java new file mode 100644 index 0000000000000000000000000000000000000000..a32b7c5600441b2ffe9ab59cfda18a97358fbc84 --- /dev/null +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillActuator.java @@ -0,0 +1,49 @@ +package fun.easycode.jointblock.datafill; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 数据填充执行器 + * @param 数据类型 + */ +public class DataFillActuator { + // 数据 + private final Collection data; + // 填充过程 + private final List> processes = new ArrayList<>(); + + /** + * 构造函数 + * @param data 数据 + */ + public DataFillActuator(Collection data) { + this.data = data; + } + + /** + * 增加填充过程 + * @param process 填充过程 + */ + public DataFillActuator addProcess(DataFillProcess process) { + processes.add(process); + return this; + } + + /** + * 执行 + */ + public void execute() { + List> tasks = processes.stream() + .map(process -> new DataFillTask<>(process, data)) + .collect(Collectors.toList()); + try { + // 执行任务 + DataFillThreadExecutor.execParallelTasks(tasks); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillExecutor.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillExecutor.java deleted file mode 100644 index 4a363e703982568b45cd1ca193dd6f9278851809..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillExecutor.java +++ /dev/null @@ -1,193 +0,0 @@ -package fun.easycode.jointblock.datafill; - -import cn.hutool.core.util.ClassUtil; -import cn.hutool.core.util.HashUtil; -import cn.hutool.core.util.ReflectUtil; -import fun.easycode.jointblock.core.PageDto; -import fun.easycode.jointblock.exception.CheckException; -import lombok.extern.slf4j.Slf4j; - -import java.lang.reflect.Field; -import java.util.*; -import java.util.stream.Collectors; - -/** - * 数据填充工厂 - * - * @author xuzhe - */ -@Slf4j -public class DataFillExecutor { - - /** - * 填充框架执行 - * - * @param o 填充对象 - */ - public void execute(Object o) { - List metadataList = new ArrayList<>(); - fetch(o, metadataList); - executeMetaDataList(metadataList); - } - - private void executeMetaDataList(List metadataList) { - - executeDataFill(metadataList); - List metadataListSub = new ArrayList<>(); - - for (DataFillMetadata metadata : metadataList) { - fetch(metadata.getTargetObjMap().values(), metadataListSub); - } - if (metadataListSub.size() > 0) { - executeMetaDataList(metadataListSub); - } - } - - private void executeDataFill(List metadataList) { - //异步任务 - List>> tasks = new ArrayList<>(); - - // 现根据sourceClass进行分组 - // 实际上就是BaseMapper或者FeignContract的实现 - // 其它实现不支持,默认不处理 - Map, List> anClassMetadatas = metadataList.stream() - .collect(Collectors.groupingBy(dataFillMetadata -> dataFillMetadata.getDataFill().source())); - - // 根据params hash 进行group分组生成task - anClassMetadatas.forEach((sourceClass, sourceClassMetadataList) -> { - sourceClassMetadataList.stream().collect(Collectors.groupingBy(this::hashParams)).values() - .forEach(hashMetadataList -> { - DataFillStrategyContext.getStrategy(sourceClass).ifPresent(strategy -> { - tasks.add(new DataFillTask<>(strategy, hashMetadataList)); - }); - }); - }); - - try { - DataFillThreadPoolManager.execParallelTasks(tasks); - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * 填充信息拉取 - * - * @param o 处理对象 - * @param metadataList 元数据集合 - */ - @SuppressWarnings("all") - private void fetch(Object o, List metadataList) { - if (o == null || metadataList == null) { - return; - } - if (o instanceof Collection) { - for (Object o1 : ((Collection) o)) { - fetch(o1, metadataList); - } - } else if (o instanceof Map) { - for (Object value : ((Map) o).values()) { - fetch(value, metadataList); - } - } else if (o instanceof PageDto) { - PageDto pageDto = (PageDto) o; - fetch(pageDto.getData(), metadataList); - } else { - Class oClass = o.getClass(); - // 这里抛出的异常其最终目的就是中断当前集合的遍历, - // 如果集合容器中第一个节点到达这里并判断没有填充标识,则当前容器就不需要在继续遍历下去了 - if (oClass.getAnnotation(EnableDataFill.class) == null) { - return; - } - - // 如果传入操作对象是基本数据类型或者String不进行下面的逻辑 - if (ClassUtil.isBasicType(oClass) - || oClass.isAssignableFrom(String.class)) { - return; - } - - Map fieldMap = ReflectUtil.getFieldMap(oClass); - - for (Map.Entry entry : fieldMap.entrySet()) { - - Field field = entry.getValue(); - - // 非基本类型而且拥有DataFill的注解才代表需要处理 - DataFill dataFill = field.getAnnotation(DataFill.class); - - // 字段上未增加DataFill注解,代表字段不需要处理 - if (dataFill == null) { - continue; - } - - Object fieldValue = ReflectUtil.getFieldValue(o, field); - - if (ClassUtil.isBasicType(field.getType()) - || field.getType().isAssignableFrom(String.class)) { - // 基本类型或者String 给fieldValue复制null, 方便下边的判断 - fieldValue = null; - } else { - if (fieldValue == null) { - continue; - } - } - - DataFillMetadata dataFillMetadata = new DataFillMetadata(); - dataFillMetadata.setDataFill(dataFill); - dataFillMetadata.setOpObj(o); - dataFillMetadata.setOpField(field); - - Map targetObjMap = new HashMap<>(); - - // DataFill可以依附的对象 - // - Collection - // - 普通对象 - // - 基本类型 - if (fieldValue == null) { - // 如果要操作的field是基本数据类型或者String - // 对象或者List都是去对象、List内对象里面的主键属性 - // 但是基本数据类型取的是opObj操作对象的属性 - // 这里是结合上面的判断得出的fieldValue == null 是基本数据类型或者String - Field primaryKeyField = fieldMap.get(dataFill.value()); - Object primaryKeyValue = ReflectUtil.getFieldValue(o, primaryKeyField); - targetObjMap.put(primaryKeyValue, null); - } else if (fieldValue instanceof Collection) { - for (Object targetObj : (Collection) fieldValue) { - Field primaryKeyField = ReflectUtil.getField(targetObj.getClass(), dataFill.value()); - Object primaryKeyValue = ReflectUtil.getFieldValue(targetObj, primaryKeyField); - targetObjMap.put(primaryKeyValue, targetObj); - } - } else { - // 如果是普通对象 - Field primaryKeyField = ReflectUtil.getField(fieldValue.getClass(), dataFill.value()); - Object primaryKeyValue = ReflectUtil.getFieldValue(fieldValue, primaryKeyField); - - targetObjMap.put(primaryKeyValue, fieldValue); - } - - dataFillMetadata.setTargetObjMap(targetObjMap); - - metadataList.add(dataFillMetadata); - } - } - } - - /** - * 根据DataParam[] 参数列表计算一个hash - * 用于分组,不同的条件需要不同的查询 - * - * @param metadata DataFillMetadata - * @return long hash - */ - public long hashParams(DataFillMetadata metadata) { - DataParam[] params = metadata.getDataFill().params(); - Map paramMap = Arrays.stream(params) - .collect(Collectors.toMap(DataParam::name, p -> p)); - - List paramNameList = Arrays.stream(params).map(DataParam::name) - .sorted().collect(Collectors.toList()); - String grouping = paramNameList.stream().map(paramName -> paramName + paramMap.get(paramName)) - .collect(Collectors.joining()); - return HashUtil.mixHash(grouping); - } -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillId.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillId.java deleted file mode 100644 index 4f27c1bab60fb620230916cd6434160b088430a0..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillId.java +++ /dev/null @@ -1,9 +0,0 @@ -package fun.easycode.jointblock.datafill; - -import java.lang.annotation.*; - -@Target({ElementType.FIELD,ElementType.ANNOTATION_TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface DataFillId { -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillMetadata.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillMetadata.java deleted file mode 100644 index 5a0ccbf1cc41e15f4222458003c3fa3526060971..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillMetadata.java +++ /dev/null @@ -1,35 +0,0 @@ -package fun.easycode.jointblock.datafill; - -import lombok.Data; - -import java.lang.reflect.Field; -import java.util.Map; - -/** - * 元数据填充类型 - * @author xuzhe - */ -@Data -public class DataFillMetadata { - - /** - * 数据填充操作对象 - */ - private Object opObj; - /** - * 数据填充操作属性 - */ - private Field opField; - - /** - * 数据填充目标对象 - * key = 主键 - * value = 仅有主键属性的空对象 - */ - private Map targetObjMap; - - /** - * 数据填充操作属性上标注的@DataFill注解 - */ - private DataFill dataFill; -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillProcess.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillProcess.java new file mode 100644 index 0000000000000000000000000000000000000000..92eaae1385543f7d342a768da02aacc861f010cd --- /dev/null +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillProcess.java @@ -0,0 +1,38 @@ +package fun.easycode.jointblock.datafill; + +import lombok.Getter; + +import java.util.Collection; +import java.util.function.BiConsumer; +import java.util.function.Function; + +/** + * 数据填充过程 + * @author xuzhen97 + */ +@Getter +public class DataFillProcess { + + // key获取函数 + private final Function keyGetFun; + // 填充数据获取函数 + private final Function, Collection> fillDataGetFun; + // 填充数据key获取函数 + private final Function fillKeyGetFun; + // 填充函数 + private final BiConsumer fillFun; + + /** + * 构造函数 + * @param keyGetFun key获取函数 + * @param fillDataGetFun 填充数据获取函数 + * @param fillKeyGetFun 填充数据key获取函数 + * @param fillFun 填充函数 + */ + protected DataFillProcess(Function keyGetFun, Function, Collection> fillDataGetFun, Function fillKeyGetFun, BiConsumer fillFun) { + this.keyGetFun = keyGetFun; + this.fillDataGetFun = fillDataGetFun; + this.fillKeyGetFun = fillKeyGetFun; + this.fillFun = fillFun; + } +} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillProcessBuilder.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillProcessBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..541df79c4a541c2361b25657e226f7d1b1133b6e --- /dev/null +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillProcessBuilder.java @@ -0,0 +1,107 @@ +package fun.easycode.jointblock.datafill; + +import cn.hutool.core.util.ReflectUtil; + +import java.util.Collection; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * 数据填充过程构建器 + * @param 数据类型 + * @param 填充逻辑远程返回的数据类型 + * @author xuzhen97 + */ +public class DataFillProcessBuilder { + + // key获取函数 + private Function keyGetFun; + // 填充数据获取函数 + private Function, Collection> fillDataGetFun; + // 填充数据key获取函数 + private Function fillKeyGetFun; + // 填充函数 + private BiConsumer fillFun; + + /** + * 设置key获取函数 + * @param keyGetFun key获取函数 + * @return this + */ + public DataFillProcessBuilder keyGetFun(Function keyGetFun) { + this.keyGetFun = keyGetFun; + return this; + } + + /** + * 设置填充数据获取函数 + * @param fillDataGetFun 填充数据获取函数 + * @return this + */ + public DataFillProcessBuilder fillDataGetFun(Function, Collection> fillDataGetFun) { + this.fillDataGetFun = fillDataGetFun; + return this; + } + + /** + * 设置填充数据key获取函数 + * @param fillKeyGetFun 填充数据key获取函数 + * @return this + */ + public DataFillProcessBuilder fillKeyGetFun(Function fillKeyGetFun) { + this.fillKeyGetFun = fillKeyGetFun; + return this; + } + + /** + * 设置填充函数 + * @param fillFun 填充函数 + * @return this + */ + public DataFillProcessBuilder fillFun(BiConsumer fillFun) { + this.fillFun = fillFun; + return this; + } + + /** + * 构建 + * @return 数据填充过程 + */ + public DataFillProcess build() { + if (keyGetFun == null) { + throw new IllegalArgumentException("keyGetFun不能为空"); + } + if (fillDataGetFun == null) { + throw new IllegalArgumentException("fillDataGetFun不能为空"); + } + if(fillKeyGetFun == null){ + // 默认使用id字段 + fillKeyGetFun = e -> ReflectUtil.getFieldValue(e, "id"); + } + if (fillFun == null) { + throw new IllegalArgumentException("fillFun不能为空"); + } + return new DataFillProcess<>(keyGetFun, fillDataGetFun,fillKeyGetFun, fillFun); + } + + /** + * 获取构建器 + * @param 数据类型 + * @param 填充逻辑远程返回的数据类型 + * @return 构建器 + */ + public static DataFillProcessBuilder builder() { + return new DataFillProcessBuilder<>(); + } + + /** + * 请求, 简化填充写法 + * @param supplier 请求函数 + * @param 返回类型 + * @return 返回值 + */ + public static RE request(Supplier supplier){ + return supplier.get(); + } +} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillStrategy.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillStrategy.java deleted file mode 100644 index 0268ee673ac02d74b973e24d2e4b556a7d2030de..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillStrategy.java +++ /dev/null @@ -1,110 +0,0 @@ -package fun.easycode.jointblock.datafill; - -import cn.hutool.core.util.ClassUtil; -import cn.hutool.core.util.ReflectUtil; -import org.springframework.util.StringUtils; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.util.List; -import java.util.Optional; - -/** - * 数据填充策略 - * - * @author xuzhe - */ -public interface DataFillStrategy { - - /** - * 填充逻辑 - * - * @param metadataList 元数据集合 - */ - void fill(List metadataList); - - /** - * 获取可以处理的类型Class - * - * @return Class - */ - default Class getType() { - return void.class; - } - - /** - * 获取注解,数据源对象上增加注解也可以 - * @return - */ - default Class getAnnotation(){ - return null; - } - - /** - * 基本数据类型set逻辑 - * @param metadata - * @param container - * @param ofAnnoClass - * @param - */ - default void basicTypeSet(DataFillMetadata metadata, Object container , Class ofAnnoClass){ - Object primaryKey = metadata.getTargetObjMap().keySet().stream().findFirst().get(); - Object databaseObj = DataCopy.getInstance().getObjById(primaryKey, container, ofAnnoClass); - - // 查找不到填充对象则本地处理结束 - if(databaseObj == null){ - return; - } - - // 如果返回值是基本数据类型那么就直接就是值 - if(ClassUtil.isBasicType(databaseObj.getClass()) - || databaseObj.getClass().isAssignableFrom(String.class)){ - ReflectUtil.setFieldValue(metadata.getOpObj(), metadata.getOpField(), databaseObj); - }else { - - Object value = null; - - // 如果用户自定义了spEl表达式,那么完全以el表达式的结果作为value - // 如果没有定义spEl表达式则查找同名的属性进行填充 - if (!StringUtils.isEmpty(metadata.getDataFill().spEl())) { - Optional valueOptional = DataCopy.getInstance().parserSpEl( - databaseObj, - null, - metadata.getDataFill().spEl(), - metadata.getOpField().getType()); - value = valueOptional.orElse(null); - } else { - Field databaseObjField = ReflectUtil.getField(databaseObj.getClass() - , metadata.getOpField().getName()); - - if (databaseObjField != null) { - value = ReflectUtil.getFieldValue(databaseObj, databaseObjField); - } - } - ReflectUtil.setFieldValue(metadata.getOpObj(), metadata.getOpField(), value); - } - } - - /** - * 处理返回结构 - * @param metadata 元数据 - * @param container 数据源查询后返回的容器,就是list或者map - * @param idAnno 当返回的数据主键不是id的时候,用什么注解进行的标注 - */ - default void handlerResult(DataFillMetadata metadata, Object container, Class idAnno){ - // 如果目标对象是基本类型,我们直接去找source中有没有重名的属性 - // 有就取值 - if (ClassUtil.isBasicType(metadata.getOpField().getType()) - || metadata.getOpField().getType().isAssignableFrom(String.class)) { - basicTypeSet(metadata, container, idAnno); - }else{ - metadata.getTargetObjMap().forEach((key, value) -> { - Object databaseObj = DataCopy.getInstance().getObjById(key, container, idAnno); - if (databaseObj != null) { - DataCopy.getInstance().copy(databaseObj - , value, metadata.getDataFill().spEl()); - } - }); - } - } -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillStrategyContext.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillStrategyContext.java deleted file mode 100644 index 2ae407fe7d2f2e4adb4777b9f5018fa8aa66ce44..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillStrategyContext.java +++ /dev/null @@ -1,38 +0,0 @@ -package fun.easycode.jointblock.datafill; - -import cn.hutool.core.util.ClassUtil; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; - -import java.util.Map; -import java.util.Optional; - -/** - * 填充策略Context - * @author xuzhe - */ -public class DataFillStrategyContext implements ApplicationContextAware { - - // 实际上这里的key无用,因为判断条件需要ClassUtil.isAssignable判断 - // 并不是一个简单的key - private static Map strategies; - - /** - * 获取策略,就是根据DataFill上面的class - * @param type 类型 - * @return 填充策略 - */ - public static Optional getStrategy(Class type){ - // 填充策略寻找规则,注解和type的方式只要有一种即可 - return strategies.values().stream() - .filter(sgy -> (sgy.getAnnotation() != null && type.isAnnotationPresent(sgy.getAnnotation())) - || (sgy.getType() != void.class && ClassUtil.isAssignable(sgy.getType(), type))) - .findFirst(); - } - - @Override - public void setApplicationContext(ApplicationContext context) throws BeansException { - DataFillStrategyContext.strategies = context.getBeansOfType(DataFillStrategy.class); - } -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillTask.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillTask.java index 8aedf6f32dd8e500241fb2377f0fbece9f5f29df..6eae5793cdbf88baf3707c5b3bb838312872e1fd 100644 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillTask.java +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillTask.java @@ -2,35 +2,46 @@ package fun.easycode.jointblock.datafill; import lombok.extern.slf4j.Slf4j; +import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.concurrent.Callable; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.stream.Collectors; /** * 数据框架填充任务 - * @author xuzhe + * @author xuzhen97 */ @Slf4j -public class DataFillTask implements Callable> { +public class DataFillTask implements Callable { - private final DataFillStrategy strategy; - private final List metadataList; + private final DataFillProcess process; + private final Collection data; - public DataFillTask(DataFillStrategy handler, List metadataList){ - this.strategy = handler; - this.metadataList = metadataList; - } - - @Override - public List call() { - try { - strategy.fill(metadataList); - }catch (Exception e){ - if(log.isDebugEnabled()) { - e.printStackTrace(); - } - log.error(e.getMessage()); + public DataFillTask(DataFillProcess process, Collection data) { + this.process = process; + this.data = data; } - return null; - } + @Override + public Void call() throws Exception { + Function keyGetFun = process.getKeyGetFun(); + Function, Collection> fillDataGetFun = process.getFillDataGetFun(); + Function fillKeyGetFun = process.getFillKeyGetFun(); + BiConsumer fillFun = process.getFillFun(); + // 获取keys + List keys = data.stream().map(keyGetFun).collect(Collectors.toList()); + // 获取填充数据 + Collection fillData = fillDataGetFun.apply(keys); + // 处理填充数据成key map 方便查找 + Map idFillDataMap = fillData.stream().collect(Collectors.toMap(fillKeyGetFun, e -> e)); + // 填充数据 + data.stream().filter(d-> idFillDataMap.containsKey(keyGetFun.apply(d))).forEach(d -> { + E e = idFillDataMap.get(keyGetFun.apply(d)); + fillFun.accept(d, e); + }); + return null; + } } diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillThreadPoolManager.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillThreadExecutor.java similarity index 49% rename from jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillThreadPoolManager.java rename to jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillThreadExecutor.java index 7d36e6da65a1fcccdb6af0e35c9e0ab7d6af3442..668353cf0651daaad122c499fb13a82d8f1be1ba 100644 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillThreadPoolManager.java +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataFillThreadExecutor.java @@ -1,9 +1,6 @@ package fun.easycode.jointblock.datafill; import com.alibaba.ttl.TtlCallable; -import lombok.extern.slf4j.Slf4j; -import org.springframework.jmx.export.annotation.ManagedAttribute; -import org.springframework.jmx.export.annotation.ManagedOperation; import java.util.Collection; import java.util.List; @@ -11,13 +8,13 @@ import java.util.concurrent.*; import java.util.stream.Collectors; /** - * @author xuzhe + * 数据填充线程池 + * @author xuzhen97 */ -@Slf4j -public class DataFillThreadPoolManager { +public final class DataFillThreadExecutor { /** - * 异步任务线程池,最大队列数,无需即使性 + * 异步任务线程池,最大队列数,无需及时返回的任务,请使用此线程池 */ public static final ThreadPoolExecutor executor = new ThreadPoolExecutor(50 @@ -27,42 +24,6 @@ public class DataFillThreadPoolManager { , new LinkedBlockingQueue<>() , new ThreadPoolExecutor.AbortPolicy()); - @ManagedAttribute - public int getCorePoolSize() { - return executor.getCorePoolSize(); - } - - @ManagedAttribute - public int getMaximumPoolSize(){ - return executor.getMaximumPoolSize(); - } - - /** - * 设置线程池初始数量 - * @param corePoolSize 初始数量 - */ - @ManagedAttribute - public void setCorePoolSize(int corePoolSize){ - executor.setCorePoolSize(corePoolSize); - } - - /** - * 设置线程池最大数量 - * @param maximumPoolSize 最大数量 - */ - @ManagedAttribute - public void setMaximumPoolSize(int maximumPoolSize){ - executor.setMaximumPoolSize(maximumPoolSize); - } - - /** - * 打印填充线程池基本信息 - */ - @ManagedOperation - public void printPoolExecutorInfo() { - log.info("填充线程池corePoolSize:{}、maximumPoolSize:{}.", executor.getCorePoolSize(), executor.getMaximumPoolSize()); - } - /** * 执行异步任务,会等待全部执行完成 * @param tasks 任务集合 @@ -75,11 +36,11 @@ public class DataFillThreadPoolManager { // 主要是为了中转用户信息 List> ttlCallables = tasks.stream().map(TtlCallable::get) .collect(Collectors.toList()); + List> futures = executor.invokeAll(ttlCallables); for (Future future : futures) { future.get(); } } - } diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataGenerate.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataGenerate.java deleted file mode 100644 index 1393257cd9ec0cb87133ba784d17ac377e025b00..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataGenerate.java +++ /dev/null @@ -1,42 +0,0 @@ -package fun.easycode.jointblock.datafill; - -import java.lang.annotation.*; - -/** - * 级联查询的时候对于id生成空对象的策略 - * @author xuzhe - */ -@Target({ElementType.FIELD,ElementType.ANNOTATION_TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface DataGenerate { - /** - * 实体的主键名, 默认id - * @return id字段名 - */ - String value(); - - /** - * 目标 - * @return - */ - String targetField(); - - /** - * 如果是多对象模式集合下的的属性类型Class - * @return - */ - Class listClass() default void.class; - - /** - * 多对象模式集合下的主键分隔符默认, - * @return - */ - String listSeparator() default ","; - - /** - * id生成的模式 - * @return - */ - IDGenerateMode mode() default IDGenerateMode.DEFAULT; -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataParam.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataParam.java deleted file mode 100644 index 158eeefec36f0652fa9d45b7f541299ddf5cf84e..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/DataParam.java +++ /dev/null @@ -1,21 +0,0 @@ -package fun.easycode.jointblock.datafill; - -import java.lang.annotation.*; - -/** - * @author xuzhe - */ -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Target({ElementType.ANNOTATION_TYPE}) -public @interface DataParam { - /** - * 方法的参数名称 - */ - String name(); - - /** - * 方法参数传参值,支持spEl表达式 - */ - String value(); -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/EnableDataFill.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/EnableDataFill.java deleted file mode 100644 index feef90154c0ca34f40f83da73cbbe8e7138ef134..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/EnableDataFill.java +++ /dev/null @@ -1,13 +0,0 @@ -package fun.easycode.jointblock.datafill; - -import java.lang.annotation.*; - -/** - * 减少不必要的对象扫描 - * @author xuzhe - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@Documented -public @interface EnableDataFill { -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/FeignDataFillStrategy.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/FeignDataFillStrategy.java deleted file mode 100644 index d614cab6fd48351b1032a1f3f19651f2f66b7b32..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/FeignDataFillStrategy.java +++ /dev/null @@ -1,130 +0,0 @@ -package fun.easycode.jointblock.datafill; - -import cn.hutool.core.util.ReflectUtil; -import fun.easycode.jointblock.exception.CheckException; -import fun.easycode.jointblock.validator.IValidate; -import org.springframework.beans.BeansException; -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.cloud.openfeign.FeignContext; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.util.StringUtils; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -import java.util.*; -import java.util.stream.Collectors; - -/** - * feign填充策略 - * - * @author xuzhe - */ -public class FeignDataFillStrategy implements DataFillStrategy, ApplicationContextAware { - - private ApplicationContext context; - - @Override - public void fill(List metadataList) { - - Map, List> sourceMap = metadataList.stream() - .collect(Collectors.groupingBy(metadata -> metadata.getDataFill().source())); - - - sourceMap.forEach((feignClass, feignClassMetadataList) -> { - - FeignClient feignClient = feignClass.getAnnotation(FeignClient.class); - Object clientObj = getFeignBean(feignClient.name(), feignClass); - // 获取DataFill , 只要被处理器处理过传过来的,元数据集合注解的信息是一致的 - DataFill dataFill = metadataList.stream().findFirst().get().getDataFill(); - - Method method; - - if(StringUtils.isEmpty(dataFill.methodName())){ - Method[] methods = ReflectUtil.getMethods(feignClass); - - if (methods.length != 1) { - throw new CheckException(feignClass.getName() + "用于@DataFill时,如果不指定methodName, 则必须有且仅能有一个方法!"); - } - - method = methods[0]; - }else{ - method = ReflectUtil.getMethodByName(feignClass, dataFill.methodName()); - if(method == null){ - throw new CheckException("@DataFill 指定methodName找不到对应的方法!"); - } - } - - // 获取到client请求的主键信息 - List ids = feignClassMetadataList.stream() - .flatMap(metadata -> metadata.getTargetObjMap().keySet().stream()) - .collect(Collectors.toList()); - - // el表达式可以使用的变量 - Map contextProps = new HashMap<>(); - contextProps.put("ids", ids); - - Map> paramMap = Arrays.stream(dataFill.params()) - .collect(Collectors.toMap(DataParam::name - , param -> DataCopy.getInstance() - .parserSpEl(param.value(), Object.class, contextProps))); - - Object invoke; - // 如果标注@DataParam注解,我们将完全按照DataParam标记传参 - // 如果没有注解标记,我们认为方法有且只有一个参数,并传入ids - if (paramMap.size() == 0) { - invoke = ReflectUtil.invoke(clientObj, method, ids); - } else { - // 处理参数列表 - Parameter[] parameters = method.getParameters(); - Object[] args = new Object[parameters.length]; - - // 如果发现feign调用方法的第一个参数实现了IValidate,说明是类传参 - if(IValidate.class.isAssignableFrom(parameters[0].getType())){ - try { - Object cmd = parameters[0].getType().newInstance(); - paramMap.forEach((key, value) -> { - value.ifPresent(o -> ReflectUtil.setFieldValue(cmd, key, o)); - }); - args[0] = cmd; - } catch (Exception e) { - throw new CheckException(parameters[0].getType().getName() + "缺少无参构造!"); - } - }else{ - // 不是非类传参就是直接传参 - for (int i = 0; i < parameters.length; i++) { - Parameter parameter = parameters[i]; - args[i] = paramMap.get(parameter.getName()) == null - ? null : paramMap.get(parameter.getName()).orElse(null); - } - } - // 调用方法 - invoke = ReflectUtil.invoke(clientObj, method, args); - } - - if(invoke != null){ - feignClassMetadataList.forEach(metadata -> { - handlerResult(metadata, invoke, FeignId.class); - }); - } - }); - - } - - @Override - public Class getAnnotation() { - return FeignClient.class; - } - - @Override - public void setApplicationContext(ApplicationContext context) throws BeansException { - this.context = context; - } - - public T getFeignBean(String beanName, Class tClass) { - FeignContext feignContext = context.getBean("feignContext", FeignContext.class); - System.out.println(feignContext.getContextNames()); - return feignContext.getInstance(beanName, tClass); - } -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/FeignId.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/FeignId.java deleted file mode 100644 index 9624286835f36ca3fded16154eca614d9f75b963..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/FeignId.java +++ /dev/null @@ -1,13 +0,0 @@ -package fun.easycode.jointblock.datafill; - -import java.lang.annotation.*; - -/** - * 用来标识feign请求返回对象中哪个字段是id - * @author xuzhe - */ -@Target({ElementType.FIELD,ElementType.ANNOTATION_TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface FeignId { -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/IDGenerateMode.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/IDGenerateMode.java deleted file mode 100644 index ba3d44e4937c35068db0eb64e5fda5e11553ed05..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/IDGenerateMode.java +++ /dev/null @@ -1,18 +0,0 @@ -package fun.easycode.jointblock.datafill; - -/** - * 需要填充的id字段要做的处理 - * 因为有些是,分割的所以就需要处理,正常的1对多就没有问题 - * @author xuzhe - */ -public enum IDGenerateMode { - /** - * 默认不做任何处理 - */ - DEFAULT, - /** - * 多个,认为一个字段中有多个Id是用,号分割 - * 排序也会默认根据id的排序进行排序 - */ - MULTIPLE -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/MyBatisPlusDataFillStrategy.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/MyBatisPlusDataFillStrategy.java deleted file mode 100644 index cc031ce37b3c6ba2cec00a98a8efdf39aaca16c4..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/datafill/MyBatisPlusDataFillStrategy.java +++ /dev/null @@ -1,90 +0,0 @@ -package fun.easycode.jointblock.datafill; - -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * mybatis-plus填充策略 - * @author xuzhe - */ -public class MyBatisPlusDataFillStrategy implements DataFillStrategy, ApplicationContextAware { - - private ApplicationContext context; - - @SuppressWarnings("all") - @Override - public void fill(List metadataList) { - - Map, List> collect = metadataList.stream() - .collect(Collectors.groupingBy(metadata -> metadata.getDataFill().source())); - - collect.forEach((sourceClass, sourceClassMetadataList) -> { - BaseMapper baseMapper = (BaseMapper) context.getBean(sourceClass); - - List ids = sourceClassMetadataList.stream() - .flatMap(metadata-> metadata.getTargetObjMap().keySet().stream()) - .collect(Collectors.toList()); - - // el表达式可以使用的变量 - Map contextProps = new HashMap<>(); - contextProps.put("ids", ids); - - Map> paramMap = Arrays.stream(sourceClassMetadataList.stream() - .findFirst().get() - .getDataFill().params()) - .collect(Collectors.toMap(DataParam::name - , param -> DataCopy.getInstance() - .parserSpEl(param.value(), Object.class, contextProps))); - - Object invoke; - // 如果标注@DataParam注解,我们将完全按照DataParam标记传参 - // 如果没有注解标记,直接调用BaseMapper的selectBatchIds方法 - if (paramMap.size() == 0) { - invoke = baseMapper.selectBatchIds(ids); - } else { - // 如果传入DataParam列表,将按照参数列表插叙 - // 不要忘记自行塞入 ids, 参考el表达式 - QueryWrapper wrapper = new QueryWrapper<>(); - paramMap.forEach((paramName,paramValue)->{ - if(paramValue.isPresent()){ - // 这里支持Collection参数和单个参数, 用In或者eq - if(paramValue.get() instanceof Collection){ - wrapper.in(paramName, (Collection) paramValue.get()); - }else{ - wrapper.eq(paramName, paramValue.get()); - } - } - }); - invoke = baseMapper.selectList(wrapper); - } - - sourceClassMetadataList.forEach(metadata -> { - // 如果是指定使用自定义注解,我们将使用自定义注解 - if(metadata.getDataFill().isCustomMyBatisPlusAnno()) { - handlerResult(metadata, invoke, DataFillId.class); - }else { - handlerResult(metadata, invoke, TableId.class); - } - }); - }); - } - - @Override - public Class getType() { - return BaseMapper.class; - } - - @Override - public void setApplicationContext(ApplicationContext context) throws BeansException { - this.context = context; - } - - -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/generator/JointBlockGenerator.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/generator/JointBlockGenerator.java index c9b8edbc42b74479bbce1941cf9f76e69586fa89..9f634d1b8f4d9884fd05c2398247112443f16d4d 100644 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/generator/JointBlockGenerator.java +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/generator/JointBlockGenerator.java @@ -56,10 +56,10 @@ public final class JointBlockGenerator { .enableRemoveIsPrefix() // 去除boolean类型数据的is_前缀 .enableTableFieldAnnotation() .logicDeleteColumnName("is_deleted") - .addTableFills(new Column("created_by", FieldFill.INSERT) - , new Column("last_modified_by", FieldFill.INSERT_UPDATE) - , new Column("created_date", FieldFill.INSERT) - , new Column("last_modified_date", FieldFill.INSERT_UPDATE)); + .addTableFills(new Column("create_by", FieldFill.INSERT) + , new Column("update_by", FieldFill.INSERT_UPDATE) + , new Column("create_time", FieldFill.INSERT) + , new Column("update_time", FieldFill.INSERT_UPDATE)); // 只有表的前缀有值的时候才进行设置 if(!StringUtils.isEmpty(generatorConfig.getTablePrefix())){ diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/BatchUtil.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/BatchUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..30e66822f6fff8dfb2ced7ad618f046990253d6d --- /dev/null +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/BatchUtil.java @@ -0,0 +1,63 @@ +package fun.easycode.jointblock.util; + +import fun.easycode.jointblock.core.JointBlockMapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.session.ExecutorType; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; + +import java.util.List; + +/** + * 批处理工具类 + * 必须被spring管理才可以使用 + * @author xuzhen97 + */ +@Slf4j +public class BatchUtil { + /** + * 批处理数量 + */ + private static final int BATCH = 1000; + + /** + * MyBatis SqlSessionFactory + */ + private static SqlSessionFactory SQL_SESSION_FACTORY; + + public BatchUtil(SqlSessionFactory sqlSessionFactory) { + SQL_SESSION_FACTORY = sqlSessionFactory; + } + + /** + * 批量插入方法 + * + * @param data 需要被处理的数据 + * @param mapper 实体类的mapper + * @return int 影响的总行数 + */ + public static int saveBatch(List data, JointBlockMapper mapper) { + + if(SQL_SESSION_FACTORY == null){ + throw new RuntimeException("SQL_SESSION_FACTORY is null, 请先使用spring管理BatchUtil"); + } + + int count = 0; + SqlSession batchSqlSession = SQL_SESSION_FACTORY.openSession(ExecutorType.BATCH); + try { + for (int index = 0; index < data.size(); index++) { + count += mapper.insert(data.get(index)); + if (index != 0 && index % BATCH == 0) { + batchSqlSession.flushStatements(); + } + } + batchSqlSession.commit(); + } catch (Exception e) { + batchSqlSession.rollback(); + log.error(e.getMessage(), e); + } finally { + batchSqlSession.close(); + } + return count; + } +} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/CamelUnderUtil.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/CamelUnderUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..f2dfa69e4b9aee7654e2310634fc1b33934a96f1 --- /dev/null +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/CamelUnderUtil.java @@ -0,0 +1,38 @@ +package fun.easycode.jointblock.util; + +/** + * 驼峰下划线转换工具类 + * @author xuzhen97 + */ +public final class CamelUnderUtil { + /** + * 功能:驼峰命名转下划线命名 + * 小写和大写紧挨一起的地方,加上分隔符,然后全部转小写 + */ + public static String camel2under(String c) + { + String separator = "_"; + c = c.replaceAll("([a-z])([A-Z])", "$1"+separator+"$2").toLowerCase(); + return c; + } + + /** + * 功能:下划线命名转驼峰命名 + * 将下划线替换为空格,将字符串根据空格分割成数组,再将每个单词首字母大写 + * @param s + * @return + */ + public static String under2camel(String s) + { + String separator = "_"; + String under=""; + s = s.toLowerCase().replace(separator, " "); + String sarr[]=s.split(" "); + for(int i=0;i { + private final E error; + private final R result; + + private Either(E error, R result) { + this.error = error; + this.result = result; + } + + public static Either error(E error) { + return new Either<>(error, null); + } + + public static Either result(R result) { + return new Either<>(null, result); + } + + public Optional getError() { + return Optional.ofNullable(error); + } + + public Optional getResult() { + return Optional.ofNullable(result); + } + + public boolean isError() { + return error != null; + } + + public boolean isResult() { + return result != null; + } + + public Optional mapError(Function mapper) { + if (isError()) { + return Optional.of(mapper.apply(error)); + } + return Optional.empty(); + } + + public Optional mapResult(Function mapper) { + if (isResult()) { + return Optional.of(mapper.apply(result)); + } + return Optional.empty(); + } + + @Override + public String toString() { + if (isError()) { + return "Error: " + error.toString(); + } + return "Result: " + result.toString(); + } + + /** + * 将一个可能抛出异常的方法转换为Either + * @param function 可能抛出异常的方法 + * @return Either + * @param 参数类型 + * @param 返回值类型 + */ + public static Function> wrap(Function function){ + return t -> { + try { + return Either.result(function.apply(t)); + } catch (Exception e) { + return Either.error(e); + } + }; + } +} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/ExceptionUtil.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/ExceptionUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..27f5bce616fb5140805cf8ba172245f6ae045232 --- /dev/null +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/ExceptionUtil.java @@ -0,0 +1,13 @@ +package fun.easycode.jointblock.util; + +import java.io.PrintWriter; +import java.io.StringWriter; + +public class ExceptionUtil { + public static String getStackTrace(Throwable throwable) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw, true); + throwable.printStackTrace(pw); + return sw.getBuffer().toString(); + } +} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/LogUtil.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/LogUtil.java deleted file mode 100644 index 257a525d1de3fd10a99d58843cec76526d5b954e..0000000000000000000000000000000000000000 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/LogUtil.java +++ /dev/null @@ -1,119 +0,0 @@ -package fun.easycode.jointblock.util; - -import cn.hutool.core.annotation.AnnotationUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.ReflectUtil; -import fun.easycode.jointblock.core.Alias; -import fun.easycode.jointblock.core.DynamicOperate; -import fun.easycode.jointblock.exception.CheckException; - -import java.lang.reflect.Field; -import java.util.*; -import java.util.Map.Entry; -import java.util.stream.Collectors; - -/** - * entity update log 工具类 - * 用来获取实体的修改日志,哪些字段变更了 - * @author xuzhen97 - */ - -public class LogUtil { - - private static final ThreadLocal> OLD_ENTITY_JSON = ThreadLocal.withInitial(HashMap::new); - private static final ThreadLocal> NEW_ENTITY_JSON = ThreadLocal.withInitial(HashMap::new); - private static final ThreadLocal>> UPDATE_FIELD_QUEUE = ThreadLocal.withInitial(HashMap::new); - - /** - * 设置原先的实体 - * @param key String 唯一id, 实体id - * @param oldEntity 实体 - */ - public static void setOldEntity(String key, Object oldEntity){ - OLD_ENTITY_JSON.get().put(key, JacksonUtil.toJson(oldEntity)); - } - - /** - * 设置修改后的实体 - * @param key String 唯一id, 实体id - * @param oldEntity Object - */ - public static void setNewEntity(String key, Object oldEntity){ - NEW_ENTITY_JSON.get().put(key, JacksonUtil.toJson(oldEntity)); - } - - public static void setUpdateField(String key, String fieldName, Object fieldValue){ - Queue queue = UPDATE_FIELD_QUEUE.get().computeIfAbsent(key, k-> new LinkedList<>()); - queue.offer(fieldName); - queue.offer(fieldValue); - } - - /** - * 获取update log 先调用 - * setOldEntity - * setNewEntity - * 获取后数据会被清除,自行保存字符串 - * @param key String - * @return String - */ - public static String getUpdateLog(String key){ - - String oldJson = OLD_ENTITY_JSON.get().get(key); - String newJson = NEW_ENTITY_JSON.get().get(key); - - OLD_ENTITY_JSON.get().remove(key); - NEW_ENTITY_JSON.get().remove(key); - - return JacksonUtil.diff(oldJson, newJson); - } - - /** - * 获取数据修改日志 - * @param key - * @return - */ - public static String getFieldUpdateLog(String key, T entity){ - Queue queue = UPDATE_FIELD_QUEUE.get().get(key); - - Map entityFieldMap = ReflectUtil - .getFieldMap(entity.getClass()).entrySet().stream().map(entry->{ - // 将field map key转换成小写_ 的形式,如果Alias有值就取 - // 这里其实就是为了转换成数据库filed的形式,为了和mybatis对起来 - String mapKey; - if(AnnotationUtil.hasAnnotation(entry.getValue(), Alias.class)) { - Alias alias = entry.getValue().getAnnotation(Alias.class); - mapKey = alias.value(); - }else{ - mapKey = DynamicOperate.camel2under(entry.getKey()); - } - - return new AbstractMap.SimpleEntry<>(mapKey, entry.getValue()); - }) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); - - StringBuilder updateLogBuilder = new StringBuilder(); - - while (!queue.isEmpty()){ - - String updateFieldName = (String) queue.remove(); - Object updateFieldValue = queue.remove(); - - Field entityField = entityFieldMap.get(updateFieldName); - - if(entityField == null){ - throw new CheckException("数据库实体字段不存在!"); - } - - Object entityFieldValue = ReflectUtil.getFieldValue(entity, entityField); - // 只有值真正改变的才会打印日志 - if(ObjectUtil.notEqual(updateFieldValue, entityFieldValue)) { - updateLogBuilder.append("[key=").append(updateFieldName).append(",oldValue=") - .append(entityFieldValue).append(",newValue=").append(updateFieldValue).append("];"); - } - } - UPDATE_FIELD_QUEUE.get().remove(key); - - return updateLogBuilder.toString(); - } - -} diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/MyBatisPlusSQLLog.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/MyBatisPlusSQLLog.java similarity index 95% rename from jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/MyBatisPlusSQLLog.java rename to jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/MyBatisPlusSQLLog.java index 10099272060a5af37d3b9886c3765b68efd49645..0df77637d78bbd5887338ea768d09a952c501d12 100644 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/mybatisplus/MyBatisPlusSQLLog.java +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/MyBatisPlusSQLLog.java @@ -1,4 +1,4 @@ -package fun.easycode.jointblock.mybatisplus; +package fun.easycode.jointblock.util; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.logging.Log; diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/RequestHolder.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/RequestHolder.java similarity index 99% rename from jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/RequestHolder.java rename to jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/RequestHolder.java index 9f1aaaa9af0cd055b8346a315dcbcd5bc9414b0d..8bc63440094fe4170d50588ed2fc38aeb5100734 100644 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/core/RequestHolder.java +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/RequestHolder.java @@ -1,4 +1,4 @@ -package fun.easycode.jointblock.core; +package fun.easycode.jointblock.util; import lombok.extern.slf4j.Slf4j; import org.springframework.web.context.request.RequestAttributes; diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/URLUtil.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/URLUtil.java index 638baaa2a79a0f69b16036d81fe1a3246890f3e4..e847bc729f08e821b867c74ebee6fb35365d41e0 100644 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/URLUtil.java +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/util/URLUtil.java @@ -1,6 +1,5 @@ package fun.easycode.jointblock.util; -import fun.easycode.jointblock.core.RequestHolder; import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; diff --git a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/validator/IValidate.java b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/validator/IValidate.java index e38b51ecaee1be658379a44b0a1955f9cfe8551f..2fd0b92a5d292a92c65bb2cf67a0dac5e598cb05 100644 --- a/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/validator/IValidate.java +++ b/jointblock-spring-boot/src/main/java/fun/easycode/jointblock/validator/IValidate.java @@ -1,7 +1,6 @@ package fun.easycode.jointblock.validator; -import fun.easycode.jointblock.exception.CheckException; -import org.springframework.util.StringUtils; +import fun.easycode.jointblock.core.CheckException; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; diff --git a/pom.xml b/pom.xml index 5e6c16721fd8d7a5421495982752550de246ac29..55fa29f9db42964f747b14cf87d10a0bbf845218 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ 8 8 UTF-8 - 0.29.0 + 0.30.0