diff --git a/.gitignore b/.gitignore index 2a2237645ffd36e5a9133cd9bc91a335f5c307a8..3f6cead8ce1981e9673315f9e20df71e2b16e7bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ # Ignore Gradle project-specific cache directory .gradle *.iml -application.properties application-*.properties .DS_Store diff --git a/README.md b/README.md index f71bb37f7717edff5bb5684dde47e195201ec910..0c368225d1045dcfa1a585c3abc00df6a30d6a42 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -> 可以告别常规SQL和CRUD了!diboot新用户: [看视频快速了解diboot](https://www.bilibili.com/video/BV17V411r7Cc) 、 [手把手跟我来体验](https://www.diboot.com/guide/newer/bootstrap.html) +> 用上diboot,告别常规SQL和CRUD,写的更少,性能更好! + +> 新用户指南: [手把手来体验](https://www.diboot.com/guide/newer/bootstrap.html) 、[看视频了解我](https://www.bilibili.com/video/BV17V411r7Cc) 、[如何做到高性能](https://www.bilibili.com/video/BV1tL411p7CD) # diboot - 基础组件化繁为简,高效工具以简驭繁

@@ -14,46 +16,39 @@ ![diboot平台组成结构图](https://www.diboot.com/structure.png) -spring boot版本目前实现: core高效内核 + devtools开发助理 + IAM身份认证、file文件处理、scheduler定时任务等基础组件 + diboot-*-admin基础后台。 - -> [spring cloud版本,移步这里->](https://github.com/dibo-software/diboot-cloud) +> [diboot-cloud 微服务版本,看这里->](https://www.diboot.com/ent/service.html) -> [diboot-workflow 工作流授权](https://www.diboot.com/ent/service.html) +> [diboot-workflow 工作流版本,看这里->](https://www.diboot.com/ent/service.html) ## diboot基础组件 -### 1、 diboot-core: 精简优化内核 -高效精简内核,重构查询方式(拆解关联查询,程序中Join),简化开发,主要实现: -#### 1). 单表CRUD无SQL - > 基于Mybatis-Plus实现(Mybatis-Plus具备通用Mapper方案和灵活的查询构造器) -#### 2). 关联绑定无SQL(注解自动绑定) - > 扩展实现了多表关联查询的无SQL方案,只需要一个简单注解@Bind*,就可以实现关联对象(含字段、字段集合、实体、实体集合等)的数据绑定,且实现方案是将关联查询拆解为单表查询,保障最佳性能。 -#### 3). 数据字典无SQL(注解自动绑定) - > 通过@BindDict注解实现数据字典(枚举)的存储值value与显示值name的转换。 -#### 4). 跨表查询无SQL(自动构建QueryWrapper与查询) - > @BindQuery注解绑定字段查询方式及关联表,自动构建QueryWrapper,并动态执行单表或Join联表查询。 -#### 5). BaseService扩展增强,支持常规的单表及关联开发场景接口 - > createEntityAndRelatedEntities、getValuesOfField、exists、getKeyValueList、getViewObject*等接口 -#### 6). 其他常用工具类、状态码、异常处理的最佳实践封装 - > JsonResult、字符串处理、常用校验、BeanUtils、DateUtils等 - -基于diboot-core 2.x版本的CRUD和简单关联的常规功能实现,代码量比传统Mybatis项目减少80%+),且实现更高效更易维护。 +### 1、 diboot-core: 精简优化内核:写的更少,性能更好 +主要特性: +* 单表CRUD无SQL +* 关联绑定无SQL(注解自动绑定) +* 数据字典无SQL(注解自动绑定) +* 跨表查询无SQL(自动构建QueryWrapper与查询) +* BaseService扩展增强,支持常规的单表及关联开发场景接口 +* 其他常用工具类、状态码、异常处理的更优实践封装 + +基于diboot-core的CRUD和常规关联的功能实现,代码量比传统Mybatis项目减少80%+,且性能更好更易维护。 > 详细文档: [diboot-core文档](https://www.diboot.com/guide/diboot-core/%E7%AE%80%E4%BB%8B.html). ### 2、IAM 身份认证基础组件 及 配套VUE前端框架(diboot-antd-admin、diboot-element-admin) * 开箱即用的RBAC角色权限模型与预置组织人员岗位模型 -* 基于JWT的认证授权,支持申请token、刷新token +* 基于JWT的认证授权,支持申请token、刷新token、无状态认证 * 简化的BindPermission注解,支持兼容shiro的简化权限绑定与自动鉴权 -* 简化的Log注解记录操作日志 * 自动提取需要验证的后端接口, 借助前端功能方便绑定前后端菜单按钮权限 -* 支持基于注解的数据权限实现 +* 无缝适配redis,引入redis依赖即可启用shiro的redis缓存 +* 支持基于注解的数据权限实现、简化的Log注解记录操作日志等 * 支持灵活的扩展能力(扩展多种登录方式、灵活替换用户实体类、自定义缓存等) > 详细文档: [diboot-iam文档](https://www.diboot.com/guide/diboot-iam/%E4%BB%8B%E7%BB%8D.html). ### 3、diboot-file 文件相关处理组件 * EasyExcel轻量封装,支持Java注解校验与@ExcelBind*注解实现字典及关联字段的name-value转换,并提供完善的校验错误提示 -* 封装常用的文件本地存储、上传下载、图片压缩水印等常用处理 +* 文件存储接口化,预置本地存储,简单扩展OSS、分布式存储等实现 +* 封装常用的文件上传下载、图片压缩水印等常用处理 > 详细文档: [diboot-file文档](https://www.diboot.com/guide/diboot-file/%E4%BB%8B%E7%BB%8D.html). ### 4、diboot-scheduler 定时任务组件 @@ -67,22 +62,18 @@ spring boot版本目前实现: core高效内核 + devtools开发助理 + IAM身 * 支持多通道的消息通知发送 ## devtools开发助理 +* 极简易用(引入依赖jar,配置参数后,即可随应用启动运行) +* 功能强大(数据结构与代码同步、前后端代码一键生成、前端面板组件编排) +* 配置灵活(可按需配置生成代码路径及启用`Lombok`、`Swagger`等) +* 代码标准(devtools标准化了数据结构定义与代码实现,降低维护成本) +* 支持多库(MySQL、MariaDB、ORACLE、SQLServer、PostgreSQL) -* 使用很简单(UI界面操作,引入依赖配置参数后,即可随Spring boot/Spring cloud本地项目启动运行) -* 功能很强大: - * 单表与关联场景CRUD导入导出的完整功能全自动生成,无需手写代码 - * 结合前端面板组件编排能力,覆盖更多场景的自动化生成 - * 数据结构变更与代码联动同步,自动记录变更SQL、维护索引 - * 一键生成代码&非覆盖式更新本地后端代码 -* 配置很灵活(可按需配置生成代码路径,是否启用`Lombok`、`Swagger`等) -* SQL与代码很标准(devtools标准化了数据结构定义与代码实现) -* 支持多数据库(MySQL、MariaDB、ORACLE、SQLServer、PostgreSQL) > 详细文档: [diboot-devtools文档](https://www.diboot.com/guide/diboot-devtools/%E4%BB%8B%E7%BB%8D.html). ## 捐助支持 捐助二维码 -感谢所有捐助的朋友为开源事业的发展做出的努力。 +感谢每一位支持diboot的粉丝朋友。 ## 技术交流 如遇diboot相关技术问题,欢迎加群交流: diff --git a/diboot-core-starter/pom.xml b/diboot-core-starter/pom.xml index e976c435a63f84e533b2eb5f6ffae537682aa8f1..f29cb0149786f63f503a5fc237848b45dd9d15fc 100644 --- a/diboot-core-starter/pom.xml +++ b/diboot-core-starter/pom.xml @@ -7,11 +7,11 @@ com.diboot diboot-root - 2.2.1 + 2.3.0 diboot-core-spring-boot-starter - 2.2.1 + 2.3.0 jar diboot core starter project @@ -41,6 +41,12 @@ ${diboot.version} + + + org.springframework.boot + spring-boot-starter-data-redis + provided + org.springframework.boot @@ -52,6 +58,5 @@ junit test - - \ No newline at end of file + diff --git a/diboot-core-starter/src/main/java/com/diboot/core/starter/CoreAutoConfiguration.java b/diboot-core-starter/src/main/java/com/diboot/core/starter/CoreAutoConfig.java similarity index 98% rename from diboot-core-starter/src/main/java/com/diboot/core/starter/CoreAutoConfiguration.java rename to diboot-core-starter/src/main/java/com/diboot/core/starter/CoreAutoConfig.java index 5b7f665a615546903ab76db151b440b01c5156a8..ea83aa3efed062ed02c20d70ec2f56a71deb0530 100644 --- a/diboot-core-starter/src/main/java/com/diboot/core/starter/CoreAutoConfiguration.java +++ b/diboot-core-starter/src/main/java/com/diboot/core/starter/CoreAutoConfig.java @@ -55,8 +55,8 @@ import java.util.TimeZone; @EnableConfigurationProperties(CoreProperties.class) @ComponentScan(basePackages = {"com.diboot.core"}) @MapperScan(basePackages = {"com.diboot.core.mapper"}) -public class CoreAutoConfiguration implements WebMvcConfigurer { - private static final Logger log = LoggerFactory.getLogger(CoreAutoConfiguration.class); +public class CoreAutoConfig implements WebMvcConfigurer { + private static final Logger log = LoggerFactory.getLogger(CoreAutoConfig.class); @Value("${spring.jackson.date-format:"+D.FORMAT_DATETIME_Y4MDHMS+"}") private String defaultDatePattern; diff --git a/diboot-core-starter/src/main/java/com/diboot/core/starter/CoreProperties.java b/diboot-core-starter/src/main/java/com/diboot/core/starter/CoreProperties.java index 9a2624367be7fc1150cfeb60b59d7ca60c7a8e40..57f5a3f9c1a33bef1d8fed2bbe84a37af3c99caf 100644 --- a/diboot-core-starter/src/main/java/com/diboot/core/starter/CoreProperties.java +++ b/diboot-core-starter/src/main/java/com/diboot/core/starter/CoreProperties.java @@ -25,7 +25,14 @@ import org.springframework.boot.context.properties.ConfigurationProperties; */ @ConfigurationProperties(prefix = "diboot.core") public class CoreProperties { - + /** + * 每页记录数量 + */ + private int pageSize = 20; + /** + * 每批次数量 + */ + private int batchSize = 1000; /** * 是否初始化,默认true自动安装SQL */ @@ -38,4 +45,21 @@ public class CoreProperties { public void setInitSql(boolean initSql) { this.initSql = initSql; } + + public void setBatchSize(int batchSize) { + this.batchSize = batchSize; + } + + public int getBatchSize() { + return batchSize; + } + + public void setPageSize(int pageSize) { + this.pageSize = pageSize; + } + + public int getPageSize() { + return pageSize; + } + } diff --git a/diboot-core-starter/src/main/java/com/diboot/core/starter/RedisAutoConfig.java b/diboot-core-starter/src/main/java/com/diboot/core/starter/RedisAutoConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..055868addb8e6907921978a750b83cc437beffdc --- /dev/null +++ b/diboot-core-starter/src/main/java/com/diboot/core/starter/RedisAutoConfig.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.diboot.core.starter; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.ser.FilterProvider; +import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; +import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnResource; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * Redis 自动配置 + * + * @author wind + * @version v2.3.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass(RedisOperations.class) +@ConditionalOnResource(resources = "org/springframework/data/redis") +public class RedisAutoConfig { + + @Bean + @ConditionalOnMissingBean + public RedisTemplate redisTemplate(LettuceConnectionFactory connectionFactory) { + //... 初始化RedisTemplate + RedisTemplate redisTemplate = new RedisTemplate<>(); + + StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); + // 用StringRedisSerializer 序列化和反序列化key值 + redisTemplate.setKeySerializer(stringRedisSerializer); + redisTemplate.setHashKeySerializer(stringRedisSerializer); + + // 用Jackson2JsonRedisSerializer 序列化和反序列化value值 + redisTemplate.setValueSerializer(jackson2JsonRedisSerializer()); + redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer()); + + redisTemplate.setConnectionFactory(connectionFactory); + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } + + private Jackson2JsonRedisSerializer jackson2JsonRedisSerializer() { + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.setVisibility(PropertyAccessor.ALL , JsonAutoDetect.Visibility.ANY); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES , false); + objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS , false); + objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator() , ObjectMapper.DefaultTyping.NON_FINAL); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + FilterProvider filterProvider = new SimpleFilterProvider().addFilter("rewrite-bean" , SimpleBeanPropertyFilter.serializeAllExcept("realmNames")); + objectMapper.setFilterProvider(filterProvider); + jackson2JsonRedisSerializer.setObjectMapper(objectMapper); + return jackson2JsonRedisSerializer; + } + +} diff --git a/diboot-core-starter/src/main/resources/META-INF/spring.factories b/diboot-core-starter/src/main/resources/META-INF/spring.factories index b949e883423492888b2e8a5ed4fd3b6943581e62..0b873c6f86ec3d04ab044bd2daf34b7ac36d5d69 100644 --- a/diboot-core-starter/src/main/resources/META-INF/spring.factories +++ b/diboot-core-starter/src/main/resources/META-INF/spring.factories @@ -1 +1 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.diboot.core.starter.CoreAutoConfiguration \ No newline at end of file +org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.diboot.core.starter.CoreAutoConfig \ No newline at end of file diff --git a/diboot-core-starter/src/test/java/diboot/core/test/binder/TestDictBinder.java b/diboot-core-starter/src/test/java/diboot/core/test/binder/TestDictBinder.java new file mode 100644 index 0000000000000000000000000000000000000000..e95c94498177c131e8f0bb70248df0d53253de20 --- /dev/null +++ b/diboot-core-starter/src/test/java/diboot/core/test/binder/TestDictBinder.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package diboot.core.test.binder; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.diboot.core.binding.Binder; +import com.diboot.core.util.BeanUtils; +import com.diboot.core.util.JSON; +import com.diboot.core.util.V; +import diboot.core.test.StartupApplication; +import diboot.core.test.binder.entity.User; +import diboot.core.test.binder.service.UserService; +import diboot.core.test.binder.vo.UserVO; +import diboot.core.test.config.SpringMvcConfig; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.List; + +/** + * 测试字典绑定 + * @author mazc@dibo.ltd + * @version v2.0 + * @date 2021/06/18 + */ +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = {SpringMvcConfig.class}) +@SpringBootTest(classes = {StartupApplication.class}) +public class TestDictBinder { + + @Autowired + UserService userService; + + @Test + public void testBinder(){ + // 加载测试数据 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(User::getId, 1001L, 1002L); + List userList = userService.getEntityList(queryWrapper); + // 自动绑定 + List voList = Binder.convertAndBindRelations(userList, UserVO.class); + // 验证绑定结果 + Assert.assertTrue(V.notEmpty(voList)); + for(UserVO vo : voList){ + // 验证直接关联和通过中间表间接关联的绑定 + Assert.assertNotNull(vo.getGenderLabel()); + System.out.println(JSON.stringify(vo)); + } + // 单个entity接口测试 + UserVO singleVO = BeanUtils.convert(userList.get(1), UserVO.class); + Binder.bindRelations(singleVO); + // 验证直接关联和通过中间表间接关联的绑定 + Assert.assertNotNull(singleVO.getGenderLabel()); + System.out.println(JSON.stringify(singleVO)); + } + +} diff --git a/diboot-core-starter/src/test/java/diboot/core/test/binder/TestFieldBinder.java b/diboot-core-starter/src/test/java/diboot/core/test/binder/TestFieldBinder.java index a9d813aaeadb9bc9a9070e55371be554ee16c5b7..634caa1e5f70020418591a79a5ed5d8674756fb5 100644 --- a/diboot-core-starter/src/test/java/diboot/core/test/binder/TestFieldBinder.java +++ b/diboot-core-starter/src/test/java/diboot/core/test/binder/TestFieldBinder.java @@ -100,6 +100,22 @@ public class TestFieldBinder { } } + @Test + public void testBinderWithEscCol(){ + // 加载测试数据 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(User::getId, 1001L, 1002L); + List userList = userService.getEntityList(queryWrapper); + // 自动绑定 + List voList = Binder.convertAndBindRelations(userList, UserEscVO.class); + if(V.notEmpty(voList)){ + for(UserEscVO vo : voList){ + Assert.assertNotNull(vo.getDeptCharacter()); + Assert.assertNotNull(vo.getDeptName()); + Assert.assertNotNull(vo.getOrgName()); + } + } + } @Test public void testDictVoBind(){ diff --git a/diboot-core-starter/src/test/java/diboot/core/test/binder/dto/DepartmentDTO.java b/diboot-core-starter/src/test/java/diboot/core/test/binder/dto/DepartmentDTO.java index 4e947950ab3d414b727c87510a165adac0f212d3..be0c98a076f92bac71ddaf84af5a97e05bf277c8 100644 --- a/diboot-core-starter/src/test/java/diboot/core/test/binder/dto/DepartmentDTO.java +++ b/diboot-core-starter/src/test/java/diboot/core/test/binder/dto/DepartmentDTO.java @@ -15,6 +15,7 @@ */ package diboot.core.test.binder.dto; +import com.baomidou.mybatisplus.annotation.TableField; import com.diboot.core.binding.data.CheckpointType; import com.diboot.core.binding.data.DataAccessCheckpoint; import com.diboot.core.binding.query.BindQuery; @@ -28,6 +29,7 @@ import lombok.experimental.Accessors; import java.io.Serializable; import java.util.Date; +import java.util.List; /** * Department DTO @@ -65,6 +67,13 @@ public class DepartmentDTO implements Serializable { @BindQuery(comparison = Comparison.LT, field = "createTime") private Date createTimeEnd; + @BindQuery(field = "parent_id", comparison = Comparison.IN) + private List parentIds; + + @BindQuery(comparison = Comparison.LIKE) + @TableField("`character`") + private String character; + public Date getCreateTimeEnd(){ return D.nextDay(createTime); } diff --git a/diboot-core-starter/src/test/java/diboot/core/test/binder/dto/UserDTO.java b/diboot-core-starter/src/test/java/diboot/core/test/binder/dto/UserDTO.java index 3a5b4f07d7f4e52bcfdaf4d00c78cb660c6a2a6f..5cb77e15c825ed0a741f6066f4128bd9d40cd774 100644 --- a/diboot-core-starter/src/test/java/diboot/core/test/binder/dto/UserDTO.java +++ b/diboot-core-starter/src/test/java/diboot/core/test/binder/dto/UserDTO.java @@ -23,6 +23,9 @@ import diboot.core.test.binder.entity.Organization; import diboot.core.test.binder.entity.Role; import diboot.core.test.binder.entity.User; import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; import java.util.List; @@ -32,7 +35,8 @@ import java.util.List; * @version v2.0 * @date 2018/12/27 */ -@Data +@Getter +@Setter public class UserDTO extends User { // 字段关联 @@ -45,7 +49,7 @@ public class UserDTO extends User { // 通过中间表关联Entity @BindQuery(comparison = Comparison.ENDSWITH, entity = Organization.class, field = "name", - condition = "this.department_id=department.id AND department.org_id=id AND parent_id=0", strategy = Strategy.INCLUDE_EMPTY_STRING) + condition = "this.department_id=department.id AND department.org_id=id AND parent_id=0", strategy = Strategy.INCLUDE_EMPTY) private String orgName; // LEFT JOIN department r2m ON self.department_id = r2m.id // LEFT JOIN organization r1 ON r2m.org_id=r2.id diff --git a/diboot-core-starter/src/test/java/diboot/core/test/binder/entity/Department.java b/diboot-core-starter/src/test/java/diboot/core/test/binder/entity/Department.java index 90d9b8f235efce9ecda9aae68e803893bebd5e6f..2715d376641ffbfe1339679f807d9a8ad7f98229 100644 --- a/diboot-core-starter/src/test/java/diboot/core/test/binder/entity/Department.java +++ b/diboot-core-starter/src/test/java/diboot/core/test/binder/entity/Department.java @@ -44,4 +44,7 @@ public class Department extends BaseEntity { @BindQuery(comparison = Comparison.CONTAINS) @TableField private String name; + + @TableField("`character`") + private String character; } \ No newline at end of file diff --git a/diboot-core-starter/src/test/java/diboot/core/test/binder/entity/MyBaseEntity.java b/diboot-core-starter/src/test/java/diboot/core/test/binder/entity/MyBaseEntity.java index 44cf0cfd921750e66a02ddcf159c42dcbc2fa7ec..d602552db1b8e2d221378dda59a4d0fa533eabb4 100644 --- a/diboot-core-starter/src/test/java/diboot/core/test/binder/entity/MyBaseEntity.java +++ b/diboot-core-starter/src/test/java/diboot/core/test/binder/entity/MyBaseEntity.java @@ -35,8 +35,4 @@ public abstract class MyBaseEntity implements Serializable { updateStrategy = FieldStrategy.NOT_NULL) private LocalDateTime updateTs; - - @Version - private Integer version; - } diff --git a/diboot-core-starter/src/test/java/diboot/core/test/binder/entity/User.java b/diboot-core-starter/src/test/java/diboot/core/test/binder/entity/User.java index f264cfa93469badbcc4f3aef9d9660dbcbba36ad..53fdff2297c296ffeb066ace3e48e7fc09fa8232 100644 --- a/diboot-core-starter/src/test/java/diboot/core/test/binder/entity/User.java +++ b/diboot-core-starter/src/test/java/diboot/core/test/binder/entity/User.java @@ -53,4 +53,7 @@ public class User extends BaseEntity { @JsonFormat(pattern = D.FORMAT_DATE_Y4MD) private LocalDateTime localDatetime; + + @TableField("`character`") + private String character; } diff --git a/diboot-core-starter/src/test/java/diboot/core/test/binder/mapper/DbGoodsGoodsInfoMapper.java b/diboot-core-starter/src/test/java/diboot/core/test/binder/mapper/DbGoodsGoodsInfoMapper.java index 3e2eb470374af7b46b3207823aa663a36d72f70b..58b23154f8a2acd3985aebff498b475343c4f969 100644 --- a/diboot-core-starter/src/test/java/diboot/core/test/binder/mapper/DbGoodsGoodsInfoMapper.java +++ b/diboot-core-starter/src/test/java/diboot/core/test/binder/mapper/DbGoodsGoodsInfoMapper.java @@ -2,7 +2,6 @@ package diboot.core.test.binder.mapper; import com.diboot.core.mapper.BaseCrudMapper; import diboot.core.test.binder.entity.DbGoodsGoodsInfo; -import diboot.core.test.binder.entity.DbPurchaseFormPlan; import org.apache.ibatis.annotations.Mapper; /** diff --git a/diboot-core-starter/src/test/java/diboot/core/test/binder/mapper/DbPurchaseFormPlanMapper.java b/diboot-core-starter/src/test/java/diboot/core/test/binder/mapper/DbPurchaseFormPlanMapper.java index ac4d71e21ed922ffadc3d9cfc4dcf740cfbcd677..376ccccda4758fb755b8314103144c8d13e91d1e 100644 --- a/diboot-core-starter/src/test/java/diboot/core/test/binder/mapper/DbPurchaseFormPlanMapper.java +++ b/diboot-core-starter/src/test/java/diboot/core/test/binder/mapper/DbPurchaseFormPlanMapper.java @@ -1,6 +1,5 @@ package diboot.core.test.binder.mapper; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.diboot.core.mapper.BaseCrudMapper; import diboot.core.test.binder.entity.DbPurchaseFormPlan; import org.apache.ibatis.annotations.Mapper; diff --git a/diboot-core-starter/src/test/java/diboot/core/test/binder/mapper/DbPurchaseRelPlanGoodsMapper.java b/diboot-core-starter/src/test/java/diboot/core/test/binder/mapper/DbPurchaseRelPlanGoodsMapper.java index c1856cb611d1fd5f759189c4d78fe84fde89e40d..00d1eff9be0a5c3d2a5c3a4932f60fca585042fe 100644 --- a/diboot-core-starter/src/test/java/diboot/core/test/binder/mapper/DbPurchaseRelPlanGoodsMapper.java +++ b/diboot-core-starter/src/test/java/diboot/core/test/binder/mapper/DbPurchaseRelPlanGoodsMapper.java @@ -1,7 +1,6 @@ package diboot.core.test.binder.mapper; import com.diboot.core.mapper.BaseCrudMapper; -import diboot.core.test.binder.entity.DbPurchaseFormPlan; import diboot.core.test.binder.entity.DbPurchaseRelPlanGoods; import org.apache.ibatis.annotations.Mapper; diff --git a/diboot-core-starter/src/test/java/diboot/core/test/binder/service/DbPurchaseFormPlanSvc.java b/diboot-core-starter/src/test/java/diboot/core/test/binder/service/DbPurchaseFormPlanSvc.java index 7438ac748ea12ff0d87ff4b8f344c06b48aa010b..1685a39dabda92e33c2426eaa0d7bb401755fcf4 100644 --- a/diboot-core-starter/src/test/java/diboot/core/test/binder/service/DbPurchaseFormPlanSvc.java +++ b/diboot-core-starter/src/test/java/diboot/core/test/binder/service/DbPurchaseFormPlanSvc.java @@ -1,6 +1,5 @@ package diboot.core.test.binder.service; -import com.baomidou.mybatisplus.extension.service.IService; import com.diboot.core.service.BaseService; import diboot.core.test.binder.entity.DbPurchaseFormPlan; diff --git a/diboot-core-starter/src/test/java/diboot/core/test/binder/service/impl/DbPurchaseFormPlanSvcImpl.java b/diboot-core-starter/src/test/java/diboot/core/test/binder/service/impl/DbPurchaseFormPlanSvcImpl.java index 4e3276408c186acad47e4749154a0c5646e5b444..02396ca4b7dff56b119454e4a0af4060fb2ec3b9 100644 --- a/diboot-core-starter/src/test/java/diboot/core/test/binder/service/impl/DbPurchaseFormPlanSvcImpl.java +++ b/diboot-core-starter/src/test/java/diboot/core/test/binder/service/impl/DbPurchaseFormPlanSvcImpl.java @@ -15,7 +15,6 @@ */ package diboot.core.test.binder.service.impl; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.diboot.core.service.impl.BaseServiceImpl; import diboot.core.test.binder.entity.DbPurchaseFormPlan; import diboot.core.test.binder.mapper.DbPurchaseFormPlanMapper; diff --git a/diboot-core-starter/src/test/java/diboot/core/test/binder/vo/DeepBindVO.java b/diboot-core-starter/src/test/java/diboot/core/test/binder/vo/DeepBindVO.java index 760e7c8e42574f104fd51dd03f599552a1c6b6d0..bac3e46431ccaf3724499a789ceeb1230bb75589 100644 --- a/diboot-core-starter/src/test/java/diboot/core/test/binder/vo/DeepBindVO.java +++ b/diboot-core-starter/src/test/java/diboot/core/test/binder/vo/DeepBindVO.java @@ -45,7 +45,7 @@ public class DeepBindVO extends Department{ @BindEntity(entity = Organization.class, condition = "this.org_id=id", deepBind = true) // AND ... private OrganizationVO organizationVO; - @BindEntityList(entity = Department.class, condition = "this.id=parent_id AND this.name IS NOT NULL", deepBind = true) // AND ... + @BindEntityList(entity = Department.class, condition = "parent_id=this.id AND this.name IS NOT NULL", deepBind = true) // AND ... private List children; } \ No newline at end of file diff --git a/diboot-core-starter/src/test/java/diboot/core/test/binder/vo/OrganizationVO.java b/diboot-core-starter/src/test/java/diboot/core/test/binder/vo/OrganizationVO.java index 9aaeaee51bfe53069faf947fa92cef27a1433c28..1be1d3ca8400c0353254bb93e8b33d4950663ee1 100644 --- a/diboot-core-starter/src/test/java/diboot/core/test/binder/vo/OrganizationVO.java +++ b/diboot-core-starter/src/test/java/diboot/core/test/binder/vo/OrganizationVO.java @@ -15,7 +15,6 @@ */ package diboot.core.test.binder.vo; -import com.baomidou.mybatisplus.annotation.TableField; import com.diboot.core.binding.annotation.BindDict; import com.diboot.core.binding.annotation.BindEntity; import com.diboot.core.binding.annotation.BindField; diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/util/AnnotationUtils.java b/diboot-core-starter/src/test/java/diboot/core/test/binder/vo/SimpleDictionaryVO.java similarity index 52% rename from diboot-iam-starter/src/main/java/com/diboot/iam/util/AnnotationUtils.java rename to diboot-core-starter/src/test/java/diboot/core/test/binder/vo/SimpleDictionaryVO.java index 64f74c15eceaf35ee8c8985b9421a8ab8d0d3060..a5ec9e4551cc23eee90590f86ef54cdd97c7c7e8 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/util/AnnotationUtils.java +++ b/diboot-core-starter/src/test/java/diboot/core/test/binder/vo/SimpleDictionaryVO.java @@ -13,16 +13,31 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.diboot.iam.util; +package diboot.core.test.binder.vo; + +import com.diboot.core.binding.annotation.BindEntityList; +import com.diboot.core.entity.Dictionary; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.util.List; /** - * 注解相关工具类 - * @author mazc@dibo.ltd - * @version v2.0 - * @date 2019/12/23 - * @see com.diboot.core.util.AnnotationUtils + * 数据字典的简化VO测试类 */ -@Deprecated -public class AnnotationUtils extends com.diboot.core.util.AnnotationUtils { +@Getter +@Setter +@Accessors(chain = true) +public class SimpleDictionaryVO { + + private Long id; + + private String type; + + private String sortId; + + @BindEntityList(entity= Dictionary.class, condition="this.type=type AND this.id=parent_id", orderBy = "sort_id:ASC") + private List children; -} \ No newline at end of file +} diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/annotation/process/ApiPermissionCache.java b/diboot-core-starter/src/test/java/diboot/core/test/binder/vo/UserEscVO.java similarity index 37% rename from diboot-iam-starter/src/main/java/com/diboot/iam/annotation/process/ApiPermissionCache.java rename to diboot-core-starter/src/test/java/diboot/core/test/binder/vo/UserEscVO.java index aab2962606c9fe311ae0da57774977eec24cfa96..91433ef56ef24cfaa6511a3c32d04a4525dd6356 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/annotation/process/ApiPermissionCache.java +++ b/diboot-core-starter/src/test/java/diboot/core/test/binder/vo/UserEscVO.java @@ -13,53 +13,35 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.diboot.iam.annotation.process; +package diboot.core.test.binder.vo; -import com.diboot.iam.cache.IamCacheManager; - -import java.util.List; +import com.diboot.core.binding.annotation.BindField; +import diboot.core.test.binder.entity.Department; +import diboot.core.test.binder.entity.Organization; +import diboot.core.test.binder.entity.User; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; /** - * 注解相关缓存 - * {@link IamCacheManager} + * + * * @author mazc@dibo.ltd * @version v2.0 - * @date 2019/12/30 + * @date 2019/06/22 */ -@Deprecated -public class ApiPermissionCache { - - /** - * 读取缓存permission - * {@link IamCacheManager#getPermissionCode(String)} - * @param requestMethodAndUrl - * @return - */ - @Deprecated - public static String getPermissionCode(String requestMethodAndUrl){ - return IamCacheManager.getPermissionCode(requestMethodAndUrl); - } +@Getter +@Setter +@Accessors(chain = true) +public class UserEscVO extends User { + private static final long serialVersionUID = -5566013299475080343L; - /** - * 读取缓存permission - * {@link IamCacheManager#getPermissionCode(String, String)} - * @param requestMethod - * @param url - * @return - */ - @Deprecated - public static String getPermissionCode(String requestMethod, String url){ - return IamCacheManager.getPermissionCode(requestMethod, url); - } + @BindField(entity= Department.class, field="character", condition="this.department_id=id") + private String deptCharacter; - /** - * 返回全部ApiPermissionVO - * {@link IamCacheManager#getApiPermissionVoList()} - * @return - */ - @Deprecated - public static List getApiPermissionVoList(){ - return IamCacheManager.getApiPermissionVoList(); - } + @BindField(entity = Department.class, field="name", condition="this.`character`=`character`") + private String deptName; -} \ No newline at end of file + @BindField(entity = Organization.class, field="name", condition="this.`character`=department.`character` AND department.org_id=id") + private String orgName; +} diff --git a/diboot-core-starter/src/test/java/diboot/core/test/query/TestIssue72.java b/diboot-core-starter/src/test/java/diboot/core/test/query/TestIssue72.java index 3b9db32275a975a4dda5f90a6902ed553a08513a..712e11437948e853b87aed69e3a599a84ec6cf78 100644 --- a/diboot-core-starter/src/test/java/diboot/core/test/query/TestIssue72.java +++ b/diboot-core-starter/src/test/java/diboot/core/test/query/TestIssue72.java @@ -4,11 +4,11 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.diboot.core.binding.QueryBuilder; import com.diboot.core.vo.Pagination; import diboot.core.test.StartupApplication; +import diboot.core.test.binder.dto.PurchaseFormPlanQueryDto; +import diboot.core.test.binder.entity.DbPurchaseFormPlan; import diboot.core.test.binder.service.DbPurchaseFormPlanSvc; import diboot.core.test.binder.vo.DbPurchaseFormPlanVO; import diboot.core.test.config.SpringMvcConfig; -import diboot.core.test.binder.entity.DbPurchaseFormPlan; -import diboot.core.test.binder.dto.PurchaseFormPlanQueryDto; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/diboot-core-starter/src/test/java/diboot/core/test/query/TestJoinQuery.java b/diboot-core-starter/src/test/java/diboot/core/test/query/TestJoinQuery.java index c122fbe84a37c77fa9e18a84dfd97664a31a6e48..29ea1103abc5d10e565d45bed09c8a343ee5fb9a 100644 --- a/diboot-core-starter/src/test/java/diboot/core/test/query/TestJoinQuery.java +++ b/diboot-core-starter/src/test/java/diboot/core/test/query/TestJoinQuery.java @@ -61,11 +61,28 @@ public class TestJoinQuery { Department example = departmentService.list(null).get(0); DepartmentDTO departmentDTO = new DepartmentDTO(); departmentDTO.setCreateTime(example.getCreateTime()); + departmentDTO.setCharacter(example.getCharacter()); QueryWrapper queryWrapper = QueryBuilder.toQueryWrapper(departmentDTO); List list = departmentService.list(queryWrapper); Assert.assertTrue(list.size() >= 1); } + @Test + public void testInQuery(){ + List parentIds = new ArrayList<>(); + DepartmentDTO departmentDTO = new DepartmentDTO(); + departmentDTO.setParentIds(parentIds); + QueryWrapper queryWrapper = QueryBuilder.toQueryWrapper(departmentDTO); + System.out.println(queryWrapper.getExpression()); + List list = Binder.joinQueryList(queryWrapper, Department.class); + Assert.assertTrue(list.size() > 1); + + parentIds.add(10001L); + queryWrapper = QueryBuilder.toQueryWrapper(departmentDTO); + list = Binder.joinQueryList(queryWrapper, Department.class); + Assert.assertTrue(list.size() > 0); + } + @Test public void testSingleTableQuery(){ Department entity = new Department(); diff --git a/diboot-core-starter/src/test/java/diboot/core/test/service/BaseServiceTest.java b/diboot-core-starter/src/test/java/diboot/core/test/service/BaseServiceTest.java index 690317cfa6623ae1d968f42d5fc2d9a42399155c..a429db4acb5f01616719f49ad5121b1154fd1b5a 100644 --- a/diboot-core-starter/src/test/java/diboot/core/test/service/BaseServiceTest.java +++ b/diboot-core-starter/src/test/java/diboot/core/test/service/BaseServiceTest.java @@ -32,8 +32,12 @@ import com.diboot.core.util.*; import com.diboot.core.vo.*; import diboot.core.test.StartupApplication; import diboot.core.test.binder.entity.CcCityInfo; +import diboot.core.test.binder.entity.Department; +import diboot.core.test.binder.entity.User; import diboot.core.test.binder.entity.UserRole; +import diboot.core.test.binder.service.DepartmentService; import diboot.core.test.binder.service.UserService; +import diboot.core.test.binder.vo.SimpleDictionaryVO; import diboot.core.test.config.SpringMvcConfig; import org.junit.Assert; import org.junit.Test; @@ -63,6 +67,9 @@ public class BaseServiceTest { @Autowired UserService userService; + @Autowired + DepartmentService departmentService; + @Test public void testGet(){ // 查询总数 @@ -100,6 +107,10 @@ public class BaseServiceTest { List> mapList = dictionaryService.getMapList(null, new Pagination()); Assert.assertTrue(mapList.size() > 0 && mapList.size() <= BaseConfig.getPageSize()); + List userIds = Arrays.asList(1001L, 1002L); + Map id2NameMap = userService.getId2NameMap(userIds, User::getUsername); + Assert.assertTrue(id2NameMap != null); + Assert.assertTrue(id2NameMap.get(1001) != null); } @Test @@ -243,6 +254,23 @@ public class BaseServiceTest { Assert.assertTrue(V.isEmpty(dictionaryList2)); } + @Test + public void testEscInService(){ + LambdaQueryWrapper queryWrapper = new QueryWrapper() + .lambda().isNotNull(Department::getCharacter); + List departments = departmentService.list(queryWrapper); + Assert.assertTrue(departments != null); + Department dept = (Department) departments.get(0); + Assert.assertTrue(dept.getCharacter() != null); + + LambdaQueryWrapper queryWrapperUser = new QueryWrapper() + .lambda().isNotNull(User::getCharacter); + List users = userService.getEntityList(queryWrapperUser); + Assert.assertTrue(users != null); + User user = (User) users.get(0); + Assert.assertTrue(user.getCharacter() != null); + } + @Test public void testJsonResult(){ Map map = new HashMap(); @@ -262,6 +290,12 @@ public class BaseServiceTest { public void testExist(){ boolean exists = dictionaryService.exists(Dictionary::getType, "GENDER"); Assert.assertTrue(exists); + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("type", "GENDER"); + queryWrapper.eq("item_value", "F"); + exists = dictionaryService.exists(queryWrapper); + Assert.assertTrue(exists); } @Test @@ -340,6 +374,17 @@ public class BaseServiceTest { List keyValues = dictionaryService.getKeyValueList("GENDER"); Assert.assertTrue(keyValues.size() >= 2); + + } + + @Test + public void testSimpleDictVo(){ + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("parent_id", 0).eq("type", "GENDER"); + + List simpleVOList = dictionaryService.getViewObjectList(queryWrapper, null, SimpleDictionaryVO.class); + Assert.assertTrue(simpleVOList.size() == 1); + Assert.assertTrue(simpleVOList.get(0).getChildren().size() >= 2); } /** diff --git a/diboot-core-starter/src/test/java/diboot/core/test/util/BeanUtilsTest.java b/diboot-core-starter/src/test/java/diboot/core/test/util/BeanUtilsTest.java index fbec8cd46ccdf27a56d7ff1a08258fe42ad4a194..aa0b856d4ae553c7ae25c396c60eccd4c692fc49 100644 --- a/diboot-core-starter/src/test/java/diboot/core/test/util/BeanUtilsTest.java +++ b/diboot-core-starter/src/test/java/diboot/core/test/util/BeanUtilsTest.java @@ -16,14 +16,26 @@ package diboot.core.test.util; import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.extension.service.IService; +import com.diboot.core.binding.cache.BindingCacheManager; import com.diboot.core.entity.Dictionary; +import com.diboot.core.service.DictionaryService; import com.diboot.core.util.BeanUtils; import com.diboot.core.util.JSON; import com.diboot.core.vo.DictionaryVO; +import com.sun.management.OperatingSystemMXBean; +import diboot.core.test.StartupApplication; import diboot.core.test.binder.entity.User; +import diboot.core.test.config.SpringMvcConfig; import org.junit.Assert; import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; +import java.lang.management.ManagementFactory; import java.lang.reflect.Field; import java.util.*; @@ -33,7 +45,32 @@ import java.util.*; * @version v2.0 * @date 2019/06/02 */ +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = {SpringMvcConfig.class}) +@SpringBootTest(classes = {StartupApplication.class}) public class BeanUtilsTest { + private static OperatingSystemMXBean osmxb = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); + + @Autowired + private DictionaryService dictionaryService; + + @Test + public void testFields(){ + List fields = BindingCacheManager.getFields(Dictionary.class); + Assert.assertTrue(fields.size() > 0); + long start = System.currentTimeMillis(); + for(int i=0; i<10000; i++){ + Class dict = BeanUtils.getGenericityClass(dictionaryService, 1); + } + long end = System.currentTimeMillis() - start; + System.out.println(" takes "+ end); + start = System.currentTimeMillis(); + for(int i=0; i<10000; i++){ + Class dict = ((IService)dictionaryService).getEntityClass(); + } + end = System.currentTimeMillis() - start; + System.out.println(" takes "+ end); + } @Test public void testCopyBean(){ diff --git a/diboot-core-starter/src/test/resources/application.properties b/diboot-core-starter/src/test/resources/application.properties new file mode 100644 index 0000000000000000000000000000000000000000..a030edc62bf22ce836fdd1a28da8dbe7026ad0a9 --- /dev/null +++ b/diboot-core-starter/src/test/resources/application.properties @@ -0,0 +1,21 @@ +server.port=8080 +server.servlet.context-path=/api + +#datasource config +spring.datasource.url=jdbc:mysql://localhost:3306/playground?characterEncoding=utf8&serverTimezone=GMT%2B8 +spring.datasource.username=diboot +spring.datasource.password=123456 +spring.datasource.hikari.maximum-pool-size=5 +spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver + +spring.main.allow-bean-definition-overriding=true + +mybatis-plus.global-config.mapper-locations=classpath*:mapper/*Mapper.xml +mybatis-plus.global-config.db-config.logic-delete-value=1 +mybatis-plus.global-config.db-config.logic-not-delete-value=0 + +logging.level.root=DEBUG +logging.level.org.apache=info +logging.level.org.hibernate.validator=info +logging.level.org.springframework=info +logging.level.com.zaxxer.hikari=info \ No newline at end of file diff --git a/diboot-core-starter/src/test/resources/init-mysql.sql b/diboot-core-starter/src/test/resources/init-mysql.sql index 24b760a4602df20a056c6d6fc0f45e763f2f6471..a7cd2f657b745eb311024eb931e6a21336ef044b 100644 --- a/diboot-core-starter/src/test/resources/init-mysql.sql +++ b/diboot-core-starter/src/test/resources/init-mysql.sql @@ -25,6 +25,7 @@ create table department org_id bigint not null comment '单位ID', name varchar(50) not null comment '名称', extdata varchar(100) null comment '扩展字段', + `character` varchar(100) null comment '关键字', is_deleted tinyint(1) default 0 not null comment '已删除', create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间' ) @@ -59,6 +60,7 @@ create table user username varchar(20) null, gender varchar(20) null, birthdate date null, + `character` varchar(100) null comment '关键字', is_deleted tinyint(1) default 0 null, create_time timestamp default CURRENT_TIMESTAMP null comment '创建时间', local_datetime datetime null comment '本地时间' @@ -83,6 +85,8 @@ create table cc_city_info CREATE TABLE `db_goods_goods_info` ( `goods_id` bigint DEFAULT NULL, `goods_nm` varchar(10) DEFAULT NULL, + `create_ts` timestamp default CURRENT_TIMESTAMP null, + `update_ts` timestamp null, `is_del` tinyint DEFAULT '0' ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -90,24 +94,28 @@ CREATE TABLE `db_purchase_rel_plan_goods` ( `rel_id` bigint DEFAULT NULL, `purchase_form_plan_id` bigint DEFAULT NULL, `goods_id` bigint DEFAULT NULL, + `create_ts` timestamp default CURRENT_TIMESTAMP null, + `update_ts` timestamp null, `is_del` tinyint DEFAULT '0' ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `db_purchase_form_plan` ( `purchase_form_plan_id` bigint DEFAULT NULL, `name` varchar(100) DEFAULT NULL, - `is_del` tinyint DEFAULT '0' + `is_del` tinyint DEFAULT '0', + `create_ts` timestamp default CURRENT_TIMESTAMP null, + `update_ts` timestamp null ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- 初始化样例数据 -INSERT INTO department (id, parent_id, org_id, name) VALUES (10001, 0, 100001, '产品部'), (10002, 10001, 100001, '研发组'), (10003, 10001, 100001, '测试组'), +INSERT INTO department (id, parent_id, org_id, name, `character`) VALUES (10001, 0, 100001, '产品部', 'CP'), (10002, 10001, 100001, '研发组', 'YF'), (10003, 10001, 100001, '测试组', 'CS'), (10004, 10001, 100001, 'UI组'), (10005, 10003, 100001, '自动化测试'), (10006, 10003, 100001, '功能测试'); INSERT INTO dictionary (id, parent_id, app_module, type, item_name, item_value) VALUES (1, 0, '', 'GENDER', '性别', null), (2, 1, '', 'GENDER', '男', 'M'), (3, 1, '', 'GENDER', '女', 'F'); INSERT INTO organization (id, parent_id, name, telphone, manager_id) VALUES (100001, 0, '苏州帝博', '0512-62988949', 1001), (100002, 0, '成都帝博', null, null); INSERT INTO role (id, name, code) VALUES (101, '管理员', 'ADMIN'), (102, '操作员', 'OPERATOR'); -INSERT INTO user (id, department_id, username, gender) VALUES (1001, 10002, '张三', 'M'), (1002, 10002, '李四', 'F'); +INSERT INTO user (id, department_id, username, gender, `character`) VALUES (1001, 10002, '张三', 'M', 'ZS'), (1002, 10002, '李四', 'F', 'LS'); INSERT INTO user_role (user_type, user_id, role_id) VALUES ('SysUser', 1001, 101),('SysUser', 1001, 102),('OrgUser', 1002, 102); INSERT INTO cc_city_info (id, parent_id, region_id, region_name) VALUES (10000, 0, 10000, '江苏省'), (10010, 10000, 10010, '苏州市'), (10020, 10010, 10020, '园区'); INSERT INTO db_goods_goods_info (goods_id, goods_nm, is_del) VALUES(1001, 'abcde', 0), (1002, 'abcd', 0); INSERT INTO db_purchase_rel_plan_goods(rel_id, purchase_form_plan_id, goods_id, is_del)VALUES(1, 1, 1001, 0), (2, 1, 1002, 0); -INSERT INTO db_purchase_form_plan(plan_id, name, is_del)VALUES(1, '5月份采购计划', 0); +INSERT INTO db_purchase_form_plan(purchase_form_plan_id, name, is_del)VALUES(1, '5月份采购计划', 0); diff --git a/diboot-core/pom.xml b/diboot-core/pom.xml index a0a717661b989c0f96bc1a99119ecabdb06c97ed..9a77620ab27b5cac92064fcdd2ae88e302d0cb86 100644 --- a/diboot-core/pom.xml +++ b/diboot-core/pom.xml @@ -7,11 +7,11 @@ com.diboot diboot-root - 2.2.1 + 2.3.0 diboot-core - 2.2.1 + 2.3.0 jar diboot core project diff --git a/diboot-core/src/main/java/com/diboot/core/binding/QueryBuilder.java b/diboot-core/src/main/java/com/diboot/core/binding/QueryBuilder.java index 73e55b0607a7a44258fcaa6ae34db107db77270d..aff890937ae58a3396c4c65ab9efecc15517a029 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/QueryBuilder.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/QueryBuilder.java @@ -21,7 +21,6 @@ import com.baomidou.mybatisplus.core.conditions.ISqlSegment; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.segments.NormalSegmentList; -import com.diboot.core.binding.cache.BindingCacheManager; import com.diboot.core.binding.parser.ParserCache; import com.diboot.core.binding.query.BindQuery; import com.diboot.core.binding.query.Comparison; @@ -155,11 +154,17 @@ public class QueryBuilder { continue; } Object value = entry.getValue(); - // 处理空字符串"" - if(value instanceof String && S.isEmpty((String)value)){ - if(query != null && query.strategy().equals(Strategy.IGNORE_EMPTY_STRING)){ + if(query != null && query.strategy().equals(Strategy.IGNORE_EMPTY)){ + // 处理空字符串"" + if(value instanceof String && S.isEmpty((String)value)){ continue; } + if(value instanceof Collection){ + Collection valueList = (Collection)value; + if(valueList.size() == 0){ + continue; + } + } } // 对比类型 Comparison comparison = Comparison.EQ; @@ -251,15 +256,20 @@ public class QueryBuilder { } } // 支持逗号分隔的字符串 - else if(value instanceof String && ((String) value).contains(",")){ - Object[] valueArray = ((String) value).split(","); + else if(value instanceof String && ((String) value).contains(Cons.SEPARATOR_COMMA)){ + Object[] valueArray = ((String) value).split(Cons.SEPARATOR_COMMA); wrapper.between(columnName, valueArray[0], valueArray[1]); } else{ wrapper.ge(columnName, value); } break; + // 不等于 + case NOT_EQ: + wrapper.ne(columnName, value); + break; default: + break; } } return wrapper; @@ -297,7 +307,6 @@ public class QueryBuilder { private static LinkedHashMap extractNotNullValues(DTO dto, Collection fields){ LinkedHashMap resultMap = new LinkedHashMap<>(); Class dtoClass = dto.getClass(); - // 转换 List declaredFields = BeanUtils.extractAllFields(dtoClass); for (Field field : declaredFields) { @@ -328,7 +337,7 @@ public class QueryBuilder { } catch (IllegalAccessException e) { log.error("通过反射获取属性值出错:{}", e.getMessage()); } catch (NoSuchMethodException e) { - log.warn("通过反射获取属性方法不存在:{}", e.getMessage()); + log.debug("通过反射获取属性方法不存在:{}", e.getMessage()); } catch (InvocationTargetException e) { log.warn("通过反射执行属性方法出错:{}", e.getMessage()); } diff --git a/diboot-core/src/main/java/com/diboot/core/binding/binder/BaseBinder.java b/diboot-core/src/main/java/com/diboot/core/binding/binder/BaseBinder.java index 5b60d061d94bf2a7eb85866e0928676ffca20792..ea53148c87b1ef074b7383ccb557381040a36bb7 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/binder/BaseBinder.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/binder/BaseBinder.java @@ -19,6 +19,7 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.IService; import com.diboot.core.binding.cache.BindingCacheManager; +import com.diboot.core.binding.helper.ResultAssembler; import com.diboot.core.binding.parser.MiddleTable; import com.diboot.core.binding.parser.PropInfo; import com.diboot.core.config.BaseConfig; @@ -92,7 +93,7 @@ public abstract class BaseBinder { this.annoObjPropInfo = BindingCacheManager.getPropInfoByClass(voList.get(0).getClass()); } this.queryWrapper = new QueryWrapper<>(); - this.referencedEntityClass = BeanUtils.getGenericityClass(referencedService, 1); + this.referencedEntityClass = referencedService.getEntityClass(); this.refObjPropInfo = BindingCacheManager.getPropInfoByClass(this.referencedEntityClass); // 列集合 this.annoObjJoinCols = new ArrayList<>(8); @@ -256,6 +257,11 @@ public abstract class BaseBinder { } } + /** + * 简化select列,仅select必需列 + */ + protected abstract void simplifySelectColumns(); + /** * 获取EntityList * @param queryWrapper @@ -308,16 +314,7 @@ public abstract class BaseBinder { * @return */ protected Object getValueIgnoreKeyCase(Map map, String key){ - if(key == null){ - return null; - } - if(map.containsKey(key)){ - return map.get(key); - } - if(map.containsKey(key.toUpperCase())){ - return map.get(key.toUpperCase()); - } - return null; + return ResultAssembler.getValueIgnoreKeyCase(map, key); } /** @@ -344,10 +341,10 @@ public abstract class BaseBinder { * 注解宿主对象的列名转换为字段名 * @return */ - public List getAnnoObjJoinFlds(){ - List fields = new ArrayList<>(annoObjJoinCols.size()); - for(String col : annoObjJoinCols){ - fields.add(toAnnoObjField(col)); + public String[] getAnnoObjJoinFlds(){ + String[] fields = new String[annoObjJoinCols.size()]; + for(int i=0; i extends BaseBinder { } // 直接关联Entity if(middleTable == null){ + simplifySelectColumns(); // @BindEntity(entity = Department.class, condition="this.department_id=id AND this.type=type") // Department department; super.buildQueryWrapperJoinOn(); @@ -109,6 +112,7 @@ public class EntityBinder extends BaseBinder { Map valueEntityMap = new HashMap<>(); Map middleTableResultMap = middleTable.executeOneToOneQuery(trunkObjCol2ValuesMap); if(V.notEmpty(middleTableResultMap)){ + simplifySelectColumns(); // 提取entity主键值集合 Collection refObjValues = middleTableResultMap.values().stream().distinct().collect(Collectors.toList()); // 构建查询条件 @@ -145,21 +149,26 @@ public class EntityBinder extends BaseBinder { */ private Map buildMatchKey2EntityMap(List list){ Map key2TargetMap = new HashMap<>(list.size()); - List joinOnValues = new ArrayList<>(refObjJoinCols.size()); + StringBuilder sb = new StringBuilder(); for(T entity : list){ - joinOnValues.clear(); - for(String refObjJoinOnCol : refObjJoinCols){ + sb.setLength(0); + for(int i=0; i 0){ + sb.append(Cons.SEPARATOR_COMMA); + } + sb.append(pkValue); } - String matchKey = S.join(joinOnValues); - + // 查找匹配Key + String matchKey = sb.toString(); Object target = entity; if(target instanceof Map == false){ target = cloneOrConvertBean(entity); } key2TargetMap.put(matchKey, target); } + sb.setLength(0); return key2TargetMap; } @@ -178,4 +187,15 @@ public class EntityBinder extends BaseBinder { return BeanUtils.convert(value, annoObjectFieldClass); } } + + /** + * 简化select列,仅select必需列 + */ + @Override + protected void simplifySelectColumns(){ + if(!referencedEntityClass.getName().equals(annoObjectFieldClass.getName())){ + queryWrapper = (QueryWrapper) ServiceAdaptor.optimizeSelect(queryWrapper, referencedEntityClass, annoObjectFieldClass); + } + } + } diff --git a/diboot-core/src/main/java/com/diboot/core/binding/binder/EntityListBinder.java b/diboot-core/src/main/java/com/diboot/core/binding/binder/EntityListBinder.java index 0a89ab54391ac340558632e3381e4bf8e66f73ea..d42e3c3f3115d6f36a4ae371cbcddeadfc79fdce 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/binder/EntityListBinder.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/binder/EntityListBinder.java @@ -67,6 +67,7 @@ public class EntityListBinder extends EntityBinder { } Map valueEntityListMap = new HashMap<>(); if(middleTable == null){ + super.simplifySelectColumns(); super.buildQueryWrapperJoinOn(); //处理orderBy,附加排序 this.appendOrderBy(); @@ -87,6 +88,7 @@ public class EntityListBinder extends EntityBinder { if(V.isEmpty(middleTableResultMap)){ return; } + super.simplifySelectColumns(); // 收集查询结果values集合 List entityIdList = extractIdValueFromMap(middleTableResultMap); // 构建查询条件 @@ -128,14 +130,19 @@ public class EntityListBinder extends EntityBinder { */ private Map buildMatchKey2EntityListMap(List list){ Map key2TargetListMap = new HashMap<>(list.size()); - List joinOnValues = new ArrayList<>(refObjJoinCols.size()); + StringBuilder sb = new StringBuilder(); for(T entity : list){ - joinOnValues.clear(); - for(String refObjJoinOnCol : refObjJoinCols){ + sb.setLength(0); + for(int i=0; i 0){ + sb.append(Cons.SEPARATOR_COMMA); + } + sb.append(pkValue); } - String matchKey = S.join(joinOnValues); + // 查找匹配Key + String matchKey = sb.toString(); // 获取list List entityList = key2TargetListMap.get(matchKey); if(entityList == null){ @@ -148,6 +155,7 @@ public class EntityListBinder extends EntityBinder { } entityList.add(target); } + sb.setLength(0); return key2TargetListMap; } diff --git a/diboot-core/src/main/java/com/diboot/core/binding/binder/FieldBinder.java b/diboot-core/src/main/java/com/diboot/core/binding/binder/FieldBinder.java index 50a7c4423ccb21af627271034776dc50b0c54860..94507cba02a011a879b56fb34e6319177ca0c54e 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/binder/FieldBinder.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/binder/FieldBinder.java @@ -16,8 +16,7 @@ package com.diboot.core.binding.binder; import com.baomidou.mybatisplus.extension.service.IService; -import com.diboot.core.binding.cache.BindingCacheManager; -import com.diboot.core.binding.parser.PropInfo; +import com.diboot.core.config.Cons; import com.diboot.core.exception.BusinessException; import com.diboot.core.util.*; import org.slf4j.Logger; @@ -71,11 +70,11 @@ public class FieldBinder extends BaseBinder { */ public FieldBinder link(String fromDoField, String toVoField){ if(annoObjectSetterPropNameList == null){ - annoObjectSetterPropNameList = new ArrayList<>(); + annoObjectSetterPropNameList = new ArrayList<>(8); } annoObjectSetterPropNameList.add(toVoField); if(referencedGetterFieldNameList == null){ - referencedGetterFieldNameList = new ArrayList<>(); + referencedGetterFieldNameList = new ArrayList<>(8); } referencedGetterFieldNameList.add(fromDoField); return this; @@ -96,7 +95,7 @@ public class FieldBinder extends BaseBinder { } // 直接关联 if(middleTable == null){ - this.buildSelectColumns(); + this.simplifySelectColumns(); super.buildQueryWrapperJoinOn(); // 获取匹配结果的mapList List> mapList = getMapList(queryWrapper); @@ -124,7 +123,7 @@ public class FieldBinder extends BaseBinder { } // 收集查询结果values集合 Collection refObjValues = middleTableResultMap.values().stream().distinct().collect(Collectors.toList()); - this.buildSelectColumns(); + this.simplifySelectColumns(); // 构建查询条件 String refObjJoinOnCol = refObjJoinCols.get(0); queryWrapper.in(refObjJoinOnCol, refObjValues); @@ -187,13 +186,17 @@ public class FieldBinder extends BaseBinder { * @return */ private String buildMatchKey(Object annoObject){ - List joinOnValues = new ArrayList<>(annoObjJoinCols.size()); - for(String annoJoinOn : annoObjJoinCols){ + StringBuilder sb = new StringBuilder(); + for(int i=0; i 0){ + sb.append(Cons.SEPARATOR_COMMA); + } + sb.append(val); } - return S.join(joinOnValues); + return sb.toString(); } /** @@ -203,7 +206,8 @@ public class FieldBinder extends BaseBinder { * @return */ private String buildMatchKey(Object annoObject, Map middleTableResultMap){ - List joinOnValues = new ArrayList<>(middleTable.getTrunkObjColMapping().size()); + StringBuilder sb = new StringBuilder(); + boolean appendComma = false; for(Map.Entry entry : middleTable.getTrunkObjColMapping().entrySet()){ String getterField = toAnnoObjField(entry.getKey()); String fieldValue = BeanUtils.getStringProperty(annoObject, getterField); @@ -212,17 +216,29 @@ public class FieldBinder extends BaseBinder { Object value = middleTableResultMap.get(fieldValue); fieldValue = String.valueOf(value); } - joinOnValues.add(fieldValue); + if(appendComma){ + sb.append(Cons.SEPARATOR_COMMA); + } + sb.append(fieldValue); + if(appendComma == false){ + appendComma = true; + } } // 查找匹配Key - return S.join(joinOnValues); + return sb.toString(); } - protected void buildSelectColumns(){ - List selectColumns = new ArrayList<>(); + @Override + protected void simplifySelectColumns() { + List selectColumns = new ArrayList<>(8); selectColumns.addAll(refObjJoinCols); - for(String referencedGetterField : referencedGetterFieldNameList){ - selectColumns.add(toRefObjColumn(referencedGetterField)); + if(V.notEmpty(referencedGetterFieldNameList)){ + for(String referencedGetterField : referencedGetterFieldNameList){ + String refObjCol = toRefObjColumn(referencedGetterField); + if(!selectColumns.contains(refObjCol)){ + selectColumns.add(refObjCol); + } + } } queryWrapper.select(S.toStringArray(selectColumns)); } diff --git a/diboot-core/src/main/java/com/diboot/core/binding/binder/FieldListBinder.java b/diboot-core/src/main/java/com/diboot/core/binding/binder/FieldListBinder.java index 8197290014610d98d70b3505fb2fda146a76102d..425ab3e303866fe7085af71139035f5feb36cefa 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/binder/FieldListBinder.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/binder/FieldListBinder.java @@ -16,9 +16,9 @@ package com.diboot.core.binding.binder; import com.baomidou.mybatisplus.extension.service.IService; +import com.diboot.core.config.Cons; import com.diboot.core.exception.BusinessException; import com.diboot.core.util.BeanUtils; -import com.diboot.core.util.S; import com.diboot.core.util.V; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -60,9 +60,9 @@ public class FieldListBinder extends FieldBinder { return; } Map valueEntityListMap = new HashMap<>(); - super.buildSelectColumns(); // 直接关联 if(middleTable == null){ + super.simplifySelectColumns(); super.buildQueryWrapperJoinOn(); // 查询entity列表: List List list = getEntityList(queryWrapper); @@ -84,6 +84,7 @@ public class FieldListBinder extends FieldBinder { if(V.isEmpty(middleTableResultMap)){ return; } + super.simplifySelectColumns(); // 收集查询结果values集合 List entityIdList = extractIdValueFromMap(middleTableResultMap); // 构建查询条件 @@ -127,16 +128,20 @@ public class FieldListBinder extends FieldBinder { if(V.isEmpty(fromList) || V.isEmpty(valueMatchMap)){ return; } - List fieldValues = new ArrayList<>(annoObjJoinCols.size()); + StringBuilder sb = new StringBuilder(); try{ for(E object : fromList){ - fieldValues.clear(); - for(String annoObjJoinCol : annoObjJoinCols){ - String fieldValue = BeanUtils.getStringProperty(object, toAnnoObjField(annoObjJoinCol)); - fieldValues.add(fieldValue); + sb.setLength(0); + for(int i=0; i0){ + sb.append(Cons.SEPARATOR_COMMA); + } + sb.append(val); } // 查找匹配Key - String matchKey = S.join(fieldValues); + String matchKey = sb.toString(); List entityList = valueMatchMap.get(matchKey); if(entityList != null){ // 赋值 @@ -146,6 +151,7 @@ public class FieldListBinder extends FieldBinder { } } } + sb.setLength(0); } catch (Exception e){ log.warn("设置属性值异常", e); @@ -163,18 +169,24 @@ public class FieldListBinder extends FieldBinder { if(V.isEmpty(fromList) || V.isEmpty(valueMatchMap)){ return; } - List fieldValues = new ArrayList<>(trunkObjColMapping.size()); + StringBuilder sb = new StringBuilder(); try{ for(E object : fromList){ - fieldValues.clear(); + boolean appendComma = false; + sb.setLength(0); for(Map.Entry entry :trunkObjColMapping.entrySet()){ String getterField = toAnnoObjField(entry.getKey()); String fieldValue = BeanUtils.getStringProperty(object, getterField); - fieldValues.add(fieldValue); + if(appendComma){ + sb.append(Cons.SEPARATOR_COMMA); + } + sb.append(fieldValue); + if(appendComma == false){ + appendComma = true; + } } // 查找匹配Key - String matchKey = S.join(fieldValues); - List entityList = valueMatchMap.get(matchKey); + List entityList = valueMatchMap.get(sb.toString()); if(entityList != null){ // 赋值 for(int i = 0; i< annoObjectSetterPropNameList.size(); i++){ @@ -196,14 +208,18 @@ public class FieldListBinder extends FieldBinder { */ private Map buildMatchKey2FieldListMap(List list){ Map key2TargetListMap = new HashMap<>(list.size()); - List joinOnValues = new ArrayList<>(refObjJoinCols.size()); + StringBuilder sb = new StringBuilder(); for(T entity : list){ - joinOnValues.clear(); - for(String refObjJoinOnCol : refObjJoinCols){ + sb.setLength(0); + for(int i=0; i 0){ + sb.append(Cons.SEPARATOR_COMMA); + } + sb.append(fldValue); } - String matchKey = S.join(joinOnValues); + String matchKey = sb.toString(); // 获取list List entityList = key2TargetListMap.get(matchKey); if(entityList == null){ @@ -212,6 +228,7 @@ public class FieldListBinder extends FieldBinder { } entityList.add(entity); } + sb.setLength(0); return key2TargetListMap; } diff --git a/diboot-core/src/main/java/com/diboot/core/binding/cache/BindingCacheManager.java b/diboot-core/src/main/java/com/diboot/core/binding/cache/BindingCacheManager.java index 1ad590922eef6cfebf3f34e6110790f9e11eb1aa..f3df6bf607be09e13dc2b77fc355c95d1515039a 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/cache/BindingCacheManager.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/cache/BindingCacheManager.java @@ -23,17 +23,17 @@ import com.diboot.core.binding.parser.PropInfo; import com.diboot.core.cache.StaticMemoryCacheManager; import com.diboot.core.util.BeanUtils; import com.diboot.core.util.ContextHelper; +import com.diboot.core.util.S; import com.diboot.core.util.V; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.session.SqlSessionFactory; import org.springframework.context.annotation.Primary; +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * CacheManager @@ -63,6 +63,14 @@ public class BindingCacheManager { * Entity类的SimpleName-Entity Class的缓存key */ private static final String CACHE_NAME_ENTITYNAME_CLASS = "NAME_CLASS"; + /** + * 类-fields缓存 + */ + private static final String CACHE_NAME_CLASS_FIELDS = "CLASS_FIELDS"; + /** + * 类- name-field Map缓存 + */ + private static final String CACHE_NAME_CLASS_NAME2FLDMAP = "CLASS_NAME2FLDMAP"; private static StaticMemoryCacheManager getCacheManager(){ if(cacheManager == null){ @@ -70,7 +78,9 @@ public class BindingCacheManager { CACHE_NAME_CLASS_ENTITY, CACHE_NAME_TABLE_ENTITY, CACHE_NAME_CLASS_PROP, - CACHE_NAME_ENTITYNAME_CLASS); + CACHE_NAME_ENTITYNAME_CLASS, + CACHE_NAME_CLASS_FIELDS, + CACHE_NAME_CLASS_NAME2FLDMAP); } return cacheManager; } @@ -168,6 +178,50 @@ public class BindingCacheManager { return null; } + /** + * 获取class的fields + * @param beanClazz + * @return + */ + public static List getFields(Class beanClazz){ + List fields = getCacheManager().getCacheObj(CACHE_NAME_CLASS_FIELDS, beanClazz.getName(), List.class); + if(fields == null){ + fields = initClassFields(beanClazz, null); + getCacheManager().putCacheObj(CACHE_NAME_CLASS_FIELDS, beanClazz.getName(), fields); + } + return fields; + } + + /** + * 获取class中包含指定注解的的fields + * @param beanClazz + * @return + */ + public static List getFields(Class beanClazz, Class annotation){ + String key = S.join(beanClazz.getName(), annotation.getClass().getName()); + List fields = getCacheManager().getCacheObj(CACHE_NAME_CLASS_FIELDS, key, List.class); + if(fields == null){ + fields = initClassFields(beanClazz, annotation); + getCacheManager().putCacheObj(CACHE_NAME_CLASS_FIELDS, key, fields); + } + return fields; + } + + /** + * 获取class的fields + * @param beanClazz + * @return + */ + public static Map getFieldsMap(Class beanClazz){ + Map fieldsMap = getCacheManager().getCacheObj(CACHE_NAME_CLASS_NAME2FLDMAP, beanClazz.getName(), Map.class); + if(fieldsMap == null){ + List fields = getFields(beanClazz); + fieldsMap = BeanUtils.convertToStringKeyObjectMap(fields, "name"); + getCacheManager().putCacheObj(CACHE_NAME_CLASS_NAME2FLDMAP, beanClazz.getName(), fieldsMap); + } + return fieldsMap; + } + /** * 初始化 */ @@ -248,8 +302,44 @@ public class BindingCacheManager { */ private static PropInfo initPropInfoCache(Class beanClazz) { PropInfo propInfoCache = new PropInfo(beanClazz); - cacheManager.putCacheObj(CACHE_NAME_CLASS_PROP, beanClazz.getName(), propInfoCache); + getCacheManager().putCacheObj(CACHE_NAME_CLASS_PROP, beanClazz.getName(), propInfoCache); return propInfoCache; } + /** + * 初始化fields + * @param beanClazz + * @return + */ + private static List initClassFields(Class beanClazz, Class annotation){ + List fieldList = new ArrayList<>(); + Set fieldNameSet = new HashSet<>(); + loopFindFields(beanClazz, annotation, fieldList, fieldNameSet); + return fieldList; + } + + /** + * 循环向上查找fields + * @param beanClazz + * @param annotation + * @param fieldList + * @param fieldNameSet + */ + private static void loopFindFields(Class beanClazz, Class annotation, List fieldList, Set fieldNameSet){ + if(beanClazz == null) { + return; + } + Field[] fields = beanClazz.getDeclaredFields(); + if(V.notEmpty(fields)){ //被重写属性,以子类override的为准 + Arrays.stream(fields).forEach((field)->{ + if(!fieldNameSet.contains(field.getName()) && + (annotation == null || field.getAnnotation(annotation) != null)){ + fieldList.add(field); + fieldNameSet.add(field.getName()); + } + }); + } + loopFindFields(beanClazz.getSuperclass(), annotation, fieldList, fieldNameSet); + } + } diff --git a/diboot-core/src/main/java/com/diboot/core/binding/copy/AcceptAnnoCopier.java b/diboot-core/src/main/java/com/diboot/core/binding/copy/AcceptAnnoCopier.java index 9125db2184cd511070fc923bb8bf9aa893492d6e..e27ff1545e9f7360ca26935e713e0fedb60988f0 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/copy/AcceptAnnoCopier.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/copy/AcceptAnnoCopier.java @@ -37,7 +37,7 @@ public class AcceptAnnoCopier { /** * 注解缓存 */ - private static Map> CLASS_ACCEPT_ANNO_CACHE_MAP = new ConcurrentHashMap<>(); + private static final Map> CLASS_ACCEPT_ANNO_CACHE_MAP = new ConcurrentHashMap<>(); // 下标 private static final int IDX_TARGET_FIELD = 0, IDX_SOURCE_FIELD = 1, IDX_OVERRIDE = 2; diff --git a/diboot-core/src/main/java/com/diboot/core/binding/data/DataAccessAnnoCache.java b/diboot-core/src/main/java/com/diboot/core/binding/data/DataAccessAnnoCache.java index 469a69afe79d0c2e019f169c1caa140179dc26d0..54735985a8cadd22fe74f8cff5c25c3d32497189 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/data/DataAccessAnnoCache.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/data/DataAccessAnnoCache.java @@ -38,7 +38,7 @@ public class DataAccessAnnoCache { /** * 注解缓存 */ - private static Map DATA_PERMISSION_ANNO_CACHE = new ConcurrentHashMap<>(); + private static final Map DATA_PERMISSION_ANNO_CACHE = new ConcurrentHashMap<>(); /** * 是否有检查点注解 diff --git a/diboot-core/src/main/java/com/diboot/core/binding/helper/ResultAssembler.java b/diboot-core/src/main/java/com/diboot/core/binding/helper/ResultAssembler.java index 074084cbe0aaca9acf768d34bf4a0322687534b9..a09a1a4b530e517b8ab6b58afb4b2a612fa2f19e 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/helper/ResultAssembler.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/helper/ResultAssembler.java @@ -15,6 +15,7 @@ */ package com.diboot.core.binding.helper; +import com.diboot.core.config.Cons; import com.diboot.core.util.BeanUtils; import com.diboot.core.util.S; import com.diboot.core.util.V; @@ -39,25 +40,29 @@ public class ResultAssembler { * @param valueMatchMap * @param */ - public static void bindPropValue(String setterFieldName, List fromList, List getterFields, Map valueMatchMap){ + public static void bindPropValue(String setterFieldName, List fromList, String[] getterFields, Map valueMatchMap){ if(V.isEmpty(fromList) || V.isEmpty(valueMatchMap)){ return; } - List fieldValues = new ArrayList<>(getterFields.size()); + StringBuilder sb = new StringBuilder(); try{ for(E object : fromList){ - fieldValues.clear(); - for(String getterField : getterFields){ - String fieldValue = BeanUtils.getStringProperty(object, getterField); - fieldValues.add(fieldValue); + sb.setLength(0); + for(int i=0; i 0){ + sb.append(Cons.SEPARATOR_COMMA); + } + sb.append(fieldValue); } // 查找匹配Key - String matchKey = S.join(fieldValues); + String matchKey = sb.toString(); if(valueMatchMap.containsKey(matchKey)){ // 赋值 BeanUtils.setProperty(object, setterFieldName, valueMatchMap.get(matchKey)); } } + sb.setLength(0); } catch (Exception e){ log.warn("设置属性值异常, setterFieldName="+setterFieldName, e); @@ -76,10 +81,11 @@ public class ResultAssembler { if(V.isEmpty(fromList) || V.isEmpty(valueMatchMap)){ return; } - List fieldValues = new ArrayList<>(trunkObjColMapping.size()); + StringBuilder sb = new StringBuilder(); try{ for(E object : fromList){ - fieldValues.clear(); + boolean appendComma = false; + sb.setLength(0); for(Map.Entry entry :trunkObjColMapping.entrySet()){ //转换为字段名 String getterField = col2FieldMapping.get(entry.getKey()); @@ -87,16 +93,22 @@ public class ResultAssembler { getterField = S.toLowerCaseCamel(entry.getKey()); } String fieldValue = BeanUtils.getStringProperty(object, getterField); - fieldValues.add(fieldValue); + if(appendComma){ + sb.append(Cons.SEPARATOR_COMMA); + } + sb.append(fieldValue); + if(appendComma == false){ + appendComma = true; + } } // 查找匹配Key - String matchKey = S.join(fieldValues); + String matchKey = sb.toString(); if(valueMatchMap.containsKey(matchKey)){ // 赋值 BeanUtils.setProperty(object, setterFieldName, valueMatchMap.get(matchKey)); } } - fieldValues.clear(); + sb.setLength(0); } catch (Exception e){ log.warn("设置属性值异常, setterFieldName="+setterFieldName, e); @@ -118,15 +130,22 @@ public class ResultAssembler { // 获取valueName String valueName = branchObjColMapping.entrySet().iterator().next().getKey(); // 合并list为map - Map resultMap = new HashMap<>(); - List fieldValues = new ArrayList<>(trunkObjColMapping.size()); + Map resultMap = new HashMap<>(resultSetMapList.size()); + StringBuilder sb = new StringBuilder(); for(Map row : resultSetMapList){ - fieldValues.clear(); + boolean appendComma = false; + sb.setLength(0); for(Map.Entry entry : trunkObjColMapping.entrySet()){ - Object keyObj = row.containsKey(entry.getValue())? row.get(entry.getValue()) : row.get(entry.getValue().toUpperCase()); - fieldValues.add(S.valueOf(keyObj)); + Object keyObj = getValueIgnoreKeyCase(row, entry.getValue()); + if(appendComma){ + sb.append(Cons.SEPARATOR_COMMA); + } + sb.append(S.valueOf(keyObj)); + if(appendComma == false){ + appendComma = true; + } } - String matchKeys = S.join(fieldValues); + String matchKeys = sb.toString(); Object valueObj = row.containsKey(valueName)? row.get(valueName) : row.get(valueName.toUpperCase()); resultMap.put(matchKeys, valueObj); } @@ -149,14 +168,21 @@ public class ResultAssembler { String valueName = branchObjColMapping.entrySet().iterator().next().getKey(); // 合并list为map Map resultMap = new HashMap<>(); - List fieldValues = new ArrayList<>(trunkObjColMapping.size()); + StringBuilder sb = new StringBuilder(); for(Map row : resultSetMapList){ - fieldValues.clear(); + boolean appendComma = false; + sb.setLength(0); for(Map.Entry entry : trunkObjColMapping.entrySet()){ - Object keyObj = row.containsKey(entry.getValue())? row.get(entry.getValue()) : row.get(entry.getValue().toUpperCase()); - fieldValues.add(S.valueOf(keyObj)); + Object keyObj = getValueIgnoreKeyCase(row, entry.getValue()); + if(appendComma){ + sb.append(Cons.SEPARATOR_COMMA); + } + sb.append(S.valueOf(keyObj)); + if(appendComma == false){ + appendComma = true; + } } - String matchKeys = S.join(fieldValues); + String matchKeys = sb.toString(); Object valueObj = row.containsKey(valueName)? row.get(valueName) : row.get(valueName.toUpperCase()); if(valueObj != null){ List valueList = resultMap.get(matchKeys); @@ -167,7 +193,29 @@ public class ResultAssembler { valueList.add(valueObj); } } + sb.setLength(0); return resultMap; } + + /** + * 从map中取值,如直接取为null尝试转换大写后再取,以支持ORACLE等大写命名数据库 + * @param map + * @param key + * @return + */ + public static Object getValueIgnoreKeyCase(Map map, String key){ + if(map == null || key == null){ + return null; + } + key = S.removeEsc(key); + if(map.containsKey(key)){ + return map.get(key); + } + if(map.containsKey(key.toUpperCase())){ + return map.get(key.toUpperCase()); + } + return null; + } + } diff --git a/diboot-core/src/main/java/com/diboot/core/binding/helper/ServiceAdaptor.java b/diboot-core/src/main/java/com/diboot/core/binding/helper/ServiceAdaptor.java index da3ffe915415db5aa0858a5794bf522e1c7badfa..b4c092227765738797c35995fac88af23756f1a3 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/helper/ServiceAdaptor.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/helper/ServiceAdaptor.java @@ -15,16 +15,25 @@ */ package com.diboot.core.binding.helper; +import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.metadata.TableFieldInfo; +import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; +import com.diboot.core.binding.cache.BindingCacheManager; import com.diboot.core.config.Cons; import com.diboot.core.service.BaseService; import com.diboot.core.util.ContextHelper; +import com.diboot.core.util.S; +import com.diboot.core.util.V; import com.diboot.core.vo.Pagination; +import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * Service适配器 @@ -86,7 +95,7 @@ public class ServiceAdaptor { IPage page = convertToIPage(pagination, entityClass); page = iService.page(page, queryWrapper); // 如果重新执行了count进行查询,则更新pagination中的总数 - if(page.isSearchCount()){ + if(page.searchCount()){ pagination.setTotalCount(page.getTotal()); } return page.getRecords(); @@ -118,7 +127,38 @@ public class ServiceAdaptor { pagination.setDefaultCreateTimeOrderBy(); } } - return (Page)pagination.toPage(); + return pagination.toPage(); + } + + /** + * 基于VO提取最小集select字段 + * @param queryWrapper + * @param voClass + */ + public static Wrapper optimizeSelect(Wrapper queryWrapper, Class entityClass, Class voClass){ + if(!(queryWrapper instanceof QueryWrapper) || queryWrapper.getSqlSelect() != null){ + return queryWrapper; + } + List allColumns = TableInfoHelper.getTableInfo(entityClass).getFieldList(); + if(V.isEmpty(allColumns)){ + return queryWrapper; + } + List columns = new ArrayList<>(); + String pk = ContextHelper.getIdColumnName(entityClass); + if(V.notEmpty(pk)){ + columns.add(pk); + } + Map fieldsMap = BindingCacheManager.getFieldsMap(voClass); + for(TableFieldInfo col : allColumns){ + if(fieldsMap.containsKey(col.getField().getName()) && V.notEmpty(col.getColumn())){ + columns.add(col.getSqlSelect()); + } + } + // select全部列,不特殊处理 + if(allColumns.size() == columns.size()){ + return queryWrapper; + } + return ((QueryWrapper)queryWrapper).select(S.toStringArray(columns)); } } \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/binding/parser/BaseConditionManager.java b/diboot-core/src/main/java/com/diboot/core/binding/parser/BaseConditionManager.java index 5fed0f77146e6891a417b1a47c95e681c0ed81d9..7e4add5eeac6e9af923fcbb579e56284a206559c 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/parser/BaseConditionManager.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/parser/BaseConditionManager.java @@ -40,7 +40,7 @@ public class BaseConditionManager { /** * 表达式缓存Map */ - private static Map> expressionParseResultMap = new ConcurrentHashMap<>(); + private static final Map> expressionParseResultMap = new ConcurrentHashMap<>(); /** * 获取解析后的Expression列表 diff --git a/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionManager.java b/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionManager.java index 5bdc8297f7b67857ff0db98d42ff3ae6dfdcc51d..45dd384de2f7c55286bab72648b021c670b30e5f 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionManager.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionManager.java @@ -74,7 +74,13 @@ public class ConditionManager extends BaseConditionManager{ String annoColumn = removeLeftAlias(express.getLeftExpression().toString()); if(express.getRightExpression() instanceof Column){ String entityColumn = removeLeftAlias(express.getRightExpression().toString()); - binder.joinOn(annoColumn, entityColumn); + // xx=this.yy,翻转 + if(isCurrentObjColumn(express.getRightExpression().toString()) && !isCurrentObjColumn(express.getLeftExpression().toString())){ + binder.joinOn(entityColumn, annoColumn); + } + else{ + binder.joinOn(annoColumn, entityColumn); + } } else{ binder.andEQ(annoColumn, express.getRightExpression().toString()); diff --git a/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionParser.java b/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionParser.java index bf165e2eead6b74422dd0702e16fc51f736dab34..5a06d734a4f4632ae23bd9a3f4d634c211dbcea8 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionParser.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionParser.java @@ -40,7 +40,7 @@ public class ConditionParser implements ExpressionVisitor,ItemsListVisitor { } private List errorMsgList = null; - private List expressList = new ArrayList<>(); + private List expressList = new ArrayList<>(8); /** * 添加错误信息 diff --git a/diboot-core/src/main/java/com/diboot/core/binding/parser/EntityInfoCache.java b/diboot-core/src/main/java/com/diboot/core/binding/parser/EntityInfoCache.java index 98e3b02c4371f6c16d6192e34e6f6cfa273bec31..c14782d17b8f5c346d6c68138ead4f629e20e993 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/parser/EntityInfoCache.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/parser/EntityInfoCache.java @@ -90,6 +90,17 @@ public class EntityInfoCache implements Serializable { return this.propInfo.getColumnToFieldMap().get(columnName); } + /** + * 根据列名获取字段 + * @return + */ + public String getColumnByField(String fieldName){ + if(this.propInfo == null || V.isEmpty(this.propInfo.getFieldToColumnMap())){ + return null; + } + return this.propInfo.getFieldToColumnMap().get(fieldName); + } + /** * 获取ID列 * @return diff --git a/diboot-core/src/main/java/com/diboot/core/binding/parser/MiddleTable.java b/diboot-core/src/main/java/com/diboot/core/binding/parser/MiddleTable.java index 7fac48963b5d2371ed2afa8be0da26b85227705f..b62a85761200a2cfaf7edc552a85ef510be2f271 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/parser/MiddleTable.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/parser/MiddleTable.java @@ -23,7 +23,6 @@ import com.diboot.core.binding.helper.ResultAssembler; import com.diboot.core.config.BaseConfig; import com.diboot.core.config.Cons; import com.diboot.core.exception.BusinessException; -import com.diboot.core.util.ContextHelper; import com.diboot.core.util.S; import com.diboot.core.util.SqlExecutor; import com.diboot.core.util.V; diff --git a/diboot-core/src/main/java/com/diboot/core/binding/parser/ParserCache.java b/diboot-core/src/main/java/com/diboot/core/binding/parser/ParserCache.java index 066c7c1f1f53c182349840f2341bdf3765a2a29c..7637147fa704be065f693245631eebbeeb20f91a 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/parser/ParserCache.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/parser/ParserCache.java @@ -22,12 +22,10 @@ import com.diboot.core.binding.query.BindQuery; import com.diboot.core.binding.query.dynamic.AnnoJoiner; import com.diboot.core.exception.BusinessException; import com.diboot.core.util.BeanUtils; -import com.diboot.core.util.ContextHelper; import com.diboot.core.util.S; import com.diboot.core.util.V; import com.diboot.core.vo.Status; import lombok.extern.slf4j.Slf4j; -import org.apache.ibatis.session.SqlSessionFactory; import org.springframework.core.annotation.AnnotationUtils; import java.lang.annotation.Annotation; @@ -48,15 +46,11 @@ public class ParserCache { /** * VO类-绑定注解缓存 */ - private static Map allVoBindAnnotationCacheMap = new ConcurrentHashMap<>(); - /** - * 表及相关信息的缓存 - */ - private static Map tableToLinkageCacheMap = new ConcurrentHashMap<>(); + private static final Map allVoBindAnnotationCacheMap = new ConcurrentHashMap<>(); /** * dto类-BindQuery注解的缓存 */ - private static Map> dtoClassBindQueryCacheMap = new ConcurrentHashMap<>(); + private static final Map> dtoClassBindQueryCacheMap = new ConcurrentHashMap<>(); /** * 获取指定class对应的Bind相关注解 @@ -97,39 +91,6 @@ public class ParserCache { return group; } - /** - * 初始化Table的相关对象信息 - */ - @Deprecated - private static void initTableToLinkageCacheMap(){ - if(tableToLinkageCacheMap.isEmpty()){ - SqlSessionFactory sqlSessionFactory = ContextHelper.getBean(SqlSessionFactory.class); - Collection> mappers = sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers(); - if(V.notEmpty(mappers)){ - mappers.forEach(m->{ - Type[] types = m.getGenericInterfaces(); - try{ - if(types != null && types.length > 0 && types[0] != null){ - ParameterizedType genericType = (ParameterizedType) types[0]; - Type[] superTypes = genericType.getActualTypeArguments(); - if(superTypes != null && superTypes.length > 0 && superTypes[0] != null){ - String entityClassName = superTypes[0].getTypeName(); - if(entityClassName.length() > 1){ - Class entityClass = Class.forName(entityClassName); - TableLinkage linkage = new TableLinkage(entityClass, m); - tableToLinkageCacheMap.put(linkage.getTable(), linkage); - } - } - } - } - catch (Exception e){ - log.warn("解析mapper异常", e); - } - }); - } - } - } - /** * 是否有is_deleted列 * @return @@ -143,17 +104,6 @@ public class ParserCache { return null; } - /** - * 获取table相关信息 - * @return - */ - @Deprecated - public static TableLinkage getTableLinkage(String table){ - initTableToLinkageCacheMap(); - TableLinkage linkage = tableToLinkageCacheMap.get(table); - return linkage; - } - /** * 获取entity对应的表名 * @param entityClass diff --git a/diboot-core/src/main/java/com/diboot/core/binding/parser/PropInfo.java b/diboot-core/src/main/java/com/diboot/core/binding/parser/PropInfo.java index 6346f0721358c85d8dbb9de19fcdf6ac1a006edb..4d1816abbefcfefa33f0a44f2d3a97b19d4259b5 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/parser/PropInfo.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/parser/PropInfo.java @@ -9,6 +9,7 @@ import com.diboot.core.util.V; import lombok.Getter; import lombok.Setter; +import java.io.Serializable; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; @@ -24,7 +25,8 @@ import java.util.Map; */ @Getter @Setter -public class PropInfo { +public class PropInfo implements Serializable { + private static final long serialVersionUID = 5921667308129991326L; private String idColumn; @@ -73,7 +75,7 @@ public class PropInfo { } // 主键 TableId tableId = fld.getAnnotation(TableId.class); - if(tableId != null){ + if(tableId != null && this.idColumn == null){ if (V.notEmpty(tableId.value())){ columnName = tableId.value(); } diff --git a/diboot-core/src/main/java/com/diboot/core/binding/parser/TableLinkage.java b/diboot-core/src/main/java/com/diboot/core/binding/parser/TableLinkage.java deleted file mode 100644 index 2e6c312faab789fea7904f8f1f50bed87c4e582b..0000000000000000000000000000000000000000 --- a/diboot-core/src/main/java/com/diboot/core/binding/parser/TableLinkage.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd). - *

- * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - *

- * https://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.diboot.core.binding.parser; - -import com.baomidou.mybatisplus.annotation.TableField; -import com.diboot.core.config.Cons; -import com.diboot.core.util.BeanUtils; -import lombok.Getter; -import lombok.Setter; - -import java.io.Serializable; -import java.lang.reflect.Field; - -/** - * table的相关线索信息(已废弃,请换用 EntityInfoCache) - * @author mazc@dibo.ltd - * @version v2.1 - * @date 2020/06/02 - */ -@Getter @Setter -@Deprecated -public class TableLinkage implements Serializable { - private static final long serialVersionUID = 4416187849283913895L; - - public TableLinkage(Class entityClass, Class mapperClass){ - this.entityClass = entityClass; - this.mapperClass = mapperClass; - this.table = ParserCache.getEntityTableName(entityClass); - // 初始化是否有is_deleted - Field field = BeanUtils.extractField(entityClass, Cons.FieldName.deleted.name()); - if(field != null){ - TableField tableField = field.getAnnotation(TableField.class); - if(tableField != null && tableField.exist() == true){ - this.hasDeleted = true; - } - } - } - - private String table; - /** - * 表对应的entity类 - */ - private Class entityClass; - - /** - * 表对应的mapper类 - */ - private Class mapperClass; - - /** - * 是否有逻辑删除字段 - */ - private boolean hasDeleted = false; - -} diff --git a/diboot-core/src/main/java/com/diboot/core/binding/query/BindQuery.java b/diboot-core/src/main/java/com/diboot/core/binding/query/BindQuery.java index e409e689116d7488ba3de195d2a5c721cc63075a..830dbad7be65ac883af8b8b9b80485f0232dbc2c 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/query/BindQuery.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/query/BindQuery.java @@ -63,5 +63,5 @@ public @interface BindQuery { * 查询处理策略:默认忽略空字符串 * @return */ - Strategy strategy() default Strategy.IGNORE_EMPTY_STRING; + Strategy strategy() default Strategy.IGNORE_EMPTY; } \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/binding/query/Comparison.java b/diboot-core/src/main/java/com/diboot/core/binding/query/Comparison.java index cfe1f846555e4b8f4d538946010ccd871cccc20c..5d8d8bd0401f0e1bdd27a3d82a843be7db9db2cc 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/query/Comparison.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/query/Comparison.java @@ -37,5 +37,7 @@ public enum Comparison { BETWEEN, //介于-之间 BETWEEN_BEGIN, //介于之后 - BETWEEN_END //介于之前 + BETWEEN_END, //介于之前 + + NOT_EQ, //不等于 } diff --git a/diboot-core/src/main/java/com/diboot/core/binding/query/Strategy.java b/diboot-core/src/main/java/com/diboot/core/binding/query/Strategy.java index 6238507d96ad4cce5d1e1cb36f9696ce75ce926c..6301481dac6f559f3109fe909430c2e2b22f01bf 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/query/Strategy.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/query/Strategy.java @@ -24,11 +24,11 @@ package com.diboot.core.binding.query; */ public enum Strategy { /** - * 忽略空字符串"" + * 忽略空字符串"",空集合等 */ - IGNORE_EMPTY_STRING, + IGNORE_EMPTY, /** - * 包含空字符串""参与查询 + * 包含空字符串""空集合等参与查询 */ - INCLUDE_EMPTY_STRING + INCLUDE_EMPTY, } \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/DynamicSqlProvider.java b/diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/DynamicSqlProvider.java index 31a41e92ceed45eb5a798c62fc349491854c214a..a882c31085a300b1aef3bb8e227df388e129687b 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/DynamicSqlProvider.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/DynamicSqlProvider.java @@ -21,12 +21,12 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.diboot.core.binding.QueryBuilder; import com.diboot.core.binding.parser.ParserCache; import com.diboot.core.config.BaseConfig; +import com.diboot.core.config.Cons; import com.diboot.core.util.S; import com.diboot.core.util.V; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.jdbc.SQL; -import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -141,12 +141,15 @@ public class DynamicSqlProvider { */ private String formatSqlSelect(String sqlSelect){ String[] columns = S.split(sqlSelect); - List selects = new ArrayList<>(columns.length); - for(String column : columns){ - column = S.removeDuplicateBlank(column).trim(); - selects.add("self."+S.toSnakeCase(column)); + StringBuilder sb = new StringBuilder(); + for(int i=0; i0){ + sb.append(Cons.SEPARATOR_COMMA); + } + sb.append("self."+S.toSnakeCase(column)); } - return S.join(selects); + return sb.toString(); } /** diff --git a/diboot-core/src/main/java/com/diboot/core/config/BaseConfig.java b/diboot-core/src/main/java/com/diboot/core/config/BaseConfig.java index c6646bcacc1fae2f22c6846500cdf5367dc403f5..5546e7859fb745d8d8b34a23a5f6b449a92f4df6 100644 --- a/diboot-core/src/main/java/com/diboot/core/config/BaseConfig.java +++ b/diboot-core/src/main/java/com/diboot/core/config/BaseConfig.java @@ -83,7 +83,7 @@ public class BaseConfig { */ public static int getCutLength(){ if(cutLength == null){ - cutLength = PropertiesUtils.getInteger("system.default.cutLength"); + cutLength = PropertiesUtils.getInteger("diboot.core.cutLength"); if(cutLength == null){ cutLength = 20; } @@ -98,7 +98,7 @@ public class BaseConfig { */ public static int getPageSize() { if(pageSize == null){ - pageSize = PropertiesUtils.getInteger("system.pagination.pageSize"); + pageSize = PropertiesUtils.getInteger("diboot.core.pageSize"); if(pageSize == null){ pageSize = 20; } @@ -113,7 +113,7 @@ public class BaseConfig { */ public static int getBatchSize() { if(batchSize == null){ - batchSize = PropertiesUtils.getInteger("system.batch.size"); + batchSize = PropertiesUtils.getInteger("diboot.core.batchSize"); if(batchSize == null){ batchSize = 1000; } diff --git a/diboot-core/src/main/java/com/diboot/core/config/Cons.java b/diboot-core/src/main/java/com/diboot/core/config/Cons.java index 4cdd6c01f0f1c9290df27f1c4cedb088effb1a42..7aedd93ccf1248b6b7e08eaa7f2d85fe97b76f51 100644 --- a/diboot-core/src/main/java/com/diboot/core/config/Cons.java +++ b/diboot-core/src/main/java/com/diboot/core/config/Cons.java @@ -129,7 +129,7 @@ public class Cons { /** * 成功/失败 结果状态字典定义 */ - public static enum RESULT_STATUS{ + public enum RESULT_STATUS{ /** * 正常 */ diff --git a/diboot-core/src/main/java/com/diboot/core/controller/BaseCrudRestController.java b/diboot-core/src/main/java/com/diboot/core/controller/BaseCrudRestController.java index 50fd52c3f0ab4dc0cad0b7c096c66fac0ae72a1b..8d37ea4666973a6a61caf50a0f9f096d6b147cf6 100644 --- a/diboot-core/src/main/java/com/diboot/core/controller/BaseCrudRestController.java +++ b/diboot-core/src/main/java/com/diboot/core/controller/BaseCrudRestController.java @@ -234,6 +234,26 @@ public class BaseCrudRestController extends BaseContro } } + /*** + * 根据id撤回删除 + * @param id + * @return + * @throws Exception + */ + public JsonResult cancelDeletedEntity(Serializable id) throws Exception { + boolean success = getService().cancelDeletedById(id); + E entity = null; + if (success){ + entity = (E) getService().getEntity(id); + this.afterDeletedCanceled(entity); + log.info("撤回删除操作成功,{}:{}", entity.getClass().getSimpleName(), id); + return JsonResult.OK("撤回成功"); + } else { + log.warn("撤回删除操作未成功,{}:{}", entity.getClass().getSimpleName(), id); + return JsonResult.FAIL_OPERATION("撤回失败"); + } + } + /*** * 根据id批量删除资源对象,用于子类重写的方法 * @param ids @@ -283,7 +303,7 @@ public class BaseCrudRestController extends BaseContro * @param entityOrDto * @return */ - protected String beforeCreate(Object entityOrDto) throws Exception { + protected String beforeCreate(E entityOrDto) throws Exception { return null; } @@ -292,7 +312,7 @@ public class BaseCrudRestController extends BaseContro * @param entityOrDto * @return */ - protected void afterCreated(Object entityOrDto) throws Exception { + protected void afterCreated(E entityOrDto) throws Exception { } /*** @@ -300,7 +320,7 @@ public class BaseCrudRestController extends BaseContro * @param entityOrDto * @return */ - protected String beforeUpdate(Object entityOrDto) throws Exception { + protected String beforeUpdate(E entityOrDto) throws Exception { return null; } @@ -309,7 +329,7 @@ public class BaseCrudRestController extends BaseContro * @param entityOrDto * @return */ - protected void afterUpdated(Object entityOrDto) throws Exception { + protected void afterUpdated(E entityOrDto) throws Exception { } /*** @@ -317,7 +337,7 @@ public class BaseCrudRestController extends BaseContro * @param entityOrDto * @return */ - protected String beforeDelete(Object entityOrDto) throws Exception{ + protected String beforeDelete(E entityOrDto) throws Exception{ return null; } @@ -326,7 +346,15 @@ public class BaseCrudRestController extends BaseContro * @param entityOrDto * @return */ - protected void afterDeleted(Object entityOrDto) throws Exception { + protected void afterDeleted(E entityOrDto) throws Exception { + } + + /*** + * 撤销删除成功后的相关处理 + * @param entityOrDto + * @throws Exception + */ + protected void afterDeletedCanceled(E entityOrDto) throws Exception { } /*** diff --git a/diboot-core/src/main/java/com/diboot/core/exception/BusinessException.java b/diboot-core/src/main/java/com/diboot/core/exception/BusinessException.java index 7eba84549342168a37cd69147c9f2dfc5afd49fa..78857d85b5e1afe1981ca0693823b7ee220c1945 100644 --- a/diboot-core/src/main/java/com/diboot/core/exception/BusinessException.java +++ b/diboot-core/src/main/java/com/diboot/core/exception/BusinessException.java @@ -29,6 +29,8 @@ import java.util.Map; */ public class BusinessException extends RuntimeException { + private Integer code; + /** * 错误的状态 */ @@ -70,12 +72,23 @@ public class BusinessException extends RuntimeException { this.status = status; } + /** + * 自定义状态码和内容提示 + * @param code + * @param msg + */ + public BusinessException(int code, String msg) { + super(msg); + this.code = code; + } + /** * 自定义内容提示 * @param msg */ public BusinessException(String msg) { - super( msg); + super(msg); + this.status = Status.FAIL_OPERATION; } /** @@ -88,13 +101,23 @@ public class BusinessException extends RuntimeException { this.status = status; } + /** + * 自定义内容提示 + * @param code + * @param msg + */ + public BusinessException(int code, String msg, Throwable ex) { + super(msg, ex); + this.code = code; + } + /** * 转换为Map * @return */ public Map toMap(){ - Map map = new HashMap<>(); - map.put("code", status.code()); + Map map = new HashMap<>(8); + map.put("code", getCode()); map.put("msg", getMessage()); return map; } @@ -106,4 +129,12 @@ public class BusinessException extends RuntimeException { public Status getStatus(){ return this.status; } + + private int getCode(){ + if(this.code == null && this.status != null){ + this.code = this.status.code(); + } + return this.code; + } + } diff --git a/diboot-core/src/main/java/com/diboot/core/handler/IEncryptorHandler.java b/diboot-core/src/main/java/com/diboot/core/handler/IEncryptorHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..22c3e68a7df194f057e9f782449807bee8dbb749 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/handler/IEncryptorHandler.java @@ -0,0 +1,32 @@ +package com.diboot.core.handler; + +/** + * 加解密接口 + * + * @author : uu + * @version : v1.0 + * @Date 2021/7/13 09:45 + */ +public interface IEncryptorHandler { + + /** + * 解密 + * @param content + * @return + * @throws Exception + */ + default String encrypt(String content) throws Exception{ + return content; + } + + /** + * 解密 + * @param content + * @return + * @throws Exception + */ + default String decrypt(String content) throws Exception{ + return content; + } + +} diff --git a/diboot-core/src/main/java/com/diboot/core/holder/AnnotationRestApiHolder.java b/diboot-core/src/main/java/com/diboot/core/holder/AnnotationRestApiHolder.java index e72b8705ca51bca4c8f1cf82c77f398a4dfaf55e..3caf4cc482d9cd38ffdb514b326e5790f0e16217 100644 --- a/diboot-core/src/main/java/com/diboot/core/holder/AnnotationRestApiHolder.java +++ b/diboot-core/src/main/java/com/diboot/core/holder/AnnotationRestApiHolder.java @@ -20,14 +20,18 @@ import com.diboot.core.config.Cons; import com.diboot.core.holder.api.CollectThisApi; import com.diboot.core.holder.api.RestApi; import com.diboot.core.holder.api.RestApiWrapper; -import com.diboot.core.util.*; +import com.diboot.core.util.AnnotationUtils; +import com.diboot.core.util.BeanUtils; +import com.diboot.core.util.ContextHelper; +import com.diboot.core.util.V; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.*; +import java.util.ArrayList; +import java.util.List; /** * 注解 RestApi 信息缓存 diff --git a/diboot-core/src/main/java/com/diboot/core/holder/api/RestApi.java b/diboot-core/src/main/java/com/diboot/core/holder/api/RestApi.java index 905a0edb536679c57892e7e509571e9dc6e7c827..1ff8b343b767b151c750ced22c206c383e22d559 100644 --- a/diboot-core/src/main/java/com/diboot/core/holder/api/RestApi.java +++ b/diboot-core/src/main/java/com/diboot/core/holder/api/RestApi.java @@ -15,13 +15,11 @@ */ package com.diboot.core.holder.api; -import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; import java.io.Serializable; -import java.lang.annotation.Annotation; import java.util.List; /** diff --git a/diboot-core/src/main/java/com/diboot/core/mapper/BaseCrudMapper.java b/diboot-core/src/main/java/com/diboot/core/mapper/BaseCrudMapper.java index 2dd7ab6b5bcb8ecd28a080875d20302d3a56b201..3676613fc25a86e8819b21d061c8c67dade98a19 100644 --- a/diboot-core/src/main/java/com/diboot/core/mapper/BaseCrudMapper.java +++ b/diboot-core/src/main/java/com/diboot/core/mapper/BaseCrudMapper.java @@ -16,6 +16,10 @@ package com.diboot.core.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Update; + +import java.io.Serializable; /** * 基础CRUD的父类Mapper @@ -25,4 +29,12 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; */ public interface BaseCrudMapper extends BaseMapper { + /*** + * 通过id撤回当前记录的删除状态 + * @param tableName + * @param id + * @return + */ + @Update("UPDATE `${tableName}` SET is_deleted=0 WHERE id=#{id}") + int cancelDeletedById(@Param("tableName") String tableName, @Param("id") Serializable id); } \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/service/BaseService.java b/diboot-core/src/main/java/com/diboot/core/service/BaseService.java index 305701c627ff7b082fe7c7d4964720e15ca819c4..b303302b707b30f668ab8f2cb6eaf985b83ad4d4 100644 --- a/diboot-core/src/main/java/com/diboot/core/service/BaseService.java +++ b/diboot-core/src/main/java/com/diboot/core/service/BaseService.java @@ -22,7 +22,6 @@ import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapp import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper; import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper; -import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; import com.diboot.core.binding.binder.EntityBinder; import com.diboot.core.binding.binder.EntityListBinder; import com.diboot.core.binding.binder.FieldBinder; @@ -191,6 +190,13 @@ public interface BaseService { */ boolean deleteEntity(Serializable id); + /** + * 根据主键撤销删除 + * @param id + * @return + */ + boolean cancelDeletedById(Serializable id); + /** * 按条件删除实体 * @param queryWrapper @@ -308,6 +314,15 @@ public interface BaseService { */ Map getKeyValueMap(Wrapper queryWrapper); + /** + * 获取id-指定name的映射map + * @param entityIds + * @param getterFn + * @param + * @return + */ + Map getId2NameMap(List entityIds, IGetter getterFn); + /** * 获取Map * @param queryWrapper diff --git a/diboot-core/src/main/java/com/diboot/core/service/impl/BaseServiceImpl.java b/diboot-core/src/main/java/com/diboot/core/service/impl/BaseServiceImpl.java index c77617f80c163fb8aa6ece30cc2e98113207b0f6..df1b23c5df75965fd02dd06db3b341f6c256ccb4 100644 --- a/diboot-core/src/main/java/com/diboot/core/service/impl/BaseServiceImpl.java +++ b/diboot-core/src/main/java/com/diboot/core/service/impl/BaseServiceImpl.java @@ -22,6 +22,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.LambdaUtils; +import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta; import com.baomidou.mybatisplus.core.toolkit.support.SFunction; import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper; @@ -30,13 +31,14 @@ import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers; -import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; import com.diboot.core.binding.Binder; import com.diboot.core.binding.binder.EntityBinder; import com.diboot.core.binding.binder.EntityListBinder; import com.diboot.core.binding.binder.FieldBinder; import com.diboot.core.binding.binder.FieldListBinder; +import com.diboot.core.binding.cache.BindingCacheManager; import com.diboot.core.binding.helper.ServiceAdaptor; +import com.diboot.core.binding.parser.EntityInfoCache; import com.diboot.core.binding.query.dynamic.DynamicJoinQueryWrapper; import com.diboot.core.config.BaseConfig; import com.diboot.core.config.Cons; @@ -102,12 +104,15 @@ public class BaseServiceImpl, T> extends ServiceImpl @Override public FT getValueOfField(SFunction idGetterFn, Serializable idVal, SFunction getterFn) { - String fieldName = convertGetterToFieldName(getterFn); LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() .select(idGetterFn, getterFn) .eq(idGetterFn, idVal); T entity = getSingleEntity(queryWrapper); - return entity != null? (FT)BeanUtils.getProperty(entity, fieldName) : null; + if(entity == null){ + return null; + } + String fieldName = convertGetterToFieldName(getterFn); + return (FT)BeanUtils.getProperty(entity, fieldName); } @Override @@ -203,14 +208,27 @@ public class BaseServiceImpl, T> extends ServiceImpl } } + /** + * 用于更新之前的自动填充等场景调用 + */ + protected void beforeUpdateEntity(T entity){ + } + + @Override + public boolean updateById(T entity) { + return updateEntity(entity); + } + @Override public boolean updateEntity(T entity) { + beforeUpdateEntity(entity); boolean success = super.updateById(entity); return success; } @Override public boolean updateEntity(T entity, Wrapper updateWrapper) { + beforeUpdateEntity(entity); boolean success = super.update(entity, updateWrapper); return success; } @@ -227,6 +245,9 @@ public class BaseServiceImpl, T> extends ServiceImpl if(V.isEmpty(entityList)){ return false; } + for(T entity : entityList){ + beforeUpdateEntity(entity); + } boolean success = super.updateBatchById(entityList); return success; } @@ -261,10 +282,10 @@ public class BaseServiceImpl, T> extends ServiceImpl throw new BusinessException(Status.FAIL_INVALID_PARAM, "主动ID值不能为空!"); } // 从getter中获取class和fieldName - com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda lambda = LambdaUtils.resolve(driverIdGetter); - Class middleTableClass = (Class) lambda.getImplClass(); + LambdaMeta lambdaMeta = LambdaUtils.extract(driverIdGetter); + Class middleTableClass = (Class) lambdaMeta.getInstantiatedClass(); // 获取主动从动字段名 - String driverFieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName()); + String driverFieldName = PropertyNamer.methodToProperty(lambdaMeta.getImplMethodName()); String followerFieldName = convertGetterToFieldName(followerIdGetter); List n2nRelations = null; if(V.notEmpty(followerIdList)){ @@ -403,7 +424,14 @@ public class BaseServiceImpl, T> extends ServiceImpl return super.removeById(id); } - @Override + @Override + public boolean cancelDeletedById(Serializable id) { + EntityInfoCache info = BindingCacheManager.getEntityInfoByClass(super.getEntityClass()); + String tableName = info.getTableName(); + return this.getMapper().cancelDeletedById(tableName, id) > 0; + } + + @Override public boolean deleteEntities(Wrapper queryWrapper){ // 执行 return super.remove(queryWrapper); @@ -439,7 +467,7 @@ public class BaseServiceImpl, T> extends ServiceImpl IPage page = convertToIPage(queryWrapper, pagination); page = super.page(page, queryWrapper); // 如果重新执行了count进行查询,则更新pagination中的总数 - if(page.isSearchCount()){ + if(page.searchCount()){ pagination.setTotalCount(page.getTotal()); } return page.getRecords(); @@ -520,16 +548,19 @@ public class BaseServiceImpl, T> extends ServiceImpl @Override public boolean exists(Wrapper queryWrapper) { - List entityList = getEntityListLimit(queryWrapper, 1); - boolean isExists = V.notEmpty(entityList) && entityList.size() > 0; - entityList = null; - return isExists; + if((queryWrapper instanceof QueryWrapper) && queryWrapper.getSqlSelect() == null){ + String pk = ContextHelper.getIdFieldName(getEntityClass()); + ((QueryWrapper)queryWrapper).select(pk); + } + T entity = getSingleEntity(queryWrapper); + return entity != null; } @Override public List getEntityListByIds(List ids) { QueryWrapper queryWrapper = new QueryWrapper(); - queryWrapper.in(Cons.FieldName.id.name(), ids); + String pk = ContextHelper.getIdFieldName(getEntityClass()); + queryWrapper.in(pk, ids); return getEntityList(queryWrapper); } @@ -544,7 +575,7 @@ public class BaseServiceImpl, T> extends ServiceImpl IPage page = convertToIPage(queryWrapper, pagination); IPage> resultPage = super.pageMaps(page, queryWrapper); // 如果重新执行了count进行查询,则更新pagination中的总数 - if(page.isSearchCount()){ + if(page.searchCount()){ pagination.setTotalCount(page.getTotal()); } return resultPage.getRecords(); @@ -614,6 +645,33 @@ public class BaseServiceImpl, T> extends ServiceImpl return BeanUtils.convertKeyValueList2Map(keyValueList); } + @Override + public Map getId2NameMap(List entityIds, IGetter getterFn) { + if(V.isEmpty(entityIds)){ + return Collections.emptyMap(); + } + String fieldName = BeanUtils.convertToFieldName(getterFn); + EntityInfoCache entityInfo = BindingCacheManager.getEntityInfoByClass(this.getEntityClass()); + String columnName = entityInfo.getColumnByField(fieldName); + QueryWrapper queryWrapper = new QueryWrapper().select( + entityInfo.getIdColumn(), + columnName + ).in(entityInfo.getIdColumn(), entityIds); + // map列表 + List> mapList = getMapList(queryWrapper); + if(V.isEmpty(mapList)){ + return Collections.emptyMap(); + } + Map idNameMap = new HashMap<>(); + for(Map map : mapList){ + ID key = (ID)map.get(entityInfo.getIdColumn()); + String value = S.valueOf(map.get(columnName)); + idNameMap.put(key, value); + } + return idNameMap; + } + + @Override public Map getMap(Wrapper queryWrapper) { return super.getMap(queryWrapper); } @@ -655,6 +713,7 @@ public class BaseServiceImpl, T> extends ServiceImpl @Override public List getViewObjectList(Wrapper queryWrapper, Pagination pagination, Class voClass) { + queryWrapper = ServiceAdaptor.optimizeSelect(queryWrapper, getEntityClass(), voClass); List entityList = getEntityList(queryWrapper, pagination); // 自动转换为VO并绑定关联对象 List voList = Binder.convertAndBindRelations(entityList, voClass); @@ -688,8 +747,8 @@ public class BaseServiceImpl, T> extends ServiceImpl * @return */ private String convertGetterToFieldName(SFunction getterFn) { - com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda lambda = LambdaUtils.resolve(getterFn); - String fieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName()); + LambdaMeta lambdaMeta = LambdaUtils.extract(getterFn); + String fieldName = PropertyNamer.methodToProperty(lambdaMeta.getImplMethodName()); return fieldName; } diff --git a/diboot-core/src/main/java/com/diboot/core/util/BeanUtils.java b/diboot-core/src/main/java/com/diboot/core/util/BeanUtils.java index 074fa52d2b06dc88b38c9a8f83501dbb31d1fcd7..437be019517a81d4f5b6e836686e7253b4a7b1dd 100644 --- a/diboot-core/src/main/java/com/diboot/core/util/BeanUtils.java +++ b/diboot-core/src/main/java/com/diboot/core/util/BeanUtils.java @@ -15,7 +15,7 @@ */ package com.diboot.core.util; - +import com.diboot.core.binding.cache.BindingCacheManager; import com.diboot.core.binding.copy.AcceptAnnoCopier; import com.diboot.core.config.Cons; import com.diboot.core.entity.BaseEntity; @@ -113,12 +113,12 @@ public class BeanUtils { if(V.isEmpty(sourceList)){ return Collections.emptyList(); } - List resultList = new ArrayList<>(); // 类型相同,直接跳过 if(clazz.getName().equals(sourceList.get(0).getClass().getName())){ return sourceList; } // 不同,则转换 + List resultList = new ArrayList<>(sourceList.size()); try{ for(Object source : sourceList){ T target = clazz.getConstructor().newInstance(); @@ -142,8 +142,7 @@ public class BeanUtils { if(V.isEmpty(propMap)){ return; } - List fields = extractAllFields(model.getClass()); - Map fieldNameMaps = convertToStringKeyObjectMap(fields, "name"); + Map fieldNameMaps = BindingCacheManager.getFieldsMap(model.getClass()); for(Map.Entry entry : propMap.entrySet()){ Field field = fieldNameMaps.get(entry.getKey()); if(field != null){ @@ -560,6 +559,9 @@ public class BeanUtils { * @return */ public static List collectToList(List objectList, String getterPropName){ + if(V.isEmpty(objectList)){ + return Collections.emptyList(); + } List fieldValueList = new ArrayList(); try{ for(E object : objectList){ @@ -584,6 +586,9 @@ public class BeanUtils { * @return */ public static List collectToList(List objectList, String getterPropName, boolean[] hasNullFlags){ + if(V.isEmpty(objectList)){ + return Collections.emptyList(); + } List fieldValueList = new ArrayList(); try{ for(E object : objectList){ @@ -698,45 +703,16 @@ public class BeanUtils { * @return */ public static List extractAllFields(Class clazz){ - List fieldList = new ArrayList<>(); - Set fieldNameSet = new HashSet<>(); - while (clazz != null) { - Field[] fields = clazz.getDeclaredFields(); - if(V.notEmpty(fields)){ //被重写属性,以子类override的为准 - Arrays.stream(fields).forEach((field)->{ - if(!fieldNameSet.contains(field.getName())){ - fieldList.add(field); - fieldNameSet.add(field.getName()); - } - }); - } - clazz = clazz.getSuperclass(); - } - return fieldList; + return BindingCacheManager.getFields(clazz); } - /** * 获取类所有属性(包含父类中属性) * @param clazz * @return */ public static List extractFields(Class clazz, Class annotation){ - List fieldList = new ArrayList<>(); - Set fieldNameSet = new HashSet<>(); - while (clazz != null) { - Field[] fields = clazz.getDeclaredFields(); - if(V.notEmpty(fields)){ //被重写属性,以子类override的为准 - Arrays.stream(fields).forEach((field)->{ - if(!fieldNameSet.contains(field.getName()) && field.getAnnotation(annotation) != null){ - fieldList.add(field); - fieldNameSet.add(field.getName()); - } - }); - } - clazz = clazz.getSuperclass(); - } - return fieldList; + return BindingCacheManager.getFields(clazz, annotation); } /** @@ -765,8 +741,8 @@ public class BeanUtils { * @return */ public static Class getGenericityClass(Object instance, int index){ + //TODO 可缓存 Class hostClass = getTargetClass(instance); - ResolvableType resolvableType = ResolvableType.forClass(hostClass).getSuperType(); ResolvableType[] types = resolvableType.getGenerics(); if(V.isEmpty(types) || index >= types.length){ diff --git a/diboot-core/src/main/java/com/diboot/core/util/D.java b/diboot-core/src/main/java/com/diboot/core/util/D.java index abceef060e4389058a84bb86447ac0cf7d8e4717..879186cabee6918d7bbeff45dc30251c5347693f 100644 --- a/diboot-core/src/main/java/com/diboot/core/util/D.java +++ b/diboot-core/src/main/java/com/diboot/core/util/D.java @@ -71,10 +71,10 @@ public class D extends DateUtils{ /** * 时间类型-毫秒数定义 */ - public static long MS_1SECOND = 1000; - public static long MS_1MINUTE = 60 * MS_1SECOND; - public static long MS_1HOUR = 60 * MS_1MINUTE; - public static long MS_1DAY = 24 * MS_1HOUR; + public static final long MS_1SECOND = 1000; + public static final long MS_1MINUTE = 60 * MS_1SECOND; + public static final long MS_1HOUR = 60 * MS_1MINUTE; + public static final long MS_1DAY = 24 * MS_1HOUR; /*** * 当前的日期时间 diff --git a/diboot-core/src/main/java/com/diboot/core/util/Encryptor.java b/diboot-core/src/main/java/com/diboot/core/util/Encryptor.java index 4b8c1909f42311ab4b37b6941d6b44e105b44f7f..add90b943007955b31fceb013656bdb5f3c377f5 100644 --- a/diboot-core/src/main/java/com/diboot/core/util/Encryptor.java +++ b/diboot-core/src/main/java/com/diboot/core/util/Encryptor.java @@ -45,11 +45,24 @@ public class Encryptor { /** * 加密Cipher缓存 */ - private static Map encryptorMap = new ConcurrentHashMap<>(); + private static Map encryptorMap = null; + private static Map getEncryptorMap(){ + if(encryptorMap == null){ + encryptorMap = new ConcurrentHashMap<>(); + } + return encryptorMap; + } + /** * 解密Cipher缓存 */ - private static Map decryptorMap = new ConcurrentHashMap<>(); + private static Map decryptorMap = null; + private static Map getDecryptorMap(){ + if(decryptorMap == null){ + decryptorMap = new ConcurrentHashMap<>(); + } + return decryptorMap; + } /** * 加密字符串(可指定加密密钥) @@ -102,13 +115,13 @@ public class Encryptor { */ private static Cipher getEncryptor(String key) throws Exception{ byte[] keyBytes = getKey(key); - Cipher encryptor = encryptorMap.get(new String(keyBytes)); + Cipher encryptor = getEncryptorMap().get(new String(keyBytes)); if(encryptor == null){ SecretKeySpec skeyspec = new SecretKeySpec(keyBytes, KEY_ALGORITHM); encryptor = Cipher.getInstance(CIPHER_ALGORITHM); encryptor.init(Cipher.ENCRYPT_MODE, skeyspec); // 放入缓存 - encryptorMap.put(key, encryptor); + getEncryptorMap().put(key, encryptor); } return encryptor; } @@ -121,13 +134,13 @@ public class Encryptor { */ private static Cipher getDecryptor(String key) throws Exception{ byte[] keyBytes = getKey(key); - Cipher decryptor = encryptorMap.get(new String(keyBytes)); + Cipher decryptor = getDecryptorMap().get(new String(keyBytes)); if(decryptor == null){ SecretKeySpec skeyspec = new SecretKeySpec(keyBytes, KEY_ALGORITHM); decryptor = Cipher.getInstance(CIPHER_ALGORITHM); decryptor.init(Cipher.DECRYPT_MODE, skeyspec); // 放入缓存 - decryptorMap.put(key, decryptor); + getDecryptorMap().put(key, decryptor); } return decryptor; } diff --git a/diboot-core/src/main/java/com/diboot/core/util/S.java b/diboot-core/src/main/java/com/diboot/core/util/S.java index 138a5a5251a676cf6d052664c43db4f500b2049c..8ecd21c1fefbe34c7de0b9f6b3b875a91a2c288e 100644 --- a/diboot-core/src/main/java/com/diboot/core/util/S.java +++ b/diboot-core/src/main/java/com/diboot/core/util/S.java @@ -362,4 +362,19 @@ public class S extends StringUtils{ return list; } + /** + * 移除转义符 + * @param columnName + * @return + */ + public static String removeEsc(String columnName){ + if(V.isEmpty(columnName)){ + return columnName; + } + if(startsWithAny(columnName, "`", "\"", "[")){ + return substring(columnName, 1, columnName.length()-1); + } + return columnName; + } + } diff --git a/diboot-core/src/main/java/com/diboot/core/util/SqlFileInitializer.java b/diboot-core/src/main/java/com/diboot/core/util/SqlFileInitializer.java index 2149b4e56da1844e7e630a93ca6e90685ac12eb2..8ca18167dcecd52dfe1198a9618829fffa96421f 100644 --- a/diboot-core/src/main/java/com/diboot/core/util/SqlFileInitializer.java +++ b/diboot-core/src/main/java/com/diboot/core/util/SqlFileInitializer.java @@ -25,7 +25,6 @@ import org.springframework.core.env.Environment; import java.io.FileNotFoundException; import java.io.InputStream; import java.sql.Connection; -import java.sql.PreparedStatement; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -236,6 +235,19 @@ public class SqlFileInitializer { * @return */ public static boolean executeMultipleUpdateSqlsWithTransaction(List sqlStatementList){ + try { + return executeMultipleUpdateSqlsWithTransactionThrowException(sqlStatementList); + } catch(Exception e) { + return false; + } + } + + /*** + * 执行多条批量更新SQL,并在执行异常时跑出异常(支持事务,有报错即回滚) + * @param sqlStatementList + * @return + */ + public static boolean executeMultipleUpdateSqlsWithTransactionThrowException(List sqlStatementList) throws Exception{ if(V.isEmpty(sqlStatementList)){ return false; } @@ -257,7 +269,8 @@ public class SqlFileInitializer { catch (Exception e){ log.error("SQL执行异常,请检查:", e); session.rollback(); - return false; + session.close(); + throw e; } finally { if(session != null){ diff --git a/diboot-file-starter/pom.xml b/diboot-file-starter/pom.xml index 458da247cc23e6dd6aaeb017a19e46d5cb069dc4..eaba93d5829befe9cc85ec9941c01b5bb34038af 100644 --- a/diboot-file-starter/pom.xml +++ b/diboot-file-starter/pom.xml @@ -7,11 +7,11 @@ diboot-root com.diboot - 2.2.1 + 2.3.0 diboot-file-spring-boot-starter - 2.2.1 + 2.3.0 jar diboot file component project @@ -32,7 +32,7 @@ com.alibaba easyexcel - 2.2.8 + 2.2.10 diff --git a/diboot-file-starter/src/main/java/com/diboot/file/controller/BaseExcelFileController.java b/diboot-file-starter/src/main/java/com/diboot/file/controller/BaseExcelFileController.java index 3b016b69e31f0de9d619c89317589e6997a1e4f3..a78896d732cc33fec2c1645410695b73368ecc8f 100644 --- a/diboot-file-starter/src/main/java/com/diboot/file/controller/BaseExcelFileController.java +++ b/diboot-file-starter/src/main/java/com/diboot/file/controller/BaseExcelFileController.java @@ -94,7 +94,7 @@ public abstract class BaseExcelFileController extends BaseFileController { } String fileUid = S.substringBefore(previewFileName, "."); String fullPath = FileHelper.getFullPath(previewFileName); - String accessUrl = FileHelper.getRelativePath(previewFileName); + String accessUrl = buildAccessUrl(previewFileName); String ext = FileHelper.getFileExtByName(originFileName); // 描述 String description = getString("description"); @@ -121,7 +121,7 @@ public abstract class BaseExcelFileController extends BaseFileController { } String fileUid = S.substringBefore(previewFileName, "."); String fullPath = FileHelper.getFullPath(previewFileName); - String accessUrl = FileHelper.getRelativePath(previewFileName); + String accessUrl = buildAccessUrl(previewFileName); String ext = FileHelper.getFileExtByName(originFileName); // 保存文件上传记录 UploadFile uploadFile = new UploadFile().setUuid(fileUid) @@ -180,11 +180,16 @@ public abstract class BaseExcelFileController extends BaseFileController { dataMap.put(ORIGIN_FILE_NAME, originFileName); dataMap.put(PREVIEW_FILE_NAME, FileHelper.getFileName(uploadFile.getStoragePath())); List dataList = listener.getDataList(); - if (V.notEmpty(dataList) && dataList.size() > BaseConfig.getPageSize()) { - dataList = dataList.subList(0, BaseConfig.getPageSize()); + int totalCount = 0; + if (V.notEmpty(dataList)) { + totalCount = dataList.size(); + if(dataList.size() > BaseConfig.getPageSize()){ + dataList = dataList.subList(0, BaseConfig.getPageSize()); + } } - //最多返回前端十条数据 + //最多返回前端1页数据 dataMap.put("dataList", dataList); + dataMap.put("totalCount", totalCount); return dataMap; } diff --git a/diboot-scheduler-starter/src/main/java/com/diboot/scheduler/annotation/BindJob.java b/diboot-file-starter/src/main/java/com/diboot/file/excel/annotation/ExcelOption.java similarity index 43% rename from diboot-scheduler-starter/src/main/java/com/diboot/scheduler/annotation/BindJob.java rename to diboot-file-starter/src/main/java/com/diboot/file/excel/annotation/ExcelOption.java index c5e019b5907d776015a8eb4e9405879ff3bf78bb..d75eb22c8d142dc0d8869ff37ba118b4586be199 100644 --- a/diboot-scheduler-starter/src/main/java/com/diboot/scheduler/annotation/BindJob.java +++ b/diboot-file-starter/src/main/java/com/diboot/file/excel/annotation/ExcelOption.java @@ -13,80 +13,68 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.diboot.scheduler.annotation; +package com.diboot.file.excel.annotation; -import org.springframework.context.annotation.Lazy; import org.springframework.core.annotation.AliasFor; -import org.springframework.stereotype.Component; import java.lang.annotation.*; /** - * 定时任务注解,已替换为CollectThisJob - * 请调用: ${@link CollectThisJob} - * @author : uu - * @version : v1.0 - * @Date 2020/12/1 12:49 + * Excel 单元格验证 (单元格下拉选项) + *

+ * 可自定义选项或关联字典 + * + * @author wind + * @version v2.3.0 */ -@Target({ElementType.TYPE}) +@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) +@Inherited @Documented -@Component -@Lazy -@Deprecated -public @interface BindJob { - - /** - * bean Name - * - * @return - */ - @AliasFor( - annotation = Component.class - ) - String value() default ""; +public @interface ExcelOption { /** - * 是否懒加载 + * 此属性为{@link #options}的别名 + *

+ * 当不指定其他属性值时使用而不是指定{@link #options} — 例如:{@code @ExcelOption({"选项1","选项2"})} * - * @return + * @see #options */ - @AliasFor( - annotation = Lazy.class, - value = "value" - ) - boolean lazy() default true; + @AliasFor("options") + String[] value() default {}; /** - * 定时任务的名称 - * - * @return + * 下拉选项列表 + *

+ * {@link #value}是此属性的别名(并与之互斥)。 + *

+ * 优先级: dict > options */ - String name(); + @AliasFor("value") + String[] options() default {}; /** - * cron表达式 - * - * @return + * 关联字典类型 + *

+ * 优先级: dict > options */ - String cron() default ""; + String dict() default ""; /** - * json格式的参数字符串 - * - * @return + * 起始行索引 + *

+ * 当该值为-1时为整个列,且不可小于-1 + *

+ * 默认起始索引为 1 */ - String paramJson() default ""; + int firstRow() default 1; /** - * json格式class参数 + * 行数 *

- * 如果paramJson有值优先使用paramJson定义的值 + * 行数应大于0,不大于0时不添加 单元格验证(单元下拉选项) *

- * 如果参数过多,paramJson不方便,可以定义实体类书写,并给予默认值 - * - * @return + * 默认值 10,000 */ - Class paramClass() default Object.class; - -} \ No newline at end of file + int rows() default 10_000; +} diff --git a/diboot-file-starter/src/main/java/com/diboot/file/excel/cache/ExcelBindAnnoHandler.java b/diboot-file-starter/src/main/java/com/diboot/file/excel/cache/ExcelBindAnnoHandler.java index 86208bedd718e96675c4df47cd6fec6589127a93..db075d08c50360c27461aa26441ebcc6a454a38b 100644 --- a/diboot-file-starter/src/main/java/com/diboot/file/excel/cache/ExcelBindAnnoHandler.java +++ b/diboot-file-starter/src/main/java/com/diboot/file/excel/cache/ExcelBindAnnoHandler.java @@ -47,7 +47,7 @@ public class ExcelBindAnnoHandler { /** * 注解缓存 */ - private static Map> MODEL_BINDANNO_CACHE = new ConcurrentHashMap<>(); + private static final Map> MODEL_BINDANNO_CACHE = new ConcurrentHashMap<>(); /** * 获取字段-绑定注解之间的map diff --git a/diboot-file-starter/src/main/java/com/diboot/file/excel/listener/FixedHeadExcelListener.java b/diboot-file-starter/src/main/java/com/diboot/file/excel/listener/FixedHeadExcelListener.java index 61d0ccb13726fb175da647a3c08fc211ed11f449..2814debb415125c3c6e4c40df4fc353d4cacea5b 100644 --- a/diboot-file-starter/src/main/java/com/diboot/file/excel/listener/FixedHeadExcelListener.java +++ b/diboot-file-starter/src/main/java/com/diboot/file/excel/listener/FixedHeadExcelListener.java @@ -55,7 +55,7 @@ public abstract class FixedHeadExcelListener extends A //解析后的数据实体list private List dataList = new ArrayList<>(); //错误信息 - private List validateErrorMsgs = new ArrayList<>(); + private List validateErrorMsgs = null; // 注入request private Map requestParams; // 是否为预览模式 @@ -105,7 +105,7 @@ public abstract class FixedHeadExcelListener extends A // 提取校验结果 dataList.stream().forEach(data->{ if(V.notEmpty(data.getValidateError())){ - validateErrorMsgs.add(data.getRowIndex() + "行: " + data.getValidateError()); + addErrorMsg(data.getRowIndex() + "行: " + data.getValidateError()); } }); // 有错误 抛出异常 @@ -138,11 +138,11 @@ public abstract class FixedHeadExcelListener extends A String data = ex.getCellData().getStringValue(); errorMsg = currentRowNum+"行" + ex.getColumnIndex()+ "列: 数据格式转换异常,'"+data+"' 非期望的数据类型["+type+"]"; } - validateErrorMsgs.add(errorMsg); + addErrorMsg(errorMsg); } else{//其他异常 log.error("出现未预知的异常:",exception); - validateErrorMsgs.add("解析异常: "+exception.getMessage()); + addErrorMsg("解析异常: "+exception.getMessage()); } } @@ -256,14 +256,6 @@ public abstract class FixedHeadExcelListener extends A */ protected abstract void saveData(List dataList, Map requestParams); - /** - * 校验错误信息 - * @return - */ - public List getErrorMsgs(){ - return this.validateErrorMsgs; - } - /** * 返回表头 * @return @@ -314,4 +306,22 @@ public abstract class FixedHeadExcelListener extends A return BeanUtils.getGenericityClass(this, 0); } + /** + * 校验错误信息 + * @return + */ + public List getErrorMsgs(){ + return this.validateErrorMsgs; + } + + /** + * 添加错误信息 + * @param errorMsg + */ + private void addErrorMsg(String errorMsg){ + if(this.validateErrorMsgs == null){ + this.validateErrorMsgs = new ArrayList<>(); + } + this.validateErrorMsgs.add(errorMsg); + } } diff --git a/diboot-file-starter/src/main/java/com/diboot/file/excel/write/OptionWriteHandler.java b/diboot-file-starter/src/main/java/com/diboot/file/excel/write/OptionWriteHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..73d86cd7c00fccef069ed3964cca7e8237cb82ab --- /dev/null +++ b/diboot-file-starter/src/main/java/com/diboot/file/excel/write/OptionWriteHandler.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.diboot.file.excel.write; + +import com.alibaba.excel.util.ClassUtils; +import com.alibaba.excel.write.handler.AbstractSheetWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; +import com.diboot.core.exception.BusinessException; +import com.diboot.core.service.DictionaryServiceExtProvider; +import com.diboot.core.util.AnnotationUtils; +import com.diboot.core.util.ContextHelper; +import com.diboot.core.util.V; +import com.diboot.core.vo.KeyValue; +import com.diboot.core.vo.Status; +import com.diboot.file.excel.annotation.ExcelOption; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationConstraint; +import org.apache.poi.ss.usermodel.DataValidationHelper; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.xssf.usermodel.XSSFDataValidation; + +import java.lang.reflect.Field; +import java.util.Map; +import java.util.TreeMap; +import java.util.function.BiConsumer; + +/** + * Excel写入 下拉选项 Handler + * + * @author wind + * @version v2.3.0 + */ +@Slf4j +public class OptionWriteHandler extends AbstractSheetWriteHandler { + + /** + * ExcelModel + */ + private final Class clazz; + + /** + * 构造方法 + * + * @param clazz ExcelModel + */ + public OptionWriteHandler(Class clazz) { + this.clazz = clazz; + } + + /** + * Sheet页创建之后,设置单元格下拉框选项 + */ + @Override + public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + Sheet sheet = writeSheetHolder.getSheet(); + // 开始设置下拉框 + DataValidationHelper helper = sheet.getDataValidationHelper(); + setCellOption(clazz, (addressList, options) -> { + // 设置下拉框数据 + DataValidationConstraint constraint = helper.createExplicitListConstraint(options); + DataValidation dataValidation = helper.createValidation(constraint, addressList); + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } else { + dataValidation.setSuppressDropDownArrow(false); + } + sheet.addValidationData(dataValidation); + }); + } + + /** + * 设置单元格下拉框选项 + * + * @param clazz ExcelModel.class + * @param action 设置单元格下拉框选项的行动 + */ + private void setCellOption(Class clazz, BiConsumer action) { + Map sortedAllFiledMap = new TreeMap<>(); + // 获取 Excel 中的字段(排序后的) + ClassUtils.declaredFields(clazz, sortedAllFiledMap, null, false, null); + for (Map.Entry entry : sortedAllFiledMap.entrySet()) { + ExcelOption option = AnnotationUtils.getAnnotation(entry.getValue(), ExcelOption.class); + // 行数不大于0时不添加 单元格验证(单元下拉选项) + if (option != null && option.rows() > 0) { + String[] options = null; + String dictType = option.dict(); + if (V.notEmpty(dictType)) { + options = getDictOptions(dictType); + } + if (V.isEmpty(options)) { + options = option.options(); + } + // 下拉框选为空时不添加 单元格验证(单元下拉选项) + if (V.notEmpty(options)) { + int col = entry.getKey(); + int firstRow = option.firstRow(); + int lastRow = firstRow - 1 + option.rows(); + // 创建单元格范围 —— 起始行、终止行、起始列、终止列 + CellRangeAddressList addressList = new CellRangeAddressList(firstRow, lastRow, col, col); + action.accept(addressList, options); + } + } + } + } + + /** + * 从字典中获取选项 + * + * @param dictType 字典类型 + * @return 选项数组 + */ + private String[] getDictOptions(String dictType) { + DictionaryServiceExtProvider bindDictService = ContextHelper.getBean(DictionaryServiceExtProvider.class); + if (bindDictService == null) { + throw new BusinessException(Status.FAIL_SERVICE_UNAVAILABLE, "DictionaryService未实现,@ExcelOption无法关联字典!"); + } + String[] options = bindDictService.getKeyValueList(dictType).stream().map(KeyValue::getK).toArray(String[]::new); + if (V.isEmpty(options)) { + log.warn(clazz.getSimpleName() + " @ExcelOption 关联字典: " + dictType + " 无值"); + } + return options; + } +} diff --git a/diboot-file-starter/src/main/java/com/diboot/file/starter/FileAutoConfiguration.java b/diboot-file-starter/src/main/java/com/diboot/file/starter/FileAutoConfig.java similarity index 98% rename from diboot-file-starter/src/main/java/com/diboot/file/starter/FileAutoConfiguration.java rename to diboot-file-starter/src/main/java/com/diboot/file/starter/FileAutoConfig.java index 0372aadfb9b4ca87c48f8cb2c0d6f2014342193a..21f90042d52c942713a00e34b3edd33caa173107 100644 --- a/diboot-file-starter/src/main/java/com/diboot/file/starter/FileAutoConfiguration.java +++ b/diboot-file-starter/src/main/java/com/diboot/file/starter/FileAutoConfig.java @@ -41,7 +41,7 @@ import org.springframework.web.multipart.commons.CommonsMultipartResolver; @EnableConfigurationProperties(FileProperties.class) @ComponentScan(basePackages = {"com.diboot.file"}) @MapperScan(basePackages = {"com.diboot.file.mapper"}) -public class FileAutoConfiguration { +public class FileAutoConfig { @Autowired private FileProperties fileProperties; diff --git a/diboot-file-starter/src/main/java/com/diboot/file/starter/FileProperties.java b/diboot-file-starter/src/main/java/com/diboot/file/starter/FileProperties.java index 848e10ca821205566950a45811855753452d089e..d363a8746d7623bdb9f6622ac72106b6060ca86a 100644 --- a/diboot-file-starter/src/main/java/com/diboot/file/starter/FileProperties.java +++ b/diboot-file-starter/src/main/java/com/diboot/file/starter/FileProperties.java @@ -16,15 +16,24 @@ package com.diboot.file.starter; import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; import org.springframework.boot.context.properties.ConfigurationProperties; /** * @author diboot */ -@Data +@Getter +@Setter @ConfigurationProperties(prefix = "diboot.component.file") public class FileProperties { + /** + * 文件存储路径 + */ + private String storageDirectory; + /** * 是否初始化,默认true自动安装SQL */ diff --git a/diboot-file-starter/src/main/java/com/diboot/file/util/ExcelHelper.java b/diboot-file-starter/src/main/java/com/diboot/file/util/ExcelHelper.java index bbb64b122b46df9040f0af25d051ebdefc81c695..fb9c4181b02696464986bf4d575b9e12e90bebb5 100644 --- a/diboot-file-starter/src/main/java/com/diboot/file/util/ExcelHelper.java +++ b/diboot-file-starter/src/main/java/com/diboot/file/util/ExcelHelper.java @@ -16,6 +16,8 @@ package com.diboot.file.util; import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.write.builder.ExcelWriterBuilder; +import com.alibaba.excel.write.handler.WriteHandler; import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; import com.diboot.core.exception.BusinessException; import com.diboot.core.util.BeanUtils; @@ -24,6 +26,7 @@ import com.diboot.core.vo.Status; import com.diboot.file.excel.BaseExcelModel; import com.diboot.file.excel.listener.DynamicHeadExcelListener; import com.diboot.file.excel.listener.FixedHeadExcelListener; +import com.diboot.file.excel.write.OptionWriteHandler; import lombok.extern.slf4j.Slf4j; import javax.servlet.http.HttpServletResponse; @@ -117,16 +120,27 @@ public class ExcelHelper { } /** - * 简单将数据写入excel文件,列宽自适应数据长度 + * 简单将数据写入excel文件 + *

默认列宽自适应数据长度, 可自定义

* * @param filePath * @param sheetName * @param dataList + * @param writeHandlers * @return */ - public static boolean writeDynamicData(String filePath, String sheetName, List> dataList) throws Exception { + public static boolean writeDynamicData(String filePath, String sheetName, List> dataList, + WriteHandler... writeHandlers) throws Exception { try { - EasyExcel.write(filePath).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet(sheetName).doWrite(dataList); + ExcelWriterBuilder write = EasyExcel.write(filePath); + if (writeHandlers.length == 0) { + write = write.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()); + } else { + for (WriteHandler handler : writeHandlers) { + write = write.registerWriteHandler(handler); + } + } + write.sheet(sheetName).doWrite(dataList); return true; } catch (Exception e) { log.error("数据写入excel文件失败", e); @@ -135,21 +149,33 @@ public class ExcelHelper { } /** - * 简单将数据写入excel文件,列宽自适应数据长度 + * 简单将数据写入excel文件 + *

默认列宽自适应数据长度、写入单元格下拉选项, 可自定义

* * @param filePath * @param sheetName * @param dataList * @param + * @param writeHandlers * @return */ - public static boolean writeData(String filePath, String sheetName, List dataList) throws Exception { + public static boolean writeData(String filePath, String sheetName, List dataList, + WriteHandler... writeHandlers) throws Exception { try { if (V.isEmpty(dataList)) { return writeDynamicData(filePath, sheetName, Collections.emptyList()); } Class tClass = (Class) dataList.get(0).getClass(); - EasyExcel.write(filePath, tClass).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet(sheetName).doWrite(dataList); + ExcelWriterBuilder write = EasyExcel.write(filePath, tClass); + if (writeHandlers.length == 0) { + write = write.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) + .registerWriteHandler(new OptionWriteHandler(tClass)); + } else { + for (WriteHandler handler : writeHandlers) { + write = write.registerWriteHandler(handler); + } + } + write.sheet(sheetName).doWrite(dataList); return true; } catch (Exception e) { log.error("数据写入excel文件失败", e); @@ -159,20 +185,31 @@ public class ExcelHelper { /** * web 导出excel + *

默认列宽自适应数据长度、写入单元格下拉选项, 可自定义

* * @param response - * @param clazz 导出的类 - * @param data 导出的数据 - * @param + * @param clazz 导出的类 + * @param data 导出的数据 + * @param writeHandlers 写入处理程序 * @throws Exception */ - public static void exportExcel(HttpServletResponse response, String fileName, Class clazz, List data) throws Exception { + public static void exportExcel(HttpServletResponse response, String fileName, + Class clazz, List data, + WriteHandler... writeHandlers) throws Exception { try { setExportExcelResponseHeader(response, fileName); + + ExcelWriterBuilder write = EasyExcel.write(response.getOutputStream(), clazz); + if (writeHandlers.length == 0) { + write = write.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) + .registerWriteHandler(new OptionWriteHandler(clazz)); + } else { + for (WriteHandler handler : writeHandlers) { + write = write.registerWriteHandler(handler); + } + } // 这里需要设置不关闭流 - EasyExcel.write(response.getOutputStream(), clazz) - .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) - .autoCloseStream(Boolean.FALSE) + write.autoCloseStream(Boolean.FALSE) .sheet("sheet1") .doWrite(data); } catch (Exception e) { diff --git a/diboot-file-starter/src/main/java/com/diboot/file/util/FileHelper.java b/diboot-file-starter/src/main/java/com/diboot/file/util/FileHelper.java index f9b39d1e2daa0cc09f6de4a03eaf99d4280b5679..090d2167478648c87b8156b78ca38b128b106f28 100644 --- a/diboot-file-starter/src/main/java/com/diboot/file/util/FileHelper.java +++ b/diboot-file-starter/src/main/java/com/diboot/file/util/FileHelper.java @@ -41,7 +41,7 @@ public class FileHelper{ /** * file验证 */ - public static final List DANGER_FILE_SUFFIX = Arrays.asList("exe", "bat", "bin", "dll", "sh"); + public static final List DANGER_FILE_SUFFIX = Arrays.asList("exe","bat","bin","dll","sh","php","pl","cgi","asp","aspx","jsp","php5","php4","php3","htm","html"); /** * excel格式 @@ -51,7 +51,7 @@ public class FileHelper{ /** * 文件存储路径参数名 */ - public static final String FILE_STORAGE_DIRECTORY = "files.storage.directory"; + public static final String FILE_STORAGE_DIRECTORY = "diboot.component.file.storageDirectory"; /** * 文件存储路径 */ diff --git a/diboot-file-starter/src/main/java/com/diboot/file/util/HttpHelper.java b/diboot-file-starter/src/main/java/com/diboot/file/util/HttpHelper.java index ec068d0b853a8661826df4f6d4ec705f9494ddd1..e5caa1ad391a9cd43bb13a75918d7cc4cdd17854 100644 --- a/diboot-file-starter/src/main/java/com/diboot/file/util/HttpHelper.java +++ b/diboot-file-starter/src/main/java/com/diboot/file/util/HttpHelper.java @@ -57,7 +57,7 @@ public class HttpHelper { /*** * 文件扩展名-ContentType的对应关系 */ - private static Map EXT_CONTENT_TYPE_MAP = new HashMap(){{ + private static final Map EXT_CONTENT_TYPE_MAP = new HashMap(){{ put("xls", "application/x-msdownload"); put("xlsx", "application/x-msdownload"); put("doc", "application/x-msdownload"); diff --git a/diboot-file-starter/src/main/resources/META-INF/spring.factories b/diboot-file-starter/src/main/resources/META-INF/spring.factories index 044e50350fac54c133db94b7584a455a89110890..34654598ee5902533263fa7ba88b6fe6f272f422 100644 --- a/diboot-file-starter/src/main/resources/META-INF/spring.factories +++ b/diboot-file-starter/src/main/resources/META-INF/spring.factories @@ -1 +1 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.diboot.file.starter.FileAutoConfiguration \ No newline at end of file +org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.diboot.file.starter.FileAutoConfig \ No newline at end of file diff --git a/diboot-iam-starter/pom.xml b/diboot-iam-starter/pom.xml index a79c3fa7fc9cdff743b63750f94e6cc62b1e27b1..0e340478d074878b94e9836fd609f194e132581c 100644 --- a/diboot-iam-starter/pom.xml +++ b/diboot-iam-starter/pom.xml @@ -7,16 +7,15 @@ com.diboot diboot-root - 2.2.1 + 2.3.0 diboot-iam-spring-boot-starter - 2.2.1 + 2.3.0 jar diboot IAM project - com.diboot @@ -46,6 +45,13 @@ 1.9.6 + + + org.springframework.boot + spring-boot-starter-data-redis + provided + + org.springframework.boot diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/annotation/Operation.java b/diboot-iam-starter/src/main/java/com/diboot/iam/annotation/Operation.java index 7c312eec4604f7cc5ca13b8219f975f042d9a857..cdd6f147bd5967de677b50040e66b301f42c53d0 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/annotation/Operation.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/annotation/Operation.java @@ -22,17 +22,6 @@ package com.diboot.iam.annotation; * @date 2019/12/23 */ public class Operation { - @Deprecated - public static final String LIST = "list"; - @Deprecated - public static final String DETAIL = "detail"; - @Deprecated - public static final String CREATE = "create"; - @Deprecated - public static final String UPDATE = "update"; - @Deprecated - public static final String DELETE = "delete"; - /** * 操作权限类型 - 首页 */ diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/annotation/process/LogAspect.java b/diboot-iam-starter/src/main/java/com/diboot/iam/annotation/process/LogAspect.java index d8577d2db42cb4425fcc1c0ad9dd12b7ec958447..fa88e92ac205be33f67610e65a7460c22d1dd85a 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/annotation/process/LogAspect.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/annotation/process/LogAspect.java @@ -30,15 +30,12 @@ import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.InputStreamSource; import org.springframework.stereotype.Component; -import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; -import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; -import java.util.Collections; import java.util.HashMap; import java.util.Map; diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/auth/AuthServiceFactory.java b/diboot-iam-starter/src/main/java/com/diboot/iam/auth/AuthServiceFactory.java index d7a73c1aaff6de11d54898d905fe8f6607519d86..a2a0a8beba23ff05c8519bc43096bf78c2f9b527 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/auth/AuthServiceFactory.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/auth/AuthServiceFactory.java @@ -18,6 +18,7 @@ package com.diboot.iam.auth; import com.diboot.core.util.ContextHelper; import com.diboot.core.util.V; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Primary; import java.util.HashMap; import java.util.List; @@ -31,7 +32,7 @@ import java.util.Map; */ @Slf4j public class AuthServiceFactory { - private static Map AUTHTYPE_SERVICE_CACHE = new HashMap<>(); + private static final Map AUTHTYPE_SERVICE_CACHE = new HashMap<>(); /** * 获取对应认证类型的Service实现 @@ -43,7 +44,14 @@ public class AuthServiceFactory { List authServiceList = ContextHelper.getBeans(AuthService.class); if(V.notEmpty(authServiceList)){ authServiceList.stream().forEach((service)->{ - AUTHTYPE_SERVICE_CACHE.put(service.getAuthType(), service); + if(AUTHTYPE_SERVICE_CACHE.containsKey(service.getAuthType())){ + if(service.getClass().getAnnotation(Primary.class) != null){ + AUTHTYPE_SERVICE_CACHE.put(service.getAuthType(), service); + } + } + else{ + AUTHTYPE_SERVICE_CACHE.put(service.getAuthType(), service); + } }); } } diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/auth/impl/IamCustomizeImpl.java b/diboot-iam-starter/src/main/java/com/diboot/iam/auth/impl/IamCustomizeImpl.java index 6d9fe2e22602b15b622248672cbbd9a1ddace0a2..e1135410453d8a36a202a8664ca6a6f8dc563947 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/auth/impl/IamCustomizeImpl.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/auth/impl/IamCustomizeImpl.java @@ -16,12 +16,14 @@ package com.diboot.iam.auth.impl; import com.diboot.core.util.AnnotationUtils; +import com.diboot.core.vo.Status; import com.diboot.iam.auth.IamCustomize; import com.diboot.iam.entity.BaseLoginUser; import com.diboot.iam.entity.IamAccount; import com.diboot.iam.exception.PermissionException; import com.diboot.iam.starter.IamProperties; import com.diboot.iam.util.IamSecurityUtils; +import org.apache.shiro.authz.UnauthenticatedException; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -63,6 +65,9 @@ public class IamCustomizeImpl implements IamCustomize { try{ IamSecurityUtils.getSubject().checkPermission(permissionCode); } + catch (UnauthenticatedException e){ + throw new PermissionException(Status.FAIL_INVALID_TOKEN, e); + } catch (Exception e){ throw new PermissionException(e); } diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/auth/impl/PwdAuthServiceImpl.java b/diboot-iam-starter/src/main/java/com/diboot/iam/auth/impl/PwdAuthServiceImpl.java index 40114fdbbbd17edf451fbc9510be9ed07166c69c..0d9e55ba11c54677a917cc952a33d00ef9885700 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/auth/impl/PwdAuthServiceImpl.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/auth/impl/PwdAuthServiceImpl.java @@ -133,11 +133,11 @@ public class PwdAuthServiceImpl implements AuthService { private BaseJwtAuthToken initBaseJwtAuthToken(AuthCredential credential){ BaseJwtAuthToken token = new BaseJwtAuthToken(getAuthType(), credential.getUserTypeClass()); // 设置账号密码 - token.setAuthAccount(credential.getAuthAccount()) - .setAuthSecret(credential.getAuthSecret()) - .setRememberMe(credential.isRememberMe()) - .setTenantId(credential.getTenantId()) - .setExtObj(credential.getExtObj()); + token.setAuthAccount(credential.getAuthAccount()); + token.setAuthSecret(credential.getAuthSecret()); + token.setRememberMe(credential.isRememberMe()); + token.setTenantId(credential.getTenantId()); + token.setExtObj(credential.getExtObj()); // 生成token return token.generateAuthtoken(getExpiresInMinutes()); } diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/auth/impl/SSOAuthServiceImpl.java b/diboot-iam-starter/src/main/java/com/diboot/iam/auth/impl/SSOAuthServiceImpl.java index de407d51cf4964080f71428f02b31c2e0f15ce07..fcb795f70d39fc8ec6df881baf8ce941454cb899 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/auth/impl/SSOAuthServiceImpl.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/auth/impl/SSOAuthServiceImpl.java @@ -137,9 +137,9 @@ public class SSOAuthServiceImpl implements AuthService { String username = parseCasTicket(ssoCredential); ssoCredential.setAuthAccount(username); // 设置账号密码 - token.setAuthAccount(ssoCredential.getAuthAccount()) - .setTenantId(credential.getTenantId()) - .setRememberMe(ssoCredential.isRememberMe()); + token.setAuthAccount(ssoCredential.getAuthAccount()); + token.setTenantId(credential.getTenantId()); + token.setRememberMe(ssoCredential.isRememberMe()); // 生成token return token.generateAuthtoken(getExpiresInMinutes()); } diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/cache/IamCacheManager.java b/diboot-iam-starter/src/main/java/com/diboot/iam/cache/IamCacheManager.java index 9ec4105a4fe001576bdc4bbe34bc3aa60f1e141a..bfe50a32f34e5104e9787343a88bb981d6fff532 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/cache/IamCacheManager.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/cache/IamCacheManager.java @@ -45,12 +45,13 @@ public class IamCacheManager { /** * url-permission 缓存 */ - private static Map URL_PERMISSIONCODE_CACHE = new ConcurrentHashMap<>(); + private static Map URL_PERMISSIONCODE_CACHE = new ConcurrentHashMap<>(8); private static StaticMemoryCacheManager getCacheManager(){ if(iamMemoryCacheManager == null){ iamMemoryCacheManager = new StaticMemoryCacheManager( CACHE_NAME_CONTROLLER_API); + URL_PERMISSIONCODE_CACHE.clear(); } return iamMemoryCacheManager; } @@ -106,7 +107,7 @@ public class IamCacheManager { /** * 初始化 */ - private static void initApiPermissionCache() { + private static synchronized void initApiPermissionCache() { StaticMemoryCacheManager cacheManager = getCacheManager(); if (cacheManager.isUninitializedCache(CACHE_NAME_CONTROLLER_API) == false) { return; diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/config/Cons.java b/diboot-iam-starter/src/main/java/com/diboot/iam/config/Cons.java index 9dd207bd4e5ef576302f47b78e31866712a2fa87..52af88a6b6980b6bf90cc2a6e11e103c487cc568 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/config/Cons.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/config/Cons.java @@ -28,7 +28,7 @@ public class Cons extends com.diboot.core.config.Cons { /** * 数据字典类型定义 */ - public static enum DICTTYPE{ + public enum DICTTYPE{ DATA_PERMISSION_TYPE, AUTH_TYPE, ACCOUNT_STATUS, @@ -43,7 +43,7 @@ public class Cons extends com.diboot.core.config.Cons { /** * 字典编码 - 数据权限类型 */ - public static enum DICTCODE_DATA_PERMISSION_TYPE{ + public enum DICTCODE_DATA_PERMISSION_TYPE{ INDIVIDUAL, DEPT, DEPT_MEMS, @@ -53,7 +53,7 @@ public class Cons extends com.diboot.core.config.Cons { /** * 字典编码 - 认证方式 */ - public static enum DICTCODE_AUTH_TYPE{ + public enum DICTCODE_AUTH_TYPE{ PWD, SSO, WX_MP, @@ -64,7 +64,7 @@ public class Cons extends com.diboot.core.config.Cons { /** * 字典编码 - 账号状态 */ - public static enum DICTCODE_ACCOUNT_STATUS{ + public enum DICTCODE_ACCOUNT_STATUS{ A, L, I @@ -73,7 +73,7 @@ public class Cons extends com.diboot.core.config.Cons { /** * 字典编码 - 用户状态 */ - public static enum DICTCODE_USER_STATUS{ + public enum DICTCODE_USER_STATUS{ A, L, I @@ -82,7 +82,7 @@ public class Cons extends com.diboot.core.config.Cons { /** * 字典编码 - 组织类型 */ - public static enum DICTCODE_ORG_TYPE{ + public enum DICTCODE_ORG_TYPE{ COMP, DEPT } @@ -90,7 +90,7 @@ public class Cons extends com.diboot.core.config.Cons { /** * 字典编码 - 权限类型 */ - public static enum DICTCODE_PERMISSION_TYPE{ + public enum DICTCODE_PERMISSION_TYPE{ MENU, OPERATION, OTHER @@ -99,17 +99,7 @@ public class Cons extends com.diboot.core.config.Cons { /** * 前端权限类型 */ - @Deprecated - public static enum FRONTEND_PERMISSION_DISPLAY_TYPE{ - MODULE, - MENU, - PERMISSION - } - - /** - * 前端权限类型 - */ - public static enum RESOURCE_PERMISSION_DISPLAY_TYPE{ + public enum RESOURCE_PERMISSION_DISPLAY_TYPE{ MODULE, MENU, PERMISSION @@ -124,4 +114,8 @@ public class Cons extends com.diboot.core.config.Cons { * 超级管理员 */ public static final String ROLE_SUPER_ADMIN = "SUPER_ADMIN"; + + public static final String AUTHENTICATION_CAHCE_NAME = "com.diboot.iam.jwt.BaseJwtRealm.authenticationCache"; + public static final String AUTHORIZATION_CAHCE_NAME = "com.diboot.iam.jwt.BaseJwtRealm.authorizationCache"; + } \ No newline at end of file diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/dto/EncryptCredential.java b/diboot-iam-starter/src/main/java/com/diboot/iam/dto/EncryptCredential.java new file mode 100644 index 0000000000000000000000000000000000000000..58c71d8264a76c60e91952cb0e083a614d85c0fa --- /dev/null +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/dto/EncryptCredential.java @@ -0,0 +1,58 @@ +package com.diboot.iam.dto; + +import com.baomidou.mybatisplus.core.toolkit.ReflectionKit; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.diboot.core.exception.BusinessException; +import com.diboot.core.handler.IEncryptorHandler; +import com.diboot.core.util.BeanUtils; +import com.diboot.core.util.JSON; +import com.diboot.core.util.V; +import com.diboot.core.vo.Status; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import lombok.extern.slf4j.Slf4j; + +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** + * 加密认证 + * + * @author : uu + * @version : v1.0 + * @Date 2021/7/13 09:35 + */ +@Getter +@Setter +@Accessors(chain = true) +@Slf4j +public class EncryptCredential implements Serializable { + private static final long serialVersionUID = 8178800708883555475L; + + /** + * 密文 + */ + @NotNull(message = "认证信息不能为空") + private String ciphertext; + + /** + * 获取认证信息 + * + * @return + */ + public T getAuthCredential(IEncryptorHandler encryptorHandler, Class authCredentialCls) { + try { + String decryptContent = encryptorHandler.decrypt(ciphertext); + T result = (T) JSON.parseObject(decryptContent, authCredentialCls); + String errMsg = V.validateBean(result); + if (V.notEmpty(errMsg)) { + throw new BusinessException(Status.FAIL_INVALID_PARAM, errMsg); + } + return result; + } catch (Exception e) { + log.error("获取认证信息失败!", e); + throw new BusinessException(e.getMessage()); + } + } +} diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/dto/IamPositionFormDTO.java b/diboot-iam-starter/src/main/java/com/diboot/iam/dto/IamPositionFormDTO.java index 22ffe9951aa519647c516cfb3f8da4b808af642b..23f4ea646d4233d6e3c635f5c57d17c080a2f82d 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/dto/IamPositionFormDTO.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/dto/IamPositionFormDTO.java @@ -17,6 +17,9 @@ package com.diboot.iam.dto; import com.diboot.iam.entity.IamPosition; import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; import java.util.List; @@ -26,7 +29,9 @@ import java.util.List; * @version v2.2 * @date 2020/12/1 */ -@Data +@Getter +@Setter +@Accessors(chain = true) public class IamPositionFormDTO extends IamPosition { private static final long serialVersionUID = -2721951760052373607L; diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/dto/IamUserPositionBatchDTO.java b/diboot-iam-starter/src/main/java/com/diboot/iam/dto/IamUserPositionBatchDTO.java index c9764ff33b8d57e7203c7bde25142c2accbd1d8f..0d302f24f255c825f67ed3280b0a046e2f08718f 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/dto/IamUserPositionBatchDTO.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/dto/IamUserPositionBatchDTO.java @@ -17,6 +17,9 @@ package com.diboot.iam.dto; import com.diboot.iam.entity.IamUserPosition; import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; import java.util.List; @@ -26,7 +29,9 @@ import java.util.List; * @version v2.2 * @date 2020/12/1 */ -@Data +@Getter +@Setter +@Accessors(chain = true) public class IamUserPositionBatchDTO extends IamUserPosition { private static final long serialVersionUID = 5531280576807490425L; diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/entity/BaseLoginUser.java b/diboot-iam-starter/src/main/java/com/diboot/iam/entity/BaseLoginUser.java index 923cd5a5350d27afb6584450ee98339ce5d7c92d..79cab470462d877c3cb9d8a9f960d46f01379469 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/entity/BaseLoginUser.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/entity/BaseLoginUser.java @@ -18,6 +18,7 @@ package com.diboot.iam.entity; import com.baomidou.mybatisplus.annotation.TableField; import com.diboot.core.entity.BaseEntity; import com.diboot.core.vo.KeyValue; +import com.fasterxml.jackson.annotation.JsonIgnore; /** * 可登录用户Base类定义 @@ -39,6 +40,13 @@ public abstract class BaseLoginUser extends BaseEntity { */ public abstract String getUserType(); + /** + * 附加对象,当前auth-token + */ + @JsonIgnore + @TableField(exist = false) + private String authToken; + /** * 附加对象,用于岗位等扩展 */ @@ -52,4 +60,14 @@ public abstract class BaseLoginUser extends BaseEntity { this.extentionObj = extentionObj; } + public String getAuthToken(){ + return this.authToken; + } + public void setAuthToken(String authToken){ + this.authToken = authToken; + } + + public String toString(){ + return this.authToken; + } } \ No newline at end of file diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/exception/PermissionException.java b/diboot-iam-starter/src/main/java/com/diboot/iam/exception/PermissionException.java index a5d3e8349c5ac2ee3b88102928883f0447af9a3d..cd41d645fe87880ea9108606f85910be2e81487b 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/exception/PermissionException.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/exception/PermissionException.java @@ -30,8 +30,12 @@ public class PermissionException extends BusinessException { super(Status.FAIL_NO_PERMISSION); } + public PermissionException(Status status,Throwable ex) { + super(status,ex); + } + public PermissionException(Throwable ex) { - super(Status.FAIL_NO_PERMISSION, ex); + this(Status.FAIL_NO_PERMISSION, ex); } public PermissionException(String msg) { diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/jwt/BaseJwtAuthToken.java b/diboot-iam-starter/src/main/java/com/diboot/iam/jwt/BaseJwtAuthToken.java index a9f8e275c8d9bc1a854da4f4119674be01eceffc..5188b615289fa912a0e5aa38c254b0c7534e5690 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/jwt/BaseJwtAuthToken.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/jwt/BaseJwtAuthToken.java @@ -31,7 +31,7 @@ import java.util.Map; * @version v2.0 * @date 2019/6/6 */ -@Getter @Setter @Accessors(chain = true) +@Getter @Setter @Slf4j public class BaseJwtAuthToken implements RememberMeAuthenticationToken { private static final long serialVersionUID = -5518501153334708409L; @@ -75,6 +75,10 @@ public class BaseJwtAuthToken implements RememberMeAuthenticationToken { */ private boolean validPassword = true; + private Object principal; + + private Object credentials; + public BaseJwtAuthToken(){ } @@ -117,8 +121,6 @@ public class BaseJwtAuthToken implements RememberMeAuthenticationToken { return userTypeClass.getSimpleName(); } - - /** * 生成token tenantId,account,userTypeClass,authType,60 * @param expiresInMinutes diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/jwt/BaseJwtRealm.java b/diboot-iam-starter/src/main/java/com/diboot/iam/jwt/BaseJwtRealm.java index 020af3e97a0a1e55fa5b44b7bc367d501cca3dcc..2f191770bd3cd58188065fa8e4d45299d2d86205 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/jwt/BaseJwtRealm.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/jwt/BaseJwtRealm.java @@ -104,6 +104,7 @@ public class BaseJwtRealm extends AuthorizingRealm { if(loginUser == null){ throw new AuthenticationException("用户不存在"); } + loginUser.setAuthToken(jwtToken.getAuthtoken()); IamExtensible iamExtensible = getIamUserRoleService().getIamExtensible(); if(iamExtensible != null){ KeyValue extentionObj = iamExtensible.getUserExtentionObj(jwtToken.getUserTypeClass().getSimpleName(), account.getUserId(), jwtToken.getExtObj()); diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/jwt/DefaultJwtAuthFilter.java b/diboot-iam-starter/src/main/java/com/diboot/iam/jwt/DefaultJwtAuthFilter.java index 7e6a54de4e89a1aae65ea48cb298a568fa01b226..df6b5c9cfbb194152af274fb3b0acb77dccbf278 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/jwt/DefaultJwtAuthFilter.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/jwt/DefaultJwtAuthFilter.java @@ -56,16 +56,17 @@ public class DefaultJwtAuthFilter extends BasicHttpAuthenticationFilter { } //解密是否成功 if(V.notEmpty(claims.getSubject())){ - log.debug("Token验证成功!account={}", claims.getSubject()); + log.debug("Token验证成功!account={}, url={}", claims.getSubject(), httpRequest.getRequestURI()); // 如果临近过期,则生成新的token返回 String refreshToken = JwtUtils.generateNewTokenIfRequired(claims); if(refreshToken != null){ // 写入response header中 JwtUtils.addTokenToResponseHeader((HttpServletResponse) response, refreshToken); + log.debug("返回新的token: {}", refreshToken); } return true; } - log.debug("Token验证失败!url=" + httpRequest.getRequestURL()); + log.debug("Token验证失败!url=" + httpRequest.getRequestURI()); return false; } @@ -75,7 +76,8 @@ public class DefaultJwtAuthFilter extends BasicHttpAuthenticationFilter { */ @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { - log.debug("Token认证失败: onAccessDenied"); + HttpServletRequest httpRequest = (HttpServletRequest) request; + log.debug("Token认证失败: onAccessDenied。url={}", httpRequest.getRequestURI()); JsonResult jsonResult = new JsonResult(Status.FAIL_INVALID_TOKEN); this.responseJson((HttpServletResponse) response, jsonResult); return false; diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/jwt/StatelessJwtAuthFilter.java b/diboot-iam-starter/src/main/java/com/diboot/iam/jwt/StatelessJwtAuthFilter.java index e14283168147fe9276ec34f5a334a6391ec30f75..91ada38e5394502fa4057a48c0ead0dc7576ca57 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/jwt/StatelessJwtAuthFilter.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/jwt/StatelessJwtAuthFilter.java @@ -21,6 +21,7 @@ import com.diboot.core.util.V; import com.diboot.core.vo.JsonResult; import com.diboot.core.vo.Status; import com.diboot.iam.config.Cons; +import com.diboot.iam.entity.IamUser; import com.diboot.iam.util.IamSecurityUtils; import com.diboot.iam.util.JwtUtils; import io.jsonwebtoken.Claims; @@ -46,7 +47,7 @@ import java.io.PrintWriter; * @Date 2020/11/19 10:46 */ @Slf4j -@Getter@Setter +@Getter @Setter public class StatelessJwtAuthFilter extends BasicHttpAuthenticationFilter { /** @@ -79,21 +80,26 @@ public class StatelessJwtAuthFilter extends BasicHttpAuthenticationFilter { JwtUtils.addTokenToResponseHeader((HttpServletResponse) response, refreshToken); } // 构建登陆的token - // tenantId,account,userTypeClass,authType,60 - String[] subjectDetail = subject.split(Cons.SEPARATOR_COMMA); - BaseJwtAuthToken baseJwtAuthToken = new BaseJwtAuthToken(); - baseJwtAuthToken.setTenantId(Long.valueOf(subjectDetail[0])); - baseJwtAuthToken.setAuthAccount(subjectDetail[1]); - try { - baseJwtAuthToken.setUserTypeClass(Class.forName(subjectDetail[2])); - } catch (ClassNotFoundException e) { - log.debug("Token验证失败!用户类型{}不存在,url={}", subjectDetail[2], httpRequest.getRequestURL()); - return false; + if(IamSecurityUtils.getSubject().isAuthenticated() == false){ + // tenantId,account,userTypeClass,authType,60 + String[] subjectDetail = subject.split(Cons.SEPARATOR_COMMA); + BaseJwtAuthToken baseJwtAuthToken = new BaseJwtAuthToken(); + baseJwtAuthToken.setTenantId(Long.valueOf(subjectDetail[0])); + baseJwtAuthToken.setAuthAccount(subjectDetail[1]); + if(!IamUser.class.getName().equals(subjectDetail[2])){ + try { + baseJwtAuthToken.setUserTypeClass(Class.forName(subjectDetail[2])); + } catch (ClassNotFoundException e) { + log.debug("Token验证失败!用户类型{}不存在,url={}", subjectDetail[2], httpRequest.getRequestURL()); + return false; + } + } + baseJwtAuthToken.setAuthType(subjectDetail[3]); + baseJwtAuthToken.setAuthtoken(currentToken); + baseJwtAuthToken.setValidPassword(false); + IamSecurityUtils.getSubject().login(baseJwtAuthToken); + log.debug("无状态token自动登录完成"); } - baseJwtAuthToken.setAuthType(subjectDetail[3]); - baseJwtAuthToken.setAuthtoken(currentToken); - baseJwtAuthToken.setValidPassword(false); - IamSecurityUtils.getSubject().login(baseJwtAuthToken); return true; } log.debug("Token验证失败!url=" + httpRequest.getRequestURL()); diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/mapper/IamUserMapper.java b/diboot-iam-starter/src/main/java/com/diboot/iam/mapper/IamUserMapper.java index a86a4d57ab3808d9cc4f4f97fd6625155310ff0f..7db8518fa1aa475237b5bd7a0aae5a3ca9e94327 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/mapper/IamUserMapper.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/mapper/IamUserMapper.java @@ -32,13 +32,5 @@ import org.apache.ibatis.annotations.Select; @Mapper public interface IamUserMapper extends BaseCrudMapper { - /*** - * 通过org层级的排序来获取用户分页数据 - * @param page - * @param queryWrapper - * @return - */ - @Select("select u.* from iam_user u left join iam_org o on u.org_id=o.id ${ew.customSqlSegment} order by o.depth asc, o.sort_id desc, o.id desc, u.id desc") - IPage selectPageSortByOrg(IPage page, @Param("ew") Wrapper queryWrapper); } diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/redis/ShiroRedisCache.java b/diboot-iam-starter/src/main/java/com/diboot/iam/redis/ShiroRedisCache.java new file mode 100644 index 0000000000000000000000000000000000000000..facc61fc1da7855ed5625548798ec4336f48ce16 --- /dev/null +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/redis/ShiroRedisCache.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2015-2021, www.dibo.ltd (service@dibo.ltd). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.diboot.iam.redis; + +import org.apache.shiro.cache.Cache; +import org.apache.shiro.cache.CacheException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.redis.core.RedisTemplate; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * RedisCache缓存定义 + * + * @author : uu + * @version : v1.0 + * @Date 2021/6/8 18:36 + * Copyright © diboot.com + */ +public class ShiroRedisCache implements Cache { + private static final Logger log = LoggerFactory.getLogger(ShiroRedisCache.class); + + private RedisTemplate redisTemplate; + private String cacheName; + private int tokenExpireMinutes; + + public ShiroRedisCache(String cacheName, RedisTemplate redisTemplate, int tokenExpireMinutes) { + this.cacheName = cacheName; + this.redisTemplate = redisTemplate; + this.tokenExpireMinutes = tokenExpireMinutes; + } + + private String getKey(String key){ + return this.cacheName + ":" + key; + } + + @Override + public V get(K k) throws CacheException { + String key = this.getKey(k.toString()); + log.debug("get key : {}", key); + return (V)redisTemplate.opsForValue().get(key); + } + + @Override + public V put(K k , V v) throws CacheException { + if (k == null || v == null) { + return null; + } + String key = this.getKey(k.toString()); + log.debug("put key : {}, value: {}", key, v); + redisTemplate.opsForValue().set(key, v, tokenExpireMinutes, TimeUnit.MINUTES); + return v; + } + + @Override + public V remove(K k) throws CacheException { + if (k == null) { + return null; + } + String key = this.getKey(k.toString()); + V value = get(k); + log.debug("remove key : {}", key); + redisTemplate.delete(key); + return value; + } + + @Override + public void clear() throws CacheException { + redisTemplate.delete(this.keys()); + } + + @Override + public int size() { + return keys().size(); + } + + @Override + public Set keys() { + return redisTemplate.keys(getKey("*")); + } + + @Override + public Collection values() { + Set keys = keys(); + Set values = new HashSet<>(keys.size()); + for (K key: keys) { + V value = (V)redisTemplate.opsForValue().get(key); + if(value != null){ + values.add(value); + } + } + return values; + } +} \ No newline at end of file diff --git a/diboot-file-starter/src/main/java/com/diboot/file/excel/converter/DictConverter.java b/diboot-iam-starter/src/main/java/com/diboot/iam/redis/ShiroRedisCacheManager.java similarity index 36% rename from diboot-file-starter/src/main/java/com/diboot/file/excel/converter/DictConverter.java rename to diboot-iam-starter/src/main/java/com/diboot/iam/redis/ShiroRedisCacheManager.java index 65ea5bad57d996c79e0925f5e1133db96f774684..46ce201119fae9b0673074263bb2d45838ed58c7 100644 --- a/diboot-file-starter/src/main/java/com/diboot/file/excel/converter/DictConverter.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/redis/ShiroRedisCacheManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd). + * Copyright (c) 2015-2021, www.dibo.ltd (service@dibo.ltd). *

* Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -13,41 +13,37 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.diboot.file.excel.converter; +package com.diboot.iam.redis; -import com.alibaba.excel.converters.Converter; -import com.alibaba.excel.enums.CellDataTypeEnum; -import com.alibaba.excel.metadata.CellData; -import com.alibaba.excel.metadata.GlobalConfiguration; -import com.alibaba.excel.metadata.property.ExcelContentProperty; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.cache.AbstractCacheManager; +import org.apache.shiro.cache.Cache; +import org.apache.shiro.cache.CacheException; +import org.springframework.data.redis.core.RedisTemplate; /** - * 枚举转化器 (已废弃,不再需要指定) + * ShiroRedisCacheManager * * @author : uu * @version : v1.0 - * @Date 2020-01-07 16:53 + * @Date 2021/6/8 18:36 + * Copyright © diboot.com */ -@Deprecated -public class DictConverter implements Converter { +@Slf4j +public class ShiroRedisCacheManager extends AbstractCacheManager { - @Override - public Class supportJavaTypeKey() { - return DictConverter.class; - } + private RedisTemplate redisTemplate; + private int tokenExpireMinutes; - @Override - public CellDataTypeEnum supportExcelTypeKey() { - return CellDataTypeEnum.STRING; + public ShiroRedisCacheManager(RedisTemplate redisTemplate, int tokenExpireMinutes){ + this.redisTemplate = redisTemplate; + this.tokenExpireMinutes = tokenExpireMinutes; } @Override - public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception { - return cellData.getStringValue(); + protected Cache createCache(String cacheName) throws CacheException { + log.debug("create redis cache: {}", cacheName); + return new ShiroRedisCache(cacheName, redisTemplate, tokenExpireMinutes); } - @Override - public CellData convertToExcelData(String value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception { - return new CellData(value); - } } diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/service/IamAccountService.java b/diboot-iam-starter/src/main/java/com/diboot/iam/service/IamAccountService.java index 228031c1ad3b51efb350e62f9efb4e3b1bc2b71e..9ec544c03243c2a1f99fead69e1e4d7611a7ccb6 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/service/IamAccountService.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/service/IamAccountService.java @@ -59,4 +59,11 @@ public interface IamAccountService extends BaseIamService { */ String getAuthAccount(String userType, Long userId); + /** + * 账号是否已存在 + * @param iamAccount + * @return + */ + boolean isAccountExists(IamAccount iamAccount); + } \ No newline at end of file diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/service/IamResourcePermissionService.java b/diboot-iam-starter/src/main/java/com/diboot/iam/service/IamResourcePermissionService.java index 66efa6ff128355fd9699c86d6c150768a6e86f92..b9f9ec3b493608277b94f3bb66e24cad32f0f8a2 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/service/IamResourcePermissionService.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/service/IamResourcePermissionService.java @@ -19,7 +19,6 @@ package com.diboot.iam.service; import com.diboot.iam.dto.IamResourcePermissionDTO; import com.diboot.iam.entity.IamResourcePermission; import com.diboot.iam.vo.IamResourcePermissionListVO; -import com.diboot.iam.vo.InvalidResourcePermissionVO; import java.util.List; import java.util.Map; diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/service/IamUserService.java b/diboot-iam-starter/src/main/java/com/diboot/iam/service/IamUserService.java index 5d9c3dfce07f6dab7d78bcfeea6f4265248192c1..c0613e2f21723a6b87aad520b1bc22cc9adb8c11 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/service/IamUserService.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/service/IamUserService.java @@ -31,22 +31,6 @@ import java.util.List; */ public interface IamUserService extends BaseIamService { - /*** - * 通过org进行排序 - * @param queryWrapper - * @param pagination - * @return - */ - List getEntityListSortByOrg(QueryWrapper queryWrapper, Pagination pagination); - - /*** - * 通过org进行排序 - * @param queryWrapper - * @param pagination - * @return - */ - List getViewObjectListSortByOrg(QueryWrapper queryWrapper, Pagination pagination, Class voClass); - /** * 构建role-permission角色权限数据格式(合并role等),用于前端适配 * @param iamUser @@ -96,4 +80,12 @@ public interface IamUserService extends BaseIamService { */ List filterDuplicateUserNums(List userNumList); -} \ No newline at end of file + /** + * 用户编号是否存在 + * @param id + * @param userNum + * @return + */ + boolean isUserNumExists(Long id, String userNum); + +} diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/service/impl/IamAccountServiceImpl.java b/diboot-iam-starter/src/main/java/com/diboot/iam/service/impl/IamAccountServiceImpl.java index e697e11bbc0062cc78aa5d94b11f453da2a59955..92399a184cdfcd6d206c6319bfcd34f771c10c64 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/service/impl/IamAccountServiceImpl.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/service/impl/IamAccountServiceImpl.java @@ -53,32 +53,22 @@ public class IamAccountServiceImpl extends BaseIamServiceImpl accountList){ - if(V.notEmpty(accountList)){ - accountList.stream().forEach(account->{ - // 生成加密盐并加密 - iamCustomize.encryptPwd(account); - }); - } - // 保存 - try{ - return super.createEntities(accountList); + if(V.isEmpty(accountList)){ + return true; } - catch (Exception e){ // 重复账号创建会异常 - log.warn("保存账号异常: "+e.getMessage(), e); - throw new BusinessException(Status.FAIL_VALIDATION, "账号中可能包含已存在账号,请检查!"); + for(IamAccount account : accountList){ + // 生成加密盐并加密 + if (V.notEmpty(account.getAuthSecret())){ + iamCustomize.encryptPwd(account); + } } + return super.createEntities(accountList); } @Override @@ -119,4 +109,40 @@ public class IamAccountServiceImpl extends BaseIamServiceImpl queryWrapper = new QueryWrapper().lambda() + .eq(IamAccount::getAuthAccount, iamAccount.getAuthAccount()) + .eq(IamAccount::getAuthType, iamAccount.getAuthType()) + .eq(IamAccount::getUserType, iamAccount.getUserType()); + if(iamAccount.getId() != null){ + queryWrapper.ne(IamAccount::getId, iamAccount.getId()); + } + return exists(queryWrapper); + } + + /** + * 判断账号是否存在 + * @param iamAccount + * @return + */ + @Override + protected void beforeCreateEntity(IamAccount iamAccount){ + if(isAccountExists(iamAccount)){ + String errorMsg = "账号 "+ iamAccount.getAuthAccount() +" 已存在,请重新设置!"; + log.warn("保存账号异常: {}", errorMsg); + throw new BusinessException(Status.FAIL_VALIDATION, errorMsg); + } + } + + /** + * 判断账号是否存在 + * @param iamAccount + * @return + */ + @Override + protected void beforeUpdateEntity(IamAccount iamAccount){ + beforeCreateEntity(iamAccount); + } + } diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/service/impl/IamOrgServiceImpl.java b/diboot-iam-starter/src/main/java/com/diboot/iam/service/impl/IamOrgServiceImpl.java index 5973e6df7c3addce565b2e377c576084ffd3d176..e7ecdb29afa5c9bc72e2cdaf519a6cf417e83d75 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/service/impl/IamOrgServiceImpl.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/service/impl/IamOrgServiceImpl.java @@ -139,13 +139,16 @@ public class IamOrgServiceImpl extends BaseIamServiceImpl @Override public List getParentOrgIds(Long orgId, boolean includeThis) { + if(orgId == null){ + return Collections.emptyList(); + } List scopeIds = new ArrayList<>(); if(includeThis){ scopeIds.add(orgId); } // 查询所有上级 IamOrg org = getEntity(orgId); - if(org.getDepth() != null){ + if(org != null && org.getDepth() != null){ if(org.getDepth() >= 2){ scopeIds.add(org.getParentId()); if(org.getDepth() > 2) { diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/service/impl/IamPositionServiceImpl.java b/diboot-iam-starter/src/main/java/com/diboot/iam/service/impl/IamPositionServiceImpl.java index e55a472fcc776391a7ec345c0d71f3baa9dda620..21330cfdde05d1cdc929ffe758300801ed688671 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/service/impl/IamPositionServiceImpl.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/service/impl/IamPositionServiceImpl.java @@ -36,6 +36,7 @@ import java.util.List; /** * 岗位相关Service实现 + * * @author mazc@dibo.ltd * @version 2.2 * @date 2019-12-03 @@ -93,7 +94,8 @@ public class IamPositionServiceImpl extends BaseIamServiceImpl getEntityListSortByOrg(QueryWrapper queryWrapper, Pagination pagination) { - // 如果是动态join,则调用JoinsBinder - if(queryWrapper instanceof DynamicJoinQueryWrapper){ - return Binder.joinQueryList((DynamicJoinQueryWrapper)queryWrapper, entityClass, pagination); - } - // 否则,调用MP默认实现 - if(pagination != null){ - IPage page = convertToIPage(queryWrapper, pagination); - page = super.getBaseMapper().selectPageSortByOrg(page, queryWrapper); - // 如果重新执行了count进行查询,则更新pagination中的总数 - if(page.isSearchCount()){ - pagination.setTotalCount(page.getTotal()); - } - return page.getRecords(); - } - else{ - List list = super.list(queryWrapper); - if(list == null){ - list = Collections.emptyList(); - } - else if(list.size() > BaseConfig.getBatchSize()){ - log.warn("单次查询记录数量过大,返回结果数={}", list.size()); - } - return list; - } - } - - @Override - public List getViewObjectListSortByOrg(QueryWrapper queryWrapper, Pagination pagination, Class voClass) { - List entityList = this.getEntityListSortByOrg(queryWrapper, pagination); - List voList = RelationsBinder.convertAndBind(entityList, voClass); - return voList; - } - @Override public IamRoleVO buildRoleVo4FrontEnd(IamUser iamUser) { List roleVOList = getAllRoleVOList(iamUser); @@ -229,6 +195,20 @@ public class IamUserServiceImpl extends BaseIamServiceImpl wrapper = Wrappers.lambdaQuery() + .select(IamUser::getUserNum) + .eq(IamUser::getUserNum, userNum); + if (V.notEmpty(id)){ + wrapper.ne(IamUser::getId, id); + } + return exists(wrapper); + } + /*** * 检查重复用户编号 * @param userNumList @@ -277,6 +257,32 @@ public class IamUserServiceImpl extends BaseIamServiceImpl anonUrls = iamProperties.getAnonUrls(); if (V.notEmpty(anonUrls)) { - for (String url : anonUrls.split(Cons.SEPARATOR_COMMA)) { + for (String url : anonUrls) { filterChainMap.put(url, "anon"); if (url.equals("/**")) { allAnon = true; @@ -166,8 +171,7 @@ public class IamAutoConfig { } } filterChainMap.put("/login", "authc"); - filterChainMap.put("/logout", "logout"); - if (allAnon && iamProperties.isEnablePermissionCheck() == false) { + if (allAnon && !iamProperties.isEnablePermissionCheck()) { filterChainMap.put("/**", "anon"); } else { filterChainMap.put("/**", "jwt"); diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/starter/IamPluginInitializer.java b/diboot-iam-starter/src/main/java/com/diboot/iam/starter/IamPluginInitializer.java index aeca651d1b75224f87560461d72392ce6e55912a..a19064effe5e89779e74dfa1b74c2b86b2a218d8 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/starter/IamPluginInitializer.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/starter/IamPluginInitializer.java @@ -15,6 +15,7 @@ */ package com.diboot.iam.starter; +import com.diboot.core.entity.Dictionary; import com.diboot.core.exception.BusinessException; import com.diboot.core.service.DictionaryService; import com.diboot.core.util.ContextHelper; @@ -69,65 +70,79 @@ public class IamPluginInitializer implements ApplicationRunner { /** * 插入初始化数据 */ - private void insertInitData(){ + private synchronized void insertInitData(){ // 插入iam组件所需的数据字典 - String[] DICT_INIT_DATA = { - "{\"type\":\"AUTH_TYPE\", \"itemName\":\"登录认证方式\", \"description\":\"IAM用户登录认证方式\", \"children\":[{\"itemName\":\"用户名密码\", \"itemValue\":\"PWD\", \"sortId\":1},{\"itemName\":\"单点登录\", \"itemValue\":\"SSO\", \"sortId\":2},{\"itemName\":\"公众号\", \"itemValue\":\"WX_MP\", \"sortId\":3},{\"itemName\":\"企业微信\", \"itemValue\":\"WX_CP\", \"sortId\":4},{\"itemName\":\"其他\", \"itemValue\":\"OTHER\", \"sortId\":5}]}", - "{\"type\":\"ACCOUNT_STATUS\", \"itemName\":\"账号状态\", \"description\":\"IAM登录账号状态\", \"children\":[{\"itemName\":\"有效\", \"itemValue\":\"A\", \"sortId\":1},{\"itemName\":\"无效\", \"itemValue\":\"I\", \"sortId\":2},{\"itemName\":\"锁定\", \"itemValue\":\"L\", \"sortId\":3}]}", - "{\"type\":\"USER_STATUS\", \"itemName\":\"用户状态\", \"description\":\"IAM用户状态\", \"editable\":true, \"children\":[{\"itemName\":\"在职\", \"itemValue\":\"A\", \"sortId\":1},{\"itemName\":\"离职\", \"itemValue\":\"I\", \"sortId\":2}]}", - "{\"itemName\":\"用户性别\",\"type\":\"GENDER\",\"description\":\"用户性别数据字典\",\"children\":[{\"itemValue\":\"F\",\"sortId\":99,\"itemName\":\"女\"},{\"itemValue\":\"M\",\"sortId\":99,\"itemName\":\"男\"}]}", - "{\"type\":\"PERMISSION_TYPE\", \"itemName\":\"权限类型\", \"description\":\"IAM权限类型\", \"children\":[{\"itemName\":\"菜单\", \"itemValue\":\"MENU\", \"sortId\":1},{\"itemName\":\"操作\", \"itemValue\":\"OPERATION\", \"sortId\":2}]}", - "{\"itemName\":\"前端按钮/权限编码\",\"type\":\"RESOURCE_PERMISSION_CODE\",\"description\":\"前端按钮/权限编码 常用选项\",\"children\":[{\"sortId\":1,\"itemName\":\"详情\",\"itemValue\":\"detail\"},{\"sortId\":2,\"itemName\":\"新建\",\"itemValue\":\"create\"},{\"sortId\":3,\"itemName\":\"更新\",\"itemValue\":\"update\"},{\"sortId\":4,\"itemName\":\"删除\",\"itemValue\":\"delete\"},{\"sortId\":5,\"itemName\":\"导出\",\"itemValue\":\"export\"},{\"sortId\":6,\"itemName\":\"导入\",\"itemValue\":\"import\"}]}", - "{\"type\":\"ORG_TYPE\", \"itemName\":\"组织类型\", \"description\":\"组织节点类型\", \"editable\":false, \"children\":[{\"itemName\":\"部门\", \"itemValue\":\"DEPT\", \"sortId\":1},{\"itemName\":\"公司\", \"itemValue\":\"COMP\", \"sortId\":2}]}", - "{\"type\":\"DATA_PERMISSION_TYPE\", \"itemName\":\"IAM数据权限类型\", \"description\":\"IAM数据权限类型定义\", \"editable\":true, \"children\":[{\"itemName\":\"本人\", \"itemValue\":\"SELF\", \"sortId\":1},{\"itemName\":\"本人及下属\", \"itemValue\":\"SELF_AND_SUB\", \"sortId\":2},{\"itemName\":\"本部门\", \"itemValue\":\"DEPT\", \"sortId\":3},{\"itemName\":\"本部门及下属部门\", \"itemValue\":\"DEPT_AND_SUB\", \"sortId\":4},{\"itemName\":\"全部\", \"itemValue\":\"ALL\", \"sortId\":5}]}", - "{\"type\":\"POSITION_GRADE\", \"itemName\":\"职级定义\", \"description\":\"职务级别定义\", \"editable\":true, \"children\":[{\"itemName\":\"初级\", \"itemValue\":\"E1\", \"sortId\":1},{\"itemName\":\"中级\", \"itemValue\":\"E2\", \"sortId\":2},{\"itemName\":\"高级\", \"itemValue\":\"E3\", \"sortId\":3},{\"itemName\":\"专家\", \"itemValue\":\"E4\", \"sortId\":4}]}" - }; - // 插入数据字典 - for(String dictJson : DICT_INIT_DATA){ - DictionaryVO dictVo = JSON.toJavaObject(dictJson, DictionaryVO.class); - ContextHelper.getBean(DictionaryService.class).createDictAndChildren(dictVo); + DictionaryService dictionaryService = ContextHelper.getBean(DictionaryService.class); + if(dictionaryService != null && !dictionaryService.exists(Dictionary::getType, "AUTH_TYPE")){ + String[] DICT_INIT_DATA = { + "{\"type\":\"AUTH_TYPE\", \"itemName\":\"登录认证方式\", \"description\":\"IAM用户登录认证方式\", \"children\":[{\"itemName\":\"用户名密码\", \"itemValue\":\"PWD\", \"sortId\":1},{\"itemName\":\"单点登录\", \"itemValue\":\"SSO\", \"sortId\":2},{\"itemName\":\"公众号\", \"itemValue\":\"WX_MP\", \"sortId\":3},{\"itemName\":\"企业微信\", \"itemValue\":\"WX_CP\", \"sortId\":4},{\"itemName\":\"其他\", \"itemValue\":\"OTHER\", \"sortId\":5}]}", + "{\"type\":\"ACCOUNT_STATUS\", \"itemName\":\"账号状态\", \"description\":\"IAM登录账号状态\", \"children\":[{\"itemName\":\"有效\", \"itemValue\":\"A\", \"sortId\":1},{\"itemName\":\"无效\", \"itemValue\":\"I\", \"sortId\":2},{\"itemName\":\"锁定\", \"itemValue\":\"L\", \"sortId\":3}]}", + "{\"type\":\"USER_STATUS\", \"itemName\":\"用户状态\", \"description\":\"IAM用户状态\", \"editable\":true, \"children\":[{\"itemName\":\"在职\", \"itemValue\":\"A\", \"sortId\":1},{\"itemName\":\"离职\", \"itemValue\":\"I\", \"sortId\":2}]}", + "{\"itemName\":\"用户性别\",\"type\":\"GENDER\",\"description\":\"用户性别数据字典\",\"children\":[{\"itemValue\":\"F\",\"sortId\":99,\"itemName\":\"女\"},{\"itemValue\":\"M\",\"sortId\":99,\"itemName\":\"男\"}]}", + "{\"type\":\"PERMISSION_TYPE\", \"itemName\":\"权限类型\", \"description\":\"IAM权限类型\", \"children\":[{\"itemName\":\"菜单\", \"itemValue\":\"MENU\", \"sortId\":1},{\"itemName\":\"操作\", \"itemValue\":\"OPERATION\", \"sortId\":2}]}", + "{\"itemName\":\"前端按钮/权限编码\",\"type\":\"RESOURCE_PERMISSION_CODE\",\"description\":\"前端按钮/权限编码 常用选项\",\"children\":[{\"sortId\":1,\"itemName\":\"详情\",\"itemValue\":\"detail\"},{\"sortId\":2,\"itemName\":\"新建\",\"itemValue\":\"create\"},{\"sortId\":3,\"itemName\":\"更新\",\"itemValue\":\"update\"},{\"sortId\":4,\"itemName\":\"删除\",\"itemValue\":\"delete\"},{\"sortId\":5,\"itemName\":\"导出\",\"itemValue\":\"export\"},{\"sortId\":6,\"itemName\":\"导入\",\"itemValue\":\"import\"}]}", + "{\"type\":\"ORG_TYPE\", \"itemName\":\"组织类型\", \"description\":\"组织节点类型\", \"editable\":false, \"children\":[{\"itemName\":\"部门\", \"itemValue\":\"DEPT\", \"sortId\":1},{\"itemName\":\"公司\", \"itemValue\":\"COMP\", \"sortId\":2}]}", + "{\"type\":\"DATA_PERMISSION_TYPE\", \"itemName\":\"IAM数据权限类型\", \"description\":\"IAM数据权限类型定义\", \"editable\":true, \"children\":[{\"itemName\":\"本人\", \"itemValue\":\"SELF\", \"sortId\":1},{\"itemName\":\"本人及下属\", \"itemValue\":\"SELF_AND_SUB\", \"sortId\":2},{\"itemName\":\"本部门\", \"itemValue\":\"DEPT\", \"sortId\":3},{\"itemName\":\"本部门及下属部门\", \"itemValue\":\"DEPT_AND_SUB\", \"sortId\":4},{\"itemName\":\"全部\", \"itemValue\":\"ALL\", \"sortId\":5}]}", + "{\"type\":\"POSITION_GRADE\", \"itemName\":\"职级定义\", \"description\":\"职务级别定义\", \"editable\":true, \"children\":[{\"itemName\":\"初级\", \"itemValue\":\"E1\", \"sortId\":1},{\"itemName\":\"中级\", \"itemValue\":\"E2\", \"sortId\":2},{\"itemName\":\"高级\", \"itemValue\":\"E3\", \"sortId\":3},{\"itemName\":\"专家\", \"itemValue\":\"E4\", \"sortId\":4}]}" + }; + // 插入数据字典 + for(String dictJson : DICT_INIT_DATA){ + DictionaryVO dictVo = JSON.toJavaObject(dictJson, DictionaryVO.class); + dictionaryService.createDictAndChildren(dictVo); + } + DICT_INIT_DATA = null; } - DICT_INIT_DATA = null; // 插入iam组件所需的初始权限数据 - String[] RESOURCE_PERMISSION_DATA = { - "{\"displayType\":\"MENU\",\"displayName\":\"系统管理\",\"resourceCode\":\"system\",\"children\":[{\"displayType\":\"MENU\",\"displayName\":\"数据字典管理\",\"resourceCode\":\"Dictionary\",\"apiSet\":\"GET:/dictionary/list\",\"sortId\":\"10030\",\"children\":[{\"displayType\":\"PERMISSION\",\"displayName\":\"详情\",\"resourceCode\":\"detail\",\"apiSet\":\"GET:/dictionary/{id}\",\"sortId\":\"6\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"新建\",\"resourceCode\":\"create\",\"apiSet\":\"POST:/dictionary/\",\"sortId\":\"5\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"更新\",\"resourceCode\":\"update\",\"apiSet\":\"PUT:/dictionary/{id}\",\"sortId\":\"4\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"删除\",\"resourceCode\":\"delete\",\"apiSet\":\"DELETE:/dictionary/{id}\",\"sortId\":\"3\"}]},{\"displayType\":\"MENU\",\"displayName\":\"系统用户管理\",\"resourceCode\":\"IamUser\",\"apiSet\":\"GET:/iam/user/list\",\"sortId\":\"10029\",\"children\":[{\"displayType\":\"PERMISSION\",\"displayName\":\"部门查看\",\"resourceCode\":\"orgTree\",\"apiSet\":\"GET:/iam/org/tree\",\"sortId\":\"12\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"详情\",\"resourceCode\":\"detail\",\"apiSet\":\"GET:/iam/user/{id}\",\"sortId\":\"11\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"新建\",\"resourceCode\":\"create\",\"apiSet\":\"POST:/iam/user/\",\"sortId\":\"10\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"更新\",\"resourceCode\":\"update\",\"apiSet\":\"PUT:/iam/user/{id}\",\"sortId\":\"9\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"删除\",\"resourceCode\":\"delete\",\"apiSet\":\"DELETE:/iam/user/{id}\",\"sortId\":\"8\"}]},{\"displayType\":\"MENU\",\"displayName\":\"角色资源管理\",\"resourceCode\":\"IamRole\",\"apiSet\":\"GET:/iam/role/list\",\"sortId\":\"10023\",\"children\":[{\"displayType\":\"PERMISSION\",\"displayName\":\"详情\",\"resourceCode\":\"detail\",\"apiSet\":\"GET:/iam/role/{id}\",\"sortId\":\"16\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"新建\",\"resourceCode\":\"create\",\"apiSet\":\"POST:/iam/role/\",\"sortId\":\"15\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"更新\",\"resourceCode\":\"update\",\"apiSet\":\"PUT:/iam/role/{id}\",\"sortId\":\"14\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"删除\",\"resourceCode\":\"delete\",\"apiSet\":\"DELETE:/iam/role/{id}\",\"sortId\":\"13\"}]},{\"displayType\":\"MENU\",\"displayName\":\"资源权限管理\",\"resourceCode\":\"IamResourcePermission\",\"apiSet\":\"GET:/iam/resourcePermission/list\",\"sortId\":\"10017\",\"children\":[{\"displayType\":\"PERMISSION\",\"displayName\":\"详情\",\"resourceCode\":\"detail\",\"apiSet\":\"GET:/iam/resourcePermission/{id}\",\"sortId\":\"23\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"新建\",\"resourceCode\":\"create\",\"apiSet\":\"POST:/iam/resourcePermission/\",\"sortId\":\"21\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"更新\",\"resourceCode\":\"update\",\"apiSet\":\"PUT:/iam/resourcePermission/{id}\",\"sortId\":\"20\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"删除\",\"resourceCode\":\"delete\",\"apiSet\":\"DELETE:/iam/resourcePermission/{id}\",\"sortId\":\"19\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"排序\",\"resourceCode\":\"sort\",\"apiSet\":\"POST:/iam/resourcePermission/sortList\",\"sortId\":\"18\"}]},{\"displayType\":\"MENU\",\"displayName\":\"定时任务管理\",\"resourceCode\":\"ScheduleJob\",\"apiSet\":\"GET:/scheduleJob/list\",\"sortId\":\"10012\",\"children\":[{\"displayType\":\"PERMISSION\",\"displayName\":\"删除\",\"resourceCode\":\"delete\",\"apiSet\":\"DELETE:/scheduleJob/{id}\",\"sortId\":\"5\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"更新\",\"resourceCode\":\"update\",\"apiSet\":\"PUT:/scheduleJob/{id}/{action}\",\"sortId\":\"4\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"新建\",\"resourceCode\":\"create\",\"apiSet\":\"POST:/scheduleJob/\",\"sortId\":\"3\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"详情\",\"resourceCode\":\"detail\",\"apiSet\":\"GET:/scheduleJob/{id},GET:/scheduleJob/log/list,GET:/scheduleJob/log/{id}\",\"sortId\":\"2\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"运行一次\",\"resourceCode\":\"executeOnce\",\"apiSet\":\"PUT:/scheduleJob/executeOnce/{id}\",\"sortId\":\"1\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"日志记录\",\"resourceCode\":\"logList\",\"apiSet\":\"GET:/scheduleJob/log/list,GET:/scheduleJob/log/{id}\",\"sortId\":\"0\"}]},{\"displayType\":\"MENU\",\"displayName\":\"操作日志查看\",\"resourceCode\":\"IamOperationLog\",\"apiSet\":\"GET:/iam/operationLog/list\",\"sortId\":\"10006\",\"children\":[]},{\"displayType\":\"MENU\",\"displayName\":\"登录日志查看\",\"resourceCode\":\"IamLoginTrace\",\"apiSet\":\"GET:/iam/loginTrace/list\",\"sortId\":\"10001\",\"children\":[]}]}", - "{\"displayType\":\"MENU\",\"displayName\":\"组织机构\",\"resourceCode\":\"orgStructure\",\"children\":[{\"displayType\":\"MENU\",\"displayName\":\"组织机构管理\",\"resourceCode\":\"IamOrg\",\"apiSet\":\"POST:/iam/org/sortList,GET:/iam/org/tree,GET:/iam/org/tree/{parentNodeId},GET:/iam/org/list\",\"sortId\":\"10044\",\"children\":[{\"displayType\":\"PERMISSION\",\"displayName\":\"排序\",\"resourceCode\":\"sort\",\"apiSet\":\"POST:/iam/org/sortList\",\"sortId\":\"106\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"删除\",\"resourceCode\":\"delete\",\"apiSet\":\"DELETE:/iam/org/{id}\",\"sortId\":\"105\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"更新\",\"resourceCode\":\"update\",\"apiSet\":\"PUT:/iam/org/{id}\",\"sortId\":\"104\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"新建\",\"resourceCode\":\"create\",\"apiSet\":\"POST:/iam/org/\",\"sortId\":\"103\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"详情\",\"resourceCode\":\"detail\",\"apiSet\":\"GET:/iam/org/{id}\",\"sortId\":\"102\"}]},{\"displayType\":\"MENU\",\"displayName\":\"岗位管理\",\"resourceCode\":\"IamPosition\",\"apiSet\":\"GET:/iam/position/list\",\"sortId\":\"10038\",\"children\":[{\"displayType\":\"PERMISSION\",\"displayName\":\"删除\",\"resourceCode\":\"delete\",\"apiSet\":\"DELETE:/iam/position/{id}\",\"sortId\":\"112\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"详情\",\"resourceCode\":\"detail\",\"apiSet\":\"GET:/iam/position/{id}\",\"sortId\":\"111\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"更新\",\"resourceCode\":\"update\",\"apiSet\":\"PUT:/iam/position/{id}\",\"sortId\":\"110\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"新建\",\"resourceCode\":\"create\",\"apiSet\":\"POST:/iam/position/\",\"sortId\":\"108\"}]},{\"displayType\":\"MENU\",\"displayName\":\"组织人员管理\",\"resourceCode\":\"IamOrgUser\",\"apiSet\":\"GET:/iam/org/tree,GET:/iam/user/list\",\"sortId\":\"10032\",\"children\":[{\"displayType\":\"PERMISSION\",\"displayName\":\"新建\",\"resourceCode\":\"create\",\"apiSet\":\"POST:/iam/user/\",\"sortId\":\"40\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"更新\",\"resourceCode\":\"update\",\"apiSet\":\"PUT:/iam/org/{id}\",\"sortId\":\"39\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"删除\",\"resourceCode\":\"delete\",\"apiSet\":\"DELETE:/iam/user/{id}\",\"sortId\":\"38\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"详情\",\"resourceCode\":\"detail\",\"apiSet\":\"GET:/iam/user/{id}\",\"sortId\":\"37\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"导入\",\"resourceCode\":\"import\",\"apiSet\":\"POST:/iam/user/excel/previewSave,POST:/iam/user/excel/upload\",\"sortId\":\"36\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"导出\",\"resourceCode\":\"export\",\"apiSet\":\"GET:/iam/user/excel/export\",\"sortId\":\"35\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"人员岗位设置\",\"resourceCode\":\"position\",\"apiSet\":\"POST:/iam/position/batchUpdateUserPositionRelations,GET:/iam/position/listUserPositions/{userType}/{userId},GET:/iam/positionkvList\",\"sortId\":\"34\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"添加岗位\",\"resourceCode\":\"addPosition\",\"apiSet\":\"POST:/iam/position/\",\"sortId\":\"33\"}]}]}" - }; - // 插入多层级资源权限初始数据 - try { - for (String resourcePermissionJson : RESOURCE_PERMISSION_DATA) { - IamResourcePermissionListVO permissionListVO = JSON.toJavaObject(resourcePermissionJson, IamResourcePermissionListVO.class); - ContextHelper.getBean(IamResourcePermissionService.class).deepCreatePermissionAndChildren(permissionListVO); + IamResourcePermissionService resourcePermissionService = ContextHelper.getBean(IamResourcePermissionService.class); + if(resourcePermissionService != null && !resourcePermissionService.exists(IamResourcePermission::getResourceCode, "system")){ + String[] RESOURCE_PERMISSION_DATA = { + "{\"displayType\":\"MENU\",\"displayName\":\"系统管理\",\"resourceCode\":\"system\",\"children\":[{\"displayType\":\"MENU\",\"displayName\":\"数据字典管理\",\"resourceCode\":\"Dictionary\",\"apiSet\":\"GET:/dictionary/list\",\"sortId\":\"10030\",\"children\":[{\"displayType\":\"PERMISSION\",\"displayName\":\"详情\",\"resourceCode\":\"detail\",\"apiSet\":\"GET:/dictionary/{id}\",\"sortId\":\"6\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"新建\",\"resourceCode\":\"create\",\"apiSet\":\"POST:/dictionary/\",\"sortId\":\"5\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"更新\",\"resourceCode\":\"update\",\"apiSet\":\"PUT:/dictionary/{id},GET:/dictionary/{id}\",\"sortId\":\"4\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"删除\",\"resourceCode\":\"delete\",\"apiSet\":\"DELETE:/dictionary/{id}\",\"sortId\":\"3\"}]},{\"displayType\":\"MENU\",\"displayName\":\"系统用户管理\",\"resourceCode\":\"IamUser\",\"apiSet\":\"GET:/iam/user/list\",\"sortId\":\"10029\",\"children\":[{\"displayType\":\"PERMISSION\",\"displayName\":\"部门查看\",\"resourceCode\":\"orgTree\",\"apiSet\":\"GET:/iam/org/tree\",\"sortId\":\"12\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"详情\",\"resourceCode\":\"detail\",\"apiSet\":\"GET:/iam/user/{id}\",\"sortId\":\"11\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"新建\",\"resourceCode\":\"create\",\"apiSet\":\"POST:/iam/user/\",\"sortId\":\"10\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"更新\",\"resourceCode\":\"update\",\"apiSet\":\"PUT:/iam/user/{id},GET:/iam/user/{id}\",\"sortId\":\"9\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"删除\",\"resourceCode\":\"delete\",\"apiSet\":\"DELETE:/iam/user/{id}\",\"sortId\":\"8\"}]},{\"displayType\":\"MENU\",\"displayName\":\"角色资源管理\",\"resourceCode\":\"IamRole\",\"apiSet\":\"GET:/iam/role/list\",\"sortId\":\"10023\",\"children\":[{\"displayType\":\"PERMISSION\",\"displayName\":\"详情\",\"resourceCode\":\"detail\",\"apiSet\":\"GET:/iam/role/{id}\",\"sortId\":\"16\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"新建\",\"resourceCode\":\"create\",\"apiSet\":\"POST:/iam/role/\",\"sortId\":\"15\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"更新\",\"resourceCode\":\"update\",\"apiSet\":\"PUT:/iam/role/{id},GET:/iam/role/{id}\",\"sortId\":\"14\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"删除\",\"resourceCode\":\"delete\",\"apiSet\":\"DELETE:/iam/role/{id}\",\"sortId\":\"13\"}]},{\"displayType\":\"MENU\",\"displayName\":\"资源权限管理\",\"resourceCode\":\"IamResourcePermission\",\"apiSet\":\"GET:/iam/resourcePermission/list\",\"sortId\":\"10017\",\"children\":[{\"displayType\":\"PERMISSION\",\"displayName\":\"详情\",\"resourceCode\":\"detail\",\"apiSet\":\"GET:/iam/resourcePermission/{id}\",\"sortId\":\"23\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"新建\",\"resourceCode\":\"create\",\"apiSet\":\"POST:/iam/resourcePermission/\",\"sortId\":\"21\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"更新\",\"resourceCode\":\"update\",\"apiSet\":\"PUT:/iam/resourcePermission/{id},GET:/iam/resourcePermission/{id}\",\"sortId\":\"20\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"删除\",\"resourceCode\":\"delete\",\"apiSet\":\"DELETE:/iam/resourcePermission/{id}\",\"sortId\":\"19\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"排序\",\"resourceCode\":\"sort\",\"apiSet\":\"POST:/iam/resourcePermission/sortList\",\"sortId\":\"18\"}]},{\"displayType\":\"MENU\",\"displayName\":\"定时任务管理\",\"resourceCode\":\"ScheduleJob\",\"apiSet\":\"GET:/scheduleJob/list\",\"sortId\":\"10012\",\"children\":[{\"displayType\":\"PERMISSION\",\"displayName\":\"删除\",\"resourceCode\":\"delete\",\"apiSet\":\"DELETE:/scheduleJob/{id}\",\"sortId\":\"7\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"更新\",\"resourceCode\":\"update\",\"apiSet\":\"PUT:/scheduleJob/{id}/{action},GET:/scheduleJob/{id}\",\"sortId\":\"6\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"新建\",\"resourceCode\":\"create\",\"apiSet\":\"POST:/scheduleJob/\",\"sortId\":\"5\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"详情\",\"resourceCode\":\"detail\",\"apiSet\":\"GET:/scheduleJob/{id},GET:/scheduleJob/log/list,GET:/scheduleJob/log/{id}\",\"sortId\":\"4\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"运行一次\",\"resourceCode\":\"executeOnce\",\"apiSet\":\"PUT:/scheduleJob/executeOnce/{id}\",\"sortId\":\"3\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"日志记录\",\"resourceCode\":\"logList\",\"apiSet\":\"GET:/scheduleJob/log/list,GET:/scheduleJob/log/{id}\",\"sortId\":\"2\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"日志删除\",\"resourceCode\":\"logDelete\",\"apiSet\":\"DELETE:/scheduleJob/log/{id}\",\"sortId\":\"1\"}]},{\"displayType\":\"MENU\",\"displayName\":\"操作日志查看\",\"resourceCode\":\"IamOperationLog\",\"apiSet\":\"GET:/iam/operationLog/list\",\"sortId\":\"10006\",\"children\":[]},{\"displayType\":\"MENU\",\"displayName\":\"登录日志查看\",\"resourceCode\":\"IamLoginTrace\",\"apiSet\":\"GET:/iam/loginTrace/list\",\"sortId\":\"10001\",\"children\":[]}]}", + "{\"displayType\":\"MENU\",\"displayName\":\"组织机构\",\"resourceCode\":\"orgStructure\",\"children\":[{\"displayType\":\"MENU\",\"displayName\":\"组织机构管理\",\"resourceCode\":\"IamOrg\",\"apiSet\":\"POST:/iam/org/sortList,GET:/iam/org/tree,GET:/iam/org/tree/{parentNodeId},GET:/iam/org/list\",\"sortId\":\"10044\",\"children\":[{\"displayType\":\"PERMISSION\",\"displayName\":\"排序\",\"resourceCode\":\"sort\",\"apiSet\":\"POST:/iam/org/sortList\",\"sortId\":\"106\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"删除\",\"resourceCode\":\"delete\",\"apiSet\":\"DELETE:/iam/org/{id}\",\"sortId\":\"105\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"更新\",\"resourceCode\":\"update\",\"apiSet\":\"PUT:/iam/org/{id},GET:/iam/org/{id}\",\"sortId\":\"104\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"新建\",\"resourceCode\":\"create\",\"apiSet\":\"POST:/iam/org/\",\"sortId\":\"103\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"详情\",\"resourceCode\":\"detail\",\"apiSet\":\"GET:/iam/org/{id}\",\"sortId\":\"102\"}]},{\"displayType\":\"MENU\",\"displayName\":\"岗位管理\",\"resourceCode\":\"IamPosition\",\"apiSet\":\"GET:/iam/position/list\",\"sortId\":\"10038\",\"children\":[{\"displayType\":\"PERMISSION\",\"displayName\":\"删除\",\"resourceCode\":\"delete\",\"apiSet\":\"DELETE:/iam/position/{id}\",\"sortId\":\"112\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"详情\",\"resourceCode\":\"detail\",\"apiSet\":\"GET:/iam/position/{id}\",\"sortId\":\"111\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"更新\",\"resourceCode\":\"update\",\"apiSet\":\"PUT:/iam/position/{id},GET:/iam/position/{id}\",\"sortId\":\"110\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"新建\",\"resourceCode\":\"create\",\"apiSet\":\"POST:/iam/position/\",\"sortId\":\"108\"}]},{\"displayType\":\"MENU\",\"displayName\":\"组织人员管理\",\"resourceCode\":\"IamOrgUser\",\"apiSet\":\"GET:/iam/org/tree,GET:/iam/user/list\",\"sortId\":\"10032\",\"children\":[{\"displayType\":\"PERMISSION\",\"displayName\":\"新建\",\"resourceCode\":\"create\",\"apiSet\":\"POST:/iam/user/\",\"sortId\":\"40\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"更新\",\"resourceCode\":\"update\",\"apiSet\":\"PUT:/iam/user/{id},GET:/iam/user/{id}\",\"sortId\":\"39\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"删除\",\"resourceCode\":\"delete\",\"apiSet\":\"DELETE:/iam/user/{id}\",\"sortId\":\"38\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"详情\",\"resourceCode\":\"detail\",\"apiSet\":\"GET:/iam/user/{id}\",\"sortId\":\"37\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"导入\",\"resourceCode\":\"import\",\"apiSet\":\"POST:/iam/user/excel/previewSave,POST:/iam/user/excel/upload,POST:/iam/user/excel/preview,GET:/iam/user/excel/downloadExample\",\"sortId\":\"36\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"导出\",\"resourceCode\":\"export\",\"apiSet\":\"GET:/iam/user/excel/export\",\"sortId\":\"35\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"人员岗位设置\",\"resourceCode\":\"position\",\"apiSet\":\"POST:/iam/position/batchUpdateUserPositionRelations,GET:/iam/position/listUserPositions/{userType}/{userId},GET:/iam/positionkvList\",\"sortId\":\"34\"},{\"displayType\":\"PERMISSION\",\"displayName\":\"添加岗位\",\"resourceCode\":\"addPosition\",\"apiSet\":\"POST:/iam/position/\",\"sortId\":\"33\"}]}]}" + }; + // 插入多层级资源权限初始数据 + try { + for (String resourcePermissionJson : RESOURCE_PERMISSION_DATA) { + IamResourcePermissionListVO permissionListVO = JSON.toJavaObject(resourcePermissionJson, IamResourcePermissionListVO.class); + resourcePermissionService.deepCreatePermissionAndChildren(permissionListVO); + } + RESOURCE_PERMISSION_DATA = null; + } catch (BusinessException e){ + log.error("初始化资源权限数据出错,请手动配置前端资源初始的权限数据", e.getMessage()); } - RESOURCE_PERMISSION_DATA = null; - } catch (BusinessException e){ - log.error("初始化资源权限数据出错,请手动配置前端资源初始的权限数据", e.getMessage()); } - IamOrg iamOrg = new IamOrg(); - iamOrg.setCode("ROOT").setDepth(1).setTopOrgId(1L).setName("我的公司").setShortName("我的公司").setType(Cons.DICTCODE_ORG_TYPE.COMP.name()).setOrgComment("初始根节点,请按需修改") - .setId(1L); - ContextHelper.getBean(IamOrgService.class).createEntity(iamOrg); + // 插入公司根节点 + IamOrgService iamOrgService = ContextHelper.getBean(IamOrgService.class); + if(iamOrgService != null && iamOrgService.getEntityListCount(null) == 0){ + IamOrg iamOrg = new IamOrg(); + iamOrg.setCode("ROOT").setDepth(1).setTopOrgId(1L).setName("我的公司").setShortName("我的公司") + .setType(Cons.DICTCODE_ORG_TYPE.COMP.name()).setOrgComment("初始根节点,请按需修改").setId(1L); + iamOrgService.createEntity(iamOrg); + } // 插入超级管理员用户及角色 - IamRole iamRole = new IamRole(); - iamRole.setName("超级管理员").setCode(Cons.ROLE_SUPER_ADMIN); - ContextHelper.getBean(IamRoleService.class).createEntity(iamRole); + IamRoleService iamRoleService = ContextHelper.getBean(IamRoleService.class); + if(iamRoleService != null && iamRoleService.getEntityListCount(null) == 0){ + IamRole iamRole = new IamRole(); + iamRole.setName("超级管理员").setCode(Cons.ROLE_SUPER_ADMIN); + iamRoleService.createEntity(iamRole); - IamUser iamUser = new IamUser(); - iamUser.setOrgId(0L).setRealname("DIBOOT").setUserNum("0000").setGender("M").setMobilePhone("10000000000"); - ContextHelper.getBean(IamUserService.class).createEntity(iamUser); + IamUser iamUser = new IamUser(); + iamUser.setOrgId(0L).setRealname("DIBOOT").setUserNum("0000").setGender("M").setMobilePhone("10000000000"); + ContextHelper.getBean(IamUserService.class).createEntity(iamUser); - // 插入对象 - IamUserRole iamUserRole = new IamUserRole(IamUser.class.getSimpleName(), iamUser.getId(), iamRole.getId()); - ContextHelper.getBean(IamUserRoleService.class).getMapper().insert(iamUserRole); + // 插入对象 + IamUserRole iamUserRole = new IamUserRole(IamUser.class.getSimpleName(), iamUser.getId(), iamRole.getId()); + ContextHelper.getBean(IamUserRoleService.class).getMapper().insert(iamUserRole); + + // 创建账号 + IamAccount iamAccount = new IamAccount(); + iamAccount.setUserType(IamUser.class.getSimpleName()).setUserId(iamUser.getId()) + .setAuthType(Cons.DICTCODE_AUTH_TYPE.PWD.name()) + .setAuthAccount("admin").setAuthSecret("123456"); + ContextHelper.getBean(IamAccountService.class).createEntity(iamAccount); + } - // 创建账号 - IamAccount iamAccount = new IamAccount(); - iamAccount.setUserType(IamUser.class.getSimpleName()).setUserId(iamUser.getId()) - .setAuthType(Cons.DICTCODE_AUTH_TYPE.PWD.name()) - .setAuthAccount("admin").setAuthSecret("123456"); - ContextHelper.getBean(IamAccountService.class).createEntity(iamAccount); } } \ No newline at end of file diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/starter/IamProperties.java b/diboot-iam-starter/src/main/java/com/diboot/iam/starter/IamProperties.java index a4198b0ab037905a2d146a4ab273370f38e4c4ce..e8cfe06517ff562bb5c65c05d1c2af3b505f1a6f 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/starter/IamProperties.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/starter/IamProperties.java @@ -19,6 +19,8 @@ import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; +import java.util.Set; + /** * 认证相关的配置参数 * @author mazc@dibo.ltd @@ -51,7 +53,7 @@ public class IamProperties { /** * 匿名的url,以,逗号分隔 */ - private String anonUrls; + private Set anonUrls; /** * 是否初始化SQL */ @@ -60,5 +62,8 @@ public class IamProperties { * 是否开启权限检查(开发环境可关闭方便调试) */ private boolean enablePermissionCheck = true; - + /** + * 是否开启无状态 Jwt 身份验证过滤器 + */ + private boolean enableStatelessSession = false; } diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/starter/IamRedisAutoConfig.java b/diboot-iam-starter/src/main/java/com/diboot/iam/starter/IamRedisAutoConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..24b65feedc45902f570be55bbaad976120329589 --- /dev/null +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/starter/IamRedisAutoConfig.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015-2021, www.dibo.ltd (service@dibo.ltd). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.diboot.iam.starter; + +import com.diboot.iam.redis.ShiroRedisCacheManager; +import org.apache.shiro.cache.CacheManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +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.autoconfigure.condition.ConditionalOnResource; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.data.redis.core.RedisOperations; +import org.springframework.data.redis.core.RedisTemplate; + +/** + * Shiro の Redis 缓存自动配置 + * + * @author wind + * @version v2.3.0 + * @date 2021/7/20 + * Copyright © diboot.com + */ +@Order(921) +@Configuration +@ConditionalOnBean(RedisTemplate.class) +@ConditionalOnClass(RedisOperations.class) +@ConditionalOnResource(resources = "org/springframework/data/redis") +@AutoConfigureBefore({IamAutoConfig.class}) +public class IamRedisAutoConfig { + @Autowired + private IamProperties iamProperties; + + /** + * 启用RedisCacheManager定义 + * @return + */ + @Bean(name = "shiroCacheManager") + @ConditionalOnMissingBean(CacheManager.class) + public CacheManager shiroCacheManager(RedisTemplate redisTemplate) { + return new ShiroRedisCacheManager(redisTemplate, iamProperties.getJwtTokenExpiresMinutes()); + } + +} \ No newline at end of file diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/util/IamHelper.java b/diboot-iam-starter/src/main/java/com/diboot/iam/util/IamHelper.java index 2b290aff9e09823c60d002135ff7a7f4d2db80bc..b6d827dccb923f72f4fd281556d139b2e8be9ffc 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/util/IamHelper.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/util/IamHelper.java @@ -56,8 +56,8 @@ public class IamHelper { return null; } // 对RoleList做聚合处理,以适配前端 - List nameList = new ArrayList<>(); - List codeList = new ArrayList<>(); + List nameList = new ArrayList<>(roleVOList.size()); + List codeList = new ArrayList<>(roleVOList.size()); List allPermissionList = new ArrayList<>(); roleVOList.forEach(vo -> { nameList.add(vo.getName()); diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/util/IamSecurityUtils.java b/diboot-iam-starter/src/main/java/com/diboot/iam/util/IamSecurityUtils.java index c689a52fb7c4ef0ed62915c73f1cd6d8bb6fd8bc..4e70378ab04b59a123112fc9508fa54bf6ef42a9 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/util/IamSecurityUtils.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/util/IamSecurityUtils.java @@ -106,9 +106,7 @@ public class IamSecurityUtils extends SecurityUtils { if(baseJwtRealm != null){ Cache cache = baseJwtRealm.getAuthorizationCache(); if(cache != null) { - for(Object key : cache.keys()) { - cache.remove(key); - } + cache.clear(); log.debug("已清空全部登录用户的权限缓存,以便新权限生效."); } } diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/util/JwtUtils.java b/diboot-iam-starter/src/main/java/com/diboot/iam/util/JwtUtils.java index 431a66b7bb77cfbfbde6bb742c4051e2235a0c96..1320baa54fdb794f0d3f6bb6fe466117f28934be 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/util/JwtUtils.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/util/JwtUtils.java @@ -16,6 +16,7 @@ package com.diboot.iam.util; import com.diboot.core.config.BaseConfig; +import com.diboot.core.util.ContextHelper; import com.diboot.core.util.S; import com.diboot.core.util.V; import com.diboot.iam.config.Cons; @@ -23,6 +24,7 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; +import org.apache.shiro.cache.CacheManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -67,6 +69,15 @@ public class JwtUtils { } catch (ExpiredJwtException e) { log.warn("token已过期:{}", authtoken); + CacheManager cacheManager = ContextHelper.getBean(CacheManager.class); + if(cacheManager != null){ + if(cacheManager.getCache(Cons.AUTHENTICATION_CAHCE_NAME) != null){ + cacheManager.getCache(Cons.AUTHENTICATION_CAHCE_NAME).remove(authtoken); + } + if(cacheManager.getCache(Cons.AUTHORIZATION_CAHCE_NAME) != null){ + cacheManager.getCache(Cons.AUTHORIZATION_CAHCE_NAME).remove(authtoken); + } + } } catch (Exception e){ log.warn("token解析异常", e); diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/vo/IamAccountVO.java b/diboot-iam-starter/src/main/java/com/diboot/iam/vo/IamAccountVO.java index fbe35c16720c8ace9819568f567aeb6657a7b4b4..e8aad711d305b194526d229ea8c5d6dc130f9bdc 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/vo/IamAccountVO.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/vo/IamAccountVO.java @@ -17,6 +17,9 @@ package com.diboot.iam.vo; import com.diboot.iam.entity.IamAccount; import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; /** * 认证用户 VO定义 @@ -24,7 +27,9 @@ import lombok.Data; * @version 2.0 * @date 2019-12-03 */ -@Data +@Getter +@Setter +@Accessors(chain = true) public class IamAccountVO extends IamAccount { private static final long serialVersionUID = -6502855671661095919L; diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/vo/IamLoginTraceVO.java b/diboot-iam-starter/src/main/java/com/diboot/iam/vo/IamLoginTraceVO.java index b2398e48d600bd59d975c843520dffa31d3ec0ac..2d009783abdbb3fae7d84ecc41f71dbc78b44874 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/vo/IamLoginTraceVO.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/vo/IamLoginTraceVO.java @@ -18,6 +18,9 @@ package com.diboot.iam.vo; import com.diboot.core.binding.annotation.BindDict; import com.diboot.iam.entity.IamLoginTrace; import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; /** * 登录记录 VO定义 @@ -25,7 +28,9 @@ import lombok.Data; * @version 2.0 * @date 2019-12-17 */ -@Data +@Getter +@Setter +@Accessors(chain = true) public class IamLoginTraceVO extends IamLoginTrace { private static final long serialVersionUID = -753084580143028183L; diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/vo/IamRoleVO.java b/diboot-iam-starter/src/main/java/com/diboot/iam/vo/IamRoleVO.java index f7d050c105a44c912296a4225d0c8ce583372a03..425a51981b9d68a1bc3adb65fefc9b88f852e609 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/vo/IamRoleVO.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/vo/IamRoleVO.java @@ -21,6 +21,9 @@ import com.diboot.iam.config.Cons; import com.diboot.iam.entity.IamResourcePermission; import com.diboot.iam.entity.IamRole; import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; import java.util.List; @@ -30,7 +33,9 @@ import java.util.List; * @version 2.0 * @date 2019-12-03 */ -@Data +@Getter +@Setter +@Accessors(chain = true) public class IamRoleVO extends IamRole { private static final long serialVersionUID = -6778550575399070076L; diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/vo/IamUserVO.java b/diboot-iam-starter/src/main/java/com/diboot/iam/vo/IamUserVO.java index a3e39166a1b235f77aa197bf15766d38f18e52d9..cf378a8b7a996379440e757069e55d1bb2ace70c 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/vo/IamUserVO.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/vo/IamUserVO.java @@ -22,6 +22,9 @@ import com.diboot.iam.entity.IamOrg; import com.diboot.iam.entity.IamRole; import com.diboot.iam.entity.IamUser; import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; import java.util.List; @@ -31,7 +34,9 @@ import java.util.List; * @version 2.0 * @date 2019-12-17 */ -@Data +@Getter +@Setter +@Accessors(chain = true) public class IamUserVO extends IamUser { private static final long serialVersionUID = 7571698765478647277L; diff --git a/diboot-iam-starter/src/main/java/com/diboot/iam/vo/InvalidResourcePermissionVO.java b/diboot-iam-starter/src/main/java/com/diboot/iam/vo/InvalidResourcePermissionVO.java index b1c62876deb0e5bbd2d152bfcbbb7147399233af..4cadf72cf9513e022784cbf08892c38ffee6ad17 100644 --- a/diboot-iam-starter/src/main/java/com/diboot/iam/vo/InvalidResourcePermissionVO.java +++ b/diboot-iam-starter/src/main/java/com/diboot/iam/vo/InvalidResourcePermissionVO.java @@ -16,7 +16,6 @@ package com.diboot.iam.vo; import com.diboot.core.binding.annotation.BindDict; -import com.diboot.core.binding.annotation.BindEntityList; import com.diboot.core.binding.annotation.BindField; import com.diboot.core.util.V; import com.diboot.iam.entity.IamResourcePermission; @@ -24,7 +23,6 @@ import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; -import java.io.Serializable; import java.util.HashSet; import java.util.List; import java.util.Set; diff --git a/diboot-iam-starter/src/main/java/org/apache/shiro/subject/SimplePrincipalCollection.java b/diboot-iam-starter/src/main/java/org/apache/shiro/subject/SimplePrincipalCollection.java new file mode 100644 index 0000000000000000000000000000000000000000..47046621e1b13d7adf268f0ab83575c464e99ec6 --- /dev/null +++ b/diboot-iam-starter/src/main/java/org/apache/shiro/subject/SimplePrincipalCollection.java @@ -0,0 +1,302 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.shiro.subject; + +import com.fasterxml.jackson.annotation.JsonFilter; +import org.apache.shiro.util.CollectionUtils; +import org.apache.shiro.util.StringUtils; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.*; + +/** + * A simple implementation of the {@link MutablePrincipalCollection} interface that tracks principals internally + * by storing them in a {@link LinkedHashMap}. + * + * @since 0.9 + */ +@SuppressWarnings({"unchecked"}) +@JsonFilter("rewrite-bean") +public class SimplePrincipalCollection implements MutablePrincipalCollection { + + // Serialization reminder: + // You _MUST_ change this number if you introduce a change to this class + // that is NOT serialization backwards compatible. Serialization-compatible + // changes do not require a change to this number. If you need to generate + // a new number in this case, use the JDK's 'serialver' program to generate it. + private static final long serialVersionUID = -6305224034025797558L; + + //TODO - complete JavaDoc + + private Map realmPrincipals; + + private transient String cachedToString; //cached toString() result, as this can be printed many times in logging + + public SimplePrincipalCollection() { + } + + public SimplePrincipalCollection(Object principal, String realmName) { + if (principal instanceof Collection) { + addAll((Collection) principal, realmName); + } else { + add(principal, realmName); + } + } + + public SimplePrincipalCollection(Collection principals, String realmName) { + addAll(principals, realmName); + } + + public SimplePrincipalCollection(PrincipalCollection principals) { + addAll(principals); + } + + protected Collection getPrincipalsLazy(String realmName) { + if (realmPrincipals == null) { + realmPrincipals = new LinkedHashMap(); + } + Set principals = realmPrincipals.get(realmName); + if (principals == null) { + principals = new LinkedHashSet(); + realmPrincipals.put(realmName, principals); + } + return principals; + } + + /** + * Returns the first available principal from any of the {@code Realm} principals, or {@code null} if there are + * no principals yet. + *

+ * The 'first available principal' is interpreted as the principal that would be returned by + * {@link #iterator() iterator()}.{@link java.util.Iterator#next() next()}. + * + * @inheritDoc + */ + public Object getPrimaryPrincipal() { + if (isEmpty()) { + return null; + } + return iterator().next(); + } + + public void add(Object principal, String realmName) { + if (realmName == null) { + throw new NullPointerException("realmName argument cannot be null."); + } + if (principal == null) { + throw new NullPointerException("principal argument cannot be null."); + } + this.cachedToString = null; + getPrincipalsLazy(realmName).add(principal); + } + + public void addAll(Collection principals, String realmName) { + if (realmName == null) { + throw new NullPointerException("realmName argument cannot be null."); + } + if (principals == null) { + throw new NullPointerException("principals argument cannot be null."); + } + if (principals.isEmpty()) { + throw new IllegalArgumentException("principals argument cannot be an empty collection."); + } + this.cachedToString = null; + getPrincipalsLazy(realmName).addAll(principals); + } + + public void addAll(PrincipalCollection principals) { + if (principals.getRealmNames() != null) { + for (String realmName : principals.getRealmNames()) { + for (Object principal : principals.fromRealm(realmName)) { + add(principal, realmName); + } + } + } + } + + public T oneByType(Class type) { + if (realmPrincipals == null || realmPrincipals.isEmpty()) { + return null; + } + Collection values = realmPrincipals.values(); + for (Set set : values) { + for (Object o : set) { + if (type.isAssignableFrom(o.getClass())) { + return (T) o; + } + } + } + return null; + } + + public Collection byType(Class type) { + if (realmPrincipals == null || realmPrincipals.isEmpty()) { + return Collections.EMPTY_SET; + } + Set typed = new LinkedHashSet(); + Collection values = realmPrincipals.values(); + for (Set set : values) { + for (Object o : set) { + if (type.isAssignableFrom(o.getClass())) { + typed.add((T) o); + } + } + } + if (typed.isEmpty()) { + return Collections.EMPTY_SET; + } + return Collections.unmodifiableSet(typed); + } + + public List asList() { + Set all = asSet(); + if (all.isEmpty()) { + return Collections.EMPTY_LIST; + } + return Collections.unmodifiableList(new ArrayList(all)); + } + + public Set asSet() { + if (realmPrincipals == null || realmPrincipals.isEmpty()) { + return Collections.EMPTY_SET; + } + Set aggregated = new LinkedHashSet(); + Collection values = realmPrincipals.values(); + for (Set set : values) { + aggregated.addAll(set); + } + if (aggregated.isEmpty()) { + return Collections.EMPTY_SET; + } + return Collections.unmodifiableSet(aggregated); + } + + public Collection fromRealm(String realmName) { + if (realmPrincipals == null || realmPrincipals.isEmpty()) { + return Collections.EMPTY_SET; + } + Set principals = realmPrincipals.get(realmName); + if (principals == null || principals.isEmpty()) { + principals = Collections.EMPTY_SET; + } + return Collections.unmodifiableSet(principals); + } + + public Set getRealmNames() { + if (realmPrincipals == null) { + return null; + } else { + return realmPrincipals.keySet(); + } + } + + public boolean isEmpty() { + return realmPrincipals == null || realmPrincipals.isEmpty(); + } + + public void clear() { + this.cachedToString = null; + if (realmPrincipals != null) { + realmPrincipals.clear(); + realmPrincipals = null; + } + } + + public Iterator iterator() { + return asSet().iterator(); + } + + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof SimplePrincipalCollection) { + SimplePrincipalCollection other = (SimplePrincipalCollection) o; + return this.realmPrincipals != null ? this.realmPrincipals.equals(other.realmPrincipals) : other.realmPrincipals == null; + } + return false; + } + + public int hashCode() { + if (this.realmPrincipals != null && !realmPrincipals.isEmpty()) { + return realmPrincipals.hashCode(); + } + return super.hashCode(); + } + + /** + * Returns a simple string representation suitable for printing. + * + * @return a simple string representation suitable for printing. + * @since 1.0 + */ + public String toString() { + if (this.cachedToString == null) { + Set principals = asSet(); + if (!CollectionUtils.isEmpty(principals)) { + this.cachedToString = StringUtils.toString(principals.toArray()); + } else { + this.cachedToString = "empty"; + } + } + return this.cachedToString; + } + + + /** + * Serialization write support. + *

+ * NOTE: Don't forget to change the serialVersionUID constant at the top of this class + * if you make any backwards-incompatible serialization changes!!! + * (use the JDK 'serialver' program for this) + * + * @param out output stream provided by Java serialization + * @throws IOException if there is a stream error + */ + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + boolean principalsExist = !CollectionUtils.isEmpty(realmPrincipals); + out.writeBoolean(principalsExist); + if (principalsExist) { + out.writeObject(realmPrincipals); + } + } + + /** + * Serialization read support - reads in the Map principals collection if it exists in the + * input stream. + *

+ * NOTE: Don't forget to change the serialVersionUID constant at the top of this class + * if you make any backwards-incompatible serialization changes!!! + * (use the JDK 'serialver' program for this) + * + * @param in input stream provided by + * @throws IOException if there is an input/output problem + * @throws ClassNotFoundException if the underlying Map implementation class is not available to the classloader. + */ + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + boolean principalsExist = in.readBoolean(); + if (principalsExist) { + this.realmPrincipals = (Map) in.readObject(); + } + } +} diff --git a/diboot-iam-starter/src/main/resources/META-INF/sql/init-iam-mysql.sql b/diboot-iam-starter/src/main/resources/META-INF/sql/init-iam-mysql.sql index ca40d21924cb2d09fe0d7f41689476561c7c1200..05d0aed94381120601d89643cd65ffaf488854ed 100644 --- a/diboot-iam-starter/src/main/resources/META-INF/sql/init-iam-mysql.sql +++ b/diboot-iam-starter/src/main/resources/META-INF/sql/init-iam-mysql.sql @@ -18,7 +18,7 @@ create table iam_user -- 索引 create index idx_iam_user_1 on iam_user (org_id); create index idx_iam_user_2 on iam_user (mobile_phone); -create unique index uidx_iam_user on iam_user (tenant_id, user_num); +create index idx_iam_user_num on iam_user (user_num); create index idx_iam_user_tenant on iam_user (tenant_id); -- 账号表 @@ -37,7 +37,7 @@ create table iam_account create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间' ) AUTO_INCREMENT=10000 DEFAULT CHARSET=utf8 COMMENT '登录账号'; -- 创建索引 -create unique index idx_iam_account on iam_account(tenant_id, auth_account, auth_type, user_type); +create index idx_iam_account on iam_account(auth_account, auth_type, user_type); create index idx_iam_account_tenant on iam_account (tenant_id); -- 角色表 diff --git a/diboot-iam-starter/src/main/resources/META-INF/sql/init-iam-oracle.sql b/diboot-iam-starter/src/main/resources/META-INF/sql/init-iam-oracle.sql index 2b2f21d1a7ea908e09c4264fdfa5de0d6f6adedb..0dc424287061bcc557491055435e7740386679e1 100644 --- a/diboot-iam-starter/src/main/resources/META-INF/sql/init-iam-oracle.sql +++ b/diboot-iam-starter/src/main/resources/META-INF/sql/init-iam-oracle.sql @@ -34,7 +34,7 @@ comment on table ${SCHEMA}.iam_user is '系统用户'; -- 索引 create index idx_iam_user_1 on ${SCHEMA}.iam_user (org_id); create index idx_iam_user_2 on ${SCHEMA}.iam_user (mobile_phone); -create unique index uidx_iam_user on ${SCHEMA}.iam_user (tenant_id, user_num); +create index idx_iam_user_num on ${SCHEMA}.iam_user (user_num); create index idx_iam_user_tenant on ${SCHEMA}.iam_user (tenant_id); -- 账号表 @@ -66,7 +66,7 @@ comment on column ${SCHEMA}.iam_account.is_deleted is '是否删除'; comment on column ${SCHEMA}.iam_account.create_time is '创建时间'; comment on table ${SCHEMA}.iam_account is '登录账号'; -- 创建索引 -create unique index idx_iam_account on ${SCHEMA}.iam_account(tenant_id, auth_account, auth_type, user_type); +create index idx_iam_account on ${SCHEMA}.iam_account(auth_account, auth_type, user_type); create index idx_iam_account_tenant on ${SCHEMA}.iam_account (tenant_id); -- 角色表 diff --git a/diboot-iam-starter/src/main/resources/META-INF/sql/init-iam-postgresql.sql b/diboot-iam-starter/src/main/resources/META-INF/sql/init-iam-postgresql.sql index 0f71c36ff46f0133afe930b3153b94cf9f2df644..62842a5c01058719f7199a0abf076a9fa5b4d9db 100644 --- a/diboot-iam-starter/src/main/resources/META-INF/sql/init-iam-postgresql.sql +++ b/diboot-iam-starter/src/main/resources/META-INF/sql/init-iam-postgresql.sql @@ -33,7 +33,7 @@ comment on table iam_user is '系统用户'; -- 索引 create index idx_iam_user_1 on iam_user (org_id); create index idx_iam_user_2 on iam_user (mobile_phone); -create unique index uidx_iam_user on iam_user (tenant_id, user_num); +create index idx_iam_user_num on iam_user (user_num); create index idx_iam_user_tenant on iam_user(tenant_id); -- 账号表 @@ -64,7 +64,7 @@ comment on column iam_account.is_deleted is '是否删除'; comment on column iam_account.create_time is '创建时间'; comment on table iam_account is '登录账号'; -- 创建索引 -create unique index idx_iam_account on iam_account(tenant_id, auth_account, auth_type, user_type); +create index idx_iam_account on iam_account(auth_account, auth_type, user_type); create index idx_iam_account_tenant on iam_account(tenant_id); -- 角色表 diff --git a/diboot-iam-starter/src/main/resources/META-INF/sql/init-iam-sqlserver.sql b/diboot-iam-starter/src/main/resources/META-INF/sql/init-iam-sqlserver.sql index ba6c657987bc0ccca151440b6da1bbc9cd5d62cc..89048017898651568de290cb54c90034a0b8beeb 100644 --- a/diboot-iam-starter/src/main/resources/META-INF/sql/init-iam-sqlserver.sql +++ b/diboot-iam-starter/src/main/resources/META-INF/sql/init-iam-sqlserver.sql @@ -34,7 +34,7 @@ execute sp_addextendedproperty 'MS_Description', N'系统用户', 'SCHEMA', '${S -- 索引 create nonclustered index idx_iam_user_1 on iam_user (org_id); create nonclustered index idx_iam_user_2 on iam_user (mobile_phone); -create unique index uidx_iam_user on iam_user (tenant_id, user_num); +create nonclustered index idx_iam_user_num on iam_user (user_num); create nonclustered index idx_iam_user_tenant on iam_user(tenant_id); -- 账号表 @@ -66,7 +66,7 @@ execute sp_addextendedproperty 'MS_Description', N'是否删除', 'SCHEMA', '${S execute sp_addextendedproperty 'MS_Description', N'创建时间', 'SCHEMA', '${SCHEMA}', 'table', iam_account, 'column', 'create_time'; execute sp_addextendedproperty 'MS_Description', N'登录账号', 'SCHEMA', '${SCHEMA}', 'table', iam_account, null, null; -- 创建索引 -create unique index idx_iam_account on iam_account(tenant_id, auth_account, auth_type, user_type); +create index idx_iam_account on iam_account(auth_account, auth_type, user_type); create nonclustered index idx_iam_account_tenant on iam_account(tenant_id); -- 角色表 diff --git a/diboot-message-starter/pom.xml b/diboot-message-starter/pom.xml index 99d622c5949e3bcaea6d69da958d4951ab047ec3..fded314f12b4967f8a934bc6eac1434183f412d9 100644 --- a/diboot-message-starter/pom.xml +++ b/diboot-message-starter/pom.xml @@ -5,12 +5,12 @@ diboot-root com.diboot - 2.2.1 + 2.3.0 4.0.0 diboot-message-spring-boot-starter - 2.2.1 + 2.3.0 jar diboot消息组件 diff --git a/diboot-message-starter/src/main/java/com/diboot/message/channel/SimpleEmailChannel.java b/diboot-message-starter/src/main/java/com/diboot/message/channel/SimpleEmailChannel.java index 096cb3f5d74f13e87a95ece49a71912b24977c23..abb6c725390947ec7ca280e1e1ecb5ffe7f57ec7 100644 --- a/diboot-message-starter/src/main/java/com/diboot/message/channel/SimpleEmailChannel.java +++ b/diboot-message-starter/src/main/java/com/diboot/message/channel/SimpleEmailChannel.java @@ -23,10 +23,14 @@ import com.diboot.message.entity.Message; import com.diboot.message.service.MessageService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.FileSystemResource; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.scheduling.annotation.Async; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeUtility; import java.util.Date; /** @@ -56,28 +60,62 @@ public class SimpleEmailChannel implements ChannelStrategy { String result = "success"; String status = Cons.MESSAGE_STATUS.DELIVERY.getItemValue(); try { - // 构建一个邮件对象 - SimpleMailMessage simpleMailMessage = new SimpleMailMessage(); - // 设置邮件主题 - simpleMailMessage.setSubject(message.getTitle()); - // 设置发送人 - simpleMailMessage.setFrom(message.getSender()); - // 优先使用Receivers - if (V.notEmpty(message.getReceivers())) { - simpleMailMessage.setTo(message.getReceivers()); - } else { - simpleMailMessage.setTo(message.getReceiver()); + if (V.isEmpty(message.getExtDataMap().get(Message.ATTACHMENTS))) { + // 构建一个邮件对象 + SimpleMailMessage simpleMailMessage = new SimpleMailMessage(); + // 设置邮件主题 + simpleMailMessage.setSubject(message.getTitle()); + // 设置发送人 + simpleMailMessage.setFrom(message.getSender()); + // 优先使用Receivers + if (V.notEmpty(message.getReceivers())) { + simpleMailMessage.setTo(message.getReceivers()); + } else { + simpleMailMessage.setTo(message.getReceiver()); + } + // 设置抄送人 + simpleMailMessage.setCc(message.getCcEmails()); + // 设置隐秘抄送人 + simpleMailMessage.setBcc(message.getBccEmails()); + // 设置邮件发送日期 + simpleMailMessage.setSentDate(new Date()); + // 设置邮件内容 + simpleMailMessage.setText(message.getContent()); + + // 发送邮件(无附件) + javaMailSender.send(simpleMailMessage); + } else{ + MimeMessage mimeMessage = javaMailSender.createMimeMessage(); + //发送有附件邮件 + MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true, "UTF-8"); + // 设置邮件主题 + messageHelper.setSubject(message.getTitle()); + // 设置发送人 + messageHelper.setFrom(message.getSender()); + // 优先使用Receivers + if (V.notEmpty(message.getReceivers())) { + // 设置发送人 + messageHelper.setTo(message.getReceivers()); + }else{ + messageHelper.setTo(message.getReceiver()); + } + // 设置抄送人 + messageHelper.setCc(message.getCcEmails()); + // 设置隐秘抄送人 + messageHelper.setBcc(message.getBccEmails()); + // 设置邮件发送日期 + messageHelper.setSentDate(new Date()); + // 设置邮件内容 + messageHelper.setText(message.getContent()); + //添加附件 + String[] fileUrlList = message.getAttachments(); + for (String url:fileUrlList){ + FileSystemResource fileSystemResource = new FileSystemResource(url); + messageHelper.addAttachment(MimeUtility.encodeWord(fileSystemResource.getFilename(),"utf-8","B"), fileSystemResource); + } + //发送邮件(有附件) + javaMailSender.send(mimeMessage); } - // 设置抄送人 - simpleMailMessage.setCc(message.getCcEmails()); - // 设置隐秘抄送人 - simpleMailMessage.setBcc(message.getBccEmails()); - // 设置邮件发送日期 - simpleMailMessage.setSentDate(new Date()); - // 设置邮件内容 - simpleMailMessage.setText(message.getContent()); - // 发送邮件 - javaMailSender.send(simpleMailMessage); } catch (Exception e) { log.error("[发送邮件失败]:信息为: {} , 异常", message, e); result = e.getMessage(); diff --git a/diboot-message-starter/src/main/java/com/diboot/message/service/impl/MessageTemplateServiceImpl.java b/diboot-message-starter/src/main/java/com/diboot/message/service/impl/MessageTemplateServiceImpl.java index 799e16115dd6f32cc07cf95aba76423d2d76c038..933935760aa36960866157315075b8d3536df482 100644 --- a/diboot-message-starter/src/main/java/com/diboot/message/service/impl/MessageTemplateServiceImpl.java +++ b/diboot-message-starter/src/main/java/com/diboot/message/service/impl/MessageTemplateServiceImpl.java @@ -55,7 +55,7 @@ public class MessageTemplateServiceImpl extends BaseServiceImpl diboot-root com.diboot - 2.2.1 + 2.3.0 4.0.0 com.diboot diboot-scheduler-spring-boot-starter - 2.2.1 + 2.3.0 diff --git a/diboot-scheduler-starter/src/main/java/com/diboot/scheduler/service/impl/QuartzSchedulerService.java b/diboot-scheduler-starter/src/main/java/com/diboot/scheduler/service/impl/QuartzSchedulerService.java index ba681f89656f119b7e955d077d90d8c5593b068b..902b608a8cf12ed9a98b3b61ee342bca406fadd1 100644 --- a/diboot-scheduler-starter/src/main/java/com/diboot/scheduler/service/impl/QuartzSchedulerService.java +++ b/diboot-scheduler-starter/src/main/java/com/diboot/scheduler/service/impl/QuartzSchedulerService.java @@ -15,11 +15,9 @@ */ package com.diboot.scheduler.service.impl; -import ch.qos.logback.core.db.BindDataSourceToJNDIAction; import com.diboot.core.exception.BusinessException; import com.diboot.core.util.*; import com.diboot.core.vo.Status; -import com.diboot.scheduler.annotation.BindJob; import com.diboot.scheduler.annotation.CollectThisJob; import com.diboot.scheduler.entity.ScheduleJob; import com.diboot.scheduler.starter.SchedulerProperties; @@ -30,7 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; -import java.lang.annotation.Annotation; import java.util.*; /** @@ -93,12 +90,6 @@ public class QuartzSchedulerService { List> result = loadJobs(annoJobList); CACHE_JOB.addAll(result); } - // 兼容旧版本BindJob注解 - annoJobList = ContextHelper.getBeansByAnnotation(BindJob.class); - if (V.notEmpty(annoJobList)) { - List> result = loadJobs(annoJobList); - CACHE_JOB.addAll(result); - } return CACHE_JOB; } @@ -125,15 +116,6 @@ public class QuartzSchedulerService { paramJson = annotation.paramJson(); paramClass = annotation.paramClass(); } - else{ // 兼容旧版本BindJob注解 - BindJob bindJobAnno = (BindJob) targetClass.getAnnotation(BindJob.class); - if(bindJobAnno != null){ - jobCron = bindJobAnno.cron(); - jobName = bindJobAnno.name(); - paramJson = bindJobAnno.paramJson(); - paramClass = bindJobAnno.paramClass(); - } - } temp.put("jobCron", jobCron); temp.put("jobName", jobName); temp.put("jobClass", targetClass); diff --git a/pom.xml b/pom.xml index bcb9ca3386aefb5ede1bec668d330e7789bcbec2..9f56c356ba47322cb6ecf3a11f25cea0497d66b5 100644 --- a/pom.xml +++ b/pom.xml @@ -7,13 +7,13 @@ org.springframework.boot spring-boot-starter-parent - 2.4.5 + 2.5.3 com.diboot diboot-root - 2.2.1 + 2.3.0 pom @@ -27,11 +27,11 @@ 1.8 - 2.4.5 + 2.5.3 6.2.0.Final - 3.4.2 + 3.4.3.1 4.9.1 - 2.2.1 + 2.3.0 @@ -48,6 +48,11 @@ 8.0.23 provided + + org.springframework.boot + spring-boot-configuration-processor + true +